AbstractSchemaManager.php 29 KB

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