1<?php 2 3namespace Doctrine\DBAL\Schema; 4 5use Doctrine\DBAL\Connection; 6use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs; 7use Doctrine\DBAL\Event\SchemaIndexDefinitionEventArgs; 8use Doctrine\DBAL\Events; 9use Doctrine\DBAL\Exception; 10use Doctrine\DBAL\Platforms\AbstractPlatform; 11use Doctrine\Deprecations\Deprecation; 12use Throwable; 13 14use function array_filter; 15use function array_intersect; 16use function array_map; 17use function array_values; 18use function assert; 19use function call_user_func_array; 20use function count; 21use function func_get_args; 22use function is_callable; 23use function is_string; 24use function preg_match; 25use function str_replace; 26use function strtolower; 27 28/** 29 * Base class for schema managers. Schema managers are used to inspect and/or 30 * modify the database schema/structure. 31 */ 32abstract class AbstractSchemaManager 33{ 34 /** 35 * Holds instance of the Doctrine connection for this schema manager. 36 * 37 * @var Connection 38 */ 39 protected $_conn; 40 41 /** 42 * Holds instance of the database platform used for this schema manager. 43 * 44 * @var AbstractPlatform 45 */ 46 protected $_platform; 47 48 public function __construct(Connection $connection, AbstractPlatform $platform) 49 { 50 $this->_conn = $connection; 51 $this->_platform = $platform; 52 } 53 54 /** 55 * Returns the associated platform. 56 * 57 * @return AbstractPlatform 58 */ 59 public function getDatabasePlatform() 60 { 61 return $this->_platform; 62 } 63 64 /** 65 * Tries any method on the schema manager. Normally a method throws an 66 * exception when your DBMS doesn't support it or if an error occurs. 67 * This method allows you to try and method on your SchemaManager 68 * instance and will return false if it does not work or is not supported. 69 * 70 * <code> 71 * $result = $sm->tryMethod('dropView', 'view_name'); 72 * </code> 73 * 74 * @return mixed 75 */ 76 public function tryMethod() 77 { 78 $args = func_get_args(); 79 $method = $args[0]; 80 unset($args[0]); 81 $args = array_values($args); 82 83 $callback = [$this, $method]; 84 assert(is_callable($callback)); 85 86 try { 87 return call_user_func_array($callback, $args); 88 } catch (Throwable $e) { 89 return false; 90 } 91 } 92 93 /** 94 * Lists the available databases for this connection. 95 * 96 * @return string[] 97 * 98 * @throws Exception 99 */ 100 public function listDatabases() 101 { 102 $sql = $this->_platform->getListDatabasesSQL(); 103 104 $databases = $this->_conn->fetchAllAssociative($sql); 105 106 return $this->_getPortableDatabasesList($databases); 107 } 108 109 /** 110 * Returns a list of all namespaces in the current database. 111 * 112 * @deprecated Use {@link listSchemaNames()} instead. 113 * 114 * @return string[] 115 * 116 * @throws Exception 117 */ 118 public function listNamespaceNames() 119 { 120 Deprecation::triggerIfCalledFromOutside( 121 'doctrine/dbal', 122 'https://github.com/doctrine/dbal/issues/4503', 123 'AbstractSchemaManager::listNamespaceNames() is deprecated,' 124 . ' use AbstractSchemaManager::listSchemaNames() instead.' 125 ); 126 127 $sql = $this->_platform->getListNamespacesSQL(); 128 129 $namespaces = $this->_conn->fetchAllAssociative($sql); 130 131 return $this->getPortableNamespacesList($namespaces); 132 } 133 134 /** 135 * Returns a list of the names of all schemata in the current database. 136 * 137 * @return list<string> 138 * 139 * @throws Exception 140 */ 141 public function listSchemaNames(): array 142 { 143 throw Exception::notSupported(__METHOD__); 144 } 145 146 /** 147 * Lists the available sequences for this connection. 148 * 149 * @param string|null $database 150 * 151 * @return Sequence[] 152 * 153 * @throws Exception 154 */ 155 public function listSequences($database = null) 156 { 157 if ($database === null) { 158 $database = $this->_conn->getDatabase(); 159 } 160 161 $sql = $this->_platform->getListSequencesSQL($database); 162 163 $sequences = $this->_conn->fetchAllAssociative($sql); 164 165 return $this->filterAssetNames($this->_getPortableSequencesList($sequences)); 166 } 167 168 /** 169 * Lists the columns for a given table. 170 * 171 * In contrast to other libraries and to the old version of Doctrine, 172 * this column definition does try to contain the 'primary' column for 173 * the reason that it is not portable across different RDBMS. Use 174 * {@see listTableIndexes($tableName)} to retrieve the primary key 175 * of a table. Where a RDBMS specifies more details, these are held 176 * in the platformDetails array. 177 * 178 * @param string $table The name of the table. 179 * @param string|null $database 180 * 181 * @return Column[] 182 * 183 * @throws Exception 184 */ 185 public function listTableColumns($table, $database = null) 186 { 187 if ($database === null) { 188 $database = $this->_conn->getDatabase(); 189 } 190 191 $sql = $this->_platform->getListTableColumnsSQL($table, $database); 192 193 $tableColumns = $this->_conn->fetchAllAssociative($sql); 194 195 return $this->_getPortableTableColumnList($table, $database, $tableColumns); 196 } 197 198 /** 199 * Lists the indexes for a given table returning an array of Index instances. 200 * 201 * Keys of the portable indexes list are all lower-cased. 202 * 203 * @param string $table The name of the table. 204 * 205 * @return Index[] 206 * 207 * @throws Exception 208 */ 209 public function listTableIndexes($table) 210 { 211 $sql = $this->_platform->getListTableIndexesSQL($table, $this->_conn->getDatabase()); 212 213 $tableIndexes = $this->_conn->fetchAllAssociative($sql); 214 215 return $this->_getPortableTableIndexesList($tableIndexes, $table); 216 } 217 218 /** 219 * Returns true if all the given tables exist. 220 * 221 * The usage of a string $tableNames is deprecated. Pass a one-element array instead. 222 * 223 * @param string|string[] $names 224 * 225 * @return bool 226 * 227 * @throws Exception 228 */ 229 public function tablesExist($names) 230 { 231 if (is_string($names)) { 232 Deprecation::trigger( 233 'doctrine/dbal', 234 'https://github.com/doctrine/dbal/issues/3580', 235 'The usage of a string $tableNames in AbstractSchemaManager::tablesExist() is deprecated. ' . 236 'Pass a one-element array instead.' 237 ); 238 } 239 240 $names = array_map('strtolower', (array) $names); 241 242 return count($names) === count(array_intersect($names, array_map('strtolower', $this->listTableNames()))); 243 } 244 245 /** 246 * Returns a list of all tables in the current database. 247 * 248 * @return string[] 249 * 250 * @throws Exception 251 */ 252 public function listTableNames() 253 { 254 $sql = $this->_platform->getListTablesSQL(); 255 256 $tables = $this->_conn->fetchAllAssociative($sql); 257 $tableNames = $this->_getPortableTablesList($tables); 258 259 return $this->filterAssetNames($tableNames); 260 } 261 262 /** 263 * Filters asset names if they are configured to return only a subset of all 264 * the found elements. 265 * 266 * @param mixed[] $assetNames 267 * 268 * @return mixed[] 269 */ 270 protected function filterAssetNames($assetNames) 271 { 272 $filter = $this->_conn->getConfiguration()->getSchemaAssetsFilter(); 273 if ($filter === null) { 274 return $assetNames; 275 } 276 277 return array_values(array_filter($assetNames, $filter)); 278 } 279 280 /** 281 * Lists the tables for this connection. 282 * 283 * @return Table[] 284 * 285 * @throws Exception 286 */ 287 public function listTables() 288 { 289 $tableNames = $this->listTableNames(); 290 291 $tables = []; 292 foreach ($tableNames as $tableName) { 293 $tables[] = $this->listTableDetails($tableName); 294 } 295 296 return $tables; 297 } 298 299 /** 300 * @param string $name 301 * 302 * @return Table 303 * 304 * @throws Exception 305 */ 306 public function listTableDetails($name) 307 { 308 $columns = $this->listTableColumns($name); 309 $foreignKeys = []; 310 311 if ($this->_platform->supportsForeignKeyConstraints()) { 312 $foreignKeys = $this->listTableForeignKeys($name); 313 } 314 315 $indexes = $this->listTableIndexes($name); 316 317 return new Table($name, $columns, $indexes, [], $foreignKeys); 318 } 319 320 /** 321 * Lists the views this connection has. 322 * 323 * @return View[] 324 * 325 * @throws Exception 326 */ 327 public function listViews() 328 { 329 $database = $this->_conn->getDatabase(); 330 $sql = $this->_platform->getListViewsSQL($database); 331 $views = $this->_conn->fetchAllAssociative($sql); 332 333 return $this->_getPortableViewsList($views); 334 } 335 336 /** 337 * Lists the foreign keys for the given table. 338 * 339 * @param string $table The name of the table. 340 * @param string|null $database 341 * 342 * @return ForeignKeyConstraint[] 343 * 344 * @throws Exception 345 */ 346 public function listTableForeignKeys($table, $database = null) 347 { 348 if ($database === null) { 349 $database = $this->_conn->getDatabase(); 350 } 351 352 $sql = $this->_platform->getListTableForeignKeysSQL($table, $database); 353 $tableForeignKeys = $this->_conn->fetchAllAssociative($sql); 354 355 return $this->_getPortableTableForeignKeysList($tableForeignKeys); 356 } 357 358 /* drop*() Methods */ 359 360 /** 361 * Drops a database. 362 * 363 * NOTE: You can not drop the database this SchemaManager is currently connected to. 364 * 365 * @param string $database The name of the database to drop. 366 * 367 * @return void 368 * 369 * @throws Exception 370 */ 371 public function dropDatabase($database) 372 { 373 $this->_execSql($this->_platform->getDropDatabaseSQL($database)); 374 } 375 376 /** 377 * Drops a schema. 378 * 379 * @throws Exception 380 */ 381 public function dropSchema(string $schemaName): void 382 { 383 $this->_execSql($this->_platform->getDropSchemaSQL($schemaName)); 384 } 385 386 /** 387 * Drops the given table. 388 * 389 * @param string $name The name of the table to drop. 390 * 391 * @return void 392 * 393 * @throws Exception 394 */ 395 public function dropTable($name) 396 { 397 $this->_execSql($this->_platform->getDropTableSQL($name)); 398 } 399 400 /** 401 * Drops the index from the given table. 402 * 403 * @param Index|string $index The name of the index. 404 * @param Table|string $table The name of the table. 405 * 406 * @return void 407 * 408 * @throws Exception 409 */ 410 public function dropIndex($index, $table) 411 { 412 if ($index instanceof Index) { 413 $index = $index->getQuotedName($this->_platform); 414 } 415 416 $this->_execSql($this->_platform->getDropIndexSQL($index, $table)); 417 } 418 419 /** 420 * Drops the constraint from the given table. 421 * 422 * @param Table|string $table The name of the table. 423 * 424 * @return void 425 * 426 * @throws Exception 427 */ 428 public function dropConstraint(Constraint $constraint, $table) 429 { 430 $this->_execSql($this->_platform->getDropConstraintSQL($constraint, $table)); 431 } 432 433 /** 434 * Drops a foreign key from a table. 435 * 436 * @param ForeignKeyConstraint|string $foreignKey The name of the foreign key. 437 * @param Table|string $table The name of the table with the foreign key. 438 * 439 * @return void 440 * 441 * @throws Exception 442 */ 443 public function dropForeignKey($foreignKey, $table) 444 { 445 $this->_execSql($this->_platform->getDropForeignKeySQL($foreignKey, $table)); 446 } 447 448 /** 449 * Drops a sequence with a given name. 450 * 451 * @param string $name The name of the sequence to drop. 452 * 453 * @return void 454 * 455 * @throws Exception 456 */ 457 public function dropSequence($name) 458 { 459 $this->_execSql($this->_platform->getDropSequenceSQL($name)); 460 } 461 462 /** 463 * Drops a view. 464 * 465 * @param string $name The name of the view. 466 * 467 * @return void 468 * 469 * @throws Exception 470 */ 471 public function dropView($name) 472 { 473 $this->_execSql($this->_platform->getDropViewSQL($name)); 474 } 475 476 /* create*() Methods */ 477 478 /** 479 * Creates a new database. 480 * 481 * @param string $database The name of the database to create. 482 * 483 * @return void 484 * 485 * @throws Exception 486 */ 487 public function createDatabase($database) 488 { 489 $this->_execSql($this->_platform->getCreateDatabaseSQL($database)); 490 } 491 492 /** 493 * Creates a new table. 494 * 495 * @return void 496 * 497 * @throws Exception 498 */ 499 public function createTable(Table $table) 500 { 501 $createFlags = AbstractPlatform::CREATE_INDEXES | AbstractPlatform::CREATE_FOREIGNKEYS; 502 $this->_execSql($this->_platform->getCreateTableSQL($table, $createFlags)); 503 } 504 505 /** 506 * Creates a new sequence. 507 * 508 * @param Sequence $sequence 509 * 510 * @return void 511 * 512 * @throws Exception 513 */ 514 public function createSequence($sequence) 515 { 516 $this->_execSql($this->_platform->getCreateSequenceSQL($sequence)); 517 } 518 519 /** 520 * Creates a constraint on a table. 521 * 522 * @param Table|string $table 523 * 524 * @return void 525 * 526 * @throws Exception 527 */ 528 public function createConstraint(Constraint $constraint, $table) 529 { 530 $this->_execSql($this->_platform->getCreateConstraintSQL($constraint, $table)); 531 } 532 533 /** 534 * Creates a new index on a table. 535 * 536 * @param Table|string $table The name of the table on which the index is to be created. 537 * 538 * @return void 539 * 540 * @throws Exception 541 */ 542 public function createIndex(Index $index, $table) 543 { 544 $this->_execSql($this->_platform->getCreateIndexSQL($index, $table)); 545 } 546 547 /** 548 * Creates a new foreign key. 549 * 550 * @param ForeignKeyConstraint $foreignKey The ForeignKey instance. 551 * @param Table|string $table The name of the table on which the foreign key is to be created. 552 * 553 * @return void 554 * 555 * @throws Exception 556 */ 557 public function createForeignKey(ForeignKeyConstraint $foreignKey, $table) 558 { 559 $this->_execSql($this->_platform->getCreateForeignKeySQL($foreignKey, $table)); 560 } 561 562 /** 563 * Creates a new view. 564 * 565 * @return void 566 * 567 * @throws Exception 568 */ 569 public function createView(View $view) 570 { 571 $this->_execSql($this->_platform->getCreateViewSQL($view->getQuotedName($this->_platform), $view->getSql())); 572 } 573 574 /* dropAndCreate*() Methods */ 575 576 /** 577 * Drops and creates a constraint. 578 * 579 * @see dropConstraint() 580 * @see createConstraint() 581 * 582 * @param Table|string $table 583 * 584 * @return void 585 * 586 * @throws Exception 587 */ 588 public function dropAndCreateConstraint(Constraint $constraint, $table) 589 { 590 $this->tryMethod('dropConstraint', $constraint, $table); 591 $this->createConstraint($constraint, $table); 592 } 593 594 /** 595 * Drops and creates a new index on a table. 596 * 597 * @param Table|string $table The name of the table on which the index is to be created. 598 * 599 * @return void 600 * 601 * @throws Exception 602 */ 603 public function dropAndCreateIndex(Index $index, $table) 604 { 605 $this->tryMethod('dropIndex', $index->getQuotedName($this->_platform), $table); 606 $this->createIndex($index, $table); 607 } 608 609 /** 610 * Drops and creates a new foreign key. 611 * 612 * @param ForeignKeyConstraint $foreignKey An associative array that defines properties 613 * of the foreign key to be created. 614 * @param Table|string $table The name of the table on which the foreign key is to be created. 615 * 616 * @return void 617 * 618 * @throws Exception 619 */ 620 public function dropAndCreateForeignKey(ForeignKeyConstraint $foreignKey, $table) 621 { 622 $this->tryMethod('dropForeignKey', $foreignKey, $table); 623 $this->createForeignKey($foreignKey, $table); 624 } 625 626 /** 627 * Drops and create a new sequence. 628 * 629 * @return void 630 * 631 * @throws Exception 632 */ 633 public function dropAndCreateSequence(Sequence $sequence) 634 { 635 $this->tryMethod('dropSequence', $sequence->getQuotedName($this->_platform)); 636 $this->createSequence($sequence); 637 } 638 639 /** 640 * Drops and creates a new table. 641 * 642 * @return void 643 * 644 * @throws Exception 645 */ 646 public function dropAndCreateTable(Table $table) 647 { 648 $this->tryMethod('dropTable', $table->getQuotedName($this->_platform)); 649 $this->createTable($table); 650 } 651 652 /** 653 * Drops and creates a new database. 654 * 655 * @param string $database The name of the database to create. 656 * 657 * @return void 658 * 659 * @throws Exception 660 */ 661 public function dropAndCreateDatabase($database) 662 { 663 $this->tryMethod('dropDatabase', $database); 664 $this->createDatabase($database); 665 } 666 667 /** 668 * Drops and creates a new view. 669 * 670 * @return void 671 * 672 * @throws Exception 673 */ 674 public function dropAndCreateView(View $view) 675 { 676 $this->tryMethod('dropView', $view->getQuotedName($this->_platform)); 677 $this->createView($view); 678 } 679 680 /* alterTable() Methods */ 681 682 /** 683 * Alters an existing tables schema. 684 * 685 * @return void 686 * 687 * @throws Exception 688 */ 689 public function alterTable(TableDiff $tableDiff) 690 { 691 foreach ($this->_platform->getAlterTableSQL($tableDiff) as $ddlQuery) { 692 $this->_execSql($ddlQuery); 693 } 694 } 695 696 /** 697 * Renames a given table to another name. 698 * 699 * @param string $name The current name of the table. 700 * @param string $newName The new name of the table. 701 * 702 * @return void 703 * 704 * @throws Exception 705 */ 706 public function renameTable($name, $newName) 707 { 708 $tableDiff = new TableDiff($name); 709 $tableDiff->newName = $newName; 710 $this->alterTable($tableDiff); 711 } 712 713 /** 714 * Methods for filtering return values of list*() methods to convert 715 * the native DBMS data definition to a portable Doctrine definition 716 */ 717 718 /** 719 * @param mixed[] $databases 720 * 721 * @return string[] 722 */ 723 protected function _getPortableDatabasesList($databases) 724 { 725 $list = []; 726 foreach ($databases as $value) { 727 $list[] = $this->_getPortableDatabaseDefinition($value); 728 } 729 730 return $list; 731 } 732 733 /** 734 * Converts a list of namespace names from the native DBMS data definition to a portable Doctrine definition. 735 * 736 * @deprecated Use {@link listSchemaNames()} instead. 737 * 738 * @param array<int, array<string, mixed>> $namespaces The list of namespace names 739 * in the native DBMS data definition. 740 * 741 * @return string[] 742 */ 743 protected function getPortableNamespacesList(array $namespaces) 744 { 745 Deprecation::triggerIfCalledFromOutside( 746 'doctrine/dbal', 747 'https://github.com/doctrine/dbal/issues/4503', 748 'AbstractSchemaManager::getPortableNamespacesList() is deprecated,' 749 . ' use AbstractSchemaManager::listSchemaNames() instead.' 750 ); 751 752 $namespacesList = []; 753 754 foreach ($namespaces as $namespace) { 755 $namespacesList[] = $this->getPortableNamespaceDefinition($namespace); 756 } 757 758 return $namespacesList; 759 } 760 761 /** 762 * @param mixed $database 763 * 764 * @return mixed 765 */ 766 protected function _getPortableDatabaseDefinition($database) 767 { 768 return $database; 769 } 770 771 /** 772 * Converts a namespace definition from the native DBMS data definition to a portable Doctrine definition. 773 * 774 * @deprecated Use {@link listSchemaNames()} instead. 775 * 776 * @param array<string, mixed> $namespace The native DBMS namespace definition. 777 * 778 * @return mixed 779 */ 780 protected function getPortableNamespaceDefinition(array $namespace) 781 { 782 Deprecation::triggerIfCalledFromOutside( 783 'doctrine/dbal', 784 'https://github.com/doctrine/dbal/issues/4503', 785 'AbstractSchemaManager::getPortableNamespaceDefinition() is deprecated,' 786 . ' use AbstractSchemaManager::listSchemaNames() instead.' 787 ); 788 789 return $namespace; 790 } 791 792 /** 793 * @param mixed[][] $triggers 794 * 795 * @return mixed[][] 796 */ 797 protected function _getPortableTriggersList($triggers) 798 { 799 $list = []; 800 foreach ($triggers as $value) { 801 $value = $this->_getPortableTriggerDefinition($value); 802 803 if (! $value) { 804 continue; 805 } 806 807 $list[] = $value; 808 } 809 810 return $list; 811 } 812 813 /** 814 * @param mixed[] $trigger 815 * 816 * @return mixed 817 */ 818 protected function _getPortableTriggerDefinition($trigger) 819 { 820 return $trigger; 821 } 822 823 /** 824 * @param mixed[][] $sequences 825 * 826 * @return Sequence[] 827 * 828 * @throws Exception 829 */ 830 protected function _getPortableSequencesList($sequences) 831 { 832 $list = []; 833 834 foreach ($sequences as $value) { 835 $list[] = $this->_getPortableSequenceDefinition($value); 836 } 837 838 return $list; 839 } 840 841 /** 842 * @param mixed[] $sequence 843 * 844 * @return Sequence 845 * 846 * @throws Exception 847 */ 848 protected function _getPortableSequenceDefinition($sequence) 849 { 850 throw Exception::notSupported('Sequences'); 851 } 852 853 /** 854 * Independent of the database the keys of the column list result are lowercased. 855 * 856 * The name of the created column instance however is kept in its case. 857 * 858 * @param string $table The name of the table. 859 * @param string $database 860 * @param mixed[][] $tableColumns 861 * 862 * @return Column[] 863 * 864 * @throws Exception 865 */ 866 protected function _getPortableTableColumnList($table, $database, $tableColumns) 867 { 868 $eventManager = $this->_platform->getEventManager(); 869 870 $list = []; 871 foreach ($tableColumns as $tableColumn) { 872 $column = null; 873 $defaultPrevented = false; 874 875 if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaColumnDefinition)) { 876 $eventArgs = new SchemaColumnDefinitionEventArgs($tableColumn, $table, $database, $this->_conn); 877 $eventManager->dispatchEvent(Events::onSchemaColumnDefinition, $eventArgs); 878 879 $defaultPrevented = $eventArgs->isDefaultPrevented(); 880 $column = $eventArgs->getColumn(); 881 } 882 883 if (! $defaultPrevented) { 884 $column = $this->_getPortableTableColumnDefinition($tableColumn); 885 } 886 887 if ($column === null) { 888 continue; 889 } 890 891 $name = strtolower($column->getQuotedName($this->_platform)); 892 $list[$name] = $column; 893 } 894 895 return $list; 896 } 897 898 /** 899 * Gets Table Column Definition. 900 * 901 * @param mixed[] $tableColumn 902 * 903 * @return Column 904 * 905 * @throws Exception 906 */ 907 abstract protected function _getPortableTableColumnDefinition($tableColumn); 908 909 /** 910 * Aggregates and groups the index results according to the required data result. 911 * 912 * @param mixed[][] $tableIndexes 913 * @param string|null $tableName 914 * 915 * @return Index[] 916 * 917 * @throws Exception 918 */ 919 protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) 920 { 921 $result = []; 922 foreach ($tableIndexes as $tableIndex) { 923 $indexName = $keyName = $tableIndex['key_name']; 924 if ($tableIndex['primary']) { 925 $keyName = 'primary'; 926 } 927 928 $keyName = strtolower($keyName); 929 930 if (! isset($result[$keyName])) { 931 $options = [ 932 'lengths' => [], 933 ]; 934 935 if (isset($tableIndex['where'])) { 936 $options['where'] = $tableIndex['where']; 937 } 938 939 $result[$keyName] = [ 940 'name' => $indexName, 941 'columns' => [], 942 'unique' => ! $tableIndex['non_unique'], 943 'primary' => $tableIndex['primary'], 944 'flags' => $tableIndex['flags'] ?? [], 945 'options' => $options, 946 ]; 947 } 948 949 $result[$keyName]['columns'][] = $tableIndex['column_name']; 950 $result[$keyName]['options']['lengths'][] = $tableIndex['length'] ?? null; 951 } 952 953 $eventManager = $this->_platform->getEventManager(); 954 955 $indexes = []; 956 foreach ($result as $indexKey => $data) { 957 $index = null; 958 $defaultPrevented = false; 959 960 if ($eventManager !== null && $eventManager->hasListeners(Events::onSchemaIndexDefinition)) { 961 $eventArgs = new SchemaIndexDefinitionEventArgs($data, $tableName, $this->_conn); 962 $eventManager->dispatchEvent(Events::onSchemaIndexDefinition, $eventArgs); 963 964 $defaultPrevented = $eventArgs->isDefaultPrevented(); 965 $index = $eventArgs->getIndex(); 966 } 967 968 if (! $defaultPrevented) { 969 $index = new Index( 970 $data['name'], 971 $data['columns'], 972 $data['unique'], 973 $data['primary'], 974 $data['flags'], 975 $data['options'] 976 ); 977 } 978 979 if ($index === null) { 980 continue; 981 } 982 983 $indexes[$indexKey] = $index; 984 } 985 986 return $indexes; 987 } 988 989 /** 990 * @param mixed[][] $tables 991 * 992 * @return string[] 993 */ 994 protected function _getPortableTablesList($tables) 995 { 996 $list = []; 997 foreach ($tables as $value) { 998 $list[] = $this->_getPortableTableDefinition($value); 999 } 1000 1001 return $list; 1002 } 1003 1004 /** 1005 * @param mixed $table 1006 * 1007 * @return string 1008 */ 1009 protected function _getPortableTableDefinition($table) 1010 { 1011 return $table; 1012 } 1013 1014 /** 1015 * @param mixed[][] $users 1016 * 1017 * @return string[][] 1018 */ 1019 protected function _getPortableUsersList($users) 1020 { 1021 $list = []; 1022 foreach ($users as $value) { 1023 $list[] = $this->_getPortableUserDefinition($value); 1024 } 1025 1026 return $list; 1027 } 1028 1029 /** 1030 * @param string[] $user 1031 * 1032 * @return string[] 1033 */ 1034 protected function _getPortableUserDefinition($user) 1035 { 1036 return $user; 1037 } 1038 1039 /** 1040 * @param mixed[][] $views 1041 * 1042 * @return View[] 1043 */ 1044 protected function _getPortableViewsList($views) 1045 { 1046 $list = []; 1047 foreach ($views as $value) { 1048 $view = $this->_getPortableViewDefinition($value); 1049 1050 if ($view === false) { 1051 continue; 1052 } 1053 1054 $viewName = strtolower($view->getQuotedName($this->_platform)); 1055 $list[$viewName] = $view; 1056 } 1057 1058 return $list; 1059 } 1060 1061 /** 1062 * @param mixed[] $view 1063 * 1064 * @return View|false 1065 */ 1066 protected function _getPortableViewDefinition($view) 1067 { 1068 return false; 1069 } 1070 1071 /** 1072 * @param mixed[][] $tableForeignKeys 1073 * 1074 * @return ForeignKeyConstraint[] 1075 */ 1076 protected function _getPortableTableForeignKeysList($tableForeignKeys) 1077 { 1078 $list = []; 1079 1080 foreach ($tableForeignKeys as $value) { 1081 $list[] = $this->_getPortableTableForeignKeyDefinition($value); 1082 } 1083 1084 return $list; 1085 } 1086 1087 /** 1088 * @param mixed $tableForeignKey 1089 * 1090 * @return ForeignKeyConstraint 1091 */ 1092 protected function _getPortableTableForeignKeyDefinition($tableForeignKey) 1093 { 1094 return $tableForeignKey; 1095 } 1096 1097 /** 1098 * @param string[]|string $sql 1099 * 1100 * @return void 1101 * 1102 * @throws Exception 1103 */ 1104 protected function _execSql($sql) 1105 { 1106 foreach ((array) $sql as $query) { 1107 $this->_conn->executeStatement($query); 1108 } 1109 } 1110 1111 /** 1112 * Creates a schema instance for the current database. 1113 * 1114 * @return Schema 1115 * 1116 * @throws Exception 1117 */ 1118 public function createSchema() 1119 { 1120 $schemaNames = []; 1121 1122 if ($this->_platform->supportsSchemas()) { 1123 $schemaNames = $this->listNamespaceNames(); 1124 } 1125 1126 $sequences = []; 1127 1128 if ($this->_platform->supportsSequences()) { 1129 $sequences = $this->listSequences(); 1130 } 1131 1132 $tables = $this->listTables(); 1133 1134 return new Schema($tables, $sequences, $this->createSchemaConfig(), $schemaNames); 1135 } 1136 1137 /** 1138 * Creates the configuration for this schema. 1139 * 1140 * @return SchemaConfig 1141 * 1142 * @throws Exception 1143 */ 1144 public function createSchemaConfig() 1145 { 1146 $schemaConfig = new SchemaConfig(); 1147 $schemaConfig->setMaxIdentifierLength($this->_platform->getMaxIdentifierLength()); 1148 1149 $searchPaths = $this->getSchemaSearchPaths(); 1150 if (isset($searchPaths[0])) { 1151 $schemaConfig->setName($searchPaths[0]); 1152 } 1153 1154 $params = $this->_conn->getParams(); 1155 if (! isset($params['defaultTableOptions'])) { 1156 $params['defaultTableOptions'] = []; 1157 } 1158 1159 if (! isset($params['defaultTableOptions']['charset']) && isset($params['charset'])) { 1160 $params['defaultTableOptions']['charset'] = $params['charset']; 1161 } 1162 1163 $schemaConfig->setDefaultTableOptions($params['defaultTableOptions']); 1164 1165 return $schemaConfig; 1166 } 1167 1168 /** 1169 * The search path for namespaces in the currently connected database. 1170 * 1171 * The first entry is usually the default namespace in the Schema. All 1172 * further namespaces contain tables/sequences which can also be addressed 1173 * with a short, not full-qualified name. 1174 * 1175 * For databases that don't support subschema/namespaces this method 1176 * returns the name of the currently connected database. 1177 * 1178 * @return string[] 1179 * 1180 * @throws Exception 1181 */ 1182 public function getSchemaSearchPaths() 1183 { 1184 $database = $this->_conn->getDatabase(); 1185 1186 if ($database !== null) { 1187 return [$database]; 1188 } 1189 1190 return []; 1191 } 1192 1193 /** 1194 * Given a table comment this method tries to extract a typehint for Doctrine Type, or returns 1195 * the type given as default. 1196 * 1197 * @param string|null $comment 1198 * @param string $currentType 1199 * 1200 * @return string 1201 */ 1202 public function extractDoctrineTypeFromComment($comment, $currentType) 1203 { 1204 if ($comment !== null && preg_match('(\(DC2Type:(((?!\)).)+)\))', $comment, $match) === 1) { 1205 return $match[1]; 1206 } 1207 1208 return $currentType; 1209 } 1210 1211 /** 1212 * @param string|null $comment 1213 * @param string|null $type 1214 * 1215 * @return string|null 1216 */ 1217 public function removeDoctrineTypeFromComment($comment, $type) 1218 { 1219 if ($comment === null) { 1220 return null; 1221 } 1222 1223 return str_replace('(DC2Type:' . $type . ')', '', $comment); 1224 } 1225} 1226