API Apply SilverStripe\SQLite namespace to module

This commit is contained in:
Damian Mooyman 2016-06-29 13:55:45 +12:00
parent 0bd28649f5
commit e36e74ab2f
9 changed files with 224 additions and 145 deletions

7
.upgrade.yml Normal file
View File

@ -0,0 +1,7 @@
mappings:
SQLite3Connector: SilverStripe\SQLite\SQLite3Connector
SQLite3Database: SilverStripe\SQLite\SQLite3Database
SQLite3Query: SilverStripe\SQLite\SQLite3Query
SQLite3QueryBuilder: SilverStripe\SQLite\SQLite3QueryBuilder
SQLite3SchemaManager: SilverStripe\SQLite\SQLite3SchemaManager
SQLiteDatabaseConfigurationHelper: SilverStripe\SQLite\SQLiteDatabaseConfigurationHelper

View File

@ -3,27 +3,34 @@ name: sqlite3connectors
--- ---
Injector: Injector:
SQLite3PDODatabase: SQLite3PDODatabase:
class: 'SQLite3Database' class: 'SilverStripe\SQLite\SQLite3Database'
properties: properties:
connector: %$PDOConnector connector: %$PDOConnector
schemaManager: %$SQLite3SchemaManager schemaManager: %$SQLite3SchemaManager
queryBuilder: %$SQLite3QueryBuilder queryBuilder: %$SQLite3QueryBuilder
SQLite3Database: SQLite3Database:
class: 'SQLite3Database' class: 'SilverStripe\SQLite\SQLite3Database'
properties: properties:
connector: %$SQLite3Connector connector: %$SQLite3Connector
schemaManager: %$SQLite3SchemaManager schemaManager: %$SQLite3SchemaManager
queryBuilder: %$SQLite3QueryBuilder queryBuilder: %$SQLite3QueryBuilder
# Legacy connector names # Legacy connector names
SQLiteDatabase: SQLiteDatabase:
class: 'SQLite3Database' class: 'SilverStripe\SQLite\SQLite3Database'
properties: properties:
connector: %$SQLite3Connector connector: %$SQLite3Connector
schemaManager: %$SQLite3SchemaManager schemaManager: %$SQLite3SchemaManager
queryBuilder: %$SQLite3QueryBuilder queryBuilder: %$SQLite3QueryBuilder
SQLitePDODatabase: SQLitePDODatabase:
class: 'SQLite3Database' class: 'SilverStripe\SQLite\SQLite3Database'
properties: properties:
connector: %$SQLite3Connector connector: %$SQLite3Connector
schemaManager: %$SQLite3SchemaManager schemaManager: %$SQLite3SchemaManager
queryBuilder: %$SQLite3QueryBuilder queryBuilder: %$SQLite3QueryBuilder
SQLite3Connector:
class: 'SilverStripe\SQLite\SQLite3Connector'
type: prototype
SQLite3SchemaManager:
class: 'SilverStripe\SQLite\SQLite3SchemaManager'
SQLite3QueryBuilder:
class: 'SilverStripe\SQLite\SQLite3QueryBuilder'

View File

@ -1,8 +1,13 @@
<?php <?php
namespace SilverStripe\SQLite;
use SilverStripe\ORM\Connect\DBConnector;
use SQLite3;
/** /**
* SQLite connector class * SQLite connector class
* *
* @package SQLite3 * @package SQLite3
*/ */
class SQLite3Connector extends DBConnector class SQLite3Connector extends DBConnector
@ -10,14 +15,14 @@ class SQLite3Connector extends DBConnector
/** /**
* The name of the database. * The name of the database.
* *
* @var string * @var string
*/ */
protected $databaseName; protected $databaseName;
/** /**
* Connection to the DBMS. * Connection to the DBMS.
* *
* @var SQLite3 * @var SQLite3
*/ */
protected $dbConn; protected $dbConn;
@ -66,7 +71,7 @@ class SQLite3Connector extends DBConnector
/** /**
* Prepares the list of parameters in preparation for passing to mysqli_stmt_bind_param * Prepares the list of parameters in preparation for passing to mysqli_stmt_bind_param
* *
* @param array $parameters List of parameters * @param array $parameters List of parameters
* @return array List of parameters types and values * @return array List of parameters types and values
*/ */
@ -133,14 +138,14 @@ class SQLite3Connector extends DBConnector
$type = $parsedParameters[$i]['type']; $type = $parsedParameters[$i]['type'];
$statement->bindValue($i+1, $value, $type); $statement->bindValue($i+1, $value, $type);
} }
// Return successful result // Return successful result
$handle = $statement->execute(); $handle = $statement->execute();
if ($handle) { if ($handle) {
return new SQLite3Query($this, $handle); return new SQLite3Query($this, $handle);
} }
} }
// Handle error // Handle error
$values = $this->parameterValues($parameters); $values = $this->parameterValues($parameters);
$this->databaseError($this->getLastError(), $errorLevel, $sql, $values); $this->databaseError($this->getLastError(), $errorLevel, $sql, $values);
@ -154,7 +159,7 @@ class SQLite3Connector extends DBConnector
if ($handle) { if ($handle) {
return new SQLite3Query($this, $handle); return new SQLite3Query($this, $handle);
} }
// Handle error // Handle error
$this->databaseError($this->getLastError(), $errorLevel, $sql); $this->databaseError($this->getLastError(), $errorLevel, $sql);
return null; return null;

View File

@ -1,8 +1,19 @@
<?php <?php
namespace SilverStripe\SQLite;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\Connect\SS_Database;
use Config;
use Deprecation;
use PaginatedList;
use SilverStripe\ORM\Queries\SQLSelect;
/** /**
* SQLite database controller class * SQLite database controller class
* *
* @package SQLite3 * @package SQLite3
*/ */
class SQLite3Database extends SS_Database class SQLite3Database extends SS_Database
@ -10,7 +21,7 @@ class SQLite3Database extends SS_Database
/** /**
* Database schema manager object * Database schema manager object
* *
* @var SQLite3SchemaManager * @var SQLite3SchemaManager
*/ */
protected $schemaManager = null; protected $schemaManager = null;
@ -18,21 +29,21 @@ class SQLite3Database extends SS_Database
/* /*
* This holds the parameters that the original connection was created with, * This holds the parameters that the original connection was created with,
* so we can switch back to it if necessary (used for unit tests) * so we can switch back to it if necessary (used for unit tests)
* *
* @var array * @var array
*/ */
protected $parameters; protected $parameters;
/* /*
* if we're on a In-Memory db * if we're on a In-Memory db
* *
* @var boolean * @var boolean
*/ */
protected $livesInMemory = false; protected $livesInMemory = false;
/** /**
* List of default pragma values * List of default pragma values
* *
* @todo Migrate to SS config * @todo Migrate to SS config
* *
* @var array * @var array
@ -46,7 +57,7 @@ class SQLite3Database extends SS_Database
/** /**
* Extension used to distinguish between sqllite database files and other files. * Extension used to distinguish between sqllite database files and other files.
* Required to handle multiple databases. * Required to handle multiple databases.
* *
* @return string * @return string
*/ */
public static function database_extension() public static function database_extension()
@ -56,7 +67,7 @@ class SQLite3Database extends SS_Database
/** /**
* Check if a database name has a valid extension * Check if a database name has a valid extension
* *
* @param string $name * @param string $name
* @return boolean * @return boolean
*/ */
@ -89,7 +100,7 @@ class SQLite3Database extends SS_Database
unset($parameters['memory']); unset($parameters['memory']);
$parameters['path'] = ':memory:'; $parameters['path'] = ':memory:';
} }
//We will store these connection parameters for use elsewhere (ie, unit tests) //We will store these connection parameters for use elsewhere (ie, unit tests)
$this->parameters = $parameters; $this->parameters = $parameters;
$this->schemaManager->flushCache(); $this->schemaManager->flushCache();
@ -122,7 +133,7 @@ class SQLite3Database extends SS_Database
SQLiteDatabaseConfigurationHelper::secure_db_dir($parameters['path']); SQLiteDatabaseConfigurationHelper::secure_db_dir($parameters['path']);
} }
} }
// 'path' and 'database' are merged into the full file path, which // 'path' and 'database' are merged into the full file path, which
// is the format that connectors such as PDOConnector expect // is the format that connectors such as PDOConnector expect
$parameters['filepath'] = $file; $parameters['filepath'] = $file;
@ -145,7 +156,7 @@ class SQLite3Database extends SS_Database
/** /**
* Retrieve parameters used to connect to this SQLLite database * Retrieve parameters used to connect to this SQLLite database
* *
* @return array * @return array
*/ */
public function getParameters() public function getParameters()
@ -170,9 +181,9 @@ class SQLite3Database extends SS_Database
/** /**
* Execute PRAGMA commands. * Execute PRAGMA commands.
* *
* @param string pragma name * @param string $pragma name
* @param string value to set * @param string $value to set
*/ */
public function setPragma($pragma, $value) public function setPragma($pragma, $value)
{ {
@ -181,8 +192,8 @@ class SQLite3Database extends SS_Database
/** /**
* Gets pragma value. * Gets pragma value.
* *
* @param string pragma name * @param string $pragma name
* @return string the pragma value * @return string the pragma value
*/ */
public function getPragma($pragma) public function getPragma($pragma)
@ -236,9 +247,17 @@ class SQLite3Database extends SS_Database
* - there must not be more than one MATCH operator per statement * - there must not be more than one MATCH operator per statement
* - the fts3 extension needs to be available * - the fts3 extension needs to be available
* for now we use the MySQL implementation with the MATCH()AGAINST() uglily replaced with LIKE * for now we use the MySQL implementation with the MATCH()AGAINST() uglily replaced with LIKE
* *
* @param array $classesToSearch
* @param string $keywords Keywords as a space separated string * @param string $keywords Keywords as a space separated string
* @return object DataObjectSet of result pages * @param int $start
* @param int $pageLength
* @param string $sortBy
* @param string $extraFilter
* @param bool $booleanSearch
* @param string $alternativeFileFilter
* @param bool $invertedMatch
* @return PaginatedList DataObjectSet of result pages
*/ */
public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC",
$extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false
@ -260,7 +279,7 @@ class SQLite3Database extends SS_Database
// Always ensure that only pages with ShowInSearch = 1 can be searched // Always ensure that only pages with ShowInSearch = 1 can be searched
$extraFilters['SiteTree'] .= ' AND ShowInSearch <> 0'; $extraFilters['SiteTree'] .= ' AND ShowInSearch <> 0';
// File.ShowInSearch was added later, keep the database driver backwards compatible // File.ShowInSearch was added later, keep the database driver backwards compatible
// by checking for its existence first // by checking for its existence first
$fields = $this->getSchemaManager()->fieldList('File'); $fields = $this->getSchemaManager()->fieldList('File');
if (array_key_exists('ShowInSearch', $fields)) { if (array_key_exists('ShowInSearch', $fields)) {
@ -291,7 +310,10 @@ class SQLite3Database extends SS_Database
$baseClasses = array('SiteTree' => '', 'File' => ''); $baseClasses = array('SiteTree' => '', 'File' => '');
$queries = array(); $queries = array();
foreach ($classesToSearch as $class) { foreach ($classesToSearch as $class) {
$queries[$class] = DataList::create($class)->where($notMatch . $match[$class] . $extraFilters[$class], "")->dataQuery()->query(); $queries[$class] = DataList::create($class)
->where($notMatch . $match[$class] . $extraFilters[$class])
->dataQuery()
->query();
$fromArr = $queries[$class]->getFrom(); $fromArr = $queries[$class]->getFrom();
$baseClasses[$class] = reset($fromArr); $baseClasses[$class] = reset($fromArr);
} }
@ -348,6 +370,7 @@ class SQLite3Database extends SS_Database
$queryParameters = array(); $queryParameters = array();
$totalCount = 0; $totalCount = 0;
foreach ($queries as $query) { foreach ($queries as $query) {
/** @var SQLSelect $query */
$querySQLs[] = $query->sql($parameters); $querySQLs[] = $query->sql($parameters);
$queryParameters = array_merge($queryParameters, $parameters); $queryParameters = array_merge($queryParameters, $parameters);
$totalCount += $query->unlimitedRowCount(); $totalCount += $query->unlimitedRowCount();
@ -368,7 +391,7 @@ class SQLite3Database extends SS_Database
} }
$list = new PaginatedList($doSet); $list = new PaginatedList($doSet);
$list->setPageStart($start); $list->setPageStart($start);
$list->setPageLEngth($pageLength); $list->setPageLength($pageLength);
$list->setTotalItems($totalCount); $list->setTotalItems($totalCount);
return $list; return $list;
} }

View File

@ -1,8 +1,13 @@
<?php <?php
namespace SilverStripe\SQLite;
use SilverStripe\ORM\Connect\SS_Query;
use SQLite3Result;
/** /**
* A result-set from a SQLite3 database. * A result-set from a SQLite3 database.
* *
* @package SQLite3 * @package SQLite3
*/ */
class SQLite3Query extends SS_Query class SQLite3Query extends SS_Query
@ -10,14 +15,14 @@ class SQLite3Query extends SS_Query
/** /**
* The SQLite3Connector object that created this result set. * The SQLite3Connector object that created this result set.
* *
* @var SQLite3Connector * @var SQLite3Connector
*/ */
protected $database; protected $database;
/** /**
* The internal sqlite3 handle that points to the result set. * The internal sqlite3 handle that points to the result set.
* *
* @var SQLite3Result * @var SQLite3Result
*/ */
protected $handle; protected $handle;

View File

@ -1,13 +1,21 @@
<?php <?php
namespace SilverStripe\SQLite;
use SilverStripe\ORM\Queries\SQLAssignmentRow;
use SilverStripe\ORM\Queries\SQLInsert;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\Connect\DBQueryBuilder;
use InvalidArgumentException;
/** /**
* Builds a SQL query string from a SQLExpression object * Builds a SQL query string from a SQLExpression object
* *
* @package SQLite3 * @package SQLite3
*/ */
class SQLite3QueryBuilder extends DBQueryBuilder class SQLite3QueryBuilder extends DBQueryBuilder
{ {
/** /**
* @param SQLInsert $query * @param SQLInsert $query
* @param array $parameters * @param array $parameters
@ -20,14 +28,15 @@ class SQLite3QueryBuilder extends DBQueryBuilder
$nl = $this->getSeparator(); $nl = $this->getSeparator();
$into = $query->getInto(); $into = $query->getInto();
// Column identifiers // Column identifiers
$columns = $query->getColumns(); $columns = $query->getColumns();
// Build all rows // Build all rows
$rowParts = array(); $rowParts = array();
foreach ($query->getRows() as $row) { foreach ($query->getRows() as $row) {
// Build all columns in this row // Build all columns in this row
/** @var SQLAssignmentRow $row */
$assignments = $row->getAssignments(); $assignments = $row->getAssignments();
// Join SET components together, considering parameters // Join SET components together, considering parameters
$parts = array(); $parts = array();
@ -50,7 +59,7 @@ class SQLite3QueryBuilder extends DBQueryBuilder
} }
$columnSQL = implode(', ', $columns); $columnSQL = implode(', ', $columns);
$sql = "INSERT INTO {$into}{$nl}($columnSQL){$nl}SELECT " . implode("{$nl}UNION ALL SELECT ", $rowParts); $sql = "INSERT INTO {$into}{$nl}($columnSQL){$nl}SELECT " . implode("{$nl}UNION ALL SELECT ", $rowParts);
return $sql; return $sql;
} }
@ -89,7 +98,7 @@ class SQLite3QueryBuilder extends DBQueryBuilder
} else { } else {
$clause .= "LIMIT -1 "; $clause .= "LIMIT -1 ";
} }
if (isset($limit['start']) && is_numeric($limit['start']) && $limit['start'] !== 0) { if (isset($limit['start']) && is_numeric($limit['start']) && $limit['start'] !== 0) {
$clause .= "OFFSET {$limit['start']}"; $clause .= "OFFSET {$limit['start']}";
} }

View File

@ -1,8 +1,16 @@
<?php <?php
namespace SilverStripe\SQLite;
use SilverStripe\ORM\Connect\DBSchemaManager;
use Exception;
use SapphireTest;
use Debug;
use Director;
/** /**
* SQLite schema manager class * SQLite schema manager class
* *
* @package SQLite3 * @package SQLite3
*/ */
class SQLite3SchemaManager extends DBSchemaManager class SQLite3SchemaManager extends DBSchemaManager
@ -10,7 +18,7 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Instance of the database controller this schema belongs to * Instance of the database controller this schema belongs to
* *
* @var SQLite3Database * @var SQLite3Database
*/ */
protected $database = null; protected $database = null;
@ -28,7 +36,7 @@ class SQLite3SchemaManager extends DBSchemaManager
* @var boolean * @var boolean
*/ */
public static $vacuum = true; public static $vacuum = true;
public function createDatabase($name) public function createDatabase($name)
{ {
// Ensure that any existing database is cleared before connection // Ensure that any existing database is cleared before connection
@ -41,7 +49,7 @@ class SQLite3SchemaManager extends DBSchemaManager
if ($this->database->getLivesInMemory()) { if ($this->database->getLivesInMemory()) {
return; return;
} }
// If using file based database ensure any existing file is removed // If using file based database ensure any existing file is removed
$parameters = $this->database->getParameters(); $parameters = $this->database->getParameters();
$fullpath = $parameters['path'] . '/' . $name; $fullpath = $parameters['path'] . '/' . $name;
@ -49,35 +57,35 @@ class SQLite3SchemaManager extends DBSchemaManager
unlink($fullpath); unlink($fullpath);
} }
} }
public function databaseList() public function databaseList()
{ {
$parameters = $this->database->getParameters(); $parameters = $this->database->getParameters();
// If in-memory use the current database name only // If in-memory use the current database name only
if ($this->database->getLivesInMemory()) { if ($this->database->getLivesInMemory()) {
return array($parameters['database']); return array($parameters['database']);
} }
// If using file based database enumerate files in the database directory // If using file based database enumerate files in the database directory
$directory = $parameters['path']; $directory = $parameters['path'];
$files = scandir($directory); $files = scandir($directory);
// Filter each file in this directory // Filter each file in this directory
$databases = array(); $databases = array();
if ($files !== false) { if ($files !== false) {
foreach ($files as $file) { foreach ($files as $file) {
// Filter non-files // Filter non-files
if (!is_file("$directory/$file")) { if (!is_file("$directory/$file")) {
continue; continue;
} }
// Filter those with correct extension // Filter those with correct extension
if (!SQLite3Database::is_valid_database_name($file)) { if (!SQLite3Database::is_valid_database_name($file)) {
continue; continue;
} }
$databases[] = $file; $databases[] = $file;
} }
} }
@ -89,7 +97,7 @@ class SQLite3SchemaManager extends DBSchemaManager
$databases = $this->databaseList(); $databases = $this->databaseList();
return in_array($name, $databases); return in_array($name, $databases);
} }
/** /**
* Empties any cached enum values * Empties any cached enum values
*/ */
@ -97,14 +105,14 @@ class SQLite3SchemaManager extends DBSchemaManager
{ {
$this->enum_map = array(); $this->enum_map = array();
} }
public function schemaUpdate($callback) public function schemaUpdate($callback)
{ {
// Set locking mode // Set locking mode
$this->database->setPragma('locking_mode', 'EXCLUSIVE'); $this->database->setPragma('locking_mode', 'EXCLUSIVE');
$this->checkAndRepairTable(); $this->checkAndRepairTable();
$this->flushCache(); $this->flushCache();
// Initiate schema update // Initiate schema update
$error = null; $error = null;
try { try {
@ -112,10 +120,10 @@ class SQLite3SchemaManager extends DBSchemaManager
} catch (Exception $ex) { } catch (Exception $ex) {
$error = $ex; $error = $ex;
} }
// Revert locking mode // Revert locking mode
$this->database->setPragma('locking_mode', SQLite3Database::$default_pragma['locking_mode']); $this->database->setPragma('locking_mode', SQLite3Database::$default_pragma['locking_mode']);
if ($error) { if ($error) {
throw $error; throw $error;
} }
@ -123,13 +131,13 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Empty a specific table * Empty a specific table
* *
* @param string $table * @param string $table
*/ */
public function clearTable($table) public function clearTable($table)
{ {
if ($table != 'SQLiteEnums') { if ($table != 'SQLiteEnums') {
$this->dbConn->query("DELETE FROM \"$table\""); $this->query("DELETE FROM \"$table\"");
} }
} }
@ -189,7 +197,7 @@ class SQLite3SchemaManager extends DBSchemaManager
} }
} }
} }
public function renameTable($oldTableName, $newTableName) public function renameTable($oldTableName, $newTableName)
{ {
$this->query("ALTER TABLE \"$oldTableName\" RENAME TO \"$newTableName\""); $this->query("ALTER TABLE \"$oldTableName\" RENAME TO \"$newTableName\"");
@ -201,7 +209,7 @@ class SQLite3SchemaManager extends DBSchemaManager
if (!SapphireTest::using_temp_db() && !self::$checked_and_repaired) { if (!SapphireTest::using_temp_db() && !self::$checked_and_repaired) {
$this->alterationMessage("Checking database integrity", "repaired"); $this->alterationMessage("Checking database integrity", "repaired");
// Check for any tables with failed integrity // Check for any tables with failed integrity
if ($messages = $this->query('PRAGMA integrity_check')) { if ($messages = $this->query('PRAGMA integrity_check')) {
foreach ($messages as $message) { foreach ($messages as $message) {
@ -211,7 +219,7 @@ class SQLite3SchemaManager extends DBSchemaManager
} }
} }
} }
// If enabled vacuum (clean and rebuild) the database // If enabled vacuum (clean and rebuild) the database
if (self::$vacuum) { if (self::$vacuum) {
$this->query('VACUUM', E_USER_NOTICE); $this->query('VACUUM', E_USER_NOTICE);
@ -224,7 +232,7 @@ class SQLite3SchemaManager extends DBSchemaManager
} }
self::$checked_and_repaired = true; self::$checked_and_repaired = true;
} }
return $ok; return $ok;
} }
@ -270,7 +278,7 @@ class SQLite3SchemaManager extends DBSchemaManager
// Remember original indexes // Remember original indexes
$indexList = $this->indexList($tableName); $indexList = $this->indexList($tableName);
// Then alter the table column // Then alter the table column
foreach ($queries as $query) { foreach ($queries as $query) {
$this->query($query.';'); $this->query($query.';');
@ -290,7 +298,7 @@ class SQLite3SchemaManager extends DBSchemaManager
if (!array_key_exists($oldName, $oldFieldList)) { if (!array_key_exists($oldName, $oldFieldList)) {
return; return;
} }
// Determine column mappings // Determine column mappings
$oldCols = array(); $oldCols = array();
$newColsSpec = array(); $newColsSpec = array();
@ -328,10 +336,10 @@ class SQLite3SchemaManager extends DBSchemaManager
public function fieldList($table) public function fieldList($table)
{ {
$sqlCreate = $this->preparedQuery( $sqlCreate = $this->preparedQuery(
'SELECT sql FROM sqlite_master WHERE type = ? AND name = ?', 'SELECT "sql" FROM "sqlite_master" WHERE "type" = ? AND "name" = ?',
array('table', $table) array('table', $table)
)->record(); )->record();
$fieldList = array(); $fieldList = array();
if ($sqlCreate && $sqlCreate['sql']) { if ($sqlCreate && $sqlCreate['sql']) {
preg_match('/^[\s]*CREATE[\s]+TABLE[\s]+[\'"]?[a-zA-Z0-9_\\\]+[\'"]?[\s]*\((.+)\)[\s]*$/ims', preg_match('/^[\s]*CREATE[\s]+TABLE[\s]+[\'"]?[a-zA-Z0-9_\\\]+[\'"]?[\s]*\((.+)\)[\s]*$/ims',
@ -352,7 +360,7 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Create an index on a table. * Create an index on a table.
* *
* @param string $tableName The name of the table. * @param string $tableName The name of the table.
* @param string $indexName The name of the index. * @param string $indexName The name of the index.
* @param array $indexSpec The specification of the index, see Database::requireIndex() for more details. * @param array $indexSpec The specification of the index, see Database::requireIndex() for more details.
@ -371,17 +379,17 @@ class SQLite3SchemaManager extends DBSchemaManager
// Drop existing index // Drop existing index
$sqliteName = $this->buildSQLiteIndexName($tableName, $indexName); $sqliteName = $this->buildSQLiteIndexName($tableName, $indexName);
$this->query("DROP INDEX IF EXISTS \"$sqliteName\""); $this->query("DROP INDEX IF EXISTS \"$sqliteName\"");
// Create the index // Create the index
$this->createIndex($tableName, $indexName, $indexSpec); $this->createIndex($tableName, $indexName, $indexSpec);
} }
/** /**
* Builds the internal SQLLite index name given the silverstripe table and index name. * Builds the internal SQLLite index name given the silverstripe table and index name.
* *
* The name is built using the table and index name in order to prevent name collisions * The name is built using the table and index name in order to prevent name collisions
* between indexes of the same name across multiple tables * between indexes of the same name across multiple tables
* *
* @param string $tableName * @param string $tableName
* @param string $indexName * @param string $indexName
* @return string The SQLite3 name of the index * @return string The SQLite3 name of the index
@ -390,19 +398,19 @@ class SQLite3SchemaManager extends DBSchemaManager
{ {
return "{$tableName}_{$indexName}"; return "{$tableName}_{$indexName}";
} }
protected function parseIndexSpec($name, $spec) protected function parseIndexSpec($name, $spec)
{ {
$spec = parent::parseIndexSpec($name, $spec); $spec = parent::parseIndexSpec($name, $spec);
// Only allow index / unique index types // Only allow index / unique index types
if (!in_array($spec['type'], array('index', 'unique'))) { if (!in_array($spec['type'], array('index', 'unique'))) {
$spec['type'] = 'index'; $spec['type'] = 'index';
} }
return $spec; return $spec;
} }
public function indexKey($table, $index, $spec) public function indexKey($table, $index, $spec)
{ {
return $this->buildSQLiteIndexName($table, $index); return $this->buildSQLiteIndexName($table, $index);
@ -411,20 +419,20 @@ class SQLite3SchemaManager extends DBSchemaManager
public function indexList($table) public function indexList($table)
{ {
$indexList = array(); $indexList = array();
// Enumerate each index and related fields // Enumerate each index and related fields
foreach ($this->query("PRAGMA index_list(\"$table\")") as $index) { foreach ($this->query("PRAGMA index_list(\"$table\")") as $index) {
// The SQLite internal index name, not the actual Silverstripe name // The SQLite internal index name, not the actual Silverstripe name
$indexName = $index["name"]; $indexName = $index["name"];
$indexType = $index['unique'] ? 'unique' : 'index'; $indexType = $index['unique'] ? 'unique' : 'index';
// Determine a clean list of column names within this index // Determine a clean list of column names within this index
$list = array(); $list = array();
foreach ($this->query("PRAGMA index_info(\"$indexName\")") as $details) { foreach ($this->query("PRAGMA index_info(\"$indexName\")") as $details) {
$list[] = preg_replace('/^"?(.*)"?$/', '$1', $details['name']); $list[] = preg_replace('/^"?(.*)"?$/', '$1', $details['name']);
} }
// Safely encode this spec // Safely encode this spec
$indexList[$indexName] = $this->parseIndexSpec($indexName, array( $indexList[$indexName] = $this->parseIndexSpec($indexName, array(
'name' => $indexName, 'name' => $indexName,
@ -446,11 +454,11 @@ class SQLite3SchemaManager extends DBSchemaManager
} }
return $tables; return $tables;
} }
/** /**
* Return a boolean type-formatted string * Return a boolean type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function boolean($values) public function boolean($values)
@ -461,8 +469,8 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Return a date type-formatted string * Return a date type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function date($values) public function date($values)
@ -472,11 +480,11 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Return a decimal type-formatted string * Return a decimal type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function decimal($values, $asDbValue = false) public function decimal($values)
{ {
$default = isset($values['default']) && is_numeric($values['default']) ? $values['default'] : 0; $default = isset($values['default']) && is_numeric($values['default']) ? $values['default'] : 0;
return "NUMERIC NOT NULL DEFAULT $default"; return "NUMERIC NOT NULL DEFAULT $default";
@ -488,25 +496,25 @@ class SQLite3SchemaManager extends DBSchemaManager
* @var array * @var array
*/ */
protected $enum_map = array(); protected $enum_map = array();
/** /**
* Return a enum type-formatted string * Return a enum type-formatted string
* *
* enums are not supported. as a workaround to store allowed values we creates an additional table * enums are not supported. as a workaround to store allowed values we creates an additional table
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function enum($values) public function enum($values)
{ {
$tablefield = $values['table'] . '.' . $values['name']; $tablefield = $values['table'] . '.' . $values['name'];
$enumValues = implode(',', $values['enums']); $enumValues = implode(',', $values['enums']);
// Ensure the cache table exists // Ensure the cache table exists
if (empty($this->enum_map)) { if (empty($this->enum_map)) {
$this->query("CREATE TABLE IF NOT EXISTS \"SQLiteEnums\" (\"TableColumn\" TEXT PRIMARY KEY, \"EnumList\" TEXT)"); $this->query("CREATE TABLE IF NOT EXISTS \"SQLiteEnums\" (\"TableColumn\" TEXT PRIMARY KEY, \"EnumList\" TEXT)");
} }
// Ensure the table row exists // Ensure the table row exists
if (empty($this->enum_map[$tablefield]) || $this->enum_map[$tablefield] != $enumValues) { if (empty($this->enum_map[$tablefield]) || $this->enum_map[$tablefield] != $enumValues) {
$this->preparedQuery( $this->preparedQuery(
@ -515,7 +523,7 @@ class SQLite3SchemaManager extends DBSchemaManager
); );
$this->enum_map[$tablefield] = $enumValues; $this->enum_map[$tablefield] = $enumValues;
} }
// Set default // Set default
if (!empty($values['default'])) { if (!empty($values['default'])) {
$default = str_replace(array('"', "'", "\\", "\0"), "", $values['default']); $default = str_replace(array('"', "'", "\\", "\0"), "", $values['default']);
@ -524,14 +532,14 @@ class SQLite3SchemaManager extends DBSchemaManager
return 'TEXT'; return 'TEXT';
} }
} }
/** /**
* Return a set type-formatted string * Return a set type-formatted string
* This type doesn't exist in SQLite either * This type doesn't exist in SQLite either
* *
* @see SQLite3SchemaManager::enum() * @see SQLite3SchemaManager::enum()
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function set($values) public function set($values)
@ -541,33 +549,33 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Return a float type-formatted string * Return a float type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function float($values, $asDbValue = false) public function float($values)
{ {
return "REAL"; return "REAL";
} }
/** /**
* Return a Double type-formatted string * Return a Double type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function double($values, $asDbValue = false) public function double($values)
{ {
return "REAL"; return "REAL";
} }
/** /**
* Return a int type-formatted string * Return a int type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function int($values, $asDbValue = false) public function int($values)
{ {
return "INTEGER({$values['precision']}) " . strtoupper($values['null']) . " DEFAULT " . (int)$values['default']; return "INTEGER({$values['precision']}) " . strtoupper($values['null']) . " DEFAULT " . (int)$values['default'];
} }
@ -575,41 +583,41 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Return a bigint type-formatted string * Return a bigint type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function bigint($values, $asDbValue = false) public function bigint($values)
{ {
return $this->int($values, $asDbValue); return $this->int($values);
} }
/** /**
* Return a datetime type-formatted string * Return a datetime type-formatted string
* For SQLite3, we simply return the word 'TEXT', no other parameters are necessary * For SQLite3, we simply return the word 'TEXT', no other parameters are necessary
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function ss_datetime($values, $asDbValue = false) public function datetime($values)
{ {
return "DATETIME"; return "DATETIME";
} }
/** /**
* Return a text type-formatted string * Return a text type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function text($values, $asDbValue = false) public function text($values)
{ {
return 'TEXT'; return 'TEXT';
} }
/** /**
* Return a time type-formatted string * Return a time type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function time($values) public function time($values)
@ -619,11 +627,11 @@ class SQLite3SchemaManager extends DBSchemaManager
/** /**
* Return a varchar type-formatted string * Return a varchar type-formatted string
* *
* @params array $values Contains a tokenised list of info about this data type * @param array $values Contains a tokenised list of info about this data type
* @return string * @return string
*/ */
public function varchar($values, $asDbValue = false) public function varchar($values)
{ {
return "VARCHAR({$values['precision']}) COLLATE NOCASE"; return "VARCHAR({$values['precision']}) COLLATE NOCASE";
} }
@ -645,25 +653,27 @@ class SQLite3SchemaManager extends DBSchemaManager
public function hasTable($tableName) public function hasTable($tableName)
{ {
return (bool)$this->preparedQuery( return (bool)$this->preparedQuery(
'SELECT name FROM sqlite_master WHERE type = ? AND name = ?', 'SELECT "name" FROM "sqlite_master" WHERE "type" = ? AND "name" = ?',
array('table', $tableName) array('table', $tableName)
)->first(); )->first();
} }
/** /**
* Return enum values for the given field * Return enum values for the given field
* *
* @param string $tableName
* @param string $fieldName
* @return array * @return array
*/ */
public function enumValuesForField($tableName, $fieldName) public function enumValuesForField($tableName, $fieldName)
{ {
$tablefield = "$tableName.$fieldName"; $tablefield = "$tableName.$fieldName";
// Check already cached values for this field // Check already cached values for this field
if (!empty($this->enum_map[$tablefield])) { if (!empty($this->enum_map[$tablefield])) {
return explode(',', $this->enum_map[$tablefield]); return explode(',', $this->enum_map[$tablefield]);
} }
// Retrieve and cache these details from the database // Retrieve and cache these details from the database
$classnameinfo = $this->preparedQuery( $classnameinfo = $this->preparedQuery(
"SELECT EnumList FROM SQLiteEnums WHERE TableColumn = ?", "SELECT EnumList FROM SQLiteEnums WHERE TableColumn = ?",
@ -674,17 +684,17 @@ class SQLite3SchemaManager extends DBSchemaManager
$this->enum_map[$tablefield] = $valueList; $this->enum_map[$tablefield] = $valueList;
return explode(',', $valueList); return explode(',', $valueList);
} }
// Fallback to empty list // Fallback to empty list
return array(); return array();
} }
public function dbDataType($type) public function dbDataType($type)
{ {
$values = array( $values = array(
'unsigned integer' => 'INT' 'unsigned integer' => 'INT'
); );
if (isset($values[$type])) { if (isset($values[$type])) {
return $values[$type]; return $values[$type];
} else { } else {

View File

@ -1,19 +1,27 @@
<?php <?php
namespace SilverStripe\SQLite;
use DatabaseConfigurationHelper;
use SQLite3;
use PDO;
use Exception;
use DatabaseAdapterRegistry;
/** /**
* This is a helper class for the SS installer. * This is a helper class for the SS installer.
* *
* It does all the specific checking for SQLiteDatabase * It does all the specific checking for SQLiteDatabase
* to ensure that the configuration is setup correctly. * to ensure that the configuration is setup correctly.
* *
* @package SQLite3 * @package SQLite3
*/ */
class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
{ {
/** /**
* Create a connection of the appropriate type * Create a connection of the appropriate type
* *
* @param array $databaseConfig * @param array $databaseConfig
* @param string $error Error message passed by value * @param string $error Error message passed by value
* @return mixed|null Either the connection object, or null if error * @return mixed|null Either the connection object, or null if error
@ -28,7 +36,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
} }
$file = $databaseConfig['path'] . '/' . $databaseConfig['database']; $file = $databaseConfig['path'] . '/' . $databaseConfig['database'];
$conn = null; $conn = null;
switch ($databaseConfig['type']) { switch ($databaseConfig['type']) {
case 'SQLite3Database': case 'SQLite3Database':
if (empty($databaseConfig['key'])) { if (empty($databaseConfig['key'])) {
@ -45,7 +53,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
$error = 'Invalid connection type'; $error = 'Invalid connection type';
return null; return null;
} }
if ($conn) { if ($conn) {
return $conn; return $conn;
} else { } else {
@ -57,7 +65,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
return null; return null;
} }
} }
public function requireDatabaseFunctions($databaseConfig) public function requireDatabaseFunctions($databaseConfig)
{ {
$data = DatabaseAdapterRegistry::get_adapter($databaseConfig['type']); $data = DatabaseAdapterRegistry::get_adapter($databaseConfig['type']);
@ -88,9 +96,9 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
/** /**
* Ensure a database connection is possible using credentials provided. * Ensure a database connection is possible using credentials provided.
* *
* @todo Validate path * @todo Validate path
* *
* @param array $databaseConfig Associative array of db configuration, e.g. "type", "path" etc * @param array $databaseConfig Associative array of db configuration, e.g. "type", "path" etc
* @return array Result - e.g. array('success' => true, 'error' => 'details of error') * @return array Result - e.g. array('success' => true, 'error' => 'details of error')
*/ */
@ -109,7 +117,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
'error' => "Missing database filename" 'error' => "Missing database filename"
); );
} }
// Create and secure db directory // Create and secure db directory
$path = $databaseConfig['path']; $path = $databaseConfig['path'];
$dirCreated = self::create_db_dir($path); $dirCreated = self::create_db_dir($path);
@ -129,7 +137,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
$conn = $this->createConnection($databaseConfig, $error); $conn = $this->createConnection($databaseConfig, $error);
$success = !empty($conn); $success = !empty($conn);
return array( return array(
'success' => $success, 'success' => $success,
'connection' => $conn, 'connection' => $conn,
@ -140,7 +148,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
public function getDatabaseVersion($databaseConfig) public function getDatabaseVersion($databaseConfig)
{ {
$version = 0; $version = 0;
switch ($databaseConfig['type']) { switch ($databaseConfig['type']) {
case 'SQLite3Database': case 'SQLite3Database':
$info = SQLite3::version(); $info = SQLite3::version();
@ -186,12 +194,12 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
'alreadyExists' => $alreadyExists, 'alreadyExists' => $alreadyExists,
); );
} }
/** /**
* Creates the provided directory and prepares it for * Creates the provided directory and prepares it for
* storing SQLlite. Use {@link secure_db_dir()} to * storing SQLlite. Use {@link secure_db_dir()} to
* secure it against unauthorized access. * secure it against unauthorized access.
* *
* @param String $path Absolute path, usually with a hidden folder. * @param String $path Absolute path, usually with a hidden folder.
* @return boolean * @return boolean
*/ */
@ -199,14 +207,14 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
{ {
return file_exists($path) || mkdir($path); return file_exists($path) || mkdir($path);
} }
/** /**
* Secure the provided directory via web-access * Secure the provided directory via web-access
* by placing a .htaccess file in it. * by placing a .htaccess file in it.
* This is just required if the database directory * This is just required if the database directory
* is placed within a publically accessible webroot (the * is placed within a publically accessible webroot (the
* default path is in a hidden folder within assets/). * default path is in a hidden folder within assets/).
* *
* @param String $path Absolute path, containing a SQLite datatbase * @param String $path Absolute path, containing a SQLite datatbase
* @return boolean * @return boolean
*/ */
@ -214,7 +222,7 @@ class SQLiteDatabaseConfigurationHelper implements DatabaseConfigurationHelper
{ {
return (is_writeable($path)) ? file_put_contents($path . '/.htaccess', 'deny from all') : false; return (is_writeable($path)) ? file_put_contents($path . '/.htaccess', 'deny from all') : false;
} }
public function requireDatabaseAlterPermissions($databaseConfig) public function requireDatabaseAlterPermissions($databaseConfig)
{ {
// no concept of table-specific permissions; If you can connect you can alter schema // no concept of table-specific permissions; If you can connect you can alter schema

View File

@ -24,6 +24,11 @@
"dev-master": "2.0.x-dev" "dev-master": "2.0.x-dev"
} }
}, },
"autoload": {
"psr-4": {
"SilverStripe\\SQLite\\": "code/"
}
},
"minimum-stability": "dev", "minimum-stability": "dev",
"prefer-stable": true "prefer-stable": true
} }