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
|
|
|
|
24
|
|
|
class Index extends AbstractAsset implements Constraint |
25
|
|
|
{ |
26
|
|
|
/** |
27
|
|
|
* Asset identifier instances of the column names the index is associated with. |
28
|
|
|
* array($columnName => Identifier) |
29
|
|
|
* |
30
|
|
|
* @var Identifier[] |
31
|
|
|
*/ |
32
|
|
|
protected $_columns = array(); |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var boolean |
36
|
|
|
*/ |
37
|
|
|
protected $_isUnique = false; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var boolean |
41
|
|
|
*/ |
42
|
|
|
protected $_isPrimary = false; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* Platform specific flags for indexes. |
46
|
|
|
* array($flagName => true) |
47
|
|
|
* |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
protected $_flags = array(); |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Platform specific options |
54
|
|
|
* |
55
|
|
|
* @todo $_flags should eventually be refactored into options |
56
|
|
|
* |
57
|
|
|
* @var array |
58
|
|
|
*/ |
59
|
|
|
private $options = array(); |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @param string $indexName |
63
|
|
|
* @param string[] $columns |
64
|
|
|
* @param boolean $isUnique |
65
|
|
|
* @param boolean $isPrimary |
66
|
|
|
* @param string[] $flags |
67
|
|
|
* @param array $options |
68
|
|
|
*/ |
69
|
621 |
|
public function __construct($indexName, array $columns, $isUnique = false, $isPrimary = false, array $flags = array(), array $options = array()) |
70
|
|
|
{ |
71
|
621 |
|
$isUnique = $isUnique || $isPrimary; |
72
|
|
|
|
73
|
621 |
|
$this->_setName($indexName); |
74
|
621 |
|
$this->_isUnique = $isUnique; |
75
|
621 |
|
$this->_isPrimary = $isPrimary; |
76
|
621 |
|
$this->options = $options; |
77
|
|
|
|
78
|
621 |
|
foreach ($columns as $column) { |
79
|
600 |
|
$this->_addColumn($column); |
80
|
|
|
} |
81
|
621 |
|
foreach ($flags as $flag) { |
82
|
18 |
|
$this->addFlag($flag); |
83
|
|
|
} |
84
|
621 |
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @param string $column |
88
|
|
|
* |
89
|
|
|
* @return void |
90
|
|
|
* |
91
|
|
|
* @throws \InvalidArgumentException |
92
|
|
|
*/ |
93
|
600 |
|
protected function _addColumn($column) |
94
|
|
|
{ |
95
|
600 |
|
if (is_string($column)) { |
96
|
600 |
|
$this->_columns[$column] = new Identifier($column); |
97
|
|
|
} else { |
98
|
|
|
throw new \InvalidArgumentException("Expecting a string as Index Column"); |
99
|
|
|
} |
100
|
600 |
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* {@inheritdoc} |
104
|
|
|
*/ |
105
|
306 |
|
public function getColumns() |
106
|
|
|
{ |
107
|
306 |
|
return array_keys($this->_columns); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* {@inheritdoc} |
112
|
|
|
*/ |
113
|
379 |
View Code Duplication |
public function getQuotedColumns(AbstractPlatform $platform) |
|
|
|
|
114
|
|
|
{ |
115
|
379 |
|
$columns = array(); |
116
|
|
|
|
117
|
379 |
|
foreach ($this->_columns as $column) { |
118
|
371 |
|
$columns[] = $column->getQuotedName($platform); |
119
|
|
|
} |
120
|
|
|
|
121
|
379 |
|
return $columns; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @return string[] |
126
|
|
|
*/ |
127
|
1 |
|
public function getUnquotedColumns() |
128
|
|
|
{ |
129
|
1 |
|
return array_map(array($this, 'trimQuotes'), $this->getColumns()); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Is the index neither unique nor primary key? |
134
|
|
|
* |
135
|
|
|
* @return boolean |
136
|
|
|
*/ |
137
|
1 |
|
public function isSimpleIndex() |
138
|
|
|
{ |
139
|
1 |
|
return !$this->_isPrimary && !$this->_isUnique; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return boolean |
144
|
|
|
*/ |
145
|
276 |
|
public function isUnique() |
146
|
|
|
{ |
147
|
276 |
|
return $this->_isUnique; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
/** |
151
|
|
|
* @return boolean |
152
|
|
|
*/ |
153
|
576 |
|
public function isPrimary() |
154
|
|
|
{ |
155
|
576 |
|
return $this->_isPrimary; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @param string $columnName |
160
|
|
|
* @param integer $pos |
161
|
|
|
* |
162
|
|
|
* @return boolean |
163
|
|
|
*/ |
164
|
1 |
|
public function hasColumnAtPosition($columnName, $pos = 0) |
165
|
|
|
{ |
166
|
1 |
|
$columnName = $this->trimQuotes(strtolower($columnName)); |
167
|
1 |
|
$indexColumns = array_map('strtolower', $this->getUnquotedColumns()); |
168
|
|
|
|
169
|
1 |
|
return array_search($columnName, $indexColumns) === $pos; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Checks if this index exactly spans the given column names in the correct order. |
174
|
|
|
* |
175
|
|
|
* @param array $columnNames |
176
|
|
|
* |
177
|
|
|
* @return boolean |
178
|
|
|
*/ |
179
|
120 |
|
public function spansColumns(array $columnNames) |
180
|
|
|
{ |
181
|
120 |
|
$columns = $this->getColumns(); |
182
|
120 |
|
$numberOfColumns = count($columns); |
183
|
120 |
|
$sameColumns = true; |
184
|
|
|
|
185
|
120 |
|
for ($i = 0; $i < $numberOfColumns; $i++) { |
186
|
120 |
|
if ( ! isset($columnNames[$i]) || $this->trimQuotes(strtolower($columns[$i])) !== $this->trimQuotes(strtolower($columnNames[$i]))) { |
187
|
52 |
|
$sameColumns = false; |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
|
191
|
120 |
|
return $sameColumns; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Checks if the other index already fulfills all the indexing and constraint needs of the current one. |
196
|
|
|
* |
197
|
|
|
* @param Index $other |
198
|
|
|
* |
199
|
|
|
* @return boolean |
200
|
|
|
*/ |
201
|
120 |
|
public function isFullfilledBy(Index $other) |
202
|
|
|
{ |
203
|
|
|
// allow the other index to be longer or equally large. |
204
|
120 |
|
if (count($other->getColumns()) < count($this->getColumns())) { |
205
|
15 |
|
return false; |
206
|
|
|
} |
207
|
|
|
|
208
|
118 |
|
if (count($other->getColumns()) != count($this->getColumns())) { |
209
|
|
|
// A special case where an index won't be fullffiled by a longer one: |
210
|
|
|
// PRIMARY KEY(foo, bar) |
211
|
|
|
// UNIQUE(foo) |
212
|
9 |
|
if ( ! $this->isPrimary() && $this->isUnique() && $other->isPrimary()) { |
213
|
1 |
|
return false; |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
// Check if columns are the same, and even in the same order |
218
|
117 |
|
$sameColumns = $this->spansColumns($other->getColumns()); |
219
|
|
|
|
220
|
117 |
|
if ($sameColumns) { |
221
|
86 |
|
if ( ! $this->samePartialIndex($other)) { |
222
|
1 |
|
return false; |
223
|
|
|
} |
224
|
|
|
|
225
|
86 |
|
if ( ! $this->isUnique() && ! $this->isPrimary()) { |
226
|
|
|
// this is a special case: If the current key is neither primary or unique, any unique or |
227
|
|
|
// primary key will always have the same effect for the index and there cannot be any constraint |
228
|
|
|
// overlaps. This means a primary or unique index can always fulfill the requirements of just an |
229
|
|
|
// index that has no constraints. |
230
|
60 |
|
return true; |
231
|
|
|
} |
232
|
|
|
|
233
|
26 |
|
if ($other->isPrimary() != $this->isPrimary()) { |
234
|
3 |
|
return false; |
235
|
|
|
} |
236
|
|
|
|
237
|
24 |
|
if ($other->isUnique() != $this->isUnique()) { |
238
|
1 |
|
return false; |
239
|
|
|
} |
240
|
|
|
|
241
|
24 |
|
return true; |
242
|
|
|
} |
243
|
|
|
|
244
|
52 |
|
return false; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Detects if the other index is a non-unique, non primary index that can be overwritten by this one. |
249
|
|
|
* |
250
|
|
|
* @param Index $other |
251
|
|
|
* |
252
|
|
|
* @return boolean |
253
|
|
|
*/ |
254
|
1 |
|
public function overrules(Index $other) |
255
|
|
|
{ |
256
|
1 |
|
if ($other->isPrimary()) { |
257
|
|
|
return false; |
258
|
1 |
|
} elseif ($this->isSimpleIndex() && $other->isUnique()) { |
259
|
|
|
return false; |
260
|
|
|
} |
261
|
|
|
|
262
|
1 |
|
if ($this->spansColumns($other->getColumns()) && ($this->isPrimary() || $this->isUnique()) && $this->samePartialIndex($other)) { |
263
|
1 |
|
return true; |
264
|
|
|
} |
265
|
|
|
|
266
|
1 |
|
return false; |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
/** |
270
|
|
|
* Returns platform specific flags for indexes. |
271
|
|
|
* |
272
|
|
|
* @return string[] |
273
|
|
|
*/ |
274
|
12 |
|
public function getFlags() |
275
|
|
|
{ |
276
|
12 |
|
return array_keys($this->_flags); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* Adds Flag for an index that translates to platform specific handling. |
281
|
|
|
* |
282
|
|
|
* @example $index->addFlag('CLUSTERED') |
283
|
|
|
* |
284
|
|
|
* @param string $flag |
285
|
|
|
* |
286
|
|
|
* @return Index |
287
|
|
|
*/ |
288
|
33 |
|
public function addFlag($flag) |
289
|
|
|
{ |
290
|
33 |
|
$this->_flags[strtolower($flag)] = true; |
291
|
|
|
|
292
|
33 |
|
return $this; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Does this index have a specific flag? |
297
|
|
|
* |
298
|
|
|
* @param string $flag |
299
|
|
|
* |
300
|
|
|
* @return boolean |
301
|
|
|
*/ |
302
|
147 |
|
public function hasFlag($flag) |
303
|
|
|
{ |
304
|
147 |
|
return isset($this->_flags[strtolower($flag)]); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Removes a flag. |
309
|
|
|
* |
310
|
|
|
* @param string $flag |
311
|
|
|
* |
312
|
|
|
* @return void |
313
|
|
|
*/ |
314
|
1 |
|
public function removeFlag($flag) |
315
|
|
|
{ |
316
|
1 |
|
unset($this->_flags[strtolower($flag)]); |
317
|
1 |
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* @param string $name |
321
|
|
|
* |
322
|
|
|
* @return boolean |
323
|
|
|
*/ |
324
|
120 |
|
public function hasOption($name) |
325
|
|
|
{ |
326
|
120 |
|
return isset($this->options[strtolower($name)]); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* @param string $name |
331
|
|
|
* |
332
|
|
|
* @return mixed |
333
|
|
|
*/ |
334
|
7 |
|
public function getOption($name) |
335
|
|
|
{ |
336
|
7 |
|
return $this->options[strtolower($name)]; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* @return array |
341
|
|
|
*/ |
342
|
12 |
|
public function getOptions() |
343
|
|
|
{ |
344
|
12 |
|
return $this->options; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* Return whether the two indexes have the same partial index |
349
|
|
|
* @param \Doctrine\DBAL\Schema\Index $other |
350
|
|
|
* |
351
|
|
|
* @return boolean |
352
|
|
|
*/ |
353
|
87 |
|
private function samePartialIndex(Index $other) |
354
|
|
|
{ |
355
|
87 |
|
if ($this->hasOption('where') && $other->hasOption('where') && $this->getOption('where') == $other->getOption('where')) { |
356
|
2 |
|
return true; |
357
|
|
|
} |
358
|
|
|
|
359
|
87 |
|
if ( ! $this->hasOption('where') && ! $other->hasOption('where')) { |
360
|
85 |
|
return true; |
361
|
|
|
} |
362
|
|
|
|
363
|
2 |
|
return false; |
364
|
|
|
} |
365
|
|
|
|
366
|
|
|
} |
367
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.