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 | } | ||
