diff options
Diffstat (limited to 'vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php')
-rw-r--r-- | vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php | 548 |
1 files changed, 548 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php new file mode 100644 index 0000000..249be13 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php | |||
@@ -0,0 +1,548 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Schema; | ||
6 | |||
7 | use Doctrine\DBAL\Exception; | ||
8 | use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; | ||
9 | use Doctrine\DBAL\Platforms\MariaDBPlatform; | ||
10 | use Doctrine\DBAL\Platforms\MySQL; | ||
11 | use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider\CachingCharsetMetadataProvider; | ||
12 | use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider\ConnectionCharsetMetadataProvider; | ||
13 | use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider\CachingCollationMetadataProvider; | ||
14 | use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider\ConnectionCollationMetadataProvider; | ||
15 | use Doctrine\DBAL\Platforms\MySQL\DefaultTableOptions; | ||
16 | use Doctrine\DBAL\Result; | ||
17 | use Doctrine\DBAL\Types\Type; | ||
18 | |||
19 | use function array_change_key_case; | ||
20 | use function assert; | ||
21 | use function explode; | ||
22 | use function implode; | ||
23 | use function is_string; | ||
24 | use function preg_match; | ||
25 | use function str_contains; | ||
26 | use function strtok; | ||
27 | use function strtolower; | ||
28 | use function strtr; | ||
29 | |||
30 | use const CASE_LOWER; | ||
31 | |||
32 | /** | ||
33 | * Schema manager for the MySQL RDBMS. | ||
34 | * | ||
35 | * @extends AbstractSchemaManager<AbstractMySQLPlatform> | ||
36 | */ | ||
37 | class MySQLSchemaManager extends AbstractSchemaManager | ||
38 | { | ||
39 | /** @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences */ | ||
40 | private const MARIADB_ESCAPE_SEQUENCES = [ | ||
41 | '\\0' => "\0", | ||
42 | "\\'" => "'", | ||
43 | '\\"' => '"', | ||
44 | '\\b' => "\b", | ||
45 | '\\n' => "\n", | ||
46 | '\\r' => "\r", | ||
47 | '\\t' => "\t", | ||
48 | '\\Z' => "\x1a", | ||
49 | '\\\\' => '\\', | ||
50 | '\\%' => '%', | ||
51 | '\\_' => '_', | ||
52 | |||
53 | // Internally, MariaDB escapes single quotes using the standard syntax | ||
54 | "''" => "'", | ||
55 | ]; | ||
56 | |||
57 | private ?DefaultTableOptions $defaultTableOptions = null; | ||
58 | |||
59 | /** | ||
60 | * {@inheritDoc} | ||
61 | */ | ||
62 | protected function _getPortableTableDefinition(array $table): string | ||
63 | { | ||
64 | return $table['TABLE_NAME']; | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * {@inheritDoc} | ||
69 | */ | ||
70 | protected function _getPortableViewDefinition(array $view): View | ||
71 | { | ||
72 | return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * {@inheritDoc} | ||
77 | */ | ||
78 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
79 | { | ||
80 | foreach ($tableIndexes as $k => $v) { | ||
81 | $v = array_change_key_case($v, CASE_LOWER); | ||
82 | if ($v['key_name'] === 'PRIMARY') { | ||
83 | $v['primary'] = true; | ||
84 | } else { | ||
85 | $v['primary'] = false; | ||
86 | } | ||
87 | |||
88 | if (str_contains($v['index_type'], 'FULLTEXT')) { | ||
89 | $v['flags'] = ['FULLTEXT']; | ||
90 | } elseif (str_contains($v['index_type'], 'SPATIAL')) { | ||
91 | $v['flags'] = ['SPATIAL']; | ||
92 | } | ||
93 | |||
94 | // Ignore prohibited prefix `length` for spatial index | ||
95 | if (! str_contains($v['index_type'], 'SPATIAL')) { | ||
96 | $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; | ||
97 | } | ||
98 | |||
99 | $tableIndexes[$k] = $v; | ||
100 | } | ||
101 | |||
102 | return parent::_getPortableTableIndexesList($tableIndexes, $tableName); | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * {@inheritDoc} | ||
107 | */ | ||
108 | protected function _getPortableDatabaseDefinition(array $database): string | ||
109 | { | ||
110 | return $database['Database']; | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * {@inheritDoc} | ||
115 | */ | ||
116 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
117 | { | ||
118 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); | ||
119 | |||
120 | $dbType = strtolower($tableColumn['type']); | ||
121 | $dbType = strtok($dbType, '(), '); | ||
122 | assert(is_string($dbType)); | ||
123 | |||
124 | $length = $tableColumn['length'] ?? strtok('(), '); | ||
125 | |||
126 | $fixed = false; | ||
127 | |||
128 | if (! isset($tableColumn['name'])) { | ||
129 | $tableColumn['name'] = ''; | ||
130 | } | ||
131 | |||
132 | $scale = 0; | ||
133 | $precision = null; | ||
134 | |||
135 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
136 | |||
137 | switch ($dbType) { | ||
138 | case 'char': | ||
139 | case 'binary': | ||
140 | $fixed = true; | ||
141 | break; | ||
142 | |||
143 | case 'float': | ||
144 | case 'double': | ||
145 | case 'real': | ||
146 | case 'numeric': | ||
147 | case 'decimal': | ||
148 | if ( | ||
149 | preg_match( | ||
150 | '([A-Za-z]+\(([0-9]+),([0-9]+)\))', | ||
151 | $tableColumn['type'], | ||
152 | $match, | ||
153 | ) === 1 | ||
154 | ) { | ||
155 | $precision = (int) $match[1]; | ||
156 | $scale = (int) $match[2]; | ||
157 | $length = null; | ||
158 | } | ||
159 | |||
160 | break; | ||
161 | |||
162 | case 'tinytext': | ||
163 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYTEXT; | ||
164 | break; | ||
165 | |||
166 | case 'text': | ||
167 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_TEXT; | ||
168 | break; | ||
169 | |||
170 | case 'mediumtext': | ||
171 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT; | ||
172 | break; | ||
173 | |||
174 | case 'tinyblob': | ||
175 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYBLOB; | ||
176 | break; | ||
177 | |||
178 | case 'blob': | ||
179 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_BLOB; | ||
180 | break; | ||
181 | |||
182 | case 'mediumblob': | ||
183 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB; | ||
184 | break; | ||
185 | |||
186 | case 'tinyint': | ||
187 | case 'smallint': | ||
188 | case 'mediumint': | ||
189 | case 'int': | ||
190 | case 'integer': | ||
191 | case 'bigint': | ||
192 | case 'year': | ||
193 | $length = null; | ||
194 | break; | ||
195 | } | ||
196 | |||
197 | if ($this->platform instanceof MariaDBPlatform) { | ||
198 | $columnDefault = $this->getMariaDBColumnDefault($this->platform, $tableColumn['default']); | ||
199 | } else { | ||
200 | $columnDefault = $tableColumn['default']; | ||
201 | } | ||
202 | |||
203 | $options = [ | ||
204 | 'length' => $length !== null ? (int) $length : null, | ||
205 | 'unsigned' => str_contains($tableColumn['type'], 'unsigned'), | ||
206 | 'fixed' => $fixed, | ||
207 | 'default' => $columnDefault, | ||
208 | 'notnull' => $tableColumn['null'] !== 'YES', | ||
209 | 'scale' => $scale, | ||
210 | 'precision' => $precision, | ||
211 | 'autoincrement' => str_contains($tableColumn['extra'], 'auto_increment'), | ||
212 | ]; | ||
213 | |||
214 | if (isset($tableColumn['comment'])) { | ||
215 | $options['comment'] = $tableColumn['comment']; | ||
216 | } | ||
217 | |||
218 | $column = new Column($tableColumn['field'], Type::getType($type), $options); | ||
219 | |||
220 | if (isset($tableColumn['characterset'])) { | ||
221 | $column->setPlatformOption('charset', $tableColumn['characterset']); | ||
222 | } | ||
223 | |||
224 | if (isset($tableColumn['collation'])) { | ||
225 | $column->setPlatformOption('collation', $tableColumn['collation']); | ||
226 | } | ||
227 | |||
228 | return $column; | ||
229 | } | ||
230 | |||
231 | /** | ||
232 | * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. | ||
233 | * | ||
234 | * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted | ||
235 | * to distinguish them from expressions (see MDEV-10134). | ||
236 | * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema | ||
237 | * as current_timestamp(), currdate(), currtime() | ||
238 | * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have | ||
239 | * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) | ||
240 | * - \' is always stored as '' in information_schema (normalized) | ||
241 | * | ||
242 | * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ | ||
243 | * @link https://jira.mariadb.org/browse/MDEV-13132 | ||
244 | * | ||
245 | * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 | ||
246 | */ | ||
247 | private function getMariaDBColumnDefault(MariaDBPlatform $platform, ?string $columnDefault): ?string | ||
248 | { | ||
249 | if ($columnDefault === 'NULL' || $columnDefault === null) { | ||
250 | return null; | ||
251 | } | ||
252 | |||
253 | if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) { | ||
254 | return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); | ||
255 | } | ||
256 | |||
257 | return match ($columnDefault) { | ||
258 | 'current_timestamp()' => $platform->getCurrentTimestampSQL(), | ||
259 | 'curdate()' => $platform->getCurrentDateSQL(), | ||
260 | 'curtime()' => $platform->getCurrentTimeSQL(), | ||
261 | default => $columnDefault, | ||
262 | }; | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * {@inheritDoc} | ||
267 | */ | ||
268 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
269 | { | ||
270 | $list = []; | ||
271 | foreach ($tableForeignKeys as $value) { | ||
272 | $value = array_change_key_case($value, CASE_LOWER); | ||
273 | if (! isset($list[$value['constraint_name']])) { | ||
274 | if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') { | ||
275 | $value['delete_rule'] = null; | ||
276 | } | ||
277 | |||
278 | if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') { | ||
279 | $value['update_rule'] = null; | ||
280 | } | ||
281 | |||
282 | $list[$value['constraint_name']] = [ | ||
283 | 'name' => $value['constraint_name'], | ||
284 | 'local' => [], | ||
285 | 'foreign' => [], | ||
286 | 'foreignTable' => $value['referenced_table_name'], | ||
287 | 'onDelete' => $value['delete_rule'], | ||
288 | 'onUpdate' => $value['update_rule'], | ||
289 | ]; | ||
290 | } | ||
291 | |||
292 | $list[$value['constraint_name']]['local'][] = $value['column_name']; | ||
293 | $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; | ||
294 | } | ||
295 | |||
296 | return parent::_getPortableTableForeignKeysList($list); | ||
297 | } | ||
298 | |||
299 | /** | ||
300 | * {@inheritDoc} | ||
301 | */ | ||
302 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
303 | { | ||
304 | return new ForeignKeyConstraint( | ||
305 | $tableForeignKey['local'], | ||
306 | $tableForeignKey['foreignTable'], | ||
307 | $tableForeignKey['foreign'], | ||
308 | $tableForeignKey['name'], | ||
309 | [ | ||
310 | 'onDelete' => $tableForeignKey['onDelete'], | ||
311 | 'onUpdate' => $tableForeignKey['onUpdate'], | ||
312 | ], | ||
313 | ); | ||
314 | } | ||
315 | |||
316 | /** @throws Exception */ | ||
317 | public function createComparator(): Comparator | ||
318 | { | ||
319 | return new MySQL\Comparator( | ||
320 | $this->platform, | ||
321 | new CachingCharsetMetadataProvider( | ||
322 | new ConnectionCharsetMetadataProvider($this->connection), | ||
323 | ), | ||
324 | new CachingCollationMetadataProvider( | ||
325 | new ConnectionCollationMetadataProvider($this->connection), | ||
326 | ), | ||
327 | $this->getDefaultTableOptions(), | ||
328 | ); | ||
329 | } | ||
330 | |||
331 | protected function selectTableNames(string $databaseName): Result | ||
332 | { | ||
333 | $sql = <<<'SQL' | ||
334 | SELECT TABLE_NAME | ||
335 | FROM information_schema.TABLES | ||
336 | WHERE TABLE_SCHEMA = ? | ||
337 | AND TABLE_TYPE = 'BASE TABLE' | ||
338 | ORDER BY TABLE_NAME | ||
339 | SQL; | ||
340 | |||
341 | return $this->connection->executeQuery($sql, [$databaseName]); | ||
342 | } | ||
343 | |||
344 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
345 | { | ||
346 | $columnTypeSQL = $this->platform->getColumnTypeSQLSnippet('c', $databaseName); | ||
347 | |||
348 | $sql = 'SELECT'; | ||
349 | |||
350 | if ($tableName === null) { | ||
351 | $sql .= ' c.TABLE_NAME,'; | ||
352 | } | ||
353 | |||
354 | $sql .= <<<SQL | ||
355 | c.COLUMN_NAME AS field, | ||
356 | $columnTypeSQL AS type, | ||
357 | c.IS_NULLABLE AS `null`, | ||
358 | c.COLUMN_KEY AS `key`, | ||
359 | c.COLUMN_DEFAULT AS `default`, | ||
360 | c.EXTRA, | ||
361 | c.COLUMN_COMMENT AS comment, | ||
362 | c.CHARACTER_SET_NAME AS characterset, | ||
363 | c.COLLATION_NAME AS collation | ||
364 | FROM information_schema.COLUMNS c | ||
365 | INNER JOIN information_schema.TABLES t | ||
366 | ON t.TABLE_NAME = c.TABLE_NAME | ||
367 | SQL; | ||
368 | |||
369 | // The schema name is passed multiple times as a literal in the WHERE clause instead of using a JOIN condition | ||
370 | // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions | ||
371 | // caused by https://bugs.mysql.com/bug.php?id=81347 | ||
372 | $conditions = ['c.TABLE_SCHEMA = ?', 't.TABLE_SCHEMA = ?', "t.TABLE_TYPE = 'BASE TABLE'"]; | ||
373 | $params = [$databaseName, $databaseName]; | ||
374 | |||
375 | if ($tableName !== null) { | ||
376 | $conditions[] = 't.TABLE_NAME = ?'; | ||
377 | $params[] = $tableName; | ||
378 | } | ||
379 | |||
380 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY ORDINAL_POSITION'; | ||
381 | |||
382 | return $this->connection->executeQuery($sql, $params); | ||
383 | } | ||
384 | |||
385 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
386 | { | ||
387 | $sql = 'SELECT'; | ||
388 | |||
389 | if ($tableName === null) { | ||
390 | $sql .= ' TABLE_NAME,'; | ||
391 | } | ||
392 | |||
393 | $sql .= <<<'SQL' | ||
394 | NON_UNIQUE AS Non_Unique, | ||
395 | INDEX_NAME AS Key_name, | ||
396 | COLUMN_NAME AS Column_Name, | ||
397 | SUB_PART AS Sub_Part, | ||
398 | INDEX_TYPE AS Index_Type | ||
399 | FROM information_schema.STATISTICS | ||
400 | SQL; | ||
401 | |||
402 | $conditions = ['TABLE_SCHEMA = ?']; | ||
403 | $params = [$databaseName]; | ||
404 | |||
405 | if ($tableName !== null) { | ||
406 | $conditions[] = 'TABLE_NAME = ?'; | ||
407 | $params[] = $tableName; | ||
408 | } | ||
409 | |||
410 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; | ||
411 | |||
412 | return $this->connection->executeQuery($sql, $params); | ||
413 | } | ||
414 | |||
415 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
416 | { | ||
417 | $sql = 'SELECT DISTINCT'; | ||
418 | |||
419 | if ($tableName === null) { | ||
420 | $sql .= ' k.TABLE_NAME,'; | ||
421 | } | ||
422 | |||
423 | $sql .= <<<'SQL' | ||
424 | k.CONSTRAINT_NAME, | ||
425 | k.COLUMN_NAME, | ||
426 | k.REFERENCED_TABLE_NAME, | ||
427 | k.REFERENCED_COLUMN_NAME, | ||
428 | k.ORDINAL_POSITION, | ||
429 | c.UPDATE_RULE, | ||
430 | c.DELETE_RULE | ||
431 | FROM information_schema.key_column_usage k | ||
432 | INNER JOIN information_schema.referential_constraints c | ||
433 | ON c.CONSTRAINT_NAME = k.CONSTRAINT_NAME | ||
434 | AND c.TABLE_NAME = k.TABLE_NAME | ||
435 | SQL; | ||
436 | |||
437 | $conditions = ['k.TABLE_SCHEMA = ?']; | ||
438 | $params = [$databaseName]; | ||
439 | |||
440 | if ($tableName !== null) { | ||
441 | $conditions[] = 'k.TABLE_NAME = ?'; | ||
442 | $params[] = $tableName; | ||
443 | } | ||
444 | |||
445 | // The schema name is passed multiple times in the WHERE clause instead of using a JOIN condition | ||
446 | // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions | ||
447 | // caused by https://bugs.mysql.com/bug.php?id=81347 | ||
448 | $conditions[] = 'c.CONSTRAINT_SCHEMA = ?'; | ||
449 | $params[] = $databaseName; | ||
450 | |||
451 | $conditions[] = 'k.REFERENCED_COLUMN_NAME IS NOT NULL'; | ||
452 | |||
453 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY k.ORDINAL_POSITION'; | ||
454 | |||
455 | return $this->connection->executeQuery($sql, $params); | ||
456 | } | ||
457 | |||
458 | /** | ||
459 | * {@inheritDoc} | ||
460 | */ | ||
461 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
462 | { | ||
463 | // MariaDB-10.10.1 added FULL_COLLATION_NAME to the information_schema.COLLATION_CHARACTER_SET_APPLICABILITY. | ||
464 | // A base collation like uca1400_ai_ci can refer to multiple character sets. The value in the | ||
465 | // information_schema.TABLES.TABLE_COLLATION corresponds to the full collation name. | ||
466 | // The MariaDB executable comment syntax with version, /*M!101001, is exclusively executed on | ||
467 | // MariaDB-10.10.1+ servers for backwards compatibility, and compatiblity to MySQL servers. | ||
468 | $sql = <<<'SQL' | ||
469 | SELECT t.TABLE_NAME, | ||
470 | t.ENGINE, | ||
471 | t.AUTO_INCREMENT, | ||
472 | t.TABLE_COMMENT, | ||
473 | t.CREATE_OPTIONS, | ||
474 | t.TABLE_COLLATION, | ||
475 | ccsa.CHARACTER_SET_NAME | ||
476 | FROM information_schema.TABLES t | ||
477 | INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa | ||
478 | ON /*M!101001 ccsa.FULL_COLLATION_NAME = t.TABLE_COLLATION OR */ | ||
479 | ccsa.COLLATION_NAME = t.TABLE_COLLATION | ||
480 | SQL; | ||
481 | |||
482 | $conditions = ['t.TABLE_SCHEMA = ?']; | ||
483 | $params = [$databaseName]; | ||
484 | |||
485 | if ($tableName !== null) { | ||
486 | $conditions[] = 't.TABLE_NAME = ?'; | ||
487 | $params[] = $tableName; | ||
488 | } | ||
489 | |||
490 | $conditions[] = "t.TABLE_TYPE = 'BASE TABLE'"; | ||
491 | |||
492 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
493 | |||
494 | /** @var array<string,array<string,mixed>> $metadata */ | ||
495 | $metadata = $this->connection->executeQuery($sql, $params) | ||
496 | ->fetchAllAssociativeIndexed(); | ||
497 | |||
498 | $tableOptions = []; | ||
499 | foreach ($metadata as $table => $data) { | ||
500 | $data = array_change_key_case($data, CASE_LOWER); | ||
501 | |||
502 | $tableOptions[$table] = [ | ||
503 | 'engine' => $data['engine'], | ||
504 | 'collation' => $data['table_collation'], | ||
505 | 'charset' => $data['character_set_name'], | ||
506 | 'autoincrement' => $data['auto_increment'], | ||
507 | 'comment' => $data['table_comment'], | ||
508 | 'create_options' => $this->parseCreateOptions($data['create_options']), | ||
509 | ]; | ||
510 | } | ||
511 | |||
512 | return $tableOptions; | ||
513 | } | ||
514 | |||
515 | /** @return array<string, string>|array<string, true> */ | ||
516 | private function parseCreateOptions(?string $string): array | ||
517 | { | ||
518 | $options = []; | ||
519 | |||
520 | if ($string === null || $string === '') { | ||
521 | return $options; | ||
522 | } | ||
523 | |||
524 | foreach (explode(' ', $string) as $pair) { | ||
525 | $parts = explode('=', $pair, 2); | ||
526 | |||
527 | $options[$parts[0]] = $parts[1] ?? true; | ||
528 | } | ||
529 | |||
530 | return $options; | ||
531 | } | ||
532 | |||
533 | /** @throws Exception */ | ||
534 | private function getDefaultTableOptions(): DefaultTableOptions | ||
535 | { | ||
536 | if ($this->defaultTableOptions === null) { | ||
537 | $row = $this->connection->fetchNumeric( | ||
538 | 'SELECT @@character_set_database, @@collation_database', | ||
539 | ); | ||
540 | |||
541 | assert($row !== false); | ||
542 | |||
543 | $this->defaultTableOptions = new DefaultTableOptions(...$row); | ||
544 | } | ||
545 | |||
546 | return $this->defaultTableOptions; | ||
547 | } | ||
548 | } | ||