Passed
Pull Request — develop (#3565)
by Jonathan
10:02
created

AbstractSchemaManager::listTableIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Schema;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\ConnectionException;
9
use Doctrine\DBAL\DBALException;
10
use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs;
11
use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs;
12
use Doctrine\DBAL\Events;
13
use Doctrine\DBAL\Platforms\AbstractPlatform;
14
use Doctrine\DBAL\Platforms\Exception\NotSupported;
15
use Throwable;
16
use function array_filter;
17
use function array_intersect;
18
use function array_map;
19
use function array_shift;
20
use function array_values;
21
use function assert;
22
use function call_user_func_array;
23
use function count;
24
use function func_get_args;
25
use function is_array;
26
use function is_callable;
27
use function preg_match;
28
use function strtolower;
29
30
/**
31
 * Base class for schema managers. Schema managers are used to inspect and/or
32
 * modify the database schema/structure.
33
 */
34
abstract class AbstractSchemaManager
35
{
36
    /**
37
     * Holds instance of the Doctrine connection for this schema manager.
38
     *
39
     * @var Connection
40
     */
41
    protected $_conn;
42
43
    /**
44
     * Holds instance of the database platform used for this schema manager.
45
     *
46
     * @var AbstractPlatform
47
     */
48
    protected $_platform;
49
50
    /**
51
     * Constructor. Accepts the Connection instance to manage the schema for.
52
     */
53 6348
    public function __construct(Connection $conn, ?AbstractPlatform $platform = null)
54
    {
55 6348
        $this->_conn     = $conn;
56 6348
        $this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
57 6348
    }
58
59
    /**
60
     * Returns the associated platform.
61
     */
62 4118
    public function getDatabasePlatform() : AbstractPlatform
63
    {
64 4118
        return $this->_platform;
65
    }
66
67
    /**
68
     * Tries any method on the schema manager. Normally a method throws an
69
     * exception when your DBMS doesn't support it or if an error occurs.
70
     * This method allows you to try and method on your SchemaManager
71
     * instance and will return false if it does not work or is not supported.
72
     *
73
     * <code>
74
     * $result = $sm->tryMethod('dropView', 'view_name');
75
     * </code>
76
     *
77
     * @return mixed
78
     */
79 6214
    public function tryMethod()
80
    {
81 6214
        $args   = func_get_args();
82 6214
        $method = $args[0];
83 6214
        unset($args[0]);
84 6214
        $args = array_values($args);
85
86 6214
        $callback = [$this, $method];
87 6214
        assert(is_callable($callback));
88
89
        try {
90 6214
            return call_user_func_array($callback, $args);
91 6197
        } catch (Throwable $e) {
92 6197
            return false;
93
        }
94
    }
95
96
    /**
97
     * Lists the available databases for this connection.
98
     *
99
     * @return array<int, string>
100
     */
101 3667
    public function listDatabases() : array
102
    {
103 3667
        $sql = $this->_platform->getListDatabasesSQL();
104
105 3522
        $databases = $this->_conn->fetchAll($sql);
106
107 3522
        return $this->_getPortableDatabasesList($databases);
108
    }
109
110
    /**
111
     * Returns a list of all namespaces in the current database.
112
     *
113
     * @return array<int, string>
114
     */
115 1125
    public function listNamespaceNames() : array
116
    {
117 1125
        $sql = $this->_platform->getListNamespacesSQL();
118
119 1125
        $namespaces = $this->_conn->fetchAll($sql);
120
121 1125
        return $this->getPortableNamespacesList($namespaces);
122
    }
123
124
    /**
125
     * Lists the available sequences for this connection.
126
     *
127
     * @return array<int, Sequence>
128
     */
129 1154
    public function listSequences(?string $database = null) : array
130
    {
131 1154
        if ($database === null) {
132 1154
            $database = $this->_conn->getDatabase();
133
        }
134 1154
        $sql = $this->_platform->getListSequencesSQL($database);
135
136 1154
        $sequences = $this->_conn->fetchAll($sql);
137
138 1154
        return $this->filterAssetNames($this->_getPortableSequencesList($sequences));
139
    }
140
141
    /**
142
     * Lists the columns for a given table.
143
     *
144
     * In contrast to other libraries and to the old version of Doctrine,
145
     * this column definition does try to contain the 'primary' field for
146
     * the reason that it is not portable across different RDBMS. Use
147
     * {@see listTableIndexes($tableName)} to retrieve the primary key
148
     * of a table. We're a RDBMS specifies more details these are held
149
     * in the platformDetails array.
150
     *
151
     * @return array<string, Column>
152
     */
153 5127
    public function listTableColumns(string $table, ?string $database = null) : array
154
    {
155 5127
        if (! $database) {
156 5126
            $database = $this->_conn->getDatabase();
157
        }
158
159 5127
        $sql = $this->_platform->getListTableColumnsSQL($table, $database);
160
161 5127
        $tableColumns = $this->_conn->fetchAll($sql);
162
163 5127
        return $this->_getPortableTableColumnList($table, $database, $tableColumns);
164
    }
165
166
    /**
167
     * Lists the indexes for a given table returning an array of Index instances.
168
     *
169
     * Keys of the portable indexes list are all lower-cased.
170
     *
171
     * @return array<string, Index>
172
     */
173 4788
    public function listTableIndexes(string $table)
174
    {
175 4788
        $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
176
177 4788
        $tableIndexes = $this->_conn->fetchAll($sql);
178
179 4788
        return $this->_getPortableTableIndexesList($tableIndexes, $table);
180
    }
181
182
    /**
183
     * Returns true if all the given tables exist.
184
     *
185
     * @param string|array<int, string> $tableNames
186
     */
187 5552
    public function tablesExist($tableNames) : bool
188
    {
189 5552
        $tableNames = array_map('strtolower', (array) $tableNames);
190
191 5552
        return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
192
    }
193
194
    /**
195
     * Returns a list of all tables in the current database.
196
     *
197
     * @return array<int, string>
198
     */
199 5381
    public function listTableNames() : array
200
    {
201 5381
        $sql = $this->_platform->getListTablesSQL();
202
203 5381
        $tables     = $this->_conn->fetchAll($sql);
204 5381
        $tableNames = $this->_getPortableTablesList($tables);
205
206 5381
        return $this->filterAssetNames($tableNames);
207
    }
208
209
    /**
210
     * Filters asset names if they are configured to return only a subset of all
211
     * the found elements.
212
     *
213
     * @param array<int, mixed> $assetNames
214
     *
215
     * @return array<int, mixed>
216
     */
217 5569
    protected function filterAssetNames(array $assetNames)
218
    {
219 5569
        $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
220 5569
        if (! $filter) {
221 5569
            return $assetNames;
222
        }
223
224 1780
        return array_values(array_filter($assetNames, $filter));
225
    }
226
227
    /**
228
     * Lists the tables for this connection.
229
     *
230
     * @return array<int, Table>
231
     */
232 4842
    public function listTables() : array
233
    {
234 4842
        $tableNames = $this->listTableNames();
235
236 4842
        $tables = [];
237 4842
        foreach ($tableNames as $tableName) {
238 4803
            $tables[] = $this->listTableDetails($tableName);
239
        }
240
241 4842
        return $tables;
242
    }
243
244 5115
    public function listTableDetails(string $tableName) : Table
245
    {
246 5115
        $columns     = $this->listTableColumns($tableName);
247 5115
        $foreignKeys = [];
248
249 5115
        if ($this->_platform->supportsForeignKeyConstraints()) {
250 4951
            $foreignKeys = $this->listTableForeignKeys($tableName);
251
        }
252
253 5115
        $indexes = $this->listTableIndexes($tableName);
254
255 5115
        return new Table($tableName, $columns, $indexes, [], $foreignKeys, []);
256
    }
257
258
    /**
259
     * Lists the views this connection has.
260
     *
261
     * @return array<string, View>
262
     */
263 3401
    public function listViews() : array
264
    {
265 3401
        $database = $this->_conn->getDatabase();
266 3401
        $sql      = $this->_platform->getListViewsSQL($database);
267 3401
        $views    = $this->_conn->fetchAll($sql);
268
269 3401
        return $this->_getPortableViewsList($views);
270
    }
271
272
    /**
273
     * Lists the foreign keys for the given table.
274
     *
275
     * @return array<int|string, ForeignKeyConstraint>
276
     */
277 4991
    public function listTableForeignKeys(string $table, ?string $database = null) : array
278
    {
279 4991
        if ($database === null) {
280 4991
            $database = $this->_conn->getDatabase();
281
        }
282 4991
        $sql              = $this->_platform->getListTableForeignKeysSQL($table, $database);
283 4991
        $tableForeignKeys = $this->_conn->fetchAll($sql);
284
285 4991
        return $this->_getPortableTableForeignKeysList($tableForeignKeys);
286
    }
287
288
    /* drop*() Methods */
289
290
    /**
291
     * Drops a database.
292
     *
293
     * NOTE: You can not drop the database this SchemaManager is currently connected to.
294
     */
295 5710
    public function dropDatabase(string $database) : void
296
    {
297 5710
        $this->_execSql($this->_platform->getDropDatabaseSQL($database));
298 5076
    }
299
300
    /**
301
     * Drops the given table.
302
     */
303 6191
    public function dropTable(string $tableName) : void
304
    {
305 6191
        $this->_execSql($this->_platform->getDropTableSQL($tableName));
306 6115
    }
307
308
    /**
309
     * Drops the index from the given table.
310
     *
311
     * @param Index|string $index The name of the index.
312
     * @param Table|string $table The name of the table.
313
     */
314 3536
    public function dropIndex($index, $table) : void
315
    {
316 3536
        if ($index instanceof Index) {
317
            $index = $index->getQuotedName($this->_platform);
318
        }
319
320 3536
        $this->_execSql($this->_platform->getDropIndexSQL($index, $table));
321 3536
    }
322
323
    /**
324
     * Drops the constraint from the given table.
325
     *
326
     * @param Table|string $table The name of the table.
327
     */
328
    public function dropConstraint(Constraint $constraint, $table) : void
329
    {
330
        $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table));
331
    }
332
333
    /**
334
     * Drops a foreign key from a table.
335
     *
336
     * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key.
337
     * @param Table|string                $table      The name of the table with the foreign key.
338
     */
339
    public function dropForeignKey($foreignKey, $table) : void
340
    {
341
        $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table));
342
    }
343
344
    /**
345
     * Drops a sequence with a given name.
346
     */
347 1151
    public function dropSequence(string $name) : void
348
    {
349 1151
        $this->_execSql($this->_platform->getDropSequenceSQL($name));
350
    }
351
352
    /**
353
     * Drops a view.
354
     */
355 3562
    public function dropView(string $name) : void
356
    {
357 3562
        $this->_execSql($this->_platform->getDropViewSQL($name));
358
    }
359
360
    /* create*() Methods */
361
362
    /**
363
     * Creates a new database.
364
     */
365 5708
    public function createDatabase(string $database) : void
366
    {
367 5708
        $this->_execSql($this->_platform->getCreateDatabaseSQL($database));
368 5708
    }
369
370
    /**
371
     * Creates a new table.
372
     */
373 6209
    public function createTable(Table $table) : void
374
    {
375 6209
        $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
376 6209
        $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
377 6188
    }
378
379
    /**
380
     * Creates a new sequence.
381
     *
382
     * @throws ConnectionException If something fails at database level.
383
     */
384 1153
    public function createSequence(Sequence $sequence) : void
385
    {
386 1153
        $this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
387 1153
    }
388
389
    /**
390
     * Creates a constraint on a table.
391
     *
392
     * @param Table|string $table
393
     */
394 1
    public function createConstraint(Constraint $constraint, $table) : void
395
    {
396 1
        $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
397 1
    }
398
399
    /**
400
     * Creates a new index on a table.
401
     *
402
     * @param Table|string $table The name of the table on which the index is to be created.
403
     */
404 3536
    public function createIndex(Index $index, $table) : void
405
    {
406 3536
        $this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
407 3536
    }
408
409
    /**
410
     * Creates a new foreign key.
411
     *
412
     * @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
413
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
414
     */
415 3536
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) : void
416
    {
417 3536
        $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
418 3536
    }
419
420
    /**
421
     * Creates a new view.
422
     */
423 3562
    public function createView(View $view) : void
424
    {
425 3562
        $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
426 3562
    }
427
428
    /* dropAndCreate*() Methods */
429
430
    /**
431
     * Drops and creates a constraint.
432
     *
433
     * @see dropConstraint()
434
     * @see createConstraint()
435
     *
436
     * @param Table|string $table
437
     */
438
    public function dropAndCreateConstraint(Constraint $constraint, $table) : void
439
    {
440
        $this->tryMethod('dropConstraint', $constraint, $table);
441
        $this->createConstraint($constraint, $table);
442
    }
443
444
    /**
445
     * Drops and creates a new index on a table.
446
     *
447
     * @param Table|string $table The name of the table on which the index is to be created.
448
     */
449 3536
    public function dropAndCreateIndex(Index $index, $table) : void
450
    {
451 3536
        $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
452 3536
        $this->createIndex($index, $table);
453 3536
    }
454
455
    /**
456
     * Drops and creates a new foreign key.
457
     *
458
     * @param ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created.
459
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
460
     */
461
    public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) : void
462
    {
463
        $this->tryMethod('dropForeignKey', $foreignKey, $table);
464
        $this->createForeignKey($foreignKey, $table);
465
    }
466
467
    /**
468
     * Drops and create a new sequence.
469
     *
470
     * @throws ConnectionException If something fails at database level.
471
     */
472 1151
    public function dropAndCreateSequence(Sequence $sequence) : void
473
    {
474 1151
        $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
475 1151
        $this->createSequence($sequence);
476 1151
    }
477
478
    /**
479
     * Drops and creates a new table.
480
     */
481 6154
    public function dropAndCreateTable(Table $table) : void
482
    {
483 6154
        $this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
484 6154
        $this->createTable($table);
485 6154
    }
486
487
    /**
488
     * Drops and creates a new database.
489
     */
490 5853
    public function dropAndCreateDatabase(string $database) : void
491
    {
492 5853
        $this->tryMethod('dropDatabase', $database);
493 5853
        $this->createDatabase($database);
494 5853
    }
495
496
    /**
497
     * Drops and creates a new view.
498
     */
499 3562
    public function dropAndCreateView(View $view) : void
500
    {
501 3562
        $this->tryMethod('dropView', $view->getQuotedName($this->_platform));
502 3562
        $this->createView($view);
503 3562
    }
504
505
    /* alterTable() Methods */
506
507
    /**
508
     * Alters an existing tables schema.
509
     */
510 4320
    public function alterTable(TableDiff $tableDiff) : void
511
    {
512 4320
        $queries = $this->_platform->getAlterTableSQL($tableDiff);
513
514 4320
        if (! is_array($queries) || ! count($queries)) {
515 1846
            return;
516
        }
517
518 4320
        foreach ($queries as $ddlQuery) {
519 4320
            $this->_execSql($ddlQuery);
520
        }
521 4320
    }
522
523
    /**
524
     * Renames a given table to another name.
525
     */
526 1
    public function renameTable(string $name, string $newName) : void
527
    {
528 1
        $tableDiff          = new TableDiff($name);
529 1
        $tableDiff->newName = $newName;
530 1
        $this->alterTable($tableDiff);
531 1
    }
532
533
    /**
534
     * Methods for filtering return values of list*() methods to convert
535
     * the native DBMS data definition to a portable Doctrine definition
536
     */
537
538
    /**
539
     * @param array<int, mixed> $databases
540
     *
541
     * @return array<int, string>
542
     */
543 3522
    protected function _getPortableDatabasesList(array $databases) : array
544
    {
545 3522
        $list = [];
546 3522
        foreach ($databases as $value) {
547 3522
            $value = $this->_getPortableDatabaseDefinition($value);
548
549 3522
            if (! $value) {
550
                continue;
551
            }
552
553 3522
            $list[] = $value;
554
        }
555
556 3522
        return $list;
557
    }
558
559
    /**
560
     * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
561
     *
562
     * @param array<int, array<int, mixed>> $namespaces The list of namespace names in the native DBMS data definition.
563
     *
564
     * @return array<int, string>
565
     */
566 1125
    protected function getPortableNamespacesList(array $namespaces) : array
567
    {
568 1125
        $namespacesList = [];
569
570 1125
        foreach ($namespaces as $namespace) {
571 1125
            $namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
572
        }
573
574 1125
        return $namespacesList;
575
    }
576
577
    /**
578
     * @param mixed $database
579
     *
580
     * @return mixed
581
     */
582
    protected function _getPortableDatabaseDefinition($database)
583
    {
584
        return $database;
585
    }
586
587
    /**
588
     * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
589
     *
590
     * @param array<string|int, mixed> $namespace The native DBMS namespace definition.
591
     *
592
     * @return mixed
593
     */
594
    protected function getPortableNamespaceDefinition(array $namespace)
595
    {
596
        return $namespace;
597
    }
598
599
    /**
600
     * @param array<int, array<int, mixed>> $functions
601
     *
602
     * @return array<int, array<int, mixed>>
603
     */
604
    protected function _getPortableFunctionsList(array $functions)
605
    {
606
        $list = [];
607
        foreach ($functions as $value) {
608
            $value = $this->_getPortableFunctionDefinition($value);
609
610
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array<integer,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
611
                continue;
612
            }
613
614
            $list[] = $value;
615
        }
616
617
        return $list;
618
    }
619
620
    /**
621
     * @param array<string|int, mixed> $function
622
     *
623
     * @return mixed
624
     */
625
    protected function _getPortableFunctionDefinition(array $function)
626
    {
627
        return $function;
628
    }
629
630
    /**
631
     * @param array<int, array<int, mixed>> $triggers
632
     *
633
     * @return array<int, array<int, mixed>>
634
     */
635
    protected function _getPortableTriggersList(array $triggers)
636
    {
637
        $list = [];
638
        foreach ($triggers as $value) {
639
            $value = $this->_getPortableTriggerDefinition($value);
640
641
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array<integer,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
642
                continue;
643
            }
644
645
            $list[] = $value;
646
        }
647
648
        return $list;
649
    }
650
651
    /**
652
     * @param array<string|int, mixed> $trigger
653
     *
654
     * @return mixed
655
     */
656
    protected function _getPortableTriggerDefinition(array $trigger)
657
    {
658
        return $trigger;
659
    }
660
661
    /**
662
     * @param array<int, array<string, mixed>> $sequences
663
     *
664
     * @return array<int, Sequence>
665
     */
666 294
    protected function _getPortableSequencesList(array $sequences)
667
    {
668 294
        $list = [];
669
670 294
        foreach ($sequences as $value) {
671 294
            $list[] = $this->_getPortableSequenceDefinition($value);
672
        }
673
674 294
        return $list;
675
    }
676
677
    /**
678
     * @param array<string, mixed> $sequence
679
     *
680
     * @throws DBALException
681
     */
682
    protected function _getPortableSequenceDefinition(array $sequence) : Sequence
683
    {
684
        throw NotSupported::new('Sequences');
685
    }
686
687
    /**
688
     * Independent of the database the keys of the column list result are lowercased.
689
     *
690
     * The name of the created column instance however is kept in its case.
691
     *
692
     * @param array<int, array<string, mixed>> $tableColumns
693
     *
694
     * @return array<string, Column>
695
     */
696 5127
    protected function _getPortableTableColumnList(string $table, ?string $database, array $tableColumns) : array
697
    {
698 5127
        $eventManager = $this->_platform->getEventManager();
699
700 5127
        $list = [];
701 5127
        foreach ($tableColumns as $tableColumn) {
702 5127
            $column           = null;
703 5127
            $defaultPrevented = false;
704
705 5127
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
706 3644
                $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
707 3644
                $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
708
709 3644
                $defaultPrevented = $eventArgs->isDefaultPrevented();
710 3644
                $column           = $eventArgs->getColumn();
711
            }
712
713 5127
            if (! $defaultPrevented) {
714 5127
                $column = $this->_getPortableTableColumnDefinition($tableColumn);
715
            }
716
717 5127
            if (! $column) {
718
                continue;
719
            }
720
721 5127
            $name        = strtolower($column->getQuotedName($this->_platform));
722 5127
            $list[$name] = $column;
723
        }
724
725 5127
        return $list;
726
    }
727
728
    /**
729
     * Gets Table Column Definition.
730
     *
731
     * @param array<string, mixed> $tableColumn
732
     */
733
    abstract protected function _getPortableTableColumnDefinition(array $tableColumn) : Column;
734
735
    /**
736
     * Aggregates and groups the index results according to the required data result.
737
     *
738
     * @param array<int, array<string, mixed>> $tableIndexRows
739
     *
740
     * @return array<string, Index>
741
     */
742 5120
    protected function _getPortableTableIndexesList(array $tableIndexRows, string $tableName) : array
743
    {
744 5120
        $result = [];
745 5120
        foreach ($tableIndexRows as $tableIndex) {
746 4912
            $indexName = $keyName = $tableIndex['key_name'];
747 4912
            if ($tableIndex['primary']) {
748 4910
                $keyName = 'primary';
749
            }
750 4912
            $keyName = strtolower($keyName);
751
752 4912
            if (! isset($result[$keyName])) {
753
                $options = [
754 4912
                    'lengths' => [],
755
                ];
756
757 4912
                if (isset($tableIndex['where'])) {
758 919
                    $options['where'] = $tableIndex['where'];
759
                }
760
761 4912
                $result[$keyName] = [
762 4912
                    'name' => $indexName,
763
                    'columns' => [],
764 4912
                    'unique' => ! $tableIndex['non_unique'],
765 4912
                    'primary' => $tableIndex['primary'],
766 4912
                    'flags' => $tableIndex['flags'] ?? [],
767 4912
                    'options' => $options,
768
                ];
769
            }
770
771 4912
            $result[$keyName]['columns'][]            = $tableIndex['column_name'];
772 4912
            $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
773
        }
774
775 5120
        $eventManager = $this->_platform->getEventManager();
776
777 5120
        $indexes = [];
778 5120
        foreach ($result as $indexKey => $data) {
779 4912
            $index            = null;
780 4912
            $defaultPrevented = false;
781
782 4912
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
783 3617
                $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
784 3617
                $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
785
786 3617
                $defaultPrevented = $eventArgs->isDefaultPrevented();
787 3617
                $index            = $eventArgs->getIndex();
788
            }
789
790 4912
            if (! $defaultPrevented) {
791 4912
                $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
792
            }
793
794 4912
            if (! $index) {
795
                continue;
796
            }
797
798 4912
            $indexes[$indexKey] = $index;
799
        }
800
801 5120
        return $indexes;
802
    }
803
804
    /**
805
     * @param array<int, array<string, mixed>> $tables
806
     *
807
     * @return array<int, string>
808
     */
809 5381
    protected function _getPortableTablesList(array $tables) : array
810
    {
811 5381
        $list = [];
812 5381
        foreach ($tables as $value) {
813 5379
            $value = $this->_getPortableTableDefinition($value);
814
815 5379
            if (! $value) {
816
                continue;
817
            }
818
819 5379
            $list[] = $value;
820
        }
821
822 5381
        return $list;
823
    }
824
825
    /**
826
     * @param array<string, string> $table
827
     */
828
    protected function _getPortableTableDefinition(array $table) : string
829
    {
830
        return array_shift($table);
831
    }
832
833
    /**
834
     * @param array<int, array<string, mixed>> $users
835
     *
836
     * @return array<int, array<string, mixed>>
837
     */
838
    protected function _getPortableUsersList(array $users) : array
839
    {
840
        $list = [];
841
        foreach ($users as $value) {
842
            $value = $this->_getPortableUserDefinition($value);
843
844
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array<string,mixed> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
845
                continue;
846
            }
847
848
            $list[] = $value;
849
        }
850
851
        return $list;
852
    }
853
854
    /**
855
     * @param array<string, mixed> $user
856
     *
857
     * @return array<string, mixed>
858
     */
859
    protected function _getPortableUserDefinition(array $user) : array
860
    {
861
        return $user;
862
    }
863
864
    /**
865
     * @param array<int, array<string, mixed>> $views
866
     *
867
     * @return array<string, View>
868
     */
869 3401
    protected function _getPortableViewsList(array $views) : array
870
    {
871 3401
        $list = [];
872 3401
        foreach ($views as $value) {
873 3401
            $view = $this->_getPortableViewDefinition($value);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $view is correct as $this->_getPortableViewDefinition($value) targeting Doctrine\DBAL\Schema\Abs...ortableViewDefinition() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
874
875 3401
            if ($view === null) {
876
                continue;
877
            }
878
879 3401
            $viewName        = strtolower($view->getQuotedName($this->_platform));
880 3401
            $list[$viewName] = $view;
881
        }
882
883 3401
        return $list;
884
    }
885
886
    /**
887
     * @param array<string, mixed> $view
888
     */
889
    protected function _getPortableViewDefinition(array $view) : ?View
890
    {
891
        return null;
892
    }
893
894
    /**
895
     * @param array<int|string, array<string, mixed>> $tableForeignKeys
896
     *
897
     * @return array<int, ForeignKeyConstraint>
898
     */
899 1599
    protected function _getPortableTableForeignKeysList(array $tableForeignKeys) : array
900
    {
901 1599
        $list = [];
902
903 1599
        foreach ($tableForeignKeys as $value) {
904 1379
            $list[] = $this->_getPortableTableForeignKeyDefinition($value);
905
        }
906
907 1599
        return $list;
908
    }
909
910
    /**
911
     * @param array<string, mixed> $tableForeignKey
912
     */
913
    protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey) : ForeignKeyConstraint
0 ignored issues
show
Unused Code introduced by
The parameter $tableForeignKey is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

913
    protected function _getPortableTableForeignKeyDefinition(/** @scrutinizer ignore-unused */ array $tableForeignKey) : ForeignKeyConstraint

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
914
    {
915
        throw NotSupported::new('ForeignKey');
916
    }
917
918
    /**
919
     * @param array<int, string>|string $sql
920
     */
921 6243
    protected function _execSql($sql) : void
922
    {
923 6243
        foreach ((array) $sql as $query) {
924 6243
            $this->_conn->executeUpdate($query);
925
        }
926 6216
    }
927
928
    /**
929
     * Creates a schema instance for the current database.
930
     */
931 4669
    public function createSchema() : Schema
932
    {
933 4669
        $namespaces = [];
934
935 4669
        if ($this->_platform->supportsSchemas()) {
936 1043
            $namespaces = $this->listNamespaceNames();
937
        }
938
939 4669
        $sequences = [];
940
941 4669
        if ($this->_platform->supportsSequences()) {
942 1044
            $sequences = $this->listSequences();
943
        }
944
945 4669
        $tables = $this->listTables();
946
947 4669
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
948
    }
949
950
    /**
951
     * Creates the configuration for this schema.
952
     */
953 4896
    public function createSchemaConfig() : SchemaConfig
954
    {
955 4896
        $schemaConfig = new SchemaConfig();
956 4896
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
957
958 4896
        $searchPaths = $this->getSchemaSearchPaths();
959 4896
        if (isset($searchPaths[0])) {
960 4895
            $schemaConfig->setName($searchPaths[0]);
961
        }
962
963 4896
        $params = $this->_conn->getParams();
964 4896
        if (! isset($params['defaultTableOptions'])) {
965 4896
            $params['defaultTableOptions'] = [];
966
        }
967 4896
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
968 1027
            $params['defaultTableOptions']['charset'] = $params['charset'];
969
        }
970 4896
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
971
972 4896
        return $schemaConfig;
973
    }
974
975
    /**
976
     * The search path for namespaces in the currently connected database.
977
     *
978
     * The first entry is usually the default namespace in the Schema. All
979
     * further namespaces contain tables/sequences which can also be addressed
980
     * with a short, not full-qualified name.
981
     *
982
     * For databases that don't support subschema/namespaces this method
983
     * returns the name of the currently connected database.
984
     *
985
     * @return array<int, string>
986
     */
987 4169
    public function getSchemaSearchPaths() : array
988
    {
989 4169
        return [$this->_conn->getDatabase()];
990
    }
991
992
    /**
993
     * Given a table comment this method tries to extract a type hint for Doctrine Type. If the type hint is found,
994
     * it's removed from the comment.
995
     *
996
     * @return string|null The extracted Doctrine type or NULL of the type hint was not found.
997
     */
998 5134
    final protected function extractDoctrineTypeFromComment(?string &$comment) : ?string
999
    {
1000 5134
        if ($comment === null || ! preg_match('/(.*)\(DC2Type:(((?!\)).)+)\)(.*)/', $comment, $match)) {
1001 5129
            return null;
1002
        }
1003
1004 3817
        $comment = $match[1] . $match[4];
1005
1006 3817
        return $match[2];
1007
    }
1008
}
1009