Completed
Pull Request — 2.10.x (#4009)
by Grégoire
08:50
created

_getPortableTableColumnList()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.0061

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 17
cts 18
cp 0.9444
rs 8.8017
c 0
b 0
f 0
cc 6
nc 9
nop 3
crap 6.0061
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 5454
    public function __construct(Connection $conn, ?AbstractPlatform $platform = null)
51
    {
52 5454
        $this->_conn     = $conn;
53 5454
        $this->_platform = $platform ?: $this->_conn->getDatabasePlatform();
54 5454
    }
55
56
    /**
57
     * Returns the associated platform.
58
     *
59
     * @return AbstractPlatform
60
     */
61 3465
    public function getDatabasePlatform()
62
    {
63 3465
        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 5252
    public function tryMethod()
79
    {
80 5252
        $args   = func_get_args();
81 5252
        $method = $args[0];
82 5252
        unset($args[0]);
83 5252
        $args = array_values($args);
84
85 5252
        $callback = [$this, $method];
86 5252
        assert(is_callable($callback));
87
88
        try {
89 5252
            return call_user_func_array($callback, $args);
90 5132
        } catch (Throwable $e) {
91 5132
            return false;
92
        }
93
    }
94
95
    /**
96
     * Lists the available databases for this connection.
97
     *
98
     * @return string[]
99
     */
100 3085
    public function listDatabases()
101
    {
102 3085
        $sql = $this->_platform->getListDatabasesSQL();
103
104 2935
        $databases = $this->_conn->fetchAll($sql);
105
106 2935
        return $this->_getPortableDatabasesList($databases);
107
    }
108
109
    /**
110
     * Returns a list of all namespaces in the current database.
111
     *
112
     * @return string[]
113
     */
114 1247
    public function listNamespaceNames()
115
    {
116 1247
        $sql = $this->_platform->getListNamespacesSQL();
117
118 1247
        $namespaces = $this->_conn->fetchAll($sql);
119
120 1247
        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 1279
    public function listSequences($database = null)
131
    {
132 1279
        if ($database === null) {
133 1279
            $database = $this->_conn->getDatabase();
134
        }
135
136 1279
        $sql = $this->_platform->getListSequencesSQL($database);
137
138 1279
        $sequences = $this->_conn->fetchAll($sql);
139
140 1279
        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. Where 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 4102
    public function listTableColumns($table, $database = null)
159
    {
160 4102
        if (! $database) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $database of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
161 4101
            $database = $this->_conn->getDatabase();
162
        }
163
164 4102
        $sql = $this->_platform->getListTableColumnsSQL($table, $database);
165
166 4102
        $tableColumns = $this->_conn->fetchAll($sql);
167
168 4102
        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 3783
    public function listTableIndexes($table)
181
    {
182 3783
        $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase());
183
184 3783
        $tableIndexes = $this->_conn->fetchAll($sql);
185
186 3783
        return $this->_getPortableTableIndexesList($tableIndexes, $table);
187
    }
188
189
    /**
190
     * Returns true if all the given tables exist.
191
     *
192
     * The usage of a string $tableNames is deprecated. Pass a one-element array instead.
193
     *
194
     * @param string|string[] $tableNames
195
     *
196
     * @return bool
197
     */
198 4564
    public function tablesExist($tableNames)
199
    {
200 4564
        $tableNames = array_map('strtolower', (array) $tableNames);
201
202 4564
        return count($tableNames) === count(array_intersect($tableNames, array_map('strtolower', $this->listTableNames())));
203
    }
204
205
    /**
206
     * Returns a list of all tables in the current database.
207
     *
208
     * @return string[]
209
     */
210 4398
    public function listTableNames()
211
    {
212 4398
        $sql = $this->_platform->getListTablesSQL();
213
214 4398
        $tables     = $this->_conn->fetchAll($sql);
215 4398
        $tableNames = $this->_getPortableTablesList($tables);
216
217 4398
        return $this->filterAssetNames($tableNames);
218
    }
219
220
    /**
221
     * Filters asset names if they are configured to return only a subset of all
222
     * the found elements.
223
     *
224
     * @param mixed[] $assetNames
225
     *
226
     * @return mixed[]
227
     */
228 4584
    protected function filterAssetNames($assetNames)
229
    {
230 4584
        $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter();
231 4584
        if (! $filter) {
232 4584
            return $assetNames;
233
        }
234
235 1664
        return array_values(array_filter($assetNames, $filter));
236
    }
237
238
    /**
239
     * @deprecated Use Configuration::getSchemaAssetsFilter() instead
240
     *
241
     * @return string|null
242
     */
243
    protected function getFilterSchemaAssetsExpression()
244
    {
245
        return $this->_conn->getConfiguration()->getFilterSchemaAssetsExpression();
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\DBAL\Configurat...chemaAssetsExpression() has been deprecated with message: Use Configuration::getSchemaAssetsFilter() instead

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

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

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

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
312 3967
        $tableForeignKeys = $this->_conn->fetchAll($sql);
313
314 3967
        return $this->_getPortableTableForeignKeysList($tableForeignKeys);
315
    }
316
317
    /* drop*() Methods */
318
319
    /**
320
     * Drops a database.
321
     *
322
     * NOTE: You can not drop the database this SchemaManager is currently connected to.
323
     *
324
     * @param string $database The name of the database to drop.
325
     *
326
     * @return void
327
     */
328 4752
    public function dropDatabase($database)
329
    {
330 4752
        $this->_execSql($this->_platform->getDropDatabaseSQL($database));
331 4024
    }
332
333
    /**
334
     * Drops the given table.
335
     *
336
     * @param string $tableName The name of the table to drop.
337
     *
338
     * @return void
339
     */
340 5051
    public function dropTable($tableName)
341
    {
342 5051
        $this->_execSql($this->_platform->getDropTableSQL($tableName));
343 4977
    }
344
345
    /**
346
     * Drops the index from the given table.
347
     *
348
     * @param Index|string $index The name of the index.
349
     * @param Table|string $table The name of the table.
350
     *
351
     * @return void
352
     */
353 2977
    public function dropIndex($index, $table)
354
    {
355 2977
        if ($index instanceof Index) {
356
            $index = $index->getQuotedName($this->_platform);
357
        }
358
359 2977
        $this->_execSql($this->_platform->getDropIndexSQL($index, $table));
360 2977
    }
361
362
    /**
363
     * Drops the constraint from the given table.
364
     *
365
     * @param Table|string $table The name of the table.
366
     *
367
     * @return void
368
     */
369
    public function dropConstraint(Constraint $constraint, $table)
370
    {
371
        $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table));
372
    }
373
374
    /**
375
     * Drops a foreign key from a table.
376
     *
377
     * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key.
378
     * @param Table|string                $table      The name of the table with the foreign key.
379
     *
380
     * @return void
381
     */
382
    public function dropForeignKey($foreignKey, $table)
383
    {
384
        $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table));
385
    }
386
387
    /**
388
     * Drops a sequence with a given name.
389
     *
390
     * @param string $name The name of the sequence to drop.
391
     *
392
     * @return void
393
     */
394 1276
    public function dropSequence($name)
395
    {
396 1276
        $this->_execSql($this->_platform->getDropSequenceSQL($name));
397
    }
398
399
    /**
400
     * Drops a view.
401
     *
402
     * @param string $name The name of the view.
403
     *
404
     * @return void
405
     */
406 3052
    public function dropView($name)
407
    {
408 3052
        $this->_execSql($this->_platform->getDropViewSQL($name));
409
    }
410
411
    /* create*() Methods */
412
413
    /**
414
     * Creates a new database.
415
     *
416
     * @param string $database The name of the database to create.
417
     *
418
     * @return void
419
     */
420 4750
    public function createDatabase($database)
421
    {
422 4750
        $this->_execSql($this->_platform->getCreateDatabaseSQL($database));
423 4750
    }
424
425
    /**
426
     * Creates a new table.
427
     *
428
     * @return void
429
     */
430 5068
    public function createTable(Table $table)
431
    {
432 5068
        $createFlags = AbstractPlatform::CREATE_INDEXES|AbstractPlatform::CREATE_FOREIGNKEYS;
433 5068
        $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags));
434 5047
    }
435
436
    /**
437
     * Creates a new sequence.
438
     *
439
     * @param Sequence $sequence
440
     *
441
     * @return void
442
     *
443
     * @throws ConnectionException If something fails at database level.
444
     */
445 1278
    public function createSequence($sequence)
446
    {
447 1278
        $this->_execSql($this->_platform->getCreateSequenceSQL($sequence));
448 1278
    }
449
450
    /**
451
     * Creates a constraint on a table.
452
     *
453
     * @param Table|string $table
454
     *
455
     * @return void
456
     */
457 1
    public function createConstraint(Constraint $constraint, $table)
458
    {
459 1
        $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table));
460 1
    }
461
462
    /**
463
     * Creates a new index on a table.
464
     *
465
     * @param Table|string $table The name of the table on which the index is to be created.
466
     *
467
     * @return void
468
     */
469 2977
    public function createIndex(Index $index, $table)
470
    {
471 2977
        $this->_execSql($this->_platform->getCreateIndexSQL($index, $table));
472 2977
    }
473
474
    /**
475
     * Creates a new foreign key.
476
     *
477
     * @param ForeignKeyConstraint $foreignKey The ForeignKey instance.
478
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
479
     *
480
     * @return void
481
     */
482 3021
    public function createForeignKey(ForeignKeyConstraint $foreignKey, $table)
483
    {
484 3021
        $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table));
485 3021
    }
486
487
    /**
488
     * Creates a new view.
489
     *
490
     * @return void
491
     */
492 3052
    public function createView(View $view)
493
    {
494 3052
        $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql()));
495 3052
    }
496
497
    /* dropAndCreate*() Methods */
498
499
    /**
500
     * Drops and creates a constraint.
501
     *
502
     * @see dropConstraint()
503
     * @see createConstraint()
504
     *
505
     * @param Table|string $table
506
     *
507
     * @return void
508
     */
509
    public function dropAndCreateConstraint(Constraint $constraint, $table)
510
    {
511
        $this->tryMethod('dropConstraint', $constraint, $table);
512
        $this->createConstraint($constraint, $table);
513
    }
514
515
    /**
516
     * Drops and creates a new index on a table.
517
     *
518
     * @param Table|string $table The name of the table on which the index is to be created.
519
     *
520
     * @return void
521
     */
522 2977
    public function dropAndCreateIndex(Index $index, $table)
523
    {
524 2977
        $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table);
525 2977
        $this->createIndex($index, $table);
526 2977
    }
527
528
    /**
529
     * Drops and creates a new foreign key.
530
     *
531
     * @param ForeignKeyConstraint $foreignKey An associative array that defines properties of the foreign key to be created.
532
     * @param Table|string         $table      The name of the table on which the foreign key is to be created.
533
     *
534
     * @return void
535
     */
536
    public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table)
537
    {
538
        $this->tryMethod('dropForeignKey', $foreignKey, $table);
539
        $this->createForeignKey($foreignKey, $table);
540
    }
541
542
    /**
543
     * Drops and create a new sequence.
544
     *
545
     * @return void
546
     *
547
     * @throws ConnectionException If something fails at database level.
548
     */
549 1276
    public function dropAndCreateSequence(Sequence $sequence)
550
    {
551 1276
        $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform));
552 1276
        $this->createSequence($sequence);
553 1276
    }
554
555
    /**
556
     * Drops and creates a new table.
557
     *
558
     * @return void
559
     */
560 5010
    public function dropAndCreateTable(Table $table)
561
    {
562 5010
        $this->tryMethod('dropTable', $table->getQuotedName($this->_platform));
563 5010
        $this->createTable($table);
564 5010
    }
565
566
    /**
567
     * Drops and creates a new database.
568
     *
569
     * @param string $database The name of the database to create.
570
     *
571
     * @return void
572
     */
573 4900
    public function dropAndCreateDatabase($database)
574
    {
575 4900
        $this->tryMethod('dropDatabase', $database);
576 4900
        $this->createDatabase($database);
577 4900
    }
578
579
    /**
580
     * Drops and creates a new view.
581
     *
582
     * @return void
583
     */
584 3052
    public function dropAndCreateView(View $view)
585
    {
586 3052
        $this->tryMethod('dropView', $view->getQuotedName($this->_platform));
587 3052
        $this->createView($view);
588 3052
    }
589
590
    /* alterTable() Methods */
591
592
    /**
593
     * Alters an existing tables schema.
594
     *
595
     * @return void
596
     */
597 3650
    public function alterTable(TableDiff $tableDiff)
598
    {
599 3650
        $queries = $this->_platform->getAlterTableSQL($tableDiff);
600 3650
        if (! is_array($queries) || ! count($queries)) {
601 1224
            return;
602
        }
603
604 3650
        foreach ($queries as $ddlQuery) {
605 3650
            $this->_execSql($ddlQuery);
606
        }
607 3650
    }
608
609
    /**
610
     * Renames a given table to another name.
611
     *
612
     * @param string $name    The current name of the table.
613
     * @param string $newName The new name of the table.
614
     *
615
     * @return void
616
     */
617 1
    public function renameTable($name, $newName)
618
    {
619 1
        $tableDiff          = new TableDiff($name);
620 1
        $tableDiff->newName = $newName;
621 1
        $this->alterTable($tableDiff);
622 1
    }
623
624
    /**
625
     * Methods for filtering return values of list*() methods to convert
626
     * the native DBMS data definition to a portable Doctrine definition
627
     */
628
629
    /**
630
     * @param mixed[] $databases
631
     *
632
     * @return string[]
633
     */
634 2935
    protected function _getPortableDatabasesList($databases)
635
    {
636 2935
        $list = [];
637 2935
        foreach ($databases as $value) {
638 2935
            $value = $this->_getPortableDatabaseDefinition($value);
639
640 2935
            if (! $value) {
641
                continue;
642
            }
643
644 2935
            $list[] = $value;
645
        }
646
647 2935
        return $list;
648
    }
649
650
    /**
651
     * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition.
652
     *
653
     * @param mixed[][] $namespaces The list of namespace names in the native DBMS data definition.
654
     *
655
     * @return string[]
656
     */
657 1247
    protected function getPortableNamespacesList(array $namespaces)
658
    {
659 1247
        $namespacesList = [];
660
661 1247
        foreach ($namespaces as $namespace) {
662 1247
            $namespacesList[] = $this->getPortableNamespaceDefinition($namespace);
663
        }
664
665 1247
        return $namespacesList;
666
    }
667
668
    /**
669
     * @param mixed $database
670
     *
671
     * @return mixed
672
     */
673
    protected function _getPortableDatabaseDefinition($database)
674
    {
675
        return $database;
676
    }
677
678
    /**
679
     * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition.
680
     *
681
     * @param mixed[] $namespace The native DBMS namespace definition.
682
     *
683
     * @return mixed
684
     */
685
    protected function getPortableNamespaceDefinition(array $namespace)
686
    {
687
        return $namespace;
688
    }
689
690
    /**
691
     * @deprecated
692
     *
693
     * @param mixed[][] $functions
694
     *
695
     * @return mixed[][]
696
     */
697
    protected function _getPortableFunctionsList($functions)
698
    {
699
        $list = [];
700
        foreach ($functions as $value) {
701
            $value = $this->_getPortableFunctionDefinition($value);
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\DBAL\Schema\Abs...bleFunctionDefinition() has been deprecated.

This method has been deprecated.

Loading history...
702
703
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array 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...
704
                continue;
705
            }
706
707
            $list[] = $value;
708
        }
709
710
        return $list;
711
    }
712
713
    /**
714
     * @deprecated
715
     *
716
     * @param mixed[] $function
717
     *
718
     * @return mixed
719
     */
720
    protected function _getPortableFunctionDefinition($function)
721
    {
722
        return $function;
723
    }
724
725
    /**
726
     * @param mixed[][] $triggers
727
     *
728
     * @return mixed[][]
729
     */
730
    protected function _getPortableTriggersList($triggers)
731
    {
732
        $list = [];
733
        foreach ($triggers as $value) {
734
            $value = $this->_getPortableTriggerDefinition($value);
735
736
            if (! $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $value of type array 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...
737
                continue;
738
            }
739
740
            $list[] = $value;
741
        }
742
743
        return $list;
744
    }
745
746
    /**
747
     * @param mixed[] $trigger
748
     *
749
     * @return mixed
750
     */
751
    protected function _getPortableTriggerDefinition($trigger)
752
    {
753
        return $trigger;
754
    }
755
756
    /**
757
     * @param mixed[][] $sequences
758
     *
759
     * @return Sequence[]
760
     */
761 288
    protected function _getPortableSequencesList($sequences)
762
    {
763 288
        $list = [];
764
765 288
        foreach ($sequences as $value) {
766 288
            $list[] = $this->_getPortableSequenceDefinition($value);
767
        }
768
769 288
        return $list;
770
    }
771
772
    /**
773
     * @param mixed[] $sequence
774
     *
775
     * @return Sequence
776
     *
777
     * @throws DBALException
778
     */
779
    protected function _getPortableSequenceDefinition($sequence)
780
    {
781
        throw DBALException::notSupported('Sequences');
782
    }
783
784
    /**
785
     * Independent of the database the keys of the column list result are lowercased.
786
     *
787
     * The name of the created column instance however is kept in its case.
788
     *
789
     * @param string    $table        The name of the table.
790
     * @param string    $database
791
     * @param mixed[][] $tableColumns
792
     *
793
     * @return Column[]
794
     */
795 4102
    protected function _getPortableTableColumnList($table, $database, $tableColumns)
796
    {
797 4102
        $eventManager = $this->_platform->getEventManager();
798
799 4102
        $list = [];
800 4102
        foreach ($tableColumns as $tableColumn) {
801 4102
            $column           = null;
802 4102
            $defaultPrevented = false;
803
804 4102
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) {
805 3069
                $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn);
806 3069
                $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs);
807
808 3069
                $defaultPrevented = $eventArgs->isDefaultPrevented();
809 3069
                $column           = $eventArgs->getColumn();
810
            }
811
812 4102
            if (! $defaultPrevented) {
813 4102
                $column = $this->_getPortableTableColumnDefinition($tableColumn);
814
            }
815
816 4102
            if (! $column) {
817
                continue;
818
            }
819
820 4102
            $name        = strtolower($column->getQuotedName($this->_platform));
821 4102
            $list[$name] = $column;
822
        }
823
824 4102
        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
836
    /**
837
     * Aggregates and groups the index results according to the required data result.
838
     *
839
     * @param mixed[][]   $tableIndexRows
840
     * @param string|null $tableName
841
     *
842
     * @return Index[]
843
     */
844 4095
    protected function _getPortableTableIndexesList($tableIndexRows, $tableName = null)
845
    {
846 4095
        $result = [];
847 4095
        foreach ($tableIndexRows as $tableIndex) {
848 3960
            $indexName = $keyName = $tableIndex['key_name'];
849 3960
            if ($tableIndex['primary']) {
850 3958
                $keyName = 'primary';
851
            }
852
853 3960
            $keyName = strtolower($keyName);
854
855 3960
            if (! isset($result[$keyName])) {
856
                $options = [
857 3960
                    'lengths' => [],
858
                ];
859
860 3960
                if (isset($tableIndex['where'])) {
861 1064
                    $options['where'] = $tableIndex['where'];
862
                }
863
864 3960
                $result[$keyName] = [
865 3960
                    'name' => $indexName,
866
                    'columns' => [],
867 3960
                    'unique' => ! $tableIndex['non_unique'],
868 3960
                    'primary' => $tableIndex['primary'],
869 3960
                    'flags' => $tableIndex['flags'] ?? [],
870 3960
                    'options' => $options,
871
                ];
872
            }
873
874 3960
            $result[$keyName]['columns'][]            = $tableIndex['column_name'];
875 3960
            $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null;
876
        }
877
878 4095
        $eventManager = $this->_platform->getEventManager();
879
880 4095
        $indexes = [];
881 4095
        foreach ($result as $indexKey => $data) {
882 3960
            $index            = null;
883 3960
            $defaultPrevented = false;
884
885 3960
            if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) {
886 3046
                $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn);
887 3046
                $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs);
888
889 3046
                $defaultPrevented = $eventArgs->isDefaultPrevented();
890 3046
                $index            = $eventArgs->getIndex();
891
            }
892
893 3960
            if (! $defaultPrevented) {
894 3960
                $index = new Index($data['name'], $data['columns'], $data['unique'], $data['primary'], $data['flags'], $data['options']);
895
            }
896
897 3960
            if (! $index) {
898
                continue;
899
            }
900
901 3960
            $indexes[$indexKey] = $index;
902
        }
903
904 4095
        return $indexes;
905
    }
906
907
    /**
908
     * @param mixed[][] $tables
909
     *
910
     * @return string[]
911
     */
912 4398
    protected function _getPortableTablesList($tables)
913
    {
914 4398
        $list = [];
915 4398
        foreach ($tables as $value) {
916 4396
            $value = $this->_getPortableTableDefinition($value);
917
918 4396
            if (! $value) {
919
                continue;
920
            }
921
922 4396
            $list[] = $value;
923
        }
924
925 4398
        return $list;
926
    }
927
928
    /**
929
     * @param mixed $table
930
     *
931
     * @return string
932
     */
933
    protected function _getPortableTableDefinition($table)
934
    {
935
        return $table;
936
    }
937
938
    /**
939
     * @param mixed[][] $users
940
     *
941
     * @return string[][]
942
     */
943
    protected function _getPortableUsersList($users)
944
    {
945
        $list = [];
946
        foreach ($users as $value) {
947
            $value = $this->_getPortableUserDefinition($value);
948
949
            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...
950
                continue;
951
            }
952
953
            $list[] = $value;
954
        }
955
956
        return $list;
957
    }
958
959
    /**
960
     * @param string[] $user
961
     *
962
     * @return string[]
963
     */
964
    protected function _getPortableUserDefinition($user)
965
    {
966
        return $user;
967
    }
968
969
    /**
970
     * @param mixed[][] $views
971
     *
972
     * @return View[]
973
     */
974 2860
    protected function _getPortableViewsList($views)
975
    {
976 2860
        $list = [];
977 2860
        foreach ($views as $value) {
978 2860
            $view = $this->_getPortableViewDefinition($value);
979
980 2860
            if (! $view) {
981
                continue;
982
            }
983
984 2860
            $viewName        = strtolower($view->getQuotedName($this->_platform));
985 2860
            $list[$viewName] = $view;
986
        }
987
988 2860
        return $list;
989
    }
990
991
    /**
992
     * @param mixed[] $view
993
     *
994
     * @return View|false
995
     */
996
    protected function _getPortableViewDefinition($view)
997
    {
998
        return false;
999
    }
1000
1001
    /**
1002
     * @param mixed[][] $tableForeignKeys
1003
     *
1004
     * @return ForeignKeyConstraint[]
1005
     */
1006 1682
    protected function _getPortableTableForeignKeysList($tableForeignKeys)
1007
    {
1008 1682
        $list = [];
1009
1010 1682
        foreach ($tableForeignKeys as $value) {
1011 1612
            $list[] = $this->_getPortableTableForeignKeyDefinition($value);
1012
        }
1013
1014 1682
        return $list;
1015
    }
1016
1017
    /**
1018
     * @param mixed $tableForeignKey
1019
     *
1020
     * @return ForeignKeyConstraint
1021
     */
1022
    protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
1023
    {
1024
        return $tableForeignKey;
1025
    }
1026
1027
    /**
1028
     * @param string[]|string $sql
1029
     *
1030
     * @return void
1031
     */
1032 5281
    protected function _execSql($sql)
1033
    {
1034 5281
        foreach ((array) $sql as $query) {
1035 5281
            $this->_conn->executeUpdate($query);
1036
        }
1037 5253
    }
1038
1039
    /**
1040
     * Creates a schema instance for the current database.
1041
     *
1042
     * @return Schema
1043
     */
1044 3676
    public function createSchema()
1045
    {
1046 3676
        $namespaces = [];
1047
1048 3676
        if ($this->_platform->supportsSchemas()) {
1049 1155
            $namespaces = $this->listNamespaceNames();
1050
        }
1051
1052 3676
        $sequences = [];
1053
1054 3676
        if ($this->_platform->supportsSequences()) {
1055 1156
            $sequences = $this->listSequences();
1056
        }
1057
1058 3676
        $tables = $this->listTables();
1059
1060 3676
        return new Schema($tables, $sequences, $this->createSchemaConfig(), $namespaces);
1061
    }
1062
1063
    /**
1064
     * Creates the configuration for this schema.
1065
     *
1066
     * @return SchemaConfig
1067
     */
1068 3946
    public function createSchemaConfig()
1069
    {
1070 3946
        $schemaConfig = new SchemaConfig();
1071 3946
        $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength());
1072
1073 3946
        $searchPaths = $this->getSchemaSearchPaths();
1074 3946
        if (isset($searchPaths[0])) {
1075 3795
            $schemaConfig->setName($searchPaths[0]);
1076
        }
1077
1078 3946
        $params = $this->_conn->getParams();
1079 3946
        if (! isset($params['defaultTableOptions'])) {
1080 3946
            $params['defaultTableOptions'] = [];
1081
        }
1082
1083 3946
        if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) {
1084 783
            $params['defaultTableOptions']['charset'] = $params['charset'];
1085
        }
1086
1087 3946
        $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']);
1088
1089 3946
        return $schemaConfig;
1090
    }
1091
1092
    /**
1093
     * The search path for namespaces in the currently connected database.
1094
     *
1095
     * The first entry is usually the default namespace in the Schema. All
1096
     * further namespaces contain tables/sequences which can also be addressed
1097
     * with a short, not full-qualified name.
1098
     *
1099
     * For databases that don't support subschema/namespaces this method
1100
     * returns the name of the currently connected database.
1101
     *
1102
     * @return string[]
1103
     */
1104 3071
    public function getSchemaSearchPaths()
1105
    {
1106 3071
        return [$this->_conn->getDatabase()];
1107
    }
1108
1109
    /**
1110
     * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns
1111
     * the type given as default.
1112
     *
1113
     * @param string|null $comment
1114
     * @param string      $currentType
1115
     *
1116
     * @return string
1117
     */
1118 4104
    public function extractDoctrineTypeFromComment($comment, $currentType)
1119
    {
1120 4104
        if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match)) {
1121 3309
            return $match[1];
1122
        }
1123
1124 4078
        return $currentType;
1125
    }
1126
1127
    /**
1128
     * @param string|null $comment
1129
     * @param string|null $type
1130
     *
1131
     * @return string|null
1132
     */
1133 4057
    public function removeDoctrineTypeFromComment($comment, $type)
1134
    {
1135 4057
        if ($comment === null) {
1136 2539
            return null;
1137
        }
1138
1139 3765
        return str_replace('(DC2Type:' . $type . ')', '', $comment);
1140
    }
1141
}
1142