diff options
| author | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
| commit | bf6655a534a6775d30cafa67bd801276bda1d98d (patch) | |
| tree | c6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/doctrine/dbal/src/Schema | |
| parent | 94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff) | |
| download | AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.gz AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.bz2 AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip | |
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/doctrine/dbal/src/Schema')
39 files changed, 7569 insertions, 0 deletions
diff --git a/vendor/doctrine/dbal/src/Schema/AbstractAsset.php b/vendor/doctrine/dbal/src/Schema/AbstractAsset.php new file mode 100644 index 0000000..de5b56f --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/AbstractAsset.php | |||
| @@ -0,0 +1,157 @@ | |||
| 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_map; | ||
| 10 | use function crc32; | ||
| 11 | use function dechex; | ||
| 12 | use function explode; | ||
| 13 | use function implode; | ||
| 14 | use function str_contains; | ||
| 15 | use function str_replace; | ||
| 16 | use function strtolower; | ||
| 17 | use function strtoupper; | ||
| 18 | use function substr; | ||
| 19 | |||
| 20 | /** | ||
| 21 | * The abstract asset allows to reset the name of all assets without publishing this to the public userland. | ||
| 22 | * | ||
| 23 | * This encapsulation hack is necessary to keep a consistent state of the database schema. Say we have a list of tables | ||
| 24 | * array($tableName => Table($tableName)); if you want to rename the table, you have to make sure this does not get | ||
| 25 | * recreated during schema migration. | ||
| 26 | */ | ||
| 27 | abstract class AbstractAsset | ||
| 28 | { | ||
| 29 | protected string $_name = ''; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Namespace of the asset. If none isset the default namespace is assumed. | ||
| 33 | */ | ||
| 34 | protected ?string $_namespace = null; | ||
| 35 | |||
| 36 | protected bool $_quoted = false; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Sets the name of this asset. | ||
| 40 | */ | ||
| 41 | protected function _setName(string $name): void | ||
| 42 | { | ||
| 43 | if ($this->isIdentifierQuoted($name)) { | ||
| 44 | $this->_quoted = true; | ||
| 45 | $name = $this->trimQuotes($name); | ||
| 46 | } | ||
| 47 | |||
| 48 | if (str_contains($name, '.')) { | ||
| 49 | $parts = explode('.', $name); | ||
| 50 | $this->_namespace = $parts[0]; | ||
| 51 | $name = $parts[1]; | ||
| 52 | } | ||
| 53 | |||
| 54 | $this->_name = $name; | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Is this asset in the default namespace? | ||
| 59 | */ | ||
| 60 | public function isInDefaultNamespace(string $defaultNamespaceName): bool | ||
| 61 | { | ||
| 62 | return $this->_namespace === $defaultNamespaceName || $this->_namespace === null; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** | ||
| 66 | * Gets the namespace name of this asset. | ||
| 67 | * | ||
| 68 | * If NULL is returned this means the default namespace is used. | ||
| 69 | */ | ||
| 70 | public function getNamespaceName(): ?string | ||
| 71 | { | ||
| 72 | return $this->_namespace; | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * The shortest name is stripped of the default namespace. All other | ||
| 77 | * namespaced elements are returned as full-qualified names. | ||
| 78 | */ | ||
| 79 | public function getShortestName(?string $defaultNamespaceName): string | ||
| 80 | { | ||
| 81 | $shortestName = $this->getName(); | ||
| 82 | if ($this->_namespace === $defaultNamespaceName) { | ||
| 83 | $shortestName = $this->_name; | ||
| 84 | } | ||
| 85 | |||
| 86 | return strtolower($shortestName); | ||
| 87 | } | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Checks if this asset's name is quoted. | ||
| 91 | */ | ||
| 92 | public function isQuoted(): bool | ||
| 93 | { | ||
| 94 | return $this->_quoted; | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Checks if this identifier is quoted. | ||
| 99 | */ | ||
| 100 | protected function isIdentifierQuoted(string $identifier): bool | ||
| 101 | { | ||
| 102 | return isset($identifier[0]) && ($identifier[0] === '`' || $identifier[0] === '"' || $identifier[0] === '['); | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Trim quotes from the identifier. | ||
| 107 | */ | ||
| 108 | protected function trimQuotes(string $identifier): string | ||
| 109 | { | ||
| 110 | return str_replace(['`', '"', '[', ']'], '', $identifier); | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Returns the name of this schema asset. | ||
| 115 | */ | ||
| 116 | public function getName(): string | ||
| 117 | { | ||
| 118 | if ($this->_namespace !== null) { | ||
| 119 | return $this->_namespace . '.' . $this->_name; | ||
| 120 | } | ||
| 121 | |||
| 122 | return $this->_name; | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 126 | * Gets the quoted representation of this asset but only if it was defined with one. Otherwise | ||
| 127 | * return the plain unquoted value as inserted. | ||
| 128 | */ | ||
| 129 | public function getQuotedName(AbstractPlatform $platform): string | ||
| 130 | { | ||
| 131 | $keywords = $platform->getReservedKeywordsList(); | ||
| 132 | $parts = explode('.', $this->getName()); | ||
| 133 | foreach ($parts as $k => $v) { | ||
| 134 | $parts[$k] = $this->_quoted || $keywords->isKeyword($v) ? $platform->quoteIdentifier($v) : $v; | ||
| 135 | } | ||
| 136 | |||
| 137 | return implode('.', $parts); | ||
| 138 | } | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Generates an identifier from a list of column names obeying a certain string length. | ||
| 142 | * | ||
| 143 | * This is especially important for Oracle, since it does not allow identifiers larger than 30 chars, | ||
| 144 | * however building idents automatically for foreign keys, composite keys or such can easily create | ||
| 145 | * very long names. | ||
| 146 | * | ||
| 147 | * @param array<int, string> $columnNames | ||
| 148 | */ | ||
| 149 | protected function _generateIdentifierName(array $columnNames, string $prefix = '', int $maxSize = 30): string | ||
| 150 | { | ||
| 151 | $hash = implode('', array_map(static function ($column): string { | ||
| 152 | return dechex(crc32($column)); | ||
| 153 | }, $columnNames)); | ||
| 154 | |||
| 155 | return strtoupper(substr($prefix . '_' . $hash, 0, $maxSize)); | ||
| 156 | } | ||
| 157 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php b/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php new file mode 100644 index 0000000..9730797 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/AbstractSchemaManager.php | |||
| @@ -0,0 +1,864 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Connection; | ||
| 8 | use Doctrine\DBAL\Exception; | ||
| 9 | use Doctrine\DBAL\Exception\DatabaseRequired; | ||
| 10 | use Doctrine\DBAL\Platforms\AbstractPlatform; | ||
| 11 | use Doctrine\DBAL\Platforms\Exception\NotSupported; | ||
| 12 | use Doctrine\DBAL\Result; | ||
| 13 | use Doctrine\DBAL\Schema\Exception\TableDoesNotExist; | ||
| 14 | |||
| 15 | use function array_filter; | ||
| 16 | use function array_intersect; | ||
| 17 | use function array_map; | ||
| 18 | use function array_values; | ||
| 19 | use function count; | ||
| 20 | use function strtolower; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Base class for schema managers. Schema managers are used to inspect and/or | ||
| 24 | * modify the database schema/structure. | ||
| 25 | * | ||
| 26 | * @template-covariant T of AbstractPlatform | ||
| 27 | */ | ||
| 28 | abstract class AbstractSchemaManager | ||
| 29 | { | ||
| 30 | /** @param T $platform */ | ||
| 31 | public function __construct(protected Connection $connection, protected AbstractPlatform $platform) | ||
| 32 | { | ||
| 33 | } | ||
| 34 | |||
| 35 | /** | ||
| 36 | * Lists the available databases for this connection. | ||
| 37 | * | ||
| 38 | * @return array<int, string> | ||
| 39 | * | ||
| 40 | * @throws Exception | ||
| 41 | */ | ||
| 42 | public function listDatabases(): array | ||
| 43 | { | ||
| 44 | return array_map(function (array $row): string { | ||
| 45 | return $this->_getPortableDatabaseDefinition($row); | ||
| 46 | }, $this->connection->fetchAllAssociative( | ||
| 47 | $this->platform->getListDatabasesSQL(), | ||
| 48 | )); | ||
| 49 | } | ||
| 50 | |||
| 51 | /** | ||
| 52 | * Returns a list of the names of all schemata in the current database. | ||
| 53 | * | ||
| 54 | * @return list<string> | ||
| 55 | * | ||
| 56 | * @throws Exception | ||
| 57 | */ | ||
| 58 | public function listSchemaNames(): array | ||
| 59 | { | ||
| 60 | throw NotSupported::new(__METHOD__); | ||
| 61 | } | ||
| 62 | |||
| 63 | /** | ||
| 64 | * Lists the available sequences for this connection. | ||
| 65 | * | ||
| 66 | * @return array<int, Sequence> | ||
| 67 | * | ||
| 68 | * @throws Exception | ||
| 69 | */ | ||
| 70 | public function listSequences(): array | ||
| 71 | { | ||
| 72 | return $this->filterAssetNames( | ||
| 73 | array_map(function (array $row): Sequence { | ||
| 74 | return $this->_getPortableSequenceDefinition($row); | ||
| 75 | }, $this->connection->fetchAllAssociative( | ||
| 76 | $this->platform->getListSequencesSQL( | ||
| 77 | $this->getDatabase(__METHOD__), | ||
| 78 | ), | ||
| 79 | )), | ||
| 80 | ); | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * Lists the columns for a given table. | ||
| 85 | * | ||
| 86 | * In contrast to other libraries and to the old version of Doctrine, | ||
| 87 | * this column definition does try to contain the 'primary' column for | ||
| 88 | * the reason that it is not portable across different RDBMS. Use | ||
| 89 | * {@see listTableIndexes($tableName)} to retrieve the primary key | ||
| 90 | * of a table. Where a RDBMS specifies more details, these are held | ||
| 91 | * in the platformDetails array. | ||
| 92 | * | ||
| 93 | * @return array<string, Column> | ||
| 94 | * | ||
| 95 | * @throws Exception | ||
| 96 | */ | ||
| 97 | public function listTableColumns(string $table): array | ||
| 98 | { | ||
| 99 | $database = $this->getDatabase(__METHOD__); | ||
| 100 | |||
| 101 | return $this->_getPortableTableColumnList( | ||
| 102 | $table, | ||
| 103 | $database, | ||
| 104 | $this->selectTableColumns($database, $this->normalizeName($table)) | ||
| 105 | ->fetchAllAssociative(), | ||
| 106 | ); | ||
| 107 | } | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Lists the indexes for a given table returning an array of Index instances. | ||
| 111 | * | ||
| 112 | * Keys of the portable indexes list are all lower-cased. | ||
| 113 | * | ||
| 114 | * @return array<string, Index> | ||
| 115 | * | ||
| 116 | * @throws Exception | ||
| 117 | */ | ||
| 118 | public function listTableIndexes(string $table): array | ||
| 119 | { | ||
| 120 | $database = $this->getDatabase(__METHOD__); | ||
| 121 | $table = $this->normalizeName($table); | ||
| 122 | |||
| 123 | return $this->_getPortableTableIndexesList( | ||
| 124 | $this->selectIndexColumns( | ||
| 125 | $database, | ||
| 126 | $table, | ||
| 127 | )->fetchAllAssociative(), | ||
| 128 | $table, | ||
| 129 | ); | ||
| 130 | } | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Returns true if all the given tables exist. | ||
| 134 | * | ||
| 135 | * @param array<int, string> $names | ||
| 136 | * | ||
| 137 | * @throws Exception | ||
| 138 | */ | ||
| 139 | public function tablesExist(array $names): bool | ||
| 140 | { | ||
| 141 | $names = array_map('strtolower', $names); | ||
| 142 | |||
| 143 | return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames()))); | ||
| 144 | } | ||
| 145 | |||
| 146 | public function tableExists(string $tableName): bool | ||
| 147 | { | ||
| 148 | return $this->tablesExist([$tableName]); | ||
| 149 | } | ||
| 150 | |||
| 151 | /** | ||
| 152 | * Returns a list of all tables in the current database. | ||
| 153 | * | ||
| 154 | * @return array<int, string> | ||
| 155 | * | ||
| 156 | * @throws Exception | ||
| 157 | */ | ||
| 158 | public function listTableNames(): array | ||
| 159 | { | ||
| 160 | return $this->filterAssetNames( | ||
| 161 | array_map(function (array $row): string { | ||
| 162 | return $this->_getPortableTableDefinition($row); | ||
| 163 | }, $this->selectTableNames( | ||
| 164 | $this->getDatabase(__METHOD__), | ||
| 165 | )->fetchAllAssociative()), | ||
| 166 | ); | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Filters asset names if they are configured to return only a subset of all | ||
| 171 | * the found elements. | ||
| 172 | * | ||
| 173 | * @param array<int, mixed> $assetNames | ||
| 174 | * | ||
| 175 | * @return array<int, mixed> | ||
| 176 | */ | ||
| 177 | private function filterAssetNames(array $assetNames): array | ||
| 178 | { | ||
| 179 | $filter = $this->connection->getConfiguration()->getSchemaAssetsFilter(); | ||
| 180 | |||
| 181 | return array_values(array_filter($assetNames, $filter)); | ||
| 182 | } | ||
| 183 | |||
| 184 | /** | ||
| 185 | * Lists the tables for this connection. | ||
| 186 | * | ||
| 187 | * @return list<Table> | ||
| 188 | * | ||
| 189 | * @throws Exception | ||
| 190 | */ | ||
| 191 | public function listTables(): array | ||
| 192 | { | ||
| 193 | $database = $this->getDatabase(__METHOD__); | ||
| 194 | |||
| 195 | $tableColumnsByTable = $this->fetchTableColumnsByTable($database); | ||
| 196 | $indexColumnsByTable = $this->fetchIndexColumnsByTable($database); | ||
| 197 | $foreignKeyColumnsByTable = $this->fetchForeignKeyColumnsByTable($database); | ||
| 198 | $tableOptionsByTable = $this->fetchTableOptionsByTable($database); | ||
| 199 | |||
| 200 | $filter = $this->connection->getConfiguration()->getSchemaAssetsFilter(); | ||
| 201 | $tables = []; | ||
| 202 | |||
| 203 | foreach ($tableColumnsByTable as $tableName => $tableColumns) { | ||
| 204 | if (! $filter($tableName)) { | ||
| 205 | continue; | ||
| 206 | } | ||
| 207 | |||
| 208 | $tables[] = new Table( | ||
| 209 | $tableName, | ||
| 210 | $this->_getPortableTableColumnList($tableName, $database, $tableColumns), | ||
| 211 | $this->_getPortableTableIndexesList($indexColumnsByTable[$tableName] ?? [], $tableName), | ||
| 212 | [], | ||
| 213 | $this->_getPortableTableForeignKeysList($foreignKeyColumnsByTable[$tableName] ?? []), | ||
| 214 | $tableOptionsByTable[$tableName] ?? [], | ||
| 215 | ); | ||
| 216 | } | ||
| 217 | |||
| 218 | return $tables; | ||
| 219 | } | ||
| 220 | |||
| 221 | /** | ||
| 222 | * An extension point for those platforms where case sensitivity of the object name depends on whether it's quoted. | ||
| 223 | * | ||
| 224 | * Such platforms should convert a possibly quoted name into a value of the corresponding case. | ||
| 225 | */ | ||
| 226 | protected function normalizeName(string $name): string | ||
| 227 | { | ||
| 228 | $identifier = new Identifier($name); | ||
| 229 | |||
| 230 | return $identifier->getName(); | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * Selects names of tables in the specified database. | ||
| 235 | * | ||
| 236 | * @throws Exception | ||
| 237 | */ | ||
| 238 | abstract protected function selectTableNames(string $databaseName): Result; | ||
| 239 | |||
| 240 | /** | ||
| 241 | * Selects definitions of table columns in the specified database. If the table name is specified, narrows down | ||
| 242 | * the selection to this table. | ||
| 243 | * | ||
| 244 | * @throws Exception | ||
| 245 | */ | ||
| 246 | abstract protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result; | ||
| 247 | |||
| 248 | /** | ||
| 249 | * Selects definitions of index columns in the specified database. If the table name is specified, narrows down | ||
| 250 | * the selection to this table. | ||
| 251 | * | ||
| 252 | * @throws Exception | ||
| 253 | */ | ||
| 254 | abstract protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result; | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Selects definitions of foreign key columns in the specified database. If the table name is specified, | ||
| 258 | * narrows down the selection to this table. | ||
| 259 | * | ||
| 260 | * @throws Exception | ||
| 261 | */ | ||
| 262 | abstract protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result; | ||
| 263 | |||
| 264 | /** | ||
| 265 | * Fetches definitions of table columns in the specified database and returns them grouped by table name. | ||
| 266 | * | ||
| 267 | * @return array<string,list<array<string,mixed>>> | ||
| 268 | * | ||
| 269 | * @throws Exception | ||
| 270 | */ | ||
| 271 | protected function fetchTableColumnsByTable(string $databaseName): array | ||
| 272 | { | ||
| 273 | return $this->fetchAllAssociativeGrouped($this->selectTableColumns($databaseName)); | ||
| 274 | } | ||
| 275 | |||
| 276 | /** | ||
| 277 | * Fetches definitions of index columns in the specified database and returns them grouped by table name. | ||
| 278 | * | ||
| 279 | * @return array<string,list<array<string,mixed>>> | ||
| 280 | * | ||
| 281 | * @throws Exception | ||
| 282 | */ | ||
| 283 | protected function fetchIndexColumnsByTable(string $databaseName): array | ||
| 284 | { | ||
| 285 | return $this->fetchAllAssociativeGrouped($this->selectIndexColumns($databaseName)); | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * Fetches definitions of foreign key columns in the specified database and returns them grouped by table name. | ||
| 290 | * | ||
| 291 | * @return array<string, list<array<string, mixed>>> | ||
| 292 | * | ||
| 293 | * @throws Exception | ||
| 294 | */ | ||
| 295 | protected function fetchForeignKeyColumnsByTable(string $databaseName): array | ||
| 296 | { | ||
| 297 | return $this->fetchAllAssociativeGrouped( | ||
| 298 | $this->selectForeignKeyColumns($databaseName), | ||
| 299 | ); | ||
| 300 | } | ||
| 301 | |||
| 302 | /** | ||
| 303 | * Fetches table options for the tables in the specified database and returns them grouped by table name. | ||
| 304 | * If the table name is specified, narrows down the selection to this table. | ||
| 305 | * | ||
| 306 | * @return array<string,array<string,mixed>> | ||
| 307 | * | ||
| 308 | * @throws Exception | ||
| 309 | */ | ||
| 310 | abstract protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array; | ||
| 311 | |||
| 312 | /** | ||
| 313 | * Introspects the table with the given name. | ||
| 314 | * | ||
| 315 | * @throws Exception | ||
| 316 | */ | ||
| 317 | public function introspectTable(string $name): Table | ||
| 318 | { | ||
| 319 | $columns = $this->listTableColumns($name); | ||
| 320 | |||
| 321 | if ($columns === []) { | ||
| 322 | throw TableDoesNotExist::new($name); | ||
| 323 | } | ||
| 324 | |||
| 325 | return new Table( | ||
| 326 | $name, | ||
| 327 | $columns, | ||
| 328 | $this->listTableIndexes($name), | ||
| 329 | [], | ||
| 330 | $this->listTableForeignKeys($name), | ||
| 331 | $this->getTableOptions($name), | ||
| 332 | ); | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 336 | * Lists the views this connection has. | ||
| 337 | * | ||
| 338 | * @return list<View> | ||
| 339 | * | ||
| 340 | * @throws Exception | ||
| 341 | */ | ||
| 342 | public function listViews(): array | ||
| 343 | { | ||
| 344 | return array_map(function (array $row): View { | ||
| 345 | return $this->_getPortableViewDefinition($row); | ||
| 346 | }, $this->connection->fetchAllAssociative( | ||
| 347 | $this->platform->getListViewsSQL( | ||
| 348 | $this->getDatabase(__METHOD__), | ||
| 349 | ), | ||
| 350 | )); | ||
| 351 | } | ||
| 352 | |||
| 353 | /** | ||
| 354 | * Lists the foreign keys for the given table. | ||
| 355 | * | ||
| 356 | * @return array<int|string, ForeignKeyConstraint> | ||
| 357 | * | ||
| 358 | * @throws Exception | ||
| 359 | */ | ||
| 360 | public function listTableForeignKeys(string $table): array | ||
| 361 | { | ||
| 362 | $database = $this->getDatabase(__METHOD__); | ||
| 363 | |||
| 364 | return $this->_getPortableTableForeignKeysList( | ||
| 365 | $this->selectForeignKeyColumns( | ||
| 366 | $database, | ||
| 367 | $this->normalizeName($table), | ||
| 368 | )->fetchAllAssociative(), | ||
| 369 | ); | ||
| 370 | } | ||
| 371 | |||
| 372 | /** | ||
| 373 | * @return array<string, mixed> | ||
| 374 | * | ||
| 375 | * @throws Exception | ||
| 376 | */ | ||
| 377 | private function getTableOptions(string $name): array | ||
| 378 | { | ||
| 379 | $normalizedName = $this->normalizeName($name); | ||
| 380 | |||
| 381 | return $this->fetchTableOptionsByTable( | ||
| 382 | $this->getDatabase(__METHOD__), | ||
| 383 | $normalizedName, | ||
| 384 | )[$normalizedName] ?? []; | ||
| 385 | } | ||
| 386 | |||
| 387 | /* drop*() Methods */ | ||
| 388 | |||
| 389 | /** | ||
| 390 | * Drops a database. | ||
| 391 | * | ||
| 392 | * NOTE: You can not drop the database this SchemaManager is currently connected to. | ||
| 393 | * | ||
| 394 | * @throws Exception | ||
| 395 | */ | ||
| 396 | public function dropDatabase(string $database): void | ||
| 397 | { | ||
| 398 | $this->connection->executeStatement( | ||
| 399 | $this->platform->getDropDatabaseSQL($database), | ||
| 400 | ); | ||
| 401 | } | ||
| 402 | |||
| 403 | /** | ||
| 404 | * Drops a schema. | ||
| 405 | * | ||
| 406 | * @throws Exception | ||
| 407 | */ | ||
| 408 | public function dropSchema(string $schemaName): void | ||
| 409 | { | ||
| 410 | $this->connection->executeStatement( | ||
| 411 | $this->platform->getDropSchemaSQL($schemaName), | ||
| 412 | ); | ||
| 413 | } | ||
| 414 | |||
| 415 | /** | ||
| 416 | * Drops the given table. | ||
| 417 | * | ||
| 418 | * @throws Exception | ||
| 419 | */ | ||
| 420 | public function dropTable(string $name): void | ||
| 421 | { | ||
| 422 | $this->connection->executeStatement( | ||
| 423 | $this->platform->getDropTableSQL($name), | ||
| 424 | ); | ||
| 425 | } | ||
| 426 | |||
| 427 | /** | ||
| 428 | * Drops the index from the given table. | ||
| 429 | * | ||
| 430 | * @throws Exception | ||
| 431 | */ | ||
| 432 | public function dropIndex(string $index, string $table): void | ||
| 433 | { | ||
| 434 | $this->connection->executeStatement( | ||
| 435 | $this->platform->getDropIndexSQL($index, $table), | ||
| 436 | ); | ||
| 437 | } | ||
| 438 | |||
| 439 | /** | ||
| 440 | * Drops a foreign key from a table. | ||
| 441 | * | ||
| 442 | * @throws Exception | ||
| 443 | */ | ||
| 444 | public function dropForeignKey(string $name, string $table): void | ||
| 445 | { | ||
| 446 | $this->connection->executeStatement( | ||
| 447 | $this->platform->getDropForeignKeySQL($name, $table), | ||
| 448 | ); | ||
| 449 | } | ||
| 450 | |||
| 451 | /** | ||
| 452 | * Drops a sequence with a given name. | ||
| 453 | * | ||
| 454 | * @throws Exception | ||
| 455 | */ | ||
| 456 | public function dropSequence(string $name): void | ||
| 457 | { | ||
| 458 | $this->connection->executeStatement( | ||
| 459 | $this->platform->getDropSequenceSQL($name), | ||
| 460 | ); | ||
| 461 | } | ||
| 462 | |||
| 463 | /** | ||
| 464 | * Drops the unique constraint from the given table. | ||
| 465 | * | ||
| 466 | * @throws Exception | ||
| 467 | */ | ||
| 468 | public function dropUniqueConstraint(string $name, string $tableName): void | ||
| 469 | { | ||
| 470 | $this->connection->executeStatement( | ||
| 471 | $this->platform->getDropUniqueConstraintSQL($name, $tableName), | ||
| 472 | ); | ||
| 473 | } | ||
| 474 | |||
| 475 | /** | ||
| 476 | * Drops a view. | ||
| 477 | * | ||
| 478 | * @throws Exception | ||
| 479 | */ | ||
| 480 | public function dropView(string $name): void | ||
| 481 | { | ||
| 482 | $this->connection->executeStatement( | ||
| 483 | $this->platform->getDropViewSQL($name), | ||
| 484 | ); | ||
| 485 | } | ||
| 486 | |||
| 487 | /* create*() Methods */ | ||
| 488 | |||
| 489 | /** @throws Exception */ | ||
| 490 | public function createSchemaObjects(Schema $schema): void | ||
| 491 | { | ||
| 492 | $this->executeStatements($schema->toSql($this->platform)); | ||
| 493 | } | ||
| 494 | |||
| 495 | /** | ||
| 496 | * Creates a new database. | ||
| 497 | * | ||
| 498 | * @throws Exception | ||
| 499 | */ | ||
| 500 | public function createDatabase(string $database): void | ||
| 501 | { | ||
| 502 | $this->connection->executeStatement( | ||
| 503 | $this->platform->getCreateDatabaseSQL($database), | ||
| 504 | ); | ||
| 505 | } | ||
| 506 | |||
| 507 | /** | ||
| 508 | * Creates a new table. | ||
| 509 | * | ||
| 510 | * @throws Exception | ||
| 511 | */ | ||
| 512 | public function createTable(Table $table): void | ||
| 513 | { | ||
| 514 | $this->executeStatements($this->platform->getCreateTableSQL($table)); | ||
| 515 | } | ||
| 516 | |||
| 517 | /** | ||
| 518 | * Creates a new sequence. | ||
| 519 | * | ||
| 520 | * @throws Exception | ||
| 521 | */ | ||
| 522 | public function createSequence(Sequence $sequence): void | ||
| 523 | { | ||
| 524 | $this->connection->executeStatement( | ||
| 525 | $this->platform->getCreateSequenceSQL($sequence), | ||
| 526 | ); | ||
| 527 | } | ||
| 528 | |||
| 529 | /** | ||
| 530 | * Creates a new index on a table. | ||
| 531 | * | ||
| 532 | * @param string $table The name of the table on which the index is to be created. | ||
| 533 | * | ||
| 534 | * @throws Exception | ||
| 535 | */ | ||
| 536 | public function createIndex(Index $index, string $table): void | ||
| 537 | { | ||
| 538 | $this->connection->executeStatement( | ||
| 539 | $this->platform->getCreateIndexSQL($index, $table), | ||
| 540 | ); | ||
| 541 | } | ||
| 542 | |||
| 543 | /** | ||
| 544 | * Creates a new foreign key. | ||
| 545 | * | ||
| 546 | * @param ForeignKeyConstraint $foreignKey The ForeignKey instance. | ||
| 547 | * @param string $table The name of the table on which the foreign key is to be created. | ||
| 548 | * | ||
| 549 | * @throws Exception | ||
| 550 | */ | ||
| 551 | public function createForeignKey(ForeignKeyConstraint $foreignKey, string $table): void | ||
| 552 | { | ||
| 553 | $this->connection->executeStatement( | ||
| 554 | $this->platform->getCreateForeignKeySQL($foreignKey, $table), | ||
| 555 | ); | ||
| 556 | } | ||
| 557 | |||
| 558 | /** | ||
| 559 | * Creates a unique constraint on a table. | ||
| 560 | * | ||
| 561 | * @throws Exception | ||
| 562 | */ | ||
| 563 | public function createUniqueConstraint(UniqueConstraint $uniqueConstraint, string $tableName): void | ||
| 564 | { | ||
| 565 | $this->connection->executeStatement( | ||
| 566 | $this->platform->getCreateUniqueConstraintSQL($uniqueConstraint, $tableName), | ||
| 567 | ); | ||
| 568 | } | ||
| 569 | |||
| 570 | /** | ||
| 571 | * Creates a new view. | ||
| 572 | * | ||
| 573 | * @throws Exception | ||
| 574 | */ | ||
| 575 | public function createView(View $view): void | ||
| 576 | { | ||
| 577 | $this->connection->executeStatement( | ||
| 578 | $this->platform->getCreateViewSQL( | ||
| 579 | $view->getQuotedName($this->platform), | ||
| 580 | $view->getSql(), | ||
| 581 | ), | ||
| 582 | ); | ||
| 583 | } | ||
| 584 | |||
| 585 | /** @throws Exception */ | ||
| 586 | public function dropSchemaObjects(Schema $schema): void | ||
| 587 | { | ||
| 588 | $this->executeStatements($schema->toDropSql($this->platform)); | ||
| 589 | } | ||
| 590 | |||
| 591 | /** | ||
| 592 | * Alters an existing schema. | ||
| 593 | * | ||
| 594 | * @throws Exception | ||
| 595 | */ | ||
| 596 | public function alterSchema(SchemaDiff $schemaDiff): void | ||
| 597 | { | ||
| 598 | $this->executeStatements($this->platform->getAlterSchemaSQL($schemaDiff)); | ||
| 599 | } | ||
| 600 | |||
| 601 | /** | ||
| 602 | * Migrates an existing schema to a new schema. | ||
| 603 | * | ||
| 604 | * @throws Exception | ||
| 605 | */ | ||
| 606 | public function migrateSchema(Schema $newSchema): void | ||
| 607 | { | ||
| 608 | $schemaDiff = $this->createComparator() | ||
| 609 | ->compareSchemas($this->introspectSchema(), $newSchema); | ||
| 610 | |||
| 611 | $this->alterSchema($schemaDiff); | ||
| 612 | } | ||
| 613 | |||
| 614 | /* alterTable() Methods */ | ||
| 615 | |||
| 616 | /** | ||
| 617 | * Alters an existing tables schema. | ||
| 618 | * | ||
| 619 | * @throws Exception | ||
| 620 | */ | ||
| 621 | public function alterTable(TableDiff $tableDiff): void | ||
| 622 | { | ||
| 623 | $this->executeStatements($this->platform->getAlterTableSQL($tableDiff)); | ||
| 624 | } | ||
| 625 | |||
| 626 | /** | ||
| 627 | * Renames a given table to another name. | ||
| 628 | * | ||
| 629 | * @throws Exception | ||
| 630 | */ | ||
| 631 | public function renameTable(string $name, string $newName): void | ||
| 632 | { | ||
| 633 | $this->connection->executeStatement( | ||
| 634 | $this->platform->getRenameTableSQL($name, $newName), | ||
| 635 | ); | ||
| 636 | } | ||
| 637 | |||
| 638 | /** | ||
| 639 | * Methods for filtering return values of list*() methods to convert | ||
| 640 | * the native DBMS data definition to a portable Doctrine definition | ||
| 641 | */ | ||
| 642 | |||
| 643 | /** @param array<string, string> $database */ | ||
| 644 | protected function _getPortableDatabaseDefinition(array $database): string | ||
| 645 | { | ||
| 646 | throw NotSupported::new(__METHOD__); | ||
| 647 | } | ||
| 648 | |||
| 649 | /** @param array<string, mixed> $sequence */ | ||
| 650 | protected function _getPortableSequenceDefinition(array $sequence): Sequence | ||
| 651 | { | ||
| 652 | throw NotSupported::new(__METHOD__); | ||
| 653 | } | ||
| 654 | |||
| 655 | /** | ||
| 656 | * Independent of the database the keys of the column list result are lowercased. | ||
| 657 | * | ||
| 658 | * The name of the created column instance however is kept in its case. | ||
| 659 | * | ||
| 660 | * @param array<int, array<string, mixed>> $tableColumns | ||
| 661 | * | ||
| 662 | * @return array<string, Column> | ||
| 663 | * | ||
| 664 | * @throws Exception | ||
| 665 | */ | ||
| 666 | protected function _getPortableTableColumnList(string $table, string $database, array $tableColumns): array | ||
| 667 | { | ||
| 668 | $list = []; | ||
| 669 | foreach ($tableColumns as $tableColumn) { | ||
| 670 | $column = $this->_getPortableTableColumnDefinition($tableColumn); | ||
| 671 | |||
| 672 | $name = strtolower($column->getQuotedName($this->platform)); | ||
| 673 | $list[$name] = $column; | ||
| 674 | } | ||
| 675 | |||
| 676 | return $list; | ||
| 677 | } | ||
| 678 | |||
| 679 | /** | ||
| 680 | * Gets Table Column Definition. | ||
| 681 | * | ||
| 682 | * @param array<string, mixed> $tableColumn | ||
| 683 | * | ||
| 684 | * @throws Exception | ||
| 685 | */ | ||
| 686 | abstract protected function _getPortableTableColumnDefinition(array $tableColumn): Column; | ||
| 687 | |||
| 688 | /** | ||
| 689 | * Aggregates and groups the index results according to the required data result. | ||
| 690 | * | ||
| 691 | * @param array<int, array<string, mixed>> $tableIndexes | ||
| 692 | * | ||
| 693 | * @return array<string, Index> | ||
| 694 | * | ||
| 695 | * @throws Exception | ||
| 696 | */ | ||
| 697 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 698 | { | ||
| 699 | $result = []; | ||
| 700 | foreach ($tableIndexes as $tableIndex) { | ||
| 701 | $indexName = $keyName = $tableIndex['key_name']; | ||
| 702 | if ($tableIndex['primary']) { | ||
| 703 | $keyName = 'primary'; | ||
| 704 | } | ||
| 705 | |||
| 706 | $keyName = strtolower($keyName); | ||
| 707 | |||
| 708 | if (! isset($result[$keyName])) { | ||
| 709 | $options = [ | ||
| 710 | 'lengths' => [], | ||
| 711 | ]; | ||
| 712 | |||
| 713 | if (isset($tableIndex['where'])) { | ||
| 714 | $options['where'] = $tableIndex['where']; | ||
| 715 | } | ||
| 716 | |||
| 717 | $result[$keyName] = [ | ||
| 718 | 'name' => $indexName, | ||
| 719 | 'columns' => [], | ||
| 720 | 'unique' => ! $tableIndex['non_unique'], | ||
| 721 | 'primary' => $tableIndex['primary'], | ||
| 722 | 'flags' => $tableIndex['flags'] ?? [], | ||
| 723 | 'options' => $options, | ||
| 724 | ]; | ||
| 725 | } | ||
| 726 | |||
| 727 | $result[$keyName]['columns'][] = $tableIndex['column_name']; | ||
| 728 | $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null; | ||
| 729 | } | ||
| 730 | |||
| 731 | $indexes = []; | ||
| 732 | foreach ($result as $indexKey => $data) { | ||
| 733 | $indexes[$indexKey] = new Index( | ||
| 734 | $data['name'], | ||
| 735 | $data['columns'], | ||
| 736 | $data['unique'], | ||
| 737 | $data['primary'], | ||
| 738 | $data['flags'], | ||
| 739 | $data['options'], | ||
| 740 | ); | ||
| 741 | } | ||
| 742 | |||
| 743 | return $indexes; | ||
| 744 | } | ||
| 745 | |||
| 746 | /** @param array<string, string> $table */ | ||
| 747 | abstract protected function _getPortableTableDefinition(array $table): string; | ||
| 748 | |||
| 749 | /** @param array<string, mixed> $view */ | ||
| 750 | abstract protected function _getPortableViewDefinition(array $view): View; | ||
| 751 | |||
| 752 | /** | ||
| 753 | * @param array<int|string, array<string, mixed>> $tableForeignKeys | ||
| 754 | * | ||
| 755 | * @return array<int, ForeignKeyConstraint> | ||
| 756 | */ | ||
| 757 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 758 | { | ||
| 759 | $list = []; | ||
| 760 | |||
| 761 | foreach ($tableForeignKeys as $value) { | ||
| 762 | $list[] = $this->_getPortableTableForeignKeyDefinition($value); | ||
| 763 | } | ||
| 764 | |||
| 765 | return $list; | ||
| 766 | } | ||
| 767 | |||
| 768 | /** @param array<string, mixed> $tableForeignKey */ | ||
| 769 | abstract protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint; | ||
| 770 | |||
| 771 | /** | ||
| 772 | * @param array<int, string> $sql | ||
| 773 | * | ||
| 774 | * @throws Exception | ||
| 775 | */ | ||
| 776 | private function executeStatements(array $sql): void | ||
| 777 | { | ||
| 778 | foreach ($sql as $query) { | ||
| 779 | $this->connection->executeStatement($query); | ||
| 780 | } | ||
| 781 | } | ||
| 782 | |||
| 783 | /** | ||
| 784 | * Returns a {@see Schema} instance representing the current database schema. | ||
| 785 | * | ||
| 786 | * @throws Exception | ||
| 787 | */ | ||
| 788 | public function introspectSchema(): Schema | ||
| 789 | { | ||
| 790 | $schemaNames = []; | ||
| 791 | |||
| 792 | if ($this->platform->supportsSchemas()) { | ||
| 793 | $schemaNames = $this->listSchemaNames(); | ||
| 794 | } | ||
| 795 | |||
| 796 | $sequences = []; | ||
| 797 | |||
| 798 | if ($this->platform->supportsSequences()) { | ||
| 799 | $sequences = $this->listSequences(); | ||
| 800 | } | ||
| 801 | |||
| 802 | $tables = $this->listTables(); | ||
| 803 | |||
| 804 | return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); | ||
| 805 | } | ||
| 806 | |||
| 807 | /** | ||
| 808 | * Creates the configuration for this schema. | ||
| 809 | * | ||
| 810 | * @throws Exception | ||
| 811 | */ | ||
| 812 | public function createSchemaConfig(): SchemaConfig | ||
| 813 | { | ||
| 814 | $schemaConfig = new SchemaConfig(); | ||
| 815 | $schemaConfig->setMaxIdentifierLength($this->platform->getMaxIdentifierLength()); | ||
| 816 | |||
| 817 | $params = $this->connection->getParams(); | ||
| 818 | if (! isset($params['defaultTableOptions'])) { | ||
| 819 | $params['defaultTableOptions'] = []; | ||
| 820 | } | ||
| 821 | |||
| 822 | if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) { | ||
| 823 | $params['defaultTableOptions']['charset'] = $params['charset']; | ||
| 824 | } | ||
| 825 | |||
| 826 | $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); | ||
| 827 | |||
| 828 | return $schemaConfig; | ||
| 829 | } | ||
| 830 | |||
| 831 | /** @throws Exception */ | ||
| 832 | private function getDatabase(string $methodName): string | ||
| 833 | { | ||
| 834 | $database = $this->connection->getDatabase(); | ||
| 835 | |||
| 836 | if ($database === null) { | ||
| 837 | throw DatabaseRequired::new($methodName); | ||
| 838 | } | ||
| 839 | |||
| 840 | return $database; | ||
| 841 | } | ||
| 842 | |||
| 843 | public function createComparator(): Comparator | ||
| 844 | { | ||
| 845 | return new Comparator($this->platform); | ||
| 846 | } | ||
| 847 | |||
| 848 | /** | ||
| 849 | * @return array<string,list<array<string,mixed>>> | ||
| 850 | * | ||
| 851 | * @throws Exception | ||
| 852 | */ | ||
| 853 | private function fetchAllAssociativeGrouped(Result $result): array | ||
| 854 | { | ||
| 855 | $data = []; | ||
| 856 | |||
| 857 | foreach ($result->fetchAllAssociative() as $row) { | ||
| 858 | $tableName = $this->_getPortableTableDefinition($row); | ||
| 859 | $data[$tableName][] = $row; | ||
| 860 | } | ||
| 861 | |||
| 862 | return $data; | ||
| 863 | } | ||
| 864 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Column.php b/vendor/doctrine/dbal/src/Schema/Column.php new file mode 100644 index 0000000..8963cd7 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Column.php | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\Exception\UnknownColumnOption; | ||
| 8 | use Doctrine\DBAL\Types\Type; | ||
| 9 | |||
| 10 | use function array_merge; | ||
| 11 | use function method_exists; | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Object representation of a database column. | ||
| 15 | */ | ||
| 16 | class Column extends AbstractAsset | ||
| 17 | { | ||
| 18 | protected Type $_type; | ||
| 19 | |||
| 20 | protected ?int $_length = null; | ||
| 21 | |||
| 22 | protected ?int $_precision = null; | ||
| 23 | |||
| 24 | protected int $_scale = 0; | ||
| 25 | |||
| 26 | protected bool $_unsigned = false; | ||
| 27 | |||
| 28 | protected bool $_fixed = false; | ||
| 29 | |||
| 30 | protected bool $_notnull = true; | ||
| 31 | |||
| 32 | protected mixed $_default = null; | ||
| 33 | |||
| 34 | protected bool $_autoincrement = false; | ||
| 35 | |||
| 36 | /** @var array<string, mixed> */ | ||
| 37 | protected array $_platformOptions = []; | ||
| 38 | |||
| 39 | protected ?string $_columnDefinition = null; | ||
| 40 | |||
| 41 | protected string $_comment = ''; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Creates a new Column. | ||
| 45 | * | ||
| 46 | * @param array<string, mixed> $options | ||
| 47 | */ | ||
| 48 | public function __construct(string $name, Type $type, array $options = []) | ||
| 49 | { | ||
| 50 | $this->_setName($name); | ||
| 51 | $this->setType($type); | ||
| 52 | $this->setOptions($options); | ||
| 53 | } | ||
| 54 | |||
| 55 | /** @param array<string, mixed> $options */ | ||
| 56 | public function setOptions(array $options): self | ||
| 57 | { | ||
| 58 | foreach ($options as $name => $value) { | ||
| 59 | $method = 'set' . $name; | ||
| 60 | |||
| 61 | if (! method_exists($this, $method)) { | ||
| 62 | throw UnknownColumnOption::new($name); | ||
| 63 | } | ||
| 64 | |||
| 65 | $this->$method($value); | ||
| 66 | } | ||
| 67 | |||
| 68 | return $this; | ||
| 69 | } | ||
| 70 | |||
| 71 | public function setType(Type $type): self | ||
| 72 | { | ||
| 73 | $this->_type = $type; | ||
| 74 | |||
| 75 | return $this; | ||
| 76 | } | ||
| 77 | |||
| 78 | public function setLength(?int $length): self | ||
| 79 | { | ||
| 80 | $this->_length = $length; | ||
| 81 | |||
| 82 | return $this; | ||
| 83 | } | ||
| 84 | |||
| 85 | public function setPrecision(?int $precision): self | ||
| 86 | { | ||
| 87 | $this->_precision = $precision; | ||
| 88 | |||
| 89 | return $this; | ||
| 90 | } | ||
| 91 | |||
| 92 | public function setScale(int $scale): self | ||
| 93 | { | ||
| 94 | $this->_scale = $scale; | ||
| 95 | |||
| 96 | return $this; | ||
| 97 | } | ||
| 98 | |||
| 99 | public function setUnsigned(bool $unsigned): self | ||
| 100 | { | ||
| 101 | $this->_unsigned = $unsigned; | ||
| 102 | |||
| 103 | return $this; | ||
| 104 | } | ||
| 105 | |||
| 106 | public function setFixed(bool $fixed): self | ||
| 107 | { | ||
| 108 | $this->_fixed = $fixed; | ||
| 109 | |||
| 110 | return $this; | ||
| 111 | } | ||
| 112 | |||
| 113 | public function setNotnull(bool $notnull): self | ||
| 114 | { | ||
| 115 | $this->_notnull = $notnull; | ||
| 116 | |||
| 117 | return $this; | ||
| 118 | } | ||
| 119 | |||
| 120 | public function setDefault(mixed $default): self | ||
| 121 | { | ||
| 122 | $this->_default = $default; | ||
| 123 | |||
| 124 | return $this; | ||
| 125 | } | ||
| 126 | |||
| 127 | /** @param array<string, mixed> $platformOptions */ | ||
| 128 | public function setPlatformOptions(array $platformOptions): self | ||
| 129 | { | ||
| 130 | $this->_platformOptions = $platformOptions; | ||
| 131 | |||
| 132 | return $this; | ||
| 133 | } | ||
| 134 | |||
| 135 | public function setPlatformOption(string $name, mixed $value): self | ||
| 136 | { | ||
| 137 | $this->_platformOptions[$name] = $value; | ||
| 138 | |||
| 139 | return $this; | ||
| 140 | } | ||
| 141 | |||
| 142 | public function setColumnDefinition(?string $value): self | ||
| 143 | { | ||
| 144 | $this->_columnDefinition = $value; | ||
| 145 | |||
| 146 | return $this; | ||
| 147 | } | ||
| 148 | |||
| 149 | public function getType(): Type | ||
| 150 | { | ||
| 151 | return $this->_type; | ||
| 152 | } | ||
| 153 | |||
| 154 | public function getLength(): ?int | ||
| 155 | { | ||
| 156 | return $this->_length; | ||
| 157 | } | ||
| 158 | |||
| 159 | public function getPrecision(): ?int | ||
| 160 | { | ||
| 161 | return $this->_precision; | ||
| 162 | } | ||
| 163 | |||
| 164 | public function getScale(): int | ||
| 165 | { | ||
| 166 | return $this->_scale; | ||
| 167 | } | ||
| 168 | |||
| 169 | public function getUnsigned(): bool | ||
| 170 | { | ||
| 171 | return $this->_unsigned; | ||
| 172 | } | ||
| 173 | |||
| 174 | public function getFixed(): bool | ||
| 175 | { | ||
| 176 | return $this->_fixed; | ||
| 177 | } | ||
| 178 | |||
| 179 | public function getNotnull(): bool | ||
| 180 | { | ||
| 181 | return $this->_notnull; | ||
| 182 | } | ||
| 183 | |||
| 184 | public function getDefault(): mixed | ||
| 185 | { | ||
| 186 | return $this->_default; | ||
| 187 | } | ||
| 188 | |||
| 189 | /** @return array<string, mixed> */ | ||
| 190 | public function getPlatformOptions(): array | ||
| 191 | { | ||
| 192 | return $this->_platformOptions; | ||
| 193 | } | ||
| 194 | |||
| 195 | public function hasPlatformOption(string $name): bool | ||
| 196 | { | ||
| 197 | return isset($this->_platformOptions[$name]); | ||
| 198 | } | ||
| 199 | |||
| 200 | public function getPlatformOption(string $name): mixed | ||
| 201 | { | ||
| 202 | return $this->_platformOptions[$name]; | ||
| 203 | } | ||
| 204 | |||
| 205 | public function getColumnDefinition(): ?string | ||
| 206 | { | ||
| 207 | return $this->_columnDefinition; | ||
| 208 | } | ||
| 209 | |||
| 210 | public function getAutoincrement(): bool | ||
| 211 | { | ||
| 212 | return $this->_autoincrement; | ||
| 213 | } | ||
| 214 | |||
| 215 | public function setAutoincrement(bool $flag): self | ||
| 216 | { | ||
| 217 | $this->_autoincrement = $flag; | ||
| 218 | |||
| 219 | return $this; | ||
| 220 | } | ||
| 221 | |||
| 222 | public function setComment(string $comment): self | ||
| 223 | { | ||
| 224 | $this->_comment = $comment; | ||
| 225 | |||
| 226 | return $this; | ||
| 227 | } | ||
| 228 | |||
| 229 | public function getComment(): string | ||
| 230 | { | ||
| 231 | return $this->_comment; | ||
| 232 | } | ||
| 233 | |||
| 234 | /** @return array<string, mixed> */ | ||
| 235 | public function toArray(): array | ||
| 236 | { | ||
| 237 | return array_merge([ | ||
| 238 | 'name' => $this->_name, | ||
| 239 | 'type' => $this->_type, | ||
| 240 | 'default' => $this->_default, | ||
| 241 | 'notnull' => $this->_notnull, | ||
| 242 | 'length' => $this->_length, | ||
| 243 | 'precision' => $this->_precision, | ||
| 244 | 'scale' => $this->_scale, | ||
| 245 | 'fixed' => $this->_fixed, | ||
| 246 | 'unsigned' => $this->_unsigned, | ||
| 247 | 'autoincrement' => $this->_autoincrement, | ||
| 248 | 'columnDefinition' => $this->_columnDefinition, | ||
| 249 | 'comment' => $this->_comment, | ||
| 250 | ], $this->_platformOptions); | ||
| 251 | } | ||
| 252 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/ColumnDiff.php b/vendor/doctrine/dbal/src/Schema/ColumnDiff.php new file mode 100644 index 0000000..3e4950a --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/ColumnDiff.php | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Represents the change of a column. | ||
| 9 | */ | ||
| 10 | class ColumnDiff | ||
| 11 | { | ||
| 12 | /** @internal The diff can be only instantiated by a {@see Comparator}. */ | ||
| 13 | public function __construct(private readonly Column $oldColumn, private readonly Column $newColumn) | ||
| 14 | { | ||
| 15 | } | ||
| 16 | |||
| 17 | public function getOldColumn(): Column | ||
| 18 | { | ||
| 19 | return $this->oldColumn; | ||
| 20 | } | ||
| 21 | |||
| 22 | public function getNewColumn(): Column | ||
| 23 | { | ||
| 24 | return $this->newColumn; | ||
| 25 | } | ||
| 26 | |||
| 27 | public function hasTypeChanged(): bool | ||
| 28 | { | ||
| 29 | return $this->newColumn->getType()::class !== $this->oldColumn->getType()::class; | ||
| 30 | } | ||
| 31 | |||
| 32 | public function hasLengthChanged(): bool | ||
| 33 | { | ||
| 34 | return $this->hasPropertyChanged(static function (Column $column): ?int { | ||
| 35 | return $column->getLength(); | ||
| 36 | }); | ||
| 37 | } | ||
| 38 | |||
| 39 | public function hasPrecisionChanged(): bool | ||
| 40 | { | ||
| 41 | return $this->hasPropertyChanged(static function (Column $column): ?int { | ||
| 42 | return $column->getPrecision(); | ||
| 43 | }); | ||
| 44 | } | ||
| 45 | |||
| 46 | public function hasScaleChanged(): bool | ||
| 47 | { | ||
| 48 | return $this->hasPropertyChanged(static function (Column $column): int { | ||
| 49 | return $column->getScale(); | ||
| 50 | }); | ||
| 51 | } | ||
| 52 | |||
| 53 | public function hasUnsignedChanged(): bool | ||
| 54 | { | ||
| 55 | return $this->hasPropertyChanged(static function (Column $column): bool { | ||
| 56 | return $column->getUnsigned(); | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | |||
| 60 | public function hasFixedChanged(): bool | ||
| 61 | { | ||
| 62 | return $this->hasPropertyChanged(static function (Column $column): bool { | ||
| 63 | return $column->getFixed(); | ||
| 64 | }); | ||
| 65 | } | ||
| 66 | |||
| 67 | public function hasNotNullChanged(): bool | ||
| 68 | { | ||
| 69 | return $this->hasPropertyChanged(static function (Column $column): bool { | ||
| 70 | return $column->getNotnull(); | ||
| 71 | }); | ||
| 72 | } | ||
| 73 | |||
| 74 | public function hasDefaultChanged(): bool | ||
| 75 | { | ||
| 76 | $oldDefault = $this->oldColumn->getDefault(); | ||
| 77 | $newDefault = $this->newColumn->getDefault(); | ||
| 78 | |||
| 79 | // Null values need to be checked additionally as they tell whether to create or drop a default value. | ||
| 80 | // null != 0, null != false, null != '' etc. This affects platform's table alteration SQL generation. | ||
| 81 | if (($newDefault === null) xor ($oldDefault === null)) { | ||
| 82 | return true; | ||
| 83 | } | ||
| 84 | |||
| 85 | return $newDefault != $oldDefault; | ||
| 86 | } | ||
| 87 | |||
| 88 | public function hasAutoIncrementChanged(): bool | ||
| 89 | { | ||
| 90 | return $this->hasPropertyChanged(static function (Column $column): bool { | ||
| 91 | return $column->getAutoincrement(); | ||
| 92 | }); | ||
| 93 | } | ||
| 94 | |||
| 95 | public function hasCommentChanged(): bool | ||
| 96 | { | ||
| 97 | return $this->hasPropertyChanged(static function (Column $column): string { | ||
| 98 | return $column->getComment(); | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | |||
| 102 | private function hasPropertyChanged(callable $property): bool | ||
| 103 | { | ||
| 104 | return $property($this->newColumn) !== $property($this->oldColumn); | ||
| 105 | } | ||
| 106 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Comparator.php b/vendor/doctrine/dbal/src/Schema/Comparator.php new file mode 100644 index 0000000..4c60c07 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Comparator.php | |||
| @@ -0,0 +1,417 @@ | |||
| 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_map; | ||
| 10 | use function assert; | ||
| 11 | use function count; | ||
| 12 | use function strtolower; | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Compares two Schemas and return an instance of SchemaDiff. | ||
| 16 | */ | ||
| 17 | class Comparator | ||
| 18 | { | ||
| 19 | /** @internal The comparator can be only instantiated by a schema manager. */ | ||
| 20 | public function __construct(private readonly AbstractPlatform $platform) | ||
| 21 | { | ||
| 22 | } | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Returns the differences between the schemas. | ||
| 26 | */ | ||
| 27 | public function compareSchemas(Schema $oldSchema, Schema $newSchema): SchemaDiff | ||
| 28 | { | ||
| 29 | $createdSchemas = []; | ||
| 30 | $droppedSchemas = []; | ||
| 31 | $createdTables = []; | ||
| 32 | $alteredTables = []; | ||
| 33 | $droppedTables = []; | ||
| 34 | $createdSequences = []; | ||
| 35 | $alteredSequences = []; | ||
| 36 | $droppedSequences = []; | ||
| 37 | |||
| 38 | foreach ($newSchema->getNamespaces() as $newNamespace) { | ||
| 39 | if ($oldSchema->hasNamespace($newNamespace)) { | ||
| 40 | continue; | ||
| 41 | } | ||
| 42 | |||
| 43 | $createdSchemas[] = $newNamespace; | ||
| 44 | } | ||
| 45 | |||
| 46 | foreach ($oldSchema->getNamespaces() as $oldNamespace) { | ||
| 47 | if ($newSchema->hasNamespace($oldNamespace)) { | ||
| 48 | continue; | ||
| 49 | } | ||
| 50 | |||
| 51 | $droppedSchemas[] = $oldNamespace; | ||
| 52 | } | ||
| 53 | |||
| 54 | foreach ($newSchema->getTables() as $newTable) { | ||
| 55 | $newTableName = $newTable->getShortestName($newSchema->getName()); | ||
| 56 | if (! $oldSchema->hasTable($newTableName)) { | ||
| 57 | $createdTables[] = $newSchema->getTable($newTableName); | ||
| 58 | } else { | ||
| 59 | $tableDiff = $this->compareTables( | ||
| 60 | $oldSchema->getTable($newTableName), | ||
| 61 | $newSchema->getTable($newTableName), | ||
| 62 | ); | ||
| 63 | |||
| 64 | if (! $tableDiff->isEmpty()) { | ||
| 65 | $alteredTables[] = $tableDiff; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | // Check if there are tables removed | ||
| 71 | foreach ($oldSchema->getTables() as $oldTable) { | ||
| 72 | $oldTableName = $oldTable->getShortestName($oldSchema->getName()); | ||
| 73 | |||
| 74 | $oldTable = $oldSchema->getTable($oldTableName); | ||
| 75 | if ($newSchema->hasTable($oldTableName)) { | ||
| 76 | continue; | ||
| 77 | } | ||
| 78 | |||
| 79 | $droppedTables[] = $oldTable; | ||
| 80 | } | ||
| 81 | |||
| 82 | foreach ($newSchema->getSequences() as $newSequence) { | ||
| 83 | $newSequenceName = $newSequence->getShortestName($newSchema->getName()); | ||
| 84 | if (! $oldSchema->hasSequence($newSequenceName)) { | ||
| 85 | if (! $this->isAutoIncrementSequenceInSchema($oldSchema, $newSequence)) { | ||
| 86 | $createdSequences[] = $newSequence; | ||
| 87 | } | ||
| 88 | } else { | ||
| 89 | if ($this->diffSequence($newSequence, $oldSchema->getSequence($newSequenceName))) { | ||
| 90 | $alteredSequences[] = $newSchema->getSequence($newSequenceName); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | foreach ($oldSchema->getSequences() as $oldSequence) { | ||
| 96 | if ($this->isAutoIncrementSequenceInSchema($newSchema, $oldSequence)) { | ||
| 97 | continue; | ||
| 98 | } | ||
| 99 | |||
| 100 | $oldSequenceName = $oldSequence->getShortestName($oldSchema->getName()); | ||
| 101 | |||
| 102 | if ($newSchema->hasSequence($oldSequenceName)) { | ||
| 103 | continue; | ||
| 104 | } | ||
| 105 | |||
| 106 | $droppedSequences[] = $oldSequence; | ||
| 107 | } | ||
| 108 | |||
| 109 | return new SchemaDiff( | ||
| 110 | $createdSchemas, | ||
| 111 | $droppedSchemas, | ||
| 112 | $createdTables, | ||
| 113 | $alteredTables, | ||
| 114 | $droppedTables, | ||
| 115 | $createdSequences, | ||
| 116 | $alteredSequences, | ||
| 117 | $droppedSequences, | ||
| 118 | ); | ||
| 119 | } | ||
| 120 | |||
| 121 | private function isAutoIncrementSequenceInSchema(Schema $schema, Sequence $sequence): bool | ||
| 122 | { | ||
| 123 | foreach ($schema->getTables() as $table) { | ||
| 124 | if ($sequence->isAutoIncrementsFor($table)) { | ||
| 125 | return true; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | return false; | ||
| 130 | } | ||
| 131 | |||
| 132 | public function diffSequence(Sequence $sequence1, Sequence $sequence2): bool | ||
| 133 | { | ||
| 134 | if ($sequence1->getAllocationSize() !== $sequence2->getAllocationSize()) { | ||
| 135 | return true; | ||
| 136 | } | ||
| 137 | |||
| 138 | return $sequence1->getInitialValue() !== $sequence2->getInitialValue(); | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * Compares the tables and returns the difference between them. | ||
| 143 | */ | ||
| 144 | public function compareTables(Table $oldTable, Table $newTable): TableDiff | ||
| 145 | { | ||
| 146 | $addedColumns = []; | ||
| 147 | $modifiedColumns = []; | ||
| 148 | $droppedColumns = []; | ||
| 149 | $addedIndexes = []; | ||
| 150 | $modifiedIndexes = []; | ||
| 151 | $droppedIndexes = []; | ||
| 152 | $addedForeignKeys = []; | ||
| 153 | $modifiedForeignKeys = []; | ||
| 154 | $droppedForeignKeys = []; | ||
| 155 | |||
| 156 | $oldColumns = $oldTable->getColumns(); | ||
| 157 | $newColumns = $newTable->getColumns(); | ||
| 158 | |||
| 159 | // See if all the columns in the old table exist in the new table | ||
| 160 | foreach ($newColumns as $newColumn) { | ||
| 161 | $newColumnName = strtolower($newColumn->getName()); | ||
| 162 | |||
| 163 | if ($oldTable->hasColumn($newColumnName)) { | ||
| 164 | continue; | ||
| 165 | } | ||
| 166 | |||
| 167 | $addedColumns[$newColumnName] = $newColumn; | ||
| 168 | } | ||
| 169 | |||
| 170 | // See if there are any removed columns in the new table | ||
| 171 | foreach ($oldColumns as $oldColumn) { | ||
| 172 | $oldColumnName = strtolower($oldColumn->getName()); | ||
| 173 | |||
| 174 | // See if column is removed in the new table. | ||
| 175 | if (! $newTable->hasColumn($oldColumnName)) { | ||
| 176 | $droppedColumns[$oldColumnName] = $oldColumn; | ||
| 177 | |||
| 178 | continue; | ||
| 179 | } | ||
| 180 | |||
| 181 | $newColumn = $newTable->getColumn($oldColumnName); | ||
| 182 | |||
| 183 | if ($this->columnsEqual($oldColumn, $newColumn)) { | ||
| 184 | continue; | ||
| 185 | } | ||
| 186 | |||
| 187 | $modifiedColumns[] = new ColumnDiff($oldColumn, $newColumn); | ||
| 188 | } | ||
| 189 | |||
| 190 | $renamedColumns = $this->detectRenamedColumns($addedColumns, $droppedColumns); | ||
| 191 | |||
| 192 | $oldIndexes = $oldTable->getIndexes(); | ||
| 193 | $newIndexes = $newTable->getIndexes(); | ||
| 194 | |||
| 195 | // See if all the indexes from the old table exist in the new one | ||
| 196 | foreach ($newIndexes as $newIndexName => $newIndex) { | ||
| 197 | if (($newIndex->isPrimary() && $oldTable->getPrimaryKey() !== null) || $oldTable->hasIndex($newIndexName)) { | ||
| 198 | continue; | ||
| 199 | } | ||
| 200 | |||
| 201 | $addedIndexes[$newIndexName] = $newIndex; | ||
| 202 | } | ||
| 203 | |||
| 204 | // See if there are any removed indexes in the new table | ||
| 205 | foreach ($oldIndexes as $oldIndexName => $oldIndex) { | ||
| 206 | // See if the index is removed in the new table. | ||
| 207 | if ( | ||
| 208 | ($oldIndex->isPrimary() && $newTable->getPrimaryKey() === null) || | ||
| 209 | ! $oldIndex->isPrimary() && ! $newTable->hasIndex($oldIndexName) | ||
| 210 | ) { | ||
| 211 | $droppedIndexes[$oldIndexName] = $oldIndex; | ||
| 212 | |||
| 213 | continue; | ||
| 214 | } | ||
| 215 | |||
| 216 | // See if index has changed in the new table. | ||
| 217 | $newIndex = $oldIndex->isPrimary() ? $newTable->getPrimaryKey() : $newTable->getIndex($oldIndexName); | ||
| 218 | assert($newIndex instanceof Index); | ||
| 219 | |||
| 220 | if (! $this->diffIndex($oldIndex, $newIndex)) { | ||
| 221 | continue; | ||
| 222 | } | ||
| 223 | |||
| 224 | $modifiedIndexes[] = $newIndex; | ||
| 225 | } | ||
| 226 | |||
| 227 | $renamedIndexes = $this->detectRenamedIndexes($addedIndexes, $droppedIndexes); | ||
| 228 | |||
| 229 | $oldForeignKeys = $oldTable->getForeignKeys(); | ||
| 230 | $newForeignKeys = $newTable->getForeignKeys(); | ||
| 231 | |||
| 232 | foreach ($oldForeignKeys as $oldKey => $oldForeignKey) { | ||
| 233 | foreach ($newForeignKeys as $newKey => $newForeignKey) { | ||
| 234 | if ($this->diffForeignKey($oldForeignKey, $newForeignKey) === false) { | ||
| 235 | unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); | ||
| 236 | } else { | ||
| 237 | if (strtolower($oldForeignKey->getName()) === strtolower($newForeignKey->getName())) { | ||
| 238 | $modifiedForeignKeys[] = $newForeignKey; | ||
| 239 | |||
| 240 | unset($oldForeignKeys[$oldKey], $newForeignKeys[$newKey]); | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | foreach ($oldForeignKeys as $oldForeignKey) { | ||
| 247 | $droppedForeignKeys[] = $oldForeignKey; | ||
| 248 | } | ||
| 249 | |||
| 250 | foreach ($newForeignKeys as $newForeignKey) { | ||
| 251 | $addedForeignKeys[] = $newForeignKey; | ||
| 252 | } | ||
| 253 | |||
| 254 | return new TableDiff( | ||
| 255 | $oldTable, | ||
| 256 | $addedColumns, | ||
| 257 | $modifiedColumns, | ||
| 258 | $droppedColumns, | ||
| 259 | $renamedColumns, | ||
| 260 | $addedIndexes, | ||
| 261 | $modifiedIndexes, | ||
| 262 | $droppedIndexes, | ||
| 263 | $renamedIndexes, | ||
| 264 | $addedForeignKeys, | ||
| 265 | $modifiedForeignKeys, | ||
| 266 | $droppedForeignKeys, | ||
| 267 | ); | ||
| 268 | } | ||
| 269 | |||
| 270 | /** | ||
| 271 | * Try to find columns that only changed their name, rename operations maybe cheaper than add/drop | ||
| 272 | * however ambiguities between different possibilities should not lead to renaming at all. | ||
| 273 | * | ||
| 274 | * @param array<string,Column> $addedColumns | ||
| 275 | * @param array<string,Column> $removedColumns | ||
| 276 | * | ||
| 277 | * @return array<string,Column> | ||
| 278 | */ | ||
| 279 | private function detectRenamedColumns(array &$addedColumns, array &$removedColumns): array | ||
| 280 | { | ||
| 281 | $candidatesByName = []; | ||
| 282 | |||
| 283 | foreach ($addedColumns as $addedColumnName => $addedColumn) { | ||
| 284 | foreach ($removedColumns as $removedColumn) { | ||
| 285 | if (! $this->columnsEqual($addedColumn, $removedColumn)) { | ||
| 286 | continue; | ||
| 287 | } | ||
| 288 | |||
| 289 | $candidatesByName[$addedColumn->getName()][] = [$removedColumn, $addedColumn, $addedColumnName]; | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | $renamedColumns = []; | ||
| 294 | |||
| 295 | foreach ($candidatesByName as $candidates) { | ||
| 296 | if (count($candidates) !== 1) { | ||
| 297 | continue; | ||
| 298 | } | ||
| 299 | |||
| 300 | [$removedColumn, $addedColumn] = $candidates[0]; | ||
| 301 | $removedColumnName = $removedColumn->getName(); | ||
| 302 | $addedColumnName = strtolower($addedColumn->getName()); | ||
| 303 | |||
| 304 | if (isset($renamedColumns[$removedColumnName])) { | ||
| 305 | continue; | ||
| 306 | } | ||
| 307 | |||
| 308 | $renamedColumns[$removedColumnName] = $addedColumn; | ||
| 309 | unset( | ||
| 310 | $addedColumns[$addedColumnName], | ||
| 311 | $removedColumns[strtolower($removedColumnName)], | ||
| 312 | ); | ||
| 313 | } | ||
| 314 | |||
| 315 | return $renamedColumns; | ||
| 316 | } | ||
| 317 | |||
| 318 | /** | ||
| 319 | * Try to find indexes that only changed their name, rename operations maybe cheaper than add/drop | ||
| 320 | * however ambiguities between different possibilities should not lead to renaming at all. | ||
| 321 | * | ||
| 322 | * @param array<string,Index> $addedIndexes | ||
| 323 | * @param array<string,Index> $removedIndexes | ||
| 324 | * | ||
| 325 | * @return array<string,Index> | ||
| 326 | */ | ||
| 327 | private function detectRenamedIndexes(array &$addedIndexes, array &$removedIndexes): array | ||
| 328 | { | ||
| 329 | $candidatesByName = []; | ||
| 330 | |||
| 331 | // Gather possible rename candidates by comparing each added and removed index based on semantics. | ||
| 332 | foreach ($addedIndexes as $addedIndexName => $addedIndex) { | ||
| 333 | foreach ($removedIndexes as $removedIndex) { | ||
| 334 | if ($this->diffIndex($addedIndex, $removedIndex)) { | ||
| 335 | continue; | ||
| 336 | } | ||
| 337 | |||
| 338 | $candidatesByName[$addedIndex->getName()][] = [$removedIndex, $addedIndex, $addedIndexName]; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | $renamedIndexes = []; | ||
| 343 | |||
| 344 | foreach ($candidatesByName as $candidates) { | ||
| 345 | // If the current rename candidate contains exactly one semantically equal index, | ||
| 346 | // we can safely rename it. | ||
| 347 | // Otherwise, it is unclear if a rename action is really intended, | ||
| 348 | // therefore we let those ambiguous indexes be added/dropped. | ||
| 349 | if (count($candidates) !== 1) { | ||
| 350 | continue; | ||
| 351 | } | ||
| 352 | |||
| 353 | [$removedIndex, $addedIndex] = $candidates[0]; | ||
| 354 | |||
| 355 | $removedIndexName = strtolower($removedIndex->getName()); | ||
| 356 | $addedIndexName = strtolower($addedIndex->getName()); | ||
| 357 | |||
| 358 | if (isset($renamedIndexes[$removedIndexName])) { | ||
| 359 | continue; | ||
| 360 | } | ||
| 361 | |||
| 362 | $renamedIndexes[$removedIndexName] = $addedIndex; | ||
| 363 | unset( | ||
| 364 | $addedIndexes[$addedIndexName], | ||
| 365 | $removedIndexes[$removedIndexName], | ||
| 366 | ); | ||
| 367 | } | ||
| 368 | |||
| 369 | return $renamedIndexes; | ||
| 370 | } | ||
| 371 | |||
| 372 | protected function diffForeignKey(ForeignKeyConstraint $key1, ForeignKeyConstraint $key2): bool | ||
| 373 | { | ||
| 374 | if ( | ||
| 375 | array_map('strtolower', $key1->getUnquotedLocalColumns()) | ||
| 376 | !== array_map('strtolower', $key2->getUnquotedLocalColumns()) | ||
| 377 | ) { | ||
| 378 | return true; | ||
| 379 | } | ||
| 380 | |||
| 381 | if ( | ||
| 382 | array_map('strtolower', $key1->getUnquotedForeignColumns()) | ||
| 383 | !== array_map('strtolower', $key2->getUnquotedForeignColumns()) | ||
| 384 | ) { | ||
| 385 | return true; | ||
| 386 | } | ||
| 387 | |||
| 388 | if ($key1->getUnqualifiedForeignTableName() !== $key2->getUnqualifiedForeignTableName()) { | ||
| 389 | return true; | ||
| 390 | } | ||
| 391 | |||
| 392 | if ($key1->onUpdate() !== $key2->onUpdate()) { | ||
| 393 | return true; | ||
| 394 | } | ||
| 395 | |||
| 396 | return $key1->onDelete() !== $key2->onDelete(); | ||
| 397 | } | ||
| 398 | |||
| 399 | /** | ||
| 400 | * Compares the definitions of the given columns | ||
| 401 | */ | ||
| 402 | protected function columnsEqual(Column $column1, Column $column2): bool | ||
| 403 | { | ||
| 404 | return $this->platform->columnsEqual($column1, $column2); | ||
| 405 | } | ||
| 406 | |||
| 407 | /** | ||
| 408 | * Finds the difference between the indexes $index1 and $index2. | ||
| 409 | * | ||
| 410 | * Compares $index1 with $index2 and returns true if there are any | ||
| 411 | * differences or false in case there are no differences. | ||
| 412 | */ | ||
| 413 | protected function diffIndex(Index $index1, Index $index2): bool | ||
| 414 | { | ||
| 415 | return ! ($index1->isFulfilledBy($index2) && $index2->isFulfilledBy($index1)); | ||
| 416 | } | ||
| 417 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/DB2SchemaManager.php b/vendor/doctrine/dbal/src/Schema/DB2SchemaManager.php new file mode 100644 index 0000000..f2ed089 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/DB2SchemaManager.php | |||
| @@ -0,0 +1,371 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Platforms\DB2Platform; | ||
| 9 | use Doctrine\DBAL\Result; | ||
| 10 | use Doctrine\DBAL\Types\Type; | ||
| 11 | use Doctrine\DBAL\Types\Types; | ||
| 12 | |||
| 13 | use function array_change_key_case; | ||
| 14 | use function implode; | ||
| 15 | use function preg_match; | ||
| 16 | use function str_replace; | ||
| 17 | use function strpos; | ||
| 18 | use function strtolower; | ||
| 19 | use function strtoupper; | ||
| 20 | use function substr; | ||
| 21 | |||
| 22 | use const CASE_LOWER; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * IBM Db2 Schema Manager. | ||
| 26 | * | ||
| 27 | * @extends AbstractSchemaManager<DB2Platform> | ||
| 28 | */ | ||
| 29 | class DB2SchemaManager extends AbstractSchemaManager | ||
| 30 | { | ||
| 31 | /** | ||
| 32 | * {@inheritDoc} | ||
| 33 | * | ||
| 34 | * @throws Exception | ||
| 35 | */ | ||
| 36 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 37 | { | ||
| 38 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); | ||
| 39 | |||
| 40 | $length = $precision = $default = null; | ||
| 41 | $scale = 0; | ||
| 42 | $fixed = false; | ||
| 43 | |||
| 44 | if ($tableColumn['default'] !== null && $tableColumn['default'] !== 'NULL') { | ||
| 45 | $default = $tableColumn['default']; | ||
| 46 | |||
| 47 | if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { | ||
| 48 | $default = str_replace("''", "'", $matches[1]); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | $type = $this->platform->getDoctrineTypeMapping($tableColumn['typename']); | ||
| 53 | |||
| 54 | switch (strtolower($tableColumn['typename'])) { | ||
| 55 | case 'varchar': | ||
| 56 | if ($tableColumn['codepage'] === 0) { | ||
| 57 | $type = Types::BINARY; | ||
| 58 | } | ||
| 59 | |||
| 60 | $length = $tableColumn['length']; | ||
| 61 | break; | ||
| 62 | |||
| 63 | case 'character': | ||
| 64 | if ($tableColumn['codepage'] === 0) { | ||
| 65 | $type = Types::BINARY; | ||
| 66 | } | ||
| 67 | |||
| 68 | $length = $tableColumn['length']; | ||
| 69 | $fixed = true; | ||
| 70 | break; | ||
| 71 | |||
| 72 | case 'clob': | ||
| 73 | $length = $tableColumn['length']; | ||
| 74 | break; | ||
| 75 | |||
| 76 | case 'decimal': | ||
| 77 | case 'double': | ||
| 78 | case 'real': | ||
| 79 | $scale = $tableColumn['scale']; | ||
| 80 | $precision = $tableColumn['length']; | ||
| 81 | break; | ||
| 82 | } | ||
| 83 | |||
| 84 | $options = [ | ||
| 85 | 'length' => $length, | ||
| 86 | 'unsigned' => false, | ||
| 87 | 'fixed' => $fixed, | ||
| 88 | 'default' => $default, | ||
| 89 | 'autoincrement' => (bool) $tableColumn['autoincrement'], | ||
| 90 | 'notnull' => $tableColumn['nulls'] === 'N', | ||
| 91 | 'platformOptions' => [], | ||
| 92 | ]; | ||
| 93 | |||
| 94 | if (isset($tableColumn['comment'])) { | ||
| 95 | $options['comment'] = $tableColumn['comment']; | ||
| 96 | } | ||
| 97 | |||
| 98 | if ($scale !== null && $precision !== null) { | ||
| 99 | $options['scale'] = $scale; | ||
| 100 | $options['precision'] = $precision; | ||
| 101 | } | ||
| 102 | |||
| 103 | return new Column($tableColumn['colname'], Type::getType($type), $options); | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * {@inheritDoc} | ||
| 108 | */ | ||
| 109 | protected function _getPortableTableDefinition(array $table): string | ||
| 110 | { | ||
| 111 | $table = array_change_key_case($table, CASE_LOWER); | ||
| 112 | |||
| 113 | return $table['name']; | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * {@inheritDoc} | ||
| 118 | */ | ||
| 119 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 120 | { | ||
| 121 | foreach ($tableIndexes as &$tableIndexRow) { | ||
| 122 | $tableIndexRow = array_change_key_case($tableIndexRow, CASE_LOWER); | ||
| 123 | $tableIndexRow['primary'] = (bool) $tableIndexRow['primary']; | ||
| 124 | } | ||
| 125 | |||
| 126 | return parent::_getPortableTableIndexesList($tableIndexes, $tableName); | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * {@inheritDoc} | ||
| 131 | */ | ||
| 132 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 133 | { | ||
| 134 | return new ForeignKeyConstraint( | ||
| 135 | $tableForeignKey['local_columns'], | ||
| 136 | $tableForeignKey['foreign_table'], | ||
| 137 | $tableForeignKey['foreign_columns'], | ||
| 138 | $tableForeignKey['name'], | ||
| 139 | $tableForeignKey['options'], | ||
| 140 | ); | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * {@inheritDoc} | ||
| 145 | */ | ||
| 146 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 147 | { | ||
| 148 | $foreignKeys = []; | ||
| 149 | |||
| 150 | foreach ($tableForeignKeys as $tableForeignKey) { | ||
| 151 | $tableForeignKey = array_change_key_case($tableForeignKey, CASE_LOWER); | ||
| 152 | |||
| 153 | if (! isset($foreignKeys[$tableForeignKey['index_name']])) { | ||
| 154 | $foreignKeys[$tableForeignKey['index_name']] = [ | ||
| 155 | 'local_columns' => [$tableForeignKey['local_column']], | ||
| 156 | 'foreign_table' => $tableForeignKey['foreign_table'], | ||
| 157 | 'foreign_columns' => [$tableForeignKey['foreign_column']], | ||
| 158 | 'name' => $tableForeignKey['index_name'], | ||
| 159 | 'options' => [ | ||
| 160 | 'onUpdate' => $tableForeignKey['on_update'], | ||
| 161 | 'onDelete' => $tableForeignKey['on_delete'], | ||
| 162 | ], | ||
| 163 | ]; | ||
| 164 | } else { | ||
| 165 | $foreignKeys[$tableForeignKey['index_name']]['local_columns'][] = $tableForeignKey['local_column']; | ||
| 166 | $foreignKeys[$tableForeignKey['index_name']]['foreign_columns'][] = $tableForeignKey['foreign_column']; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | return parent::_getPortableTableForeignKeysList($foreignKeys); | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * {@inheritDoc} | ||
| 175 | */ | ||
| 176 | protected function _getPortableViewDefinition(array $view): View | ||
| 177 | { | ||
| 178 | $view = array_change_key_case($view, CASE_LOWER); | ||
| 179 | |||
| 180 | $sql = ''; | ||
| 181 | $pos = strpos($view['text'], ' AS '); | ||
| 182 | |||
| 183 | if ($pos !== false) { | ||
| 184 | $sql = substr($view['text'], $pos + 4); | ||
| 185 | } | ||
| 186 | |||
| 187 | return new View($view['name'], $sql); | ||
| 188 | } | ||
| 189 | |||
| 190 | protected function normalizeName(string $name): string | ||
| 191 | { | ||
| 192 | $identifier = new Identifier($name); | ||
| 193 | |||
| 194 | return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); | ||
| 195 | } | ||
| 196 | |||
| 197 | protected function selectTableNames(string $databaseName): Result | ||
| 198 | { | ||
| 199 | $sql = <<<'SQL' | ||
| 200 | SELECT NAME | ||
| 201 | FROM SYSIBM.SYSTABLES | ||
| 202 | WHERE TYPE = 'T' | ||
| 203 | AND CREATOR = ? | ||
| 204 | SQL; | ||
| 205 | |||
| 206 | return $this->connection->executeQuery($sql, [$databaseName]); | ||
| 207 | } | ||
| 208 | |||
| 209 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 210 | { | ||
| 211 | $sql = 'SELECT'; | ||
| 212 | |||
| 213 | if ($tableName === null) { | ||
| 214 | $sql .= ' C.TABNAME AS NAME,'; | ||
| 215 | } | ||
| 216 | |||
| 217 | $sql .= <<<'SQL' | ||
| 218 | C.COLNAME, | ||
| 219 | C.TYPENAME, | ||
| 220 | C.CODEPAGE, | ||
| 221 | C.NULLS, | ||
| 222 | C.LENGTH, | ||
| 223 | C.SCALE, | ||
| 224 | C.REMARKS AS COMMENT, | ||
| 225 | CASE | ||
| 226 | WHEN C.GENERATED = 'D' THEN 1 | ||
| 227 | ELSE 0 | ||
| 228 | END AS AUTOINCREMENT, | ||
| 229 | C.DEFAULT | ||
| 230 | FROM SYSCAT.COLUMNS C | ||
| 231 | JOIN SYSCAT.TABLES AS T | ||
| 232 | ON T.TABSCHEMA = C.TABSCHEMA | ||
| 233 | AND T.TABNAME = C.TABNAME | ||
| 234 | SQL; | ||
| 235 | |||
| 236 | $conditions = ['C.TABSCHEMA = ?', "T.TYPE = 'T'"]; | ||
| 237 | $params = [$databaseName]; | ||
| 238 | |||
| 239 | if ($tableName !== null) { | ||
| 240 | $conditions[] = 'C.TABNAME = ?'; | ||
| 241 | $params[] = $tableName; | ||
| 242 | } | ||
| 243 | |||
| 244 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.TABNAME, C.COLNO'; | ||
| 245 | |||
| 246 | return $this->connection->executeQuery($sql, $params); | ||
| 247 | } | ||
| 248 | |||
| 249 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 250 | { | ||
| 251 | $sql = 'SELECT'; | ||
| 252 | |||
| 253 | if ($tableName === null) { | ||
| 254 | $sql .= ' IDX.TABNAME AS NAME,'; | ||
| 255 | } | ||
| 256 | |||
| 257 | $sql .= <<<'SQL' | ||
| 258 | IDX.INDNAME AS KEY_NAME, | ||
| 259 | IDXCOL.COLNAME AS COLUMN_NAME, | ||
| 260 | CASE | ||
| 261 | WHEN IDX.UNIQUERULE = 'P' THEN 1 | ||
| 262 | ELSE 0 | ||
| 263 | END AS PRIMARY, | ||
| 264 | CASE | ||
| 265 | WHEN IDX.UNIQUERULE = 'D' THEN 1 | ||
| 266 | ELSE 0 | ||
| 267 | END AS NON_UNIQUE | ||
| 268 | FROM SYSCAT.INDEXES AS IDX | ||
| 269 | JOIN SYSCAT.TABLES AS T | ||
| 270 | ON IDX.TABSCHEMA = T.TABSCHEMA AND IDX.TABNAME = T.TABNAME | ||
| 271 | JOIN SYSCAT.INDEXCOLUSE AS IDXCOL | ||
| 272 | ON IDX.INDSCHEMA = IDXCOL.INDSCHEMA AND IDX.INDNAME = IDXCOL.INDNAME | ||
| 273 | SQL; | ||
| 274 | |||
| 275 | $conditions = ['IDX.TABSCHEMA = ?', "T.TYPE = 'T'"]; | ||
| 276 | $params = [$databaseName]; | ||
| 277 | |||
| 278 | if ($tableName !== null) { | ||
| 279 | $conditions[] = 'IDX.TABNAME = ?'; | ||
| 280 | $params[] = $tableName; | ||
| 281 | } | ||
| 282 | |||
| 283 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IDX.INDNAME, IDXCOL.COLSEQ'; | ||
| 284 | |||
| 285 | return $this->connection->executeQuery($sql, $params); | ||
| 286 | } | ||
| 287 | |||
| 288 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 289 | { | ||
| 290 | $sql = 'SELECT'; | ||
| 291 | |||
| 292 | if ($tableName === null) { | ||
| 293 | $sql .= ' R.TABNAME AS NAME,'; | ||
| 294 | } | ||
| 295 | |||
| 296 | $sql .= <<<'SQL' | ||
| 297 | FKCOL.COLNAME AS LOCAL_COLUMN, | ||
| 298 | R.REFTABNAME AS FOREIGN_TABLE, | ||
| 299 | PKCOL.COLNAME AS FOREIGN_COLUMN, | ||
| 300 | R.CONSTNAME AS INDEX_NAME, | ||
| 301 | CASE | ||
| 302 | WHEN R.UPDATERULE = 'R' THEN 'RESTRICT' | ||
| 303 | END AS ON_UPDATE, | ||
| 304 | CASE | ||
| 305 | WHEN R.DELETERULE = 'C' THEN 'CASCADE' | ||
| 306 | WHEN R.DELETERULE = 'N' THEN 'SET NULL' | ||
| 307 | WHEN R.DELETERULE = 'R' THEN 'RESTRICT' | ||
| 308 | END AS ON_DELETE | ||
| 309 | FROM SYSCAT.REFERENCES AS R | ||
| 310 | JOIN SYSCAT.TABLES AS T | ||
| 311 | ON T.TABSCHEMA = R.TABSCHEMA | ||
| 312 | AND T.TABNAME = R.TABNAME | ||
| 313 | JOIN SYSCAT.KEYCOLUSE AS FKCOL | ||
| 314 | ON FKCOL.CONSTNAME = R.CONSTNAME | ||
| 315 | AND FKCOL.TABSCHEMA = R.TABSCHEMA | ||
| 316 | AND FKCOL.TABNAME = R.TABNAME | ||
| 317 | JOIN SYSCAT.KEYCOLUSE AS PKCOL | ||
| 318 | ON PKCOL.CONSTNAME = R.REFKEYNAME | ||
| 319 | AND PKCOL.TABSCHEMA = R.REFTABSCHEMA | ||
| 320 | AND PKCOL.TABNAME = R.REFTABNAME | ||
| 321 | AND PKCOL.COLSEQ = FKCOL.COLSEQ | ||
| 322 | SQL; | ||
| 323 | |||
| 324 | $conditions = ['R.TABSCHEMA = ?', "T.TYPE = 'T'"]; | ||
| 325 | $params = [$databaseName]; | ||
| 326 | |||
| 327 | if ($tableName !== null) { | ||
| 328 | $conditions[] = 'R.TABNAME = ?'; | ||
| 329 | $params[] = $tableName; | ||
| 330 | } | ||
| 331 | |||
| 332 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY R.CONSTNAME, FKCOL.COLSEQ'; | ||
| 333 | |||
| 334 | return $this->connection->executeQuery($sql, $params); | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * {@inheritDoc} | ||
| 339 | */ | ||
| 340 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 341 | { | ||
| 342 | $sql = 'SELECT NAME, REMARKS'; | ||
| 343 | |||
| 344 | $conditions = []; | ||
| 345 | $params = []; | ||
| 346 | |||
| 347 | if ($tableName !== null) { | ||
| 348 | $conditions[] = 'NAME = ?'; | ||
| 349 | $params[] = $tableName; | ||
| 350 | } | ||
| 351 | |||
| 352 | $sql .= ' FROM SYSIBM.SYSTABLES'; | ||
| 353 | |||
| 354 | if ($conditions !== []) { | ||
| 355 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 356 | } | ||
| 357 | |||
| 358 | /** @var array<string,array<string,mixed>> $metadata */ | ||
| 359 | $metadata = $this->connection->executeQuery($sql, $params) | ||
| 360 | ->fetchAllAssociativeIndexed(); | ||
| 361 | |||
| 362 | $tableOptions = []; | ||
| 363 | foreach ($metadata as $table => $data) { | ||
| 364 | $data = array_change_key_case($data, CASE_LOWER); | ||
| 365 | |||
| 366 | $tableOptions[$table] = ['comment' => $data['remarks']]; | ||
| 367 | } | ||
| 368 | |||
| 369 | return $tableOptions; | ||
| 370 | } | ||
| 371 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php b/vendor/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php new file mode 100644 index 0000000..efba87f --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/DefaultSchemaManagerFactory.php | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Connection; | ||
| 8 | use Doctrine\DBAL\Exception; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * A schema manager factory that returns the default schema manager for the given platform. | ||
| 12 | */ | ||
| 13 | final class DefaultSchemaManagerFactory implements SchemaManagerFactory | ||
| 14 | { | ||
| 15 | /** @throws Exception If the platform does not support creating schema managers yet. */ | ||
| 16 | public function createSchemaManager(Connection $connection): AbstractSchemaManager | ||
| 17 | { | ||
| 18 | return $connection->getDatabasePlatform()->createSchemaManager($connection); | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php b/vendor/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php new file mode 100644 index 0000000..53daac9 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/ColumnAlreadyExists.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class ColumnAlreadyExists extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $tableName, string $columnName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('The column "%s" on table "%s" already exists.', $columnName, $tableName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php b/vendor/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php new file mode 100644 index 0000000..cb1cedf --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/ColumnDoesNotExist.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class ColumnDoesNotExist extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $columnName, string $table): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('There is no column with name "%s" on table "%s".', $columnName, $table)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php b/vendor/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php new file mode 100644 index 0000000..3e4c6c6 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/ForeignKeyDoesNotExist.php | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class ForeignKeyDoesNotExist extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $foreignKeyName, string $table): self | ||
| 16 | { | ||
| 17 | return new self( | ||
| 18 | sprintf('There exists no foreign key with the name "%s" on table "%s".', $foreignKeyName, $table), | ||
| 19 | ); | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php b/vendor/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php new file mode 100644 index 0000000..e8592be --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/IndexAlreadyExists.php | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class IndexAlreadyExists extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $indexName, string $table): self | ||
| 16 | { | ||
| 17 | return new self( | ||
| 18 | sprintf('An index with name "%s" was already defined on table "%s".', $indexName, $table), | ||
| 19 | ); | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php b/vendor/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php new file mode 100644 index 0000000..ee25fd8 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/IndexDoesNotExist.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class IndexDoesNotExist extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $indexName, string $table): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('Index "%s" does not exist on table "%s".', $indexName, $table)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php b/vendor/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php new file mode 100644 index 0000000..d295eb8 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/IndexNameInvalid.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use InvalidArgumentException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class IndexNameInvalid extends InvalidArgumentException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $indexName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('Invalid index name "%s" given, has to be [a-zA-Z0-9_].', $indexName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/InvalidTableName.php b/vendor/doctrine/dbal/src/Schema/Exception/InvalidTableName.php new file mode 100644 index 0000000..3b5d89d --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/InvalidTableName.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use InvalidArgumentException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class InvalidTableName extends InvalidArgumentException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $tableName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('Invalid table name specified "%s".', $tableName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php b/vendor/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php new file mode 100644 index 0000000..4109af5 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/NamespaceAlreadyExists.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class NamespaceAlreadyExists extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $namespaceName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('The namespace with name "%s" already exists.', $namespaceName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php b/vendor/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php new file mode 100644 index 0000000..d374f27 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/SequenceAlreadyExists.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class SequenceAlreadyExists extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $sequenceName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('The sequence "%s" already exists.', $sequenceName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php b/vendor/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php new file mode 100644 index 0000000..fa98cee --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/SequenceDoesNotExist.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class SequenceDoesNotExist extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $sequenceName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('There exists no sequence with the name "%s".', $sequenceName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php b/vendor/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php new file mode 100644 index 0000000..b978dbc --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/TableAlreadyExists.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class TableAlreadyExists extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $tableName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('The table with name "%s" already exists.', $tableName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php b/vendor/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php new file mode 100644 index 0000000..8c66a11 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/TableDoesNotExist.php | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class TableDoesNotExist extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $tableName): self | ||
| 16 | { | ||
| 17 | return new self(sprintf('There is no table with name "%s" in the schema.', $tableName)); | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php b/vendor/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php new file mode 100644 index 0000000..3ae5aaa --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/UniqueConstraintDoesNotExist.php | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use LogicException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class UniqueConstraintDoesNotExist extends LogicException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $constraintName, string $table): self | ||
| 16 | { | ||
| 17 | return new self( | ||
| 18 | sprintf('There exists no unique constraint with the name "%s" on table "%s".', $constraintName, $table), | ||
| 19 | ); | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php b/vendor/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php new file mode 100644 index 0000000..a97ee76 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Exception/UnknownColumnOption.php | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema\Exception; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\SchemaException; | ||
| 8 | use InvalidArgumentException; | ||
| 9 | |||
| 10 | use function sprintf; | ||
| 11 | |||
| 12 | /** @psalm-immutable */ | ||
| 13 | final class UnknownColumnOption extends InvalidArgumentException implements SchemaException | ||
| 14 | { | ||
| 15 | public static function new(string $name): self | ||
| 16 | { | ||
| 17 | return new self( | ||
| 18 | sprintf('The "%s" column option is not supported.', $name), | ||
| 19 | ); | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/ForeignKeyConstraint.php b/vendor/doctrine/dbal/src/Schema/ForeignKeyConstraint.php new file mode 100644 index 0000000..bb5ef7f --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/ForeignKeyConstraint.php | |||
| @@ -0,0 +1,291 @@ | |||
| 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_keys; | ||
| 10 | use function array_map; | ||
| 11 | use function strrpos; | ||
| 12 | use function strtolower; | ||
| 13 | use function strtoupper; | ||
| 14 | use function substr; | ||
| 15 | |||
| 16 | /** | ||
| 17 | * An abstraction class for a foreign key constraint. | ||
| 18 | */ | ||
| 19 | class ForeignKeyConstraint extends AbstractAsset | ||
| 20 | { | ||
| 21 | /** | ||
| 22 | * Asset identifier instances of the referencing table column names the foreign key constraint is associated with. | ||
| 23 | * | ||
| 24 | * @var array<string, Identifier> | ||
| 25 | */ | ||
| 26 | protected array $_localColumnNames; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Table or asset identifier instance of the referenced table name the foreign key constraint is associated with. | ||
| 30 | */ | ||
| 31 | protected Identifier $_foreignTableName; | ||
| 32 | |||
| 33 | /** | ||
| 34 | * Asset identifier instances of the referenced table column names the foreign key constraint is associated with. | ||
| 35 | * | ||
| 36 | * @var array<string, Identifier> | ||
| 37 | */ | ||
| 38 | protected array $_foreignColumnNames; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Initializes the foreign key constraint. | ||
| 42 | * | ||
| 43 | * @param array<int, string> $localColumnNames Names of the referencing table columns. | ||
| 44 | * @param string $foreignTableName Referenced table. | ||
| 45 | * @param array<int, string> $foreignColumnNames Names of the referenced table columns. | ||
| 46 | * @param string $name Name of the foreign key constraint. | ||
| 47 | * @param array<string, mixed> $options Options associated with the foreign key constraint. | ||
| 48 | */ | ||
| 49 | public function __construct( | ||
| 50 | array $localColumnNames, | ||
| 51 | string $foreignTableName, | ||
| 52 | array $foreignColumnNames, | ||
| 53 | string $name = '', | ||
| 54 | protected array $options = [], | ||
| 55 | ) { | ||
| 56 | $this->_setName($name); | ||
| 57 | |||
| 58 | $this->_localColumnNames = $this->createIdentifierMap($localColumnNames); | ||
| 59 | $this->_foreignTableName = new Identifier($foreignTableName); | ||
| 60 | |||
| 61 | $this->_foreignColumnNames = $this->createIdentifierMap($foreignColumnNames); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * @param array<int, string> $names | ||
| 66 | * | ||
| 67 | * @return array<string, Identifier> | ||
| 68 | */ | ||
| 69 | private function createIdentifierMap(array $names): array | ||
| 70 | { | ||
| 71 | $identifiers = []; | ||
| 72 | |||
| 73 | foreach ($names as $name) { | ||
| 74 | $identifiers[$name] = new Identifier($name); | ||
| 75 | } | ||
| 76 | |||
| 77 | return $identifiers; | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Returns the names of the referencing table columns | ||
| 82 | * the foreign key constraint is associated with. | ||
| 83 | * | ||
| 84 | * @return array<int, string> | ||
| 85 | */ | ||
| 86 | public function getLocalColumns(): array | ||
| 87 | { | ||
| 88 | return array_keys($this->_localColumnNames); | ||
| 89 | } | ||
| 90 | |||
| 91 | /** | ||
| 92 | * Returns the quoted representation of the referencing table column names | ||
| 93 | * the foreign key constraint is associated with. | ||
| 94 | * | ||
| 95 | * But only if they were defined with one or the referencing table column name | ||
| 96 | * is a keyword reserved by the platform. | ||
| 97 | * Otherwise the plain unquoted value as inserted is returned. | ||
| 98 | * | ||
| 99 | * @param AbstractPlatform $platform The platform to use for quotation. | ||
| 100 | * | ||
| 101 | * @return array<int, string> | ||
| 102 | */ | ||
| 103 | public function getQuotedLocalColumns(AbstractPlatform $platform): array | ||
| 104 | { | ||
| 105 | $columns = []; | ||
| 106 | |||
| 107 | foreach ($this->_localColumnNames as $column) { | ||
| 108 | $columns[] = $column->getQuotedName($platform); | ||
| 109 | } | ||
| 110 | |||
| 111 | return $columns; | ||
| 112 | } | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Returns unquoted representation of local table column names for comparison with other FK | ||
| 116 | * | ||
| 117 | * @return array<int, string> | ||
| 118 | */ | ||
| 119 | public function getUnquotedLocalColumns(): array | ||
| 120 | { | ||
| 121 | return array_map($this->trimQuotes(...), $this->getLocalColumns()); | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Returns unquoted representation of foreign table column names for comparison with other FK | ||
| 126 | * | ||
| 127 | * @return array<int, string> | ||
| 128 | */ | ||
| 129 | public function getUnquotedForeignColumns(): array | ||
| 130 | { | ||
| 131 | return array_map($this->trimQuotes(...), $this->getForeignColumns()); | ||
| 132 | } | ||
| 133 | |||
| 134 | /** | ||
| 135 | * Returns the name of the referenced table | ||
| 136 | * the foreign key constraint is associated with. | ||
| 137 | */ | ||
| 138 | public function getForeignTableName(): string | ||
| 139 | { | ||
| 140 | return $this->_foreignTableName->getName(); | ||
| 141 | } | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Returns the non-schema qualified foreign table name. | ||
| 145 | */ | ||
| 146 | public function getUnqualifiedForeignTableName(): string | ||
| 147 | { | ||
| 148 | $name = $this->_foreignTableName->getName(); | ||
| 149 | $position = strrpos($name, '.'); | ||
| 150 | |||
| 151 | if ($position !== false) { | ||
| 152 | $name = substr($name, $position + 1); | ||
| 153 | } | ||
| 154 | |||
| 155 | return strtolower($name); | ||
| 156 | } | ||
| 157 | |||
| 158 | /** | ||
| 159 | * Returns the quoted representation of the referenced table name | ||
| 160 | * the foreign key constraint is associated with. | ||
| 161 | * | ||
| 162 | * But only if it was defined with one or the referenced table name | ||
| 163 | * is a keyword reserved by the platform. | ||
| 164 | * Otherwise the plain unquoted value as inserted is returned. | ||
| 165 | * | ||
| 166 | * @param AbstractPlatform $platform The platform to use for quotation. | ||
| 167 | */ | ||
| 168 | public function getQuotedForeignTableName(AbstractPlatform $platform): string | ||
| 169 | { | ||
| 170 | return $this->_foreignTableName->getQuotedName($platform); | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Returns the names of the referenced table columns | ||
| 175 | * the foreign key constraint is associated with. | ||
| 176 | * | ||
| 177 | * @return array<int, string> | ||
| 178 | */ | ||
| 179 | public function getForeignColumns(): array | ||
| 180 | { | ||
| 181 | return array_keys($this->_foreignColumnNames); | ||
| 182 | } | ||
| 183 | |||
| 184 | /** | ||
| 185 | * Returns the quoted representation of the referenced table column names | ||
| 186 | * the foreign key constraint is associated with. | ||
| 187 | * | ||
| 188 | * But only if they were defined with one or the referenced table column name | ||
| 189 | * is a keyword reserved by the platform. | ||
| 190 | * Otherwise the plain unquoted value as inserted is returned. | ||
| 191 | * | ||
| 192 | * @param AbstractPlatform $platform The platform to use for quotation. | ||
| 193 | * | ||
| 194 | * @return array<int, string> | ||
| 195 | */ | ||
| 196 | public function getQuotedForeignColumns(AbstractPlatform $platform): array | ||
| 197 | { | ||
| 198 | $columns = []; | ||
| 199 | |||
| 200 | foreach ($this->_foreignColumnNames as $column) { | ||
| 201 | $columns[] = $column->getQuotedName($platform); | ||
| 202 | } | ||
| 203 | |||
| 204 | return $columns; | ||
| 205 | } | ||
| 206 | |||
| 207 | /** | ||
| 208 | * Returns whether or not a given option | ||
| 209 | * is associated with the foreign key constraint. | ||
| 210 | */ | ||
| 211 | public function hasOption(string $name): bool | ||
| 212 | { | ||
| 213 | return isset($this->options[$name]); | ||
| 214 | } | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Returns an option associated with the foreign key constraint. | ||
| 218 | */ | ||
| 219 | public function getOption(string $name): mixed | ||
| 220 | { | ||
| 221 | return $this->options[$name]; | ||
| 222 | } | ||
| 223 | |||
| 224 | /** | ||
| 225 | * Returns the options associated with the foreign key constraint. | ||
| 226 | * | ||
| 227 | * @return array<string, mixed> | ||
| 228 | */ | ||
| 229 | public function getOptions(): array | ||
| 230 | { | ||
| 231 | return $this->options; | ||
| 232 | } | ||
| 233 | |||
| 234 | /** | ||
| 235 | * Returns the referential action for UPDATE operations | ||
| 236 | * on the referenced table the foreign key constraint is associated with. | ||
| 237 | */ | ||
| 238 | public function onUpdate(): ?string | ||
| 239 | { | ||
| 240 | return $this->onEvent('onUpdate'); | ||
| 241 | } | ||
| 242 | |||
| 243 | /** | ||
| 244 | * Returns the referential action for DELETE operations | ||
| 245 | * on the referenced table the foreign key constraint is associated with. | ||
| 246 | */ | ||
| 247 | public function onDelete(): ?string | ||
| 248 | { | ||
| 249 | return $this->onEvent('onDelete'); | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * Returns the referential action for a given database operation | ||
| 254 | * on the referenced table the foreign key constraint is associated with. | ||
| 255 | * | ||
| 256 | * @param string $event Name of the database operation/event to return the referential action for. | ||
| 257 | */ | ||
| 258 | private function onEvent(string $event): ?string | ||
| 259 | { | ||
| 260 | if (isset($this->options[$event])) { | ||
| 261 | $onEvent = strtoupper($this->options[$event]); | ||
| 262 | |||
| 263 | if ($onEvent !== 'NO ACTION' && $onEvent !== 'RESTRICT') { | ||
| 264 | return $onEvent; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | return null; | ||
| 269 | } | ||
| 270 | |||
| 271 | /** | ||
| 272 | * Checks whether this foreign key constraint intersects the given index columns. | ||
| 273 | * | ||
| 274 | * Returns `true` if at least one of this foreign key's local columns | ||
| 275 | * matches one of the given index's columns, `false` otherwise. | ||
| 276 | * | ||
| 277 | * @param Index $index The index to be checked against. | ||
| 278 | */ | ||
| 279 | public function intersectsIndexColumns(Index $index): bool | ||
| 280 | { | ||
| 281 | foreach ($index->getColumns() as $indexColumn) { | ||
| 282 | foreach ($this->_localColumnNames as $localColumn) { | ||
| 283 | if (strtolower($indexColumn) === strtolower($localColumn->getName())) { | ||
| 284 | return true; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | return false; | ||
| 290 | } | ||
| 291 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Identifier.php b/vendor/doctrine/dbal/src/Schema/Identifier.php new file mode 100644 index 0000000..c3c84a7 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Identifier.php | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * An abstraction class for an asset identifier. | ||
| 9 | * | ||
| 10 | * Wraps identifier names like column names in indexes / foreign keys | ||
| 11 | * in an abstract class for proper quotation capabilities. | ||
| 12 | */ | ||
| 13 | class Identifier extends AbstractAsset | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * @param string $identifier Identifier name to wrap. | ||
| 17 | * @param bool $quote Whether to force quoting the given identifier. | ||
| 18 | */ | ||
| 19 | public function __construct(string $identifier, bool $quote = false) | ||
| 20 | { | ||
| 21 | $this->_setName($identifier); | ||
| 22 | |||
| 23 | if (! $quote || $this->_quoted) { | ||
| 24 | return; | ||
| 25 | } | ||
| 26 | |||
| 27 | $this->_setName('"' . $this->getName() . '"'); | ||
| 28 | } | ||
| 29 | } | ||
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 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php b/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php new file mode 100644 index 0000000..249be13 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/MySQLSchemaManager.php | |||
| @@ -0,0 +1,548 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; | ||
| 9 | use Doctrine\DBAL\Platforms\MariaDBPlatform; | ||
| 10 | use Doctrine\DBAL\Platforms\MySQL; | ||
| 11 | use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider\CachingCharsetMetadataProvider; | ||
| 12 | use Doctrine\DBAL\Platforms\MySQL\CharsetMetadataProvider\ConnectionCharsetMetadataProvider; | ||
| 13 | use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider\CachingCollationMetadataProvider; | ||
| 14 | use Doctrine\DBAL\Platforms\MySQL\CollationMetadataProvider\ConnectionCollationMetadataProvider; | ||
| 15 | use Doctrine\DBAL\Platforms\MySQL\DefaultTableOptions; | ||
| 16 | use Doctrine\DBAL\Result; | ||
| 17 | use Doctrine\DBAL\Types\Type; | ||
| 18 | |||
| 19 | use function array_change_key_case; | ||
| 20 | use function assert; | ||
| 21 | use function explode; | ||
| 22 | use function implode; | ||
| 23 | use function is_string; | ||
| 24 | use function preg_match; | ||
| 25 | use function str_contains; | ||
| 26 | use function strtok; | ||
| 27 | use function strtolower; | ||
| 28 | use function strtr; | ||
| 29 | |||
| 30 | use const CASE_LOWER; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * Schema manager for the MySQL RDBMS. | ||
| 34 | * | ||
| 35 | * @extends AbstractSchemaManager<AbstractMySQLPlatform> | ||
| 36 | */ | ||
| 37 | class MySQLSchemaManager extends AbstractSchemaManager | ||
| 38 | { | ||
| 39 | /** @see https://mariadb.com/kb/en/library/string-literals/#escape-sequences */ | ||
| 40 | private const MARIADB_ESCAPE_SEQUENCES = [ | ||
| 41 | '\\0' => "\0", | ||
| 42 | "\\'" => "'", | ||
| 43 | '\\"' => '"', | ||
| 44 | '\\b' => "\b", | ||
| 45 | '\\n' => "\n", | ||
| 46 | '\\r' => "\r", | ||
| 47 | '\\t' => "\t", | ||
| 48 | '\\Z' => "\x1a", | ||
| 49 | '\\\\' => '\\', | ||
| 50 | '\\%' => '%', | ||
| 51 | '\\_' => '_', | ||
| 52 | |||
| 53 | // Internally, MariaDB escapes single quotes using the standard syntax | ||
| 54 | "''" => "'", | ||
| 55 | ]; | ||
| 56 | |||
| 57 | private ?DefaultTableOptions $defaultTableOptions = null; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * {@inheritDoc} | ||
| 61 | */ | ||
| 62 | protected function _getPortableTableDefinition(array $table): string | ||
| 63 | { | ||
| 64 | return $table['TABLE_NAME']; | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * {@inheritDoc} | ||
| 69 | */ | ||
| 70 | protected function _getPortableViewDefinition(array $view): View | ||
| 71 | { | ||
| 72 | return new View($view['TABLE_NAME'], $view['VIEW_DEFINITION']); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * {@inheritDoc} | ||
| 77 | */ | ||
| 78 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 79 | { | ||
| 80 | foreach ($tableIndexes as $k => $v) { | ||
| 81 | $v = array_change_key_case($v, CASE_LOWER); | ||
| 82 | if ($v['key_name'] === 'PRIMARY') { | ||
| 83 | $v['primary'] = true; | ||
| 84 | } else { | ||
| 85 | $v['primary'] = false; | ||
| 86 | } | ||
| 87 | |||
| 88 | if (str_contains($v['index_type'], 'FULLTEXT')) { | ||
| 89 | $v['flags'] = ['FULLTEXT']; | ||
| 90 | } elseif (str_contains($v['index_type'], 'SPATIAL')) { | ||
| 91 | $v['flags'] = ['SPATIAL']; | ||
| 92 | } | ||
| 93 | |||
| 94 | // Ignore prohibited prefix `length` for spatial index | ||
| 95 | if (! str_contains($v['index_type'], 'SPATIAL')) { | ||
| 96 | $v['length'] = isset($v['sub_part']) ? (int) $v['sub_part'] : null; | ||
| 97 | } | ||
| 98 | |||
| 99 | $tableIndexes[$k] = $v; | ||
| 100 | } | ||
| 101 | |||
| 102 | return parent::_getPortableTableIndexesList($tableIndexes, $tableName); | ||
| 103 | } | ||
| 104 | |||
| 105 | /** | ||
| 106 | * {@inheritDoc} | ||
| 107 | */ | ||
| 108 | protected function _getPortableDatabaseDefinition(array $database): string | ||
| 109 | { | ||
| 110 | return $database['Database']; | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * {@inheritDoc} | ||
| 115 | */ | ||
| 116 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 117 | { | ||
| 118 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); | ||
| 119 | |||
| 120 | $dbType = strtolower($tableColumn['type']); | ||
| 121 | $dbType = strtok($dbType, '(), '); | ||
| 122 | assert(is_string($dbType)); | ||
| 123 | |||
| 124 | $length = $tableColumn['length'] ?? strtok('(), '); | ||
| 125 | |||
| 126 | $fixed = false; | ||
| 127 | |||
| 128 | if (! isset($tableColumn['name'])) { | ||
| 129 | $tableColumn['name'] = ''; | ||
| 130 | } | ||
| 131 | |||
| 132 | $scale = 0; | ||
| 133 | $precision = null; | ||
| 134 | |||
| 135 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
| 136 | |||
| 137 | switch ($dbType) { | ||
| 138 | case 'char': | ||
| 139 | case 'binary': | ||
| 140 | $fixed = true; | ||
| 141 | break; | ||
| 142 | |||
| 143 | case 'float': | ||
| 144 | case 'double': | ||
| 145 | case 'real': | ||
| 146 | case 'numeric': | ||
| 147 | case 'decimal': | ||
| 148 | if ( | ||
| 149 | preg_match( | ||
| 150 | '([A-Za-z]+\(([0-9]+),([0-9]+)\))', | ||
| 151 | $tableColumn['type'], | ||
| 152 | $match, | ||
| 153 | ) === 1 | ||
| 154 | ) { | ||
| 155 | $precision = (int) $match[1]; | ||
| 156 | $scale = (int) $match[2]; | ||
| 157 | $length = null; | ||
| 158 | } | ||
| 159 | |||
| 160 | break; | ||
| 161 | |||
| 162 | case 'tinytext': | ||
| 163 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYTEXT; | ||
| 164 | break; | ||
| 165 | |||
| 166 | case 'text': | ||
| 167 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_TEXT; | ||
| 168 | break; | ||
| 169 | |||
| 170 | case 'mediumtext': | ||
| 171 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT; | ||
| 172 | break; | ||
| 173 | |||
| 174 | case 'tinyblob': | ||
| 175 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_TINYBLOB; | ||
| 176 | break; | ||
| 177 | |||
| 178 | case 'blob': | ||
| 179 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_BLOB; | ||
| 180 | break; | ||
| 181 | |||
| 182 | case 'mediumblob': | ||
| 183 | $length = AbstractMySQLPlatform::LENGTH_LIMIT_MEDIUMBLOB; | ||
| 184 | break; | ||
| 185 | |||
| 186 | case 'tinyint': | ||
| 187 | case 'smallint': | ||
| 188 | case 'mediumint': | ||
| 189 | case 'int': | ||
| 190 | case 'integer': | ||
| 191 | case 'bigint': | ||
| 192 | case 'year': | ||
| 193 | $length = null; | ||
| 194 | break; | ||
| 195 | } | ||
| 196 | |||
| 197 | if ($this->platform instanceof MariaDBPlatform) { | ||
| 198 | $columnDefault = $this->getMariaDBColumnDefault($this->platform, $tableColumn['default']); | ||
| 199 | } else { | ||
| 200 | $columnDefault = $tableColumn['default']; | ||
| 201 | } | ||
| 202 | |||
| 203 | $options = [ | ||
| 204 | 'length' => $length !== null ? (int) $length : null, | ||
| 205 | 'unsigned' => str_contains($tableColumn['type'], 'unsigned'), | ||
| 206 | 'fixed' => $fixed, | ||
| 207 | 'default' => $columnDefault, | ||
| 208 | 'notnull' => $tableColumn['null'] !== 'YES', | ||
| 209 | 'scale' => $scale, | ||
| 210 | 'precision' => $precision, | ||
| 211 | 'autoincrement' => str_contains($tableColumn['extra'], 'auto_increment'), | ||
| 212 | ]; | ||
| 213 | |||
| 214 | if (isset($tableColumn['comment'])) { | ||
| 215 | $options['comment'] = $tableColumn['comment']; | ||
| 216 | } | ||
| 217 | |||
| 218 | $column = new Column($tableColumn['field'], Type::getType($type), $options); | ||
| 219 | |||
| 220 | if (isset($tableColumn['characterset'])) { | ||
| 221 | $column->setPlatformOption('charset', $tableColumn['characterset']); | ||
| 222 | } | ||
| 223 | |||
| 224 | if (isset($tableColumn['collation'])) { | ||
| 225 | $column->setPlatformOption('collation', $tableColumn['collation']); | ||
| 226 | } | ||
| 227 | |||
| 228 | return $column; | ||
| 229 | } | ||
| 230 | |||
| 231 | /** | ||
| 232 | * Return Doctrine/Mysql-compatible column default values for MariaDB 10.2.7+ servers. | ||
| 233 | * | ||
| 234 | * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted | ||
| 235 | * to distinguish them from expressions (see MDEV-10134). | ||
| 236 | * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema | ||
| 237 | * as current_timestamp(), currdate(), currtime() | ||
| 238 | * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have | ||
| 239 | * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) | ||
| 240 | * - \' is always stored as '' in information_schema (normalized) | ||
| 241 | * | ||
| 242 | * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ | ||
| 243 | * @link https://jira.mariadb.org/browse/MDEV-13132 | ||
| 244 | * | ||
| 245 | * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 | ||
| 246 | */ | ||
| 247 | private function getMariaDBColumnDefault(MariaDBPlatform $platform, ?string $columnDefault): ?string | ||
| 248 | { | ||
| 249 | if ($columnDefault === 'NULL' || $columnDefault === null) { | ||
| 250 | return null; | ||
| 251 | } | ||
| 252 | |||
| 253 | if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) { | ||
| 254 | return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); | ||
| 255 | } | ||
| 256 | |||
| 257 | return match ($columnDefault) { | ||
| 258 | 'current_timestamp()' => $platform->getCurrentTimestampSQL(), | ||
| 259 | 'curdate()' => $platform->getCurrentDateSQL(), | ||
| 260 | 'curtime()' => $platform->getCurrentTimeSQL(), | ||
| 261 | default => $columnDefault, | ||
| 262 | }; | ||
| 263 | } | ||
| 264 | |||
| 265 | /** | ||
| 266 | * {@inheritDoc} | ||
| 267 | */ | ||
| 268 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 269 | { | ||
| 270 | $list = []; | ||
| 271 | foreach ($tableForeignKeys as $value) { | ||
| 272 | $value = array_change_key_case($value, CASE_LOWER); | ||
| 273 | if (! isset($list[$value['constraint_name']])) { | ||
| 274 | if (! isset($value['delete_rule']) || $value['delete_rule'] === 'RESTRICT') { | ||
| 275 | $value['delete_rule'] = null; | ||
| 276 | } | ||
| 277 | |||
| 278 | if (! isset($value['update_rule']) || $value['update_rule'] === 'RESTRICT') { | ||
| 279 | $value['update_rule'] = null; | ||
| 280 | } | ||
| 281 | |||
| 282 | $list[$value['constraint_name']] = [ | ||
| 283 | 'name' => $value['constraint_name'], | ||
| 284 | 'local' => [], | ||
| 285 | 'foreign' => [], | ||
| 286 | 'foreignTable' => $value['referenced_table_name'], | ||
| 287 | 'onDelete' => $value['delete_rule'], | ||
| 288 | 'onUpdate' => $value['update_rule'], | ||
| 289 | ]; | ||
| 290 | } | ||
| 291 | |||
| 292 | $list[$value['constraint_name']]['local'][] = $value['column_name']; | ||
| 293 | $list[$value['constraint_name']]['foreign'][] = $value['referenced_column_name']; | ||
| 294 | } | ||
| 295 | |||
| 296 | return parent::_getPortableTableForeignKeysList($list); | ||
| 297 | } | ||
| 298 | |||
| 299 | /** | ||
| 300 | * {@inheritDoc} | ||
| 301 | */ | ||
| 302 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 303 | { | ||
| 304 | return new ForeignKeyConstraint( | ||
| 305 | $tableForeignKey['local'], | ||
| 306 | $tableForeignKey['foreignTable'], | ||
| 307 | $tableForeignKey['foreign'], | ||
| 308 | $tableForeignKey['name'], | ||
| 309 | [ | ||
| 310 | 'onDelete' => $tableForeignKey['onDelete'], | ||
| 311 | 'onUpdate' => $tableForeignKey['onUpdate'], | ||
| 312 | ], | ||
| 313 | ); | ||
| 314 | } | ||
| 315 | |||
| 316 | /** @throws Exception */ | ||
| 317 | public function createComparator(): Comparator | ||
| 318 | { | ||
| 319 | return new MySQL\Comparator( | ||
| 320 | $this->platform, | ||
| 321 | new CachingCharsetMetadataProvider( | ||
| 322 | new ConnectionCharsetMetadataProvider($this->connection), | ||
| 323 | ), | ||
| 324 | new CachingCollationMetadataProvider( | ||
| 325 | new ConnectionCollationMetadataProvider($this->connection), | ||
| 326 | ), | ||
| 327 | $this->getDefaultTableOptions(), | ||
| 328 | ); | ||
| 329 | } | ||
| 330 | |||
| 331 | protected function selectTableNames(string $databaseName): Result | ||
| 332 | { | ||
| 333 | $sql = <<<'SQL' | ||
| 334 | SELECT TABLE_NAME | ||
| 335 | FROM information_schema.TABLES | ||
| 336 | WHERE TABLE_SCHEMA = ? | ||
| 337 | AND TABLE_TYPE = 'BASE TABLE' | ||
| 338 | ORDER BY TABLE_NAME | ||
| 339 | SQL; | ||
| 340 | |||
| 341 | return $this->connection->executeQuery($sql, [$databaseName]); | ||
| 342 | } | ||
| 343 | |||
| 344 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 345 | { | ||
| 346 | $columnTypeSQL = $this->platform->getColumnTypeSQLSnippet('c', $databaseName); | ||
| 347 | |||
| 348 | $sql = 'SELECT'; | ||
| 349 | |||
| 350 | if ($tableName === null) { | ||
| 351 | $sql .= ' c.TABLE_NAME,'; | ||
| 352 | } | ||
| 353 | |||
| 354 | $sql .= <<<SQL | ||
| 355 | c.COLUMN_NAME AS field, | ||
| 356 | $columnTypeSQL AS type, | ||
| 357 | c.IS_NULLABLE AS `null`, | ||
| 358 | c.COLUMN_KEY AS `key`, | ||
| 359 | c.COLUMN_DEFAULT AS `default`, | ||
| 360 | c.EXTRA, | ||
| 361 | c.COLUMN_COMMENT AS comment, | ||
| 362 | c.CHARACTER_SET_NAME AS characterset, | ||
| 363 | c.COLLATION_NAME AS collation | ||
| 364 | FROM information_schema.COLUMNS c | ||
| 365 | INNER JOIN information_schema.TABLES t | ||
| 366 | ON t.TABLE_NAME = c.TABLE_NAME | ||
| 367 | SQL; | ||
| 368 | |||
| 369 | // The schema name is passed multiple times as a literal in the WHERE clause instead of using a JOIN condition | ||
| 370 | // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions | ||
| 371 | // caused by https://bugs.mysql.com/bug.php?id=81347 | ||
| 372 | $conditions = ['c.TABLE_SCHEMA = ?', 't.TABLE_SCHEMA = ?', "t.TABLE_TYPE = 'BASE TABLE'"]; | ||
| 373 | $params = [$databaseName, $databaseName]; | ||
| 374 | |||
| 375 | if ($tableName !== null) { | ||
| 376 | $conditions[] = 't.TABLE_NAME = ?'; | ||
| 377 | $params[] = $tableName; | ||
| 378 | } | ||
| 379 | |||
| 380 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY ORDINAL_POSITION'; | ||
| 381 | |||
| 382 | return $this->connection->executeQuery($sql, $params); | ||
| 383 | } | ||
| 384 | |||
| 385 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 386 | { | ||
| 387 | $sql = 'SELECT'; | ||
| 388 | |||
| 389 | if ($tableName === null) { | ||
| 390 | $sql .= ' TABLE_NAME,'; | ||
| 391 | } | ||
| 392 | |||
| 393 | $sql .= <<<'SQL' | ||
| 394 | NON_UNIQUE AS Non_Unique, | ||
| 395 | INDEX_NAME AS Key_name, | ||
| 396 | COLUMN_NAME AS Column_Name, | ||
| 397 | SUB_PART AS Sub_Part, | ||
| 398 | INDEX_TYPE AS Index_Type | ||
| 399 | FROM information_schema.STATISTICS | ||
| 400 | SQL; | ||
| 401 | |||
| 402 | $conditions = ['TABLE_SCHEMA = ?']; | ||
| 403 | $params = [$databaseName]; | ||
| 404 | |||
| 405 | if ($tableName !== null) { | ||
| 406 | $conditions[] = 'TABLE_NAME = ?'; | ||
| 407 | $params[] = $tableName; | ||
| 408 | } | ||
| 409 | |||
| 410 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY SEQ_IN_INDEX'; | ||
| 411 | |||
| 412 | return $this->connection->executeQuery($sql, $params); | ||
| 413 | } | ||
| 414 | |||
| 415 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 416 | { | ||
| 417 | $sql = 'SELECT DISTINCT'; | ||
| 418 | |||
| 419 | if ($tableName === null) { | ||
| 420 | $sql .= ' k.TABLE_NAME,'; | ||
| 421 | } | ||
| 422 | |||
| 423 | $sql .= <<<'SQL' | ||
| 424 | k.CONSTRAINT_NAME, | ||
| 425 | k.COLUMN_NAME, | ||
| 426 | k.REFERENCED_TABLE_NAME, | ||
| 427 | k.REFERENCED_COLUMN_NAME, | ||
| 428 | k.ORDINAL_POSITION, | ||
| 429 | c.UPDATE_RULE, | ||
| 430 | c.DELETE_RULE | ||
| 431 | FROM information_schema.key_column_usage k | ||
| 432 | INNER JOIN information_schema.referential_constraints c | ||
| 433 | ON c.CONSTRAINT_NAME = k.CONSTRAINT_NAME | ||
| 434 | AND c.TABLE_NAME = k.TABLE_NAME | ||
| 435 | SQL; | ||
| 436 | |||
| 437 | $conditions = ['k.TABLE_SCHEMA = ?']; | ||
| 438 | $params = [$databaseName]; | ||
| 439 | |||
| 440 | if ($tableName !== null) { | ||
| 441 | $conditions[] = 'k.TABLE_NAME = ?'; | ||
| 442 | $params[] = $tableName; | ||
| 443 | } | ||
| 444 | |||
| 445 | // The schema name is passed multiple times in the WHERE clause instead of using a JOIN condition | ||
| 446 | // in order to avoid performance issues on MySQL older than 8.0 and the corresponding MariaDB versions | ||
| 447 | // caused by https://bugs.mysql.com/bug.php?id=81347 | ||
| 448 | $conditions[] = 'c.CONSTRAINT_SCHEMA = ?'; | ||
| 449 | $params[] = $databaseName; | ||
| 450 | |||
| 451 | $conditions[] = 'k.REFERENCED_COLUMN_NAME IS NOT NULL'; | ||
| 452 | |||
| 453 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY k.ORDINAL_POSITION'; | ||
| 454 | |||
| 455 | return $this->connection->executeQuery($sql, $params); | ||
| 456 | } | ||
| 457 | |||
| 458 | /** | ||
| 459 | * {@inheritDoc} | ||
| 460 | */ | ||
| 461 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 462 | { | ||
| 463 | // MariaDB-10.10.1 added FULL_COLLATION_NAME to the information_schema.COLLATION_CHARACTER_SET_APPLICABILITY. | ||
| 464 | // A base collation like uca1400_ai_ci can refer to multiple character sets. The value in the | ||
| 465 | // information_schema.TABLES.TABLE_COLLATION corresponds to the full collation name. | ||
| 466 | // The MariaDB executable comment syntax with version, /*M!101001, is exclusively executed on | ||
| 467 | // MariaDB-10.10.1+ servers for backwards compatibility, and compatiblity to MySQL servers. | ||
| 468 | $sql = <<<'SQL' | ||
| 469 | SELECT t.TABLE_NAME, | ||
| 470 | t.ENGINE, | ||
| 471 | t.AUTO_INCREMENT, | ||
| 472 | t.TABLE_COMMENT, | ||
| 473 | t.CREATE_OPTIONS, | ||
| 474 | t.TABLE_COLLATION, | ||
| 475 | ccsa.CHARACTER_SET_NAME | ||
| 476 | FROM information_schema.TABLES t | ||
| 477 | INNER JOIN information_schema.COLLATION_CHARACTER_SET_APPLICABILITY ccsa | ||
| 478 | ON /*M!101001 ccsa.FULL_COLLATION_NAME = t.TABLE_COLLATION OR */ | ||
| 479 | ccsa.COLLATION_NAME = t.TABLE_COLLATION | ||
| 480 | SQL; | ||
| 481 | |||
| 482 | $conditions = ['t.TABLE_SCHEMA = ?']; | ||
| 483 | $params = [$databaseName]; | ||
| 484 | |||
| 485 | if ($tableName !== null) { | ||
| 486 | $conditions[] = 't.TABLE_NAME = ?'; | ||
| 487 | $params[] = $tableName; | ||
| 488 | } | ||
| 489 | |||
| 490 | $conditions[] = "t.TABLE_TYPE = 'BASE TABLE'"; | ||
| 491 | |||
| 492 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 493 | |||
| 494 | /** @var array<string,array<string,mixed>> $metadata */ | ||
| 495 | $metadata = $this->connection->executeQuery($sql, $params) | ||
| 496 | ->fetchAllAssociativeIndexed(); | ||
| 497 | |||
| 498 | $tableOptions = []; | ||
| 499 | foreach ($metadata as $table => $data) { | ||
| 500 | $data = array_change_key_case($data, CASE_LOWER); | ||
| 501 | |||
| 502 | $tableOptions[$table] = [ | ||
| 503 | 'engine' => $data['engine'], | ||
| 504 | 'collation' => $data['table_collation'], | ||
| 505 | 'charset' => $data['character_set_name'], | ||
| 506 | 'autoincrement' => $data['auto_increment'], | ||
| 507 | 'comment' => $data['table_comment'], | ||
| 508 | 'create_options' => $this->parseCreateOptions($data['create_options']), | ||
| 509 | ]; | ||
| 510 | } | ||
| 511 | |||
| 512 | return $tableOptions; | ||
| 513 | } | ||
| 514 | |||
| 515 | /** @return array<string, string>|array<string, true> */ | ||
| 516 | private function parseCreateOptions(?string $string): array | ||
| 517 | { | ||
| 518 | $options = []; | ||
| 519 | |||
| 520 | if ($string === null || $string === '') { | ||
| 521 | return $options; | ||
| 522 | } | ||
| 523 | |||
| 524 | foreach (explode(' ', $string) as $pair) { | ||
| 525 | $parts = explode('=', $pair, 2); | ||
| 526 | |||
| 527 | $options[$parts[0]] = $parts[1] ?? true; | ||
| 528 | } | ||
| 529 | |||
| 530 | return $options; | ||
| 531 | } | ||
| 532 | |||
| 533 | /** @throws Exception */ | ||
| 534 | private function getDefaultTableOptions(): DefaultTableOptions | ||
| 535 | { | ||
| 536 | if ($this->defaultTableOptions === null) { | ||
| 537 | $row = $this->connection->fetchNumeric( | ||
| 538 | 'SELECT @@character_set_database, @@collation_database', | ||
| 539 | ); | ||
| 540 | |||
| 541 | assert($row !== false); | ||
| 542 | |||
| 543 | $this->defaultTableOptions = new DefaultTableOptions(...$row); | ||
| 544 | } | ||
| 545 | |||
| 546 | return $this->defaultTableOptions; | ||
| 547 | } | ||
| 548 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php b/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php new file mode 100644 index 0000000..f973eaa --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/OracleSchemaManager.php | |||
| @@ -0,0 +1,475 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Exception\DatabaseObjectNotFoundException; | ||
| 9 | use Doctrine\DBAL\Platforms\OraclePlatform; | ||
| 10 | use Doctrine\DBAL\Result; | ||
| 11 | use Doctrine\DBAL\Types\Type; | ||
| 12 | |||
| 13 | use function array_change_key_case; | ||
| 14 | use function array_key_exists; | ||
| 15 | use function array_values; | ||
| 16 | use function assert; | ||
| 17 | use function implode; | ||
| 18 | use function is_string; | ||
| 19 | use function preg_match; | ||
| 20 | use function str_contains; | ||
| 21 | use function str_replace; | ||
| 22 | use function str_starts_with; | ||
| 23 | use function strtolower; | ||
| 24 | use function strtoupper; | ||
| 25 | use function trim; | ||
| 26 | |||
| 27 | use const CASE_LOWER; | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Oracle Schema Manager. | ||
| 31 | * | ||
| 32 | * @extends AbstractSchemaManager<OraclePlatform> | ||
| 33 | */ | ||
| 34 | class OracleSchemaManager extends AbstractSchemaManager | ||
| 35 | { | ||
| 36 | /** | ||
| 37 | * {@inheritDoc} | ||
| 38 | */ | ||
| 39 | protected function _getPortableViewDefinition(array $view): View | ||
| 40 | { | ||
| 41 | $view = array_change_key_case($view, CASE_LOWER); | ||
| 42 | |||
| 43 | return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); | ||
| 44 | } | ||
| 45 | |||
| 46 | /** | ||
| 47 | * {@inheritDoc} | ||
| 48 | */ | ||
| 49 | protected function _getPortableTableDefinition(array $table): string | ||
| 50 | { | ||
| 51 | $table = array_change_key_case($table, CASE_LOWER); | ||
| 52 | |||
| 53 | return $this->getQuotedIdentifierName($table['table_name']); | ||
| 54 | } | ||
| 55 | |||
| 56 | /** | ||
| 57 | * {@inheritDoc} | ||
| 58 | * | ||
| 59 | * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html | ||
| 60 | */ | ||
| 61 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 62 | { | ||
| 63 | $indexBuffer = []; | ||
| 64 | foreach ($tableIndexes as $tableIndex) { | ||
| 65 | $tableIndex = array_change_key_case($tableIndex, CASE_LOWER); | ||
| 66 | |||
| 67 | $keyName = strtolower($tableIndex['name']); | ||
| 68 | $buffer = []; | ||
| 69 | |||
| 70 | if ($tableIndex['is_primary'] === 'P') { | ||
| 71 | $keyName = 'primary'; | ||
| 72 | $buffer['primary'] = true; | ||
| 73 | $buffer['non_unique'] = false; | ||
| 74 | } else { | ||
| 75 | $buffer['primary'] = false; | ||
| 76 | $buffer['non_unique'] = ! $tableIndex['is_unique']; | ||
| 77 | } | ||
| 78 | |||
| 79 | $buffer['key_name'] = $keyName; | ||
| 80 | $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']); | ||
| 81 | $indexBuffer[] = $buffer; | ||
| 82 | } | ||
| 83 | |||
| 84 | return parent::_getPortableTableIndexesList($indexBuffer, $tableName); | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * {@inheritDoc} | ||
| 89 | */ | ||
| 90 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 91 | { | ||
| 92 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); | ||
| 93 | |||
| 94 | $dbType = strtolower($tableColumn['data_type']); | ||
| 95 | if (str_starts_with($dbType, 'timestamp(')) { | ||
| 96 | if (str_contains($dbType, 'with time zone')) { | ||
| 97 | $dbType = 'timestamptz'; | ||
| 98 | } else { | ||
| 99 | $dbType = 'timestamp'; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | $length = $precision = null; | ||
| 104 | $scale = 0; | ||
| 105 | $fixed = false; | ||
| 106 | |||
| 107 | if (! isset($tableColumn['column_name'])) { | ||
| 108 | $tableColumn['column_name'] = ''; | ||
| 109 | } | ||
| 110 | |||
| 111 | assert(array_key_exists('data_default', $tableColumn)); | ||
| 112 | |||
| 113 | // Default values returned from database sometimes have trailing spaces. | ||
| 114 | if (is_string($tableColumn['data_default'])) { | ||
| 115 | $tableColumn['data_default'] = trim($tableColumn['data_default']); | ||
| 116 | } | ||
| 117 | |||
| 118 | if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { | ||
| 119 | $tableColumn['data_default'] = null; | ||
| 120 | } | ||
| 121 | |||
| 122 | if ($tableColumn['data_default'] !== null) { | ||
| 123 | // Default values returned from database are represented as literal expressions | ||
| 124 | if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches) === 1) { | ||
| 125 | $tableColumn['data_default'] = str_replace("''", "'", $matches[1]); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | if ($tableColumn['data_precision'] !== null) { | ||
| 130 | $precision = (int) $tableColumn['data_precision']; | ||
| 131 | } | ||
| 132 | |||
| 133 | if ($tableColumn['data_scale'] !== null) { | ||
| 134 | $scale = (int) $tableColumn['data_scale']; | ||
| 135 | } | ||
| 136 | |||
| 137 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
| 138 | |||
| 139 | switch ($dbType) { | ||
| 140 | case 'number': | ||
| 141 | if ($precision === 20 && $scale === 0) { | ||
| 142 | $type = 'bigint'; | ||
| 143 | } elseif ($precision === 5 && $scale === 0) { | ||
| 144 | $type = 'smallint'; | ||
| 145 | } elseif ($precision === 1 && $scale === 0) { | ||
| 146 | $type = 'boolean'; | ||
| 147 | } elseif ($scale > 0) { | ||
| 148 | $type = 'decimal'; | ||
| 149 | } | ||
| 150 | |||
| 151 | break; | ||
| 152 | |||
| 153 | case 'varchar': | ||
| 154 | case 'varchar2': | ||
| 155 | case 'nvarchar2': | ||
| 156 | $length = (int) $tableColumn['char_length']; | ||
| 157 | break; | ||
| 158 | |||
| 159 | case 'raw': | ||
| 160 | $length = (int) $tableColumn['data_length']; | ||
| 161 | $fixed = true; | ||
| 162 | break; | ||
| 163 | |||
| 164 | case 'char': | ||
| 165 | case 'nchar': | ||
| 166 | $length = (int) $tableColumn['char_length']; | ||
| 167 | $fixed = true; | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | |||
| 171 | $options = [ | ||
| 172 | 'notnull' => $tableColumn['nullable'] === 'N', | ||
| 173 | 'fixed' => $fixed, | ||
| 174 | 'default' => $tableColumn['data_default'], | ||
| 175 | 'length' => $length, | ||
| 176 | 'precision' => $precision, | ||
| 177 | 'scale' => $scale, | ||
| 178 | ]; | ||
| 179 | |||
| 180 | if (isset($tableColumn['comments'])) { | ||
| 181 | $options['comment'] = $tableColumn['comments']; | ||
| 182 | } | ||
| 183 | |||
| 184 | return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * {@inheritDoc} | ||
| 189 | */ | ||
| 190 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 191 | { | ||
| 192 | $list = []; | ||
| 193 | foreach ($tableForeignKeys as $value) { | ||
| 194 | $value = array_change_key_case($value, CASE_LOWER); | ||
| 195 | if (! isset($list[$value['constraint_name']])) { | ||
| 196 | if ($value['delete_rule'] === 'NO ACTION') { | ||
| 197 | $value['delete_rule'] = null; | ||
| 198 | } | ||
| 199 | |||
| 200 | $list[$value['constraint_name']] = [ | ||
| 201 | 'name' => $this->getQuotedIdentifierName($value['constraint_name']), | ||
| 202 | 'local' => [], | ||
| 203 | 'foreign' => [], | ||
| 204 | 'foreignTable' => $value['references_table'], | ||
| 205 | 'onDelete' => $value['delete_rule'], | ||
| 206 | ]; | ||
| 207 | } | ||
| 208 | |||
| 209 | $localColumn = $this->getQuotedIdentifierName($value['local_column']); | ||
| 210 | $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); | ||
| 211 | |||
| 212 | $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; | ||
| 213 | $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; | ||
| 214 | } | ||
| 215 | |||
| 216 | return parent::_getPortableTableForeignKeysList($list); | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * {@inheritDoc} | ||
| 221 | */ | ||
| 222 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 223 | { | ||
| 224 | return new ForeignKeyConstraint( | ||
| 225 | array_values($tableForeignKey['local']), | ||
| 226 | $this->getQuotedIdentifierName($tableForeignKey['foreignTable']), | ||
| 227 | array_values($tableForeignKey['foreign']), | ||
| 228 | $this->getQuotedIdentifierName($tableForeignKey['name']), | ||
| 229 | ['onDelete' => $tableForeignKey['onDelete']], | ||
| 230 | ); | ||
| 231 | } | ||
| 232 | |||
| 233 | /** | ||
| 234 | * {@inheritDoc} | ||
| 235 | */ | ||
| 236 | protected function _getPortableSequenceDefinition(array $sequence): Sequence | ||
| 237 | { | ||
| 238 | $sequence = array_change_key_case($sequence, CASE_LOWER); | ||
| 239 | |||
| 240 | return new Sequence( | ||
| 241 | $this->getQuotedIdentifierName($sequence['sequence_name']), | ||
| 242 | (int) $sequence['increment_by'], | ||
| 243 | (int) $sequence['min_value'], | ||
| 244 | ); | ||
| 245 | } | ||
| 246 | |||
| 247 | /** | ||
| 248 | * {@inheritDoc} | ||
| 249 | */ | ||
| 250 | protected function _getPortableDatabaseDefinition(array $database): string | ||
| 251 | { | ||
| 252 | $database = array_change_key_case($database, CASE_LOWER); | ||
| 253 | |||
| 254 | return $database['username']; | ||
| 255 | } | ||
| 256 | |||
| 257 | public function createDatabase(string $database): void | ||
| 258 | { | ||
| 259 | $statement = $this->platform->getCreateDatabaseSQL($database); | ||
| 260 | |||
| 261 | $params = $this->connection->getParams(); | ||
| 262 | |||
| 263 | if (isset($params['password'])) { | ||
| 264 | $statement .= ' IDENTIFIED BY ' . $params['password']; | ||
| 265 | } | ||
| 266 | |||
| 267 | $this->connection->executeStatement($statement); | ||
| 268 | |||
| 269 | $statement = 'GRANT DBA TO ' . $database; | ||
| 270 | $this->connection->executeStatement($statement); | ||
| 271 | } | ||
| 272 | |||
| 273 | /** @throws Exception */ | ||
| 274 | protected function dropAutoincrement(string $table): bool | ||
| 275 | { | ||
| 276 | $sql = $this->platform->getDropAutoincrementSql($table); | ||
| 277 | foreach ($sql as $query) { | ||
| 278 | $this->connection->executeStatement($query); | ||
| 279 | } | ||
| 280 | |||
| 281 | return true; | ||
| 282 | } | ||
| 283 | |||
| 284 | public function dropTable(string $name): void | ||
| 285 | { | ||
| 286 | try { | ||
| 287 | $this->dropAutoincrement($name); | ||
| 288 | } catch (DatabaseObjectNotFoundException) { | ||
| 289 | } | ||
| 290 | |||
| 291 | parent::dropTable($name); | ||
| 292 | } | ||
| 293 | |||
| 294 | /** | ||
| 295 | * Returns the quoted representation of the given identifier name. | ||
| 296 | * | ||
| 297 | * Quotes non-uppercase identifiers explicitly to preserve case | ||
| 298 | * and thus make references to the particular identifier work. | ||
| 299 | */ | ||
| 300 | private function getQuotedIdentifierName(string $identifier): string | ||
| 301 | { | ||
| 302 | if (preg_match('/[a-z]/', $identifier) === 1) { | ||
| 303 | return $this->platform->quoteIdentifier($identifier); | ||
| 304 | } | ||
| 305 | |||
| 306 | return $identifier; | ||
| 307 | } | ||
| 308 | |||
| 309 | protected function selectTableNames(string $databaseName): Result | ||
| 310 | { | ||
| 311 | $sql = <<<'SQL' | ||
| 312 | SELECT TABLE_NAME | ||
| 313 | FROM ALL_TABLES | ||
| 314 | WHERE OWNER = :OWNER | ||
| 315 | ORDER BY TABLE_NAME | ||
| 316 | SQL; | ||
| 317 | |||
| 318 | return $this->connection->executeQuery($sql, ['OWNER' => $databaseName]); | ||
| 319 | } | ||
| 320 | |||
| 321 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 322 | { | ||
| 323 | $sql = 'SELECT'; | ||
| 324 | |||
| 325 | if ($tableName === null) { | ||
| 326 | $sql .= ' C.TABLE_NAME,'; | ||
| 327 | } | ||
| 328 | |||
| 329 | $sql .= <<<'SQL' | ||
| 330 | C.COLUMN_NAME, | ||
| 331 | C.DATA_TYPE, | ||
| 332 | C.DATA_DEFAULT, | ||
| 333 | C.DATA_PRECISION, | ||
| 334 | C.DATA_SCALE, | ||
| 335 | C.CHAR_LENGTH, | ||
| 336 | C.DATA_LENGTH, | ||
| 337 | C.NULLABLE, | ||
| 338 | D.COMMENTS | ||
| 339 | FROM ALL_TAB_COLUMNS C | ||
| 340 | INNER JOIN ALL_TABLES T | ||
| 341 | ON T.OWNER = C.OWNER | ||
| 342 | AND T.TABLE_NAME = C.TABLE_NAME | ||
| 343 | LEFT JOIN ALL_COL_COMMENTS D | ||
| 344 | ON D.OWNER = C.OWNER | ||
| 345 | AND D.TABLE_NAME = C.TABLE_NAME | ||
| 346 | AND D.COLUMN_NAME = C.COLUMN_NAME | ||
| 347 | SQL; | ||
| 348 | |||
| 349 | $conditions = ['C.OWNER = :OWNER']; | ||
| 350 | $params = ['OWNER' => $databaseName]; | ||
| 351 | |||
| 352 | if ($tableName !== null) { | ||
| 353 | $conditions[] = 'C.TABLE_NAME = :TABLE_NAME'; | ||
| 354 | $params['TABLE_NAME'] = $tableName; | ||
| 355 | } | ||
| 356 | |||
| 357 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.COLUMN_ID'; | ||
| 358 | |||
| 359 | return $this->connection->executeQuery($sql, $params); | ||
| 360 | } | ||
| 361 | |||
| 362 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 363 | { | ||
| 364 | $sql = 'SELECT'; | ||
| 365 | |||
| 366 | if ($tableName === null) { | ||
| 367 | $sql .= ' IND_COL.TABLE_NAME,'; | ||
| 368 | } | ||
| 369 | |||
| 370 | $sql .= <<<'SQL' | ||
| 371 | IND_COL.INDEX_NAME AS NAME, | ||
| 372 | IND.INDEX_TYPE AS TYPE, | ||
| 373 | DECODE(IND.UNIQUENESS, 'NONUNIQUE', 0, 'UNIQUE', 1) AS IS_UNIQUE, | ||
| 374 | IND_COL.COLUMN_NAME, | ||
| 375 | IND_COL.COLUMN_POSITION AS COLUMN_POS, | ||
| 376 | CON.CONSTRAINT_TYPE AS IS_PRIMARY | ||
| 377 | FROM ALL_IND_COLUMNS IND_COL | ||
| 378 | LEFT JOIN ALL_INDEXES IND | ||
| 379 | ON IND.OWNER = IND_COL.INDEX_OWNER | ||
| 380 | AND IND.INDEX_NAME = IND_COL.INDEX_NAME | ||
| 381 | LEFT JOIN ALL_CONSTRAINTS CON | ||
| 382 | ON CON.OWNER = IND_COL.INDEX_OWNER | ||
| 383 | AND CON.INDEX_NAME = IND_COL.INDEX_NAME | ||
| 384 | SQL; | ||
| 385 | |||
| 386 | $conditions = ['IND_COL.INDEX_OWNER = :OWNER']; | ||
| 387 | $params = ['OWNER' => $databaseName]; | ||
| 388 | |||
| 389 | if ($tableName !== null) { | ||
| 390 | $conditions[] = 'IND_COL.TABLE_NAME = :TABLE_NAME'; | ||
| 391 | $params['TABLE_NAME'] = $tableName; | ||
| 392 | } | ||
| 393 | |||
| 394 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IND_COL.TABLE_NAME, IND_COL.INDEX_NAME' | ||
| 395 | . ', IND_COL.COLUMN_POSITION'; | ||
| 396 | |||
| 397 | return $this->connection->executeQuery($sql, $params); | ||
| 398 | } | ||
| 399 | |||
| 400 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 401 | { | ||
| 402 | $sql = 'SELECT'; | ||
| 403 | |||
| 404 | if ($tableName === null) { | ||
| 405 | $sql .= ' COLS.TABLE_NAME,'; | ||
| 406 | } | ||
| 407 | |||
| 408 | $sql .= <<<'SQL' | ||
| 409 | ALC.CONSTRAINT_NAME, | ||
| 410 | ALC.DELETE_RULE, | ||
| 411 | COLS.COLUMN_NAME LOCAL_COLUMN, | ||
| 412 | COLS.POSITION, | ||
| 413 | R_COLS.TABLE_NAME REFERENCES_TABLE, | ||
| 414 | R_COLS.COLUMN_NAME FOREIGN_COLUMN | ||
| 415 | FROM ALL_CONS_COLUMNS COLS | ||
| 416 | LEFT JOIN ALL_CONSTRAINTS ALC ON ALC.OWNER = COLS.OWNER AND ALC.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME | ||
| 417 | LEFT JOIN ALL_CONS_COLUMNS R_COLS ON R_COLS.OWNER = ALC.R_OWNER AND | ||
| 418 | R_COLS.CONSTRAINT_NAME = ALC.R_CONSTRAINT_NAME AND | ||
| 419 | R_COLS.POSITION = COLS.POSITION | ||
| 420 | SQL; | ||
| 421 | |||
| 422 | $conditions = ["ALC.CONSTRAINT_TYPE = 'R'", 'COLS.OWNER = :OWNER']; | ||
| 423 | $params = ['OWNER' => $databaseName]; | ||
| 424 | |||
| 425 | if ($tableName !== null) { | ||
| 426 | $conditions[] = 'COLS.TABLE_NAME = :TABLE_NAME'; | ||
| 427 | $params['TABLE_NAME'] = $tableName; | ||
| 428 | } | ||
| 429 | |||
| 430 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY COLS.TABLE_NAME, COLS.CONSTRAINT_NAME' | ||
| 431 | . ', COLS.POSITION'; | ||
| 432 | |||
| 433 | return $this->connection->executeQuery($sql, $params); | ||
| 434 | } | ||
| 435 | |||
| 436 | /** | ||
| 437 | * {@inheritDoc} | ||
| 438 | */ | ||
| 439 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 440 | { | ||
| 441 | $sql = 'SELECT TABLE_NAME, COMMENTS'; | ||
| 442 | |||
| 443 | $conditions = ['OWNER = :OWNER']; | ||
| 444 | $params = ['OWNER' => $databaseName]; | ||
| 445 | |||
| 446 | if ($tableName !== null) { | ||
| 447 | $conditions[] = 'TABLE_NAME = :TABLE_NAME'; | ||
| 448 | $params['TABLE_NAME'] = $tableName; | ||
| 449 | } | ||
| 450 | |||
| 451 | $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); | ||
| 452 | |||
| 453 | /** @var array<string,array<string,mixed>> $metadata */ | ||
| 454 | $metadata = $this->connection->executeQuery($sql, $params) | ||
| 455 | ->fetchAllAssociativeIndexed(); | ||
| 456 | |||
| 457 | $tableOptions = []; | ||
| 458 | foreach ($metadata as $table => $data) { | ||
| 459 | $data = array_change_key_case($data, CASE_LOWER); | ||
| 460 | |||
| 461 | $tableOptions[$table] = [ | ||
| 462 | 'comment' => $data['comments'], | ||
| 463 | ]; | ||
| 464 | } | ||
| 465 | |||
| 466 | return $tableOptions; | ||
| 467 | } | ||
| 468 | |||
| 469 | protected function normalizeName(string $name): string | ||
| 470 | { | ||
| 471 | $identifier = new Identifier($name); | ||
| 472 | |||
| 473 | return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); | ||
| 474 | } | ||
| 475 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php b/vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php new file mode 100644 index 0000000..9af16c9 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/PostgreSQLSchemaManager.php | |||
| @@ -0,0 +1,572 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Platforms\PostgreSQLPlatform; | ||
| 9 | use Doctrine\DBAL\Result; | ||
| 10 | use Doctrine\DBAL\Types\JsonType; | ||
| 11 | use Doctrine\DBAL\Types\Type; | ||
| 12 | |||
| 13 | use function array_change_key_case; | ||
| 14 | use function array_key_exists; | ||
| 15 | use function array_map; | ||
| 16 | use function array_merge; | ||
| 17 | use function assert; | ||
| 18 | use function explode; | ||
| 19 | use function implode; | ||
| 20 | use function in_array; | ||
| 21 | use function is_string; | ||
| 22 | use function preg_match; | ||
| 23 | use function sprintf; | ||
| 24 | use function str_contains; | ||
| 25 | use function str_replace; | ||
| 26 | use function strtolower; | ||
| 27 | use function trim; | ||
| 28 | |||
| 29 | use const CASE_LOWER; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * PostgreSQL Schema Manager. | ||
| 33 | * | ||
| 34 | * @extends AbstractSchemaManager<PostgreSQLPlatform> | ||
| 35 | */ | ||
| 36 | class PostgreSQLSchemaManager extends AbstractSchemaManager | ||
| 37 | { | ||
| 38 | private ?string $currentSchema = null; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * {@inheritDoc} | ||
| 42 | */ | ||
| 43 | public function listSchemaNames(): array | ||
| 44 | { | ||
| 45 | return $this->connection->fetchFirstColumn( | ||
| 46 | <<<'SQL' | ||
| 47 | SELECT schema_name | ||
| 48 | FROM information_schema.schemata | ||
| 49 | WHERE schema_name NOT LIKE 'pg\_%' | ||
| 50 | AND schema_name != 'information_schema' | ||
| 51 | SQL, | ||
| 52 | ); | ||
| 53 | } | ||
| 54 | |||
| 55 | public function createSchemaConfig(): SchemaConfig | ||
| 56 | { | ||
| 57 | $config = parent::createSchemaConfig(); | ||
| 58 | |||
| 59 | $config->setName($this->getCurrentSchema()); | ||
| 60 | |||
| 61 | return $config; | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Returns the name of the current schema. | ||
| 66 | * | ||
| 67 | * @throws Exception | ||
| 68 | */ | ||
| 69 | protected function getCurrentSchema(): ?string | ||
| 70 | { | ||
| 71 | return $this->currentSchema ??= $this->determineCurrentSchema(); | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Determines the name of the current schema. | ||
| 76 | * | ||
| 77 | * @throws Exception | ||
| 78 | */ | ||
| 79 | protected function determineCurrentSchema(): string | ||
| 80 | { | ||
| 81 | $currentSchema = $this->connection->fetchOne('SELECT current_schema()'); | ||
| 82 | assert(is_string($currentSchema)); | ||
| 83 | |||
| 84 | return $currentSchema; | ||
| 85 | } | ||
| 86 | |||
| 87 | /** | ||
| 88 | * {@inheritDoc} | ||
| 89 | */ | ||
| 90 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 91 | { | ||
| 92 | $onUpdate = null; | ||
| 93 | $onDelete = null; | ||
| 94 | |||
| 95 | if ( | ||
| 96 | preg_match( | ||
| 97 | '(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', | ||
| 98 | $tableForeignKey['condef'], | ||
| 99 | $match, | ||
| 100 | ) === 1 | ||
| 101 | ) { | ||
| 102 | $onUpdate = $match[1]; | ||
| 103 | } | ||
| 104 | |||
| 105 | if ( | ||
| 106 | preg_match( | ||
| 107 | '(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', | ||
| 108 | $tableForeignKey['condef'], | ||
| 109 | $match, | ||
| 110 | ) === 1 | ||
| 111 | ) { | ||
| 112 | $onDelete = $match[1]; | ||
| 113 | } | ||
| 114 | |||
| 115 | $result = preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values); | ||
| 116 | assert($result === 1); | ||
| 117 | |||
| 118 | // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get | ||
| 119 | // the idea to trim them here. | ||
| 120 | $localColumns = array_map('trim', explode(',', $values[1])); | ||
| 121 | $foreignColumns = array_map('trim', explode(',', $values[3])); | ||
| 122 | $foreignTable = $values[2]; | ||
| 123 | |||
| 124 | return new ForeignKeyConstraint( | ||
| 125 | $localColumns, | ||
| 126 | $foreignTable, | ||
| 127 | $foreignColumns, | ||
| 128 | $tableForeignKey['conname'], | ||
| 129 | ['onUpdate' => $onUpdate, 'onDelete' => $onDelete], | ||
| 130 | ); | ||
| 131 | } | ||
| 132 | |||
| 133 | /** | ||
| 134 | * {@inheritDoc} | ||
| 135 | */ | ||
| 136 | protected function _getPortableViewDefinition(array $view): View | ||
| 137 | { | ||
| 138 | return new View($view['schemaname'] . '.' . $view['viewname'], $view['definition']); | ||
| 139 | } | ||
| 140 | |||
| 141 | /** | ||
| 142 | * {@inheritDoc} | ||
| 143 | */ | ||
| 144 | protected function _getPortableTableDefinition(array $table): string | ||
| 145 | { | ||
| 146 | $currentSchema = $this->getCurrentSchema(); | ||
| 147 | |||
| 148 | if ($table['schema_name'] === $currentSchema) { | ||
| 149 | return $table['table_name']; | ||
| 150 | } | ||
| 151 | |||
| 152 | return $table['schema_name'] . '.' . $table['table_name']; | ||
| 153 | } | ||
| 154 | |||
| 155 | /** | ||
| 156 | * {@inheritDoc} | ||
| 157 | * | ||
| 158 | * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html | ||
| 159 | */ | ||
| 160 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 161 | { | ||
| 162 | $buffer = []; | ||
| 163 | foreach ($tableIndexes as $row) { | ||
| 164 | $colNumbers = array_map('intval', explode(' ', $row['indkey'])); | ||
| 165 | $columnNameSql = sprintf( | ||
| 166 | 'SELECT attnum, attname FROM pg_attribute WHERE attrelid=%d AND attnum IN (%s) ORDER BY attnum ASC', | ||
| 167 | $row['indrelid'], | ||
| 168 | implode(' ,', $colNumbers), | ||
| 169 | ); | ||
| 170 | |||
| 171 | $indexColumns = $this->connection->fetchAllAssociative($columnNameSql); | ||
| 172 | |||
| 173 | // required for getting the order of the columns right. | ||
| 174 | foreach ($colNumbers as $colNum) { | ||
| 175 | foreach ($indexColumns as $colRow) { | ||
| 176 | if ($colNum !== $colRow['attnum']) { | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | |||
| 180 | $buffer[] = [ | ||
| 181 | 'key_name' => $row['relname'], | ||
| 182 | 'column_name' => trim($colRow['attname']), | ||
| 183 | 'non_unique' => ! $row['indisunique'], | ||
| 184 | 'primary' => $row['indisprimary'], | ||
| 185 | 'where' => $row['where'], | ||
| 186 | ]; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | return parent::_getPortableTableIndexesList($buffer, $tableName); | ||
| 192 | } | ||
| 193 | |||
| 194 | /** | ||
| 195 | * {@inheritDoc} | ||
| 196 | */ | ||
| 197 | protected function _getPortableDatabaseDefinition(array $database): string | ||
| 198 | { | ||
| 199 | return $database['datname']; | ||
| 200 | } | ||
| 201 | |||
| 202 | /** | ||
| 203 | * {@inheritDoc} | ||
| 204 | */ | ||
| 205 | protected function _getPortableSequenceDefinition(array $sequence): Sequence | ||
| 206 | { | ||
| 207 | if ($sequence['schemaname'] !== 'public') { | ||
| 208 | $sequenceName = $sequence['schemaname'] . '.' . $sequence['relname']; | ||
| 209 | } else { | ||
| 210 | $sequenceName = $sequence['relname']; | ||
| 211 | } | ||
| 212 | |||
| 213 | return new Sequence($sequenceName, (int) $sequence['increment_by'], (int) $sequence['min_value']); | ||
| 214 | } | ||
| 215 | |||
| 216 | /** | ||
| 217 | * {@inheritDoc} | ||
| 218 | */ | ||
| 219 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 220 | { | ||
| 221 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); | ||
| 222 | |||
| 223 | $length = null; | ||
| 224 | |||
| 225 | if ( | ||
| 226 | in_array(strtolower($tableColumn['type']), ['varchar', 'bpchar'], true) | ||
| 227 | && preg_match('/\((\d*)\)/', $tableColumn['complete_type'], $matches) === 1 | ||
| 228 | ) { | ||
| 229 | $length = (int) $matches[1]; | ||
| 230 | } | ||
| 231 | |||
| 232 | $autoincrement = $tableColumn['attidentity'] === 'd'; | ||
| 233 | |||
| 234 | $matches = []; | ||
| 235 | |||
| 236 | assert(array_key_exists('default', $tableColumn)); | ||
| 237 | assert(array_key_exists('complete_type', $tableColumn)); | ||
| 238 | |||
| 239 | if ($tableColumn['default'] !== null) { | ||
| 240 | if (preg_match("/^['(](.*)[')]::/", $tableColumn['default'], $matches) === 1) { | ||
| 241 | $tableColumn['default'] = $matches[1]; | ||
| 242 | } elseif (preg_match('/^NULL::/', $tableColumn['default']) === 1) { | ||
| 243 | $tableColumn['default'] = null; | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | if ($length === -1 && isset($tableColumn['atttypmod'])) { | ||
| 248 | $length = $tableColumn['atttypmod'] - 4; | ||
| 249 | } | ||
| 250 | |||
| 251 | if ((int) $length <= 0) { | ||
| 252 | $length = null; | ||
| 253 | } | ||
| 254 | |||
| 255 | $fixed = false; | ||
| 256 | |||
| 257 | if (! isset($tableColumn['name'])) { | ||
| 258 | $tableColumn['name'] = ''; | ||
| 259 | } | ||
| 260 | |||
| 261 | $precision = null; | ||
| 262 | $scale = 0; | ||
| 263 | $jsonb = null; | ||
| 264 | |||
| 265 | $dbType = strtolower($tableColumn['type']); | ||
| 266 | if ( | ||
| 267 | $tableColumn['domain_type'] !== null | ||
| 268 | && $tableColumn['domain_type'] !== '' | ||
| 269 | && ! $this->platform->hasDoctrineTypeMappingFor($tableColumn['type']) | ||
| 270 | ) { | ||
| 271 | $dbType = strtolower($tableColumn['domain_type']); | ||
| 272 | $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; | ||
| 273 | } | ||
| 274 | |||
| 275 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
| 276 | |||
| 277 | switch ($dbType) { | ||
| 278 | case 'smallint': | ||
| 279 | case 'int2': | ||
| 280 | case 'int': | ||
| 281 | case 'int4': | ||
| 282 | case 'integer': | ||
| 283 | case 'bigint': | ||
| 284 | case 'int8': | ||
| 285 | $length = null; | ||
| 286 | break; | ||
| 287 | |||
| 288 | case 'bool': | ||
| 289 | case 'boolean': | ||
| 290 | if ($tableColumn['default'] === 'true') { | ||
| 291 | $tableColumn['default'] = true; | ||
| 292 | } | ||
| 293 | |||
| 294 | if ($tableColumn['default'] === 'false') { | ||
| 295 | $tableColumn['default'] = false; | ||
| 296 | } | ||
| 297 | |||
| 298 | $length = null; | ||
| 299 | break; | ||
| 300 | |||
| 301 | case 'json': | ||
| 302 | case 'text': | ||
| 303 | case '_varchar': | ||
| 304 | case 'varchar': | ||
| 305 | $tableColumn['default'] = $this->parseDefaultExpression($tableColumn['default']); | ||
| 306 | break; | ||
| 307 | |||
| 308 | case 'char': | ||
| 309 | case 'bpchar': | ||
| 310 | $fixed = true; | ||
| 311 | break; | ||
| 312 | |||
| 313 | case 'float': | ||
| 314 | case 'float4': | ||
| 315 | case 'float8': | ||
| 316 | case 'double': | ||
| 317 | case 'double precision': | ||
| 318 | case 'real': | ||
| 319 | case 'decimal': | ||
| 320 | case 'money': | ||
| 321 | case 'numeric': | ||
| 322 | if ( | ||
| 323 | preg_match( | ||
| 324 | '([A-Za-z]+\(([0-9]+),([0-9]+)\))', | ||
| 325 | $tableColumn['complete_type'], | ||
| 326 | $match, | ||
| 327 | ) === 1 | ||
| 328 | ) { | ||
| 329 | $precision = (int) $match[1]; | ||
| 330 | $scale = (int) $match[2]; | ||
| 331 | $length = null; | ||
| 332 | } | ||
| 333 | |||
| 334 | break; | ||
| 335 | |||
| 336 | case 'year': | ||
| 337 | $length = null; | ||
| 338 | break; | ||
| 339 | |||
| 340 | // PostgreSQL 9.4+ only | ||
| 341 | case 'jsonb': | ||
| 342 | $jsonb = true; | ||
| 343 | break; | ||
| 344 | } | ||
| 345 | |||
| 346 | if ( | ||
| 347 | is_string($tableColumn['default']) && preg_match( | ||
| 348 | "('([^']+)'::)", | ||
| 349 | $tableColumn['default'], | ||
| 350 | $match, | ||
| 351 | ) === 1 | ||
| 352 | ) { | ||
| 353 | $tableColumn['default'] = $match[1]; | ||
| 354 | } | ||
| 355 | |||
| 356 | $options = [ | ||
| 357 | 'length' => $length, | ||
| 358 | 'notnull' => (bool) $tableColumn['isnotnull'], | ||
| 359 | 'default' => $tableColumn['default'], | ||
| 360 | 'precision' => $precision, | ||
| 361 | 'scale' => $scale, | ||
| 362 | 'fixed' => $fixed, | ||
| 363 | 'autoincrement' => $autoincrement, | ||
| 364 | ]; | ||
| 365 | |||
| 366 | if (isset($tableColumn['comment'])) { | ||
| 367 | $options['comment'] = $tableColumn['comment']; | ||
| 368 | } | ||
| 369 | |||
| 370 | $column = new Column($tableColumn['field'], Type::getType($type), $options); | ||
| 371 | |||
| 372 | if (! empty($tableColumn['collation'])) { | ||
| 373 | $column->setPlatformOption('collation', $tableColumn['collation']); | ||
| 374 | } | ||
| 375 | |||
| 376 | if ($column->getType() instanceof JsonType) { | ||
| 377 | $column->setPlatformOption('jsonb', $jsonb); | ||
| 378 | } | ||
| 379 | |||
| 380 | return $column; | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Parses a default value expression as given by PostgreSQL | ||
| 385 | */ | ||
| 386 | private function parseDefaultExpression(?string $default): ?string | ||
| 387 | { | ||
| 388 | if ($default === null) { | ||
| 389 | return $default; | ||
| 390 | } | ||
| 391 | |||
| 392 | return str_replace("''", "'", $default); | ||
| 393 | } | ||
| 394 | |||
| 395 | protected function selectTableNames(string $databaseName): Result | ||
| 396 | { | ||
| 397 | $sql = <<<'SQL' | ||
| 398 | SELECT quote_ident(table_name) AS table_name, | ||
| 399 | table_schema AS schema_name | ||
| 400 | FROM information_schema.tables | ||
| 401 | WHERE table_catalog = ? | ||
| 402 | AND table_schema NOT LIKE 'pg\_%' | ||
| 403 | AND table_schema != 'information_schema' | ||
| 404 | AND table_name != 'geometry_columns' | ||
| 405 | AND table_name != 'spatial_ref_sys' | ||
| 406 | AND table_type = 'BASE TABLE' | ||
| 407 | SQL; | ||
| 408 | |||
| 409 | return $this->connection->executeQuery($sql, [$databaseName]); | ||
| 410 | } | ||
| 411 | |||
| 412 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 413 | { | ||
| 414 | $sql = 'SELECT'; | ||
| 415 | |||
| 416 | if ($tableName === null) { | ||
| 417 | $sql .= ' c.relname AS table_name, n.nspname AS schema_name,'; | ||
| 418 | } | ||
| 419 | |||
| 420 | $sql .= <<<'SQL' | ||
| 421 | a.attnum, | ||
| 422 | quote_ident(a.attname) AS field, | ||
| 423 | t.typname AS type, | ||
| 424 | format_type(a.atttypid, a.atttypmod) AS complete_type, | ||
| 425 | (SELECT tc.collcollate FROM pg_catalog.pg_collation tc WHERE tc.oid = a.attcollation) AS collation, | ||
| 426 | (SELECT t1.typname FROM pg_catalog.pg_type t1 WHERE t1.oid = t.typbasetype) AS domain_type, | ||
| 427 | (SELECT format_type(t2.typbasetype, t2.typtypmod) FROM | ||
| 428 | pg_catalog.pg_type t2 WHERE t2.typtype = 'd' AND t2.oid = a.atttypid) AS domain_complete_type, | ||
| 429 | a.attnotnull AS isnotnull, | ||
| 430 | a.attidentity, | ||
| 431 | (SELECT 't' | ||
| 432 | FROM pg_index | ||
| 433 | WHERE c.oid = pg_index.indrelid | ||
| 434 | AND pg_index.indkey[0] = a.attnum | ||
| 435 | AND pg_index.indisprimary = 't' | ||
| 436 | ) AS pri, | ||
| 437 | (SELECT pg_get_expr(adbin, adrelid) | ||
| 438 | FROM pg_attrdef | ||
| 439 | WHERE c.oid = pg_attrdef.adrelid | ||
| 440 | AND pg_attrdef.adnum=a.attnum | ||
| 441 | ) AS default, | ||
| 442 | (SELECT pg_description.description | ||
| 443 | FROM pg_description WHERE pg_description.objoid = c.oid AND a.attnum = pg_description.objsubid | ||
| 444 | ) AS comment | ||
| 445 | FROM pg_attribute a | ||
| 446 | INNER JOIN pg_class c | ||
| 447 | ON c.oid = a.attrelid | ||
| 448 | INNER JOIN pg_type t | ||
| 449 | ON t.oid = a.atttypid | ||
| 450 | INNER JOIN pg_namespace n | ||
| 451 | ON n.oid = c.relnamespace | ||
| 452 | LEFT JOIN pg_depend d | ||
| 453 | ON d.objid = c.oid | ||
| 454 | AND d.deptype = 'e' | ||
| 455 | AND d.classid = (SELECT oid FROM pg_class WHERE relname = 'pg_class') | ||
| 456 | SQL; | ||
| 457 | |||
| 458 | $conditions = array_merge([ | ||
| 459 | 'a.attnum > 0', | ||
| 460 | "c.relkind = 'r'", | ||
| 461 | 'd.refobjid IS NULL', | ||
| 462 | ], $this->buildQueryConditions($tableName)); | ||
| 463 | |||
| 464 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY a.attnum'; | ||
| 465 | |||
| 466 | return $this->connection->executeQuery($sql); | ||
| 467 | } | ||
| 468 | |||
| 469 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 470 | { | ||
| 471 | $sql = 'SELECT'; | ||
| 472 | |||
| 473 | if ($tableName === null) { | ||
| 474 | $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,'; | ||
| 475 | } | ||
| 476 | |||
| 477 | $sql .= <<<'SQL' | ||
| 478 | quote_ident(ic.relname) AS relname, | ||
| 479 | i.indisunique, | ||
| 480 | i.indisprimary, | ||
| 481 | i.indkey, | ||
| 482 | i.indrelid, | ||
| 483 | pg_get_expr(indpred, indrelid) AS "where" | ||
| 484 | FROM pg_index i | ||
| 485 | JOIN pg_class AS tc ON tc.oid = i.indrelid | ||
| 486 | JOIN pg_namespace tn ON tn.oid = tc.relnamespace | ||
| 487 | JOIN pg_class AS ic ON ic.oid = i.indexrelid | ||
| 488 | WHERE ic.oid IN ( | ||
| 489 | SELECT indexrelid | ||
| 490 | FROM pg_index i, pg_class c, pg_namespace n | ||
| 491 | SQL; | ||
| 492 | |||
| 493 | $conditions = array_merge([ | ||
| 494 | 'c.oid = i.indrelid', | ||
| 495 | 'c.relnamespace = n.oid', | ||
| 496 | ], $this->buildQueryConditions($tableName)); | ||
| 497 | |||
| 498 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ')'; | ||
| 499 | |||
| 500 | return $this->connection->executeQuery($sql); | ||
| 501 | } | ||
| 502 | |||
| 503 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 504 | { | ||
| 505 | $sql = 'SELECT'; | ||
| 506 | |||
| 507 | if ($tableName === null) { | ||
| 508 | $sql .= ' tc.relname AS table_name, tn.nspname AS schema_name,'; | ||
| 509 | } | ||
| 510 | |||
| 511 | $sql .= <<<'SQL' | ||
| 512 | quote_ident(r.conname) as conname, | ||
| 513 | pg_get_constraintdef(r.oid, true) as condef | ||
| 514 | FROM pg_constraint r | ||
| 515 | JOIN pg_class AS tc ON tc.oid = r.conrelid | ||
| 516 | JOIN pg_namespace tn ON tn.oid = tc.relnamespace | ||
| 517 | WHERE r.conrelid IN | ||
| 518 | ( | ||
| 519 | SELECT c.oid | ||
| 520 | FROM pg_class c, pg_namespace n | ||
| 521 | SQL; | ||
| 522 | |||
| 523 | $conditions = array_merge(['n.oid = c.relnamespace'], $this->buildQueryConditions($tableName)); | ||
| 524 | |||
| 525 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ") AND r.contype = 'f'"; | ||
| 526 | |||
| 527 | return $this->connection->executeQuery($sql); | ||
| 528 | } | ||
| 529 | |||
| 530 | /** | ||
| 531 | * {@inheritDoc} | ||
| 532 | */ | ||
| 533 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 534 | { | ||
| 535 | $sql = <<<'SQL' | ||
| 536 | SELECT c.relname, | ||
| 537 | CASE c.relpersistence WHEN 'u' THEN true ELSE false END as unlogged, | ||
| 538 | obj_description(c.oid, 'pg_class') AS comment | ||
| 539 | FROM pg_class c | ||
| 540 | INNER JOIN pg_namespace n | ||
| 541 | ON n.oid = c.relnamespace | ||
| 542 | SQL; | ||
| 543 | |||
| 544 | $conditions = array_merge(["c.relkind = 'r'"], $this->buildQueryConditions($tableName)); | ||
| 545 | |||
| 546 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 547 | |||
| 548 | return $this->connection->fetchAllAssociativeIndexed($sql); | ||
| 549 | } | ||
| 550 | |||
| 551 | /** @return list<string> */ | ||
| 552 | private function buildQueryConditions(?string $tableName): array | ||
| 553 | { | ||
| 554 | $conditions = []; | ||
| 555 | |||
| 556 | if ($tableName !== null) { | ||
| 557 | if (str_contains($tableName, '.')) { | ||
| 558 | [$schemaName, $tableName] = explode('.', $tableName); | ||
| 559 | $conditions[] = 'n.nspname = ' . $this->platform->quoteStringLiteral($schemaName); | ||
| 560 | } else { | ||
| 561 | $conditions[] = 'n.nspname = ANY(current_schemas(false))'; | ||
| 562 | } | ||
| 563 | |||
| 564 | $identifier = new Identifier($tableName); | ||
| 565 | $conditions[] = 'c.relname = ' . $this->platform->quoteStringLiteral($identifier->getName()); | ||
| 566 | } | ||
| 567 | |||
| 568 | $conditions[] = "n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')"; | ||
| 569 | |||
| 570 | return $conditions; | ||
| 571 | } | ||
| 572 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/SQLServerSchemaManager.php b/vendor/doctrine/dbal/src/Schema/SQLServerSchemaManager.php new file mode 100644 index 0000000..e0a74ce --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/SQLServerSchemaManager.php | |||
| @@ -0,0 +1,498 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Platforms\SQLServer; | ||
| 9 | use Doctrine\DBAL\Platforms\SQLServerPlatform; | ||
| 10 | use Doctrine\DBAL\Result; | ||
| 11 | use Doctrine\DBAL\Types\Type; | ||
| 12 | |||
| 13 | use function array_change_key_case; | ||
| 14 | use function assert; | ||
| 15 | use function explode; | ||
| 16 | use function implode; | ||
| 17 | use function is_string; | ||
| 18 | use function preg_match; | ||
| 19 | use function sprintf; | ||
| 20 | use function str_contains; | ||
| 21 | use function str_replace; | ||
| 22 | use function strtok; | ||
| 23 | |||
| 24 | use const CASE_LOWER; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * SQL Server Schema Manager. | ||
| 28 | * | ||
| 29 | * @extends AbstractSchemaManager<SQLServerPlatform> | ||
| 30 | */ | ||
| 31 | class SQLServerSchemaManager extends AbstractSchemaManager | ||
| 32 | { | ||
| 33 | private ?string $databaseCollation = null; | ||
| 34 | |||
| 35 | /** | ||
| 36 | * {@inheritDoc} | ||
| 37 | */ | ||
| 38 | public function listSchemaNames(): array | ||
| 39 | { | ||
| 40 | return $this->connection->fetchFirstColumn( | ||
| 41 | <<<'SQL' | ||
| 42 | SELECT name | ||
| 43 | FROM sys.schemas | ||
| 44 | WHERE name NOT IN('guest', 'INFORMATION_SCHEMA', 'sys') | ||
| 45 | SQL, | ||
| 46 | ); | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * {@inheritDoc} | ||
| 51 | */ | ||
| 52 | protected function _getPortableSequenceDefinition(array $sequence): Sequence | ||
| 53 | { | ||
| 54 | return new Sequence($sequence['name'], (int) $sequence['increment'], (int) $sequence['start_value']); | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * {@inheritDoc} | ||
| 59 | */ | ||
| 60 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 61 | { | ||
| 62 | $dbType = strtok($tableColumn['type'], '(), '); | ||
| 63 | assert(is_string($dbType)); | ||
| 64 | |||
| 65 | $length = (int) $tableColumn['length']; | ||
| 66 | |||
| 67 | $precision = null; | ||
| 68 | |||
| 69 | $scale = 0; | ||
| 70 | $fixed = false; | ||
| 71 | |||
| 72 | if (! isset($tableColumn['name'])) { | ||
| 73 | $tableColumn['name'] = ''; | ||
| 74 | } | ||
| 75 | |||
| 76 | if ($tableColumn['scale'] !== null) { | ||
| 77 | $scale = (int) $tableColumn['scale']; | ||
| 78 | } | ||
| 79 | |||
| 80 | if ($tableColumn['precision'] !== null) { | ||
| 81 | $precision = (int) $tableColumn['precision']; | ||
| 82 | } | ||
| 83 | |||
| 84 | switch ($dbType) { | ||
| 85 | case 'nchar': | ||
| 86 | case 'ntext': | ||
| 87 | // Unicode data requires 2 bytes per character | ||
| 88 | $length /= 2; | ||
| 89 | break; | ||
| 90 | |||
| 91 | case 'nvarchar': | ||
| 92 | if ($length === -1) { | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | |||
| 96 | // Unicode data requires 2 bytes per character | ||
| 97 | $length /= 2; | ||
| 98 | break; | ||
| 99 | |||
| 100 | case 'varchar': | ||
| 101 | // TEXT type is returned as VARCHAR(MAX) with a length of -1 | ||
| 102 | if ($length === -1) { | ||
| 103 | $dbType = 'text'; | ||
| 104 | } | ||
| 105 | |||
| 106 | break; | ||
| 107 | |||
| 108 | case 'varbinary': | ||
| 109 | if ($length === -1) { | ||
| 110 | $dbType = 'blob'; | ||
| 111 | } | ||
| 112 | |||
| 113 | break; | ||
| 114 | } | ||
| 115 | |||
| 116 | if ($dbType === 'char' || $dbType === 'nchar' || $dbType === 'binary') { | ||
| 117 | $fixed = true; | ||
| 118 | } | ||
| 119 | |||
| 120 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
| 121 | |||
| 122 | $options = [ | ||
| 123 | 'fixed' => $fixed, | ||
| 124 | 'notnull' => (bool) $tableColumn['notnull'], | ||
| 125 | 'scale' => $scale, | ||
| 126 | 'precision' => $precision, | ||
| 127 | 'autoincrement' => (bool) $tableColumn['autoincrement'], | ||
| 128 | ]; | ||
| 129 | |||
| 130 | if (isset($tableColumn['comment'])) { | ||
| 131 | $options['comment'] = $tableColumn['comment']; | ||
| 132 | } | ||
| 133 | |||
| 134 | if ($length !== 0 && ($type === 'text' || $type === 'string' || $type === 'binary')) { | ||
| 135 | $options['length'] = $length; | ||
| 136 | } | ||
| 137 | |||
| 138 | $column = new Column($tableColumn['name'], Type::getType($type), $options); | ||
| 139 | |||
| 140 | if ($tableColumn['default'] !== null) { | ||
| 141 | $default = $this->parseDefaultExpression($tableColumn['default']); | ||
| 142 | |||
| 143 | $column->setDefault($default); | ||
| 144 | $column->setPlatformOption( | ||
| 145 | SQLServerPlatform::OPTION_DEFAULT_CONSTRAINT_NAME, | ||
| 146 | $tableColumn['df_name'], | ||
| 147 | ); | ||
| 148 | } | ||
| 149 | |||
| 150 | if (isset($tableColumn['collation']) && $tableColumn['collation'] !== 'NULL') { | ||
| 151 | $column->setPlatformOption('collation', $tableColumn['collation']); | ||
| 152 | } | ||
| 153 | |||
| 154 | return $column; | ||
| 155 | } | ||
| 156 | |||
| 157 | private function parseDefaultExpression(string $value): ?string | ||
| 158 | { | ||
| 159 | while (preg_match('/^\((.*)\)$/s', $value, $matches)) { | ||
| 160 | $value = $matches[1]; | ||
| 161 | } | ||
| 162 | |||
| 163 | if ($value === 'NULL') { | ||
| 164 | return null; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (preg_match('/^\'(.*)\'$/s', $value, $matches) === 1) { | ||
| 168 | $value = str_replace("''", "'", $matches[1]); | ||
| 169 | } | ||
| 170 | |||
| 171 | if ($value === 'getdate()') { | ||
| 172 | return $this->platform->getCurrentTimestampSQL(); | ||
| 173 | } | ||
| 174 | |||
| 175 | return $value; | ||
| 176 | } | ||
| 177 | |||
| 178 | /** | ||
| 179 | * {@inheritDoc} | ||
| 180 | */ | ||
| 181 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 182 | { | ||
| 183 | $foreignKeys = []; | ||
| 184 | |||
| 185 | foreach ($tableForeignKeys as $tableForeignKey) { | ||
| 186 | $name = $tableForeignKey['ForeignKey']; | ||
| 187 | |||
| 188 | if (! isset($foreignKeys[$name])) { | ||
| 189 | $foreignKeys[$name] = [ | ||
| 190 | 'local_columns' => [$tableForeignKey['ColumnName']], | ||
| 191 | 'foreign_table' => $tableForeignKey['ReferenceTableName'], | ||
| 192 | 'foreign_columns' => [$tableForeignKey['ReferenceColumnName']], | ||
| 193 | 'name' => $name, | ||
| 194 | 'options' => [ | ||
| 195 | 'onUpdate' => str_replace('_', ' ', $tableForeignKey['update_referential_action_desc']), | ||
| 196 | 'onDelete' => str_replace('_', ' ', $tableForeignKey['delete_referential_action_desc']), | ||
| 197 | ], | ||
| 198 | ]; | ||
| 199 | } else { | ||
| 200 | $foreignKeys[$name]['local_columns'][] = $tableForeignKey['ColumnName']; | ||
| 201 | $foreignKeys[$name]['foreign_columns'][] = $tableForeignKey['ReferenceColumnName']; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | return parent::_getPortableTableForeignKeysList($foreignKeys); | ||
| 206 | } | ||
| 207 | |||
| 208 | /** | ||
| 209 | * {@inheritDoc} | ||
| 210 | */ | ||
| 211 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 212 | { | ||
| 213 | foreach ($tableIndexes as &$tableIndex) { | ||
| 214 | $tableIndex['non_unique'] = (bool) $tableIndex['non_unique']; | ||
| 215 | $tableIndex['primary'] = (bool) $tableIndex['primary']; | ||
| 216 | $tableIndex['flags'] = $tableIndex['flags'] ? [$tableIndex['flags']] : null; | ||
| 217 | } | ||
| 218 | |||
| 219 | return parent::_getPortableTableIndexesList($tableIndexes, $tableName); | ||
| 220 | } | ||
| 221 | |||
| 222 | /** | ||
| 223 | * {@inheritDoc} | ||
| 224 | */ | ||
| 225 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 226 | { | ||
| 227 | return new ForeignKeyConstraint( | ||
| 228 | $tableForeignKey['local_columns'], | ||
| 229 | $tableForeignKey['foreign_table'], | ||
| 230 | $tableForeignKey['foreign_columns'], | ||
| 231 | $tableForeignKey['name'], | ||
| 232 | $tableForeignKey['options'], | ||
| 233 | ); | ||
| 234 | } | ||
| 235 | |||
| 236 | /** | ||
| 237 | * {@inheritDoc} | ||
| 238 | */ | ||
| 239 | protected function _getPortableTableDefinition(array $table): string | ||
| 240 | { | ||
| 241 | if ($table['schema_name'] !== 'dbo') { | ||
| 242 | return $table['schema_name'] . '.' . $table['table_name']; | ||
| 243 | } | ||
| 244 | |||
| 245 | return $table['table_name']; | ||
| 246 | } | ||
| 247 | |||
| 248 | /** | ||
| 249 | * {@inheritDoc} | ||
| 250 | */ | ||
| 251 | protected function _getPortableDatabaseDefinition(array $database): string | ||
| 252 | { | ||
| 253 | return $database['name']; | ||
| 254 | } | ||
| 255 | |||
| 256 | /** | ||
| 257 | * {@inheritDoc} | ||
| 258 | */ | ||
| 259 | protected function _getPortableViewDefinition(array $view): View | ||
| 260 | { | ||
| 261 | return new View($view['name'], $view['definition']); | ||
| 262 | } | ||
| 263 | |||
| 264 | /** @throws Exception */ | ||
| 265 | public function createComparator(): Comparator | ||
| 266 | { | ||
| 267 | return new SQLServer\Comparator($this->platform, $this->getDatabaseCollation()); | ||
| 268 | } | ||
| 269 | |||
| 270 | /** @throws Exception */ | ||
| 271 | private function getDatabaseCollation(): string | ||
| 272 | { | ||
| 273 | if ($this->databaseCollation === null) { | ||
| 274 | $databaseCollation = $this->connection->fetchOne( | ||
| 275 | 'SELECT collation_name FROM sys.databases WHERE name = ' | ||
| 276 | . $this->platform->getCurrentDatabaseExpression(), | ||
| 277 | ); | ||
| 278 | |||
| 279 | // a database is always selected, even if omitted in the connection parameters | ||
| 280 | assert(is_string($databaseCollation)); | ||
| 281 | |||
| 282 | $this->databaseCollation = $databaseCollation; | ||
| 283 | } | ||
| 284 | |||
| 285 | return $this->databaseCollation; | ||
| 286 | } | ||
| 287 | |||
| 288 | protected function selectTableNames(string $databaseName): Result | ||
| 289 | { | ||
| 290 | // The "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams | ||
| 291 | $sql = <<<'SQL' | ||
| 292 | SELECT name AS table_name, | ||
| 293 | SCHEMA_NAME(schema_id) AS schema_name | ||
| 294 | FROM sys.objects | ||
| 295 | WHERE type = 'U' | ||
| 296 | AND name != 'sysdiagrams' | ||
| 297 | ORDER BY name | ||
| 298 | SQL; | ||
| 299 | |||
| 300 | return $this->connection->executeQuery($sql); | ||
| 301 | } | ||
| 302 | |||
| 303 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 304 | { | ||
| 305 | $sql = 'SELECT'; | ||
| 306 | |||
| 307 | if ($tableName === null) { | ||
| 308 | $sql .= ' obj.name AS table_name, scm.name AS schema_name,'; | ||
| 309 | } | ||
| 310 | |||
| 311 | $sql .= <<<'SQL' | ||
| 312 | col.name, | ||
| 313 | type.name AS type, | ||
| 314 | col.max_length AS length, | ||
| 315 | ~col.is_nullable AS notnull, | ||
| 316 | def.definition AS [default], | ||
| 317 | def.name AS df_name, | ||
| 318 | col.scale, | ||
| 319 | col.precision, | ||
| 320 | col.is_identity AS autoincrement, | ||
| 321 | col.collation_name AS collation, | ||
| 322 | -- CAST avoids driver error for sql_variant type | ||
| 323 | CAST(prop.value AS NVARCHAR(MAX)) AS comment | ||
| 324 | FROM sys.columns AS col | ||
| 325 | JOIN sys.types AS type | ||
| 326 | ON col.user_type_id = type.user_type_id | ||
| 327 | JOIN sys.objects AS obj | ||
| 328 | ON col.object_id = obj.object_id | ||
| 329 | JOIN sys.schemas AS scm | ||
| 330 | ON obj.schema_id = scm.schema_id | ||
| 331 | LEFT JOIN sys.default_constraints def | ||
| 332 | ON col.default_object_id = def.object_id | ||
| 333 | AND col.object_id = def.parent_object_id | ||
| 334 | LEFT JOIN sys.extended_properties AS prop | ||
| 335 | ON obj.object_id = prop.major_id | ||
| 336 | AND col.column_id = prop.minor_id | ||
| 337 | AND prop.name = 'MS_Description' | ||
| 338 | SQL; | ||
| 339 | |||
| 340 | // The "sysdiagrams" table must be ignored as it's internal SQL Server table for Database Diagrams | ||
| 341 | $conditions = ["obj.type = 'U'", "obj.name != 'sysdiagrams'"]; | ||
| 342 | $params = []; | ||
| 343 | |||
| 344 | if ($tableName !== null) { | ||
| 345 | $conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'obj.name'); | ||
| 346 | } | ||
| 347 | |||
| 348 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 349 | |||
| 350 | return $this->connection->executeQuery($sql, $params); | ||
| 351 | } | ||
| 352 | |||
| 353 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 354 | { | ||
| 355 | $sql = 'SELECT'; | ||
| 356 | |||
| 357 | if ($tableName === null) { | ||
| 358 | $sql .= ' tbl.name AS table_name, scm.name AS schema_name,'; | ||
| 359 | } | ||
| 360 | |||
| 361 | $sql .= <<<'SQL' | ||
| 362 | idx.name AS key_name, | ||
| 363 | col.name AS column_name, | ||
| 364 | ~idx.is_unique AS non_unique, | ||
| 365 | idx.is_primary_key AS [primary], | ||
| 366 | CASE idx.type | ||
| 367 | WHEN '1' THEN 'clustered' | ||
| 368 | WHEN '2' THEN 'nonclustered' | ||
| 369 | ELSE NULL | ||
| 370 | END AS flags | ||
| 371 | FROM sys.tables AS tbl | ||
| 372 | JOIN sys.schemas AS scm | ||
| 373 | ON tbl.schema_id = scm.schema_id | ||
| 374 | JOIN sys.indexes AS idx | ||
| 375 | ON tbl.object_id = idx.object_id | ||
| 376 | JOIN sys.index_columns AS idxcol | ||
| 377 | ON idx.object_id = idxcol.object_id | ||
| 378 | AND idx.index_id = idxcol.index_id | ||
| 379 | JOIN sys.columns AS col | ||
| 380 | ON idxcol.object_id = col.object_id | ||
| 381 | AND idxcol.column_id = col.column_id | ||
| 382 | SQL; | ||
| 383 | |||
| 384 | $conditions = []; | ||
| 385 | $params = []; | ||
| 386 | |||
| 387 | if ($tableName !== null) { | ||
| 388 | $conditions[] = $this->getTableWhereClause($tableName, 'scm.name', 'tbl.name'); | ||
| 389 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 390 | } | ||
| 391 | |||
| 392 | $sql .= ' ORDER BY idx.index_id, idxcol.key_ordinal'; | ||
| 393 | |||
| 394 | return $this->connection->executeQuery($sql, $params); | ||
| 395 | } | ||
| 396 | |||
| 397 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 398 | { | ||
| 399 | $sql = 'SELECT'; | ||
| 400 | |||
| 401 | if ($tableName === null) { | ||
| 402 | $sql .= ' OBJECT_NAME (f.parent_object_id) AS table_name, SCHEMA_NAME(f.schema_id) AS schema_name,'; | ||
| 403 | } | ||
| 404 | |||
| 405 | $sql .= <<<'SQL' | ||
| 406 | f.name AS ForeignKey, | ||
| 407 | SCHEMA_NAME (f.SCHEMA_ID) AS SchemaName, | ||
| 408 | OBJECT_NAME (f.parent_object_id) AS TableName, | ||
| 409 | COL_NAME (fc.parent_object_id,fc.parent_column_id) AS ColumnName, | ||
| 410 | SCHEMA_NAME (o.SCHEMA_ID) ReferenceSchemaName, | ||
| 411 | OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, | ||
| 412 | COL_NAME(fc.referenced_object_id,fc.referenced_column_id) AS ReferenceColumnName, | ||
| 413 | f.delete_referential_action_desc, | ||
| 414 | f.update_referential_action_desc | ||
| 415 | FROM sys.foreign_keys AS f | ||
| 416 | INNER JOIN sys.foreign_key_columns AS fc | ||
| 417 | INNER JOIN sys.objects AS o ON o.OBJECT_ID = fc.referenced_object_id | ||
| 418 | ON f.OBJECT_ID = fc.constraint_object_id | ||
| 419 | SQL; | ||
| 420 | |||
| 421 | $conditions = []; | ||
| 422 | $params = []; | ||
| 423 | |||
| 424 | if ($tableName !== null) { | ||
| 425 | $conditions[] = $this->getTableWhereClause( | ||
| 426 | $tableName, | ||
| 427 | 'SCHEMA_NAME(f.schema_id)', | ||
| 428 | 'OBJECT_NAME(f.parent_object_id)', | ||
| 429 | ); | ||
| 430 | |||
| 431 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 432 | } | ||
| 433 | |||
| 434 | $sql .= ' ORDER BY fc.constraint_column_id'; | ||
| 435 | |||
| 436 | return $this->connection->executeQuery($sql, $params); | ||
| 437 | } | ||
| 438 | |||
| 439 | /** | ||
| 440 | * {@inheritDoc} | ||
| 441 | */ | ||
| 442 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 443 | { | ||
| 444 | $sql = <<<'SQL' | ||
| 445 | SELECT | ||
| 446 | tbl.name, | ||
| 447 | p.value AS [table_comment] | ||
| 448 | FROM | ||
| 449 | sys.tables AS tbl | ||
| 450 | INNER JOIN sys.extended_properties AS p ON p.major_id=tbl.object_id AND p.minor_id=0 AND p.class=1 | ||
| 451 | SQL; | ||
| 452 | |||
| 453 | $conditions = ["SCHEMA_NAME(tbl.schema_id) = N'dbo'", "p.name = N'MS_Description'"]; | ||
| 454 | $params = []; | ||
| 455 | |||
| 456 | if ($tableName !== null) { | ||
| 457 | $conditions[] = "tbl.name = N'" . $tableName . "'"; | ||
| 458 | } | ||
| 459 | |||
| 460 | $sql .= ' WHERE ' . implode(' AND ', $conditions); | ||
| 461 | |||
| 462 | /** @var array<string,array<string,mixed>> $metadata */ | ||
| 463 | $metadata = $this->connection->executeQuery($sql, $params) | ||
| 464 | ->fetchAllAssociativeIndexed(); | ||
| 465 | |||
| 466 | $tableOptions = []; | ||
| 467 | foreach ($metadata as $table => $data) { | ||
| 468 | $data = array_change_key_case($data, CASE_LOWER); | ||
| 469 | |||
| 470 | $tableOptions[$table] = [ | ||
| 471 | 'comment' => $data['table_comment'], | ||
| 472 | ]; | ||
| 473 | } | ||
| 474 | |||
| 475 | return $tableOptions; | ||
| 476 | } | ||
| 477 | |||
| 478 | /** | ||
| 479 | * Returns the where clause to filter schema and table name in a query. | ||
| 480 | * | ||
| 481 | * @param string $table The full qualified name of the table. | ||
| 482 | * @param string $schemaColumn The name of the column to compare the schema to in the where clause. | ||
| 483 | * @param string $tableColumn The name of the column to compare the table to in the where clause. | ||
| 484 | */ | ||
| 485 | private function getTableWhereClause(string $table, string $schemaColumn, string $tableColumn): string | ||
| 486 | { | ||
| 487 | if (str_contains($table, '.')) { | ||
| 488 | [$schema, $table] = explode('.', $table); | ||
| 489 | $schema = $this->platform->quoteStringLiteral($schema); | ||
| 490 | $table = $this->platform->quoteStringLiteral($table); | ||
| 491 | } else { | ||
| 492 | $schema = 'SCHEMA_NAME()'; | ||
| 493 | $table = $this->platform->quoteStringLiteral($table); | ||
| 494 | } | ||
| 495 | |||
| 496 | return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); | ||
| 497 | } | ||
| 498 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/SQLiteSchemaManager.php b/vendor/doctrine/dbal/src/Schema/SQLiteSchemaManager.php new file mode 100644 index 0000000..c001c25 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/SQLiteSchemaManager.php | |||
| @@ -0,0 +1,620 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Platforms\SQLite; | ||
| 9 | use Doctrine\DBAL\Platforms\SQLitePlatform; | ||
| 10 | use Doctrine\DBAL\Result; | ||
| 11 | use Doctrine\DBAL\Types\StringType; | ||
| 12 | use Doctrine\DBAL\Types\TextType; | ||
| 13 | use Doctrine\DBAL\Types\Type; | ||
| 14 | |||
| 15 | use function array_change_key_case; | ||
| 16 | use function array_merge; | ||
| 17 | use function assert; | ||
| 18 | use function count; | ||
| 19 | use function implode; | ||
| 20 | use function is_string; | ||
| 21 | use function preg_match; | ||
| 22 | use function preg_match_all; | ||
| 23 | use function preg_quote; | ||
| 24 | use function preg_replace; | ||
| 25 | use function rtrim; | ||
| 26 | use function str_contains; | ||
| 27 | use function str_replace; | ||
| 28 | use function str_starts_with; | ||
| 29 | use function strcasecmp; | ||
| 30 | use function strtolower; | ||
| 31 | use function trim; | ||
| 32 | use function usort; | ||
| 33 | |||
| 34 | use const CASE_LOWER; | ||
| 35 | |||
| 36 | /** | ||
| 37 | * SQLite SchemaManager. | ||
| 38 | * | ||
| 39 | * @extends AbstractSchemaManager<SQLitePlatform> | ||
| 40 | */ | ||
| 41 | class SQLiteSchemaManager extends AbstractSchemaManager | ||
| 42 | { | ||
| 43 | /** | ||
| 44 | * {@inheritDoc} | ||
| 45 | */ | ||
| 46 | protected function fetchForeignKeyColumnsByTable(string $databaseName): array | ||
| 47 | { | ||
| 48 | $columnsByTable = parent::fetchForeignKeyColumnsByTable($databaseName); | ||
| 49 | |||
| 50 | if (count($columnsByTable) > 0) { | ||
| 51 | foreach ($columnsByTable as $table => $columns) { | ||
| 52 | $columnsByTable[$table] = $this->addDetailsToTableForeignKeyColumns($table, $columns); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | return $columnsByTable; | ||
| 57 | } | ||
| 58 | |||
| 59 | public function createForeignKey(ForeignKeyConstraint $foreignKey, string $table): void | ||
| 60 | { | ||
| 61 | $table = $this->introspectTable($table); | ||
| 62 | |||
| 63 | $this->alterTable(new TableDiff($table, [], [], [], [], [], [], [], [], [$foreignKey], [], [])); | ||
| 64 | } | ||
| 65 | |||
| 66 | public function dropForeignKey(string $name, string $table): void | ||
| 67 | { | ||
| 68 | $table = $this->introspectTable($table); | ||
| 69 | |||
| 70 | $foreignKey = $table->getForeignKey($name); | ||
| 71 | |||
| 72 | $this->alterTable(new TableDiff($table, [], [], [], [], [], [], [], [], [], [], [$foreignKey])); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * {@inheritDoc} | ||
| 77 | */ | ||
| 78 | public function listTableForeignKeys(string $table): array | ||
| 79 | { | ||
| 80 | $table = $this->normalizeName($table); | ||
| 81 | |||
| 82 | $columns = $this->selectForeignKeyColumns('main', $table) | ||
| 83 | ->fetchAllAssociative(); | ||
| 84 | |||
| 85 | if (count($columns) > 0) { | ||
| 86 | $columns = $this->addDetailsToTableForeignKeyColumns($table, $columns); | ||
| 87 | } | ||
| 88 | |||
| 89 | return $this->_getPortableTableForeignKeysList($columns); | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * {@inheritDoc} | ||
| 94 | */ | ||
| 95 | protected function _getPortableTableDefinition(array $table): string | ||
| 96 | { | ||
| 97 | return $table['table_name']; | ||
| 98 | } | ||
| 99 | |||
| 100 | /** | ||
| 101 | * {@inheritDoc} | ||
| 102 | * | ||
| 103 | * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html | ||
| 104 | */ | ||
| 105 | protected function _getPortableTableIndexesList(array $tableIndexes, string $tableName): array | ||
| 106 | { | ||
| 107 | $indexBuffer = []; | ||
| 108 | |||
| 109 | // fetch primary | ||
| 110 | $indexArray = $this->connection->fetchAllAssociative('SELECT * FROM PRAGMA_TABLE_INFO (?)', [$tableName]); | ||
| 111 | |||
| 112 | usort( | ||
| 113 | $indexArray, | ||
| 114 | /** | ||
| 115 | * @param array<string,mixed> $a | ||
| 116 | * @param array<string,mixed> $b | ||
| 117 | */ | ||
| 118 | static function (array $a, array $b): int { | ||
| 119 | if ($a['pk'] === $b['pk']) { | ||
| 120 | return $a['cid'] - $b['cid']; | ||
| 121 | } | ||
| 122 | |||
| 123 | return $a['pk'] - $b['pk']; | ||
| 124 | }, | ||
| 125 | ); | ||
| 126 | |||
| 127 | foreach ($indexArray as $indexColumnRow) { | ||
| 128 | if ($indexColumnRow['pk'] === 0 || $indexColumnRow['pk'] === '0') { | ||
| 129 | continue; | ||
| 130 | } | ||
| 131 | |||
| 132 | $indexBuffer[] = [ | ||
| 133 | 'key_name' => 'primary', | ||
| 134 | 'primary' => true, | ||
| 135 | 'non_unique' => false, | ||
| 136 | 'column_name' => $indexColumnRow['name'], | ||
| 137 | ]; | ||
| 138 | } | ||
| 139 | |||
| 140 | // fetch regular indexes | ||
| 141 | foreach ($tableIndexes as $tableIndex) { | ||
| 142 | // Ignore indexes with reserved names, e.g. autoindexes | ||
| 143 | if (str_starts_with($tableIndex['name'], 'sqlite_')) { | ||
| 144 | continue; | ||
| 145 | } | ||
| 146 | |||
| 147 | $keyName = $tableIndex['name']; | ||
| 148 | $idx = []; | ||
| 149 | $idx['key_name'] = $keyName; | ||
| 150 | $idx['primary'] = false; | ||
| 151 | $idx['non_unique'] = ! $tableIndex['unique']; | ||
| 152 | |||
| 153 | $indexArray = $this->connection->fetchAllAssociative('SELECT * FROM PRAGMA_INDEX_INFO (?)', [$keyName]); | ||
| 154 | |||
| 155 | foreach ($indexArray as $indexColumnRow) { | ||
| 156 | $idx['column_name'] = $indexColumnRow['name']; | ||
| 157 | $indexBuffer[] = $idx; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | return parent::_getPortableTableIndexesList($indexBuffer, $tableName); | ||
| 162 | } | ||
| 163 | |||
| 164 | /** | ||
| 165 | * {@inheritDoc} | ||
| 166 | */ | ||
| 167 | protected function _getPortableTableColumnList(string $table, string $database, array $tableColumns): array | ||
| 168 | { | ||
| 169 | $list = parent::_getPortableTableColumnList($table, $database, $tableColumns); | ||
| 170 | |||
| 171 | // find column with autoincrement | ||
| 172 | $autoincrementColumn = null; | ||
| 173 | $autoincrementCount = 0; | ||
| 174 | |||
| 175 | foreach ($tableColumns as $tableColumn) { | ||
| 176 | if ($tableColumn['pk'] === 0 || $tableColumn['pk'] === '0') { | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | |||
| 180 | $autoincrementCount++; | ||
| 181 | if ($autoincrementColumn !== null || strtolower($tableColumn['type']) !== 'integer') { | ||
| 182 | continue; | ||
| 183 | } | ||
| 184 | |||
| 185 | $autoincrementColumn = $tableColumn['name']; | ||
| 186 | } | ||
| 187 | |||
| 188 | if ($autoincrementCount === 1 && $autoincrementColumn !== null) { | ||
| 189 | foreach ($list as $column) { | ||
| 190 | if ($autoincrementColumn !== $column->getName()) { | ||
| 191 | continue; | ||
| 192 | } | ||
| 193 | |||
| 194 | $column->setAutoincrement(true); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | // inspect column collation and comments | ||
| 199 | $createSql = $this->getCreateTableSQL($table); | ||
| 200 | |||
| 201 | foreach ($list as $columnName => $column) { | ||
| 202 | $type = $column->getType(); | ||
| 203 | |||
| 204 | if ($type instanceof StringType || $type instanceof TextType) { | ||
| 205 | $column->setPlatformOption( | ||
| 206 | 'collation', | ||
| 207 | $this->parseColumnCollationFromSQL($columnName, $createSql) ?? 'BINARY', | ||
| 208 | ); | ||
| 209 | } | ||
| 210 | |||
| 211 | $comment = $this->parseColumnCommentFromSQL($columnName, $createSql); | ||
| 212 | |||
| 213 | $column->setComment($comment); | ||
| 214 | } | ||
| 215 | |||
| 216 | return $list; | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * {@inheritDoc} | ||
| 221 | */ | ||
| 222 | protected function _getPortableTableColumnDefinition(array $tableColumn): Column | ||
| 223 | { | ||
| 224 | preg_match('/^([^()]*)\\s*(\\(((\\d+)(,\\s*(\\d+))?)\\))?/', $tableColumn['type'], $matches); | ||
| 225 | |||
| 226 | $dbType = trim(strtolower($matches[1])); | ||
| 227 | |||
| 228 | $length = $precision = $unsigned = null; | ||
| 229 | $fixed = $unsigned = false; | ||
| 230 | $scale = 0; | ||
| 231 | |||
| 232 | if (count($matches) >= 6) { | ||
| 233 | $precision = (int) $matches[4]; | ||
| 234 | $scale = (int) $matches[6]; | ||
| 235 | } elseif (count($matches) >= 4) { | ||
| 236 | $length = (int) $matches[4]; | ||
| 237 | } | ||
| 238 | |||
| 239 | if (str_contains($dbType, ' unsigned')) { | ||
| 240 | $dbType = str_replace(' unsigned', '', $dbType); | ||
| 241 | $unsigned = true; | ||
| 242 | } | ||
| 243 | |||
| 244 | $type = $this->platform->getDoctrineTypeMapping($dbType); | ||
| 245 | $default = $tableColumn['dflt_value']; | ||
| 246 | if ($default === 'NULL') { | ||
| 247 | $default = null; | ||
| 248 | } | ||
| 249 | |||
| 250 | if ($default !== null) { | ||
| 251 | // SQLite returns the default value as a literal expression, so we need to parse it | ||
| 252 | if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { | ||
| 253 | $default = str_replace("''", "'", $matches[1]); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | $notnull = (bool) $tableColumn['notnull']; | ||
| 258 | |||
| 259 | if (! isset($tableColumn['name'])) { | ||
| 260 | $tableColumn['name'] = ''; | ||
| 261 | } | ||
| 262 | |||
| 263 | if ($dbType === 'char') { | ||
| 264 | $fixed = true; | ||
| 265 | } | ||
| 266 | |||
| 267 | $options = [ | ||
| 268 | 'length' => $length, | ||
| 269 | 'unsigned' => $unsigned, | ||
| 270 | 'fixed' => $fixed, | ||
| 271 | 'notnull' => $notnull, | ||
| 272 | 'default' => $default, | ||
| 273 | 'precision' => $precision, | ||
| 274 | 'scale' => $scale, | ||
| 275 | ]; | ||
| 276 | |||
| 277 | return new Column($tableColumn['name'], Type::getType($type), $options); | ||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * {@inheritDoc} | ||
| 282 | */ | ||
| 283 | protected function _getPortableViewDefinition(array $view): View | ||
| 284 | { | ||
| 285 | return new View($view['name'], $view['sql']); | ||
| 286 | } | ||
| 287 | |||
| 288 | /** | ||
| 289 | * {@inheritDoc} | ||
| 290 | */ | ||
| 291 | protected function _getPortableTableForeignKeysList(array $tableForeignKeys): array | ||
| 292 | { | ||
| 293 | $list = []; | ||
| 294 | foreach ($tableForeignKeys as $value) { | ||
| 295 | $value = array_change_key_case($value, CASE_LOWER); | ||
| 296 | $id = $value['id']; | ||
| 297 | if (! isset($list[$id])) { | ||
| 298 | if (! isset($value['on_delete']) || $value['on_delete'] === 'RESTRICT') { | ||
| 299 | $value['on_delete'] = null; | ||
| 300 | } | ||
| 301 | |||
| 302 | if (! isset($value['on_update']) || $value['on_update'] === 'RESTRICT') { | ||
| 303 | $value['on_update'] = null; | ||
| 304 | } | ||
| 305 | |||
| 306 | $list[$id] = [ | ||
| 307 | 'name' => $value['constraint_name'], | ||
| 308 | 'local' => [], | ||
| 309 | 'foreign' => [], | ||
| 310 | 'foreignTable' => $value['table'], | ||
| 311 | 'onDelete' => $value['on_delete'], | ||
| 312 | 'onUpdate' => $value['on_update'], | ||
| 313 | 'deferrable' => $value['deferrable'], | ||
| 314 | 'deferred' => $value['deferred'], | ||
| 315 | ]; | ||
| 316 | } | ||
| 317 | |||
| 318 | $list[$id]['local'][] = $value['from']; | ||
| 319 | |||
| 320 | if ($value['to'] === null) { | ||
| 321 | // Inferring a shorthand form for the foreign key constraint, where the "to" field is empty. | ||
| 322 | // @see https://www.sqlite.org/foreignkeys.html#fk_indexes. | ||
| 323 | $foreignTableIndexes = $this->_getPortableTableIndexesList([], $value['table']); | ||
| 324 | |||
| 325 | if (! isset($foreignTableIndexes['primary'])) { | ||
| 326 | continue; | ||
| 327 | } | ||
| 328 | |||
| 329 | $list[$id]['foreign'] = [...$list[$id]['foreign'], ...$foreignTableIndexes['primary']->getColumns()]; | ||
| 330 | |||
| 331 | continue; | ||
| 332 | } | ||
| 333 | |||
| 334 | $list[$id]['foreign'][] = $value['to']; | ||
| 335 | } | ||
| 336 | |||
| 337 | return parent::_getPortableTableForeignKeysList($list); | ||
| 338 | } | ||
| 339 | |||
| 340 | /** | ||
| 341 | * {@inheritDoc} | ||
| 342 | */ | ||
| 343 | protected function _getPortableTableForeignKeyDefinition(array $tableForeignKey): ForeignKeyConstraint | ||
| 344 | { | ||
| 345 | return new ForeignKeyConstraint( | ||
| 346 | $tableForeignKey['local'], | ||
| 347 | $tableForeignKey['foreignTable'], | ||
| 348 | $tableForeignKey['foreign'], | ||
| 349 | $tableForeignKey['name'], | ||
| 350 | [ | ||
| 351 | 'onDelete' => $tableForeignKey['onDelete'], | ||
| 352 | 'onUpdate' => $tableForeignKey['onUpdate'], | ||
| 353 | 'deferrable' => $tableForeignKey['deferrable'], | ||
| 354 | 'deferred' => $tableForeignKey['deferred'], | ||
| 355 | ], | ||
| 356 | ); | ||
| 357 | } | ||
| 358 | |||
| 359 | private function parseColumnCollationFromSQL(string $column, string $sql): ?string | ||
| 360 | { | ||
| 361 | $pattern = '{(?:\W' . preg_quote($column) . '\W|\W' | ||
| 362 | . preg_quote($this->platform->quoteSingleIdentifier($column)) | ||
| 363 | . '\W)[^,(]+(?:\([^()]+\)[^,]*)?(?:(?:DEFAULT|CHECK)\s*(?:\(.*?\))?[^,]*)*COLLATE\s+["\']?([^\s,"\')]+)}is'; | ||
| 364 | |||
| 365 | if (preg_match($pattern, $sql, $match) !== 1) { | ||
| 366 | return null; | ||
| 367 | } | ||
| 368 | |||
| 369 | return $match[1]; | ||
| 370 | } | ||
| 371 | |||
| 372 | private function parseTableCommentFromSQL(string $table, string $sql): ?string | ||
| 373 | { | ||
| 374 | $pattern = '/\s* # Allow whitespace characters at start of line | ||
| 375 | CREATE\sTABLE # Match "CREATE TABLE" | ||
| 376 | (?:\W"' . preg_quote($this->platform->quoteSingleIdentifier($table), '/') . '"\W|\W' . preg_quote($table, '/') | ||
| 377 | . '\W) # Match table name (quoted and unquoted) | ||
| 378 | ( # Start capture | ||
| 379 | (?:\s*--[^\n]*\n?)+ # Capture anything that starts with whitespaces followed by -- until the end of the line(s) | ||
| 380 | )/ix'; | ||
| 381 | |||
| 382 | if (preg_match($pattern, $sql, $match) !== 1) { | ||
| 383 | return null; | ||
| 384 | } | ||
| 385 | |||
| 386 | $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n")); | ||
| 387 | |||
| 388 | return $comment === '' ? null : $comment; | ||
| 389 | } | ||
| 390 | |||
| 391 | private function parseColumnCommentFromSQL(string $column, string $sql): string | ||
| 392 | { | ||
| 393 | $pattern = '{[\s(,](?:\W' . preg_quote($this->platform->quoteSingleIdentifier($column)) | ||
| 394 | . '\W|\W' . preg_quote($column) . '\W)(?:\([^)]*?\)|[^,(])*?,?((?:(?!\n))(?:\s*--[^\n]*\n?)+)}i'; | ||
| 395 | |||
| 396 | if (preg_match($pattern, $sql, $match) !== 1) { | ||
| 397 | return ''; | ||
| 398 | } | ||
| 399 | |||
| 400 | $comment = preg_replace('{^\s*--}m', '', rtrim($match[1], "\n")); | ||
| 401 | assert(is_string($comment)); | ||
| 402 | |||
| 403 | return $comment; | ||
| 404 | } | ||
| 405 | |||
| 406 | /** @throws Exception */ | ||
| 407 | private function getCreateTableSQL(string $table): string | ||
| 408 | { | ||
| 409 | $sql = $this->connection->fetchOne( | ||
| 410 | <<<'SQL' | ||
| 411 | SELECT sql | ||
| 412 | FROM ( | ||
| 413 | SELECT * | ||
| 414 | FROM sqlite_master | ||
| 415 | UNION ALL | ||
| 416 | SELECT * | ||
| 417 | FROM sqlite_temp_master | ||
| 418 | ) | ||
| 419 | WHERE type = 'table' | ||
| 420 | AND name = ? | ||
| 421 | SQL | ||
| 422 | , | ||
| 423 | [$table], | ||
| 424 | ); | ||
| 425 | |||
| 426 | if ($sql !== false) { | ||
| 427 | return $sql; | ||
| 428 | } | ||
| 429 | |||
| 430 | return ''; | ||
| 431 | } | ||
| 432 | |||
| 433 | /** | ||
| 434 | * @param list<array<string,mixed>> $columns | ||
| 435 | * | ||
| 436 | * @return list<array<string,mixed>> | ||
| 437 | * | ||
| 438 | * @throws Exception | ||
| 439 | */ | ||
| 440 | private function addDetailsToTableForeignKeyColumns(string $table, array $columns): array | ||
| 441 | { | ||
| 442 | $foreignKeyDetails = $this->getForeignKeyDetails($table); | ||
| 443 | $foreignKeyCount = count($foreignKeyDetails); | ||
| 444 | |||
| 445 | foreach ($columns as $i => $column) { | ||
| 446 | // SQLite identifies foreign keys in reverse order of appearance in SQL | ||
| 447 | $columns[$i] = array_merge($column, $foreignKeyDetails[$foreignKeyCount - $column['id'] - 1]); | ||
| 448 | } | ||
| 449 | |||
| 450 | return $columns; | ||
| 451 | } | ||
| 452 | |||
| 453 | /** | ||
| 454 | * @return list<array<string, mixed>> | ||
| 455 | * | ||
| 456 | * @throws Exception | ||
| 457 | */ | ||
| 458 | private function getForeignKeyDetails(string $table): array | ||
| 459 | { | ||
| 460 | $createSql = $this->getCreateTableSQL($table); | ||
| 461 | |||
| 462 | if ( | ||
| 463 | preg_match_all( | ||
| 464 | '# | ||
| 465 | (?:CONSTRAINT\s+(\S+)\s+)? | ||
| 466 | (?:FOREIGN\s+KEY[^)]+\)\s*)? | ||
| 467 | REFERENCES\s+\S+\s*(?:\([^)]+\))? | ||
| 468 | (?: | ||
| 469 | [^,]*? | ||
| 470 | (NOT\s+DEFERRABLE|DEFERRABLE) | ||
| 471 | (?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))? | ||
| 472 | )?#isx', | ||
| 473 | $createSql, | ||
| 474 | $match, | ||
| 475 | ) === 0 | ||
| 476 | ) { | ||
| 477 | return []; | ||
| 478 | } | ||
| 479 | |||
| 480 | $names = $match[1]; | ||
| 481 | $deferrable = $match[2]; | ||
| 482 | $deferred = $match[3]; | ||
| 483 | $details = []; | ||
| 484 | |||
| 485 | for ($i = 0, $count = count($match[0]); $i < $count; $i++) { | ||
| 486 | $details[] = [ | ||
| 487 | 'constraint_name' => $names[$i] ?? '', | ||
| 488 | 'deferrable' => isset($deferrable[$i]) && strcasecmp($deferrable[$i], 'deferrable') === 0, | ||
| 489 | 'deferred' => isset($deferred[$i]) && strcasecmp($deferred[$i], 'deferred') === 0, | ||
| 490 | ]; | ||
| 491 | } | ||
| 492 | |||
| 493 | return $details; | ||
| 494 | } | ||
| 495 | |||
| 496 | public function createComparator(): Comparator | ||
| 497 | { | ||
| 498 | return new SQLite\Comparator($this->platform); | ||
| 499 | } | ||
| 500 | |||
| 501 | protected function selectTableNames(string $databaseName): Result | ||
| 502 | { | ||
| 503 | $sql = <<<'SQL' | ||
| 504 | SELECT name AS table_name | ||
| 505 | FROM sqlite_master | ||
| 506 | WHERE type = 'table' | ||
| 507 | AND name != 'sqlite_sequence' | ||
| 508 | AND name != 'geometry_columns' | ||
| 509 | AND name != 'spatial_ref_sys' | ||
| 510 | UNION ALL | ||
| 511 | SELECT name | ||
| 512 | FROM sqlite_temp_master | ||
| 513 | WHERE type = 'table' | ||
| 514 | ORDER BY name | ||
| 515 | SQL; | ||
| 516 | |||
| 517 | return $this->connection->executeQuery($sql); | ||
| 518 | } | ||
| 519 | |||
| 520 | protected function selectTableColumns(string $databaseName, ?string $tableName = null): Result | ||
| 521 | { | ||
| 522 | $sql = <<<'SQL' | ||
| 523 | SELECT t.name AS table_name, | ||
| 524 | c.* | ||
| 525 | FROM sqlite_master t | ||
| 526 | JOIN pragma_table_info(t.name) c | ||
| 527 | SQL; | ||
| 528 | |||
| 529 | $conditions = [ | ||
| 530 | "t.type = 'table'", | ||
| 531 | "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", | ||
| 532 | ]; | ||
| 533 | $params = []; | ||
| 534 | |||
| 535 | if ($tableName !== null) { | ||
| 536 | $conditions[] = 't.name = ?'; | ||
| 537 | $params[] = str_replace('.', '__', $tableName); | ||
| 538 | } | ||
| 539 | |||
| 540 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, c.cid'; | ||
| 541 | |||
| 542 | return $this->connection->executeQuery($sql, $params); | ||
| 543 | } | ||
| 544 | |||
| 545 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null): Result | ||
| 546 | { | ||
| 547 | $sql = <<<'SQL' | ||
| 548 | SELECT t.name AS table_name, | ||
| 549 | i.* | ||
| 550 | FROM sqlite_master t | ||
| 551 | JOIN pragma_index_list(t.name) i | ||
| 552 | SQL; | ||
| 553 | |||
| 554 | $conditions = [ | ||
| 555 | "t.type = 'table'", | ||
| 556 | "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", | ||
| 557 | ]; | ||
| 558 | $params = []; | ||
| 559 | |||
| 560 | if ($tableName !== null) { | ||
| 561 | $conditions[] = 't.name = ?'; | ||
| 562 | $params[] = str_replace('.', '__', $tableName); | ||
| 563 | } | ||
| 564 | |||
| 565 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, i.seq'; | ||
| 566 | |||
| 567 | return $this->connection->executeQuery($sql, $params); | ||
| 568 | } | ||
| 569 | |||
| 570 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null): Result | ||
| 571 | { | ||
| 572 | $sql = <<<'SQL' | ||
| 573 | SELECT t.name AS table_name, | ||
| 574 | p.* | ||
| 575 | FROM sqlite_master t | ||
| 576 | JOIN pragma_foreign_key_list(t.name) p | ||
| 577 | ON p."seq" != '-1' | ||
| 578 | SQL; | ||
| 579 | |||
| 580 | $conditions = [ | ||
| 581 | "t.type = 'table'", | ||
| 582 | "t.name NOT IN ('geometry_columns', 'spatial_ref_sys', 'sqlite_sequence')", | ||
| 583 | ]; | ||
| 584 | $params = []; | ||
| 585 | |||
| 586 | if ($tableName !== null) { | ||
| 587 | $conditions[] = 't.name = ?'; | ||
| 588 | $params[] = str_replace('.', '__', $tableName); | ||
| 589 | } | ||
| 590 | |||
| 591 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY t.name, p.id DESC, p.seq'; | ||
| 592 | |||
| 593 | return $this->connection->executeQuery($sql, $params); | ||
| 594 | } | ||
| 595 | |||
| 596 | /** | ||
| 597 | * {@inheritDoc} | ||
| 598 | */ | ||
| 599 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null): array | ||
| 600 | { | ||
| 601 | if ($tableName === null) { | ||
| 602 | $tables = $this->listTableNames(); | ||
| 603 | } else { | ||
| 604 | $tables = [$tableName]; | ||
| 605 | } | ||
| 606 | |||
| 607 | $tableOptions = []; | ||
| 608 | foreach ($tables as $table) { | ||
| 609 | $comment = $this->parseTableCommentFromSQL($table, $this->getCreateTableSQL($table)); | ||
| 610 | |||
| 611 | if ($comment === null) { | ||
| 612 | continue; | ||
| 613 | } | ||
| 614 | |||
| 615 | $tableOptions[$table]['comment'] = $comment; | ||
| 616 | } | ||
| 617 | |||
| 618 | return $tableOptions; | ||
| 619 | } | ||
| 620 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Schema.php b/vendor/doctrine/dbal/src/Schema/Schema.php new file mode 100644 index 0000000..25fe4a3 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Schema.php | |||
| @@ -0,0 +1,374 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | use Doctrine\DBAL\Platforms\AbstractPlatform; | ||
| 9 | use Doctrine\DBAL\Schema\Exception\NamespaceAlreadyExists; | ||
| 10 | use Doctrine\DBAL\Schema\Exception\SequenceAlreadyExists; | ||
| 11 | use Doctrine\DBAL\Schema\Exception\SequenceDoesNotExist; | ||
| 12 | use Doctrine\DBAL\Schema\Exception\TableAlreadyExists; | ||
| 13 | use Doctrine\DBAL\Schema\Exception\TableDoesNotExist; | ||
| 14 | use Doctrine\DBAL\SQL\Builder\CreateSchemaObjectsSQLBuilder; | ||
| 15 | use Doctrine\DBAL\SQL\Builder\DropSchemaObjectsSQLBuilder; | ||
| 16 | |||
| 17 | use function array_values; | ||
| 18 | use function str_contains; | ||
| 19 | use function strtolower; | ||
| 20 | |||
| 21 | /** | ||
| 22 | * Object representation of a database schema. | ||
| 23 | * | ||
| 24 | * Different vendors have very inconsistent naming with regard to the concept | ||
| 25 | * of a "schema". Doctrine understands a schema as the entity that conceptually | ||
| 26 | * wraps a set of database objects such as tables, sequences, indexes and | ||
| 27 | * foreign keys that belong to each other into a namespace. A Doctrine Schema | ||
| 28 | * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more | ||
| 29 | * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL. | ||
| 30 | * | ||
| 31 | * Every asset in the doctrine schema has a name. A name consists of either a | ||
| 32 | * namespace.local name pair or just a local unqualified name. | ||
| 33 | * | ||
| 34 | * The abstraction layer that covers a PostgreSQL schema is the namespace of an | ||
| 35 | * database object (asset). A schema can have a name, which will be used as | ||
| 36 | * default namespace for the unqualified database objects that are created in | ||
| 37 | * the schema. | ||
| 38 | * | ||
| 39 | * In the case of MySQL where cross-database queries are allowed this leads to | ||
| 40 | * databases being "misinterpreted" as namespaces. This is intentional, however | ||
| 41 | * the CREATE/DROP SQL visitors will just filter this queries and do not | ||
| 42 | * execute them. Only the queries for the currently connected database are | ||
| 43 | * executed. | ||
| 44 | */ | ||
| 45 | class Schema extends AbstractAsset | ||
| 46 | { | ||
| 47 | /** | ||
| 48 | * The namespaces in this schema. | ||
| 49 | * | ||
| 50 | * @var array<string, string> | ||
| 51 | */ | ||
| 52 | private array $namespaces = []; | ||
| 53 | |||
| 54 | /** @var array<string, Table> */ | ||
| 55 | protected array $_tables = []; | ||
| 56 | |||
| 57 | /** @var array<string, Sequence> */ | ||
| 58 | protected array $_sequences = []; | ||
| 59 | |||
| 60 | protected SchemaConfig $_schemaConfig; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * @param array<Table> $tables | ||
| 64 | * @param array<Sequence> $sequences | ||
| 65 | * @param array<string> $namespaces | ||
| 66 | */ | ||
| 67 | public function __construct( | ||
| 68 | array $tables = [], | ||
| 69 | array $sequences = [], | ||
| 70 | ?SchemaConfig $schemaConfig = null, | ||
| 71 | array $namespaces = [], | ||
| 72 | ) { | ||
| 73 | $schemaConfig ??= new SchemaConfig(); | ||
| 74 | |||
| 75 | $this->_schemaConfig = $schemaConfig; | ||
| 76 | |||
| 77 | $name = $schemaConfig->getName(); | ||
| 78 | |||
| 79 | if ($name !== null) { | ||
| 80 | $this->_setName($name); | ||
| 81 | } | ||
| 82 | |||
| 83 | foreach ($namespaces as $namespace) { | ||
| 84 | $this->createNamespace($namespace); | ||
| 85 | } | ||
| 86 | |||
| 87 | foreach ($tables as $table) { | ||
| 88 | $this->_addTable($table); | ||
| 89 | } | ||
| 90 | |||
| 91 | foreach ($sequences as $sequence) { | ||
| 92 | $this->_addSequence($sequence); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | protected function _addTable(Table $table): void | ||
| 97 | { | ||
| 98 | $namespaceName = $table->getNamespaceName(); | ||
| 99 | $tableName = $this->normalizeName($table); | ||
| 100 | |||
| 101 | if (isset($this->_tables[$tableName])) { | ||
| 102 | throw TableAlreadyExists::new($tableName); | ||
| 103 | } | ||
| 104 | |||
| 105 | if ( | ||
| 106 | $namespaceName !== null | ||
| 107 | && ! $table->isInDefaultNamespace($this->getName()) | ||
| 108 | && ! $this->hasNamespace($namespaceName) | ||
| 109 | ) { | ||
| 110 | $this->createNamespace($namespaceName); | ||
| 111 | } | ||
| 112 | |||
| 113 | $this->_tables[$tableName] = $table; | ||
| 114 | $table->setSchemaConfig($this->_schemaConfig); | ||
| 115 | } | ||
| 116 | |||
| 117 | protected function _addSequence(Sequence $sequence): void | ||
| 118 | { | ||
| 119 | $namespaceName = $sequence->getNamespaceName(); | ||
| 120 | $seqName = $this->normalizeName($sequence); | ||
| 121 | |||
| 122 | if (isset($this->_sequences[$seqName])) { | ||
| 123 | throw SequenceAlreadyExists::new($seqName); | ||
| 124 | } | ||
| 125 | |||
| 126 | if ( | ||
| 127 | $namespaceName !== null | ||
| 128 | && ! $sequence->isInDefaultNamespace($this->getName()) | ||
| 129 | && ! $this->hasNamespace($namespaceName) | ||
| 130 | ) { | ||
| 131 | $this->createNamespace($namespaceName); | ||
| 132 | } | ||
| 133 | |||
| 134 | $this->_sequences[$seqName] = $sequence; | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Returns the namespaces of this schema. | ||
| 139 | * | ||
| 140 | * @return list<string> A list of namespace names. | ||
| 141 | */ | ||
| 142 | public function getNamespaces(): array | ||
| 143 | { | ||
| 144 | return array_values($this->namespaces); | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Gets all tables of this schema. | ||
| 149 | * | ||
| 150 | * @return list<Table> | ||
| 151 | */ | ||
| 152 | public function getTables(): array | ||
| 153 | { | ||
| 154 | return array_values($this->_tables); | ||
| 155 | } | ||
| 156 | |||
| 157 | public function getTable(string $name): Table | ||
| 158 | { | ||
| 159 | $name = $this->getFullQualifiedAssetName($name); | ||
| 160 | if (! isset($this->_tables[$name])) { | ||
| 161 | throw TableDoesNotExist::new($name); | ||
| 162 | } | ||
| 163 | |||
| 164 | return $this->_tables[$name]; | ||
| 165 | } | ||
| 166 | |||
| 167 | private function getFullQualifiedAssetName(string $name): string | ||
| 168 | { | ||
| 169 | $name = $this->getUnquotedAssetName($name); | ||
| 170 | |||
| 171 | if (! str_contains($name, '.')) { | ||
| 172 | $name = $this->getName() . '.' . $name; | ||
| 173 | } | ||
| 174 | |||
| 175 | return strtolower($name); | ||
| 176 | } | ||
| 177 | |||
| 178 | /** | ||
| 179 | * The normalized name is qualified and lower-cased. Lower-casing is | ||
| 180 | * actually wrong, but we have to do it to keep our sanity. If you are | ||
| 181 | * using database objects that only differentiate in the casing (FOO vs | ||
| 182 | * Foo) then you will NOT be able to use Doctrine Schema abstraction. | ||
| 183 | * | ||
| 184 | * Every non-namespaced element is prefixed with this schema name. | ||
| 185 | */ | ||
| 186 | private function normalizeName(AbstractAsset $asset): string | ||
| 187 | { | ||
| 188 | $name = $asset->getName(); | ||
| 189 | |||
| 190 | if ($asset->getNamespaceName() === null) { | ||
| 191 | $name = $this->getName() . '.' . $name; | ||
| 192 | } | ||
| 193 | |||
| 194 | return strtolower($name); | ||
| 195 | } | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Returns the unquoted representation of a given asset name. | ||
| 199 | */ | ||
| 200 | private function getUnquotedAssetName(string $assetName): string | ||
| 201 | { | ||
| 202 | if ($this->isIdentifierQuoted($assetName)) { | ||
| 203 | return $this->trimQuotes($assetName); | ||
| 204 | } | ||
| 205 | |||
| 206 | return $assetName; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * Does this schema have a namespace with the given name? | ||
| 211 | */ | ||
| 212 | public function hasNamespace(string $name): bool | ||
| 213 | { | ||
| 214 | $name = strtolower($this->getUnquotedAssetName($name)); | ||
| 215 | |||
| 216 | return isset($this->namespaces[$name]); | ||
| 217 | } | ||
| 218 | |||
| 219 | /** | ||
| 220 | * Does this schema have a table with the given name? | ||
| 221 | */ | ||
| 222 | public function hasTable(string $name): bool | ||
| 223 | { | ||
| 224 | $name = $this->getFullQualifiedAssetName($name); | ||
| 225 | |||
| 226 | return isset($this->_tables[$name]); | ||
| 227 | } | ||
| 228 | |||
| 229 | public function hasSequence(string $name): bool | ||
| 230 | { | ||
| 231 | $name = $this->getFullQualifiedAssetName($name); | ||
| 232 | |||
| 233 | return isset($this->_sequences[$name]); | ||
| 234 | } | ||
| 235 | |||
| 236 | public function getSequence(string $name): Sequence | ||
| 237 | { | ||
| 238 | $name = $this->getFullQualifiedAssetName($name); | ||
| 239 | if (! $this->hasSequence($name)) { | ||
| 240 | throw SequenceDoesNotExist::new($name); | ||
| 241 | } | ||
| 242 | |||
| 243 | return $this->_sequences[$name]; | ||
| 244 | } | ||
| 245 | |||
| 246 | /** @return list<Sequence> */ | ||
| 247 | public function getSequences(): array | ||
| 248 | { | ||
| 249 | return array_values($this->_sequences); | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * Creates a new namespace. | ||
| 254 | * | ||
| 255 | * @return $this | ||
| 256 | */ | ||
| 257 | public function createNamespace(string $name): self | ||
| 258 | { | ||
| 259 | $unquotedName = strtolower($this->getUnquotedAssetName($name)); | ||
| 260 | |||
| 261 | if (isset($this->namespaces[$unquotedName])) { | ||
| 262 | throw NamespaceAlreadyExists::new($unquotedName); | ||
| 263 | } | ||
| 264 | |||
| 265 | $this->namespaces[$unquotedName] = $name; | ||
| 266 | |||
| 267 | return $this; | ||
| 268 | } | ||
| 269 | |||
| 270 | /** | ||
| 271 | * Creates a new table. | ||
| 272 | */ | ||
| 273 | public function createTable(string $name): Table | ||
| 274 | { | ||
| 275 | $table = new Table($name); | ||
| 276 | $this->_addTable($table); | ||
| 277 | |||
| 278 | foreach ($this->_schemaConfig->getDefaultTableOptions() as $option => $value) { | ||
| 279 | $table->addOption($option, $value); | ||
| 280 | } | ||
| 281 | |||
| 282 | return $table; | ||
| 283 | } | ||
| 284 | |||
| 285 | /** | ||
| 286 | * Renames a table. | ||
| 287 | * | ||
| 288 | * @return $this | ||
| 289 | */ | ||
| 290 | public function renameTable(string $oldName, string $newName): self | ||
| 291 | { | ||
| 292 | $table = $this->getTable($oldName); | ||
| 293 | $table->_setName($newName); | ||
| 294 | |||
| 295 | $this->dropTable($oldName); | ||
| 296 | $this->_addTable($table); | ||
| 297 | |||
| 298 | return $this; | ||
| 299 | } | ||
| 300 | |||
| 301 | /** | ||
| 302 | * Drops a table from the schema. | ||
| 303 | * | ||
| 304 | * @return $this | ||
| 305 | */ | ||
| 306 | public function dropTable(string $name): self | ||
| 307 | { | ||
| 308 | $name = $this->getFullQualifiedAssetName($name); | ||
| 309 | $this->getTable($name); | ||
| 310 | unset($this->_tables[$name]); | ||
| 311 | |||
| 312 | return $this; | ||
| 313 | } | ||
| 314 | |||
| 315 | /** | ||
| 316 | * Creates a new sequence. | ||
| 317 | */ | ||
| 318 | public function createSequence(string $name, int $allocationSize = 1, int $initialValue = 1): Sequence | ||
| 319 | { | ||
| 320 | $seq = new Sequence($name, $allocationSize, $initialValue); | ||
| 321 | $this->_addSequence($seq); | ||
| 322 | |||
| 323 | return $seq; | ||
| 324 | } | ||
| 325 | |||
| 326 | /** @return $this */ | ||
| 327 | public function dropSequence(string $name): self | ||
| 328 | { | ||
| 329 | $name = $this->getFullQualifiedAssetName($name); | ||
| 330 | unset($this->_sequences[$name]); | ||
| 331 | |||
| 332 | return $this; | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 336 | * Returns an array of necessary SQL queries to create the schema on the given platform. | ||
| 337 | * | ||
| 338 | * @return list<string> | ||
| 339 | * | ||
| 340 | * @throws Exception | ||
| 341 | */ | ||
| 342 | public function toSql(AbstractPlatform $platform): array | ||
| 343 | { | ||
| 344 | $builder = new CreateSchemaObjectsSQLBuilder($platform); | ||
| 345 | |||
| 346 | return $builder->buildSQL($this); | ||
| 347 | } | ||
| 348 | |||
| 349 | /** | ||
| 350 | * Return an array of necessary SQL queries to drop the schema on the given platform. | ||
| 351 | * | ||
| 352 | * @return list<string> | ||
| 353 | */ | ||
| 354 | public function toDropSql(AbstractPlatform $platform): array | ||
| 355 | { | ||
| 356 | $builder = new DropSchemaObjectsSQLBuilder($platform); | ||
| 357 | |||
| 358 | return $builder->buildSQL($this); | ||
| 359 | } | ||
| 360 | |||
| 361 | /** | ||
| 362 | * Cloning a Schema triggers a deep clone of all related assets. | ||
| 363 | */ | ||
| 364 | public function __clone() | ||
| 365 | { | ||
| 366 | foreach ($this->_tables as $k => $table) { | ||
| 367 | $this->_tables[$k] = clone $table; | ||
| 368 | } | ||
| 369 | |||
| 370 | foreach ($this->_sequences as $k => $sequence) { | ||
| 371 | $this->_sequences[$k] = clone $sequence; | ||
| 372 | } | ||
| 373 | } | ||
| 374 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/SchemaConfig.php b/vendor/doctrine/dbal/src/Schema/SchemaConfig.php new file mode 100644 index 0000000..86cc84f --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/SchemaConfig.php | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Configuration for a Schema. | ||
| 9 | */ | ||
| 10 | class SchemaConfig | ||
| 11 | { | ||
| 12 | protected int $maxIdentifierLength = 63; | ||
| 13 | |||
| 14 | protected ?string $name = null; | ||
| 15 | |||
| 16 | /** @var array<string, mixed> */ | ||
| 17 | protected array $defaultTableOptions = []; | ||
| 18 | |||
| 19 | public function setMaxIdentifierLength(int $length): void | ||
| 20 | { | ||
| 21 | $this->maxIdentifierLength = $length; | ||
| 22 | } | ||
| 23 | |||
| 24 | public function getMaxIdentifierLength(): int | ||
| 25 | { | ||
| 26 | return $this->maxIdentifierLength; | ||
| 27 | } | ||
| 28 | |||
| 29 | /** | ||
| 30 | * Gets the default namespace of schema objects. | ||
| 31 | */ | ||
| 32 | public function getName(): ?string | ||
| 33 | { | ||
| 34 | return $this->name; | ||
| 35 | } | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Sets the default namespace name of schema objects. | ||
| 39 | */ | ||
| 40 | public function setName(?string $name): void | ||
| 41 | { | ||
| 42 | $this->name = $name; | ||
| 43 | } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * Gets the default options that are passed to Table instances created with | ||
| 47 | * Schema#createTable(). | ||
| 48 | * | ||
| 49 | * @return array<string, mixed> | ||
| 50 | */ | ||
| 51 | public function getDefaultTableOptions(): array | ||
| 52 | { | ||
| 53 | return $this->defaultTableOptions; | ||
| 54 | } | ||
| 55 | |||
| 56 | /** @param array<string, mixed> $defaultTableOptions */ | ||
| 57 | public function setDefaultTableOptions(array $defaultTableOptions): void | ||
| 58 | { | ||
| 59 | $this->defaultTableOptions = $defaultTableOptions; | ||
| 60 | } | ||
| 61 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/SchemaDiff.php b/vendor/doctrine/dbal/src/Schema/SchemaDiff.php new file mode 100644 index 0000000..28c014d --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/SchemaDiff.php | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use function array_filter; | ||
| 8 | use function count; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Differences between two schemas. | ||
| 12 | */ | ||
| 13 | class SchemaDiff | ||
| 14 | { | ||
| 15 | /** @var array<TableDiff> */ | ||
| 16 | private readonly array $alteredTables; | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Constructs an SchemaDiff object. | ||
| 20 | * | ||
| 21 | * @internal The diff can be only instantiated by a {@see Comparator}. | ||
| 22 | * | ||
| 23 | * @param array<string> $createdSchemas | ||
| 24 | * @param array<string> $droppedSchemas | ||
| 25 | * @param array<Table> $createdTables | ||
| 26 | * @param array<TableDiff> $alteredTables | ||
| 27 | * @param array<Table> $droppedTables | ||
| 28 | * @param array<Sequence> $createdSequences | ||
| 29 | * @param array<Sequence> $alteredSequences | ||
| 30 | * @param array<Sequence> $droppedSequences | ||
| 31 | */ | ||
| 32 | public function __construct( | ||
| 33 | private readonly array $createdSchemas, | ||
| 34 | private readonly array $droppedSchemas, | ||
| 35 | private readonly array $createdTables, | ||
| 36 | array $alteredTables, | ||
| 37 | private readonly array $droppedTables, | ||
| 38 | private readonly array $createdSequences, | ||
| 39 | private readonly array $alteredSequences, | ||
| 40 | private readonly array $droppedSequences, | ||
| 41 | ) { | ||
| 42 | $this->alteredTables = array_filter($alteredTables, static function (TableDiff $diff): bool { | ||
| 43 | return ! $diff->isEmpty(); | ||
| 44 | }); | ||
| 45 | } | ||
| 46 | |||
| 47 | /** @return array<string> */ | ||
| 48 | public function getCreatedSchemas(): array | ||
| 49 | { | ||
| 50 | return $this->createdSchemas; | ||
| 51 | } | ||
| 52 | |||
| 53 | /** @return array<string> */ | ||
| 54 | public function getDroppedSchemas(): array | ||
| 55 | { | ||
| 56 | return $this->droppedSchemas; | ||
| 57 | } | ||
| 58 | |||
| 59 | /** @return array<Table> */ | ||
| 60 | public function getCreatedTables(): array | ||
| 61 | { | ||
| 62 | return $this->createdTables; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** @return array<TableDiff> */ | ||
| 66 | public function getAlteredTables(): array | ||
| 67 | { | ||
| 68 | return $this->alteredTables; | ||
| 69 | } | ||
| 70 | |||
| 71 | /** @return array<Table> */ | ||
| 72 | public function getDroppedTables(): array | ||
| 73 | { | ||
| 74 | return $this->droppedTables; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** @return array<Sequence> */ | ||
| 78 | public function getCreatedSequences(): array | ||
| 79 | { | ||
| 80 | return $this->createdSequences; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** @return array<Sequence> */ | ||
| 84 | public function getAlteredSequences(): array | ||
| 85 | { | ||
| 86 | return $this->alteredSequences; | ||
| 87 | } | ||
| 88 | |||
| 89 | /** @return array<Sequence> */ | ||
| 90 | public function getDroppedSequences(): array | ||
| 91 | { | ||
| 92 | return $this->droppedSequences; | ||
| 93 | } | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Returns whether the diff is empty (contains no changes). | ||
| 97 | */ | ||
| 98 | public function isEmpty(): bool | ||
| 99 | { | ||
| 100 | return count($this->createdSchemas) === 0 | ||
| 101 | && count($this->droppedSchemas) === 0 | ||
| 102 | && count($this->createdTables) === 0 | ||
| 103 | && count($this->alteredTables) === 0 | ||
| 104 | && count($this->droppedTables) === 0 | ||
| 105 | && count($this->createdSequences) === 0 | ||
| 106 | && count($this->alteredSequences) === 0 | ||
| 107 | && count($this->droppedSequences) === 0; | ||
| 108 | } | ||
| 109 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/SchemaException.php b/vendor/doctrine/dbal/src/Schema/SchemaException.php new file mode 100644 index 0000000..43dd2ad --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/SchemaException.php | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Exception; | ||
| 8 | |||
| 9 | /** @psalm-immutable */ | ||
| 10 | interface SchemaException extends Exception | ||
| 11 | { | ||
| 12 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/SchemaManagerFactory.php b/vendor/doctrine/dbal/src/Schema/SchemaManagerFactory.php new file mode 100644 index 0000000..37c32e0 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/SchemaManagerFactory.php | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Connection; | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Creates a schema manager for the given connection. | ||
| 11 | * | ||
| 12 | * This interface is an extension point for applications that need to override schema managers. | ||
| 13 | */ | ||
| 14 | interface SchemaManagerFactory | ||
| 15 | { | ||
| 16 | public function createSchemaManager(Connection $connection): AbstractSchemaManager; | ||
| 17 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Sequence.php b/vendor/doctrine/dbal/src/Schema/Sequence.php new file mode 100644 index 0000000..32a5e67 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Sequence.php | |||
| @@ -0,0 +1,98 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use function count; | ||
| 8 | use function sprintf; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Sequence structure. | ||
| 12 | */ | ||
| 13 | class Sequence extends AbstractAsset | ||
| 14 | { | ||
| 15 | protected int $allocationSize = 1; | ||
| 16 | |||
| 17 | protected int $initialValue = 1; | ||
| 18 | |||
| 19 | public function __construct( | ||
| 20 | string $name, | ||
| 21 | int $allocationSize = 1, | ||
| 22 | int $initialValue = 1, | ||
| 23 | protected ?int $cache = null, | ||
| 24 | ) { | ||
| 25 | $this->_setName($name); | ||
| 26 | $this->setAllocationSize($allocationSize); | ||
| 27 | $this->setInitialValue($initialValue); | ||
| 28 | } | ||
| 29 | |||
| 30 | public function getAllocationSize(): int | ||
| 31 | { | ||
| 32 | return $this->allocationSize; | ||
| 33 | } | ||
| 34 | |||
| 35 | public function getInitialValue(): int | ||
| 36 | { | ||
| 37 | return $this->initialValue; | ||
| 38 | } | ||
| 39 | |||
| 40 | public function getCache(): ?int | ||
| 41 | { | ||
| 42 | return $this->cache; | ||
| 43 | } | ||
| 44 | |||
| 45 | public function setAllocationSize(int $allocationSize): self | ||
| 46 | { | ||
| 47 | $this->allocationSize = $allocationSize; | ||
| 48 | |||
| 49 | return $this; | ||
| 50 | } | ||
| 51 | |||
| 52 | public function setInitialValue(int $initialValue): self | ||
| 53 | { | ||
| 54 | $this->initialValue = $initialValue; | ||
| 55 | |||
| 56 | return $this; | ||
| 57 | } | ||
| 58 | |||
| 59 | public function setCache(int $cache): self | ||
| 60 | { | ||
| 61 | $this->cache = $cache; | ||
| 62 | |||
| 63 | return $this; | ||
| 64 | } | ||
| 65 | |||
| 66 | /** | ||
| 67 | * Checks if this sequence is an autoincrement sequence for a given table. | ||
| 68 | * | ||
| 69 | * This is used inside the comparator to not report sequences as missing, | ||
| 70 | * when the "from" schema implicitly creates the sequences. | ||
| 71 | */ | ||
| 72 | public function isAutoIncrementsFor(Table $table): bool | ||
| 73 | { | ||
| 74 | $primaryKey = $table->getPrimaryKey(); | ||
| 75 | |||
| 76 | if ($primaryKey === null) { | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | |||
| 80 | $pkColumns = $primaryKey->getColumns(); | ||
| 81 | |||
| 82 | if (count($pkColumns) !== 1) { | ||
| 83 | return false; | ||
| 84 | } | ||
| 85 | |||
| 86 | $column = $table->getColumn($pkColumns[0]); | ||
| 87 | |||
| 88 | if (! $column->getAutoincrement()) { | ||
| 89 | return false; | ||
| 90 | } | ||
| 91 | |||
| 92 | $sequenceName = $this->getShortestName($table->getNamespaceName()); | ||
| 93 | $tableName = $table->getShortestName($table->getNamespaceName()); | ||
| 94 | $tableSequenceName = sprintf('%s_%s_seq', $tableName, $column->getShortestName($table->getNamespaceName())); | ||
| 95 | |||
| 96 | return $tableSequenceName === $sequenceName; | ||
| 97 | } | ||
| 98 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/Table.php b/vendor/doctrine/dbal/src/Schema/Table.php new file mode 100644 index 0000000..cc7f04d --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/Table.php | |||
| @@ -0,0 +1,753 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use Doctrine\DBAL\Schema\Exception\ColumnAlreadyExists; | ||
| 8 | use Doctrine\DBAL\Schema\Exception\ColumnDoesNotExist; | ||
| 9 | use Doctrine\DBAL\Schema\Exception\ForeignKeyDoesNotExist; | ||
| 10 | use Doctrine\DBAL\Schema\Exception\IndexAlreadyExists; | ||
| 11 | use Doctrine\DBAL\Schema\Exception\IndexDoesNotExist; | ||
| 12 | use Doctrine\DBAL\Schema\Exception\IndexNameInvalid; | ||
| 13 | use Doctrine\DBAL\Schema\Exception\InvalidTableName; | ||
| 14 | use Doctrine\DBAL\Schema\Exception\UniqueConstraintDoesNotExist; | ||
| 15 | use Doctrine\DBAL\Types\Type; | ||
| 16 | |||
| 17 | use function array_merge; | ||
| 18 | use function array_values; | ||
| 19 | use function in_array; | ||
| 20 | use function is_string; | ||
| 21 | use function preg_match; | ||
| 22 | use function strtolower; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Object Representation of a table. | ||
| 26 | */ | ||
| 27 | class Table extends AbstractAsset | ||
| 28 | { | ||
| 29 | /** @var Column[] */ | ||
| 30 | protected array $_columns = []; | ||
| 31 | |||
| 32 | /** @var Index[] */ | ||
| 33 | private array $implicitIndexes = []; | ||
| 34 | |||
| 35 | /** @var Index[] */ | ||
| 36 | protected array $_indexes = []; | ||
| 37 | |||
| 38 | protected ?string $_primaryKeyName = null; | ||
| 39 | |||
| 40 | /** @var UniqueConstraint[] */ | ||
| 41 | protected array $uniqueConstraints = []; | ||
| 42 | |||
| 43 | /** @var ForeignKeyConstraint[] */ | ||
| 44 | protected array $_fkConstraints = []; | ||
| 45 | |||
| 46 | /** @var mixed[] */ | ||
| 47 | protected array $_options = [ | ||
| 48 | 'create_options' => [], | ||
| 49 | ]; | ||
| 50 | |||
| 51 | protected ?SchemaConfig $_schemaConfig = null; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @param array<Column> $columns | ||
| 55 | * @param array<Index> $indexes | ||
| 56 | * @param array<UniqueConstraint> $uniqueConstraints | ||
| 57 | * @param array<ForeignKeyConstraint> $fkConstraints | ||
| 58 | * @param array<string, mixed> $options | ||
| 59 | */ | ||
| 60 | public function __construct( | ||
| 61 | string $name, | ||
| 62 | array $columns = [], | ||
| 63 | array $indexes = [], | ||
| 64 | array $uniqueConstraints = [], | ||
| 65 | array $fkConstraints = [], | ||
| 66 | array $options = [], | ||
| 67 | ) { | ||
| 68 | if ($name === '') { | ||
| 69 | throw InvalidTableName::new($name); | ||
| 70 | } | ||
| 71 | |||
| 72 | $this->_setName($name); | ||
| 73 | |||
| 74 | foreach ($columns as $column) { | ||
| 75 | $this->_addColumn($column); | ||
| 76 | } | ||
| 77 | |||
| 78 | foreach ($indexes as $idx) { | ||
| 79 | $this->_addIndex($idx); | ||
| 80 | } | ||
| 81 | |||
| 82 | foreach ($uniqueConstraints as $uniqueConstraint) { | ||
| 83 | $this->_addUniqueConstraint($uniqueConstraint); | ||
| 84 | } | ||
| 85 | |||
| 86 | foreach ($fkConstraints as $fkConstraint) { | ||
| 87 | $this->_addForeignKeyConstraint($fkConstraint); | ||
| 88 | } | ||
| 89 | |||
| 90 | $this->_options = array_merge($this->_options, $options); | ||
| 91 | } | ||
| 92 | |||
| 93 | public function setSchemaConfig(SchemaConfig $schemaConfig): void | ||
| 94 | { | ||
| 95 | $this->_schemaConfig = $schemaConfig; | ||
| 96 | } | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Sets the Primary Key. | ||
| 100 | * | ||
| 101 | * @param array<int, string> $columnNames | ||
| 102 | */ | ||
| 103 | public function setPrimaryKey(array $columnNames, ?string $indexName = null): self | ||
| 104 | { | ||
| 105 | if ($indexName === null) { | ||
| 106 | $indexName = 'primary'; | ||
| 107 | } | ||
| 108 | |||
| 109 | $this->_addIndex($this->_createIndex($columnNames, $indexName, true, true)); | ||
| 110 | |||
| 111 | foreach ($columnNames as $columnName) { | ||
| 112 | $column = $this->getColumn($columnName); | ||
| 113 | $column->setNotnull(true); | ||
| 114 | } | ||
| 115 | |||
| 116 | return $this; | ||
| 117 | } | ||
| 118 | |||
| 119 | /** | ||
| 120 | * @param array<int, string> $columnNames | ||
| 121 | * @param array<int, string> $flags | ||
| 122 | * @param array<string, mixed> $options | ||
| 123 | */ | ||
| 124 | public function addUniqueConstraint( | ||
| 125 | array $columnNames, | ||
| 126 | ?string $indexName = null, | ||
| 127 | array $flags = [], | ||
| 128 | array $options = [], | ||
| 129 | ): self { | ||
| 130 | $indexName ??= $this->_generateIdentifierName( | ||
| 131 | array_merge([$this->getName()], $columnNames), | ||
| 132 | 'uniq', | ||
| 133 | $this->_getMaxIdentifierLength(), | ||
| 134 | ); | ||
| 135 | |||
| 136 | return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options)); | ||
| 137 | } | ||
| 138 | |||
| 139 | /** | ||
| 140 | * @param array<int, string> $columnNames | ||
| 141 | * @param array<int, string> $flags | ||
| 142 | * @param array<string, mixed> $options | ||
| 143 | */ | ||
| 144 | public function addIndex( | ||
| 145 | array $columnNames, | ||
| 146 | ?string $indexName = null, | ||
| 147 | array $flags = [], | ||
| 148 | array $options = [], | ||
| 149 | ): self { | ||
| 150 | $indexName ??= $this->_generateIdentifierName( | ||
| 151 | array_merge([$this->getName()], $columnNames), | ||
| 152 | 'idx', | ||
| 153 | $this->_getMaxIdentifierLength(), | ||
| 154 | ); | ||
| 155 | |||
| 156 | return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options)); | ||
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 160 | * Drops the primary key from this table. | ||
| 161 | */ | ||
| 162 | public function dropPrimaryKey(): void | ||
| 163 | { | ||
| 164 | if ($this->_primaryKeyName === null) { | ||
| 165 | return; | ||
| 166 | } | ||
| 167 | |||
| 168 | $this->dropIndex($this->_primaryKeyName); | ||
| 169 | $this->_primaryKeyName = null; | ||
| 170 | } | ||
| 171 | |||
| 172 | /** | ||
| 173 | * Drops an index from this table. | ||
| 174 | */ | ||
| 175 | public function dropIndex(string $name): void | ||
| 176 | { | ||
| 177 | $name = $this->normalizeIdentifier($name); | ||
| 178 | |||
| 179 | if (! $this->hasIndex($name)) { | ||
| 180 | throw IndexDoesNotExist::new($name, $this->_name); | ||
| 181 | } | ||
| 182 | |||
| 183 | unset($this->_indexes[$name]); | ||
| 184 | } | ||
| 185 | |||
| 186 | /** | ||
| 187 | * @param array<int, string> $columnNames | ||
| 188 | * @param array<string, mixed> $options | ||
| 189 | */ | ||
| 190 | public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []): self | ||
| 191 | { | ||
| 192 | $indexName ??= $this->_generateIdentifierName( | ||
| 193 | array_merge([$this->getName()], $columnNames), | ||
| 194 | 'uniq', | ||
| 195 | $this->_getMaxIdentifierLength(), | ||
| 196 | ); | ||
| 197 | |||
| 198 | return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options)); | ||
| 199 | } | ||
| 200 | |||
| 201 | /** | ||
| 202 | * Renames an index. | ||
| 203 | * | ||
| 204 | * @param string $oldName The name of the index to rename from. | ||
| 205 | * @param string|null $newName The name of the index to rename to. If null is given, the index name | ||
| 206 | * will be auto-generated. | ||
| 207 | */ | ||
| 208 | public function renameIndex(string $oldName, ?string $newName = null): self | ||
| 209 | { | ||
| 210 | $oldName = $this->normalizeIdentifier($oldName); | ||
| 211 | $normalizedNewName = $this->normalizeIdentifier($newName); | ||
| 212 | |||
| 213 | if ($oldName === $normalizedNewName) { | ||
| 214 | return $this; | ||
| 215 | } | ||
| 216 | |||
| 217 | if (! $this->hasIndex($oldName)) { | ||
| 218 | throw IndexDoesNotExist::new($oldName, $this->_name); | ||
| 219 | } | ||
| 220 | |||
| 221 | if ($this->hasIndex($normalizedNewName)) { | ||
| 222 | throw IndexAlreadyExists::new($normalizedNewName, $this->_name); | ||
| 223 | } | ||
| 224 | |||
| 225 | $oldIndex = $this->_indexes[$oldName]; | ||
| 226 | |||
| 227 | if ($oldIndex->isPrimary()) { | ||
| 228 | $this->dropPrimaryKey(); | ||
| 229 | |||
| 230 | return $this->setPrimaryKey($oldIndex->getColumns(), $newName ?? null); | ||
| 231 | } | ||
| 232 | |||
| 233 | unset($this->_indexes[$oldName]); | ||
| 234 | |||
| 235 | if ($oldIndex->isUnique()) { | ||
| 236 | return $this->addUniqueIndex($oldIndex->getColumns(), $newName, $oldIndex->getOptions()); | ||
| 237 | } | ||
| 238 | |||
| 239 | return $this->addIndex($oldIndex->getColumns(), $newName, $oldIndex->getFlags(), $oldIndex->getOptions()); | ||
| 240 | } | ||
| 241 | |||
| 242 | /** | ||
| 243 | * Checks if an index begins in the order of the given columns. | ||
| 244 | * | ||
| 245 | * @param array<int, string> $columnNames | ||
| 246 | */ | ||
| 247 | public function columnsAreIndexed(array $columnNames): bool | ||
| 248 | { | ||
| 249 | foreach ($this->getIndexes() as $index) { | ||
| 250 | if ($index->spansColumns($columnNames)) { | ||
| 251 | return true; | ||
| 252 | } | ||
| 253 | } | ||
| 254 | |||
| 255 | return false; | ||
| 256 | } | ||
| 257 | |||
| 258 | /** @param array<string, mixed> $options */ | ||
| 259 | public function addColumn(string $name, string $typeName, array $options = []): Column | ||
| 260 | { | ||
| 261 | $column = new Column($name, Type::getType($typeName), $options); | ||
| 262 | |||
| 263 | $this->_addColumn($column); | ||
| 264 | |||
| 265 | return $column; | ||
| 266 | } | ||
| 267 | |||
| 268 | /** @param array<string, mixed> $options */ | ||
| 269 | public function modifyColumn(string $name, array $options): self | ||
| 270 | { | ||
| 271 | $column = $this->getColumn($name); | ||
| 272 | $column->setOptions($options); | ||
| 273 | |||
| 274 | return $this; | ||
| 275 | } | ||
| 276 | |||
| 277 | /** | ||
| 278 | * Drops a Column from the Table. | ||
| 279 | */ | ||
| 280 | public function dropColumn(string $name): self | ||
| 281 | { | ||
| 282 | $name = $this->normalizeIdentifier($name); | ||
| 283 | |||
| 284 | unset($this->_columns[$name]); | ||
| 285 | |||
| 286 | return $this; | ||
| 287 | } | ||
| 288 | |||
| 289 | /** | ||
| 290 | * Adds a foreign key constraint. | ||
| 291 | * | ||
| 292 | * Name is inferred from the local columns. | ||
| 293 | * | ||
| 294 | * @param array<int, string> $localColumnNames | ||
| 295 | * @param array<int, string> $foreignColumnNames | ||
| 296 | * @param array<string, mixed> $options | ||
| 297 | */ | ||
| 298 | public function addForeignKeyConstraint( | ||
| 299 | string $foreignTableName, | ||
| 300 | array $localColumnNames, | ||
| 301 | array $foreignColumnNames, | ||
| 302 | array $options = [], | ||
| 303 | ?string $name = null, | ||
| 304 | ): self { | ||
| 305 | $name ??= $this->_generateIdentifierName( | ||
| 306 | array_merge([$this->getName()], $localColumnNames), | ||
| 307 | 'fk', | ||
| 308 | $this->_getMaxIdentifierLength(), | ||
| 309 | ); | ||
| 310 | |||
| 311 | foreach ($localColumnNames as $columnName) { | ||
| 312 | if (! $this->hasColumn($columnName)) { | ||
| 313 | throw ColumnDoesNotExist::new($columnName, $this->_name); | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | $constraint = new ForeignKeyConstraint( | ||
| 318 | $localColumnNames, | ||
| 319 | $foreignTableName, | ||
| 320 | $foreignColumnNames, | ||
| 321 | $name, | ||
| 322 | $options, | ||
| 323 | ); | ||
| 324 | |||
| 325 | return $this->_addForeignKeyConstraint($constraint); | ||
| 326 | } | ||
| 327 | |||
| 328 | public function addOption(string $name, mixed $value): self | ||
| 329 | { | ||
| 330 | $this->_options[$name] = $value; | ||
| 331 | |||
| 332 | return $this; | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 336 | * Returns whether this table has a foreign key constraint with the given name. | ||
| 337 | */ | ||
| 338 | public function hasForeignKey(string $name): bool | ||
| 339 | { | ||
| 340 | $name = $this->normalizeIdentifier($name); | ||
| 341 | |||
| 342 | return isset($this->_fkConstraints[$name]); | ||
| 343 | } | ||
| 344 | |||
| 345 | /** | ||
| 346 | * Returns the foreign key constraint with the given name. | ||
| 347 | */ | ||
| 348 | public function getForeignKey(string $name): ForeignKeyConstraint | ||
| 349 | { | ||
| 350 | $name = $this->normalizeIdentifier($name); | ||
| 351 | |||
| 352 | if (! $this->hasForeignKey($name)) { | ||
| 353 | throw ForeignKeyDoesNotExist::new($name, $this->_name); | ||
| 354 | } | ||
| 355 | |||
| 356 | return $this->_fkConstraints[$name]; | ||
| 357 | } | ||
| 358 | |||
| 359 | /** | ||
| 360 | * Removes the foreign key constraint with the given name. | ||
| 361 | */ | ||
| 362 | public function removeForeignKey(string $name): void | ||
| 363 | { | ||
| 364 | $name = $this->normalizeIdentifier($name); | ||
| 365 | |||
| 366 | if (! $this->hasForeignKey($name)) { | ||
| 367 | throw ForeignKeyDoesNotExist::new($name, $this->_name); | ||
| 368 | } | ||
| 369 | |||
| 370 | unset($this->_fkConstraints[$name]); | ||
| 371 | } | ||
| 372 | |||
| 373 | /** | ||
| 374 | * Returns whether this table has a unique constraint with the given name. | ||
| 375 | */ | ||
| 376 | public function hasUniqueConstraint(string $name): bool | ||
| 377 | { | ||
| 378 | $name = $this->normalizeIdentifier($name); | ||
| 379 | |||
| 380 | return isset($this->uniqueConstraints[$name]); | ||
| 381 | } | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Returns the unique constraint with the given name. | ||
| 385 | */ | ||
| 386 | public function getUniqueConstraint(string $name): UniqueConstraint | ||
| 387 | { | ||
| 388 | $name = $this->normalizeIdentifier($name); | ||
| 389 | |||
| 390 | if (! $this->hasUniqueConstraint($name)) { | ||
| 391 | throw UniqueConstraintDoesNotExist::new($name, $this->_name); | ||
| 392 | } | ||
| 393 | |||
| 394 | return $this->uniqueConstraints[$name]; | ||
| 395 | } | ||
| 396 | |||
| 397 | /** | ||
| 398 | * Removes the unique constraint with the given name. | ||
| 399 | */ | ||
| 400 | public function removeUniqueConstraint(string $name): void | ||
| 401 | { | ||
| 402 | $name = $this->normalizeIdentifier($name); | ||
| 403 | |||
| 404 | if (! $this->hasUniqueConstraint($name)) { | ||
| 405 | throw UniqueConstraintDoesNotExist::new($name, $this->_name); | ||
| 406 | } | ||
| 407 | |||
| 408 | unset($this->uniqueConstraints[$name]); | ||
| 409 | } | ||
| 410 | |||
| 411 | /** | ||
| 412 | * Returns the list of table columns. | ||
| 413 | * | ||
| 414 | * @return list<Column> | ||
| 415 | */ | ||
| 416 | public function getColumns(): array | ||
| 417 | { | ||
| 418 | return array_values($this->_columns); | ||
| 419 | } | ||
| 420 | |||
| 421 | /** | ||
| 422 | * Returns whether this table has a Column with the given name. | ||
| 423 | */ | ||
| 424 | public function hasColumn(string $name): bool | ||
| 425 | { | ||
| 426 | $name = $this->normalizeIdentifier($name); | ||
| 427 | |||
| 428 | return isset($this->_columns[$name]); | ||
| 429 | } | ||
| 430 | |||
| 431 | /** | ||
| 432 | * Returns the Column with the given name. | ||
| 433 | */ | ||
| 434 | public function getColumn(string $name): Column | ||
| 435 | { | ||
| 436 | $name = $this->normalizeIdentifier($name); | ||
| 437 | |||
| 438 | if (! $this->hasColumn($name)) { | ||
| 439 | throw ColumnDoesNotExist::new($name, $this->_name); | ||
| 440 | } | ||
| 441 | |||
| 442 | return $this->_columns[$name]; | ||
| 443 | } | ||
| 444 | |||
| 445 | /** | ||
| 446 | * Returns the primary key. | ||
| 447 | */ | ||
| 448 | public function getPrimaryKey(): ?Index | ||
| 449 | { | ||
| 450 | if ($this->_primaryKeyName !== null) { | ||
| 451 | return $this->getIndex($this->_primaryKeyName); | ||
| 452 | } | ||
| 453 | |||
| 454 | return null; | ||
| 455 | } | ||
| 456 | |||
| 457 | /** | ||
| 458 | * Returns whether this table has an Index with the given name. | ||
| 459 | */ | ||
| 460 | public function hasIndex(string $name): bool | ||
| 461 | { | ||
| 462 | $name = $this->normalizeIdentifier($name); | ||
| 463 | |||
| 464 | return isset($this->_indexes[$name]); | ||
| 465 | } | ||
| 466 | |||
| 467 | /** | ||
| 468 | * Returns the Index with the given name. | ||
| 469 | */ | ||
| 470 | public function getIndex(string $name): Index | ||
| 471 | { | ||
| 472 | $name = $this->normalizeIdentifier($name); | ||
| 473 | |||
| 474 | if (! $this->hasIndex($name)) { | ||
| 475 | throw IndexDoesNotExist::new($name, $this->_name); | ||
| 476 | } | ||
| 477 | |||
| 478 | return $this->_indexes[$name]; | ||
| 479 | } | ||
| 480 | |||
| 481 | /** @return array<string, Index> */ | ||
| 482 | public function getIndexes(): array | ||
| 483 | { | ||
| 484 | return $this->_indexes; | ||
| 485 | } | ||
| 486 | |||
| 487 | /** | ||
| 488 | * Returns the unique constraints. | ||
| 489 | * | ||
| 490 | * @return array<string, UniqueConstraint> | ||
| 491 | */ | ||
| 492 | public function getUniqueConstraints(): array | ||
| 493 | { | ||
| 494 | return $this->uniqueConstraints; | ||
| 495 | } | ||
| 496 | |||
| 497 | /** | ||
| 498 | * Returns the foreign key constraints. | ||
| 499 | * | ||
| 500 | * @return array<string, ForeignKeyConstraint> | ||
| 501 | */ | ||
| 502 | public function getForeignKeys(): array | ||
| 503 | { | ||
| 504 | return $this->_fkConstraints; | ||
| 505 | } | ||
| 506 | |||
| 507 | public function hasOption(string $name): bool | ||
| 508 | { | ||
| 509 | return isset($this->_options[$name]); | ||
| 510 | } | ||
| 511 | |||
| 512 | public function getOption(string $name): mixed | ||
| 513 | { | ||
| 514 | return $this->_options[$name] ?? null; | ||
| 515 | } | ||
| 516 | |||
| 517 | /** @return array<string, mixed> */ | ||
| 518 | public function getOptions(): array | ||
| 519 | { | ||
| 520 | return $this->_options; | ||
| 521 | } | ||
| 522 | |||
| 523 | /** | ||
| 524 | * Clone of a Table triggers a deep clone of all affected assets. | ||
| 525 | */ | ||
| 526 | public function __clone() | ||
| 527 | { | ||
| 528 | foreach ($this->_columns as $k => $column) { | ||
| 529 | $this->_columns[$k] = clone $column; | ||
| 530 | } | ||
| 531 | |||
| 532 | foreach ($this->_indexes as $k => $index) { | ||
| 533 | $this->_indexes[$k] = clone $index; | ||
| 534 | } | ||
| 535 | |||
| 536 | foreach ($this->_fkConstraints as $k => $fk) { | ||
| 537 | $this->_fkConstraints[$k] = clone $fk; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | protected function _getMaxIdentifierLength(): int | ||
| 542 | { | ||
| 543 | return $this->_schemaConfig instanceof SchemaConfig | ||
| 544 | ? $this->_schemaConfig->getMaxIdentifierLength() | ||
| 545 | : 63; | ||
| 546 | } | ||
| 547 | |||
| 548 | protected function _addColumn(Column $column): void | ||
| 549 | { | ||
| 550 | $columnName = $column->getName(); | ||
| 551 | $columnName = $this->normalizeIdentifier($columnName); | ||
| 552 | |||
| 553 | if (isset($this->_columns[$columnName])) { | ||
| 554 | throw ColumnAlreadyExists::new($this->getName(), $columnName); | ||
| 555 | } | ||
| 556 | |||
| 557 | $this->_columns[$columnName] = $column; | ||
| 558 | } | ||
| 559 | |||
| 560 | /** | ||
| 561 | * Adds an index to the table. | ||
| 562 | */ | ||
| 563 | protected function _addIndex(Index $indexCandidate): self | ||
| 564 | { | ||
| 565 | $indexName = $indexCandidate->getName(); | ||
| 566 | $indexName = $this->normalizeIdentifier($indexName); | ||
| 567 | $replacedImplicitIndexes = []; | ||
| 568 | |||
| 569 | foreach ($this->implicitIndexes as $name => $implicitIndex) { | ||
| 570 | if (! $implicitIndex->isFulfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) { | ||
| 571 | continue; | ||
| 572 | } | ||
| 573 | |||
| 574 | $replacedImplicitIndexes[] = $name; | ||
| 575 | } | ||
| 576 | |||
| 577 | if ( | ||
| 578 | (isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) || | ||
| 579 | ($this->_primaryKeyName !== null && $indexCandidate->isPrimary()) | ||
| 580 | ) { | ||
| 581 | throw IndexAlreadyExists::new($indexName, $this->_name); | ||
| 582 | } | ||
| 583 | |||
| 584 | foreach ($replacedImplicitIndexes as $name) { | ||
| 585 | unset($this->_indexes[$name], $this->implicitIndexes[$name]); | ||
| 586 | } | ||
| 587 | |||
| 588 | if ($indexCandidate->isPrimary()) { | ||
| 589 | $this->_primaryKeyName = $indexName; | ||
| 590 | } | ||
| 591 | |||
| 592 | $this->_indexes[$indexName] = $indexCandidate; | ||
| 593 | |||
| 594 | return $this; | ||
| 595 | } | ||
| 596 | |||
| 597 | protected function _addUniqueConstraint(UniqueConstraint $constraint): self | ||
| 598 | { | ||
| 599 | $name = $constraint->getName() !== '' | ||
| 600 | ? $constraint->getName() | ||
| 601 | : $this->_generateIdentifierName( | ||
| 602 | array_merge((array) $this->getName(), $constraint->getColumns()), | ||
| 603 | 'fk', | ||
| 604 | $this->_getMaxIdentifierLength(), | ||
| 605 | ); | ||
| 606 | |||
| 607 | $name = $this->normalizeIdentifier($name); | ||
| 608 | |||
| 609 | $this->uniqueConstraints[$name] = $constraint; | ||
| 610 | |||
| 611 | // If there is already an index that fulfills this requirements drop the request. In the case of __construct | ||
| 612 | // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates. | ||
| 613 | // This creates computation overhead in this case, however no duplicate indexes are ever added (column based). | ||
| 614 | $indexName = $this->_generateIdentifierName( | ||
| 615 | array_merge([$this->getName()], $constraint->getColumns()), | ||
| 616 | 'idx', | ||
| 617 | $this->_getMaxIdentifierLength(), | ||
| 618 | ); | ||
| 619 | |||
| 620 | $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false); | ||
| 621 | |||
| 622 | foreach ($this->_indexes as $existingIndex) { | ||
| 623 | if ($indexCandidate->isFulfilledBy($existingIndex)) { | ||
| 624 | return $this; | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; | ||
| 629 | |||
| 630 | return $this; | ||
| 631 | } | ||
| 632 | |||
| 633 | protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint): self | ||
| 634 | { | ||
| 635 | $name = $constraint->getName() !== '' | ||
| 636 | ? $constraint->getName() | ||
| 637 | : $this->_generateIdentifierName( | ||
| 638 | array_merge((array) $this->getName(), $constraint->getLocalColumns()), | ||
| 639 | 'fk', | ||
| 640 | $this->_getMaxIdentifierLength(), | ||
| 641 | ); | ||
| 642 | |||
| 643 | $name = $this->normalizeIdentifier($name); | ||
| 644 | |||
| 645 | $this->_fkConstraints[$name] = $constraint; | ||
| 646 | |||
| 647 | // add an explicit index on the foreign key columns. | ||
| 648 | // If there is already an index that fulfills this requirements drop the request. In the case of __construct | ||
| 649 | // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates. | ||
| 650 | // This creates computation overhead in this case, however no duplicate indexes are ever added (column based). | ||
| 651 | $indexName = $this->_generateIdentifierName( | ||
| 652 | array_merge([$this->getName()], $constraint->getLocalColumns()), | ||
| 653 | 'idx', | ||
| 654 | $this->_getMaxIdentifierLength(), | ||
| 655 | ); | ||
| 656 | |||
| 657 | $indexCandidate = $this->_createIndex($constraint->getLocalColumns(), $indexName, false, false); | ||
| 658 | |||
| 659 | foreach ($this->_indexes as $existingIndex) { | ||
| 660 | if ($indexCandidate->isFulfilledBy($existingIndex)) { | ||
| 661 | return $this; | ||
| 662 | } | ||
| 663 | } | ||
| 664 | |||
| 665 | $this->_addIndex($indexCandidate); | ||
| 666 | $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate; | ||
| 667 | |||
| 668 | return $this; | ||
| 669 | } | ||
| 670 | |||
| 671 | /** | ||
| 672 | * Normalizes a given identifier. | ||
| 673 | * | ||
| 674 | * Trims quotes and lowercases the given identifier. | ||
| 675 | */ | ||
| 676 | private function normalizeIdentifier(?string $identifier): string | ||
| 677 | { | ||
| 678 | if ($identifier === null) { | ||
| 679 | return ''; | ||
| 680 | } | ||
| 681 | |||
| 682 | return $this->trimQuotes(strtolower($identifier)); | ||
| 683 | } | ||
| 684 | |||
| 685 | public function setComment(string $comment): self | ||
| 686 | { | ||
| 687 | // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options. | ||
| 688 | $this->addOption('comment', $comment); | ||
| 689 | |||
| 690 | return $this; | ||
| 691 | } | ||
| 692 | |||
| 693 | public function getComment(): ?string | ||
| 694 | { | ||
| 695 | return $this->_options['comment'] ?? null; | ||
| 696 | } | ||
| 697 | |||
| 698 | /** | ||
| 699 | * @param array<string|int, string> $columns | ||
| 700 | * @param array<int, string> $flags | ||
| 701 | * @param array<string, mixed> $options | ||
| 702 | */ | ||
| 703 | private function _createUniqueConstraint( | ||
| 704 | array $columns, | ||
| 705 | string $indexName, | ||
| 706 | array $flags = [], | ||
| 707 | array $options = [], | ||
| 708 | ): UniqueConstraint { | ||
| 709 | if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { | ||
| 710 | throw IndexNameInvalid::new($indexName); | ||
| 711 | } | ||
| 712 | |||
| 713 | foreach ($columns as $index => $value) { | ||
| 714 | if (is_string($index)) { | ||
| 715 | $columnName = $index; | ||
| 716 | } else { | ||
| 717 | $columnName = $value; | ||
| 718 | } | ||
| 719 | |||
| 720 | if (! $this->hasColumn($columnName)) { | ||
| 721 | throw ColumnDoesNotExist::new($columnName, $this->_name); | ||
| 722 | } | ||
| 723 | } | ||
| 724 | |||
| 725 | return new UniqueConstraint($indexName, $columns, $flags, $options); | ||
| 726 | } | ||
| 727 | |||
| 728 | /** | ||
| 729 | * @param array<int, string> $columns | ||
| 730 | * @param array<int, string> $flags | ||
| 731 | * @param array<string, mixed> $options | ||
| 732 | */ | ||
| 733 | private function _createIndex( | ||
| 734 | array $columns, | ||
| 735 | string $indexName, | ||
| 736 | bool $isUnique, | ||
| 737 | bool $isPrimary, | ||
| 738 | array $flags = [], | ||
| 739 | array $options = [], | ||
| 740 | ): Index { | ||
| 741 | if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName)) === 1) { | ||
| 742 | throw IndexNameInvalid::new($indexName); | ||
| 743 | } | ||
| 744 | |||
| 745 | foreach ($columns as $columnName) { | ||
| 746 | if (! $this->hasColumn($columnName)) { | ||
| 747 | throw ColumnDoesNotExist::new($columnName, $this->_name); | ||
| 748 | } | ||
| 749 | } | ||
| 750 | |||
| 751 | return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options); | ||
| 752 | } | ||
| 753 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/TableDiff.php b/vendor/doctrine/dbal/src/Schema/TableDiff.php new file mode 100644 index 0000000..1cd59e8 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/TableDiff.php | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | use function array_filter; | ||
| 8 | use function count; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Table Diff. | ||
| 12 | */ | ||
| 13 | class TableDiff | ||
| 14 | { | ||
| 15 | /** | ||
| 16 | * Constructs a TableDiff object. | ||
| 17 | * | ||
| 18 | * @internal The diff can be only instantiated by a {@see Comparator}. | ||
| 19 | * | ||
| 20 | * @param array<ForeignKeyConstraint> $droppedForeignKeys | ||
| 21 | * @param array<Column> $addedColumns | ||
| 22 | * @param array<ColumnDiff> $modifiedColumns | ||
| 23 | * @param array<Column> $droppedColumns | ||
| 24 | * @param array<string, Column> $renamedColumns | ||
| 25 | * @param array<Index> $addedIndexes | ||
| 26 | * @param array<Index> $modifiedIndexes | ||
| 27 | * @param array<Index> $droppedIndexes | ||
| 28 | * @param array<string, Index> $renamedIndexes | ||
| 29 | * @param array<ForeignKeyConstraint> $addedForeignKeys | ||
| 30 | * @param array<ForeignKeyConstraint> $modifiedForeignKeys | ||
| 31 | */ | ||
| 32 | public function __construct( | ||
| 33 | private readonly Table $oldTable, | ||
| 34 | private readonly array $addedColumns, | ||
| 35 | private readonly array $modifiedColumns, | ||
| 36 | private readonly array $droppedColumns, | ||
| 37 | private readonly array $renamedColumns, | ||
| 38 | private array $addedIndexes, | ||
| 39 | private readonly array $modifiedIndexes, | ||
| 40 | private array $droppedIndexes, | ||
| 41 | private readonly array $renamedIndexes, | ||
| 42 | private readonly array $addedForeignKeys, | ||
| 43 | private readonly array $modifiedForeignKeys, | ||
| 44 | private readonly array $droppedForeignKeys, | ||
| 45 | ) { | ||
| 46 | } | ||
| 47 | |||
| 48 | public function getOldTable(): Table | ||
| 49 | { | ||
| 50 | return $this->oldTable; | ||
| 51 | } | ||
| 52 | |||
| 53 | /** @return array<Column> */ | ||
| 54 | public function getAddedColumns(): array | ||
| 55 | { | ||
| 56 | return $this->addedColumns; | ||
| 57 | } | ||
| 58 | |||
| 59 | /** @return array<ColumnDiff> */ | ||
| 60 | public function getModifiedColumns(): array | ||
| 61 | { | ||
| 62 | return $this->modifiedColumns; | ||
| 63 | } | ||
| 64 | |||
| 65 | /** @return array<Column> */ | ||
| 66 | public function getDroppedColumns(): array | ||
| 67 | { | ||
| 68 | return $this->droppedColumns; | ||
| 69 | } | ||
| 70 | |||
| 71 | /** @return array<string,Column> */ | ||
| 72 | public function getRenamedColumns(): array | ||
| 73 | { | ||
| 74 | return $this->renamedColumns; | ||
| 75 | } | ||
| 76 | |||
| 77 | /** @return array<Index> */ | ||
| 78 | public function getAddedIndexes(): array | ||
| 79 | { | ||
| 80 | return $this->addedIndexes; | ||
| 81 | } | ||
| 82 | |||
| 83 | /** | ||
| 84 | * @internal This method exists only for compatibility with the current implementation of schema managers | ||
| 85 | * that modify the diff while processing it. | ||
| 86 | */ | ||
| 87 | public function unsetAddedIndex(Index $index): void | ||
| 88 | { | ||
| 89 | $this->addedIndexes = array_filter( | ||
| 90 | $this->addedIndexes, | ||
| 91 | static function (Index $addedIndex) use ($index): bool { | ||
| 92 | return $addedIndex !== $index; | ||
| 93 | }, | ||
| 94 | ); | ||
| 95 | } | ||
| 96 | |||
| 97 | /** @return array<Index> */ | ||
| 98 | public function getModifiedIndexes(): array | ||
| 99 | { | ||
| 100 | return $this->modifiedIndexes; | ||
| 101 | } | ||
| 102 | |||
| 103 | /** @return array<Index> */ | ||
| 104 | public function getDroppedIndexes(): array | ||
| 105 | { | ||
| 106 | return $this->droppedIndexes; | ||
| 107 | } | ||
| 108 | |||
| 109 | /** | ||
| 110 | * @internal This method exists only for compatibility with the current implementation of schema managers | ||
| 111 | * that modify the diff while processing it. | ||
| 112 | */ | ||
| 113 | public function unsetDroppedIndex(Index $index): void | ||
| 114 | { | ||
| 115 | $this->droppedIndexes = array_filter( | ||
| 116 | $this->droppedIndexes, | ||
| 117 | static function (Index $droppedIndex) use ($index): bool { | ||
| 118 | return $droppedIndex !== $index; | ||
| 119 | }, | ||
| 120 | ); | ||
| 121 | } | ||
| 122 | |||
| 123 | /** @return array<string,Index> */ | ||
| 124 | public function getRenamedIndexes(): array | ||
| 125 | { | ||
| 126 | return $this->renamedIndexes; | ||
| 127 | } | ||
| 128 | |||
| 129 | /** @return array<ForeignKeyConstraint> */ | ||
| 130 | public function getAddedForeignKeys(): array | ||
| 131 | { | ||
| 132 | return $this->addedForeignKeys; | ||
| 133 | } | ||
| 134 | |||
| 135 | /** @return array<ForeignKeyConstraint> */ | ||
| 136 | public function getModifiedForeignKeys(): array | ||
| 137 | { | ||
| 138 | return $this->modifiedForeignKeys; | ||
| 139 | } | ||
| 140 | |||
| 141 | /** @return array<ForeignKeyConstraint> */ | ||
| 142 | public function getDroppedForeignKeys(): array | ||
| 143 | { | ||
| 144 | return $this->droppedForeignKeys; | ||
| 145 | } | ||
| 146 | |||
| 147 | /** | ||
| 148 | * Returns whether the diff is empty (contains no changes). | ||
| 149 | */ | ||
| 150 | public function isEmpty(): bool | ||
| 151 | { | ||
| 152 | return count($this->addedColumns) === 0 | ||
| 153 | && count($this->modifiedColumns) === 0 | ||
| 154 | && count($this->droppedColumns) === 0 | ||
| 155 | && count($this->renamedColumns) === 0 | ||
| 156 | && count($this->addedIndexes) === 0 | ||
| 157 | && count($this->modifiedIndexes) === 0 | ||
| 158 | && count($this->droppedIndexes) === 0 | ||
| 159 | && count($this->renamedIndexes) === 0 | ||
| 160 | && count($this->addedForeignKeys) === 0 | ||
| 161 | && count($this->modifiedForeignKeys) === 0 | ||
| 162 | && count($this->droppedForeignKeys) === 0; | ||
| 163 | } | ||
| 164 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/UniqueConstraint.php b/vendor/doctrine/dbal/src/Schema/UniqueConstraint.php new file mode 100644 index 0000000..a33d446 --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/UniqueConstraint.php | |||
| @@ -0,0 +1,152 @@ | |||
| 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_keys; | ||
| 10 | use function array_map; | ||
| 11 | use function strtolower; | ||
| 12 | |||
| 13 | /** | ||
| 14 | * Class for a unique constraint. | ||
| 15 | */ | ||
| 16 | class UniqueConstraint extends AbstractAsset | ||
| 17 | { | ||
| 18 | /** | ||
| 19 | * Asset identifier instances of the column names the unique constraint is associated with. | ||
| 20 | * | ||
| 21 | * @var array<string, Identifier> | ||
| 22 | */ | ||
| 23 | protected array $columns = []; | ||
| 24 | |||
| 25 | /** | ||
| 26 | * Platform specific flags | ||
| 27 | * | ||
| 28 | * @var array<string, true> | ||
| 29 | */ | ||
| 30 | protected array $flags = []; | ||
| 31 | |||
| 32 | /** | ||
| 33 | * @param array<string> $columns | ||
| 34 | * @param array<string> $flags | ||
| 35 | * @param array<string, mixed> $options | ||
| 36 | */ | ||
| 37 | public function __construct( | ||
| 38 | string $name, | ||
| 39 | array $columns, | ||
| 40 | array $flags = [], | ||
| 41 | private readonly array $options = [], | ||
| 42 | ) { | ||
| 43 | $this->_setName($name); | ||
| 44 | |||
| 45 | foreach ($columns as $column) { | ||
| 46 | $this->addColumn($column); | ||
| 47 | } | ||
| 48 | |||
| 49 | foreach ($flags as $flag) { | ||
| 50 | $this->addFlag($flag); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Returns the names of the referencing table columns the constraint is associated with. | ||
| 56 | * | ||
| 57 | * @return list<string> | ||
| 58 | */ | ||
| 59 | public function getColumns(): array | ||
| 60 | { | ||
| 61 | return array_keys($this->columns); | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Returns the quoted representation of the column names the constraint is associated with. | ||
| 66 | * | ||
| 67 | * But only if they were defined with one or a column name | ||
| 68 | * is a keyword reserved by the platform. | ||
| 69 | * Otherwise, the plain unquoted value as inserted is returned. | ||
| 70 | * | ||
| 71 | * @param AbstractPlatform $platform The platform to use for quotation. | ||
| 72 | * | ||
| 73 | * @return list<string> | ||
| 74 | */ | ||
| 75 | public function getQuotedColumns(AbstractPlatform $platform): array | ||
| 76 | { | ||
| 77 | $columns = []; | ||
| 78 | |||
| 79 | foreach ($this->columns as $column) { | ||
| 80 | $columns[] = $column->getQuotedName($platform); | ||
| 81 | } | ||
| 82 | |||
| 83 | return $columns; | ||
| 84 | } | ||
| 85 | |||
| 86 | /** @return array<int, string> */ | ||
| 87 | public function getUnquotedColumns(): array | ||
| 88 | { | ||
| 89 | return array_map($this->trimQuotes(...), $this->getColumns()); | ||
| 90 | } | ||
| 91 | |||
| 92 | /** | ||
| 93 | * Returns platform specific flags for unique constraint. | ||
| 94 | * | ||
| 95 | * @return array<int, string> | ||
| 96 | */ | ||
| 97 | public function getFlags(): array | ||
| 98 | { | ||
| 99 | return array_keys($this->flags); | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Adds flag for a unique constraint that translates to platform specific handling. | ||
| 104 | * | ||
| 105 | * @return $this | ||
| 106 | * | ||
| 107 | * @example $uniqueConstraint->addFlag('CLUSTERED') | ||
| 108 | */ | ||
| 109 | public function addFlag(string $flag): self | ||
| 110 | { | ||
| 111 | $this->flags[strtolower($flag)] = true; | ||
| 112 | |||
| 113 | return $this; | ||
| 114 | } | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Does this unique constraint have a specific flag? | ||
| 118 | */ | ||
| 119 | public function hasFlag(string $flag): bool | ||
| 120 | { | ||
| 121 | return isset($this->flags[strtolower($flag)]); | ||
| 122 | } | ||
| 123 | |||
| 124 | /** | ||
| 125 | * Removes a flag. | ||
| 126 | */ | ||
| 127 | public function removeFlag(string $flag): void | ||
| 128 | { | ||
| 129 | unset($this->flags[strtolower($flag)]); | ||
| 130 | } | ||
| 131 | |||
| 132 | public function hasOption(string $name): bool | ||
| 133 | { | ||
| 134 | return isset($this->options[strtolower($name)]); | ||
| 135 | } | ||
| 136 | |||
| 137 | public function getOption(string $name): mixed | ||
| 138 | { | ||
| 139 | return $this->options[strtolower($name)]; | ||
| 140 | } | ||
| 141 | |||
| 142 | /** @return array<string, mixed> */ | ||
| 143 | public function getOptions(): array | ||
| 144 | { | ||
| 145 | return $this->options; | ||
| 146 | } | ||
| 147 | |||
| 148 | protected function addColumn(string $column): void | ||
| 149 | { | ||
| 150 | $this->columns[$column] = new Identifier($column); | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/vendor/doctrine/dbal/src/Schema/View.php b/vendor/doctrine/dbal/src/Schema/View.php new file mode 100644 index 0000000..81f5f8a --- /dev/null +++ b/vendor/doctrine/dbal/src/Schema/View.php | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | <?php | ||
| 2 | |||
| 3 | declare(strict_types=1); | ||
| 4 | |||
| 5 | namespace Doctrine\DBAL\Schema; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Representation of a Database View. | ||
| 9 | */ | ||
| 10 | class View extends AbstractAsset | ||
| 11 | { | ||
| 12 | public function __construct(string $name, private readonly string $sql) | ||
| 13 | { | ||
| 14 | $this->_setName($name); | ||
| 15 | } | ||
| 16 | |||
| 17 | public function getSql(): string | ||
| 18 | { | ||
| 19 | return $this->sql; | ||
| 20 | } | ||
| 21 | } | ||
