PageRenderTime 36ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Doctrine/DBAL/Platforms/SQLAnywherePlatform.php

http://github.com/doctrine/dbal
PHP | 1519 lines | 952 code | 211 blank | 356 comment | 82 complexity | b6fed5f3ce413316cf375b2a8022afe1 MD5 | raw file
Possible License(s): Unlicense
  1. <?php
  2. namespace Doctrine\DBAL\Platforms;
  3. use Doctrine\DBAL\DBALException;
  4. use Doctrine\DBAL\LockMode;
  5. use Doctrine\DBAL\Schema\Column;
  6. use Doctrine\DBAL\Schema\ColumnDiff;
  7. use Doctrine\DBAL\Schema\Constraint;
  8. use Doctrine\DBAL\Schema\ForeignKeyConstraint;
  9. use Doctrine\DBAL\Schema\Identifier;
  10. use Doctrine\DBAL\Schema\Index;
  11. use Doctrine\DBAL\Schema\Table;
  12. use Doctrine\DBAL\Schema\TableDiff;
  13. use Doctrine\DBAL\TransactionIsolationLevel;
  14. use InvalidArgumentException;
  15. use function array_merge;
  16. use function array_unique;
  17. use function array_values;
  18. use function assert;
  19. use function count;
  20. use function explode;
  21. use function func_get_args;
  22. use function get_class;
  23. use function implode;
  24. use function is_string;
  25. use function preg_match;
  26. use function sprintf;
  27. use function strlen;
  28. use function strpos;
  29. use function strtoupper;
  30. use function substr;
  31. /**
  32. * The SQLAnywherePlatform provides the behavior, features and SQL dialect of the
  33. * SAP Sybase SQL Anywhere 10 database platform.
  34. */
  35. class SQLAnywherePlatform extends AbstractPlatform
  36. {
  37. public const FOREIGN_KEY_MATCH_SIMPLE = 1;
  38. public const FOREIGN_KEY_MATCH_FULL = 2;
  39. public const FOREIGN_KEY_MATCH_SIMPLE_UNIQUE = 129;
  40. public const FOREIGN_KEY_MATCH_FULL_UNIQUE = 130;
  41. /**
  42. * {@inheritdoc}
  43. */
  44. public function appendLockHint($fromClause, $lockMode)
  45. {
  46. switch (true) {
  47. case $lockMode === LockMode::NONE:
  48. return $fromClause . ' WITH (NOLOCK)';
  49. case $lockMode === LockMode::PESSIMISTIC_READ:
  50. return $fromClause . ' WITH (UPDLOCK)';
  51. case $lockMode === LockMode::PESSIMISTIC_WRITE:
  52. return $fromClause . ' WITH (XLOCK)';
  53. default:
  54. return $fromClause;
  55. }
  56. }
  57. /**
  58. * {@inheritdoc}
  59. *
  60. * SQL Anywhere supports a maximum length of 128 bytes for identifiers.
  61. */
  62. public function fixSchemaElementName($schemaElementName)
  63. {
  64. $maxIdentifierLength = $this->getMaxIdentifierLength();
  65. if (strlen($schemaElementName) > $maxIdentifierLength) {
  66. return substr($schemaElementName, 0, $maxIdentifierLength);
  67. }
  68. return $schemaElementName;
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function getAdvancedForeignKeyOptionsSQL(ForeignKeyConstraint $foreignKey)
  74. {
  75. $query = '';
  76. if ($foreignKey->hasOption('match')) {
  77. $query = ' MATCH ' . $this->getForeignKeyMatchClauseSQL($foreignKey->getOption('match'));
  78. }
  79. $query .= parent::getAdvancedForeignKeyOptionsSQL($foreignKey);
  80. if ($foreignKey->hasOption('check_on_commit') && (bool) $foreignKey->getOption('check_on_commit')) {
  81. $query .= ' CHECK ON COMMIT';
  82. }
  83. if ($foreignKey->hasOption('clustered') && (bool) $foreignKey->getOption('clustered')) {
  84. $query .= ' CLUSTERED';
  85. }
  86. if ($foreignKey->hasOption('for_olap_workload') && (bool) $foreignKey->getOption('for_olap_workload')) {
  87. $query .= ' FOR OLAP WORKLOAD';
  88. }
  89. return $query;
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public function getAlterTableSQL(TableDiff $diff)
  95. {
  96. $sql = [];
  97. $columnSql = [];
  98. $commentsSQL = [];
  99. $tableSql = [];
  100. $alterClauses = [];
  101. foreach ($diff->addedColumns as $column) {
  102. if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
  103. continue;
  104. }
  105. $alterClauses[] = $this->getAlterTableAddColumnClause($column);
  106. $comment = $this->getColumnComment($column);
  107. if ($comment === null || $comment === '') {
  108. continue;
  109. }
  110. $commentsSQL[] = $this->getCommentOnColumnSQL(
  111. $diff->getName($this)->getQuotedName($this),
  112. $column->getQuotedName($this),
  113. $comment
  114. );
  115. }
  116. foreach ($diff->removedColumns as $column) {
  117. if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
  118. continue;
  119. }
  120. $alterClauses[] = $this->getAlterTableRemoveColumnClause($column);
  121. }
  122. foreach ($diff->changedColumns as $columnDiff) {
  123. if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
  124. continue;
  125. }
  126. $alterClause = $this->getAlterTableChangeColumnClause($columnDiff);
  127. if ($alterClause !== null) {
  128. $alterClauses[] = $alterClause;
  129. }
  130. if (! $columnDiff->hasChanged('comment')) {
  131. continue;
  132. }
  133. $column = $columnDiff->column;
  134. $commentsSQL[] = $this->getCommentOnColumnSQL(
  135. $diff->getName($this)->getQuotedName($this),
  136. $column->getQuotedName($this),
  137. $this->getColumnComment($column)
  138. );
  139. }
  140. foreach ($diff->renamedColumns as $oldColumnName => $column) {
  141. if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
  142. continue;
  143. }
  144. $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
  145. $this->getAlterTableRenameColumnClause($oldColumnName, $column);
  146. }
  147. if (! $this->onSchemaAlterTable($diff, $tableSql)) {
  148. if (! empty($alterClauses)) {
  149. $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' . implode(', ', $alterClauses);
  150. }
  151. $sql = array_merge($sql, $commentsSQL);
  152. $newName = $diff->getNewName();
  153. if ($newName !== false) {
  154. $sql[] = $this->getAlterTableClause($diff->getName($this)) . ' ' .
  155. $this->getAlterTableRenameTableClause($newName);
  156. }
  157. $sql = array_merge(
  158. $this->getPreAlterTableIndexForeignKeySQL($diff),
  159. $sql,
  160. $this->getPostAlterTableIndexForeignKeySQL($diff)
  161. );
  162. }
  163. return array_merge($sql, $tableSql, $columnSql);
  164. }
  165. /**
  166. * Returns the SQL clause for creating a column in a table alteration.
  167. *
  168. * @param Column $column The column to add.
  169. *
  170. * @return string
  171. */
  172. protected function getAlterTableAddColumnClause(Column $column)
  173. {
  174. return 'ADD ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
  175. }
  176. /**
  177. * Returns the SQL clause for altering a table.
  178. *
  179. * @param Identifier $tableName The quoted name of the table to alter.
  180. *
  181. * @return string
  182. */
  183. protected function getAlterTableClause(Identifier $tableName)
  184. {
  185. return 'ALTER TABLE ' . $tableName->getQuotedName($this);
  186. }
  187. /**
  188. * Returns the SQL clause for dropping a column in a table alteration.
  189. *
  190. * @param Column $column The column to drop.
  191. *
  192. * @return string
  193. */
  194. protected function getAlterTableRemoveColumnClause(Column $column)
  195. {
  196. return 'DROP ' . $column->getQuotedName($this);
  197. }
  198. /**
  199. * Returns the SQL clause for renaming a column in a table alteration.
  200. *
  201. * @param string $oldColumnName The quoted name of the column to rename.
  202. * @param Column $column The column to rename to.
  203. *
  204. * @return string
  205. */
  206. protected function getAlterTableRenameColumnClause($oldColumnName, Column $column)
  207. {
  208. $oldColumnName = new Identifier($oldColumnName);
  209. return 'RENAME ' . $oldColumnName->getQuotedName($this) . ' TO ' . $column->getQuotedName($this);
  210. }
  211. /**
  212. * Returns the SQL clause for renaming a table in a table alteration.
  213. *
  214. * @param Identifier $newTableName The quoted name of the table to rename to.
  215. *
  216. * @return string
  217. */
  218. protected function getAlterTableRenameTableClause(Identifier $newTableName)
  219. {
  220. return 'RENAME ' . $newTableName->getQuotedName($this);
  221. }
  222. /**
  223. * Returns the SQL clause for altering a column in a table alteration.
  224. *
  225. * This method returns null in case that only the column comment has changed.
  226. * Changes in column comments have to be handled differently.
  227. *
  228. * @param ColumnDiff $columnDiff The diff of the column to alter.
  229. *
  230. * @return string|null
  231. */
  232. protected function getAlterTableChangeColumnClause(ColumnDiff $columnDiff)
  233. {
  234. $column = $columnDiff->column;
  235. // Do not return alter clause if only comment has changed.
  236. if (! ($columnDiff->hasChanged('comment') && count($columnDiff->changedProperties) === 1)) {
  237. $columnAlterationClause = 'ALTER ' .
  238. $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
  239. if ($columnDiff->hasChanged('default') && $column->getDefault() === null) {
  240. $columnAlterationClause .= ', ALTER ' . $column->getQuotedName($this) . ' DROP DEFAULT';
  241. }
  242. return $columnAlterationClause;
  243. }
  244. return null;
  245. }
  246. /**
  247. * {@inheritdoc}
  248. */
  249. public function getBigIntTypeDeclarationSQL(array $columnDef)
  250. {
  251. $columnDef['integer_type'] = 'BIGINT';
  252. return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
  253. }
  254. /**
  255. * {@inheritdoc}
  256. */
  257. public function getBinaryDefaultLength()
  258. {
  259. return 1;
  260. }
  261. /**
  262. * {@inheritdoc}
  263. */
  264. public function getBinaryMaxLength()
  265. {
  266. return 32767;
  267. }
  268. /**
  269. * {@inheritdoc}
  270. */
  271. public function getBlobTypeDeclarationSQL(array $field)
  272. {
  273. return 'LONG BINARY';
  274. }
  275. /**
  276. * {@inheritdoc}
  277. *
  278. * BIT type columns require an explicit NULL declaration
  279. * in SQL Anywhere if they shall be nullable.
  280. * Otherwise by just omitting the NOT NULL clause,
  281. * SQL Anywhere will declare them NOT NULL nonetheless.
  282. */
  283. public function getBooleanTypeDeclarationSQL(array $columnDef)
  284. {
  285. $nullClause = isset($columnDef['notnull']) && (bool) $columnDef['notnull'] === false ? ' NULL' : '';
  286. return 'BIT' . $nullClause;
  287. }
  288. /**
  289. * {@inheritdoc}
  290. */
  291. public function getClobTypeDeclarationSQL(array $field)
  292. {
  293. return 'TEXT';
  294. }
  295. /**
  296. * {@inheritdoc}
  297. */
  298. public function getCommentOnColumnSQL($tableName, $columnName, $comment)
  299. {
  300. $tableName = new Identifier($tableName);
  301. $columnName = new Identifier($columnName);
  302. $comment = $comment === null ? 'NULL' : $this->quoteStringLiteral($comment);
  303. return sprintf(
  304. 'COMMENT ON COLUMN %s.%s IS %s',
  305. $tableName->getQuotedName($this),
  306. $columnName->getQuotedName($this),
  307. $comment
  308. );
  309. }
  310. /**
  311. * {@inheritdoc}
  312. */
  313. public function getConcatExpression()
  314. {
  315. return 'STRING(' . implode(', ', (array) func_get_args()) . ')';
  316. }
  317. /**
  318. * {@inheritdoc}
  319. */
  320. public function getCreateConstraintSQL(Constraint $constraint, $table)
  321. {
  322. if ($constraint instanceof ForeignKeyConstraint) {
  323. return $this->getCreateForeignKeySQL($constraint, $table);
  324. }
  325. if ($table instanceof Table) {
  326. $table = $table->getQuotedName($this);
  327. }
  328. return 'ALTER TABLE ' . $table .
  329. ' ADD ' . $this->getTableConstraintDeclarationSQL($constraint, $constraint->getQuotedName($this));
  330. }
  331. /**
  332. * {@inheritdoc}
  333. */
  334. public function getCreateDatabaseSQL($database)
  335. {
  336. $database = new Identifier($database);
  337. return "CREATE DATABASE '" . $database->getName() . "'";
  338. }
  339. /**
  340. * {@inheritdoc}
  341. *
  342. * Appends SQL Anywhere specific flags if given.
  343. */
  344. public function getCreateIndexSQL(Index $index, $table)
  345. {
  346. return parent::getCreateIndexSQL($index, $table) . $this->getAdvancedIndexOptionsSQL($index);
  347. }
  348. /**
  349. * {@inheritdoc}
  350. */
  351. public function getCreatePrimaryKeySQL(Index $index, $table)
  352. {
  353. if ($table instanceof Table) {
  354. $table = $table->getQuotedName($this);
  355. }
  356. return 'ALTER TABLE ' . $table . ' ADD ' . $this->getPrimaryKeyDeclarationSQL($index);
  357. }
  358. /**
  359. * {@inheritdoc}
  360. */
  361. public function getCreateTemporaryTableSnippetSQL()
  362. {
  363. return 'CREATE ' . $this->getTemporaryTableSQL() . ' TABLE';
  364. }
  365. /**
  366. * {@inheritdoc}
  367. */
  368. public function getCreateViewSQL($name, $sql)
  369. {
  370. return 'CREATE VIEW ' . $name . ' AS ' . $sql;
  371. }
  372. /**
  373. * {@inheritdoc}
  374. */
  375. public function getCurrentDateSQL()
  376. {
  377. return 'CURRENT DATE';
  378. }
  379. /**
  380. * {@inheritdoc}
  381. */
  382. public function getCurrentTimeSQL()
  383. {
  384. return 'CURRENT TIME';
  385. }
  386. /**
  387. * {@inheritdoc}
  388. */
  389. public function getCurrentTimestampSQL()
  390. {
  391. return 'CURRENT TIMESTAMP';
  392. }
  393. /**
  394. * {@inheritdoc}
  395. */
  396. protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
  397. {
  398. $factorClause = '';
  399. if ($operator === '-') {
  400. $factorClause = '-1 * ';
  401. }
  402. return 'DATEADD(' . $unit . ', ' . $factorClause . $interval . ', ' . $date . ')';
  403. }
  404. /**
  405. * {@inheritdoc}
  406. */
  407. public function getDateDiffExpression($date1, $date2)
  408. {
  409. return 'DATEDIFF(day, ' . $date2 . ', ' . $date1 . ')';
  410. }
  411. /**
  412. * {@inheritdoc}
  413. */
  414. public function getDateTimeFormatString()
  415. {
  416. return 'Y-m-d H:i:s.u';
  417. }
  418. /**
  419. * {@inheritdoc}
  420. */
  421. public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
  422. {
  423. return 'DATETIME';
  424. }
  425. /**
  426. * {@inheritdoc}
  427. */
  428. public function getDateTimeTzFormatString()
  429. {
  430. return $this->getDateTimeFormatString();
  431. }
  432. /**
  433. * {@inheritdoc}
  434. */
  435. public function getDateTypeDeclarationSQL(array $fieldDeclaration)
  436. {
  437. return 'DATE';
  438. }
  439. /**
  440. * {@inheritdoc}
  441. */
  442. public function getDefaultTransactionIsolationLevel()
  443. {
  444. return TransactionIsolationLevel::READ_UNCOMMITTED;
  445. }
  446. /**
  447. * {@inheritdoc}
  448. */
  449. public function getDropDatabaseSQL($database)
  450. {
  451. $database = new Identifier($database);
  452. return "DROP DATABASE '" . $database->getName() . "'";
  453. }
  454. /**
  455. * {@inheritdoc}
  456. */
  457. public function getDropIndexSQL($index, $table = null)
  458. {
  459. if ($index instanceof Index) {
  460. $index = $index->getQuotedName($this);
  461. }
  462. if (! is_string($index)) {
  463. throw new InvalidArgumentException(
  464. 'SQLAnywherePlatform::getDropIndexSQL() expects $index parameter to be string or ' . Index::class . '.'
  465. );
  466. }
  467. if (! isset($table)) {
  468. return 'DROP INDEX ' . $index;
  469. }
  470. if ($table instanceof Table) {
  471. $table = $table->getQuotedName($this);
  472. }
  473. if (! is_string($table)) {
  474. throw new InvalidArgumentException(
  475. 'SQLAnywherePlatform::getDropIndexSQL() expects $table parameter to be string or ' . Index::class . '.'
  476. );
  477. }
  478. return 'DROP INDEX ' . $table . '.' . $index;
  479. }
  480. /**
  481. * {@inheritdoc}
  482. */
  483. public function getDropViewSQL($name)
  484. {
  485. return 'DROP VIEW ' . $name;
  486. }
  487. /**
  488. * {@inheritdoc}
  489. */
  490. public function getForeignKeyBaseDeclarationSQL(ForeignKeyConstraint $foreignKey)
  491. {
  492. $sql = '';
  493. $foreignKeyName = $foreignKey->getName();
  494. $localColumns = $foreignKey->getQuotedLocalColumns($this);
  495. $foreignColumns = $foreignKey->getQuotedForeignColumns($this);
  496. $foreignTableName = $foreignKey->getQuotedForeignTableName($this);
  497. if (! empty($foreignKeyName)) {
  498. $sql .= 'CONSTRAINT ' . $foreignKey->getQuotedName($this) . ' ';
  499. }
  500. if (empty($localColumns)) {
  501. throw new InvalidArgumentException("Incomplete definition. 'local' required.");
  502. }
  503. if (empty($foreignColumns)) {
  504. throw new InvalidArgumentException("Incomplete definition. 'foreign' required.");
  505. }
  506. if (empty($foreignTableName)) {
  507. throw new InvalidArgumentException("Incomplete definition. 'foreignTable' required.");
  508. }
  509. if ($foreignKey->hasOption('notnull') && (bool) $foreignKey->getOption('notnull')) {
  510. $sql .= 'NOT NULL ';
  511. }
  512. return $sql .
  513. 'FOREIGN KEY (' . $this->getIndexFieldDeclarationListSQL($localColumns) . ') ' .
  514. 'REFERENCES ' . $foreignKey->getQuotedForeignTableName($this) .
  515. ' (' . $this->getIndexFieldDeclarationListSQL($foreignColumns) . ')';
  516. }
  517. /**
  518. * Returns foreign key MATCH clause for given type.
  519. *
  520. * @param int $type The foreign key match type
  521. *
  522. * @return string
  523. *
  524. * @throws InvalidArgumentException If unknown match type given.
  525. */
  526. public function getForeignKeyMatchClauseSQL($type)
  527. {
  528. switch ((int) $type) {
  529. case self::FOREIGN_KEY_MATCH_SIMPLE:
  530. return 'SIMPLE';
  531. case self::FOREIGN_KEY_MATCH_FULL:
  532. return 'FULL';
  533. case self::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE:
  534. return 'UNIQUE SIMPLE';
  535. case self::FOREIGN_KEY_MATCH_FULL_UNIQUE:
  536. return 'UNIQUE FULL';
  537. default:
  538. throw new InvalidArgumentException('Invalid foreign key match type: ' . $type);
  539. }
  540. }
  541. /**
  542. * {@inheritdoc}
  543. */
  544. public function getForeignKeyReferentialActionSQL($action)
  545. {
  546. // NO ACTION is not supported, therefore falling back to RESTRICT.
  547. if (strtoupper($action) === 'NO ACTION') {
  548. return 'RESTRICT';
  549. }
  550. return parent::getForeignKeyReferentialActionSQL($action);
  551. }
  552. /**
  553. * {@inheritdoc}
  554. */
  555. public function getForUpdateSQL()
  556. {
  557. return '';
  558. }
  559. /**
  560. * {@inheritdoc}
  561. *
  562. * @deprecated Use application-generated UUIDs instead
  563. */
  564. public function getGuidExpression()
  565. {
  566. return 'NEWID()';
  567. }
  568. /**
  569. * {@inheritdoc}
  570. */
  571. public function getGuidTypeDeclarationSQL(array $field)
  572. {
  573. return 'UNIQUEIDENTIFIER';
  574. }
  575. /**
  576. * {@inheritdoc}
  577. */
  578. public function getIndexDeclarationSQL($name, Index $index)
  579. {
  580. // Index declaration in statements like CREATE TABLE is not supported.
  581. throw DBALException::notSupported(__METHOD__);
  582. }
  583. /**
  584. * {@inheritdoc}
  585. */
  586. public function getIntegerTypeDeclarationSQL(array $columnDef)
  587. {
  588. $columnDef['integer_type'] = 'INT';
  589. return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
  590. }
  591. /**
  592. * {@inheritdoc}
  593. */
  594. public function getListDatabasesSQL()
  595. {
  596. return 'SELECT db_name(number) AS name FROM sa_db_list()';
  597. }
  598. /**
  599. * {@inheritdoc}
  600. */
  601. public function getListTableColumnsSQL($table, $database = null)
  602. {
  603. $user = 'USER_NAME()';
  604. if (strpos($table, '.') !== false) {
  605. [$user, $table] = explode('.', $table);
  606. $user = $this->quoteStringLiteral($user);
  607. }
  608. return sprintf(
  609. <<<'SQL'
  610. SELECT col.column_name,
  611. COALESCE(def.user_type_name, def.domain_name) AS 'type',
  612. def.declared_width AS 'length',
  613. def.scale,
  614. CHARINDEX('unsigned', def.domain_name) AS 'unsigned',
  615. IF col.nulls = 'Y' THEN 0 ELSE 1 ENDIF AS 'notnull',
  616. col."default",
  617. def.is_autoincrement AS 'autoincrement',
  618. rem.remarks AS 'comment'
  619. FROM sa_describe_query('SELECT * FROM "%s"') AS def
  620. JOIN SYS.SYSTABCOL AS col
  621. ON col.table_id = def.base_table_id AND col.column_id = def.base_column_id
  622. LEFT JOIN SYS.SYSREMARK AS rem
  623. ON col.object_id = rem.object_id
  624. WHERE def.base_owner_name = %s
  625. ORDER BY def.base_column_id ASC
  626. SQL
  627. ,
  628. $table,
  629. $user
  630. );
  631. }
  632. /**
  633. * {@inheritdoc}
  634. *
  635. * @todo Where is this used? Which information should be retrieved?
  636. */
  637. public function getListTableConstraintsSQL($table)
  638. {
  639. $user = '';
  640. if (strpos($table, '.') !== false) {
  641. [$user, $table] = explode('.', $table);
  642. $user = $this->quoteStringLiteral($user);
  643. $table = $this->quoteStringLiteral($table);
  644. } else {
  645. $table = $this->quoteStringLiteral($table);
  646. }
  647. return sprintf(
  648. <<<'SQL'
  649. SELECT con.*
  650. FROM SYS.SYSCONSTRAINT AS con
  651. JOIN SYS.SYSTAB AS tab ON con.table_object_id = tab.object_id
  652. WHERE tab.table_name = %s
  653. AND tab.creator = USER_ID(%s)
  654. SQL
  655. ,
  656. $table,
  657. $user
  658. );
  659. }
  660. /**
  661. * {@inheritdoc}
  662. */
  663. public function getListTableForeignKeysSQL($table)
  664. {
  665. $user = '';
  666. if (strpos($table, '.') !== false) {
  667. [$user, $table] = explode('.', $table);
  668. $user = $this->quoteStringLiteral($user);
  669. $table = $this->quoteStringLiteral($table);
  670. } else {
  671. $table = $this->quoteStringLiteral($table);
  672. }
  673. return sprintf(
  674. <<<'SQL'
  675. SELECT fcol.column_name AS local_column,
  676. ptbl.table_name AS foreign_table,
  677. pcol.column_name AS foreign_column,
  678. idx.index_name,
  679. IF fk.nulls = 'N'
  680. THEN 1
  681. ELSE NULL
  682. ENDIF AS notnull,
  683. CASE ut.referential_action
  684. WHEN 'C' THEN 'CASCADE'
  685. WHEN 'D' THEN 'SET DEFAULT'
  686. WHEN 'N' THEN 'SET NULL'
  687. WHEN 'R' THEN 'RESTRICT'
  688. ELSE NULL
  689. END AS on_update,
  690. CASE dt.referential_action
  691. WHEN 'C' THEN 'CASCADE'
  692. WHEN 'D' THEN 'SET DEFAULT'
  693. WHEN 'N' THEN 'SET NULL'
  694. WHEN 'R' THEN 'RESTRICT'
  695. ELSE NULL
  696. END AS on_delete,
  697. IF fk.check_on_commit = 'Y'
  698. THEN 1
  699. ELSE NULL
  700. ENDIF AS check_on_commit, -- check_on_commit flag
  701. IF ftbl.clustered_index_id = idx.index_id
  702. THEN 1
  703. ELSE NULL
  704. ENDIF AS 'clustered', -- clustered flag
  705. IF fk.match_type = 0
  706. THEN NULL
  707. ELSE fk.match_type
  708. ENDIF AS 'match', -- match option
  709. IF pidx.max_key_distance = 1
  710. THEN 1
  711. ELSE NULL
  712. ENDIF AS for_olap_workload -- for_olap_workload flag
  713. FROM SYS.SYSFKEY AS fk
  714. JOIN SYS.SYSIDX AS idx
  715. ON fk.foreign_table_id = idx.table_id
  716. AND fk.foreign_index_id = idx.index_id
  717. JOIN SYS.SYSPHYSIDX pidx
  718. ON idx.table_id = pidx.table_id
  719. AND idx.phys_index_id = pidx.phys_index_id
  720. JOIN SYS.SYSTAB AS ptbl
  721. ON fk.primary_table_id = ptbl.table_id
  722. JOIN SYS.SYSTAB AS ftbl
  723. ON fk.foreign_table_id = ftbl.table_id
  724. JOIN SYS.SYSIDXCOL AS idxcol
  725. ON idx.table_id = idxcol.table_id
  726. AND idx.index_id = idxcol.index_id
  727. JOIN SYS.SYSTABCOL AS pcol
  728. ON ptbl.table_id = pcol.table_id
  729. AND idxcol.primary_column_id = pcol.column_id
  730. JOIN SYS.SYSTABCOL AS fcol
  731. ON ftbl.table_id = fcol.table_id
  732. AND idxcol.column_id = fcol.column_id
  733. LEFT JOIN SYS.SYSTRIGGER ut
  734. ON fk.foreign_table_id = ut.foreign_table_id
  735. AND fk.foreign_index_id = ut.foreign_key_id
  736. AND ut.event = 'C'
  737. LEFT JOIN SYS.SYSTRIGGER dt
  738. ON fk.foreign_table_id = dt.foreign_table_id
  739. AND fk.foreign_index_id = dt.foreign_key_id
  740. AND dt.event = 'D'
  741. WHERE ftbl.table_name = %s
  742. AND ftbl.creator = USER_ID(%s)
  743. ORDER BY fk.foreign_index_id ASC, idxcol.sequence ASC
  744. SQL
  745. ,
  746. $table,
  747. $user
  748. );
  749. }
  750. /**
  751. * {@inheritdoc}
  752. */
  753. public function getListTableIndexesSQL($table, $currentDatabase = null)
  754. {
  755. $user = '';
  756. if (strpos($table, '.') !== false) {
  757. [$user, $table] = explode('.', $table);
  758. $user = $this->quoteStringLiteral($user);
  759. $table = $this->quoteStringLiteral($table);
  760. } else {
  761. $table = $this->quoteStringLiteral($table);
  762. }
  763. return sprintf(
  764. <<<'SQL'
  765. SELECT idx.index_name AS key_name,
  766. IF idx.index_category = 1
  767. THEN 1
  768. ELSE 0
  769. ENDIF AS 'primary',
  770. col.column_name,
  771. IF idx."unique" IN(1, 2, 5)
  772. THEN 0
  773. ELSE 1
  774. ENDIF AS non_unique,
  775. IF tbl.clustered_index_id = idx.index_id
  776. THEN 1
  777. ELSE NULL
  778. ENDIF AS 'clustered', -- clustered flag
  779. IF idx."unique" = 5
  780. THEN 1
  781. ELSE NULL
  782. ENDIF AS with_nulls_not_distinct, -- with_nulls_not_distinct flag
  783. IF pidx.max_key_distance = 1
  784. THEN 1
  785. ELSE NULL
  786. ENDIF AS for_olap_workload -- for_olap_workload flag
  787. FROM SYS.SYSIDX AS idx
  788. JOIN SYS.SYSPHYSIDX pidx
  789. ON idx.table_id = pidx.table_id
  790. AND idx.phys_index_id = pidx.phys_index_id
  791. JOIN SYS.SYSIDXCOL AS idxcol
  792. ON idx.table_id = idxcol.table_id AND idx.index_id = idxcol.index_id
  793. JOIN SYS.SYSTABCOL AS col
  794. ON idxcol.table_id = col.table_id AND idxcol.column_id = col.column_id
  795. JOIN SYS.SYSTAB AS tbl
  796. ON idx.table_id = tbl.table_id
  797. WHERE tbl.table_name = %s
  798. AND tbl.creator = USER_ID(%s)
  799. AND idx.index_category != 2 -- exclude indexes implicitly created by foreign key constraints
  800. ORDER BY idx.index_id ASC, idxcol.sequence ASC
  801. SQL
  802. ,
  803. $table,
  804. $user
  805. );
  806. }
  807. /**
  808. * {@inheritdoc}
  809. */
  810. public function getListTablesSQL()
  811. {
  812. return "SELECT tbl.table_name
  813. FROM SYS.SYSTAB AS tbl
  814. JOIN SYS.SYSUSER AS usr ON tbl.creator = usr.user_id
  815. JOIN dbo.SYSOBJECTS AS obj ON tbl.object_id = obj.id
  816. WHERE tbl.table_type IN(1, 3) -- 'BASE', 'GBL TEMP'
  817. AND usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
  818. AND obj.type = 'U' -- user created tables only
  819. ORDER BY tbl.table_name ASC";
  820. }
  821. /**
  822. * {@inheritdoc}
  823. *
  824. * @todo Where is this used? Which information should be retrieved?
  825. */
  826. public function getListUsersSQL()
  827. {
  828. return 'SELECT * FROM SYS.SYSUSER ORDER BY user_name ASC';
  829. }
  830. /**
  831. * {@inheritdoc}
  832. */
  833. public function getListViewsSQL($database)
  834. {
  835. return "SELECT tbl.table_name, v.view_def
  836. FROM SYS.SYSVIEW v
  837. JOIN SYS.SYSTAB tbl ON v.view_object_id = tbl.object_id
  838. JOIN SYS.SYSUSER usr ON tbl.creator = usr.user_id
  839. JOIN dbo.SYSOBJECTS obj ON tbl.object_id = obj.id
  840. WHERE usr.user_name NOT IN('SYS', 'dbo', 'rs_systabgroup') -- exclude system users
  841. ORDER BY tbl.table_name ASC";
  842. }
  843. /**
  844. * {@inheritdoc}
  845. */
  846. public function getLocateExpression($str, $substr, $startPos = false)
  847. {
  848. if ($startPos === false) {
  849. return 'LOCATE(' . $str . ', ' . $substr . ')';
  850. }
  851. return 'LOCATE(' . $str . ', ' . $substr . ', ' . $startPos . ')';
  852. }
  853. /**
  854. * {@inheritdoc}
  855. */
  856. public function getMaxIdentifierLength()
  857. {
  858. return 128;
  859. }
  860. /**
  861. * {@inheritdoc}
  862. */
  863. public function getMd5Expression($column)
  864. {
  865. return 'HASH(' . $column . ", 'MD5')";
  866. }
  867. /**
  868. * {@inheritdoc}
  869. */
  870. public function getName()
  871. {
  872. return 'sqlanywhere';
  873. }
  874. /**
  875. * Obtain DBMS specific SQL code portion needed to set a primary key
  876. * declaration to be used in statements like ALTER TABLE.
  877. *
  878. * @param Index $index Index definition
  879. * @param string $name Name of the primary key
  880. *
  881. * @return string DBMS specific SQL code portion needed to set a primary key
  882. *
  883. * @throws InvalidArgumentException If the given index is not a primary key.
  884. */
  885. public function getPrimaryKeyDeclarationSQL(Index $index, $name = null)
  886. {
  887. if (! $index->isPrimary()) {
  888. throw new InvalidArgumentException(
  889. 'Can only create primary key declarations with getPrimaryKeyDeclarationSQL()'
  890. );
  891. }
  892. return $this->getTableConstraintDeclarationSQL($index, $name);
  893. }
  894. /**
  895. * {@inheritdoc}
  896. */
  897. public function getSetTransactionIsolationSQL($level)
  898. {
  899. return 'SET TEMPORARY OPTION isolation_level = ' . $this->_getTransactionIsolationLevelSQL($level);
  900. }
  901. /**
  902. * {@inheritdoc}
  903. */
  904. public function getSmallIntTypeDeclarationSQL(array $columnDef)
  905. {
  906. $columnDef['integer_type'] = 'SMALLINT';
  907. return $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
  908. }
  909. /**
  910. * Returns the SQL statement for starting an existing database.
  911. *
  912. * In SQL Anywhere you can start and stop databases on a
  913. * database server instance.
  914. * This is a required statement after having created a new database
  915. * as it has to be explicitly started to be usable.
  916. * SQL Anywhere does not automatically start a database after creation!
  917. *
  918. * @param string $database Name of the database to start.
  919. *
  920. * @return string
  921. */
  922. public function getStartDatabaseSQL($database)
  923. {
  924. $database = new Identifier($database);
  925. return "START DATABASE '" . $database->getName() . "' AUTOSTOP OFF";
  926. }
  927. /**
  928. * Returns the SQL statement for stopping a running database.
  929. *
  930. * In SQL Anywhere you can start and stop databases on a
  931. * database server instance.
  932. * This is a required statement before dropping an existing database
  933. * as it has to be explicitly stopped before it can be dropped.
  934. *
  935. * @param string $database Name of the database to stop.
  936. *
  937. * @return string
  938. */
  939. public function getStopDatabaseSQL($database)
  940. {
  941. $database = new Identifier($database);
  942. return 'STOP DATABASE "' . $database->getName() . '" UNCONDITIONALLY';
  943. }
  944. /**
  945. * {@inheritdoc}
  946. */
  947. public function getSubstringExpression($value, $from, $length = null)
  948. {
  949. if ($length === null) {
  950. return 'SUBSTRING(' . $value . ', ' . $from . ')';
  951. }
  952. return 'SUBSTRING(' . $value . ', ' . $from . ', ' . $length . ')';
  953. }
  954. /**
  955. * {@inheritdoc}
  956. */
  957. public function getTemporaryTableSQL()
  958. {
  959. return 'GLOBAL TEMPORARY';
  960. }
  961. /**
  962. * {@inheritdoc}
  963. */
  964. public function getTimeFormatString()
  965. {
  966. return 'H:i:s.u';
  967. }
  968. /**
  969. * {@inheritdoc}
  970. */
  971. public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
  972. {
  973. return 'TIME';
  974. }
  975. /**
  976. * {@inheritdoc}
  977. */
  978. public function getTrimExpression($str, $pos = TrimMode::UNSPECIFIED, $char = false)
  979. {
  980. if (! $char) {
  981. switch ($pos) {
  982. case TrimMode::LEADING:
  983. return $this->getLtrimExpression($str);
  984. case TrimMode::TRAILING:
  985. return $this->getRtrimExpression($str);
  986. default:
  987. return 'TRIM(' . $str . ')';
  988. }
  989. }
  990. $pattern = "'%[^' + " . $char . " + ']%'";
  991. switch ($pos) {
  992. case TrimMode::LEADING:
  993. return 'SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))';
  994. case TrimMode::TRAILING:
  995. return 'REVERSE(SUBSTR(REVERSE(' . $str . '), PATINDEX(' . $pattern . ', REVERSE(' . $str . '))))';
  996. default:
  997. return 'REVERSE(SUBSTR(REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))), ' .
  998. 'PATINDEX(' . $pattern . ', REVERSE(SUBSTR(' . $str . ', PATINDEX(' . $pattern . ', ' . $str . '))))))';
  999. }
  1000. }
  1001. /**
  1002. * {@inheritdoc}
  1003. */
  1004. public function getTruncateTableSQL($tableName, $cascade = false)
  1005. {
  1006. $tableIdentifier = new Identifier($tableName);
  1007. return 'TRUNCATE TABLE ' . $tableIdentifier->getQuotedName($this);
  1008. }
  1009. /**
  1010. * {@inheritdoc}
  1011. */
  1012. public function getUniqueConstraintDeclarationSQL($name, Index $index)
  1013. {
  1014. if ($index->isPrimary()) {
  1015. throw new InvalidArgumentException(
  1016. 'Cannot create primary key constraint declarations with getUniqueConstraintDeclarationSQL().'
  1017. );
  1018. }
  1019. if (! $index->isUnique()) {
  1020. throw new InvalidArgumentException(
  1021. 'Can only create unique constraint declarations, no common index declarations with ' .
  1022. 'getUniqueConstraintDeclarationSQL().'
  1023. );
  1024. }
  1025. return $this->getTableConstraintDeclarationSQL($index, $name);
  1026. }
  1027. /**
  1028. * {@inheritdoc}
  1029. */
  1030. public function getVarcharDefaultLength()
  1031. {
  1032. return 1;
  1033. }
  1034. /**
  1035. * {@inheritdoc}
  1036. */
  1037. public function getVarcharMaxLength()
  1038. {
  1039. return 32767;
  1040. }
  1041. /**
  1042. * {@inheritdoc}
  1043. */
  1044. public function hasNativeGuidType()
  1045. {
  1046. return true;
  1047. }
  1048. /**
  1049. * {@inheritdoc}
  1050. */
  1051. public function prefersIdentityColumns()
  1052. {
  1053. return true;
  1054. }
  1055. /**
  1056. * {@inheritdoc}
  1057. */
  1058. public function supportsCommentOnStatement()
  1059. {
  1060. return true;
  1061. }
  1062. /**
  1063. * {@inheritdoc}
  1064. */
  1065. public function supportsIdentityColumns()
  1066. {
  1067. return true;
  1068. }
  1069. /**
  1070. * {@inheritdoc}
  1071. */
  1072. protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
  1073. {
  1074. $unsigned = ! empty($columnDef['unsigned']) ? 'UNSIGNED ' : '';
  1075. $autoincrement = ! empty($columnDef['autoincrement']) ? ' IDENTITY' : '';
  1076. return $unsigned . $columnDef['integer_type'] . $autoincrement;
  1077. }
  1078. /**
  1079. * {@inheritdoc}
  1080. */
  1081. protected function _getCreateTableSQL($tableName, array $columns, array $options = [])
  1082. {
  1083. $columnListSql = $this->getColumnDeclarationListSQL($columns);
  1084. $indexSql = [];
  1085. if (! empty($options['uniqueConstraints'])) {
  1086. foreach ((array) $options['uniqueConstraints'] as $name => $definition) {
  1087. $columnListSql .= ', ' . $this->getUniqueConstraintDeclarationSQL($name, $definition);
  1088. }
  1089. }
  1090. if (! empty($options['indexes'])) {
  1091. foreach ((array) $options['indexes'] as $index) {
  1092. assert($index instanceof Index);
  1093. $indexSql[] = $this->getCreateIndexSQL($index, $tableName);
  1094. }
  1095. }
  1096. if (! empty($options['primary'])) {
  1097. $flags = '';
  1098. if (isset($options['primary_index']) && $options['primary_index']->hasFlag('clustered')) {
  1099. $flags = ' CLUSTERED ';
  1100. }
  1101. $columnListSql .= ', PRIMARY KEY' . $flags . ' (' . implode(', ', array_unique(array_values((array) $options['primary']))) . ')';
  1102. }
  1103. if (! empty($options['foreignKeys'])) {
  1104. foreach ((array) $options['foreignKeys'] as $definition) {
  1105. $columnListSql .= ', ' . $this->getForeignKeyDeclarationSQL($definition);
  1106. }
  1107. }
  1108. $query = 'CREATE TABLE ' . $tableName . ' (' . $columnListSql;
  1109. $check = $this->getCheckDeclarationSQL($columns);
  1110. if (! empty($check)) {
  1111. $query .= ', ' . $check;
  1112. }
  1113. $query .= ')';
  1114. return array_merge([$query], $indexSql);
  1115. }
  1116. /**
  1117. * {@inheritdoc}
  1118. */
  1119. protected function _getTransactionIsolationLevelSQL($level)
  1120. {
  1121. switch ($level) {
  1122. case TransactionIsolationLevel::READ_UNCOMMITTED:
  1123. return '0';
  1124. case TransactionIsolationLevel::READ_COMMITTED:
  1125. return '1';
  1126. case TransactionIsolationLevel::REPEATABLE_READ:
  1127. return '2';
  1128. case TransactionIsolationLevel::SERIALIZABLE:
  1129. return '3';
  1130. default:
  1131. throw new InvalidArgumentException('Invalid isolation level:' . $level);
  1132. }
  1133. }
  1134. /**
  1135. * {@inheritdoc}
  1136. */
  1137. protected function doModifyLimitQuery($query, $limit, $offset)
  1138. {
  1139. $limitOffsetClause = $this->getTopClauseSQL($limit, $offset);
  1140. if ($limitOffsetClause === '') {
  1141. return $query;
  1142. }
  1143. if (! preg_match('/^\s*(SELECT\s+(DISTINCT\s+)?)(.*)/i', $query, $matches)) {
  1144. return $query;
  1145. }
  1146. return $matches[1] . $limitOffsetClause . ' ' . $matches[3];
  1147. }
  1148. private function getTopClauseSQL(?int $limit, ?int $offset) : string
  1149. {
  1150. if ($offset > 0) {
  1151. return sprintf('TOP %s START AT %d', $limit ?? 'ALL', $offset + 1);
  1152. }
  1153. return $limit === null ? '' : 'TOP ' . $limit;
  1154. }
  1155. /**
  1156. * Return the INDEX query section dealing with non-standard
  1157. * SQL Anywhere options.
  1158. *
  1159. * @param Index $index Index definition
  1160. *
  1161. * @return string
  1162. */
  1163. protected function getAdvancedIndexOptionsSQL(Index $index)
  1164. {
  1165. $sql = '';
  1166. if (! $index->isPrimary() && $index->hasFlag('for_olap_workload')) {
  1167. $sql .= ' FOR OLAP WORKLOAD';
  1168. }
  1169. return $sql;
  1170. }
  1171. /**
  1172. * {@inheritdoc}
  1173. */
  1174. protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
  1175. {
  1176. return $fixed
  1177. ? 'BINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')'
  1178. : 'VARBINARY(' . ($length ?: $this->getBinaryDefaultLength()) . ')';
  1179. }
  1180. /**
  1181. * Returns the SQL snippet for creating a table constraint.
  1182. *
  1183. * @param Constraint $constraint The table constraint to create the SQL snippet for.
  1184. * @param string|null $name The table constraint name to use if any.
  1185. *
  1186. * @return string
  1187. *
  1188. * @throws InvalidArgumentException If the given table constraint type is not supported by this method.
  1189. */
  1190. protected function getTableConstraintDeclarationSQL(Constraint $constraint, $name = null)
  1191. {
  1192. if ($constraint instanceof ForeignKeyConstraint) {
  1193. return $this->getForeignKeyDeclarationSQL($constraint);
  1194. }
  1195. if (! $constraint instanceof Index) {
  1196. throw new InvalidArgumentException('Unsupported constraint type: ' . get_class($constraint));
  1197. }
  1198. if (! $constraint->isPrimary() && ! $constraint->isUnique()) {
  1199. throw new InvalidArgumentException(
  1200. 'Can only create primary, unique or foreign key constraint declarations, no common index declarations ' .
  1201. 'with getTableConstraintDeclarationSQL().'
  1202. );
  1203. }
  1204. $constraintColumns = $constraint->getQuotedColumns($this);
  1205. if (empty($constraintColumns)) {
  1206. throw new InvalidArgumentException("Incomplete definition. 'columns' required.");
  1207. }
  1208. $sql = '';
  1209. $flags = '';
  1210. if (! empty($name)) {
  1211. $name = new Identifier($name);
  1212. $sql .= 'CONSTRAINT ' . $name->getQuotedName($this) . ' ';
  1213. }
  1214. if ($constraint->hasFlag('clustered')) {
  1215. $flags = 'CLUSTERED ';
  1216. }
  1217. if ($constraint->isPrimary()) {
  1218. return $sql . 'PRIMARY KEY ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
  1219. }
  1220. return $sql . 'UNIQUE ' . $flags . '(' . $this->getIndexFieldDeclarationListSQL($constraintColumns) . ')';
  1221. }
  1222. /**
  1223. * {@inheritdoc}
  1224. */
  1225. protected function getCreateIndexSQLFlags(Index $index)
  1226. {
  1227. $type = '';
  1228. if ($index->hasFlag('virtual')) {
  1229. $type .= 'VIRTUAL ';
  1230. }
  1231. if ($index->isUnique()) {
  1232. $type .= 'UNIQUE ';
  1233. }
  1234. if ($index->hasFlag('clustered')) {
  1235. $type .= 'CLUSTERED ';
  1236. }
  1237. return $type;
  1238. }
  1239. /**
  1240. * {@inheritdoc}
  1241. */
  1242. protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
  1243. {
  1244. return ['ALTER INDEX ' . $oldIndexName . ' ON ' . $tableName . ' RENAME TO ' . $index->getQuotedName($this)];
  1245. }
  1246. /**
  1247. * {@inheritdoc}
  1248. */
  1249. protected function getReservedKeywordsClass()
  1250. {
  1251. return Keywords\SQLAnywhereKeywords::class;
  1252. }
  1253. /**
  1254. * {@inheritdoc}
  1255. */
  1256. protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
  1257. {
  1258. return $fixed
  1259. ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(' . $this->getVarcharDefaultLength() . ')')
  1260. : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(' . $this->getVarcharDefaultLength() . ')');
  1261. }
  1262. /**
  1263. * {@inheritdoc}
  1264. */
  1265. protected function initializeDoctrineTypeMappings()
  1266. {
  1267. $this->doctrineTypeMapping = [
  1268. 'char' => 'string',
  1269. 'long nvarchar' => 'text',
  1270. 'long varchar' => 'text',
  1271. 'nchar' => 'string',
  1272. 'ntext' => 'text',
  1273. 'nvarchar' => 'string',
  1274. 'text' => 'text',
  1275. 'uniqueidentifierstr' => 'guid',
  1276. 'varchar' => 'string',
  1277. 'xml' => 'text',
  1278. 'bigint' => 'bigint',
  1279. 'unsigned bigint' => 'bigint',
  1280. 'bit' => 'boolean',
  1281. 'decimal' => 'decimal',
  1282. 'double' => 'float',
  1283. 'float' => 'float',
  1284. 'int' => 'integer',
  1285. 'integer' => 'integer',
  1286. 'unsigned int' => 'integer',
  1287. 'numeric' => 'decimal',
  1288. 'smallint' => 'smallint',
  1289. 'unsigned smallint' => 'smallint',
  1290. 'tinyint' => 'smallint',
  1291. 'unsigned tinyint' => 'smallint',
  1292. 'money' => 'decimal',
  1293. 'smallmoney' => 'decimal',
  1294. 'long varbit' => 'text',
  1295. 'varbit' => 'string',
  1296. 'date' => 'date',
  1297. 'datetime' => 'datetime',
  1298. 'smalldatetime' => 'datetime',
  1299. 'time' => 'time',
  1300. 'timestamp' => 'datetime',
  1301. 'binary' => 'binary',
  1302. 'image' => 'blob',
  1303. 'long binary' => 'blob',
  1304. 'uniqueidentifier' => 'guid',
  1305. 'varbinary' => 'binary',
  1306. ];
  1307. }
  1308. }