90c4848350fbe54154ca3c9316ba420272f72000
[yaffs-website] / web / core / lib / Drupal / Core / Database / Driver / sqlite / Schema.php
1 <?php
2
3 namespace Drupal\Core\Database\Driver\sqlite;
4
5 use Drupal\Component\Utility\Unicode;
6 use Drupal\Core\Database\SchemaObjectExistsException;
7 use Drupal\Core\Database\SchemaObjectDoesNotExistException;
8 use Drupal\Core\Database\Schema as DatabaseSchema;
9
10 /**
11  * @ingroup schemaapi
12  * @{
13  */
14
15 /**
16  * SQLite implementation of \Drupal\Core\Database\Schema.
17  */
18 class Schema extends DatabaseSchema {
19
20   /**
21    * Override DatabaseSchema::$defaultSchema
22    */
23   protected $defaultSchema = 'main';
24
25   public function tableExists($table) {
26     $info = $this->getPrefixInfo($table);
27
28     // Don't use {} around sqlite_master table.
29     return (bool) $this->connection->query('SELECT 1 FROM ' . $info['schema'] . '.sqlite_master WHERE type = :type AND name = :name', [':type' => 'table', ':name' => $info['table']])->fetchField();
30   }
31
32   public function fieldExists($table, $column) {
33     $schema = $this->introspectSchema($table);
34     return !empty($schema['fields'][$column]);
35   }
36
37   /**
38    * Generate SQL to create a new table from a Drupal schema definition.
39    *
40    * @param $name
41    *   The name of the table to create.
42    * @param $table
43    *   A Schema API table definition array.
44    * @return
45    *   An array of SQL statements to create the table.
46    */
47   public function createTableSql($name, $table) {
48     $sql = [];
49     $sql[] = "CREATE TABLE {" . $name . "} (\n" . $this->createColumnsSql($name, $table) . "\n)\n";
50     return array_merge($sql, $this->createIndexSql($name, $table));
51   }
52
53   /**
54    * Build the SQL expression for indexes.
55    */
56   protected function createIndexSql($tablename, $schema) {
57     $sql = [];
58     $info = $this->getPrefixInfo($tablename);
59     if (!empty($schema['unique keys'])) {
60       foreach ($schema['unique keys'] as $key => $fields) {
61         $sql[] = 'CREATE UNIQUE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . ")\n";
62       }
63     }
64     if (!empty($schema['indexes'])) {
65       foreach ($schema['indexes'] as $key => $fields) {
66         $sql[] = 'CREATE INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $key . ' ON ' . $info['table'] . ' (' . $this->createKeySql($fields) . ")\n";
67       }
68     }
69     return $sql;
70   }
71
72   /**
73    * Build the SQL expression for creating columns.
74    */
75   protected function createColumnsSql($tablename, $schema) {
76     $sql_array = [];
77
78     // Add the SQL statement for each field.
79     foreach ($schema['fields'] as $name => $field) {
80       if (isset($field['type']) && $field['type'] == 'serial') {
81         if (isset($schema['primary key']) && ($key = array_search($name, $schema['primary key'])) !== FALSE) {
82           unset($schema['primary key'][$key]);
83         }
84       }
85       $sql_array[] = $this->createFieldSql($name, $this->processField($field));
86     }
87
88     // Process keys.
89     if (!empty($schema['primary key'])) {
90       $sql_array[] = " PRIMARY KEY (" . $this->createKeySql($schema['primary key']) . ")";
91     }
92
93     return implode(", \n", $sql_array);
94   }
95
96   /**
97    * Build the SQL expression for keys.
98    */
99   protected function createKeySql($fields) {
100     $return = [];
101     foreach ($fields as $field) {
102       if (is_array($field)) {
103         $return[] = $field[0];
104       }
105       else {
106         $return[] = $field;
107       }
108     }
109     return implode(', ', $return);
110   }
111
112   /**
113    * Set database-engine specific properties for a field.
114    *
115    * @param $field
116    *   A field description array, as specified in the schema documentation.
117    */
118   protected function processField($field) {
119     if (!isset($field['size'])) {
120       $field['size'] = 'normal';
121     }
122
123     // Set the correct database-engine specific datatype.
124     // In case one is already provided, force it to uppercase.
125     if (isset($field['sqlite_type'])) {
126       $field['sqlite_type'] = Unicode::strtoupper($field['sqlite_type']);
127     }
128     else {
129       $map = $this->getFieldTypeMap();
130       $field['sqlite_type'] = $map[$field['type'] . ':' . $field['size']];
131
132       // Numeric fields with a specified scale have to be stored as floats.
133       if ($field['sqlite_type'] === 'NUMERIC' && isset($field['scale'])) {
134         $field['sqlite_type'] = 'FLOAT';
135       }
136     }
137
138     if (isset($field['type']) && $field['type'] == 'serial') {
139       $field['auto_increment'] = TRUE;
140     }
141
142     return $field;
143   }
144
145   /**
146    * Create an SQL string for a field to be used in table creation or alteration.
147    *
148    * Before passing a field out of a schema definition into this function it has
149    * to be processed by db_processField().
150    *
151    * @param $name
152    *    Name of the field.
153    * @param $spec
154    *    The field specification, as per the schema data structure format.
155    */
156   protected function createFieldSql($name, $spec) {
157     if (!empty($spec['auto_increment'])) {
158       $sql = $name . " INTEGER PRIMARY KEY AUTOINCREMENT";
159       if (!empty($spec['unsigned'])) {
160         $sql .= ' CHECK (' . $name . '>= 0)';
161       }
162     }
163     else {
164       $sql = $name . ' ' . $spec['sqlite_type'];
165
166       if (in_array($spec['sqlite_type'], ['VARCHAR', 'TEXT'])) {
167         if (isset($spec['length'])) {
168           $sql .= '(' . $spec['length'] . ')';
169         }
170
171         if (isset($spec['binary']) && $spec['binary'] === FALSE) {
172           $sql .= ' COLLATE NOCASE_UTF8';
173         }
174       }
175
176       if (isset($spec['not null'])) {
177         if ($spec['not null']) {
178           $sql .= ' NOT NULL';
179         }
180         else {
181           $sql .= ' NULL';
182         }
183       }
184
185       if (!empty($spec['unsigned'])) {
186         $sql .= ' CHECK (' . $name . '>= 0)';
187       }
188
189       if (isset($spec['default'])) {
190         if (is_string($spec['default'])) {
191           $spec['default'] = $this->connection->quote($spec['default']);
192         }
193         $sql .= ' DEFAULT ' . $spec['default'];
194       }
195
196       if (empty($spec['not null']) && !isset($spec['default'])) {
197         $sql .= ' DEFAULT NULL';
198       }
199     }
200     return $sql;
201   }
202
203   /**
204    * This maps a generic data type in combination with its data size
205    * to the engine-specific data type.
206    */
207   public function getFieldTypeMap() {
208     // Put :normal last so it gets preserved by array_flip. This makes
209     // it much easier for modules (such as schema.module) to map
210     // database types back into schema types.
211     // $map does not use drupal_static as its value never changes.
212     static $map = [
213       'varchar_ascii:normal' => 'VARCHAR',
214
215       'varchar:normal'  => 'VARCHAR',
216       'char:normal'     => 'CHAR',
217
218       'text:tiny'       => 'TEXT',
219       'text:small'      => 'TEXT',
220       'text:medium'     => 'TEXT',
221       'text:big'        => 'TEXT',
222       'text:normal'     => 'TEXT',
223
224       'serial:tiny'     => 'INTEGER',
225       'serial:small'    => 'INTEGER',
226       'serial:medium'   => 'INTEGER',
227       'serial:big'      => 'INTEGER',
228       'serial:normal'   => 'INTEGER',
229
230       'int:tiny'        => 'INTEGER',
231       'int:small'       => 'INTEGER',
232       'int:medium'      => 'INTEGER',
233       'int:big'         => 'INTEGER',
234       'int:normal'      => 'INTEGER',
235
236       'float:tiny'      => 'FLOAT',
237       'float:small'     => 'FLOAT',
238       'float:medium'    => 'FLOAT',
239       'float:big'       => 'FLOAT',
240       'float:normal'    => 'FLOAT',
241
242       'numeric:normal'  => 'NUMERIC',
243
244       'blob:big'        => 'BLOB',
245       'blob:normal'     => 'BLOB',
246     ];
247     return $map;
248   }
249
250   public function renameTable($table, $new_name) {
251     if (!$this->tableExists($table)) {
252       throw new SchemaObjectDoesNotExistException(t("Cannot rename @table to @table_new: table @table doesn't exist.", ['@table' => $table, '@table_new' => $new_name]));
253     }
254     if ($this->tableExists($new_name)) {
255       throw new SchemaObjectExistsException(t("Cannot rename @table to @table_new: table @table_new already exists.", ['@table' => $table, '@table_new' => $new_name]));
256     }
257
258     $schema = $this->introspectSchema($table);
259
260     // SQLite doesn't allow you to rename tables outside of the current
261     // database. So the syntax '... RENAME TO database.table' would fail.
262     // So we must determine the full table name here rather than surrounding
263     // the table with curly braces in case the db_prefix contains a reference
264     // to a database outside of our existing database.
265     $info = $this->getPrefixInfo($new_name);
266     $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO ' . $info['table']);
267
268     // Drop the indexes, there is no RENAME INDEX command in SQLite.
269     if (!empty($schema['unique keys'])) {
270       foreach ($schema['unique keys'] as $key => $fields) {
271         $this->dropIndex($table, $key);
272       }
273     }
274     if (!empty($schema['indexes'])) {
275       foreach ($schema['indexes'] as $index => $fields) {
276         $this->dropIndex($table, $index);
277       }
278     }
279
280     // Recreate the indexes.
281     $statements = $this->createIndexSql($new_name, $schema);
282     foreach ($statements as $statement) {
283       $this->connection->query($statement);
284     }
285   }
286
287   public function dropTable($table) {
288     if (!$this->tableExists($table)) {
289       return FALSE;
290     }
291     $this->connection->tableDropped = TRUE;
292     $this->connection->query('DROP TABLE {' . $table . '}');
293     return TRUE;
294   }
295
296   public function addField($table, $field, $specification, $keys_new = []) {
297     if (!$this->tableExists($table)) {
298       throw new SchemaObjectDoesNotExistException(t("Cannot add field @table.@field: table doesn't exist.", ['@field' => $field, '@table' => $table]));
299     }
300     if ($this->fieldExists($table, $field)) {
301       throw new SchemaObjectExistsException(t("Cannot add field @table.@field: field already exists.", ['@field' => $field, '@table' => $table]));
302     }
303
304     // SQLite doesn't have a full-featured ALTER TABLE statement. It only
305     // supports adding new fields to a table, in some simple cases. In most
306     // cases, we have to create a new table and copy the data over.
307     if (empty($keys_new) && (empty($specification['not null']) || isset($specification['default']))) {
308       // When we don't have to create new keys and we are not creating a
309       // NOT NULL column without a default value, we can use the quicker version.
310       $query = 'ALTER TABLE {' . $table . '} ADD ' . $this->createFieldSql($field, $this->processField($specification));
311       $this->connection->query($query);
312
313       // Apply the initial value if set.
314       if (isset($specification['initial'])) {
315         $this->connection->update($table)
316           ->fields([$field => $specification['initial']])
317           ->execute();
318       }
319       if (isset($specification['initial_from_field'])) {
320         $this->connection->update($table)
321           ->expression($field, $specification['initial_from_field'])
322           ->execute();
323       }
324     }
325     else {
326       // We cannot add the field directly. Use the slower table alteration
327       // method, starting from the old schema.
328       $old_schema = $this->introspectSchema($table);
329       $new_schema = $old_schema;
330
331       // Add the new field.
332       $new_schema['fields'][$field] = $specification;
333
334       // Build the mapping between the old fields and the new fields.
335       $mapping = [];
336       if (isset($specification['initial'])) {
337         // If we have a initial value, copy it over.
338         $mapping[$field] = [
339           'expression' => ':newfieldinitial',
340           'arguments' => [':newfieldinitial' => $specification['initial']],
341         ];
342       }
343       elseif (isset($specification['initial_from_field'])) {
344         // If we have a initial value, copy it over.
345         $mapping[$field] = [
346           'expression' => $specification['initial_from_field'],
347           'arguments' => [],
348         ];
349       }
350       else {
351         // Else use the default of the field.
352         $mapping[$field] = NULL;
353       }
354
355       // Add the new indexes.
356       $new_schema = array_merge($new_schema, $keys_new);
357
358       $this->alterTable($table, $old_schema, $new_schema, $mapping);
359     }
360   }
361
362   /**
363    * Create a table with a new schema containing the old content.
364    *
365    * As SQLite does not support ALTER TABLE (with a few exceptions) it is
366    * necessary to create a new table and copy over the old content.
367    *
368    * @param $table
369    *   Name of the table to be altered.
370    * @param $old_schema
371    *   The old schema array for the table.
372    * @param $new_schema
373    *   The new schema array for the table.
374    * @param $mapping
375    *   An optional mapping between the fields of the old specification and the
376    *   fields of the new specification. An associative array, whose keys are
377    *   the fields of the new table, and values can take two possible forms:
378    *     - a simple string, which is interpreted as the name of a field of the
379    *       old table,
380    *     - an associative array with two keys 'expression' and 'arguments',
381    *       that will be used as an expression field.
382    */
383   protected function alterTable($table, $old_schema, $new_schema, array $mapping = []) {
384     $i = 0;
385     do {
386       $new_table = $table . '_' . $i++;
387     } while ($this->tableExists($new_table));
388
389     $this->createTable($new_table, $new_schema);
390
391     // Build a SQL query to migrate the data from the old table to the new.
392     $select = $this->connection->select($table);
393
394     // Complete the mapping.
395     $possible_keys = array_keys($new_schema['fields']);
396     $mapping += array_combine($possible_keys, $possible_keys);
397
398     // Now add the fields.
399     foreach ($mapping as $field_alias => $field_source) {
400       // Just ignore this field (ie. use it's default value).
401       if (!isset($field_source)) {
402         continue;
403       }
404
405       if (is_array($field_source)) {
406         $select->addExpression($field_source['expression'], $field_alias, $field_source['arguments']);
407       }
408       else {
409         $select->addField($table, $field_source, $field_alias);
410       }
411     }
412
413     // Execute the data migration query.
414     $this->connection->insert($new_table)
415       ->from($select)
416       ->execute();
417
418     $old_count = $this->connection->query('SELECT COUNT(*) FROM {' . $table . '}')->fetchField();
419     $new_count = $this->connection->query('SELECT COUNT(*) FROM {' . $new_table . '}')->fetchField();
420     if ($old_count == $new_count) {
421       $this->dropTable($table);
422       $this->renameTable($new_table, $table);
423     }
424   }
425
426   /**
427    * Find out the schema of a table.
428    *
429    * This function uses introspection methods provided by the database to
430    * create a schema array. This is useful, for example, during update when
431    * the old schema is not available.
432    *
433    * @param $table
434    *   Name of the table.
435    *
436    * @return
437    *   An array representing the schema, from drupal_get_schema().
438    *
439    * @throws \Exception
440    *   If a column of the table could not be parsed.
441    */
442   protected function introspectSchema($table) {
443     $mapped_fields = array_flip($this->getFieldTypeMap());
444     $schema = [
445       'fields' => [],
446       'primary key' => [],
447       'unique keys' => [],
448       'indexes' => [],
449     ];
450
451     $info = $this->getPrefixInfo($table);
452     $result = $this->connection->query('PRAGMA ' . $info['schema'] . '.table_info(' . $info['table'] . ')');
453     foreach ($result as $row) {
454       if (preg_match('/^([^(]+)\((.*)\)$/', $row->type, $matches)) {
455         $type = $matches[1];
456         $length = $matches[2];
457       }
458       else {
459         $type = $row->type;
460         $length = NULL;
461       }
462       if (isset($mapped_fields[$type])) {
463         list($type, $size) = explode(':', $mapped_fields[$type]);
464         $schema['fields'][$row->name] = [
465           'type' => $type,
466           'size' => $size,
467           'not null' => !empty($row->notnull),
468           'default' => trim($row->dflt_value, "'"),
469         ];
470         if ($length) {
471           $schema['fields'][$row->name]['length'] = $length;
472         }
473         if ($row->pk) {
474           $schema['primary key'][] = $row->name;
475         }
476       }
477       else {
478         throw new \Exception("Unable to parse the column type " . $row->type);
479       }
480     }
481     $indexes = [];
482     $result = $this->connection->query('PRAGMA ' . $info['schema'] . '.index_list(' . $info['table'] . ')');
483     foreach ($result as $row) {
484       if (strpos($row->name, 'sqlite_autoindex_') !== 0) {
485         $indexes[] = [
486           'schema_key' => $row->unique ? 'unique keys' : 'indexes',
487           'name' => $row->name,
488         ];
489       }
490     }
491     foreach ($indexes as $index) {
492       $name = $index['name'];
493       // Get index name without prefix.
494       $index_name = substr($name, strlen($info['table']) + 1);
495       $result = $this->connection->query('PRAGMA ' . $info['schema'] . '.index_info(' . $name . ')');
496       foreach ($result as $row) {
497         $schema[$index['schema_key']][$index_name][] = $row->name;
498       }
499     }
500     return $schema;
501   }
502
503   public function dropField($table, $field) {
504     if (!$this->fieldExists($table, $field)) {
505       return FALSE;
506     }
507
508     $old_schema = $this->introspectSchema($table);
509     $new_schema = $old_schema;
510
511     unset($new_schema['fields'][$field]);
512
513     // Handle possible primary key changes.
514     if (isset($new_schema['primary key']) && ($key = array_search($field, $new_schema['primary key'])) !== FALSE) {
515       unset($new_schema['primary key'][$key]);
516     }
517
518     // Handle possible index changes.
519     foreach ($new_schema['indexes'] as $index => $fields) {
520       foreach ($fields as $key => $field_name) {
521         if ($field_name == $field) {
522           unset($new_schema['indexes'][$index][$key]);
523         }
524       }
525       // If this index has no more fields then remove it.
526       if (empty($new_schema['indexes'][$index])) {
527         unset($new_schema['indexes'][$index]);
528       }
529     }
530     $this->alterTable($table, $old_schema, $new_schema);
531     return TRUE;
532   }
533
534   public function changeField($table, $field, $field_new, $spec, $keys_new = []) {
535     if (!$this->fieldExists($table, $field)) {
536       throw new SchemaObjectDoesNotExistException(t("Cannot change the definition of field @table.@name: field doesn't exist.", ['@table' => $table, '@name' => $field]));
537     }
538     if (($field != $field_new) && $this->fieldExists($table, $field_new)) {
539       throw new SchemaObjectExistsException(t("Cannot rename field @table.@name to @name_new: target field already exists.", ['@table' => $table, '@name' => $field, '@name_new' => $field_new]));
540     }
541
542     $old_schema = $this->introspectSchema($table);
543     $new_schema = $old_schema;
544
545     // Map the old field to the new field.
546     if ($field != $field_new) {
547       $mapping[$field_new] = $field;
548     }
549     else {
550       $mapping = [];
551     }
552
553     // Remove the previous definition and swap in the new one.
554     unset($new_schema['fields'][$field]);
555     $new_schema['fields'][$field_new] = $spec;
556
557     // Map the former indexes to the new column name.
558     $new_schema['primary key'] = $this->mapKeyDefinition($new_schema['primary key'], $mapping);
559     foreach (['unique keys', 'indexes'] as $k) {
560       foreach ($new_schema[$k] as &$key_definition) {
561         $key_definition = $this->mapKeyDefinition($key_definition, $mapping);
562       }
563     }
564
565     // Add in the keys from $keys_new.
566     if (isset($keys_new['primary key'])) {
567       $new_schema['primary key'] = $keys_new['primary key'];
568     }
569     foreach (['unique keys', 'indexes'] as $k) {
570       if (!empty($keys_new[$k])) {
571         $new_schema[$k] = $keys_new[$k] + $new_schema[$k];
572       }
573     }
574
575     $this->alterTable($table, $old_schema, $new_schema, $mapping);
576   }
577
578   /**
579    * Utility method: rename columns in an index definition according to a new mapping.
580    *
581    * @param $key_definition
582    *   The key definition.
583    * @param $mapping
584    *   The new mapping.
585    */
586   protected function mapKeyDefinition(array $key_definition, array $mapping) {
587     foreach ($key_definition as &$field) {
588       // The key definition can be an array($field, $length).
589       if (is_array($field)) {
590         $field = &$field[0];
591       }
592       if (isset($mapping[$field])) {
593         $field = $mapping[$field];
594       }
595     }
596     return $key_definition;
597   }
598
599   /**
600    * {@inheritdoc}
601    */
602   public function addIndex($table, $name, $fields, array $spec) {
603     if (!$this->tableExists($table)) {
604       throw new SchemaObjectDoesNotExistException(t("Cannot add index @name to table @table: table doesn't exist.", ['@table' => $table, '@name' => $name]));
605     }
606     if ($this->indexExists($table, $name)) {
607       throw new SchemaObjectExistsException(t("Cannot add index @name to table @table: index already exists.", ['@table' => $table, '@name' => $name]));
608     }
609
610     $schema['indexes'][$name] = $fields;
611     $statements = $this->createIndexSql($table, $schema);
612     foreach ($statements as $statement) {
613       $this->connection->query($statement);
614     }
615   }
616
617   public function indexExists($table, $name) {
618     $info = $this->getPrefixInfo($table);
619
620     return $this->connection->query('PRAGMA ' . $info['schema'] . '.index_info(' . $info['table'] . '_' . $name . ')')->fetchField() != '';
621   }
622
623   public function dropIndex($table, $name) {
624     if (!$this->indexExists($table, $name)) {
625       return FALSE;
626     }
627
628     $info = $this->getPrefixInfo($table);
629
630     $this->connection->query('DROP INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $name);
631     return TRUE;
632   }
633
634   public function addUniqueKey($table, $name, $fields) {
635     if (!$this->tableExists($table)) {
636       throw new SchemaObjectDoesNotExistException(t("Cannot add unique key @name to table @table: table doesn't exist.", ['@table' => $table, '@name' => $name]));
637     }
638     if ($this->indexExists($table, $name)) {
639       throw new SchemaObjectExistsException(t("Cannot add unique key @name to table @table: unique key already exists.", ['@table' => $table, '@name' => $name]));
640     }
641
642     $schema['unique keys'][$name] = $fields;
643     $statements = $this->createIndexSql($table, $schema);
644     foreach ($statements as $statement) {
645       $this->connection->query($statement);
646     }
647   }
648
649   public function dropUniqueKey($table, $name) {
650     if (!$this->indexExists($table, $name)) {
651       return FALSE;
652     }
653
654     $info = $this->getPrefixInfo($table);
655
656     $this->connection->query('DROP INDEX ' . $info['schema'] . '.' . $info['table'] . '_' . $name);
657     return TRUE;
658   }
659
660   public function addPrimaryKey($table, $fields) {
661     if (!$this->tableExists($table)) {
662       throw new SchemaObjectDoesNotExistException(t("Cannot add primary key to table @table: table doesn't exist.", ['@table' => $table]));
663     }
664
665     $old_schema = $this->introspectSchema($table);
666     $new_schema = $old_schema;
667
668     if (!empty($new_schema['primary key'])) {
669       throw new SchemaObjectExistsException(t("Cannot add primary key to table @table: primary key already exists.", ['@table' => $table]));
670     }
671
672     $new_schema['primary key'] = $fields;
673     $this->alterTable($table, $old_schema, $new_schema);
674   }
675
676   public function dropPrimaryKey($table) {
677     $old_schema = $this->introspectSchema($table);
678     $new_schema = $old_schema;
679
680     if (empty($new_schema['primary key'])) {
681       return FALSE;
682     }
683
684     unset($new_schema['primary key']);
685     $this->alterTable($table, $old_schema, $new_schema);
686     return TRUE;
687   }
688
689   public function fieldSetDefault($table, $field, $default) {
690     if (!$this->fieldExists($table, $field)) {
691       throw new SchemaObjectDoesNotExistException(t("Cannot set default value of field @table.@field: field doesn't exist.", ['@table' => $table, '@field' => $field]));
692     }
693
694     $old_schema = $this->introspectSchema($table);
695     $new_schema = $old_schema;
696
697     $new_schema['fields'][$field]['default'] = $default;
698     $this->alterTable($table, $old_schema, $new_schema);
699   }
700
701   public function fieldSetNoDefault($table, $field) {
702     if (!$this->fieldExists($table, $field)) {
703       throw new SchemaObjectDoesNotExistException(t("Cannot remove default value of field @table.@field: field doesn't exist.", ['@table' => $table, '@field' => $field]));
704     }
705
706     $old_schema = $this->introspectSchema($table);
707     $new_schema = $old_schema;
708
709     unset($new_schema['fields'][$field]['default']);
710     $this->alterTable($table, $old_schema, $new_schema);
711   }
712
713   /**
714    * {@inheritdoc}
715    */
716   public function findTables($table_expression) {
717     $tables = [];
718
719     // The SQLite implementation doesn't need to use the same filtering strategy
720     // as the parent one because individually prefixed tables live in their own
721     // schema (database), which means that neither the main database nor any
722     // attached one will contain a prefixed table name, so we just need to loop
723     // over all known schemas and filter by the user-supplied table expression.
724     $attached_dbs = $this->connection->getAttachedDatabases();
725     foreach ($attached_dbs as $schema) {
726       // Can't use query placeholders for the schema because the query would
727       // have to be :prefixsqlite_master, which does not work. We also need to
728       // ignore the internal SQLite tables.
729       $result = $this->connection->query("SELECT name FROM " . $schema . ".sqlite_master WHERE type = :type AND name LIKE :table_name AND name NOT LIKE :pattern", [
730         ':type' => 'table',
731         ':table_name' => $table_expression,
732         ':pattern' => 'sqlite_%',
733       ]);
734       $tables += $result->fetchAllKeyed(0, 0);
735     }
736
737     return $tables;
738   }
739
740 }