Passed
Pull Request — develop (#3501)
by Sergei
64:34
created

AbstractSchemaManager::removeDoctrineTypeFromComment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

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 2
nc 2
nop 2
crap 2
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 strtolower;
25
26
/**
27
 * Base class for schema managers. Schema managers are used to inspect and/or
28
 * modify the database schema/structure.
29
 */
30
abstract class AbstractSchemaManager
31
{
32
    /**
33
     * Holds instance of the Doctrine connection for this schema manager.
34
     *
35
     * @var Connection
36
     */
37
    protected $_conn;
38
39
    /**
40
     * Holds instance of the database platform used for this schema manager.
41
     *
42
     * @var AbstractPlatform
43
     */
44
    protected $_platform;
45
46
    /**
47
     * Constructor. Accepts the Connection instance to manage the schema for.
48
     */
49
    public function __construct(Connection $conn, ?AbstractPlatform $platform = null)
50 5537
    {
51
        $this->_conn     = $conn;
52 5537
        $this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
53 5537
    }
54 5537
55
    /**
56
     * Returns the associated platform.
57
     *
58
     * @return AbstractPlatform
59
     */
60
    public function getDatabasePlatform()
61 3718
    {
62
        return $this->_platform;
63 3718
    }
64
65
    /**
66
     * Tries any method on the schema manager. Normally a method throws an
67
     * exception when your DBMS doesn't support it or if an error occurs.
68
     * This method allows you to try and method on your SchemaManager
69
     * instance and will return false if it does not work or is not supported.
70
     *
71
     * <code>
72
     * $result = $sm->tryMethod('dropView', 'view_name');
73
     * </code>
74
     *
75
     * @return mixed
76
     */
77
    public function tryMethod()
78 5349
    {
79
        $args   = func_get_args();
80 5349
        $method = $args[0];
81 5349
        unset($args[0]);
82 5349
        $args = array_values($args);
83 5349
84
        $callback = [$this, $method];
85 5349
        assert(is_callable($callback));
86 5349
87
        try {
88
            return call_user_func_array($callback, $args);
89 5349
        } catch (Throwable $e) {
90 5223
            return false;
91 5223
        }
92
    }
93
94
    /**
95
     * Lists the available databases for this connection.
96
     *
97
     * @return string[]
98
     */
99
    public function listDatabases()
100 3301
    {
101
        $sql = $this->_platform->getListDatabasesSQL();
102 3301
103
        $databases = $this->_conn->fetchAll($sql);
104 3156
105
        return $this->_getPortableDatabasesList($databases);
106 3156
    }
107
108
    /**
109
     * Returns a list of all namespaces in the current database.
110
     *
111
     * @return string[]
112
     */
113
    public function listNamespaceNames()
114 1139
    {
115
        $sql = $this->_platform->getListNamespacesSQL();
116 1139
117
        $namespaces = $this->_conn->fetchAll($sql);
118 1139
119
        return $this->getPortableNamespacesList($namespaces);
120 1139
    }
121
122
    /**
123
     * Lists the available sequences for this connection.
124
     *
125
     * @param string|null $database
126
     *
127
     * @return Sequence[]
128
     */
129
    public function listSequences($database = null)
130 1168
    {
131
        if ($database === null) {
132 1168
            $database = $this->_conn->getDatabase();
133 1168
        }
134
        $sql = $this->_platform->getListSequencesSQL($database);
135 1168
136
        $sequences = $this->_conn->fetchAll($sql);
137 1168
138
        return $this->filterAssetNames($this->_getPortableSequencesList($sequences));
139 1168
    }
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
     * @param string      $table    The name of the table.
152
     * @param string|null $database
153
     *
154
     * @return Column[]
155
     */
156
    public function listTableColumns($table, $database = null)
157 4230
    {
158
        if (! $database) {
159 4230
            $database = $this->_conn->getDatabase();
160 4229
        }
161
162
        $sql = $this->_platform->getListTableColumnsSQL($table, $database);
163 4230
164
        $tableColumns = $this->_conn->fetchAll($sql);
165 4230
166
        return $this->_getPortableTableColumnList($table, $database, $tableColumns);
167 4230
    }
168
169
    /**
170
     * Lists the indexes for a given table returning an array of Index instances.
171
     *
172
     * Keys of the portable indexes list are all lower-cased.
173
     *
174
     * @param string $table The name of the table.
175
     *
176
     * @return Index[]
177
     */
178
    public function listTableIndexes($table)
179 3920
    {
180
        $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
181 3920
182
        $tableIndexes = $this->_conn->fetchAll($sql);
183 3920
184
        return $this->_getPortableTableIndexesList($tableIndexes, $table);
185 3920
    }
186
187
    /**
188
     * Returns true if all the given tables exist.
189
     *
190
     * @param string|string[] $tableNames
191
     *
192
     * @return bool
193
     */
194
    public function tablesExist($tableNames)
195 4646
    {
196
        $tableNames = array_map('strtolower', (array) $tableNames);
197 4646
198
        return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
199 4646
    }
200
201
    /**
202
     * Returns a list of all tables in the current database.
203
     *
204
     * @return string[]
205
     */
206
    public function listTableNames()
207 4490
    {
208
        $sql = $this->_platform->getListTablesSQL();
209 4490
210
        $tables     = $this->_conn->fetchAll($sql);
211 4490
        $tableNames = $this->_getPortableTablesList($tables);
212 4490
213
        return $this->filterAssetNames($tableNames);
214 4490
    }
215
216
    /**
217
     * Filters asset names if they are configured to return only a subset of all
218
     * the found elements.
219
     *
220
     * @param mixed[] $assetNames
221
     *
222
     * @return mixed[]
223
     */
224
    protected function filterAssetNames($assetNames)
225 4665
    {
226
        $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
227 4665
        if (! $filter) {
228 4665
            return $assetNames;
229 4665
        }
230
231
        return array_values(array_filter($assetNames, $filter));
232 1662
    }
233
234
    /**
235
     * @deprecated Use Configuration::getSchemaAssetsFilter() instead
236
     *
237
     * @return string|null
238
     */
239
    protected function getFilterSchemaAssetsExpression()
240
    {
241
        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

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

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