diff options
author | Dries Buytaert <dries@buytaert.net> | 2010-02-28 20:03:05 +0000 |
---|---|---|
committer | Dries Buytaert <dries@buytaert.net> | 2010-02-28 20:03:05 +0000 |
commit | 994fafcebf25c17776026194ca01b04a170d6d50 (patch) | |
tree | 740e0e16329fbb11f61c297406cb29102e4f24da /includes | |
parent | 79e51a71cfadc34f770c11105f14b1889c75cd79 (diff) | |
download | brdo-994fafcebf25c17776026194ca01b04a170d6d50.tar.gz brdo-994fafcebf25c17776026194ca01b04a170d6d50.tar.bz2 |
- Patch #582948 by Damien Tournoud, agentrickard: improve safety of schema manipulation to help with upgrade path.
Diffstat (limited to 'includes')
-rw-r--r-- | includes/database/mysql/schema.inc | 78 | ||||
-rw-r--r-- | includes/database/pgsql/schema.inc | 258 | ||||
-rw-r--r-- | includes/database/schema.inc | 60 | ||||
-rw-r--r-- | includes/database/sqlite/schema.inc | 241 |
4 files changed, 314 insertions, 323 deletions
diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc index b87bb16d3..b6b80af14 100644 --- a/includes/database/mysql/schema.inc +++ b/includes/database/mysql/schema.inc @@ -219,9 +219,6 @@ class DatabaseSchema_mysql extends DatabaseSchema { return $map; } - - - protected function createKeysSql($spec) { $keys = array(); @@ -269,14 +266,33 @@ class DatabaseSchema_mysql extends DatabaseSchema { } public function renameTable($table, $new_name) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name))); + } + if ($this->tableExists($new_name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name))); + } + $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}'); } public function dropTable($table) { + if (!$this->tableExists($table)) { + return FALSE; + } + $this->connection->query('DROP TABLE {' . $table . '}'); + return TRUE; } public function addField($table, $field, $spec, $keys_new = array()) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exists.", array('%field' => $field, '%table' => $table))); + } + if ($this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table))); + } + $fixnull = FALSE; if (!empty($spec['not null']) && !isset($spec['default'])) { $fixnull = TRUE; @@ -300,10 +316,19 @@ class DatabaseSchema_mysql extends DatabaseSchema { } public function dropField($table, $field) { + if (!$this->columnExists($table, $field)) { + return FALSE; + } + $this->connection->query('ALTER TABLE {' . $table . '} DROP `' . $field . '`'); + return TRUE; } public function fieldSetDefault($table, $field, $default) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exists.", array('%table' => $table, '%field' => $field))); + } + if (is_null($default)) { $default = 'NULL'; } @@ -315,6 +340,10 @@ class DatabaseSchema_mysql extends DatabaseSchema { } public function fieldSetNoDefault($table, $field) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exists.", array('%table' => $table, '%field' => $field))); + } + $this->connection->query('ALTER TABLE {' . $table . '} ALTER COLUMN `' . $field . '` DROP DEFAULT'); } @@ -323,30 +352,73 @@ class DatabaseSchema_mysql extends DatabaseSchema { } public function addPrimaryKey($table, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exists.", array('%table' => $table))); + } + if ($this->indexExists($table, 'PRIMARY')) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table))); + } + $this->connection->query('ALTER TABLE {' . $table . '} ADD PRIMARY KEY (' . $this->createKeySql($fields) . ')'); } public function dropPrimaryKey($table) { + if (!$this->indexExists($table, 'PRIMARY')) { + return FALSE; + } + $this->connection->query('ALTER TABLE {' . $table . '} DROP PRIMARY KEY'); + return TRUE; } public function addUniqueKey($table, $name, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exists.", array('%table' => $table, '%name' => $name))); + } + if ($this->indexExists($table, $name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name))); + } + $this->connection->query('ALTER TABLE {' . $table . '} ADD UNIQUE KEY `' . $name . '` (' . $this->createKeySql($fields) . ')'); } public function dropUniqueKey($table, $name) { + if (!$this->indexExists($table, $name)) { + return FALSE; + } + $this->connection->query('ALTER TABLE {' . $table . '} DROP KEY `' . $name . '`'); + return TRUE; } public function addIndex($table, $name, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exists.", array('%table' => $table, '%name' => $name))); + } + if ($this->indexExists($table, $name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name))); + } + $this->connection->query('ALTER TABLE {' . $table . '} ADD INDEX `' . $name . '` (' . $this->createKeySql($fields) . ')'); } public function dropIndex($table, $name) { + if (!$this->indexExists($table, $name)) { + return FALSE; + } + $this->connection->query('ALTER TABLE {' . $table . '} DROP INDEX `' . $name . '`'); + return TRUE; } public function changeField($table, $field, $field_new, $spec, $keys_new = array()) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exists.", array('%table' => $table, '%name' => $field))); + } + if (($field != $field_new) && $this->columnExists($table, $field_new)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $name, '%name_new' => $field_new))); + } + $sql = 'ALTER TABLE {' . $table . '} CHANGE `' . $field . '` ' . $this->createFieldSql($field_new, $this->processField($spec)); if (count($keys_new)) { $sql .= ', ADD ' . implode(', ADD ', $this->createKeysSql($keys_new)); diff --git a/includes/database/pgsql/schema.inc b/includes/database/pgsql/schema.inc index 0ee2d6f14..58eb3fcc1 100644 --- a/includes/database/pgsql/schema.inc +++ b/includes/database/pgsql/schema.inc @@ -210,7 +210,6 @@ class DatabaseSchema_pgsql extends DatabaseSchema { return $field; } - /** * This maps a generic data type in combination with its data size * to the engine-specific data type. @@ -276,6 +275,13 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } function renameTable($table, $new_name) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name))); + } + if ($this->tableExists($new_name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name))); + } + // Get the schema and tablename for the old table. $old_full_name = $this->connection->prefixTables('{' . $table . '}'); list($old_schema, $old_table_name) = strpos($old_full_name, '.') ? explode('.', $old_full_name) : array('public', $old_full_name); @@ -295,33 +301,22 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } public function dropTable($table) { + if (!$this->tableExists($table)) { + return FALSE; + } + $this->connection->query('DROP TABLE {' . $table . '}'); + return TRUE; } - /** - * Add a new field to a table. - * - * @param $table - * Name of the table to be altered. - * @param $field - * Name of the field to be added. - * @param $spec - * The field specification array, as taken from a schema definition. - * The specification may also contain the key 'initial', the newly - * created field will be set to the value of the key in all rows. - * This is most useful for creating NOT NULL columns with no default - * value in existing tables. - * @param $keys_new - * Optional keys and indexes specification to be created on the - * table along with adding the field. The format is the same as a - * table specification but without the 'fields' element. If you are - * adding a type 'serial' field, you MUST specify at least one key - * or index including it in this array. See db_change_field() for more - * explanation why. - * - * @see db_change_field() - */ public function addField($table, $field, $spec, $new_keys = array()) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exists.", array('%field' => $field, '%table' => $table))); + } + if ($this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table))); + } + $fixnull = FALSE; if (!empty($spec['not null']) && !isset($spec['default'])) { $fixnull = TRUE; @@ -347,29 +342,20 @@ class DatabaseSchema_pgsql extends DatabaseSchema { } } - /** - * Drop a field. - * - * @param $table - * The table to be altered. - * @param $field - * The field to be dropped. - */ public function dropField($table, $field) { + if (!$this->columnExists($table, $field)) { + return FALSE; + } + $this->connection->query('ALTER TABLE {' . $table . '} DROP COLUMN "' . $field . '"'); + return TRUE; } - /** - * Set the default value for a field. - * - * @param $table - * The table to be altered. - * @param $field - * The field to be altered. - * @param $default - * Default value to be set. NULL for 'default NULL'. - */ public function fieldSetDefault($table, $field, $default) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exists.", array('%table' => $table, '%field' => $field))); + } + if (is_null($default)) { $default = 'NULL'; } @@ -380,169 +366,106 @@ class DatabaseSchema_pgsql extends DatabaseSchema { $this->connection->query('ALTER TABLE {' . $table . '} ALTER COLUMN "' . $field . '" SET DEFAULT ' . $default); } - /** - * Set a field to have no default value. - * - * @param $table - * The table to be altered. - * @param $field - * The field to be altered. - */ public function fieldSetNoDefault($table, $field) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot remove default value of field %table.%field: field doesn't exists.", array('%table' => $table, '%field' => $field))); + } + $this->connection->query('ALTER TABLE {' . $table . '} ALTER COLUMN "' . $field . '" DROP DEFAULT'); } public function indexExists($table, $name) { $index_name = '{' . $table . '}_' . $name . '_idx'; - return $this->connection->query("SELECT COUNT(indexname) FROM pg_indexes WHERE indexname = '$index_name'")->fetchField(); + return $this->connection->query("SELECT 1 FROM pg_indexes WHERE indexname = '$index_name'")->fetchField(); } /** - * Add a primary key. + * Helper function: check if a constraint (PK, FK, UK) exists. * * @param $table - * The table to be altered. - * @param $fields - * Fields for the primary key. + * The name of the table. + * @param $name + * The name of the constraint (typically 'pkey' or '[constraint]_key'). */ + protected function constraintExists($table, $name) { + $constraint_name = '{' . $table . '}_' . $name; + return (bool) $this->connection->query("SELECT 1 FROM pg_constraint WHERE conname = '$constraint_name'")->fetchField(); + } + public function addPrimaryKey($table, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exists.", array('%table' => $table))); + } + if ($this->constraintExists($table, 'pkey')) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table))); + } + $this->connection->query('ALTER TABLE {' . $table . '} ADD PRIMARY KEY (' . implode(',', $fields) . ')'); } - /** - * Drop the primary key. - * - * @param $table - * The table to be altered. - */ public function dropPrimaryKey($table) { + if (!$this->constraintExists($table, 'pkey')) { + return FALSE; + } + $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT {' . $table . '}_pkey'); + return TRUE; } - /** - * Add a unique key. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the key. - * @param $fields - * An array of field names. - */ function addUniqueKey($table, $name, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exists.", array('%table' => $table, '%name' => $name))); + } + if ($this->constraintExists($table, $name . '_key')) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name))); + } + $name = '{' . $table . '}_' . $name . '_key'; $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT "' . $name . '" UNIQUE (' . implode(',', $fields) . ')'); } - /** - * Drop a unique key. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the key. - */ public function dropUniqueKey($table, $name) { + if (!$this->constraintExists($table, $name . '_key')) { + return FALSE; + } + $name = '{' . $table . '}_' . $name . '_key'; $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $name . '"'); + return TRUE; } - /** - * Add an index. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the index. - * @param $fields - * An array of field names. - */ public function addIndex($table, $name, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exists.", array('%table' => $table, '%name' => $name))); + } + if ($this->indexExists($table, $name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name))); + } + $this->connection->query($this->_createIndexSql($table, $name, $fields)); } - /** - * Drop an index. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the index. - */ public function dropIndex($table, $name) { + if (!$this->indexExists($table, $name)) { + return FALSE; + } + $name = '{' . $table . '}_' . $name . '_idx'; $this->connection->query('DROP INDEX ' . $name); + return TRUE; } - /** - * Change a field definition. - * - * IMPORTANT NOTE: To maintain database portability, you have to explicitly - * recreate all indices and primary keys that are using the changed field. - * - * That means that you have to drop all affected keys and indexes with - * db_drop_{primary_key,unique_key,index}() before calling db_change_field(). - * To recreate the keys and indices, pass the key definitions as the - * optional $new_keys argument directly to db_change_field(). - * - * For example, suppose you have: - * @code - * $schema['foo'] = array( - * 'fields' => array( - * 'bar' => array('type' => 'int', 'not null' => TRUE) - * ), - * 'primary key' => array('bar') - * ); - * @endcode - * and you want to change foo.bar to be type serial, leaving it as the - * primary key. The correct sequence is: - * @code - * db_drop_primary_key('foo'); - * db_change_field('foo', 'bar', 'bar', - * array('type' => 'serial', 'not null' => TRUE), - * array('primary key' => array('bar'))); - * @endcode - * - * The reasons for this are due to the different database engines: - * - * On PostgreSQL, changing a field definition involves adding a new field - * and dropping an old one which* causes any indices, primary keys and - * sequences (from serial-type fields) that use the changed field to be dropped. - * - * On MySQL, all type 'serial' fields must be part of at least one key - * or index as soon as they are created. You cannot use - * db_add_{primary_key,unique_key,index}() for this purpose because - * the ALTER TABLE command will fail to add the column without a key - * or index specification. The solution is to use the optional - * $new_keys argument to create the key or index at the same time as - * field. - * - * You could use db_add_{primary_key,unique_key,index}() in all cases - * unless you are converting a field to be type serial. You can use - * the $new_keys argument in all cases. - * - * @param $table - * Name of the table. - * @param $field - * Name of the field to change. - * @param $field_new - * New name for the field (set to the same as $field if you don't want to change the name). - * @param $spec - * The field specification for the new field. - * @param $new_keys - * Optional keys and indexes specification to be created on the - * table along with changing the field. The format is the same as a - * table specification but without the 'fields' element. - */ public function changeField($table, $field, $field_new, $spec, $new_keys = array()) { - $this->connection->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field . '_old"'); - $not_null = isset($spec['not null']) ? $spec['not null'] : FALSE; - unset($spec['not null']); + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exists.", array('%table' => $table, '%name' => $field))); + } + if (($field != $field_new) && $this->columnExists($table, $field_new)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $name, '%name_new' => $field_new))); + } if (!array_key_exists('size', $spec)) { $spec['size'] = 'normal'; } - $this->addField($table, "$field_new", $spec); // We need to typecast the new column to best be able to transfer the data // Schema_pgsql::getFieldTypeMap() will return possibilities that are not @@ -552,14 +475,19 @@ class DatabaseSchema_pgsql extends DatabaseSchema { if (in_array($typecast, array('serial', 'bigserial', 'numeric'))) { $typecast = 'int'; } - $this->connection->query("UPDATE {" . $table . "} SET $field_new = CAST(" . $field . "_old as " . $typecast . ")"); - if ($not_null) { - $this->connection->query("ALTER TABLE {" . $table . "} ALTER $field_new SET NOT NULL"); - } + + $this->connection->query("ALTER TABLE {" . $table . "} ALTER $field SET $field_new = CAST(" . $field . "_old as " . $typecast . ")"); + + $this->addField($table, "$field_new", $spec); $this->dropField($table, $field . '_old'); + // Rename the column if necessary. + if ($field != $field_new) { + $this->connection->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field_new . '_old"'); + } + if (isset($new_keys)) { $this->_createKeys($table, $new_keys); } diff --git a/includes/database/schema.inc b/includes/database/schema.inc index 0437999d2..d575114eb 100644 --- a/includes/database/schema.inc +++ b/includes/database/schema.inc @@ -271,6 +271,10 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The table to be renamed. * @param $new_name * The new name for the table. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table doesn't exist. + * @throws DatabaseSchemaObjectExistsException + * If the specified table target table already exist. */ abstract public function renameTable($table, $new_name); @@ -279,6 +283,9 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * * @param $table * The table to be dropped. + * @return + * TRUE if the table was successfully dropped, FALSE if there was no table + * by that name to begin with. */ abstract public function dropTable($table); @@ -302,6 +309,10 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * adding a type 'serial' field, you MUST specify at least one key * or index including it in this array. See db_change_field() for more * explanation why. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table doesn't exist. + * @throws DatabaseSchemaObjectExistsException + * If the specified table already has a field by that name. */ abstract public function addField($table, $field, $spec, $keys_new = array()); @@ -312,6 +323,9 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The table to be altered. * @param $field * The field to be dropped. + * @return + * TRUE if the field was successfully dropped, FALSE if there was no field + * by that name to begin with. */ abstract public function dropField($table, $field); @@ -324,6 +338,8 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The field to be altered. * @param $default * Default value to be set. NULL for 'default NULL'. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table or field doesn't exist. */ abstract public function fieldSetDefault($table, $field, $default); @@ -334,6 +350,8 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The table to be altered. * @param $field * The field to be altered. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table or field doesn't exist. */ abstract public function fieldSetNoDefault($table, $field); @@ -345,7 +363,7 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * @param $name * Name of the index. * @return - * Index name if the table exists. Otherwise FALSE. + * TRUE if the index exists, otherwise FALSE. */ abstract public function indexExists($table, $name); @@ -356,6 +374,10 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The table to be altered. * @param $fields * Fields for the primary key. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table doesn't exist. + * @throws DatabaseSchemaObjectExistsException + * If the specified table already has a primary key. */ abstract public function addPrimaryKey($table, $fields); @@ -364,6 +386,9 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * * @param $table * The table to be altered. + * @return + * TRUE if the primary key was successfully dropped, FALSE if there was no + * primary key on this table to begin with. */ abstract public function dropPrimaryKey($table); @@ -376,6 +401,10 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The name of the key. * @param $fields * An array of field names. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table doesn't exist. + * @throws DatabaseSchemaObjectExistsException + * If the specified table already has a key by that name. */ abstract public function addUniqueKey($table, $name, $fields); @@ -386,6 +415,9 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The table to be altered. * @param $name * The name of the key. + * @return + * TRUE if the key was successfully dropped, FALSE if there was no key by + * that name to begin with. */ abstract public function dropUniqueKey($table, $name); @@ -398,6 +430,10 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The name of the index. * @param $fields * An array of field names. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table doesn't exist. + * @throws DatabaseSchemaObjectExistsException + * If the specified table already has an index by that name. */ abstract public function addIndex($table, $name, $fields); @@ -408,6 +444,9 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The table to be altered. * @param $name * The name of the index. + * @return + * TRUE if the index was successfully dropped, FALSE if there was no index + * by that name to begin with. */ abstract public function dropIndex($table, $name); @@ -470,6 +509,10 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * Optional keys and indexes specification to be created on the * table along with changing the field. The format is the same as a * table specification but without the 'fields' element. + * @throws DatabaseSchemaObjectDoesNotExistException + * If the specified table or source field doesn't exist. + * @throws DatabaseSchemaObjectExistsException + * If the specified destination field already exists. */ abstract public function changeField($table, $field, $field_new, $spec, $keys_new = array()); @@ -480,8 +523,13 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { * The name of the table to create. * @param $table * A Schema API table definition array. + * @throws DatabaseSchemaObjectExistsException + * If the specified table already exists. */ public function createTable($name, $table) { + if ($this->tableExists($name)) { + throw new DatabaseSchemaObjectExistsException(t('Table %name already exists.', array('%name' => $name))); + } $statements = $this->createTableSql($name, $table); foreach ($statements as $statement) { $this->connection->query($statement); @@ -528,6 +576,16 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface { } /** + * Exception thrown if an object already exists on an operation creating a table, field or index. + */ +class DatabaseSchemaObjectExistsException extends Exception {} + +/** + * Exception thrown if an object doesn't exists on a modify operation. + */ +class DatabaseSchemaObjectDoesNotExistException extends Exception {} + +/** * @} End of "defgroup schemaapi". */ diff --git a/includes/database/sqlite/schema.inc b/includes/database/sqlite/schema.inc index 974d014d7..8710d917c 100644 --- a/includes/database/sqlite/schema.inc +++ b/includes/database/sqlite/schema.inc @@ -122,16 +122,16 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } /** - * Create an SQL string for a field to be used in table creation or alteration. - * - * Before passing a field out of a schema definition into this function it has - * to be processed by db_processField(). - * - * @param $name - * Name of the field. - * @param $spec - * The field specification, as per the schema data structure format. - */ + * Create an SQL string for a field to be used in table creation or alteration. + * + * Before passing a field out of a schema definition into this function it has + * to be processed by db_processField(). + * + * @param $name + * Name of the field. + * @param $spec + * The field specification, as per the schema data structure format. + */ protected function createFieldSql($name, $spec) { if (!empty($spec['auto_increment'])) { $sql = $name . " INTEGER PRIMARY KEY AUTOINCREMENT"; @@ -218,15 +218,14 @@ class DatabaseSchema_sqlite extends DatabaseSchema { return $map; } - /** - * Rename a table. - * - * @param $table - * The table to be renamed. - * @param $new_name - * The new name for the table. - */ public function renameTable($table, $new_name) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot rename %table to %table_new: table %table doesn't exist.", array('%table' => $table, '%table_new' => $new_name))); + } + if ($this->tableExists($new_name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot rename %table to %table_new: table %table_new already exists.", array('%table' => $table, '%table_new' => $new_name))); + } + $schema = $this->introspectSchema($table); $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO {' . $new_name . '}'); @@ -250,27 +249,23 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } } - /** - * Drop a table. - * - * @param $table - * The table to be dropped. - */ public function dropTable($table) { + if (!$this->tableExists($table)) { + return FALSE; + } + $this->connection->query('DROP TABLE {' . $table . '}'); + return TRUE; } - /** - * Add a new field to a table. - * - * @param $table - * Name of the table to be altered. - * @param $field - * Name of the field to be added. - * @param $spec - * The field specification array, as taken from a schema definition. - */ public function addField($table, $field, $spec, $keys_new = array()) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add field %table.%field: table doesn't exists.", array('%field' => $field, '%table' => $table))); + } + if ($this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add field %table.%field: field already exists.", array('%field' => $field, '%table' => $table))); + } + // TODO: $keys_new is not supported yet. $query = 'ALTER TABLE {' . $table . '} ADD '; $query .= $this->createFieldSql($field, $this->processField($spec)); @@ -381,18 +376,11 @@ class DatabaseSchema_sqlite extends DatabaseSchema { return $schema; } - /** - * Drop a field. - * - * This implementation can't use ALTER TABLE directly, because SQLite only - * supports a limited subset of that command. - * - * @param $table - * The table to be altered. - * @param $field - * The field to be dropped. - */ public function dropField($table, $field) { + if ($this->columnExists($table, $field)) { + return FALSE; + } + $new_schema = $this->introspectSchema($table); unset($new_schema['fields'][$field]); foreach ($new_schema['indexes'] as $index => $fields) { @@ -407,28 +395,17 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } } $this->alterTable($table, $new_schema); + return TRUE; } - /** - * Change a field definition. - * - * This implementation can't use ALTER TABLE directly, because SQLite only - * supports a limited subset of that command. - * - * @param $table - * Name of the table. - * @param $field - * Name of the field to change. - * @param $field_new - * New name for the field (set to the same as $field if you don't want to change the name). - * @param $spec - * The field specification for the new field. - * @param $keys_new - * Optional keys and indexes specification to be created on the - * table along with changing the field. The format is the same as a - * table specification but without the 'fields' element. - */ public function changeField($table, $field, $field_new, $spec, $keys_new = array()) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exists.", array('%table' => $table, '%name' => $field))); + } + if (($field != $field_new) && $this->columnExists($table, $field_new)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array('%table' => $table, '%name' => $name, '%name_new' => $field_new))); + } + $new_schema = $this->introspectSchema($table); unset($new_schema['fields'][$field]); $new_schema['fields'][$field_new] = $spec; @@ -444,17 +421,14 @@ class DatabaseSchema_sqlite extends DatabaseSchema { $this->alterTable($table, $new_schema); } - /** - * Add an index. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the index. - * @param $fields - * An array of field names. - */ public function addIndex($table, $name, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add index %name to table %table: table doesn't exists.", array('%table' => $table, '%name' => $name))); + } + if ($this->indexExists($table, $name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add index %name to table %table: index already exists.", array('%table' => $table, '%name' => $name))); + } + $schema['indexes'][$name] = $fields; $statements = $this->createIndexSql($table, $schema); foreach ($statements as $statement) { @@ -463,32 +437,26 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } public function indexExists($table, $name) { - return ($this->connection->query('PRAGMA index_info({' . $table . '}_' . $name . ')')->fetchField() != ''); + return $this->connection->query('PRAGMA index_info({' . $table . '}_' . $name . ')')->fetchField() != ''; } - /** - * Drop an index. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the index. - */ public function dropIndex($table, $name) { + if (!$this->indexExists($table, $name)) { + return FALSE; + } + $this->connection->query('DROP INDEX ' . '{' . $table . '}_' . $name); + return TRUE; } - /** - * Add a unique key. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the key. - * @param $fields - * An array of field names. - */ public function addUniqueKey($table, $name, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add unique key %name to table %table: table doesn't exists.", array('%table' => $table, '%name' => $name))); + } + if ($this->indexExists($table, $name)) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add unique key %name to table %table: unique key already exists.", array('%table' => $table, '%name' => $name))); + } + $schema['unique keys'][$name] = $fields; $statements = $this->createIndexSql($table, $schema); foreach ($statements as $statement) { @@ -496,95 +464,60 @@ class DatabaseSchema_sqlite extends DatabaseSchema { } } - /** - * Drop a unique key. - * - * @param $table - * The table to be altered. - * @param $name - * The name of the key. - */ public function dropUniqueKey($table, $name) { + if (!$this->indexExists($table, $name)) { + return FALSE; + } + $this->connection->query('DROP INDEX ' . '{' . $table . '}_' . $name); + return TRUE; } - /** - * Add a primary key. - * - * This implementation can't use ALTER TABLE directly, because SQLite only - * supports a limited subset of that command. - * - * @param $table - * The table to be altered. - * @param $fields - * Fields for the primary key. - */ public function addPrimaryKey($table, $fields) { + if (!$this->tableExists($table)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot add primary key to table %table: table doesn't exists.", array('%table' => $table))); + } + $new_schema = $this->introspectSchema($table); + if (!empty($new_schema['primary key'])) { + throw new DatabaseSchemaObjectExistsException(t("Cannot add primary key to table %table: primary key already exists.", array('%table' => $table))); + } + $new_schema['primary key'] = $fields; $this->alterTable($table, $new_schema); } - /** - * Drop the primary key. - * - * This implementation can't use ALTER TABLE directly, because SQLite only - * supports a limited subset of that command.` - * - * @param $table - * The table to be altered. - */ public function dropPrimaryKey($table) { $new_schema = $this->introspectSchema($table); + if (empty($new_schema['primary key'])) { + return FALSE; + } + unset($new_schema['primary key']); $this->alterTable($table, $new_schema); + return TRUE; } - /** - * Set the default value for a field. - * - * This implementation can't use ALTER TABLE directly, because SQLite only - * supports a limited subset of that command. - * - * @param $table - * The table to be altered. - * @param $field - * The field to be altered. - * @param $default - * Default value to be set. NULL for 'default NULL'. - */ public function fieldSetDefault($table, $field, $default) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exists.", array('%table' => $table, '%field' => $field))); + } + $new_schema = $this->introspectSchema($table); $new_schema['fields'][$field]['default'] = $default; $this->alterTable($table, $new_schema); } - /** - * Set a field to have no default value. - * - * This implementation can't use ALTER TABLE directly, because SQLite only - * supports a limited subset of that command. - * - * @param $table - * The table to be altered. - * @param $field - * The field to be altered. - */ public function fieldSetNoDefault($table, $field) { + if (!$this->columnExists($table, $field)) { + throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot set default value of field %table.%field: field doesn't exists.", array('%table' => $table, '%field' => $field))); + } + $new_schema = $this->introspectSchema($table); unset($new_schema['fields'][$field]['default']); $this->alterTable($table, $new_schema); } - /** - * Find all tables that are like the specified base table name. - * - * @param $table_expression - * An SQL expression, for example "simpletest%" (without the quotes). - * BEWARE: this is not prefixed, the caller should take care of that. - * @return - * Array, both the keys and the values are the matching tables. - */ public function findTables($table_expression) { // Don't use {} around sqlite_master table. $result = db_query("SELECT name FROM sqlite_master WHERE name LIKE :table_name", array( |