diff options
Diffstat (limited to 'vendor/doctrine/dbal/src/Schema/Index.php')
-rw-r--r-- | vendor/doctrine/dbal/src/Schema/Index.php | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Schema/Index.php b/vendor/doctrine/dbal/src/Schema/Index.php new file mode 100644 index 0000000..1755654 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Index.php | |||
@@ -0,0 +1,314 @@ | |||
1 | <?php | ||
2 | |||
3 | declare(strict_types=1); | ||
4 | |||
5 | namespace Doctrine\DBAL\Schema; | ||
6 | |||
7 | use Doctrine\DBAL\Platforms\AbstractPlatform; | ||
8 | |||
9 | use function array_filter; | ||
10 | use function array_keys; | ||
11 | use function array_map; | ||
12 | use function array_search; | ||
13 | use function array_shift; | ||
14 | use function count; | ||
15 | use function strtolower; | ||
16 | |||
17 | class Index extends AbstractAsset | ||
18 | { | ||
19 | /** | ||
20 | * Asset identifier instances of the column names the index is associated with. | ||
21 | * | ||
22 | * @var array<string, Identifier> | ||
23 | */ | ||
24 | protected array $_columns = []; | ||
25 | |||
26 | protected bool $_isUnique = false; | ||
27 | |||
28 | protected bool $_isPrimary = false; | ||
29 | |||
30 | /** | ||
31 | * Platform specific flags for indexes. | ||
32 | * | ||
33 | * @var array<string, true> | ||
34 | */ | ||
35 | protected array $_flags = []; | ||
36 | |||
37 | /** | ||
38 | * @param array<int, string> $columns | ||
39 | * @param array<int, string> $flags | ||
40 | * @param array<string, mixed> $options | ||
41 | */ | ||
42 | public function __construct( | ||
43 | ?string $name, | ||
44 | array $columns, | ||
45 | bool $isUnique = false, | ||
46 | bool $isPrimary = false, | ||
47 | array $flags = [], | ||
48 | private readonly array $options = [], | ||
49 | ) { | ||
50 | $isUnique = $isUnique || $isPrimary; | ||
51 | |||
52 | if ($name !== null) { | ||
53 | $this->_setName($name); | ||
54 | } | ||
55 | |||
56 | $this->_isUnique = $isUnique; | ||
57 | $this->_isPrimary = $isPrimary; | ||
58 | |||
59 | foreach ($columns as $column) { | ||
60 | $this->_addColumn($column); | ||
61 | } | ||
62 | |||
63 | foreach ($flags as $flag) { | ||
64 | $this->addFlag($flag); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | protected function _addColumn(string $column): void | ||
69 | { | ||
70 | $this->_columns[$column] = new Identifier($column); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Returns the names of the referencing table columns the constraint is associated with. | ||
75 | * | ||
76 | * @return list<string> | ||
77 | */ | ||
78 | public function getColumns(): array | ||
79 | { | ||
80 | return array_keys($this->_columns); | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Returns the quoted representation of the column names the constraint is associated with. | ||
85 | * | ||
86 | * But only if they were defined with one or a column name | ||
87 | * is a keyword reserved by the platform. | ||
88 | * Otherwise, the plain unquoted value as inserted is returned. | ||
89 | * | ||
90 | * @param AbstractPlatform $platform The platform to use for quotation. | ||
91 | * | ||
92 | * @return list<string> | ||
93 | */ | ||
94 | public function getQuotedColumns(AbstractPlatform $platform): array | ||
95 | { | ||
96 | $subParts = $platform->supportsColumnLengthIndexes() && $this->hasOption('lengths') | ||
97 | ? $this->getOption('lengths') : []; | ||
98 | |||
99 | $columns = []; | ||
100 | |||
101 | foreach ($this->_columns as $column) { | ||
102 | $length = array_shift($subParts); | ||
103 | |||
104 | $quotedColumn = $column->getQuotedName($platform); | ||
105 | |||
106 | if ($length !== null) { | ||
107 | $quotedColumn .= '(' . $length . ')'; | ||
108 | } | ||
109 | |||
110 | $columns[] = $quotedColumn; | ||
111 | } | ||
112 | |||
113 | return $columns; | ||
114 | } | ||
115 | |||
116 | /** @return array<int, string> */ | ||
117 | public function getUnquotedColumns(): array | ||
118 | { | ||
119 | return array_map($this->trimQuotes(...), $this->getColumns()); | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * Is the index neither unique nor primary key? | ||
124 | */ | ||
125 | public function isSimpleIndex(): bool | ||
126 | { | ||
127 | return ! $this->_isPrimary && ! $this->_isUnique; | ||
128 | } | ||
129 | |||
130 | public function isUnique(): bool | ||
131 | { | ||
132 | return $this->_isUnique; | ||
133 | } | ||
134 | |||
135 | public function isPrimary(): bool | ||
136 | { | ||
137 | return $this->_isPrimary; | ||
138 | } | ||
139 | |||
140 | public function hasColumnAtPosition(string $name, int $pos = 0): bool | ||
141 | { | ||
142 | $name = $this->trimQuotes(strtolower($name)); | ||
143 | $indexColumns = array_map('strtolower', $this->getUnquotedColumns()); | ||
144 | |||
145 | return array_search($name, $indexColumns, true) === $pos; | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * Checks if this index exactly spans the given column names in the correct order. | ||
150 | * | ||
151 | * @param array<int, string> $columnNames | ||
152 | */ | ||
153 | public function spansColumns(array $columnNames): bool | ||
154 | { | ||
155 | $columns = $this->getColumns(); | ||
156 | $numberOfColumns = count($columns); | ||
157 | $sameColumns = true; | ||
158 | |||
159 | for ($i = 0; $i < $numberOfColumns; $i++) { | ||
160 | if ( | ||
161 | isset($columnNames[$i]) | ||
162 | && $this->trimQuotes(strtolower($columns[$i])) === $this->trimQuotes(strtolower($columnNames[$i])) | ||
163 | ) { | ||
164 | continue; | ||
165 | } | ||
166 | |||
167 | $sameColumns = false; | ||
168 | } | ||
169 | |||
170 | return $sameColumns; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * Checks if the other index already fulfills all the indexing and constraint needs of the current one. | ||
175 | */ | ||
176 | public function isFulfilledBy(Index $other): bool | ||
177 | { | ||
178 | // allow the other index to be equally large only. It being larger is an option | ||
179 | // but it creates a problem with scenarios of the kind PRIMARY KEY(foo,bar) UNIQUE(foo) | ||
180 | if (count($other->getColumns()) !== count($this->getColumns())) { | ||
181 | return false; | ||
182 | } | ||
183 | |||
184 | // Check if columns are the same, and even in the same order | ||
185 | $sameColumns = $this->spansColumns($other->getColumns()); | ||
186 | |||
187 | if ($sameColumns) { | ||
188 | if (! $this->samePartialIndex($other)) { | ||
189 | return false; | ||
190 | } | ||
191 | |||
192 | if (! $this->hasSameColumnLengths($other)) { | ||
193 | return false; | ||
194 | } | ||
195 | |||
196 | if (! $this->isUnique() && ! $this->isPrimary()) { | ||
197 | // this is a special case: If the current key is neither primary or unique, any unique or | ||
198 | // primary key will always have the same effect for the index and there cannot be any constraint | ||
199 | // overlaps. This means a primary or unique index can always fulfill the requirements of just an | ||
200 | // index that has no constraints. | ||
201 | return true; | ||
202 | } | ||
203 | |||
204 | if ($other->isPrimary() !== $this->isPrimary()) { | ||
205 | return false; | ||
206 | } | ||
207 | |||
208 | return $other->isUnique() === $this->isUnique(); | ||
209 | } | ||
210 | |||
211 | return false; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * Detects if the other index is a non-unique, non primary index that can be overwritten by this one. | ||
216 | */ | ||
217 | public function overrules(Index $other): bool | ||
218 | { | ||
219 | if ($other->isPrimary()) { | ||
220 | return false; | ||
221 | } | ||
222 | |||
223 | if ($this->isSimpleIndex() && $other->isUnique()) { | ||
224 | return false; | ||
225 | } | ||
226 | |||
227 | return $this->spansColumns($other->getColumns()) | ||
228 | && ($this->isPrimary() || $this->isUnique()) | ||
229 | && $this->samePartialIndex($other); | ||
230 | } | ||
231 | |||
232 | /** | ||
233 | * Returns platform specific flags for indexes. | ||
234 | * | ||
235 | * @return array<int, string> | ||
236 | */ | ||
237 | public function getFlags(): array | ||
238 | { | ||
239 | return array_keys($this->_flags); | ||
240 | } | ||
241 | |||
242 | /** | ||
243 | * Adds Flag for an index that translates to platform specific handling. | ||
244 | * | ||
245 | * @example $index->addFlag('CLUSTERED') | ||
246 | */ | ||
247 | public function addFlag(string $flag): self | ||
248 | { | ||
249 | $this->_flags[strtolower($flag)] = true; | ||
250 | |||
251 | return $this; | ||
252 | } | ||
253 | |||
254 | /** | ||
255 | * Does this index have a specific flag? | ||
256 | */ | ||
257 | public function hasFlag(string $flag): bool | ||
258 | { | ||
259 | return isset($this->_flags[strtolower($flag)]); | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * Removes a flag. | ||
264 | */ | ||
265 | public function removeFlag(string $flag): void | ||
266 | { | ||
267 | unset($this->_flags[strtolower($flag)]); | ||
268 | } | ||
269 | |||
270 | public function hasOption(string $name): bool | ||
271 | { | ||
272 | return isset($this->options[strtolower($name)]); | ||
273 | } | ||
274 | |||
275 | public function getOption(string $name): mixed | ||
276 | { | ||
277 | return $this->options[strtolower($name)]; | ||
278 | } | ||
279 | |||
280 | /** @return array<string, mixed> */ | ||
281 | public function getOptions(): array | ||
282 | { | ||
283 | return $this->options; | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * Return whether the two indexes have the same partial index | ||
288 | */ | ||
289 | private function samePartialIndex(Index $other): bool | ||
290 | { | ||
291 | if ( | ||
292 | $this->hasOption('where') | ||
293 | && $other->hasOption('where') | ||
294 | && $this->getOption('where') === $other->getOption('where') | ||
295 | ) { | ||
296 | return true; | ||
297 | } | ||
298 | |||
299 | return ! $this->hasOption('where') && ! $other->hasOption('where'); | ||
300 | } | ||
301 | |||
302 | /** | ||
303 | * Returns whether the index has the same column lengths as the other | ||
304 | */ | ||
305 | private function hasSameColumnLengths(self $other): bool | ||
306 | { | ||
307 | $filter = static function (?int $length): bool { | ||
308 | return $length !== null; | ||
309 | }; | ||
310 | |||
311 | return array_filter($this->options['lengths'] ?? [], $filter) | ||
312 | === array_filter($other->options['lengths'] ?? [], $filter); | ||
313 | } | ||
314 | } | ||