summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorDries Buytaert <dries@buytaert.net>2010-02-28 20:03:05 +0000
committerDries Buytaert <dries@buytaert.net>2010-02-28 20:03:05 +0000
commit994fafcebf25c17776026194ca01b04a170d6d50 (patch)
tree740e0e16329fbb11f61c297406cb29102e4f24da /includes
parent79e51a71cfadc34f770c11105f14b1889c75cd79 (diff)
downloadbrdo-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.inc78
-rw-r--r--includes/database/pgsql/schema.inc258
-rw-r--r--includes/database/schema.inc60
-rw-r--r--includes/database/sqlite/schema.inc241
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(