2013-06-21 00:32:08 +02:00
|
|
|
<?php
|
|
|
|
|
2016-06-15 06:03:16 +02:00
|
|
|
namespace SilverStripe\ORM\Connect;
|
|
|
|
|
2019-03-26 02:12:59 +01:00
|
|
|
use Exception;
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Control\Director;
|
|
|
|
use SilverStripe\Core\Config\Config;
|
2017-05-17 07:40:13 +02:00
|
|
|
use SilverStripe\Core\Injector\Injector;
|
2019-03-26 02:12:59 +01:00
|
|
|
use SilverStripe\ORM\DB;
|
2016-06-03 10:51:02 +02:00
|
|
|
use SilverStripe\ORM\FieldType\DBField;
|
2019-03-26 02:12:59 +01:00
|
|
|
use SilverStripe\ORM\FieldType\DBPrimaryKey;
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2013-06-21 00:32:08 +02:00
|
|
|
/**
|
|
|
|
* Represents and handles all schema management for a database
|
|
|
|
*/
|
2016-11-29 00:31:16 +01:00
|
|
|
abstract class DBSchemaManager
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @config
|
|
|
|
* Check tables when running /dev/build, and repair them if necessary.
|
|
|
|
* In case of large databases or more fine-grained control on how to handle
|
|
|
|
* data corruption in tables, you can disable this behaviour and handle it
|
|
|
|
* outside of this class, e.g. through a nightly system task with extended logging capabilities.
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
private static $check_and_repair_on_build = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if tables should be renamed in a case-sensitive fashion.
|
|
|
|
* Note: This should still work even on case-insensitive databases.
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
|
|
|
private static $fix_table_case_on_build = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instance of the database controller this schema belongs to
|
|
|
|
*
|
|
|
|
* @var Database
|
|
|
|
*/
|
|
|
|
protected $database = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If this is false, then information about database operations
|
|
|
|
* will be displayed, eg creation of tables.
|
|
|
|
*
|
|
|
|
* @var boolean
|
|
|
|
*/
|
|
|
|
protected $supressOutput = false;
|
|
|
|
|
2017-12-11 18:32:13 +01:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected static $table_name_warnings = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string
|
2018-09-28 10:46:36 +02:00
|
|
|
* @deprecated 4.0.0:5.0.0
|
2017-12-11 18:32:13 +01:00
|
|
|
*/
|
|
|
|
public static function showTableNameWarning($table, $class)
|
|
|
|
{
|
|
|
|
static::$table_name_warnings[$table] = $class;
|
|
|
|
}
|
|
|
|
|
2016-11-29 00:31:16 +01:00
|
|
|
/**
|
|
|
|
* Injector injection point for database controller
|
|
|
|
*
|
|
|
|
* @param Database $database
|
|
|
|
*/
|
|
|
|
public function setDatabase(Database $database)
|
|
|
|
{
|
|
|
|
$this->database = $database;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The table list, generated by the tableList() function.
|
|
|
|
* Used by the requireTable() function.
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $tableList;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Keeps track whether we are currently updating the schema.
|
|
|
|
*
|
|
|
|
* @var boolean
|
|
|
|
*/
|
|
|
|
protected $schemaIsUpdating = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Large array structure that represents a schema update transaction
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
protected $schemaUpdateTransaction;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable supression of database messages.
|
2018-03-21 22:26:25 +01:00
|
|
|
*
|
|
|
|
* @param bool $quiet
|
2016-11-29 00:31:16 +01:00
|
|
|
*/
|
2018-03-21 22:26:25 +01:00
|
|
|
public function quiet($quiet = true)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2018-03-21 22:26:25 +01:00
|
|
|
$this->supressOutput = $quiet;
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute the given SQL query.
|
|
|
|
* This abstract function must be defined by subclasses as part of the actual implementation.
|
|
|
|
* It should return a subclass of SS_Query as the result.
|
|
|
|
*
|
|
|
|
* @param string $sql The SQL query to execute
|
|
|
|
* @param int $errorLevel The level of error reporting to enable for the query
|
|
|
|
* @return Query
|
|
|
|
*/
|
|
|
|
public function query($sql, $errorLevel = E_USER_ERROR)
|
|
|
|
{
|
|
|
|
return $this->database->query($sql, $errorLevel);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Execute the given SQL parameterised query with the specified arguments
|
|
|
|
*
|
|
|
|
* @param string $sql The SQL query to execute. The ? character will denote parameters.
|
|
|
|
* @param array $parameters An ordered list of arguments.
|
|
|
|
* @param int $errorLevel The level of error reporting to enable for the query
|
|
|
|
* @return Query
|
|
|
|
*/
|
|
|
|
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR)
|
|
|
|
{
|
|
|
|
return $this->database->preparedQuery($sql, $parameters, $errorLevel);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiates a schema update within a single callback
|
|
|
|
*
|
|
|
|
* @param callable $callback
|
|
|
|
*/
|
|
|
|
public function schemaUpdate($callback)
|
|
|
|
{
|
|
|
|
// Begin schema update
|
|
|
|
$this->schemaIsUpdating = true;
|
|
|
|
|
|
|
|
// Update table list
|
|
|
|
$this->tableList = array();
|
|
|
|
$tables = $this->tableList();
|
|
|
|
foreach ($tables as $table) {
|
|
|
|
$this->tableList[strtolower($table)] = $table;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear update list for client code to mess around with
|
|
|
|
$this->schemaUpdateTransaction = array();
|
|
|
|
|
|
|
|
/** @var Exception $error */
|
|
|
|
$error = null;
|
|
|
|
try {
|
|
|
|
// Yield control to client code
|
|
|
|
$callback();
|
|
|
|
|
|
|
|
// If the client code has cancelled the update then abort
|
|
|
|
if (!$this->isSchemaUpdating()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// End schema update
|
|
|
|
foreach ($this->schemaUpdateTransaction as $tableName => $changes) {
|
|
|
|
$advancedOptions = isset($changes['advancedOptions']) ? $changes['advancedOptions'] : null;
|
|
|
|
switch ($changes['command']) {
|
|
|
|
case 'create':
|
|
|
|
$this->createTable(
|
|
|
|
$tableName,
|
|
|
|
$changes['newFields'],
|
|
|
|
$changes['newIndexes'],
|
|
|
|
$changes['options'],
|
|
|
|
$advancedOptions
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'alter':
|
|
|
|
$this->alterTable(
|
|
|
|
$tableName,
|
|
|
|
$changes['newFields'],
|
|
|
|
$changes['newIndexes'],
|
|
|
|
$changes['alteredFields'],
|
|
|
|
$changes['alteredIndexes'],
|
|
|
|
$changes['alteredOptions'],
|
|
|
|
$advancedOptions
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
$this->schemaUpdateTransaction = null;
|
|
|
|
$this->schemaIsUpdating = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cancels the schema updates requested during (but not after) schemaUpdate() call.
|
|
|
|
*/
|
|
|
|
public function cancelSchemaUpdate()
|
|
|
|
{
|
|
|
|
$this->schemaUpdateTransaction = null;
|
|
|
|
$this->schemaIsUpdating = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if we are during a schema update.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
function isSchemaUpdating()
|
|
|
|
{
|
|
|
|
return $this->schemaIsUpdating;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if schema modifications were requested during (but not after) schemaUpdate() call.
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function doesSchemaNeedUpdating()
|
|
|
|
{
|
|
|
|
return (bool) $this->schemaUpdateTransaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transactional schema altering functions - they don't do anything except for update schemaUpdateTransaction
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instruct the schema manager to record a table creation to later execute
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table
|
|
|
|
* @param array $options Create table options (ENGINE, etc.)
|
|
|
|
* @param array $advanced_options Advanced table creation options
|
|
|
|
*/
|
|
|
|
public function transCreateTable($table, $options = null, $advanced_options = null)
|
|
|
|
{
|
|
|
|
$this->schemaUpdateTransaction[$table] = array(
|
|
|
|
'command' => 'create',
|
|
|
|
'newFields' => array(),
|
|
|
|
'newIndexes' => array(),
|
|
|
|
'options' => $options,
|
|
|
|
'advancedOptions' => $advanced_options
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instruct the schema manager to record a table alteration to later execute
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table
|
|
|
|
* @param array $options Create table options (ENGINE, etc.)
|
|
|
|
* @param array $advanced_options Advanced table creation options
|
|
|
|
*/
|
|
|
|
public function transAlterTable($table, $options, $advanced_options)
|
|
|
|
{
|
|
|
|
$this->transInitTable($table);
|
|
|
|
$this->schemaUpdateTransaction[$table]['alteredOptions'] = $options;
|
|
|
|
$this->schemaUpdateTransaction[$table]['advancedOptions'] = $advanced_options;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instruct the schema manager to record a field to be later created
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table to hold this field
|
|
|
|
* @param string $field Name of the field to create
|
|
|
|
* @param string $schema Field specification as a string
|
|
|
|
*/
|
|
|
|
public function transCreateField($table, $field, $schema)
|
|
|
|
{
|
|
|
|
$this->transInitTable($table);
|
|
|
|
$this->schemaUpdateTransaction[$table]['newFields'][$field] = $schema;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instruct the schema manager to record an index to be later created
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table to hold this index
|
|
|
|
* @param string $index Name of the index to create
|
|
|
|
* @param array $schema Already parsed index specification
|
|
|
|
*/
|
|
|
|
public function transCreateIndex($table, $index, $schema)
|
|
|
|
{
|
|
|
|
$this->transInitTable($table);
|
|
|
|
$this->schemaUpdateTransaction[$table]['newIndexes'][$index] = $schema;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instruct the schema manager to record a field to be later updated
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table to hold this field
|
|
|
|
* @param string $field Name of the field to update
|
|
|
|
* @param string $schema Field specification as a string
|
|
|
|
*/
|
|
|
|
public function transAlterField($table, $field, $schema)
|
|
|
|
{
|
|
|
|
$this->transInitTable($table);
|
|
|
|
$this->schemaUpdateTransaction[$table]['alteredFields'][$field] = $schema;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Instruct the schema manager to record an index to be later updated
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table to hold this index
|
|
|
|
* @param string $index Name of the index to update
|
|
|
|
* @param array $schema Already parsed index specification
|
|
|
|
*/
|
|
|
|
public function transAlterIndex($table, $index, $schema)
|
|
|
|
{
|
|
|
|
$this->transInitTable($table);
|
|
|
|
$this->schemaUpdateTransaction[$table]['alteredIndexes'][$index] = $schema;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the other transXXX methods - mark the given table as being altered
|
|
|
|
* if it doesn't already exist
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table to initialise
|
|
|
|
*/
|
|
|
|
protected function transInitTable($table)
|
|
|
|
{
|
|
|
|
if (!isset($this->schemaUpdateTransaction[$table])) {
|
|
|
|
$this->schemaUpdateTransaction[$table] = array(
|
|
|
|
'command' => 'alter',
|
|
|
|
'newFields' => array(),
|
|
|
|
'newIndexes' => array(),
|
|
|
|
'alteredFields' => array(),
|
|
|
|
'alteredIndexes' => array(),
|
|
|
|
'alteredOptions' => ''
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the following table in the database, modifying whatever already exists
|
|
|
|
* as necessary.
|
|
|
|
*
|
|
|
|
* @todo Change detection for CREATE TABLE $options other than "Engine"
|
|
|
|
*
|
|
|
|
* @param string $table The name of the table
|
|
|
|
* @param array $fieldSchema A list of the fields to create, in the same form as DataObject::$db
|
|
|
|
* @param array $indexSchema A list of indexes to create. See {@link requireIndex()}
|
|
|
|
* The values of the array can be one of:
|
|
|
|
* - true: Create a single column index on the field named the same as the index.
|
|
|
|
* - array('fields' => array('A','B','C'), 'type' => 'index/unique/fulltext'): This gives you full
|
|
|
|
* control over the index.
|
|
|
|
* @param boolean $hasAutoIncPK A flag indicating that the primary key on this table is an autoincrement type
|
|
|
|
* @param array $options Create table options (ENGINE, etc.)
|
|
|
|
* @param array|bool $extensions List of extensions
|
|
|
|
*/
|
|
|
|
public function requireTable(
|
|
|
|
$table,
|
|
|
|
$fieldSchema = null,
|
|
|
|
$indexSchema = null,
|
|
|
|
$hasAutoIncPK = true,
|
|
|
|
$options = array(),
|
|
|
|
$extensions = false
|
|
|
|
) {
|
|
|
|
if (!isset($this->tableList[strtolower($table)])) {
|
|
|
|
$this->transCreateTable($table, $options, $extensions);
|
|
|
|
$this->alterationMessage("Table $table: created", "created");
|
|
|
|
} else {
|
|
|
|
if (Config::inst()->get(static::class, 'fix_table_case_on_build')) {
|
|
|
|
$this->fixTableCase($table);
|
|
|
|
}
|
|
|
|
if (Config::inst()->get(static::class, 'check_and_repair_on_build')) {
|
|
|
|
$this->checkAndRepairTable($table);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if options changed
|
|
|
|
$tableOptionsChanged = false;
|
|
|
|
// Check for DB constant on the schema class
|
2017-05-23 01:36:15 +02:00
|
|
|
$dbIDName = sprintf('%s::ID', static::class);
|
2016-11-29 00:31:16 +01:00
|
|
|
$dbID = defined($dbIDName) ? constant($dbIDName) : null;
|
|
|
|
if ($dbID && isset($options[$dbID])) {
|
|
|
|
if (preg_match('/ENGINE=([^\s]*)/', $options[$dbID], $alteredEngineMatches)) {
|
|
|
|
$alteredEngine = $alteredEngineMatches[1];
|
|
|
|
$tableStatus = $this->query(sprintf('SHOW TABLE STATUS LIKE \'%s\'', $table))->first();
|
|
|
|
$tableOptionsChanged = ($tableStatus['Engine'] != $alteredEngine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($tableOptionsChanged || ($extensions && $this->database->supportsExtensions($extensions))) {
|
|
|
|
$this->transAlterTable($table, $options, $extensions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//DB ABSTRACTION: we need to convert this to a db-specific version:
|
|
|
|
if (!isset($fieldSchema['ID'])) {
|
|
|
|
$this->requireField($table, 'ID', $this->IdColumn(false, $hasAutoIncPK));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create custom fields
|
|
|
|
if ($fieldSchema) {
|
|
|
|
foreach ($fieldSchema as $fieldName => $fieldSpec) {
|
|
|
|
//Is this an array field?
|
|
|
|
$arrayValue = '';
|
|
|
|
if (strpos($fieldSpec, '[') !== false) {
|
|
|
|
//If so, remove it and store that info separately
|
|
|
|
$pos = strpos($fieldSpec, '[');
|
|
|
|
$arrayValue = substr($fieldSpec, $pos);
|
|
|
|
$fieldSpec = substr($fieldSpec, 0, $pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @var DBField $fieldObj */
|
2017-05-17 07:40:13 +02:00
|
|
|
$fieldObj = Injector::inst()->create($fieldSpec, $fieldName);
|
2016-11-29 00:31:16 +01:00
|
|
|
$fieldObj->setArrayValue($arrayValue);
|
|
|
|
|
|
|
|
$fieldObj->setTable($table);
|
|
|
|
|
|
|
|
if ($fieldObj instanceof DBPrimaryKey) {
|
|
|
|
/** @var DBPrimaryKey $fieldObj */
|
|
|
|
$fieldObj->setAutoIncrement($hasAutoIncPK);
|
|
|
|
}
|
|
|
|
|
|
|
|
$fieldObj->requireField();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create custom indexes
|
|
|
|
if ($indexSchema) {
|
2017-05-18 18:59:57 +02:00
|
|
|
foreach ($indexSchema as $indexName => $indexSpec) {
|
|
|
|
$this->requireIndex($table, $indexName, $indexSpec);
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-11 18:32:13 +01:00
|
|
|
|
|
|
|
// Check and display notice about $table_name
|
|
|
|
static $table_name_info_sent = false;
|
|
|
|
|
|
|
|
if (isset(static::$table_name_warnings[$table])) {
|
|
|
|
if (!$table_name_info_sent) {
|
2017-12-11 21:22:18 +01:00
|
|
|
$this->alterationMessage(
|
|
|
|
<<<'MESSAGE'
|
|
|
|
<strong>Please note:</strong> It is strongly recommended to define a
|
|
|
|
table_name for all namespaced models. Not defining a table_name may cause generated table
|
|
|
|
names to be too long and may not be supported by your current database engine. The generated
|
|
|
|
naming scheme will also change when upgrading to SilverStripe 5.0 and potentially break.
|
|
|
|
MESSAGE
|
|
|
|
,
|
2017-12-11 18:32:13 +01:00
|
|
|
'error'
|
|
|
|
);
|
|
|
|
$table_name_info_sent = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->alterationMessage('table_name not set for class ' . static::$table_name_warnings[$table], 'notice');
|
|
|
|
}
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If the given table exists, move it out of the way by renaming it to _obsolete_(tablename).
|
|
|
|
* @param string $table The table name.
|
|
|
|
*/
|
|
|
|
public function dontRequireTable($table)
|
|
|
|
{
|
2018-06-11 03:32:57 +02:00
|
|
|
if (!isset($this->tableList[strtolower($table)])) {
|
|
|
|
return;
|
|
|
|
}
|
2018-06-18 05:58:12 +02:00
|
|
|
$prefix = "_obsolete_{$table}";
|
2018-06-11 03:32:57 +02:00
|
|
|
$suffix = '';
|
2018-06-18 05:58:12 +02:00
|
|
|
$renameTo = $prefix . $suffix;
|
|
|
|
while (isset($this->tableList[strtolower($renameTo)])) {
|
2018-06-11 03:32:57 +02:00
|
|
|
$suffix = $suffix
|
|
|
|
? ((int)$suffix + 1)
|
|
|
|
: 2;
|
2018-06-18 05:58:12 +02:00
|
|
|
$renameTo = $prefix . $suffix;
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
2018-06-18 05:58:12 +02:00
|
|
|
$renameFrom = $this->tableList[strtolower($table)];
|
|
|
|
$this->renameTable($renameFrom, $renameTo);
|
|
|
|
$this->alterationMessage("Table $table: renamed to $renameTo", "obsolete");
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the given index in the database, modifying whatever already exists as necessary.
|
|
|
|
*
|
|
|
|
* The keys of the array are the names of the index.
|
|
|
|
* The values of the array can be one of:
|
|
|
|
* - true: Create a single column index on the field named the same as the index.
|
|
|
|
* - array('type' => 'index|unique|fulltext', 'value' => 'FieldA, FieldB'): This gives you full
|
|
|
|
* control over the index.
|
|
|
|
*
|
|
|
|
* @param string $table The table name.
|
|
|
|
* @param string $index The index name.
|
|
|
|
* @param string|array|boolean $spec The specification of the index in any
|
|
|
|
* loose format. See requireTable() for more information.
|
|
|
|
*/
|
|
|
|
public function requireIndex($table, $index, $spec)
|
|
|
|
{
|
|
|
|
// Detect if adding to a new table
|
|
|
|
$newTable = !isset($this->tableList[strtolower($table)]);
|
|
|
|
|
|
|
|
// Force spec into standard array format
|
|
|
|
$specString = $this->convertIndexSpec($spec);
|
|
|
|
|
|
|
|
// Check existing index
|
|
|
|
$oldSpecString = null;
|
|
|
|
$indexKey = null;
|
|
|
|
if (!$newTable) {
|
|
|
|
$indexKey = $this->indexKey($table, $index, $spec);
|
|
|
|
$indexList = $this->indexList($table);
|
|
|
|
if (isset($indexList[$indexKey])) {
|
|
|
|
// $oldSpec should be in standard array format
|
|
|
|
$oldSpec = $indexList[$indexKey];
|
|
|
|
$oldSpecString = $this->convertIndexSpec($oldSpec);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initiate either generation or modification of index
|
|
|
|
if ($newTable || !isset($indexList[$indexKey])) {
|
|
|
|
// New index
|
|
|
|
$this->transCreateIndex($table, $index, $spec);
|
|
|
|
$this->alterationMessage("Index $table.$index: created as $specString", "created");
|
|
|
|
} elseif ($oldSpecString != $specString) {
|
|
|
|
// Updated index
|
|
|
|
$this->transAlterIndex($table, $index, $spec);
|
|
|
|
$this->alterationMessage(
|
2017-07-12 04:23:04 +02:00
|
|
|
"Index $table.$index: changed to $specString <i class=\"build-info-before\">(from $oldSpecString)</i>",
|
2016-11-29 00:31:16 +01:00
|
|
|
"changed"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Splits a spec string safely, considering quoted columns, whitespace,
|
|
|
|
* and cleaning brackets
|
|
|
|
*
|
|
|
|
* @param string $spec The input index specification string
|
|
|
|
* @return array List of columns in the spec
|
|
|
|
*/
|
|
|
|
protected function explodeColumnString($spec)
|
|
|
|
{
|
|
|
|
// Remove any leading/trailing brackets and outlying modifiers
|
|
|
|
// E.g. 'unique (Title, "QuotedColumn");' => 'Title, "QuotedColumn"'
|
|
|
|
$containedSpec = preg_replace('/(.*\(\s*)|(\s*\).*)/', '', $spec);
|
|
|
|
|
|
|
|
// Split potentially quoted modifiers
|
|
|
|
// E.g. 'Title, "QuotedColumn"' => array('Title', 'QuotedColumn')
|
|
|
|
return preg_split('/"?\s*,\s*"?/', trim($containedSpec, '(") '));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds a properly quoted column list from an array
|
|
|
|
*
|
|
|
|
* @param array $columns List of columns to implode
|
|
|
|
* @return string A properly quoted list of column names
|
|
|
|
*/
|
|
|
|
protected function implodeColumnList($columns)
|
|
|
|
{
|
|
|
|
if (empty($columns)) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
return '"' . implode('","', $columns) . '"';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given an index specification in the form of a string ensure that each
|
|
|
|
* column name is property quoted, stripping brackets and modifiers.
|
|
|
|
* This index may also be in the form of a "CREATE INDEX..." sql fragment
|
|
|
|
*
|
|
|
|
* @param string $spec The input specification or query. E.g. 'unique (Column1, Column2)'
|
|
|
|
* @return string The properly quoted column list. E.g. '"Column1", "Column2"'
|
|
|
|
*/
|
|
|
|
protected function quoteColumnSpecString($spec)
|
|
|
|
{
|
|
|
|
$bits = $this->explodeColumnString($spec);
|
|
|
|
return $this->implodeColumnList($bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given an index spec determines the index type
|
|
|
|
*
|
|
|
|
* @param array|string $spec
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function determineIndexType($spec)
|
|
|
|
{
|
|
|
|
// check array spec
|
|
|
|
if (is_array($spec) && isset($spec['type'])) {
|
|
|
|
return $spec['type'];
|
|
|
|
} elseif (!is_array($spec) && preg_match('/(?<type>\w+)\s*\(/', $spec, $matchType)) {
|
|
|
|
return strtolower($matchType['type']);
|
|
|
|
} else {
|
|
|
|
return 'index';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This takes the index spec which has been provided by a class (ie static $indexes = blah blah)
|
|
|
|
* and turns it into a proper string.
|
|
|
|
* Some indexes may be arrays, such as fulltext and unique indexes, and this allows database-specific
|
|
|
|
* arrays to be created. See {@link requireTable()} for details on the index format.
|
|
|
|
*
|
|
|
|
* @see http://dev.mysql.com/doc/refman/5.0/en/create-index.html
|
|
|
|
* @see parseIndexSpec() for approximate inverse
|
|
|
|
*
|
|
|
|
* @param string|array $indexSpec
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
protected function convertIndexSpec($indexSpec)
|
|
|
|
{
|
|
|
|
// Return already converted spec
|
2017-08-07 11:40:59 +02:00
|
|
|
if (!is_array($indexSpec)
|
|
|
|
|| !array_key_exists('type', $indexSpec)
|
|
|
|
|| !array_key_exists('columns', $indexSpec)
|
|
|
|
|| !is_array($indexSpec['columns'])
|
|
|
|
|| array_key_exists('value', $indexSpec)
|
|
|
|
) {
|
|
|
|
throw new \InvalidArgumentException(
|
|
|
|
sprintf(
|
|
|
|
'argument to convertIndexSpec must be correct indexSpec, %s given',
|
|
|
|
var_export($indexSpec, true)
|
|
|
|
)
|
|
|
|
);
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Combine elements into standard string format
|
2017-05-18 18:59:57 +02:00
|
|
|
return sprintf('%s (%s)', $indexSpec['type'], $this->implodeColumnList($indexSpec['columns']));
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the given table is exists in the current database
|
|
|
|
*
|
|
|
|
* @param string $tableName Name of table to check
|
|
|
|
* @return boolean Flag indicating existence of table
|
|
|
|
*/
|
|
|
|
abstract public function hasTable($tableName);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if the table exists and already has a the field specified
|
|
|
|
*
|
|
|
|
* @param string $tableName - The table to check
|
|
|
|
* @param string $fieldName - The field to check
|
|
|
|
* @return bool - True if the table exists and the field exists on the table
|
|
|
|
*/
|
|
|
|
public function hasField($tableName, $fieldName)
|
|
|
|
{
|
|
|
|
if (!$this->hasTable($tableName)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$fields = $this->fieldList($tableName);
|
|
|
|
return array_key_exists($fieldName, $fields);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate the given field on the table, modifying whatever already exists as necessary.
|
|
|
|
*
|
|
|
|
* @param string $table The table name.
|
|
|
|
* @param string $field The field name.
|
|
|
|
* @param array|string $spec The field specification. If passed in array syntax, the specific database
|
|
|
|
* driver takes care of the ALTER TABLE syntax. If passed as a string, its assumed to
|
|
|
|
* be prepared as a direct SQL framgment ready for insertion into ALTER TABLE. In this case you'll
|
|
|
|
* need to take care of database abstraction in your DBField subclass.
|
|
|
|
*/
|
|
|
|
public function requireField($table, $field, $spec)
|
|
|
|
{
|
|
|
|
//TODO: this is starting to get extremely fragmented.
|
|
|
|
//There are two different versions of $spec floating around, and their content changes depending
|
|
|
|
//on how they are structured. This needs to be tidied up.
|
|
|
|
$fieldValue = null;
|
|
|
|
$newTable = false;
|
|
|
|
|
|
|
|
// backwards compatibility patch for pre 2.4 requireField() calls
|
|
|
|
$spec_orig = $spec;
|
|
|
|
|
|
|
|
if (!is_string($spec)) {
|
|
|
|
$spec['parts']['name'] = $field;
|
|
|
|
$spec_orig['parts']['name'] = $field;
|
|
|
|
//Convert the $spec array into a database-specific string
|
|
|
|
$spec = $this->{$spec['type']}($spec['parts'], true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collations didn't come in until MySQL 4.1. Anything earlier will throw a syntax error if you try and use
|
|
|
|
// collations.
|
|
|
|
// TODO: move this to the MySQLDatabase file, or drop it altogether?
|
|
|
|
if (!$this->database->supportsCollations()) {
|
|
|
|
$spec = preg_replace('/ *character set [^ ]+( collate [^ ]+)?( |$)/', '\\2', $spec);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isset($this->tableList[strtolower($table)])) {
|
|
|
|
$newTable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_array($spec)) {
|
|
|
|
$specValue = $this->$spec_orig['type']($spec_orig['parts']);
|
|
|
|
} else {
|
|
|
|
$specValue = $spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We need to get db-specific versions of the ID column:
|
|
|
|
if ($spec_orig == $this->IdColumn() || $spec_orig == $this->IdColumn(true)) {
|
|
|
|
$specValue = $this->IdColumn(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$newTable) {
|
|
|
|
$fieldList = $this->fieldList($table);
|
|
|
|
if (isset($fieldList[$field])) {
|
|
|
|
if (is_array($fieldList[$field])) {
|
|
|
|
$fieldValue = $fieldList[$field]['data_type'];
|
|
|
|
} else {
|
|
|
|
$fieldValue = $fieldList[$field];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the version of the field as we would create it. This is used for comparison purposes to see if the
|
|
|
|
// existing field is different to what we now want
|
|
|
|
if (is_array($spec_orig)) {
|
|
|
|
$spec_orig = $this->{$spec_orig['type']}($spec_orig['parts']);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($newTable || $fieldValue == '') {
|
|
|
|
$this->transCreateField($table, $field, $spec_orig);
|
|
|
|
$this->alterationMessage("Field $table.$field: created as $spec_orig", "created");
|
|
|
|
} elseif ($fieldValue != $specValue) {
|
|
|
|
// If enums/sets are being modified, then we need to fix existing data in the table.
|
|
|
|
// Update any records where the enum is set to a legacy value to be set to the default.
|
2019-03-26 02:12:59 +01:00
|
|
|
$enumValuesExpr = "/^(enum|set)\\s*\\(['\"](?<values>[^'\"]+)['\"]\\).*/i";
|
|
|
|
if (preg_match($enumValuesExpr, $specValue, $specMatches)
|
|
|
|
&& preg_match($enumValuesExpr, $spec_orig, $oldMatches)
|
|
|
|
) {
|
|
|
|
$new = preg_split("/'\\s*,\\s*'/", $specMatches['values']);
|
|
|
|
$old = preg_split("/'\\s*,\\s*'/", $oldMatches['values']);
|
|
|
|
|
|
|
|
$holder = array();
|
|
|
|
foreach ($old as $check) {
|
|
|
|
if (!in_array($check, $new)) {
|
|
|
|
$holder[] = $check;
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
2019-03-26 02:12:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count($holder)) {
|
|
|
|
// Get default pre-escaped for SQL. We just use this directly, as we don't have a real way to
|
|
|
|
// de-encode SQL values
|
2016-11-29 00:31:16 +01:00
|
|
|
$default = explode('default ', $spec_orig);
|
2019-03-26 02:12:59 +01:00
|
|
|
$defaultSQL = isset($default[1]) ? $default[1] : 'NULL';
|
|
|
|
// Reset to default any value in that is in the old enum, but not the new one
|
|
|
|
$placeholders = DB::placeholders($holder);
|
|
|
|
$query = "UPDATE \"{$table}\" SET \"{$field}\" = {$defaultSQL} WHERE \"{$field}\" IN ({$placeholders})";
|
|
|
|
$this->preparedQuery($query, $holder);
|
2016-11-29 00:31:16 +01:00
|
|
|
$amount = $this->database->affectedRows();
|
2019-03-26 02:12:59 +01:00
|
|
|
$this->alterationMessage(
|
|
|
|
"Changed $amount rows to default value of field $field (Value: $defaultSQL)"
|
|
|
|
);
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->transAlterField($table, $field, $spec_orig);
|
|
|
|
$this->alterationMessage(
|
2017-07-12 04:23:04 +02:00
|
|
|
"Field $table.$field: changed to $specValue <i class=\"build-info-before\">(from {$fieldValue})</i>",
|
2016-11-29 00:31:16 +01:00
|
|
|
"changed"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If the given field exists, move it out of the way by renaming it to _obsolete_(fieldname).
|
|
|
|
*
|
|
|
|
* @param string $table
|
|
|
|
* @param string $fieldName
|
|
|
|
*/
|
|
|
|
public function dontRequireField($table, $fieldName)
|
|
|
|
{
|
|
|
|
$fieldList = $this->fieldList($table);
|
|
|
|
if (array_key_exists($fieldName, $fieldList)) {
|
|
|
|
$suffix = '';
|
|
|
|
while (isset($fieldList[strtolower("_obsolete_{$fieldName}$suffix")])) {
|
|
|
|
$suffix = $suffix
|
|
|
|
? ((int)$suffix + 1)
|
|
|
|
: 2;
|
|
|
|
}
|
|
|
|
$this->renameField($table, $fieldName, "_obsolete_{$fieldName}$suffix");
|
|
|
|
$this->alterationMessage(
|
|
|
|
"Field $table.$fieldName: renamed to $table._obsolete_{$fieldName}$suffix",
|
|
|
|
"obsolete"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Show a message about database alteration
|
|
|
|
*
|
|
|
|
* @param string $message to display
|
|
|
|
* @param string $type one of [created|changed|repaired|obsolete|deleted|error]
|
|
|
|
*/
|
|
|
|
public function alterationMessage($message, $type = "")
|
|
|
|
{
|
|
|
|
if (!$this->supressOutput) {
|
|
|
|
if (Director::is_cli()) {
|
|
|
|
switch ($type) {
|
|
|
|
case "created":
|
|
|
|
case "changed":
|
|
|
|
case "repaired":
|
|
|
|
$sign = "+";
|
|
|
|
break;
|
|
|
|
case "obsolete":
|
|
|
|
case "deleted":
|
|
|
|
$sign = '-';
|
|
|
|
break;
|
|
|
|
case "notice":
|
|
|
|
$sign = '*';
|
|
|
|
break;
|
|
|
|
case "error":
|
|
|
|
$sign = "!";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$sign = " ";
|
|
|
|
}
|
|
|
|
$message = strip_tags($message);
|
|
|
|
echo " $sign $message\n";
|
|
|
|
} else {
|
|
|
|
switch ($type) {
|
|
|
|
case "created":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "success";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
case "obsolete":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "error";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
case "notice":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "warning";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
case "error":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "error";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
case "deleted":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "error";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
case "changed":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "info";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
case "repaired":
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "info";
|
2016-11-29 00:31:16 +01:00
|
|
|
break;
|
|
|
|
default:
|
2017-05-16 18:04:10 +02:00
|
|
|
$class = "";
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
2017-05-16 18:04:10 +02:00
|
|
|
echo "<li class=\"$class\">$message</li>";
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This returns the data type for the id column which is the primary key for each table
|
|
|
|
*
|
|
|
|
* @param boolean $asDbValue
|
|
|
|
* @param boolean $hasAutoIncPK
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function IdColumn($asDbValue = false, $hasAutoIncPK = true);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks a table's integrity and repairs it if necessary.
|
|
|
|
*
|
|
|
|
* @param string $tableName The name of the table.
|
|
|
|
* @return boolean Return true if the table has integrity after the method is complete.
|
|
|
|
*/
|
|
|
|
abstract public function checkAndRepairTable($tableName);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure the given table has the correct case
|
|
|
|
*
|
|
|
|
* @param string $tableName Name of table in desired case
|
|
|
|
*/
|
|
|
|
public function fixTableCase($tableName)
|
|
|
|
{
|
|
|
|
// Check if table exists
|
|
|
|
$tables = $this->tableList();
|
|
|
|
if (!array_key_exists(strtolower($tableName), $tables)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if case differs
|
|
|
|
$currentName = $tables[strtolower($tableName)];
|
|
|
|
if ($currentName === $tableName) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->alterationMessage(
|
|
|
|
"Table $tableName: renamed from $currentName",
|
|
|
|
"repaired"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Rename via temp table to avoid case-sensitivity issues
|
|
|
|
$tempTable = "__TEMP__{$tableName}";
|
|
|
|
$this->renameTable($currentName, $tempTable);
|
|
|
|
$this->renameTable($tempTable, $tableName);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the values of the given enum field
|
|
|
|
*
|
|
|
|
* @param string $tableName Name of table to check
|
|
|
|
* @param string $fieldName name of enum field to check
|
|
|
|
* @return array List of enum values
|
|
|
|
*/
|
|
|
|
abstract public function enumValuesForField($tableName, $fieldName);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2017-12-14 02:18:41 +01:00
|
|
|
* This is a lookup table for data types.
|
|
|
|
* For instance, Postgres uses 'INT', while MySQL uses 'UNSIGNED'
|
|
|
|
* So this is a DB-specific list of equivilents.
|
|
|
|
*
|
|
|
|
* @param string $type
|
|
|
|
* @return string
|
|
|
|
*/
|
2016-11-29 00:31:16 +01:00
|
|
|
abstract public function dbDataType($type);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieves the list of all databases the user has access to
|
|
|
|
*
|
|
|
|
* @return array List of database names
|
|
|
|
*/
|
|
|
|
abstract public function databaseList();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the database with the specified name exists
|
|
|
|
*
|
|
|
|
* @param string $name Name of the database to check for
|
|
|
|
* @return boolean Flag indicating whether this database exists
|
|
|
|
*/
|
|
|
|
abstract public function databaseExists($name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a database with the specified name
|
|
|
|
*
|
|
|
|
* @param string $name Name of the database to create
|
|
|
|
* @return boolean True if successful
|
|
|
|
*/
|
|
|
|
abstract public function createDatabase($name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Drops a database with the specified name
|
|
|
|
*
|
|
|
|
* @param string $name Name of the database to drop
|
|
|
|
*/
|
|
|
|
abstract public function dropDatabase($name);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alter an index on a table.
|
|
|
|
*
|
|
|
|
* @param string $tableName The name of the table.
|
|
|
|
* @param string $indexName The name of the index.
|
2017-11-21 05:13:08 +01:00
|
|
|
* @param array $indexSpec The specification of the index, see Database::requireIndex() for more details.
|
2016-11-29 00:31:16 +01:00
|
|
|
* @todo Find out where this is called from - Is it even used? Aren't indexes always dropped and re-added?
|
|
|
|
*/
|
|
|
|
abstract public function alterIndex($tableName, $indexName, $indexSpec);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determines the key that should be used to identify this index
|
|
|
|
* when retrieved from DBSchemaManager->indexList.
|
|
|
|
* In some connectors this is the database-visible name, in others the
|
|
|
|
* usercode-visible name.
|
|
|
|
*
|
|
|
|
* @param string $table
|
|
|
|
* @param string $index
|
|
|
|
* @param array $spec
|
|
|
|
* @return string Key for this index
|
|
|
|
*/
|
|
|
|
abstract protected function indexKey($table, $index, $spec);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the list of indexes in a table.
|
|
|
|
*
|
|
|
|
* @param string $table The table name.
|
|
|
|
* @return array[array] List of current indexes in the table, each in standard
|
|
|
|
* array form. The key for this array should be predictable using the indexKey
|
|
|
|
* method
|
|
|
|
*/
|
|
|
|
abstract public function indexList($table);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of all tables in the database.
|
|
|
|
* Keys are table names in lower case, values are table names in case that
|
|
|
|
* database expects.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
abstract public function tableList();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new table.
|
|
|
|
*
|
|
|
|
* @param string $table The name of the table
|
|
|
|
* @param array $fields A map of field names to field types
|
|
|
|
* @param array $indexes A map of indexes
|
|
|
|
* @param array $options An map of additional options. The available keys are as follows:
|
|
|
|
* - 'MSSQLDatabase'/'MySQLDatabase'/'PostgreSQLDatabase' - database-specific options such as "engine" for MySQL.
|
|
|
|
* - 'temporary' - If true, then a temporary table will be created
|
|
|
|
* @param array $advancedOptions Advanced creation options
|
|
|
|
* @return string The table name generated. This may be different from the table name, for example with temporary
|
|
|
|
* tables.
|
|
|
|
*/
|
|
|
|
abstract public function createTable(
|
|
|
|
$table,
|
|
|
|
$fields = null,
|
|
|
|
$indexes = null,
|
|
|
|
$options = null,
|
|
|
|
$advancedOptions = null
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alter a table's schema.
|
|
|
|
*
|
|
|
|
* @param string $table The name of the table to alter
|
|
|
|
* @param array $newFields New fields, a map of field name => field schema
|
|
|
|
* @param array $newIndexes New indexes, a map of index name => index type
|
|
|
|
* @param array $alteredFields Updated fields, a map of field name => field schema
|
|
|
|
* @param array $alteredIndexes Updated indexes, a map of index name => index type
|
|
|
|
* @param array $alteredOptions
|
|
|
|
* @param array $advancedOptions
|
|
|
|
*/
|
|
|
|
abstract public function alterTable(
|
|
|
|
$table,
|
|
|
|
$newFields = null,
|
|
|
|
$newIndexes = null,
|
|
|
|
$alteredFields = null,
|
|
|
|
$alteredIndexes = null,
|
|
|
|
$alteredOptions = null,
|
|
|
|
$advancedOptions = null
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rename a table.
|
|
|
|
*
|
|
|
|
* @param string $oldTableName The old table name.
|
|
|
|
* @param string $newTableName The new table name.
|
|
|
|
*/
|
|
|
|
abstract public function renameTable($oldTableName, $newTableName);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new field on a table.
|
|
|
|
*
|
|
|
|
* @param string $table Name of the table.
|
|
|
|
* @param string $field Name of the field to add.
|
|
|
|
* @param string $spec The field specification, eg 'INTEGER NOT NULL'
|
|
|
|
*/
|
|
|
|
abstract public function createField($table, $field, $spec);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change the database column name of the given field.
|
|
|
|
*
|
|
|
|
* @param string $tableName The name of the tbale the field is in.
|
|
|
|
* @param string $oldName The name of the field to change.
|
|
|
|
* @param string $newName The new name of the field
|
|
|
|
*/
|
|
|
|
abstract public function renameField($tableName, $oldName, $newName);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a list of all the fields for the given table.
|
|
|
|
* Returns a map of field name => field spec.
|
|
|
|
*
|
|
|
|
* @param string $table The table name.
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
abstract public function fieldList($table);
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* This allows the cached values for a table's field list to be erased.
|
|
|
|
* If $tablename is empty, then the whole cache is erased.
|
|
|
|
*
|
|
|
|
* @param string $tableName
|
|
|
|
* @return boolean
|
|
|
|
*/
|
|
|
|
public function clearCachedFieldlist($tableName = null)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'boolean' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function boolean($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'date' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function date($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'decimal' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function decimal($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'set' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function enum($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'set' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function set($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'float' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function float($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'int' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function int($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'datetime' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function datetime($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'text' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function text($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'time' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function time($values);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns data type for 'varchar' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
abstract public function varchar($values);
|
|
|
|
|
|
|
|
/*
|
2017-12-14 02:18:41 +01:00
|
|
|
* Returns data type for 'year' column
|
|
|
|
*
|
|
|
|
* @param array $values Contains a tokenised list of info about this data type
|
|
|
|
* @return string
|
|
|
|
*/
|
2016-11-29 00:31:16 +01:00
|
|
|
abstract public function year($values);
|
2013-06-21 00:32:08 +02:00
|
|
|
}
|