Passed
Push — 2.9 ( d389f3...7345cd )
by Sergei
31:44 queued 28:51
created

Table::columnsAreIndexed()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 10
ccs 4
cts 5
cp 0.8
rs 10
cc 3
nc 3
nop 1
crap 3.072
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Visitor\Visitor;
7
use Doctrine\DBAL\Types\Type;
8
use const ARRAY_FILTER_USE_KEY;
9
use function array_filter;
10
use function array_merge;
11
use function in_array;
12
use function preg_match;
13
use function strlen;
14
use function strtolower;
15
16
/**
17
 * Object Representation of a table.
18
 */
19
class Table extends AbstractAsset
20
{
21
    /** @var string */
22
    protected $_name = null;
23
24
    /** @var Column[] */
25
    protected $_columns = [];
26
27
    /** @var Index[] */
28
    private $implicitIndexes = [];
29
30
    /** @var Index[] */
31
    protected $_indexes = [];
32
33
    /** @var string */
34
    protected $_primaryKeyName = false;
35
36
    /** @var ForeignKeyConstraint[] */
37
    protected $_fkConstraints = [];
38
39
    /** @var mixed[] */
40
    protected $_options = [
41
        'create_options' => [],
42
    ];
43
44
    /** @var SchemaConfig|null */
45
    protected $_schemaConfig = null;
46
47
    /**
48
     * @param string                 $tableName
49
     * @param Column[]               $columns
50
     * @param Index[]                $indexes
51
     * @param ForeignKeyConstraint[] $fkConstraints
52
     * @param int                    $idGeneratorType
53
     * @param mixed[]                $options
54
     *
55
     * @throws DBALException
56
     */
57 27158
    public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], $idGeneratorType = 0, array $options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $idGeneratorType is not used and could be removed. ( Ignorable by Annotation )

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

57
    public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], /** @scrutinizer ignore-unused */ $idGeneratorType = 0, array $options = [])

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

Loading history...
58
    {
59 27158
        if (strlen($tableName) === 0) {
60 30
            throw DBALException::invalidTableName($tableName);
61
        }
62
63 27128
        $this->_setName($tableName);
64
65 27128
        foreach ($columns as $column) {
66 2544
            $this->_addColumn($column);
67
        }
68
69 27098
        foreach ($indexes as $idx) {
70 883
            $this->_addIndex($idx);
71
        }
72
73 27038
        foreach ($fkConstraints as $constraint) {
74 338
            $this->_addForeignKeyConstraint($constraint);
75
        }
76
77 27038
        $this->_options = array_merge($this->_options, $options);
78 27038
    }
79
80
    /**
81
     * @return void
82
     */
83 2059
    public function setSchemaConfig(SchemaConfig $schemaConfig)
84
    {
85 2059
        $this->_schemaConfig = $schemaConfig;
86 2059
    }
87
88
    /**
89
     * @return int
90
     */
91 5056
    protected function _getMaxIdentifierLength()
92
    {
93 5056
        if ($this->_schemaConfig instanceof SchemaConfig) {
94 314
            return $this->_schemaConfig->getMaxIdentifierLength();
95
        }
96
97 4800
        return 63;
98
    }
99
100
    /**
101
     * Sets the Primary Key.
102
     *
103
     * @param string[]    $columnNames
104
     * @param string|bool $indexName
105
     *
106
     * @return self
107
     */
108 11027
    public function setPrimaryKey(array $columnNames, $indexName = false)
109
    {
110 11027
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
0 ignored issues
show
Bug introduced by
It seems like $indexName ?: 'primary' can also be of type true; however, parameter $indexName of Doctrine\DBAL\Schema\Table::_createIndex() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

110
        $this->_addIndex($this->_createIndex($columnNames, /** @scrutinizer ignore-type */ $indexName ?: 'primary', true, true));
Loading history...
111
112 11027
        foreach ($columnNames as $columnName) {
113 11027
            $column = $this->getColumn($columnName);
114 11027
            $column->setNotnull(true);
115
        }
116
117 11027
        return $this;
118
    }
119
120
    /**
121
     * @param string[]    $columnNames
122
     * @param string|null $indexName
123
     * @param string[]    $flags
124
     * @param mixed[]     $options
125
     *
126
     * @return self
127
     */
128 3330
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
129
    {
130 3330
        if ($indexName === null) {
131 868
            $indexName = $this->_generateIdentifierName(
132 868
                array_merge([$this->getName()], $columnNames),
133 868
                'idx',
134 868
                $this->_getMaxIdentifierLength()
135
            );
136
        }
137
138 3330
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
139
    }
140
141
    /**
142
     * Drops the primary key from this table.
143
     *
144
     * @return void
145
     */
146 559
    public function dropPrimaryKey()
147
    {
148 559
        $this->dropIndex($this->_primaryKeyName);
149 559
        $this->_primaryKeyName = false;
0 ignored issues
show
Documentation Bug introduced by
The property $_primaryKeyName was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
150 559
    }
151
152
    /**
153
     * Drops an index from this table.
154
     *
155
     * @param string $indexName The index name.
156
     *
157
     * @return void
158
     *
159
     * @throws SchemaException If the index does not exist.
160
     */
161 968
    public function dropIndex($indexName)
162
    {
163 968
        $indexName = $this->normalizeIdentifier($indexName);
164 968
        if (! $this->hasIndex($indexName)) {
165
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
166
        }
167 968
        unset($this->_indexes[$indexName]);
168 968
    }
169
170
    /**
171
     * @param string[]    $columnNames
172
     * @param string|null $indexName
173
     * @param mixed[]     $options
174
     *
175
     * @return self
176
     */
177 983
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
178
    {
179 983
        if ($indexName === null) {
180 736
            $indexName = $this->_generateIdentifierName(
181 736
                array_merge([$this->getName()], $columnNames),
182 736
                'uniq',
183 736
                $this->_getMaxIdentifierLength()
184
            );
185
        }
186
187 983
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
188
    }
189
190
    /**
191
     * Renames an index.
192
     *
193
     * @param string      $oldIndexName The name of the index to rename from.
194
     * @param string|null $newIndexName The name of the index to rename to.
195
     *                                  If null is given, the index name will be auto-generated.
196
     *
197
     * @return self This table instance.
198
     *
199
     * @throws SchemaException If no index exists for the given current name
200
     *                         or if an index with the given new name already exists on this table.
201
     */
202 419
    public function renameIndex($oldIndexName, $newIndexName = null)
203
    {
204 419
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
205 419
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
206
207 419
        if ($oldIndexName === $normalizedNewIndexName) {
208 270
            return $this;
209
        }
210
211 419
        if (! $this->hasIndex($oldIndexName)) {
212 30
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
213
        }
214
215 389
        if ($this->hasIndex($normalizedNewIndexName)) {
216 30
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
217
        }
218
219 359
        $oldIndex = $this->_indexes[$oldIndexName];
220
221 359
        if ($oldIndex->isPrimary()) {
222 30
            $this->dropPrimaryKey();
223
224 30
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName);
225
        }
226
227 359
        unset($this->_indexes[$oldIndexName]);
228
229 359
        if ($oldIndex->isUnique()) {
230 60
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
231
        }
232
233 329
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
234
    }
235
236
    /**
237
     * Checks if an index begins in the order of the given columns.
238
     *
239
     * @param string[] $columnNames
240
     *
241
     * @return bool
242
     */
243 59
    public function columnsAreIndexed(array $columnNames)
244
    {
245 59
        foreach ($this->getIndexes() as $index) {
246
            /** @var $index Index */
247 59
            if ($index->spansColumns($columnNames)) {
248 59
                return true;
249
            }
250
        }
251
252
        return false;
253
    }
254
255
    /**
256
     * @param string[] $columnNames
257
     * @param string   $indexName
258
     * @param bool     $isUnique
259
     * @param bool     $isPrimary
260
     * @param string[] $flags
261
     * @param mixed[]  $options
262
     *
263
     * @return Index
264
     *
265
     * @throws SchemaException
266
     */
267 15554
    private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
268
    {
269 15554
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
270 30
            throw SchemaException::indexNameInvalid($indexName);
271
        }
272
273 15524
        foreach ($columnNames as $columnName) {
274 15494
            if (! $this->hasColumn($columnName)) {
275 30
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
276
            }
277
        }
278
279 15494
        return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
280
    }
281
282
    /**
283
     * @param string  $columnName
284
     * @param string  $typeName
285
     * @param mixed[] $options
286
     *
287
     * @return Column
288
     */
289 22267
    public function addColumn($columnName, $typeName, array $options = [])
290
    {
291 22267
        $column = new Column($columnName, Type::getType($typeName), $options);
292
293 22267
        $this->_addColumn($column);
294
295 22267
        return $column;
296
    }
297
298
    /**
299
     * Renames a Column.
300
     *
301
     * @deprecated
302
     *
303
     * @param string $oldColumnName
304
     * @param string $newColumnName
305
     *
306
     * @throws DBALException
307
     */
308
    public function renameColumn($oldColumnName, $newColumnName)
0 ignored issues
show
Unused Code introduced by
The parameter $newColumnName is not used and could be removed. ( Ignorable by Annotation )

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

308
    public function renameColumn($oldColumnName, /** @scrutinizer ignore-unused */ $newColumnName)

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

Loading history...
Unused Code introduced by
The parameter $oldColumnName is not used and could be removed. ( Ignorable by Annotation )

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

308
    public function renameColumn(/** @scrutinizer ignore-unused */ $oldColumnName, $newColumnName)

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

Loading history...
309
    {
310
        throw new DBALException('Table#renameColumn() was removed, because it drops and recreates ' .
311
            'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' .
312
            'column was renamed or one column was created and another one dropped.');
313
    }
314
315
    /**
316
     * Change Column Details.
317
     *
318
     * @param string  $columnName
319
     * @param mixed[] $options
320
     *
321
     * @return self
322
     */
323 360
    public function changeColumn($columnName, array $options)
324
    {
325 360
        $column = $this->getColumn($columnName);
326 360
        $column->setOptions($options);
327
328 360
        return $this;
329
    }
330
331
    /**
332
     * Drops a Column from the Table.
333
     *
334
     * @param string $columnName
335
     *
336
     * @return self
337
     */
338 270
    public function dropColumn($columnName)
339
    {
340 270
        $columnName = $this->normalizeIdentifier($columnName);
341 270
        unset($this->_columns[$columnName]);
342
343 270
        return $this;
344
    }
345
346
    /**
347
     * Adds a foreign key constraint.
348
     *
349
     * Name is inferred from the local columns.
350
     *
351
     * @param Table|string $foreignTable       Table schema instance or table name
352
     * @param string[]     $localColumnNames
353
     * @param string[]     $foreignColumnNames
354
     * @param mixed[]      $options
355
     * @param string|null  $constraintName
356
     *
357
     * @return self
358
     */
359 3370
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
360
    {
361 3370
        $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength());
362
363 3370
        return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
1 ignored issue
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\Tab...dForeignKeyConstraint() has been deprecated: Use {@link addForeignKeyConstraint} ( Ignorable by Annotation )

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

363
        return /** @scrutinizer ignore-deprecated */ $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);

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...
364
    }
365
366
    /**
367
     * Adds a foreign key constraint.
368
     *
369
     * Name is to be generated by the database itself.
370
     *
371
     * @deprecated Use {@link addForeignKeyConstraint}
372
     *
373
     * @param Table|string $foreignTable       Table schema instance or table name
374
     * @param string[]     $localColumnNames
375
     * @param string[]     $foreignColumnNames
376
     * @param mixed[]      $options
377
     *
378
     * @return self
379
     */
380 187
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
381
    {
382 187
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
383
    }
384
385
    /**
386
     * Adds a foreign key constraint with a given name.
387
     *
388
     * @deprecated Use {@link addForeignKeyConstraint}
389
     *
390
     * @param string       $name
391
     * @param Table|string $foreignTable       Table schema instance or table name
392
     * @param string[]     $localColumnNames
393
     * @param string[]     $foreignColumnNames
394
     * @param mixed[]      $options
395
     *
396
     * @return self
397
     *
398
     * @throws SchemaException
399
     */
400 3400
    public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
401
    {
402 3400
        if ($foreignTable instanceof Table) {
403 1738
            foreach ($foreignColumnNames as $columnName) {
404 1738
                if (! $foreignTable->hasColumn($columnName)) {
405 30
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
406
                }
407
            }
408
        }
409
410 3370
        foreach ($localColumnNames as $columnName) {
411 3370
            if (! $this->hasColumn($columnName)) {
412 30
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
413
            }
414
        }
415
416 3340
        $constraint = new ForeignKeyConstraint(
417 3340
            $localColumnNames,
418 3340
            $foreignTable,
419 3340
            $foreignColumnNames,
420 3340
            $name,
421 3340
            $options
422
        );
423 3340
        $this->_addForeignKeyConstraint($constraint);
424
425 3340
        return $this;
426
    }
427
428
    /**
429
     * @param string $name
430
     * @param string $value
431
     *
432
     * @return self
433
     */
434 1557
    public function addOption($name, $value)
435
    {
436 1557
        $this->_options[$name] = $value;
437
438 1557
        return $this;
439
    }
440
441
    /**
442
     * @return void
443
     *
444
     * @throws SchemaException
445
     */
446 23419
    protected function _addColumn(Column $column)
447
    {
448 23419
        $columnName = $column->getName();
449 23419
        $columnName = $this->normalizeIdentifier($columnName);
450
451 23419
        if (isset($this->_columns[$columnName])) {
452 30
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
453
        }
454
455 23419
        $this->_columns[$columnName] = $column;
456 23419
    }
457
458
    /**
459
     * Adds an index to the table.
460
     *
461
     * @return self
462
     *
463
     * @throws SchemaException
464
     */
465 15730
    protected function _addIndex(Index $indexCandidate)
466
    {
467 15730
        $indexName               = $indexCandidate->getName();
468 15730
        $indexName               = $this->normalizeIdentifier($indexName);
469 15730
        $replacedImplicitIndexes = [];
470
471 15730
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
472 990
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
473 870
                continue;
474
            }
475
476 120
            $replacedImplicitIndexes[] = $name;
477
        }
478
479 15730
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
480 15730
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
481
        ) {
482 60
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
483
        }
484
485 15730
        foreach ($replacedImplicitIndexes as $name) {
486 120
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
487
        }
488
489 15730
        if ($indexCandidate->isPrimary()) {
490 11120
            $this->_primaryKeyName = $indexName;
491
        }
492
493 15730
        $this->_indexes[$indexName] = $indexCandidate;
494
495 15730
        return $this;
496
    }
497
498
    /**
499
     * @return void
500
     */
501 3480
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
502
    {
503 3480
        $constraint->setLocalTable($this);
504
505 3480
        if (strlen($constraint->getName())) {
506 3449
            $name = $constraint->getName();
507
        } else {
508 31
            $name = $this->_generateIdentifierName(
509 31
                array_merge((array) $this->getName(), $constraint->getLocalColumns()),
510 31
                'fk',
511 31
                $this->_getMaxIdentifierLength()
512
            );
513
        }
514 3480
        $name = $this->normalizeIdentifier($name);
515
516 3480
        $this->_fkConstraints[$name] = $constraint;
517
518
        // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request.
519
        // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
520
        // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns).
521 3480
        $indexName      = $this->_generateIdentifierName(
522 3480
            array_merge([$this->getName()], $constraint->getColumns()),
523 3480
            'idx',
524 3480
            $this->_getMaxIdentifierLength()
525
        );
526 3480
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
527
528 3480
        foreach ($this->_indexes as $existingIndex) {
529 2691
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
530 1699
                return;
531
            }
532
        }
533
534 2597
        $this->_addIndex($indexCandidate);
535 2597
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
536 2597
    }
537
538
    /**
539
     * Returns whether this table has a foreign key constraint with the given name.
540
     *
541
     * @param string $constraintName
542
     *
543
     * @return bool
544
     */
545 329
    public function hasForeignKey($constraintName)
546
    {
547 329
        $constraintName = $this->normalizeIdentifier($constraintName);
548
549 329
        return isset($this->_fkConstraints[$constraintName]);
550
    }
551
552
    /**
553
     * Returns the foreign key constraint with the given name.
554
     *
555
     * @param string $constraintName The constraint name.
556
     *
557
     * @return ForeignKeyConstraint
558
     *
559
     * @throws SchemaException If the foreign key does not exist.
560
     */
561 240
    public function getForeignKey($constraintName)
562
    {
563 240
        $constraintName = $this->normalizeIdentifier($constraintName);
564 240
        if (! $this->hasForeignKey($constraintName)) {
565
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
566
        }
567
568 240
        return $this->_fkConstraints[$constraintName];
569
    }
570
571
    /**
572
     * Removes the foreign key constraint with the given name.
573
     *
574
     * @param string $constraintName The constraint name.
575
     *
576
     * @return void
577
     *
578
     * @throws SchemaException
579
     */
580 300
    public function removeForeignKey($constraintName)
581
    {
582 300
        $constraintName = $this->normalizeIdentifier($constraintName);
583 300
        if (! $this->hasForeignKey($constraintName)) {
584
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
585
        }
586
587 300
        unset($this->_fkConstraints[$constraintName]);
588 300
    }
589
590
    /**
591
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
592
     *
593
     * @return Column[]
594
     */
595 17015
    public function getColumns()
596
    {
597 17015
        $primaryKeyColumns = [];
598 17015
        if ($this->hasPrimaryKey()) {
599 7494
            $primaryKeyColumns = $this->filterColumns($this->getPrimaryKey()->getColumns());
600
        }
601
602 17015
        return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
603
    }
604
605
    /**
606
     * Returns foreign key columns
607
     *
608
     * @return Column[]
609
     */
610 17015
    private function getForeignKeyColumns()
611
    {
612 17015
        $foreignKeyColumns = [];
613 17015
        foreach ($this->getForeignKeys() as $foreignKey) {
614 1638
            $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
615
        }
616 17015
        return $this->filterColumns($foreignKeyColumns);
617
    }
618
619
    /**
620
     * Returns only columns that have specified names
621
     *
622
     * @param string[] $columnNames
623
     *
624
     * @return Column[]
625
     */
626 17015
    private function filterColumns(array $columnNames)
627
    {
628
        return array_filter($this->_columns, static function ($columnName) use ($columnNames) {
629 16235
            return in_array($columnName, $columnNames, true);
630 17015
        }, ARRAY_FILTER_USE_KEY);
631
    }
632
633
    /**
634
     * Returns whether this table has a Column with the given name.
635
     *
636
     * @param string $columnName The column name.
637
     *
638
     * @return bool
639
     */
640 18173
    public function hasColumn($columnName)
641
    {
642 18173
        $columnName = $this->normalizeIdentifier($columnName);
643
644 18173
        return isset($this->_columns[$columnName]);
645
    }
646
647
    /**
648
     * Returns the Column with the given name.
649
     *
650
     * @param string $columnName The column name.
651
     *
652
     * @return Column
653
     *
654
     * @throws SchemaException If the column does not exist.
655
     */
656 14103
    public function getColumn($columnName)
657
    {
658 14103
        $columnName = $this->normalizeIdentifier($columnName);
659 14103
        if (! $this->hasColumn($columnName)) {
660 30
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
661
        }
662
663 14073
        return $this->_columns[$columnName];
664
    }
665
666
    /**
667
     * Returns the primary key.
668
     *
669
     * @return Index|null The primary key, or null if this Table has no primary key.
670
     */
671 7644
    public function getPrimaryKey()
672
    {
673 7644
        if (! $this->hasPrimaryKey()) {
674
            return null;
675
        }
676
677 7644
        return $this->getIndex($this->_primaryKeyName);
678
    }
679
680
    /**
681
     * Returns the primary key columns.
682
     *
683
     * @return string[]
684
     *
685
     * @throws DBALException
686
     */
687 409
    public function getPrimaryKeyColumns()
688
    {
689 409
        if (! $this->hasPrimaryKey()) {
690
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
691
        }
692 409
        return $this->getPrimaryKey()->getColumns();
693
    }
694
695
    /**
696
     * Returns whether this table has a primary key.
697
     *
698
     * @return bool
699
     */
700 17255
    public function hasPrimaryKey()
701
    {
702 17255
        return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName);
703
    }
704
705
    /**
706
     * Returns whether this table has an Index with the given name.
707
     *
708
     * @param string $indexName The index name.
709
     *
710
     * @return bool
711
     */
712 9249
    public function hasIndex($indexName)
713
    {
714 9249
        $indexName = $this->normalizeIdentifier($indexName);
715
716 9249
        return isset($this->_indexes[$indexName]);
717
    }
718
719
    /**
720
     * Returns the Index with the given name.
721
     *
722
     * @param string $indexName The index name.
723
     *
724
     * @return Index
725
     *
726
     * @throws SchemaException If the index does not exist.
727
     */
728 8589
    public function getIndex($indexName)
729
    {
730 8589
        $indexName = $this->normalizeIdentifier($indexName);
731 8589
        if (! $this->hasIndex($indexName)) {
732 30
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
733
        }
734
735 8559
        return $this->_indexes[$indexName];
736
    }
737
738
    /**
739
     * @return Index[]
740
     */
741 16175
    public function getIndexes()
742
    {
743 16175
        return $this->_indexes;
744
    }
745
746
    /**
747
     * Returns the foreign key constraints.
748
     *
749
     * @return ForeignKeyConstraint[]
750
     */
751 17555
    public function getForeignKeys()
752
    {
753 17555
        return $this->_fkConstraints;
754
    }
755
756
    /**
757
     * @param string $name
758
     *
759
     * @return bool
760
     */
761 2564
    public function hasOption($name)
762
    {
763 2564
        return isset($this->_options[$name]);
764
    }
765
766
    /**
767
     * @param string $name
768
     *
769
     * @return mixed
770
     */
771 275
    public function getOption($name)
772
    {
773 275
        return $this->_options[$name];
774
    }
775
776
    /**
777
     * @return mixed[]
778
     */
779 12921
    public function getOptions()
780
    {
781 12921
        return $this->_options;
782
    }
783
784
    /**
785
     * @return void
786
     */
787 578
    public function visit(Visitor $visitor)
788
    {
789 578
        $visitor->acceptTable($this);
790
791 578
        foreach ($this->getColumns() as $column) {
792 488
            $visitor->acceptColumn($this, $column);
793
        }
794
795 578
        foreach ($this->getIndexes() as $index) {
796 324
            $visitor->acceptIndex($this, $index);
797
        }
798
799 578
        foreach ($this->getForeignKeys() as $constraint) {
800 120
            $visitor->acceptForeignKey($this, $constraint);
801
        }
802 578
    }
803
804
    /**
805
     * Clone of a Table triggers a deep clone of all affected assets.
806
     *
807
     * @return void
808
     */
809 1478
    public function __clone()
810
    {
811 1478
        foreach ($this->_columns as $k => $column) {
812 1448
            $this->_columns[$k] = clone $column;
813
        }
814 1478
        foreach ($this->_indexes as $k => $index) {
815 1089
            $this->_indexes[$k] = clone $index;
816
        }
817 1478
        foreach ($this->_fkConstraints as $k => $fk) {
818 239
            $this->_fkConstraints[$k] = clone $fk;
819 239
            $this->_fkConstraints[$k]->setLocalTable($this);
820
        }
821 1478
    }
822
823
    /**
824
     * Normalizes a given identifier.
825
     *
826
     * Trims quotes and lowercases the given identifier.
827
     *
828
     * @param string $identifier The identifier to normalize.
829
     *
830
     * @return string The normalized identifier.
831
     */
832 23539
    private function normalizeIdentifier($identifier)
833
    {
834 23539
        return $this->trimQuotes(strtolower($identifier));
835
    }
836
}
837