Failed Conditions
Pull Request — 3.0.x (#3980)
by Guilherme
30:17 queued 13s
created

Index::_addColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\Platforms\AbstractPlatform;
6
use InvalidArgumentException;
7
use function array_filter;
8
use function array_keys;
9
use function array_map;
10
use function array_search;
11
use function array_shift;
12
use function count;
13
use function strtolower;
14
15
class Index extends AbstractAsset implements Constraint
16
{
17
    /**
18
     * Asset identifier instances of the column names the index is associated with.
19
     * array($columnName => Identifier)
20
     *
21
     * @var Identifier[]
22
     */
23
    protected $_columns = [];
24
25
    /** @var bool */
26
    protected $_isUnique = false;
27
28
    /** @var bool */
29
    protected $_isPrimary = false;
30
31
    /**
32
     * Platform specific flags for indexes.
33
     * array($flagName => true)
34
     *
35
     * @var true[]
36
     */
37
    protected $_flags = [];
38
39
    /**
40
     * Platform specific options
41
     *
42
     * @todo $_flags should eventually be refactored into options
43
     * @var mixed[]
44
     */
45
    private $options = [];
46
47
    /**
48
     * @param string   $indexName
49
     * @param string[] $columns
50
     * @param bool     $isUnique
51
     * @param bool     $isPrimary
52
     * @param string[] $flags
53
     * @param mixed[]  $options
54
     */
55 10280
    public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [])
56
    {
57 10280
        $isUnique = $isUnique || $isPrimary;
58
59 10280
        $this->_setName($indexName);
60
61 10280
        $this->_isUnique  = $isUnique;
62 10280
        $this->_isPrimary = $isPrimary;
63 10280
        $this->options    = $options;
64
65 10280
        foreach ($columns as $column) {
66 10170
            $this->_addColumn($column);
67
        }
68
69 10280
        foreach ($flags as $flag) {
70 192
            $this->addFlag($flag);
71
        }
72 10280
    }
73
74
    /**
75
     * @throws InvalidArgumentException
76
     */
77 10170
    protected function _addColumn(string $column) : void
78
    {
79 10170
        $this->_columns[$column] = new Identifier($column);
80 10170
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85 8216
    public function getColumns()
86
    {
87 8216
        return array_keys($this->_columns);
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 6488
    public function getQuotedColumns(AbstractPlatform $platform)
94
    {
95 6488
        $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths')
96 6488
            ? $this->getOption('lengths') : [];
97
98 6488
        $columns = [];
99
100 6488
        foreach ($this->_columns as $column) {
101 6466
            $length = array_shift($subParts);
102
103 6466
            $quotedColumn = $column->getQuotedName($platform);
104
105 6466
            if ($length !== null) {
106 24
                $quotedColumn .= '(' . $length . ')';
107
            }
108
109 6466
            $columns[] = $quotedColumn;
110
        }
111
112 6488
        return $columns;
113
    }
114
115
    /**
116
     * @return string[]
117
     */
118 23
    public function getUnquotedColumns()
119
    {
120 23
        return array_map([$this, 'trimQuotes'], $this->getColumns());
121
    }
122
123
    /**
124
     * Is the index neither unique nor primary key?
125
     *
126
     * @return bool
127
     */
128 22
    public function isSimpleIndex()
129
    {
130 22
        return ! $this->_isPrimary && ! $this->_isUnique;
131
    }
132
133
    /**
134
     * @return bool
135
     */
136 4433
    public function isUnique()
137
    {
138 4433
        return $this->_isUnique;
139
    }
140
141
    /**
142
     * @return bool
143
     */
144 9839
    public function isPrimary()
145
    {
146 9839
        return $this->_isPrimary;
147
    }
148
149
    /**
150
     * @param string $columnName
151
     * @param int    $pos
152
     *
153
     * @return bool
154
     */
155 23
    public function hasColumnAtPosition($columnName, $pos = 0)
156
    {
157 23
        $columnName   = $this->trimQuotes(strtolower($columnName));
158 23
        $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
159
160 23
        return array_search($columnName, $indexColumns, true) === $pos;
161
    }
162
163
    /**
164
     * Checks if this index exactly spans the given column names in the correct order.
165
     *
166
     * @param string[] $columnNames
167
     *
168
     * @return bool
169
     */
170 2319
    public function spansColumns(array $columnNames)
171
    {
172 2319
        $columns         = $this->getColumns();
173 2319
        $numberOfColumns = count($columns);
174 2319
        $sameColumns     = true;
175
176 2319
        for ($i = 0; $i < $numberOfColumns; $i++) {
177 2319
            if (isset($columnNames[$i]) && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i]))) {
178 1609
                continue;
179
            }
180
181 1098
            $sameColumns = false;
182
        }
183
184 2319
        return $sameColumns;
185
    }
186
187
    /**
188
     * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
189
     *
190
     * @return bool
191
     */
192 2451
    public function isFullfilledBy(Index $other)
193
    {
194
        // allow the other index to be equally large only. It being larger is an option
195
        // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
196 2451
        if (count($other->getColumns()) !== count($this->getColumns())) {
197 308
            return false;
198
        }
199
200
        // Check if columns are the same, and even in the same order
201 2231
        $sameColumns = $this->spansColumns($other->getColumns());
202
203 2231
        if ($sameColumns) {
0 ignored issues
show
introduced by
The condition $sameColumns is always true.
Loading history...
204 1521
            if (! $this->samePartialIndex($other)) {
205 22
                return false;
206
            }
207
208 1521
            if (! $this->hasSameColumnLengths($other)) {
209 44
                return false;
210
            }
211
212 1477
            if (! $this->isUnique() && ! $this->isPrimary()) {
213
                // this is a special case: If the current key is neither primary or unique, any unique or
214
                // primary key will always have the same effect for the index and there cannot be any constraint
215
                // overlaps. This means a primary or unique index can always fulfill the requirements of just an
216
                // index that has no constraints.
217 1151
                return true;
218
            }
219
220 369
            if ($other->isPrimary() !== $this->isPrimary()) {
221 100
                return false;
222
            }
223
224 291
            return $other->isUnique() === $this->isUnique();
225
        }
226
227 1098
        return false;
228
    }
229
230
    /**
231
     * Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
232
     *
233
     * @return bool
234
     */
235 22
    public function overrules(Index $other)
236
    {
237 22
        if ($other->isPrimary()) {
238
            return false;
239
        }
240
241 22
        if ($this->isSimpleIndex() && $other->isUnique()) {
242
            return false;
243
        }
244
245 22
        return $this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other);
246
    }
247
248
    /**
249
     * Returns platform specific flags for indexes.
250
     *
251
     * @return string[]
252
     */
253 285
    public function getFlags()
254
    {
255 285
        return array_keys($this->_flags);
256
    }
257
258
    /**
259
     * Adds Flag for an index that translates to platform specific handling.
260
     *
261
     * @param string $flag
262
     *
263
     * @return Index
264
     *
265
     * @example $index->addFlag('CLUSTERED')
266
     */
267 412
    public function addFlag($flag)
268
    {
269 412
        $this->_flags[strtolower($flag)] = true;
270
271 412
        return $this;
272
    }
273
274
    /**
275
     * Does this index have a specific flag?
276
     *
277
     * @param string $flag
278
     *
279
     * @return bool
280
     */
281 1950
    public function hasFlag($flag)
282
    {
283 1950
        return isset($this->_flags[strtolower($flag)]);
284
    }
285
286
    /**
287
     * Removes a flag.
288
     *
289
     * @param string $flag
290
     *
291
     * @return void
292
     */
293 22
    public function removeFlag($flag)
294
    {
295 22
        unset($this->_flags[strtolower($flag)]);
296 22
    }
297
298
    /**
299
     * @param string $name
300
     *
301
     * @return bool
302
     */
303 4846
    public function hasOption($name)
304
    {
305 4846
        return isset($this->options[strtolower($name)]);
306
    }
307
308
    /**
309
     * @param string $name
310
     *
311
     * @return mixed
312
     */
313 139
    public function getOption($name)
314
    {
315 139
        return $this->options[strtolower($name)];
316
    }
317
318
    /**
319
     * @return mixed[]
320
     */
321 285
    public function getOptions()
322
    {
323 285
        return $this->options;
324
    }
325
326
    /**
327
     * Return whether the two indexes have the same partial index
328
     *
329
     * @return bool
330
     */
331 1543
    private function samePartialIndex(Index $other)
332
    {
333 1543
        if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') === $other->getOption('where')) {
334 49
            return true;
335
        }
336
337 1538
        return ! $this->hasOption('where') && ! $other->hasOption('where');
338
    }
339
340
    /**
341
     * Returns whether the index has the same column lengths as the other
342
     */
343 1521
    private function hasSameColumnLengths(self $other) : bool
344
    {
345
        $filter = static function (?int $length) : bool {
346 277
            return $length !== null;
347 1521
        };
348
349 1521
        return array_filter($this->options['lengths'] ?? [], $filter)
350 1521
            === array_filter($other->options['lengths'] ?? [], $filter);
351
    }
352
}
353