It is not uncommon for an index to be defined as e.g. 'fulltext'
which SQLite3 does not support without a module to create a
virtual table (rather than an index on an existing one). The code
already in place sees that definitions be updated to 'index' on
the fly during creation and later inspection (indexList) - which
causes issue when comparing existing table definitions to what
SilverStripe expects by DataObject configuration. This discrepency
leads to tables constantly being marked to update, although
effectively nothing actually changes. We can save these CPU cycles
and a bit of head scratching by converting to a supported index type.
The logic for cancelling a savepoint was incorrect, as the behaviour
the logic was modelled on was for a different RDBMS - where a COMMIT
would always close the most recently opened transaction.
SQLite on contrast will commit the entire transaction, not just the
most recent savepoint marker until current execution point. The correct
manner to deal with a 'partial' commit is to call RELEASE <savepoint>.
This revealed an error in the savepoint logic, in that if someone had
supplied a savepoint name instead of relying on generated ones, the
rollback command did not factor for this and always assumed generated
savepoint names - again causing error. For this reason a new class
member field has been introduced to track savepoint names in a stack
fashion.
Transactions that used more than one level would cause errors if
there were consecutive calls to start a transaction - because each
query executed would clear the flag indicating that a transaction
was already in progress.
The comment for the logic to reset the nesting level on a query was
indicating that DDL (data definition language) would not work within
a transaction. This is untrue, and the module itself uses a transaction
to alter table or field names. So this function has been converted to
a no-op, deprecated to be removed in version 3 of this module. It is
also no longer called upon each query.
There have been some maintenance tidyups around this area also by
abstracting the nested transaction flag manipulations into protected
functions.
Code in the field alteration logic had a queries defiend as strings to
begin and commit transactions involve with changing table or column names.
This was causing fatal errors as BEGIN is not a valid keyword within
a trasaction (see SQLite documentation excerpt below).
A new api has been introduced to deal with transactions programmatically,
and this module was updated to support this a few months ago. This is a
tidy up of some missed portions - consuming this API which correctly uses
SAVEPOINT when a nested transaction is required automatically.
https://www.sqlite.org/lang_transaction.html
Transactions created using BEGIN...COMMIT do not nest. For nested
transactions, use the SAVEPOINT and RELEASE commands.
Enum values are themselves enumerated in sqlite as they are not supported
as a type. This leads to values being stored in their own table, and a
regular TEXT field being used in a MySQL ENUM's stead. The default value
for this field was being escaped with custom string replacement, and
erroneously relacing the backslash (a redundant operation). This lead
to invalid Fully Qualified Class Names in SilverStripe 4, which is a
required trait for polymorphic relationships. As a result any polymorphic
relationship not set on first write would then proceed to cause an execution
error the next time the dataobject with the relationship was fetched from
the database. By using the PHP supplied escape function for SQLite3 we can
avoid this, and restore functionality.
Relevant section of SQLite documentation to justify the removal of escaping
various characters, such as the backslash:
A string constant is formed by enclosing the string in single quotes (').
A single quote within the string can be encoded by putting two single quotes
in a row - as in Pascal. C-style escapes using the backslash character are
not supported because they are not standard SQL.
https://www.sqlite.org/lang_expr.html
Instead of using a custom "memory" key in the $databaseConfig
which never really got used this now allows someone to simply enable
in-memory databases by setting the path to ":memory:" or to use the
following environment variable:
define('SS_SQLITE_DATABASE_PATH', ':memory:');
In preparation for https://github.com/silverstripe/sapphire/pull/1319
Probably should accept this at the same time.
If someone knows of the relevant ALTER permissions in SQLite, feel free to implement.
As opposed to LIKE, the GLOB operator is case sensitive by default
in SQlite3. It uses "*" instead of "%" for wildcards,
which necessitated a new SearchFilter->getWildcard() method.
SQlite3 doesn't support per-term modifiers,
COLLATE BINARY LIKE is case insensitive by default
unless the field collation is set up accordingly.
There's connection-level modifiers (PRAGMA case_sensitive_like = true),
but that would affect all comparisators in the executed query.
While the SQLite3 module is predominantly used for testing,
its best to leave this decision to the code using it.
We should default to a conservative setting (slower, but persistent).
Also remove coupling to SapphireTest when setting this value.
Don't bother creating the directory if running in-memory.