Completed
Pull Request — master (#2412)
by Benoît
14:20
created

Index::_addColumn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 6
ccs 3
cts 4
cp 0.75
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.0625
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Schema;
21
22
use Doctrine\DBAL\Platforms\AbstractPlatform;
23
use function array_keys;
24
use function array_map;
25
use function array_search;
26
use function count;
27
use function is_string;
28
use function strtolower;
29
30
class Index extends AbstractAsset implements Constraint
31
{
32
    /**
33
     * Asset identifier instances of the column names the index is associated with.
34
     * array($columnName => Identifier)
35
     *
36
     * @var Identifier[]
37
     */
38
    protected $_columns = [];
39
40
    /**
41
     * @var bool
42
     */
43
    protected $_isUnique = false;
44
45
    /**
46
     * @var bool
47
     */
48
    protected $_isPrimary = false;
49
50
    /**
51
     * Platform specific flags for indexes.
52
     * array($flagName => true)
53
     *
54
     * @var array
55
     */
56
    protected $_flags = [];
57
58
    /**
59
     * Platform specific options
60
     *
61
     * @todo $_flags should eventually be refactored into options
62
     *
63
     * @var array
64
     */
65
    private $options = [];
66
67
    /**
68
     * @param string   $indexName
69
     * @param string[] $columns
70
     * @param bool     $isUnique
71
     * @param bool     $isPrimary
72
     * @param string[] $flags
73
     * @param array    $options
74
     */
75 13080
    public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = [], array $options = [])
76
    {
77 13080
        $isUnique = $isUnique || $isPrimary;
78
79 13080
        $this->_setName($indexName);
80 13080
        $this->_isUnique = $isUnique;
81 13080
        $this->_isPrimary = $isPrimary;
82 13080
        $this->options = $options;
83
84 13080
        foreach ($columns as $column) {
85 12681
            $this->_addColumn($column);
86
        }
87 13080
        foreach ($flags as $flag) {
88 408
            $this->addFlag($flag);
89
        }
90 13080
    }
91
92
    /**
93
     * @param string $column
94
     *
95
     * @return void
96
     *
97
     * @throws \InvalidArgumentException
98
     */
99 12681
    protected function _addColumn($column)
100
    {
101 12681
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always true.
Loading history...
102 12681
            $this->_columns[$column] = new Identifier($column);
103
        } else {
104
            throw new \InvalidArgumentException("Expecting a string as Index Column");
105
        }
106 12681
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111 9691
    public function getColumns()
112
    {
113 9691
        return array_keys($this->_columns);
114
    }
115
116
    /**
117
     * {@inheritdoc}
118
     */
119 8277
    public function getQuotedColumns(AbstractPlatform $platform)
120
    {
121 8277
        $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths')
122 8277
            ? $this->getOption('lengths') : [];
123
124 8277
        $columns = [];
125
126 8277
        foreach ($this->_columns as $column) {
127 8125
            $length = array_shift($subParts);
128
129 8125
            $quotedColumn = $column->getQuotedName($platform);
130
131 8125
            if ($length !== null) {
132 8
                $quotedColumn .= '(' . $length . ')';
133
            }
134
135 8125
            $columns[] = $quotedColumn;
136
        }
137
138 8277
        return $columns;
139
    }
140
141
    /**
142
     * @return string[]
143
     */
144 20
    public function getUnquotedColumns()
145
    {
146 20
        return array_map([$this, 'trimQuotes'], $this->getColumns());
147
    }
148
149
    /**
150
     * Is the index neither unique nor primary key?
151
     *
152
     * @return bool
153
     */
154 19
    public function isSimpleIndex()
155
    {
156 19
        return !$this->_isPrimary && !$this->_isUnique;
157
    }
158
159
    /**
160
     * @return bool
161
     */
162 5800
    public function isUnique()
163
    {
164 5800
        return $this->_isUnique;
165
    }
166
167
    /**
168
     * @return bool
169
     */
170 12167
    public function isPrimary()
171
    {
172 12167
        return $this->_isPrimary;
173
    }
174
175
    /**
176
     * @param string $columnName
177
     * @param int    $pos
178
     *
179
     * @return bool
180
     */
181 20
    public function hasColumnAtPosition($columnName, $pos = 0)
182
    {
183 20
        $columnName   = $this->trimQuotes(strtolower($columnName));
184 20
        $indexColumns = array_map('strtolower', $this->getUnquotedColumns());
185
186 20
        return array_search($columnName, $indexColumns) === $pos;
187
    }
188
189
    /**
190
     * Checks if this index exactly spans the given column names in the correct order.
191
     *
192
     * @param array $columnNames
193
     *
194
     * @return bool
195
     */
196 2472
    public function spansColumns(array $columnNames)
197
    {
198 2472
        $columns         = $this->getColumns();
199 2472
        $numberOfColumns = count($columns);
200 2472
        $sameColumns     = true;
201
202 2472
        for ($i = 0; $i < $numberOfColumns; $i++) {
203 2472
            if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) {
204 1310
                $sameColumns = false;
205
            }
206
        }
207
208 2472
        return $sameColumns;
209
    }
210
211
    /**
212
     * Checks if the other index already fulfills all the indexing and constraint needs of the current one.
213
     *
214
     * @param Index $other
215
     *
216
     * @return bool
217
     */
218 2586
    public function isFullfilledBy(Index $other)
219
    {
220
        // allow the other index to be equally large only. It being larger is an option
221
        // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo)
222 2586
        if (count($other->getColumns()) != count($this->getColumns())) {
223 323
            return false;
224
        }
225
226
        // Check if columns are the same, and even in the same order
227 2396
        $sameColumns = $this->spansColumns($other->getColumns());
228
229 2396
        if ($sameColumns) {
230 1627
            if ( ! $this->samePartialIndex($other)) {
231 19
                return false;
232
            }
233
234 1627
            if ( ! $this->isUnique() && ! $this->isPrimary()) {
235
                // this is a special case: If the current key is neither primary or unique, any unique or
236
                // primary key will always have the same effect for the index and there cannot be any constraint
237
                // overlaps. This means a primary or unique index can always fulfill the requirements of just an
238
                // index that has no constraints.
239 1297
                return true;
240
            }
241
242 362
            if ($other->isPrimary() != $this->isPrimary()) {
243 84
                return false;
244
            }
245
246 297
            return $other->isUnique() === $this->isUnique();
247
        }
248
249 1310
        return false;
250
    }
251
252
    /**
253
     * Detects if the other index is a non-unique, non primary index that can be overwritten by this one.
254
     *
255
     * @param Index $other
256
     *
257
     * @return bool
258
     */
259 19
    public function overrules(Index $other)
260
    {
261 19
        if ($other->isPrimary()) {
262
            return false;
263 19
        } elseif ($this->isSimpleIndex() && $other->isUnique()) {
264
            return false;
265
        }
266
267 19
        return $this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other);
268
    }
269
270
    /**
271
     * Returns platform specific flags for indexes.
272
     *
273
     * @return string[]
274
     */
275 246
    public function getFlags()
276
    {
277 246
        return array_keys($this->_flags);
278
    }
279
280
    /**
281
     * Adds Flag for an index that translates to platform specific handling.
282
     *
283
     * @example $index->addFlag('CLUSTERED')
284
     *
285
     * @param string $flag
286
     *
287
     * @return Index
288
     */
289 731
    public function addFlag($flag)
290
    {
291 731
        $this->_flags[strtolower($flag)] = true;
292
293 731
        return $this;
294
    }
295
296
    /**
297
     * Does this index have a specific flag?
298
     *
299
     * @param string $flag
300
     *
301
     * @return bool
302
     */
303 3396
    public function hasFlag($flag)
304
    {
305 3396
        return isset($this->_flags[strtolower($flag)]);
306
    }
307
308
    /**
309
     * Removes a flag.
310
     *
311
     * @param string $flag
312
     *
313
     * @return void
314
     */
315 19
    public function removeFlag($flag)
316
    {
317 19
        unset($this->_flags[strtolower($flag)]);
318 19
    }
319
320
    /**
321
     * @param string $name
322
     *
323
     * @return bool
324
     */
325 4763
    public function hasOption($name)
326
    {
327 4763
        return isset($this->options[strtolower($name)]);
328
    }
329
330
    /**
331
     * @param string $name
332
     *
333
     * @return mixed
334
     */
335 166
    public function getOption($name)
336
    {
337 166
        return $this->options[strtolower($name)];
338
    }
339
340
    /**
341
     * @return array
342
     */
343 246
    public function getOptions()
344
    {
345 246
        return $this->options;
346
    }
347
348
    /**
349
     * Return whether the two indexes have the same partial index
350
     * @param \Doctrine\DBAL\Schema\Index $other
351
     *
352
     * @return bool
353
     */
354 1646
    private function samePartialIndex(Index $other)
355
    {
356 1646
        if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) {
357 44
            return true;
358
        }
359
360 1640
        return ! $this->hasOption('where') && ! $other->hasOption('where');
361
    }
362
363
}
364