Completed
Pull Request — master (#3532)
by Sebastiaan
65:20
created

_getPortableTableColumnList()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7.0084

Importance

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

242
        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...
243
    }
244
245
    /**
246
     * Lists the tables for this connection.
247
     *
248
     * @return Table[]
249
     */
250 4504
    public function listTables()
251
    {
252 4504
        $tableNames = $this->listTableNames();
253
254 4504
        $tables = [];
255 4504
        foreach ($tableNames as $tableName) {
256 4416
            $tables[] = $this->listTableDetails($tableName);
257
        }
258
259 4504
        return $tables;
260
    }
261
262
    /**
263
     * @param string $tableName
264
     *
265
     * @return Table
266
     */
267 4688
    public function listTableDetails($tableName)
268
    {
269 4688
        $columns     = $this->listTableColumns($tableName);
270 4688
        $foreignKeys = [];
271 4688
        if ($this->_platform->supportsForeignKeyConstraints()) {
272 4688
            $foreignKeys = $this->listTableForeignKeys($tableName);
273
        }
274 4688
        $indexes = $this->listTableIndexes($tableName);
275
276 4688
        return new Table($tableName, $columns, $indexes, $foreignKeys);
277
    }
278
279
    /**
280
     * Lists the views this connection has.
281
     *
282
     * @return View[]
283
     */
284 3192
    public function listViews()
285
    {
286 3192
        $database = $this->_conn->getDatabase();
287 3192
        $sql      = $this->_platform->getListViewsSQL($database);
288 3192
        $views    = $this->_conn->fetchAll($sql);
289
290 3192
        return $this->_getPortableViewsList($views);
291
    }
292
293
    /**
294
     * Lists the foreign keys for the given table.
295
     *
296
     * @param string      $table    The name of the table.
297
     * @param string|null $database
298
     *
299
     * @return ForeignKeyConstraint[]
300
     */
301 4688
    public function listTableForeignKeys($table, $database = null)
302
    {
303 4688
        if ($database === null) {
304 4688
            $database = $this->_conn->getDatabase();
305
        }
306 4688
        $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

306
        /** @scrutinizer ignore-call */ 
307
        $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...
307 4688
        $tableForeignKeys = $this->_conn->fetchAll($sql);
308
309 4688
        return $this->_getPortableTableForeignKeysList($tableForeignKeys);
310
    }
311
312
    /* drop*() Methods */
313
314
    /**
315
     * Drops a database.
316
     *
317
     * NOTE: You can not drop the database this SchemaManager is currently connected to.
318
     *
319
     * @param string $database The name of the database to drop.
320
     *
321
     * @return void
322
     */
323 5426
    public function dropDatabase($database)
324
    {
325 5426
        $this->_execSql($this->_platform->getDropDatabaseSQL($database));
326 4663
    }
327
328
    /**
329
     * Drops the given table.
330
     *
331
     * @param string $tableName The name of the table to drop.
332
     *
333
     * @return void
334
     */
335 5574
    public function dropTable($tableName)
336
    {
337 5574
        $this->_execSql($this->_platform->getDropTableSQL($tableName));
338 5549
    }
339
340
    /**
341
     * Drops the index from the given table.
342
     *
343
     * @param Index|string $index The name of the index.
344
     * @param Table|string $table The name of the table.
345
     *
346
     * @return void
347
     */
348 3319
    public function dropIndex($index, $table)
349
    {
350 3319
        if ($index instanceof Index) {
351
            $index = $index->getQuotedName($this->_platform);
352
        }
353
354 3319
        $this->_execSql($this->_platform->getDropIndexSQL($index, $table));
355 3319
    }
356
357
    /**
358
     * Drops the constraint from the given table.
359
     *
360
     * @param Table|string $table The name of the table.
361
     *
362
     * @return void
363
     */
364
    public function dropConstraint(Constraint $constraint, $table)
365
    {
366
        $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table));
367
    }
368
369
    /**
370
     * Drops a foreign key from a table.
371
     *
372
     * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key.
373
     * @param Table|string                $table      The name of the table with the foreign key.
374
     *
375
     * @return void
376
     */
377
    public function dropForeignKey($foreignKey, $table)
378
    {
379
        $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table));
380
    }
381
382
    /**
383
     * Drops a sequence with a given name.
384
     *
385
     * @param string $name The name of the sequence to drop.
386
     *
387
     * @return void
388
     */
389 1299
    public function dropSequence($name)
390
    {
391 1299
        $this->_execSql($this->_platform->getDropSequenceSQL($name));
392
    }
393
394
    /**
395
     * Drops a view.
396
     *
397
     * @param string $name The name of the view.
398
     *
399
     * @return void
400
     */
401 3384
    public function dropView($name)
402
    {
403 3384
        $this->_execSql($this->_platform->getDropViewSQL($name));
404
    }
405
406
    /* create*() Methods */
407
408
    /**
409
     * Creates a new database.
410
     *
411
     * @param string $database The name of the database to create.
412
     *
413
     * @return void
414
     */
415 5426
    public function createDatabase($database)
416
    {
417 5426
        $this->_execSql($this->_platform->getCreateDatabaseSQL($database));
418 5426
    }
419
420
    /**
421
     * Creates a new table.
422
     *
423
     * @return void
424
     */
425 5574
    public function createTable(Table $table)
426
    {
427 5574
        $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
428 5574
        $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
429 5574
    }
430
431
    /**
432
     * Creates a new sequence.
433
     *
434
     * @param Sequence $sequence
435
     *
436
     * @return void
437
     *
438
     * @throws ConnectionException If something fails at database level.
439
     */
440 1299
    public function createSequence($sequence)
441
    {
442 1299
        $this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
443 1299
    }
444
445
    /**
446
     * Creates a constraint on a table.
447
     *
448
     * @param Table|string $table
449
     *
450
     * @return void
451
     */
452
    public function createConstraint(Constraint $constraint, $table)
453
    {
454
        $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
455
    }
456
457
    /**
458
     * Creates a new index on a table.
459
     *
460
     * @param Table|string $table The name of the table on which the index is to be created.
461
     *
462
     * @return void
463
     */
464 3319
    public function createIndex(Index $index, $table)
465
    {
466 3319
        $this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
467 3319
    }
468
469
    /**
470
     * Creates a new foreign key.
471
     *
472
     * @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
473
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
474
     *
475
     * @return void
476
     */
477 3475
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
478
    {
479 3475
        $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
480 3475
    }
481
482
    /**
483
     * Creates a new view.
484
     *
485
     * @return void
486
     */
487 3384
    public function createView(View $view)
488
    {
489 3384
        $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
490 3384
    }
491
492
    /* dropAndCreate*() Methods */
493
494
    /**
495
     * Drops and creates a constraint.
496
     *
497
     * @see dropConstraint()
498
     * @see createConstraint()
499
     *
500
     * @param Table|string $table
501
     *
502
     * @return void
503
     */
504
    public function dropAndCreateConstraint(Constraint $constraint, $table)
505
    {
506
        $this->tryMethod('dropConstraint', $constraint, $table);
507
        $this->createConstraint($constraint, $table);
508
    }
509
510
    /**
511
     * Drops and creates a new index on a table.
512
     *
513
     * @param Table|string $table The name of the table on which the index is to be created.
514
     *
515
     * @return void
516
     */
517 3319
    public function dropAndCreateIndex(Index $index, $table)
518
    {
519 3319
        $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
520 3319
        $this->createIndex($index, $table);
521 3319
    }
522
523
    /**
524
     * Drops and creates a new foreign key.
525
     *
526
     * @param ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created.
527
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
528
     *
529
     * @return void
530
     */
531
    public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
532
    {
533
        $this->tryMethod('dropForeignKey', $foreignKey, $table);
534
        $this->createForeignKey($foreignKey, $table);
535
    }
536
537
    /**
538
     * Drops and create a new sequence.
539
     *
540
     * @return void
541
     *
542
     * @throws ConnectionException If something fails at database level.
543
     */
544 1299
    public function dropAndCreateSequence(Sequence $sequence)
545
    {
546 1299
        $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
547 1299
        $this->createSequence($sequence);
548 1299
    }
549
550
    /**
551
     * Drops and creates a new table.
552
     *
553
     * @return void
554
     */
555 5574
    public function dropAndCreateTable(Table $table)
556
    {
557 5574
        $this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
558 5574
        $this->createTable($table);
559 5574
    }
560
561
    /**
562
     * Drops and creates a new database.
563
     *
564
     * @param string $database The name of the database to create.
565
     *
566
     * @return void
567
     */
568 5426
    public function dropAndCreateDatabase($database)
569
    {
570 5426
        $this->tryMethod('dropDatabase', $database);
571 5426
        $this->createDatabase($database);
572 5426
    }
573
574
    /**
575
     * Drops and creates a new view.
576
     *
577
     * @return void
578
     */
579 3384
    public function dropAndCreateView(View $view)
580
    {
581 3384
        $this->tryMethod('dropView', $view->getQuotedName($this->_platform));
582 3384
        $this->createView($view);
583 3384
    }
584
585
    /* alterTable() Methods */
586
587
    /**
588
     * Alters an existing tables schema.
589
     *
590
     * @return void
591
     */
592 4006
    public function alterTable(TableDiff $tableDiff)
593
    {
594 4006
        $queries = $this->_platform->getAlterTableSQL($tableDiff);
595 4006
        if (! is_array($queries) || ! count($queries)) {
596 1552
            return;
597
        }
598
599 4006
        foreach ($queries as $ddlQuery) {
600 4006
            $this->_execSql($ddlQuery);
601
        }
602 4006
    }
603
604
    /**
605
     * Renames a given table to another name.
606
     *
607
     * @param string $name    The current name of the table.
608
     * @param string $newName The new name of the table.
609
     *
610
     * @return void
611
     */
612
    public function renameTable($name, $newName)
613
    {
614
        $tableDiff          = new TableDiff($name);
615
        $tableDiff->newName = $newName;
616
        $this->alterTable($tableDiff);
617
    }
618
619
    /**
620
     * Methods for filtering return values of list*() methods to convert
621
     * the native DBMS data definition to a portable Doctrine definition
622
     */
623
624
    /**
625
     * @param mixed[] $databases
626
     *
627
     * @return string[]
628
     */
629 3268
    protected function _getPortableDatabasesList($databases)
630
    {
631 3268
        $list = [];
632 3268
        foreach ($databases as $value) {
633 3268
            $value = $this->_getPortableDatabaseDefinition($value);
634
635 3268
            if (! $value) {
636
                continue;
637
            }
638
639 3268
            $list[] = $value;
640
        }
641
642 3268
        return $list;
643
    }
644
645
    /**
646
     * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
647
     *
648
     * @param mixed[][] $namespaces The list of namespace names in the native DBMS data definition.
649
     *
650
     * @return string[]
651
     */
652 1272
    protected function getPortableNamespacesList(array $namespaces)
653
    {
654 1272
        $namespacesList = [];
655
656 1272
        foreach ($namespaces as $namespace) {
657 1272
            $namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
658
        }
659
660 1272
        return $namespacesList;
661
    }
662
663
    /**
664
     * @param mixed $database
665
     *
666
     * @return mixed
667
     */
668
    protected function _getPortableDatabaseDefinition($database)
669
    {
670
        return $database;
671
    }
672
673
    /**
674
     * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
675
     *
676
     * @param mixed[] $namespace The native DBMS namespace definition.
677
     *
678
     * @return mixed
679
     */
680
    protected function getPortableNamespaceDefinition(array $namespace)
681
    {
682
        return $namespace;
683
    }
684
685
    /**
686
     * @param mixed[][] $functions
687
     *
688
     * @return mixed[][]
689
     */
690
    protected function _getPortableFunctionsList($functions)
691
    {
692
        $list = [];
693
        foreach ($functions as $value) {
694
            $value = $this->_getPortableFunctionDefinition($value);
695
696
            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...
697
                continue;
698
            }
699
700
            $list[] = $value;
701
        }
702
703
        return $list;
704
    }
705
706
    /**
707
     * @param mixed[] $function
708
     *
709
     * @return mixed
710
     */
711
    protected function _getPortableFunctionDefinition($function)
712
    {
713
        return $function;
714
    }
715
716
    /**
717
     * @param mixed[][] $triggers
718
     *
719
     * @return mixed[][]
720
     */
721
    protected function _getPortableTriggersList($triggers)
722
    {
723
        $list = [];
724
        foreach ($triggers as $value) {
725
            $value = $this->_getPortableTriggerDefinition($value);
726
727
            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...
728
                continue;
729
            }
730
731
            $list[] = $value;
732
        }
733
734
        return $list;
735
    }
736
737
    /**
738
     * @param mixed[] $trigger
739
     *
740
     * @return mixed
741
     */
742
    protected function _getPortableTriggerDefinition($trigger)
743
    {
744
        return $trigger;
745
    }
746
747
    /**
748
     * @param mixed[][] $sequences
749
     *
750
     * @return Sequence[]
751
     */
752 293
    protected function _getPortableSequencesList($sequences)
753
    {
754 293
        $list = [];
755
756 293
        foreach ($sequences as $value) {
757 293
            $list[] = $this->_getPortableSequenceDefinition($value);
758
        }
759
760 293
        return $list;
761
    }
762
763
    /**
764
     * @param mixed[] $sequence
765
     *
766
     * @return Sequence
767
     *
768
     * @throws DBALException
769
     */
770
    protected function _getPortableSequenceDefinition($sequence)
771
    {
772
        throw DBALException::notSupported('Sequences');
773
    }
774
775
    /**
776
     * Independent of the database the keys of the column list result are lowercased.
777
     *
778
     * The name of the created column instance however is kept in its case.
779
     *
780
     * @param string    $table        The name of the table.
781
     * @param string    $database
782
     * @param mixed[][] $tableColumns
783
     *
784
     * @return Column[]
785
     */
786 4688
    protected function _getPortableTableColumnList($table, $database, $tableColumns)
787
    {
788 4688
        $eventManager = $this->_platform->getEventManager();
789
790 4688
        $list = [];
791 4688
        foreach ($tableColumns as $tableColumn) {
792 4688
            $column           = null;
793 4688
            $defaultPrevented = false;
794
795 4688
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
796 3419
                $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
797 3419
                $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
798
799 3419
                $defaultPrevented = $eventArgs->isDefaultPrevented();
800 3419
                $column           = $eventArgs->getColumn();
801
            }
802
803 4688
            if (! $defaultPrevented) {
804 4688
                try {
805
                    $column = $this->_getPortableTableColumnDefinition($tableColumn);
806
                } catch (\Doctrine\DBAL\DBALException $e) {
807 4688
                    throw new \Doctrine\DBAL\DBALException($e->getMessage() . ' ' . sprintf(
808
                        'at %s.%s.%s',
809
                        $database,
810
                        $table,
811 4688
                        $tableColumn
0 ignored issues
show
Bug introduced by
$tableColumn of type array<mixed,mixed> is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

811
                        /** @scrutinizer ignore-type */ $tableColumn
Loading history...
812 4688
                    ));
813
                }
814
            }
815 4688
816
            if (! $column) {
817
                continue;
818
            }
819
820
            $name        = strtolower($column->getQuotedName($this->_platform));
821
            $list[$name] = $column;
822
        }
823
824
        return $list;
825
    }
826
827
    /**
828
     * Gets Table Column Definition.
829
     *
830
     * @param mixed[] $tableColumn
831
     *
832
     * @return Column
833
     */
834
    abstract protected function _getPortableTableColumnDefinition($tableColumn);
835 4688
836
    /**
837 4688
     * Aggregates and groups the index results according to the required data result.
838 4688
     *
839 4512
     * @param mixed[][]   $tableIndexRows
840 4512
     * @param string|null $tableName
841 4512
     *
842
     * @return Index[]
843 4512
     */
844
    protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null)
845 4512
    {
846
        $result = [];
847 4512
        foreach ($tableIndexRows as $tableIndex) {
848
            $indexName = $keyName = $tableIndex['key_name'];
849
            if ($tableIndex['primary']) {
850 4512
                $keyName = 'primary';
851 1079
            }
852
            $keyName = strtolower($keyName);
853
854 4512
            if (! isset($result[$keyName])) {
855 4512
                $options = [
856
                    'lengths' => [],
857 4512
                ];
858 4512
859 4512
                if (isset($tableIndex['where'])) {
860 4512
                    $options['where'] = $tableIndex['where'];
861
                }
862
863
                $result[$keyName] = [
864 4512
                    'name' => $indexName,
865 4512
                    'columns' => [],
866
                    'unique' => ! $tableIndex['non_unique'],
867
                    'primary' => $tableIndex['primary'],
868 4688
                    'flags' => $tableIndex['flags'] ?? [],
869
                    'options' => $options,
870 4688
                ];
871 4688
            }
872 4512
873 4512
            $result[$keyName]['columns'][]            = $tableIndex['column_name'];
874
            $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
875 4512
        }
876 3394
877 3394
        $eventManager = $this->_platform->getEventManager();
878
879 3394
        $indexes = [];
880 3394
        foreach ($result as $indexKey => $data) {
881
            $index            = null;
882
            $defaultPrevented = false;
883 4512
884 4512
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
885
                $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
886
                $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
887 4512
888
                $defaultPrevented = $eventArgs->isDefaultPrevented();
889
                $index            = $eventArgs->getIndex();
890
            }
891 4512
892
            if (! $defaultPrevented) {
893
                $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
894 4688
            }
895
896
            if (! $index) {
897
                continue;
898
            }
899
900
            $indexes[$indexKey] = $index;
901
        }
902 4754
903
        return $indexes;
904 4754
    }
905 4754
906 4752
    /**
907
     * @param mixed[][] $tables
908 4752
     *
909
     * @return string[]
910
     */
911
    protected function _getPortableTablesList($tables)
912 4752
    {
913
        $list = [];
914
        foreach ($tables as $value) {
915 4754
            $value = $this->_getPortableTableDefinition($value);
916
917
            if (! $value) {
918
                continue;
919
            }
920
921
            $list[] = $value;
922
        }
923
924
        return $list;
925
    }
926
927
    /**
928
     * @param mixed $table
929
     *
930
     * @return string
931
     */
932
    protected function _getPortableTableDefinition($table)
933
    {
934
        return $table;
935
    }
936
937
    /**
938
     * @param mixed[][] $users
939
     *
940
     * @return string[][]
941
     */
942
    protected function _getPortableUsersList($users)
943
    {
944
        $list = [];
945
        foreach ($users as $value) {
946
            $value = $this->_getPortableUserDefinition($value);
947
948
            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...
949
                continue;
950
            }
951
952
            $list[] = $value;
953
        }
954
955
        return $list;
956
    }
957
958
    /**
959
     * @param string[] $user
960
     *
961
     * @return string[]
962
     */
963
    protected function _getPortableUserDefinition($user)
964 3192
    {
965
        return $user;
966 3192
    }
967 3192
968 3192
    /**
969
     * @param mixed[][] $views
970 3192
     *
971
     * @return View[]
972
     */
973
    protected function _getPortableViewsList($views)
974 3192
    {
975 3192
        $list = [];
976
        foreach ($views as $value) {
977
            $view = $this->_getPortableViewDefinition($value);
978 3192
979
            if (! $view) {
980
                continue;
981
            }
982
983
            $viewName        = strtolower($view->getQuotedName($this->_platform));
984
            $list[$viewName] = $view;
985
        }
986
987
        return $list;
988
    }
989
990
    /**
991
     * @param mixed[] $view
992
     *
993
     * @return View|false
994
     */
995
    protected function _getPortableViewDefinition($view)
996 1942
    {
997
        return false;
998 1942
    }
999
1000 1942
    /**
1001 1694
     * @param mixed[][] $tableForeignKeys
1002
     *
1003
     * @return ForeignKeyConstraint[]
1004 1942
     */
1005
    protected function _getPortableTableForeignKeysList($tableForeignKeys)
1006
    {
1007
        $list = [];
1008
1009
        foreach ($tableForeignKeys as $value) {
1010
            $list[] = $this->_getPortableTableForeignKeyDefinition($value);
1011
        }
1012
1013
        return $list;
1014
    }
1015
1016
    /**
1017
     * @param mixed $tableForeignKey
1018
     *
1019
     * @return ForeignKeyConstraint
1020
     */
1021
    protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
1022 5795
    {
1023
        return $tableForeignKey;
1024 5795
    }
1025 5795
1026
    /**
1027 5795
     * @param string[]|string $sql
1028
     *
1029
     * @return void
1030
     */
1031
    protected function _execSql($sql)
1032
    {
1033
        foreach ((array) $sql as $query) {
1034 4313
            $this->_conn->executeUpdate($query);
1035
        }
1036 4313
    }
1037
1038 4313
    /**
1039 1180
     * Creates a schema instance for the current database.
1040
     *
1041
     * @return Schema
1042 4313
     */
1043
    public function createSchema()
1044 4313
    {
1045 1180
        $namespaces = [];
1046
1047
        if ($this->_platform->supportsSchemas()) {
1048 4313
            $namespaces = $this->listNamespaceNames();
1049
        }
1050 4313
1051
        $sequences = [];
1052
1053
        if ($this->_platform->supportsSequences()) {
1054
            $sequences = $this->listSequences();
1055
        }
1056
1057
        $tables = $this->listTables();
1058 4539
1059
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
1060 4539
    }
1061 4539
1062
    /**
1063 4539
     * Creates the configuration for this schema.
1064 4539
     *
1065 4539
     * @return SchemaConfig
1066
     */
1067
    public function createSchemaConfig()
1068 4539
    {
1069 4539
        $schemaConfig = new SchemaConfig();
1070 4539
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
1071
1072 4539
        $searchPaths = $this->getSchemaSearchPaths();
1073 950
        if (isset($searchPaths[0])) {
1074
            $schemaConfig->setName($searchPaths[0]);
1075 4539
        }
1076
1077 4539
        $params = $this->_conn->getParams();
1078
        if (! isset($params['defaultTableOptions'])) {
1079
            $params['defaultTableOptions'] = [];
1080
        }
1081
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
1082
            $params['defaultTableOptions']['charset'] = $params['charset'];
1083
        }
1084
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
1085
1086
        return $schemaConfig;
1087
    }
1088
1089
    /**
1090
     * The search path for namespaces in the currently connected database.
1091
     *
1092 3684
     * The first entry is usually the default namespace in the Schema. All
1093
     * further namespaces contain tables/sequences which can also be addressed
1094 3684
     * with a short, not full-qualified name.
1095
     *
1096
     * For databases that don't support subschema/namespaces this method
1097
     * returns the name of the currently connected database.
1098
     *
1099
     * @return string[]
1100
     */
1101
    public function getSchemaSearchPaths()
1102
    {
1103
        return [$this->_conn->getDatabase()];
1104
    }
1105
1106 4686
    /**
1107
     * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns
1108 4686
     * the type given as default.
1109 3699
     *
1110
     * @param string|null $comment
1111
     * @param string      $currentType
1112 4648
     *
1113
     * @return string
1114
     */
1115
    public function extractDoctrineTypeFromComment($comment, $currentType)
1116
    {
1117
        if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match)) {
1118
            return $match[1];
1119
        }
1120
1121 4686
        return $currentType;
1122
    }
1123 4686
1124 2783
    /**
1125
     * @param string|null $comment
1126
     * @param string|null $type
1127 4357
     *
1128
     * @return string|null
1129
     */
1130
    public function removeDoctrineTypeFromComment($comment, $type)
1131
    {
1132
        if ($comment === null) {
1133
            return null;
1134
        }
1135
1136
        return str_replace('(DC2Type:' . $type . ')', '', $comment);
1137
    }
1138
}
1139