Completed
Pull Request — develop (#3515)
by Sergei
20:18
created

_getPortableTableColumnList()   A

Complexity

Conditions 6
Paths 9

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.0061

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 30
ccs 17
cts 18
cp 0.9444
rs 9.0777
c 0
b 0
f 0
cc 6
nc 9
nop 3
crap 6.0061
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 Throwable;
15
use function array_filter;
16
use function array_intersect;
17
use function array_map;
18
use function array_values;
19
use function assert;
20
use function call_user_func_array;
21
use function count;
22
use function func_get_args;
23
use function is_array;
24
use function is_callable;
25
use function preg_match;
26
use function strtolower;
27
28
/**
29
 * Base class for schema managers. Schema managers are used to inspect and/or
30
 * modify the database schema/structure.
31
 */
32
abstract class AbstractSchemaManager
33
{
34
    /**
35
     * Holds instance of the Doctrine connection for this schema manager.
36
     *
37
     * @var Connection
38
     */
39
    protected $_conn;
40
41
    /**
42
     * Holds instance of the database platform used for this schema manager.
43
     *
44
     * @var AbstractPlatform
45
     */
46
    protected $_platform;
47
48
    /**
49
     * Constructor. Accepts the Connection instance to manage the schema for.
50
     */
51 5517
    public function __construct(Connection $conn, ?AbstractPlatform $platform = null)
52
    {
53 5517
        $this->_conn     = $conn;
54 5517
        $this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
55 5517
    }
56
57
    /**
58
     * Returns the associated platform.
59
     *
60
     * @return AbstractPlatform
61
     */
62 3701
    public function getDatabasePlatform()
63
    {
64 3701
        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 5329
    public function tryMethod()
80
    {
81 5329
        $args   = func_get_args();
82 5329
        $method = $args[0];
83 5329
        unset($args[0]);
84 5329
        $args = array_values($args);
85
86 5329
        $callback = [$this, $method];
87 5329
        assert(is_callable($callback));
88
89
        try {
90 5329
            return call_user_func_array($callback, $args);
91 5203
        } catch (Throwable $e) {
92 5203
            return false;
93
        }
94
    }
95
96
    /**
97
     * Lists the available databases for this connection.
98
     *
99
     * @return string[]
100
     */
101 3282
    public function listDatabases()
102
    {
103 3282
        $sql = $this->_platform->getListDatabasesSQL();
104
105 3135
        $databases = $this->_conn->fetchAll($sql);
106
107 3135
        return $this->_getPortableDatabasesList($databases);
108
    }
109
110
    /**
111
     * Returns a list of all namespaces in the current database.
112
     *
113
     * @return string[]
114
     */
115 1137
    public function listNamespaceNames()
116
    {
117 1137
        $sql = $this->_platform->getListNamespacesSQL();
118
119 1137
        $namespaces = $this->_conn->fetchAll($sql);
120
121 1137
        return $this->getPortableNamespacesList($namespaces);
122
    }
123
124
    /**
125
     * Lists the available sequences for this connection.
126
     *
127
     * @param string|null $database
128
     *
129
     * @return Sequence[]
130
     */
131 1166
    public function listSequences($database = null)
132
    {
133 1166
        if ($database === null) {
134 1166
            $database = $this->_conn->getDatabase();
135
        }
136 1166
        $sql = $this->_platform->getListSequencesSQL($database);
137
138 1166
        $sequences = $this->_conn->fetchAll($sql);
139
140 1166
        return $this->filterAssetNames($this->_getPortableSequencesList($sequences));
141
    }
142
143
    /**
144
     * Lists the columns for a given table.
145
     *
146
     * In contrast to other libraries and to the old version of Doctrine,
147
     * this column definition does try to contain the 'primary' field for
148
     * the reason that it is not portable across different RDBMS. Use
149
     * {@see listTableIndexes($tableName)} to retrieve the primary key
150
     * of a table. We're a RDBMS specifies more details these are held
151
     * in the platformDetails array.
152
     *
153
     * @param string      $table    The name of the table.
154
     * @param string|null $database
155
     *
156
     * @return Column[]
157
     */
158 4210
    public function listTableColumns($table, $database = null)
159
    {
160 4210
        if (! $database) {
161 4209
            $database = $this->_conn->getDatabase();
162
        }
163
164 4210
        $sql = $this->_platform->getListTableColumnsSQL($table, $database);
165
166 4210
        $tableColumns = $this->_conn->fetchAll($sql);
167
168 4210
        return $this->_getPortableTableColumnList($table, $database, $tableColumns);
169
    }
170
171
    /**
172
     * Lists the indexes for a given table returning an array of Index instances.
173
     *
174
     * Keys of the portable indexes list are all lower-cased.
175
     *
176
     * @param string $table The name of the table.
177
     *
178
     * @return Index[]
179
     */
180 3900
    public function listTableIndexes($table)
181
    {
182 3900
        $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
183
184 3900
        $tableIndexes = $this->_conn->fetchAll($sql);
185
186 3900
        return $this->_getPortableTableIndexesList($tableIndexes, $table);
187
    }
188
189
    /**
190
     * Returns true if all the given tables exist.
191
     *
192
     * @param string|string[] $tableNames
193
     *
194
     * @return bool
195
     */
196 4625
    public function tablesExist($tableNames)
197
    {
198 4625
        $tableNames = array_map('strtolower', (array) $tableNames);
199
200 4625
        return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
201
    }
202
203
    /**
204
     * Returns a list of all tables in the current database.
205
     *
206
     * @return string[]
207
     */
208 4467
    public function listTableNames()
209
    {
210 4467
        $sql = $this->_platform->getListTablesSQL();
211
212 4467
        $tables     = $this->_conn->fetchAll($sql);
213 4467
        $tableNames = $this->_getPortableTablesList($tables);
214
215 4467
        return $this->filterAssetNames($tableNames);
216
    }
217
218
    /**
219
     * Filters asset names if they are configured to return only a subset of all
220
     * the found elements.
221
     *
222
     * @param mixed[] $assetNames
223
     *
224
     * @return mixed[]
225
     */
226 4645
    protected function filterAssetNames($assetNames)
227
    {
228 4645
        $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
229 4645
        if (! $filter) {
230 4645
            return $assetNames;
231
        }
232
233 1660
        return array_values(array_filter($assetNames, $filter));
234
    }
235
236
    /**
237
     * @deprecated Use Configuration::getSchemaAssetsFilter() instead
238
     *
239
     * @return string|null
240
     */
241
    protected function getFilterSchemaAssetsExpression()
242
    {
243
        return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression();
1 ignored issue
show
Deprecated Code introduced by
The function Doctrine\DBAL\Configurat...chemaAssetsExpression() has been deprecated: Use Configuration::getSchemaAssetsFilter() instead ( Ignorable by Annotation )

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

243
        return /** @scrutinizer ignore-deprecated */ $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
244
    }
245
246
    /**
247
     * Lists the tables for this connection.
248
     *
249
     * @return Table[]
250
     */
251 4082
    public function listTables()
252
    {
253 4082
        $tableNames = $this->listTableNames();
254
255 4082
        $tables = [];
256 4082
        foreach ($tableNames as $tableName) {
257 4037
            $tables[] = $this->listTableDetails($tableName);
258
        }
259
260 4082
        return $tables;
261
    }
262
263
    /**
264
     * @param string $tableName
265
     *
266
     * @return Table
267
     */
268 4198
    public function listTableDetails($tableName)
269
    {
270 4198
        $columns     = $this->listTableColumns($tableName);
271 4198
        $foreignKeys = [];
272
273 4198
        if ($this->_platform->supportsForeignKeyConstraints()) {
274 4047
            $foreignKeys = $this->listTableForeignKeys($tableName);
275
        }
276
277 4198
        $indexes = $this->listTableIndexes($tableName);
278
279 4198
        return new Table($tableName, $columns, $indexes, [], $foreignKeys, []);
280
    }
281
282
    /**
283
     * Lists the views this connection has.
284
     *
285
     * @return View[]
286
     */
287 3045
    public function listViews()
288
    {
289 3045
        $database = $this->_conn->getDatabase();
290 3045
        $sql      = $this->_platform->getListViewsSQL($database);
291 3045
        $views    = $this->_conn->fetchAll($sql);
292
293 3045
        return $this->_getPortableViewsList($views);
294
    }
295
296
    /**
297
     * Lists the foreign keys for the given table.
298
     *
299
     * @param string      $table    The name of the table.
300
     * @param string|null $database
301
     *
302
     * @return ForeignKeyConstraint[]
303
     */
304 4087
    public function listTableForeignKeys($table, $database = null)
305
    {
306 4087
        if ($database === null) {
307 4087
            $database = $this->_conn->getDatabase();
308
        }
309 4087
        $sql              = $this->_platform->getListTableForeignKeysSQL($table, $database);
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\DBAL\Platforms\...stTableForeignKeysSQL() has too many arguments starting with $database. ( Ignorable by Annotation )

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

309
        /** @scrutinizer ignore-call */ 
310
        $sql              = $this->_platform->getListTableForeignKeysSQL($table, $database);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
310 4087
        $tableForeignKeys = $this->_conn->fetchAll($sql);
311
312 4087
        return $this->_getPortableTableForeignKeysList($tableForeignKeys);
313
    }
314
315
    /* drop*() Methods */
316
317
    /**
318
     * Drops a database.
319
     *
320
     * NOTE: You can not drop the database this SchemaManager is currently connected to.
321
     *
322
     * @param string $database The name of the database to drop.
323
     *
324
     * @return void
325
     */
326 4858
    public function dropDatabase($database)
327
    {
328 4858
        $this->_execSql($this->_platform->getDropDatabaseSQL($database));
329 4307
    }
330
331
    /**
332
     * Drops the given table.
333
     *
334
     * @param string $tableName The name of the table to drop.
335
     *
336
     * @return void
337
     */
338 5139
    public function dropTable($tableName)
339
    {
340 5139
        $this->_execSql($this->_platform->getDropTableSQL($tableName));
341 5067
    }
342
343
    /**
344
     * Drops the index from the given table.
345
     *
346
     * @param Index|string $index The name of the index.
347
     * @param Table|string $table The name of the table.
348
     *
349
     * @return void
350
     */
351 3165
    public function dropIndex($index, $table)
352
    {
353 3165
        if ($index instanceof Index) {
354
            $index = $index->getQuotedName($this->_platform);
355
        }
356
357 3165
        $this->_execSql($this->_platform->getDropIndexSQL($index, $table));
358 3165
    }
359
360
    /**
361
     * Drops the constraint from the given table.
362
     *
363
     * @param Table|string $table The name of the table.
364
     *
365
     * @return void
366
     */
367
    public function dropConstraint(Constraint $constraint, $table)
368
    {
369
        $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table));
370
    }
371
372
    /**
373
     * Drops a foreign key from a table.
374
     *
375
     * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key.
376
     * @param Table|string                $table      The name of the table with the foreign key.
377
     *
378
     * @return void
379
     */
380
    public function dropForeignKey($foreignKey, $table)
381
    {
382
        $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table));
383
    }
384
385
    /**
386
     * Drops a sequence with a given name.
387
     *
388
     * @param string $name The name of the sequence to drop.
389
     *
390
     * @return void
391
     */
392 1163
    public function dropSequence($name)
393
    {
394 1163
        $this->_execSql($this->_platform->getDropSequenceSQL($name));
395
    }
396
397
    /**
398
     * Drops a view.
399
     *
400
     * @param string $name The name of the view.
401
     *
402
     * @return void
403
     */
404 3211
    public function dropView($name)
405
    {
406 3211
        $this->_execSql($this->_platform->getDropViewSQL($name));
407
    }
408
409
    /* create*() Methods */
410
411
    /**
412
     * Creates a new database.
413
     *
414
     * @param string $database The name of the database to create.
415
     *
416
     * @return void
417
     */
418 4856
    public function createDatabase($database)
419
    {
420 4856
        $this->_execSql($this->_platform->getCreateDatabaseSQL($database));
421 4856
    }
422
423
    /**
424
     * Creates a new table.
425
     *
426
     * @return void
427
     */
428 5157
    public function createTable(Table $table)
429
    {
430 5157
        $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
431 5157
        $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
432 5136
    }
433
434
    /**
435
     * Creates a new sequence.
436
     *
437
     * @param Sequence $sequence
438
     *
439
     * @return void
440
     *
441
     * @throws ConnectionException If something fails at database level.
442
     */
443 1165
    public function createSequence($sequence)
444
    {
445 1165
        $this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
446 1165
    }
447
448
    /**
449
     * Creates a constraint on a table.
450
     *
451
     * @param Table|string $table
452
     *
453
     * @return void
454
     */
455 1
    public function createConstraint(Constraint $constraint, $table)
456
    {
457 1
        $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
458 1
    }
459
460
    /**
461
     * Creates a new index on a table.
462
     *
463
     * @param Table|string $table The name of the table on which the index is to be created.
464
     *
465
     * @return void
466
     */
467 3165
    public function createIndex(Index $index, $table)
468
    {
469 3165
        $this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
470 3165
    }
471
472
    /**
473
     * Creates a new foreign key.
474
     *
475
     * @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
476
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
477
     *
478
     * @return void
479
     */
480 3174
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
481
    {
482 3174
        $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
483 3174
    }
484
485
    /**
486
     * Creates a new view.
487
     *
488
     * @return void
489
     */
490 3211
    public function createView(View $view)
491
    {
492 3211
        $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
493 3211
    }
494
495
    /* dropAndCreate*() Methods */
496
497
    /**
498
     * Drops and creates a constraint.
499
     *
500
     * @see dropConstraint()
501
     * @see createConstraint()
502
     *
503
     * @param Table|string $table
504
     *
505
     * @return void
506
     */
507
    public function dropAndCreateConstraint(Constraint $constraint, $table)
508
    {
509
        $this->tryMethod('dropConstraint', $constraint, $table);
510
        $this->createConstraint($constraint, $table);
511
    }
512
513
    /**
514
     * Drops and creates a new index on a table.
515
     *
516
     * @param Table|string $table The name of the table on which the index is to be created.
517
     *
518
     * @return void
519
     */
520 3165
    public function dropAndCreateIndex(Index $index, $table)
521
    {
522 3165
        $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
523 3165
        $this->createIndex($index, $table);
524 3165
    }
525
526
    /**
527
     * Drops and creates a new foreign key.
528
     *
529
     * @param ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created.
530
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
531
     *
532
     * @return void
533
     */
534
    public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
535
    {
536
        $this->tryMethod('dropForeignKey', $foreignKey, $table);
537
        $this->createForeignKey($foreignKey, $table);
538
    }
539
540
    /**
541
     * Drops and create a new sequence.
542
     *
543
     * @return void
544
     *
545
     * @throws ConnectionException If something fails at database level.
546
     */
547 1163
    public function dropAndCreateSequence(Sequence $sequence)
548
    {
549 1163
        $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
550 1163
        $this->createSequence($sequence);
551 1163
    }
552
553
    /**
554
     * Drops and creates a new table.
555
     *
556
     * @return void
557
     */
558 5100
    public function dropAndCreateTable(Table $table)
559
    {
560 5100
        $this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
561 5100
        $this->createTable($table);
562 5100
    }
563
564
    /**
565
     * Drops and creates a new database.
566
     *
567
     * @param string $database The name of the database to create.
568
     *
569
     * @return void
570
     */
571 5003
    public function dropAndCreateDatabase($database)
572
    {
573 5003
        $this->tryMethod('dropDatabase', $database);
574 5003
        $this->createDatabase($database);
575 5003
    }
576
577
    /**
578
     * Drops and creates a new view.
579
     *
580
     * @return void
581
     */
582 3211
    public function dropAndCreateView(View $view)
583
    {
584 3211
        $this->tryMethod('dropView', $view->getQuotedName($this->_platform));
585 3211
        $this->createView($view);
586 3211
    }
587
588
    /* alterTable() Methods */
589
590
    /**
591
     * Alters an existing tables schema.
592
     *
593
     * @return void
594
     */
595 3875
    public function alterTable(TableDiff $tableDiff)
596
    {
597 3875
        $queries = $this->_platform->getAlterTableSQL($tableDiff);
598
599 3875
        if (! is_array($queries) || ! count($queries)) {
600 1566
            return;
601
        }
602
603 3875
        foreach ($queries as $ddlQuery) {
604 3875
            $this->_execSql($ddlQuery);
605
        }
606 3875
    }
607
608
    /**
609
     * Renames a given table to another name.
610
     *
611
     * @param string $name    The current name of the table.
612
     * @param string $newName The new name of the table.
613
     *
614
     * @return void
615
     */
616 1
    public function renameTable($name, $newName)
617
    {
618 1
        $tableDiff          = new TableDiff($name);
619 1
        $tableDiff->newName = $newName;
620 1
        $this->alterTable($tableDiff);
621 1
    }
622
623
    /**
624
     * Methods for filtering return values of list*() methods to convert
625
     * the native DBMS data definition to a portable Doctrine definition
626
     */
627
628
    /**
629
     * @param mixed[] $databases
630
     *
631
     * @return string[]
632
     */
633 3135
    protected function _getPortableDatabasesList($databases)
634
    {
635 3135
        $list = [];
636 3135
        foreach ($databases as $value) {
637 3135
            $value = $this->_getPortableDatabaseDefinition($value);
638
639 3135
            if (! $value) {
640
                continue;
641
            }
642
643 3135
            $list[] = $value;
644
        }
645
646 3135
        return $list;
647
    }
648
649
    /**
650
     * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
651
     *
652
     * @param mixed[][] $namespaces The list of namespace names in the native DBMS data definition.
653
     *
654
     * @return string[]
655
     */
656 1137
    protected function getPortableNamespacesList(array $namespaces)
657
    {
658 1137
        $namespacesList = [];
659
660 1137
        foreach ($namespaces as $namespace) {
661 1137
            $namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
662
        }
663
664 1137
        return $namespacesList;
665
    }
666
667
    /**
668
     * @param mixed $database
669
     *
670
     * @return mixed
671
     */
672
    protected function _getPortableDatabaseDefinition($database)
673
    {
674
        return $database;
675
    }
676
677
    /**
678
     * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
679
     *
680
     * @param mixed[] $namespace The native DBMS namespace definition.
681
     *
682
     * @return mixed
683
     */
684
    protected function getPortableNamespaceDefinition(array $namespace)
685
    {
686
        return $namespace;
687
    }
688
689
    /**
690
     * @param mixed[][] $functions
691
     *
692
     * @return mixed[][]
693
     */
694
    protected function _getPortableFunctionsList($functions)
695
    {
696
        $list = [];
697
        foreach ($functions as $value) {
698
            $value = $this->_getPortableFunctionDefinition($value);
699
700
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array<mixed,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...
701
                continue;
702
            }
703
704
            $list[] = $value;
705
        }
706
707
        return $list;
708
    }
709
710
    /**
711
     * @param mixed[] $function
712
     *
713
     * @return mixed
714
     */
715
    protected function _getPortableFunctionDefinition($function)
716
    {
717
        return $function;
718
    }
719
720
    /**
721
     * @param mixed[][] $triggers
722
     *
723
     * @return mixed[][]
724
     */
725
    protected function _getPortableTriggersList($triggers)
726
    {
727
        $list = [];
728
        foreach ($triggers as $value) {
729
            $value = $this->_getPortableTriggerDefinition($value);
730
731
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array<mixed,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...
732
                continue;
733
            }
734
735
            $list[] = $value;
736
        }
737
738
        return $list;
739
    }
740
741
    /**
742
     * @param mixed[] $trigger
743
     *
744
     * @return mixed
745
     */
746
    protected function _getPortableTriggerDefinition($trigger)
747
    {
748
        return $trigger;
749
    }
750
751
    /**
752
     * @param mixed[][] $sequences
753
     *
754
     * @return Sequence[]
755
     */
756 296
    protected function _getPortableSequencesList($sequences)
757
    {
758 296
        $list = [];
759
760 296
        foreach ($sequences as $value) {
761 296
            $list[] = $this->_getPortableSequenceDefinition($value);
762
        }
763
764 296
        return $list;
765
    }
766
767
    /**
768
     * @param mixed[] $sequence
769
     *
770
     * @return Sequence
771
     *
772
     * @throws DBALException
773
     */
774
    protected function _getPortableSequenceDefinition($sequence)
775
    {
776
        throw DBALException::notSupported('Sequences');
777
    }
778
779
    /**
780
     * Independent of the database the keys of the column list result are lowercased.
781
     *
782
     * The name of the created column instance however is kept in its case.
783
     *
784
     * @param string    $table        The name of the table.
785
     * @param string    $database
786
     * @param mixed[][] $tableColumns
787
     *
788
     * @return Column[]
789
     */
790 4210
    protected function _getPortableTableColumnList($table, $database, $tableColumns)
791
    {
792 4210
        $eventManager = $this->_platform->getEventManager();
793
794 4210
        $list = [];
795 4210
        foreach ($tableColumns as $tableColumn) {
796 4210
            $column           = null;
797 4210
            $defaultPrevented = false;
798
799 4210
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
800 3261
                $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
801 3261
                $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
802
803 3261
                $defaultPrevented = $eventArgs->isDefaultPrevented();
804 3261
                $column           = $eventArgs->getColumn();
805
            }
806
807 4210
            if (! $defaultPrevented) {
808 4210
                $column = $this->_getPortableTableColumnDefinition($tableColumn);
809
            }
810
811 4210
            if (! $column) {
812
                continue;
813
            }
814
815 4210
            $name        = strtolower($column->getQuotedName($this->_platform));
816 4210
            $list[$name] = $column;
817
        }
818
819 4210
        return $list;
820
    }
821
822
    /**
823
     * Gets Table Column Definition.
824
     *
825
     * @param mixed[] $tableColumn
826
     *
827
     * @return Column
828
     */
829
    abstract protected function _getPortableTableColumnDefinition($tableColumn);
830
831
    /**
832
     * Aggregates and groups the index results according to the required data result.
833
     *
834
     * @param mixed[][] $tableIndexRows
835
     *
836
     * @return Index[]
837
     */
838 4203
    protected function _getPortableTableIndexesList(array $tableIndexRows, string $tableName) : array
839
    {
840 4203
        $result = [];
841 4203
        foreach ($tableIndexRows as $tableIndex) {
842 4146
            $indexName = $keyName = $tableIndex['key_name'];
843 4146
            if ($tableIndex['primary']) {
844 4144
                $keyName = 'primary';
845
            }
846 4146
            $keyName = strtolower($keyName);
847
848 4146
            if (! isset($result[$keyName])) {
849
                $options = [
850 4146
                    'lengths' => [],
851
                ];
852
853 4146
                if (isset($tableIndex['where'])) {
854 934
                    $options['where'] = $tableIndex['where'];
855
                }
856
857 4146
                $result[$keyName] = [
858 4146
                    'name' => $indexName,
859
                    'columns' => [],
860 4146
                    'unique' => ! $tableIndex['non_unique'],
861 4146
                    'primary' => $tableIndex['primary'],
862 4146
                    'flags' => $tableIndex['flags'] ?? [],
863 4146
                    'options' => $options,
864
                ];
865
            }
866
867 4146
            $result[$keyName]['columns'][]            = $tableIndex['column_name'];
868 4146
            $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
869
        }
870
871 4203
        $eventManager = $this->_platform->getEventManager();
872
873 4203
        $indexes = [];
874 4203
        foreach ($result as $indexKey => $data) {
875 4146
            $index            = null;
876 4146
            $defaultPrevented = false;
877
878 4146
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
879 3237
                $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
880 3237
                $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
881
882 3237
                $defaultPrevented = $eventArgs->isDefaultPrevented();
883 3237
                $index            = $eventArgs->getIndex();
884
            }
885
886 4146
            if (! $defaultPrevented) {
887 4146
                $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
888
            }
889
890 4146
            if (! $index) {
891
                continue;
892
            }
893
894 4146
            $indexes[$indexKey] = $index;
895
        }
896
897 4203
        return $indexes;
898
    }
899
900
    /**
901
     * @param mixed[][] $tables
902
     *
903
     * @return string[]
904
     */
905 4467
    protected function _getPortableTablesList($tables)
906
    {
907 4467
        $list = [];
908 4467
        foreach ($tables as $value) {
909 4465
            $value = $this->_getPortableTableDefinition($value);
910
911 4465
            if (! $value) {
912
                continue;
913
            }
914
915 4465
            $list[] = $value;
916
        }
917
918 4467
        return $list;
919
    }
920
921
    /**
922
     * @param mixed $table
923
     *
924
     * @return string
925
     */
926
    protected function _getPortableTableDefinition($table)
927
    {
928
        return $table;
929
    }
930
931
    /**
932
     * @param mixed[][] $users
933
     *
934
     * @return string[][]
935
     */
936
    protected function _getPortableUsersList($users)
937
    {
938
        $list = [];
939
        foreach ($users as $value) {
940
            $value = $this->_getPortableUserDefinition($value);
941
942
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type string[] 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...
943
                continue;
944
            }
945
946
            $list[] = $value;
947
        }
948
949
        return $list;
950
    }
951
952
    /**
953
     * @param string[] $user
954
     *
955
     * @return string[]
956
     */
957
    protected function _getPortableUserDefinition($user)
958
    {
959
        return $user;
960
    }
961
962
    /**
963
     * @param mixed[][] $views
964
     *
965
     * @return View[]
966
     */
967 3045
    protected function _getPortableViewsList($views)
968
    {
969 3045
        $list = [];
970 3045
        foreach ($views as $value) {
971 3045
            $view = $this->_getPortableViewDefinition($value);
972
973 3045
            if (! $view) {
974
                continue;
975
            }
976
977 3045
            $viewName        = strtolower($view->getQuotedName($this->_platform));
978 3045
            $list[$viewName] = $view;
979
        }
980
981 3045
        return $list;
982
    }
983
984
    /**
985
     * @param mixed[] $view
986
     *
987
     * @return View|false
988
     */
989
    protected function _getPortableViewDefinition($view)
990
    {
991
        return false;
992
    }
993
994
    /**
995
     * @param mixed[][] $tableForeignKeys
996
     *
997
     * @return ForeignKeyConstraint[]
998
     */
999 1482
    protected function _getPortableTableForeignKeysList($tableForeignKeys)
1000
    {
1001 1482
        $list = [];
1002
1003 1482
        foreach ($tableForeignKeys as $value) {
1004 1383
            $list[] = $this->_getPortableTableForeignKeyDefinition($value);
1005
        }
1006
1007 1482
        return $list;
1008
    }
1009
1010
    /**
1011
     * @param mixed $tableForeignKey
1012
     *
1013
     * @return ForeignKeyConstraint
1014
     */
1015
    protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
1016
    {
1017
        return $tableForeignKey;
1018
    }
1019
1020
    /**
1021
     * @param string[]|string $sql
1022
     *
1023
     * @return void
1024
     */
1025 5358
    protected function _execSql($sql)
1026
    {
1027 5358
        foreach ((array) $sql as $query) {
1028 5358
            $this->_conn->executeUpdate($query);
1029
        }
1030 5331
    }
1031
1032
    /**
1033
     * Creates a schema instance for the current database.
1034
     *
1035
     * @return Schema
1036
     */
1037 3904
    public function createSchema()
1038
    {
1039 3904
        $namespaces = [];
1040
1041 3904
        if ($this->_platform->supportsSchemas()) {
1042 1055
            $namespaces = $this->listNamespaceNames();
1043
        }
1044
1045 3904
        $sequences = [];
1046
1047 3904
        if ($this->_platform->supportsSequences()) {
1048 1056
            $sequences = $this->listSequences();
1049
        }
1050
1051 3904
        $tables = $this->listTables();
1052
1053 3904
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
1054
    }
1055
1056
    /**
1057
     * Creates the configuration for this schema.
1058
     *
1059
     * @return SchemaConfig
1060
     */
1061 4136
    public function createSchemaConfig()
1062
    {
1063 4136
        $schemaConfig = new SchemaConfig();
1064 4136
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
1065
1066 4136
        $searchPaths = $this->getSchemaSearchPaths();
1067 4136
        if (isset($searchPaths[0])) {
1068 3988
            $schemaConfig->setName($searchPaths[0]);
1069
        }
1070
1071 4136
        $params = $this->_conn->getParams();
1072 4136
        if (! isset($params['defaultTableOptions'])) {
1073 4136
            $params['defaultTableOptions'] = [];
1074
        }
1075 4136
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
1076 913
            $params['defaultTableOptions']['charset'] = $params['charset'];
1077
        }
1078 4136
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
1079
1080 4136
        return $schemaConfig;
1081
    }
1082
1083
    /**
1084
     * The search path for namespaces in the currently connected database.
1085
     *
1086
     * The first entry is usually the default namespace in the Schema. All
1087
     * further namespaces contain tables/sequences which can also be addressed
1088
     * with a short, not full-qualified name.
1089
     *
1090
     * For databases that don't support subschema/namespaces this method
1091
     * returns the name of the currently connected database.
1092
     *
1093
     * @return string[]
1094
     */
1095 3394
    public function getSchemaSearchPaths()
1096
    {
1097 3394
        return [$this->_conn->getDatabase()];
1098
    }
1099
1100
    /**
1101
     * Given a table comment this method tries to extract a type hint for Doctrine Type. If the type hint is found,
1102
     * it's removed from the comment.
1103
     *
1104
     * @return string|null The extracted Doctrine type or NULL of the type hint was not found.
1105
     */
1106 4217
    final protected function extractDoctrineTypeFromComment(?string &$comment) : ?string
1107
    {
1108 4217
        if ($comment === null || ! preg_match('/(.*)\(DC2Type:(((?!\)).)+)\)(.*)/', $comment, $match)) {
1109 4210
            return null;
1110
        }
1111
1112 3562
        $comment = $match[1] . $match[4];
1113
1114 3562
        return $match[2];
1115
    }
1116
}
1117