diff --git a/core/includes/database.inc b/core/includes/database.inc index 75f6a96..67c879e 100644 --- a/core/includes/database.inc +++ b/core/includes/database.inc @@ -680,6 +680,18 @@ function db_rename_table($table, $new_name) { } /** + * Copies the structure of a table. + * + * @param string $source + * The name of the table to be copied. + * @param string $destination + * The name for the new table. + */ +function db_copy_table($source, $destination) { + return Database::getConnection()->schema()->copyTable($source, $destination); +} + +/** * Drops a table. * * @param $table diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php index 5788bef..42be880 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php @@ -327,6 +327,21 @@ public function dropTable($table) { return TRUE; } + /** + * {@inheritdoc} + */ + public function copyTable($source, $destination) { + if (!$this->tableExists($source)) { + throw new SchemaObjectDoesNotExistException(t("Cannot copy @source to @destination: table @source doesn't exist.", array('@source' => $source, '@destination' => $destination))); + } + if ($this->tableExists($destination)) { + throw new SchemaObjectExistsException(t("Cannot copy @source to @destination: table @destination already exists.", array('@source' => $source, '@destination' => $destination))); + } + + $info = $this->getPrefixInfo($destination); + return $this->connection->query('CREATE TABLE `' . $info['table'] . '` LIKE {' . $source . '}'); + } + public function addField($table, $field, $spec, $keys_new = array()) { if (!$this->tableExists($table)) { throw new SchemaObjectDoesNotExistException(t("Cannot add field @table.@field: table doesn't exist.", array('@field' => $field, '@table' => $table))); diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php index e2798e1..f8ddd93 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php @@ -348,6 +348,25 @@ function renameTable($table, $new_name) { $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO ' . $prefixInfo['table']); } + /** + * {@inheritdoc} + */ + public function copyTable($source, $destination) { + if (!$this->tableExists($source)) { + throw new SchemaObjectDoesNotExistException(t("Cannot copy @source to @destination: table @source doesn't exist.", array('@source' => $source, '@destination' => $destination))); + } + if ($this->tableExists($destination)) { + throw new SchemaObjectExistsException(t("Cannot copy @source to @destination: table @destination already exists.", array('@source' => $source, '@destination' => $destination))); + } + // @TODO The server is likely going to rename indexes and constraints + // during the copy process, and it will not match our + // table_name + constraint name convention anymore. + throw new \Exception('Not implemented, see https://drupal.org/node/2056133'); + // This is how the implementation could look like: + $info = $this->getPrefixInfo($destination); + return $this->connection->query('CREATE TABLE `' . $info['table'] . '` (LIKE {' . $source . '} INCLUDING ALL)'); + } + public function dropTable($table) { if (!$this->tableExists($table)) { return FALSE; diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php index f8050cd..89651bb 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php @@ -272,6 +272,20 @@ public function renameTable($table, $new_name) { } } + /** + * {@inheritdoc} + */ + public function copyTable($source, $destination) { + if (!$this->tableExists($source)) { + throw new SchemaObjectDoesNotExistException(t("Cannot copy @source to @destination: table @source doesn't exist.", array('@source' => $source, '@destination' => $destination))); + } + if ($this->tableExists($destination)) { + throw new SchemaObjectExistsException(t("Cannot copy @source to @destination: table @destination already exists.", array('@source' => $source, '@destination' => $destination))); + } + + $this->createTable($destination, $this->introspectSchema($source)); + } + public function dropTable($table) { if (!$this->tableExists($table)) { return FALSE; diff --git a/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php index 7b884f7..d3e1ba4 100644 --- a/core/lib/Drupal/Core/Database/Schema.php +++ b/core/lib/Drupal/Core/Database/Schema.php @@ -408,6 +408,24 @@ public function fieldExists($table, $column) { abstract public function dropTable($table); /** + * Copies the structure of a table without copying the content. + * + * @param string $source + * The name of the table to be used as source. + * @param string $destination + * The name of the table to be used as destination. + * + * @return \Drupal\Core\Database\StatementInterface + * The result of the executed query. + * + * @throws \Drupal\Core\Database\SchemaObjectExistsException + * Thrown when the source table does not exist. + * @throws \Drupal\Core\Database\SchemaObjectDoesNotExistException + * Thrown when the destination table already exists. + */ + abstract public function copyTable($source, $destination); + + /** * Add a new field to a table. * * @param $table diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/SchemaTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/SchemaTest.php index 4845751..4c0085b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/SchemaTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/SchemaTest.php @@ -87,6 +87,19 @@ function testSchema() { $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); $this->assertTrue($index_exists, 'Index was renamed.'); + // Copy the schema of the table. + db_copy_table('test_table2', 'test_table3'); + + // Index should be copied. + $index_exists = Database::getConnection()->schema()->indexExists('test_table3', 'test_field'); + $this->assertTrue($index_exists, 'Index was copied.'); + + // Data should still exist on the old table but not on the new one. + $count = db_select('test_table2')->countQuery()->execute()->fetchField(); + $this->assertEqual($count, 1, 'The old table still has its content.'); + $count = db_select('test_table3')->countQuery()->execute()->fetchField(); + $this->assertEqual($count, 0, 'The new table has no content.'); + // We need the default so that we can insert after the rename. db_field_set_default('test_table2', 'test_field', 0); $this->assertFalse($this->tryInsert(), 'Insert into the old table failed.'); @@ -154,9 +167,9 @@ function testSchema() { */ function tryInsert($table = 'test_table') { try { - db_insert($table) - ->fields(array('id' => mt_rand(10, 20))) - ->execute(); + db_insert($table) + ->fields(array('id' => mt_rand(10, 20))) + ->execute(); return TRUE; } catch (\Exception $e) { @@ -227,8 +240,8 @@ function testUnsignedColumns() { function tryUnsignedInsert($table_name, $column_name) { try { db_insert($table_name) - ->fields(array($column_name => -1)) - ->execute(); + ->fields(array($column_name => -1)) + ->execute(); return TRUE; } catch (\Exception $e) {