3 namespace Drupal\KernelTests\Core\Database;
5 use Drupal\Core\Database\Database;
6 use Drupal\Core\Database\SchemaException;
7 use Drupal\Core\Database\SchemaObjectDoesNotExistException;
8 use Drupal\Core\Database\SchemaObjectExistsException;
9 use Drupal\KernelTests\KernelTestBase;
10 use Drupal\Component\Utility\Unicode;
13 * Tests table creation and modification via the schema API.
17 class SchemaTest extends KernelTestBase {
20 * A global counter for table and field creation.
25 * Tests database interactions.
27 public function testSchema() {
28 // Try creating a table.
29 $table_specification = [
30 'description' => 'Schema table description may contain "quotes" and could be long—very long indeed.',
39 'description' => 'Schema table description may contain "quotes" and could be long—very long indeed. There could be "multiple quoted regions".',
41 'test_field_string' => [
45 'default' => "'\"funky default'\"",
46 'description' => 'Schema column description for string.',
48 'test_field_string_ascii' => [
49 'type' => 'varchar_ascii',
51 'description' => 'Schema column description for ASCII string.',
55 db_create_table('test_table', $table_specification);
57 // Assert that the table exists.
58 $this->assertTrue(db_table_exists('test_table'), 'The table exists.');
60 // Assert that the table comment has been set.
61 $this->checkSchemaComment($table_specification['description'], 'test_table');
63 // Assert that the column comment has been set.
64 $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
66 if (Database::getConnection()->databaseType() == 'mysql') {
67 // Make sure that varchar fields have the correct collation.
68 $columns = db_query('SHOW FULL COLUMNS FROM {test_table}');
69 foreach ($columns as $column) {
70 if ($column->Field == 'test_field_string') {
71 $string_check = ($column->Collation == 'utf8mb4_general_ci');
73 if ($column->Field == 'test_field_string_ascii') {
74 $string_ascii_check = ($column->Collation == 'ascii_general_ci');
77 $this->assertTrue(!empty($string_check), 'string field has the right collation.');
78 $this->assertTrue(!empty($string_ascii_check), 'ASCII string field has the right collation.');
81 // An insert without a value for the column 'test_table' should fail.
82 $this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
84 // Add a default value to the column.
85 db_field_set_default('test_table', 'test_field', 0);
86 // The insert should now succeed.
87 $this->assertTrue($this->tryInsert(), 'Insert with a default succeeded.');
89 // Remove the default.
90 db_field_set_no_default('test_table', 'test_field');
91 // The insert should fail again.
92 $this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
94 // Test for fake index and test for the boolean result of indexExists().
95 $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
96 $this->assertIdentical($index_exists, FALSE, 'Fake index does not exists');
98 db_add_index('test_table', 'test_field', ['test_field'], $table_specification);
99 // Test for created index and test for the boolean result of indexExists().
100 $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
101 $this->assertIdentical($index_exists, TRUE, 'Index created.');
104 db_rename_table('test_table', 'test_table2');
106 // Index should be renamed.
107 $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
108 $this->assertTrue($index_exists, 'Index was renamed.');
110 // We need the default so that we can insert after the rename.
111 db_field_set_default('test_table2', 'test_field', 0);
112 $this->assertFalse($this->tryInsert(), 'Insert into the old table failed.');
113 $this->assertTrue($this->tryInsert('test_table2'), 'Insert into the new table succeeded.');
115 // We should have successfully inserted exactly two rows.
116 $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
117 $this->assertEqual($count, 2, 'Two fields were successfully inserted.');
119 // Try to drop the table.
120 db_drop_table('test_table2');
121 $this->assertFalse(db_table_exists('test_table2'), 'The dropped table does not exist.');
123 // Recreate the table.
124 db_create_table('test_table', $table_specification);
125 db_field_set_default('test_table', 'test_field', 0);
126 db_add_field('test_table', 'test_serial', ['type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.']);
128 // Assert that the column comment has been set.
129 $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
131 // Change the new field to a serial column.
132 db_change_field('test_table', 'test_serial', 'test_serial', ['type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'], ['primary key' => ['test_serial']]);
134 // Assert that the column comment has been set.
135 $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
137 $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
138 $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
139 $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
140 $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
141 $this->assertTrue($max2 > $max1, 'The serial is monotone.');
143 $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
144 $this->assertEqual($count, 2, 'There were two rows.');
146 // Test adding a serial field to an existing table.
147 db_drop_table('test_table');
148 db_create_table('test_table', $table_specification);
149 db_field_set_default('test_table', 'test_field', 0);
150 db_add_field('test_table', 'test_serial', ['type' => 'serial', 'not null' => TRUE], ['primary key' => ['test_serial']]);
152 $this->assertPrimaryKeyColumns('test_table', ['test_serial']);
154 $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
155 $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
156 $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
157 $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
158 $this->assertTrue($max2 > $max1, 'The serial is monotone.');
160 $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
161 $this->assertEqual($count, 2, 'There were two rows.');
163 // Test adding a new column and form a composite primary key with it.
164 db_add_field('test_table', 'test_composite_primary_key', ['type' => 'int', 'not null' => TRUE, 'default' => 0], ['primary key' => ['test_serial', 'test_composite_primary_key']]);
166 $this->assertPrimaryKeyColumns('test_table', ['test_serial', 'test_composite_primary_key']);
168 // Test renaming of keys and constraints.
169 db_drop_table('test_table');
170 $table_specification = [
181 'primary key' => ['id'],
183 'test_field' => ['test_field'],
186 db_create_table('test_table', $table_specification);
188 // Tests for indexes are Database specific.
189 $db_type = Database::getConnection()->databaseType();
191 // Test for existing primary and unique keys.
194 $primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table', '__pkey');
195 $unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table', 'test_field' . '__key');
198 // SQLite does not create a standalone index for primary keys.
199 $primary_key_exists = TRUE;
200 $unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
203 $primary_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'PRIMARY');
204 $unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
207 $this->assertIdentical($primary_key_exists, TRUE, 'Primary key created.');
208 $this->assertIdentical($unique_key_exists, TRUE, 'Unique key created.');
210 db_rename_table('test_table', 'test_table2');
212 // Test for renamed primary and unique keys.
215 $renamed_primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', '__pkey');
216 $renamed_unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', 'test_field' . '__key');
219 // SQLite does not create a standalone index for primary keys.
220 $renamed_primary_key_exists = TRUE;
221 $renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
224 $renamed_primary_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'PRIMARY');
225 $renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
228 $this->assertIdentical($renamed_primary_key_exists, TRUE, 'Primary key was renamed.');
229 $this->assertIdentical($renamed_unique_key_exists, TRUE, 'Unique key was renamed.');
231 // For PostgreSQL check in addition that sequence was renamed.
232 if ($db_type == 'pgsql') {
233 // Get information about new table.
234 $info = Database::getConnection()->schema()->queryTableInformation('test_table2');
235 $sequence_name = Database::getConnection()->schema()->prefixNonTable('test_table2', 'id', 'seq');
236 $this->assertEqual($sequence_name, current($info->sequences), 'Sequence was renamed.');
239 // Use database specific data type and ensure that table is created.
240 $table_specification = [
241 'description' => 'Schema table description.',
244 'mysql_type' => 'timestamp',
245 'pgsql_type' => 'timestamp',
246 'sqlite_type' => 'datetime',
253 db_create_table('test_timestamp', $table_specification);
255 catch (\Exception $e) {
257 $this->assertTrue(db_table_exists('test_timestamp'), 'Table with database specific datatype was created.');
261 * Tests that indexes on string fields are limited to 191 characters on MySQL.
263 * @see \Drupal\Core\Database\Driver\mysql\Schema::getNormalizedIndexes()
265 public function testIndexLength() {
266 if (Database::getConnection()->databaseType() != 'mysql') {
269 $table_specification = [
275 'test_field_text' => [
279 'test_field_string_long' => [
284 'test_field_string_ascii_long' => [
285 'type' => 'varchar_ascii',
288 'test_field_string_short' => [
297 'test_field_string_long',
298 'test_field_string_ascii_long',
299 'test_field_string_short',
302 ['test_field_text', 128],
303 ['test_field_string_long', 128],
304 ['test_field_string_ascii_long', 128],
305 ['test_field_string_short', 128],
308 ['test_field_text', 200],
309 'test_field_string_long',
310 ['test_field_string_ascii_long', 200],
311 'test_field_string_short',
315 db_create_table('test_table_index_length', $table_specification);
317 $schema_object = Database::getConnection()->schema();
319 // Ensure expected exception thrown when adding index with missing info.
320 $expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index";
321 $missing_field_spec = $table_specification;
322 unset($missing_field_spec['fields']['test_field_text']);
324 $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec);
325 $this->fail('SchemaException not thrown when adding index with missing information.');
327 catch (SchemaException $e) {
328 $this->assertEqual($expected_exception_message, $e->getMessage());
331 // Add a separate index.
332 $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
333 $table_specification_with_new_index = $table_specification;
334 $table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]];
336 // Ensure that the exceptions of addIndex are thrown as expected.
339 $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
340 $this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.');
342 catch (SchemaObjectExistsException $e) {
343 $this->pass('\Drupal\Core\Database\SchemaObjectExistsException thrown when index already exists.');
347 $schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification);
348 $this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.');
350 catch (SchemaObjectDoesNotExistException $e) {
351 $this->pass('\Drupal\Core\Database\SchemaObjectDoesNotExistException thrown when index already exists.');
354 // Get index information.
355 $results = db_query('SHOW INDEX FROM {test_table_index_length}');
356 $expected_lengths = [
358 'test_field_text' => 191,
359 'test_field_string_long' => 191,
360 'test_field_string_ascii_long' => NULL,
361 'test_field_string_short' => NULL,
364 'test_field_text' => 128,
365 'test_field_string_long' => 128,
366 'test_field_string_ascii_long' => 128,
367 'test_field_string_short' => NULL,
370 'test_field_text' => 191,
371 'test_field_string_long' => 191,
372 'test_field_string_ascii_long' => 200,
373 'test_field_string_short' => NULL,
376 'test_field_text' => 191,
380 // Count the number of columns defined in the indexes.
382 foreach ($table_specification_with_new_index['indexes'] as $index) {
383 foreach ($index as $field) {
388 foreach ($results as $result) {
389 $this->assertEqual($result->Sub_part, $expected_lengths[$result->Key_name][$result->Column_name], 'Index length matches expected value.');
392 $this->assertEqual($test_count, $column_count, 'Number of tests matches expected value.');
396 * Tests inserting data into an existing table.
399 * The database table to insert data into.
402 * TRUE if the insert succeeded, FALSE otherwise.
404 public function tryInsert($table = 'test_table') {
407 ->fields(['id' => mt_rand(10, 20)])
411 catch (\Exception $e) {
417 * Checks that a table or column comment matches a given description.
419 * @param $description
420 * The asserted description.
424 * Optional column to test.
426 public function checkSchemaComment($description, $table, $column = NULL) {
427 if (method_exists(Database::getConnection()->schema(), 'getComment')) {
428 $comment = Database::getConnection()->schema()->getComment($table, $column);
429 // The schema comment truncation for mysql is different.
430 if (Database::getConnection()->databaseType() == 'mysql') {
431 $max_length = $column ? 255 : 60;
432 $description = Unicode::truncate($description, $max_length, TRUE, TRUE);
434 $this->assertEqual($comment, $description, 'The comment matches the schema description.');
439 * Tests creating unsigned columns and data integrity thereof.
441 public function testUnsignedColumns() {
442 // First create the table with just a serial column.
443 $table_name = 'unsigned_table';
445 'fields' => ['serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE]],
446 'primary key' => ['serial_column'],
448 db_create_table($table_name, $table_spec);
450 // Now set up columns for the other types.
451 $types = ['int', 'float', 'numeric'];
452 foreach ($types as $type) {
453 $column_spec = ['type' => $type, 'unsigned' => TRUE];
454 if ($type == 'numeric') {
455 $column_spec += ['precision' => 10, 'scale' => 0];
457 $column_name = $type . '_column';
458 $table_spec['fields'][$column_name] = $column_spec;
459 db_add_field($table_name, $column_name, $column_spec);
462 // Finally, check each column and try to insert invalid values into them.
463 foreach ($table_spec['fields'] as $column_name => $column_spec) {
464 $this->assertTrue(db_field_exists($table_name, $column_name), format_string('Unsigned @type column was created.', ['@type' => $column_spec['type']]));
465 $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('Unsigned @type column rejected a negative value.', ['@type' => $column_spec['type']]));
470 * Tries to insert a negative value into columns defined as unsigned.
473 * The table to insert.
474 * @param $column_name
475 * The column to insert.
478 * TRUE if the insert succeeded, FALSE otherwise.
480 public function tryUnsignedInsert($table_name, $column_name) {
482 db_insert($table_name)
483 ->fields([$column_name => -1])
487 catch (\Exception $e) {
493 * Tests adding columns to an existing table.
495 public function testSchemaAddField() {
496 // Test varchar types.
497 foreach ([1, 32, 128, 256, 512] as $length) {
503 ['not null' => FALSE],
504 ['not null' => FALSE, 'default' => '7'],
505 ['not null' => FALSE, 'default' => substr('"thing"', 0, $length)],
506 ['not null' => FALSE, 'default' => substr("\"'hing", 0, $length)],
507 ['not null' => TRUE, 'initial' => 'd'],
508 ['not null' => FALSE, 'default' => NULL],
509 ['not null' => TRUE, 'initial' => 'd', 'default' => '7'],
512 foreach ($variations as $variation) {
513 $field_spec = $variation + $base_field_spec;
514 $this->assertFieldAdditionRemoval($field_spec);
518 // Test int and float types.
519 foreach (['int', 'float'] as $type) {
520 foreach (['tiny', 'small', 'medium', 'normal', 'big'] as $size) {
526 ['not null' => FALSE],
527 ['not null' => FALSE, 'default' => 7],
528 ['not null' => TRUE, 'initial' => 1],
529 ['not null' => TRUE, 'initial' => 1, 'default' => 7],
530 ['not null' => TRUE, 'initial_from_field' => 'serial_column'],
533 foreach ($variations as $variation) {
534 $field_spec = $variation + $base_field_spec;
535 $this->assertFieldAdditionRemoval($field_spec);
540 // Test numeric types.
541 foreach ([1, 5, 10, 40, 65] as $precision) {
542 foreach ([0, 2, 10, 30] as $scale) {
543 // Skip combinations where precision is smaller than scale.
544 if ($precision <= $scale) {
551 'precision' => $precision,
554 ['not null' => FALSE],
555 ['not null' => FALSE, 'default' => 7],
556 ['not null' => TRUE, 'initial' => 1],
557 ['not null' => TRUE, 'initial' => 1, 'default' => 7],
558 ['not null' => TRUE, 'initial_from_field' => 'serial_column'],
561 foreach ($variations as $variation) {
562 $field_spec = $variation + $base_field_spec;
563 $this->assertFieldAdditionRemoval($field_spec);
570 * Asserts that a given field can be added and removed from a table.
572 * The addition test covers both defining a field of a given specification
573 * when initially creating at table and extending an existing table.
576 * The schema specification of the field.
578 protected function assertFieldAdditionRemoval($field_spec) {
579 // Try creating the field on a new table.
580 $table_name = 'test_table_' . ($this->counter++);
583 'serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE],
584 'test_field' => $field_spec,
586 'primary key' => ['serial_column'],
588 db_create_table($table_name, $table_spec);
589 $this->pass(format_string('Table %table created.', ['%table' => $table_name]));
591 // Check the characteristics of the field.
592 $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
595 db_drop_table($table_name);
597 // Try adding a field to an existing table.
598 $table_name = 'test_table_' . ($this->counter++);
601 'serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE],
603 'primary key' => ['serial_column'],
605 db_create_table($table_name, $table_spec);
606 $this->pass(format_string('Table %table created.', ['%table' => $table_name]));
608 // Insert some rows to the table to test the handling of initial values.
609 for ($i = 0; $i < 3; $i++) {
610 db_insert($table_name)
611 ->useDefaults(['serial_column'])
615 db_add_field($table_name, 'test_field', $field_spec);
616 $this->pass(format_string('Column %column created.', ['%column' => 'test_field']));
618 // Check the characteristics of the field.
619 $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
622 db_drop_field($table_name, 'test_field');
624 // Add back the field and then try to delete a field which is also a primary
626 db_add_field($table_name, 'test_field', $field_spec);
627 db_drop_field($table_name, 'serial_column');
628 db_drop_table($table_name);
632 * Asserts that a newly added field has the correct characteristics.
634 protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) {
635 // Check that the initial value has been registered.
636 if (isset($field_spec['initial'])) {
637 // There should be no row with a value different then $field_spec['initial'].
638 $count = db_select($table_name)
639 ->fields($table_name, ['serial_column'])
640 ->condition($field_name, $field_spec['initial'], '<>')
644 $this->assertEqual($count, 0, 'Initial values filled out.');
647 // Check that the initial value from another field has been registered.
648 if (isset($field_spec['initial_from_field'])) {
649 // There should be no row with a value different than
650 // $field_spec['initial_from_field'].
651 $count = db_select($table_name)
652 ->fields($table_name, ['serial_column'])
653 ->where($table_name . '.' . $field_spec['initial_from_field'] . ' <> ' . $table_name . '.' . $field_name)
657 $this->assertEqual($count, 0, 'Initial values from another field filled out.');
660 // Check that the default value has been registered.
661 if (isset($field_spec['default'])) {
662 // Try inserting a row, and check the resulting value of the new column.
663 $id = db_insert($table_name)
664 ->useDefaults(['serial_column'])
666 $field_value = db_select($table_name)
667 ->fields($table_name, [$field_name])
668 ->condition('serial_column', $id)
671 $this->assertEqual($field_value, $field_spec['default'], 'Default value registered.');
676 * Tests changing columns between types.
678 public function testSchemaChangeField() {
680 ['type' => 'int', 'size' => 'normal', 'not null' => FALSE],
681 ['type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 17],
682 ['type' => 'float', 'size' => 'normal', 'not null' => FALSE],
683 ['type' => 'float', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 7.3],
684 ['type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => FALSE],
685 ['type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => TRUE, 'initial' => 1, 'default' => 7],
688 foreach ($field_specs as $i => $old_spec) {
689 foreach ($field_specs as $j => $new_spec) {
691 // Do not change a field into itself.
694 $this->assertFieldChange($old_spec, $new_spec);
699 ['type' => 'varchar_ascii', 'length' => '255'],
700 ['type' => 'varchar', 'length' => '255'],
702 ['type' => 'blob', 'size' => 'big'],
705 foreach ($field_specs as $i => $old_spec) {
706 foreach ($field_specs as $j => $new_spec) {
708 // Do not change a field into itself.
711 // Note if the serialized data contained an object this would fail on
713 // @see https://www.drupal.org/node/1031122
714 $this->assertFieldChange($old_spec, $new_spec, serialize(['string' => "This \n has \\\\ some backslash \"*string action.\\n"]));
721 * Asserts that a field can be changed from one spec to another.
724 * The beginning field specification.
726 * The ending field specification.
728 protected function assertFieldChange($old_spec, $new_spec, $test_data = NULL) {
729 $table_name = 'test_table_' . ($this->counter++);
732 'serial_column' => ['type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE],
733 'test_field' => $old_spec,
735 'primary key' => ['serial_column'],
737 db_create_table($table_name, $table_spec);
738 $this->pass(format_string('Table %table created.', ['%table' => $table_name]));
740 // Check the characteristics of the field.
741 $this->assertFieldCharacteristics($table_name, 'test_field', $old_spec);
743 // Remove inserted rows.
744 db_truncate($table_name)->execute();
747 $id = db_insert($table_name)
748 ->fields(['test_field'], [$test_data])
753 db_change_field($table_name, 'test_field', 'test_field', $new_spec);
756 $field_value = db_select($table_name)
757 ->fields($table_name, ['test_field'])
758 ->condition('serial_column', $id)
761 $this->assertIdentical($field_value, $test_data);
764 // Check the field was changed.
765 $this->assertFieldCharacteristics($table_name, 'test_field', $new_spec);
768 db_drop_table($table_name);
772 * Tests the findTables() method.
774 public function testFindTables() {
775 // We will be testing with three tables, two of them using the default
776 // prefix and the third one with an individually specified prefix.
778 // Set up a new connection with different connection info.
779 $connection_info = Database::getConnectionInfo();
781 // Add per-table prefix to the second table.
782 $new_connection_info = $connection_info['default'];
783 $new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
784 Database::addConnectionInfo('test', 'default', $new_connection_info);
786 Database::setActiveConnection('test');
788 // Create the tables.
789 $table_specification = [
790 'description' => 'Test table.',
798 Database::getConnection()->schema()->createTable('test_1_table', $table_specification);
799 Database::getConnection()->schema()->createTable('test_2_table', $table_specification);
800 Database::getConnection()->schema()->createTable('the_third_table', $table_specification);
802 // Check the "all tables" syntax.
803 $tables = Database::getConnection()->schema()->findTables('%');
806 // The 'config' table is added by
807 // \Drupal\KernelTests\KernelTestBase::containerBuild().
810 // This table uses a per-table prefix, yet it is returned as un-prefixed.
814 $this->assertEqual($tables, $expected, 'All tables were found.');
816 // Check the restrictive syntax.
817 $tables = Database::getConnection()->schema()->findTables('test_%');
823 $this->assertEqual($tables, $expected, 'Two tables were found.');
825 // Go back to the initial connection.
826 Database::setActiveConnection('default');
830 * Tests the primary keys of a table.
832 * @param string $table_name
833 * The name of the table to check.
834 * @param array $primary_key
835 * The expected key column specifier for a table's primary key.
837 protected function assertPrimaryKeyColumns($table_name, array $primary_key = []) {
838 $db_type = Database::getConnection()->databaseType();
842 $result = Database::getConnection()->query("SHOW KEYS FROM {" . $table_name . "} WHERE Key_name = 'PRIMARY'")->fetchAllAssoc('Column_name');
843 $this->assertSame($primary_key, array_keys($result));
847 $result = Database::getConnection()->query("SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS data_type
849 JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
850 WHERE i.indrelid = '{" . $table_name . "}'::regclass AND i.indisprimary")
851 ->fetchAllAssoc('attname');
852 $this->assertSame($primary_key, array_keys($result));
856 // For SQLite we need access to the protected
857 // \Drupal\Core\Database\Driver\sqlite\Schema::introspectSchema() method
858 // because we have no other way of getting the table prefixes needed for
859 // running a straight PRAGMA query.
860 $schema_object = Database::getConnection()->schema();
861 $reflection = new \ReflectionMethod($schema_object, 'introspectSchema');
862 $reflection->setAccessible(TRUE);
864 $table_info = $reflection->invoke($schema_object, $table_name);
865 $this->assertSame($primary_key, $table_info['primary key']);