DB2Platform.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\DBAL\Platforms;
  20. use Doctrine\DBAL\DBALException;
  21. use Doctrine\DBAL\Schema\Index;
  22. use Doctrine\DBAL\Schema\TableDiff;
  23. class DB2Platform extends AbstractPlatform
  24. {
  25. /**
  26. * Gets the SQL Snippet used to declare a BLOB column type.
  27. */
  28. public function getBlobTypeDeclarationSQL(array $field)
  29. {
  30. throw DBALException::notSupported(__METHOD__);
  31. }
  32. public function initializeDoctrineTypeMappings()
  33. {
  34. $this->doctrineTypeMapping = array(
  35. 'smallint' => 'smallint',
  36. 'bigint' => 'bigint',
  37. 'integer' => 'integer',
  38. 'time' => 'time',
  39. 'date' => 'date',
  40. 'varchar' => 'string',
  41. 'character' => 'string',
  42. 'clob' => 'text',
  43. 'decimal' => 'decimal',
  44. 'double' => 'float',
  45. 'real' => 'float',
  46. 'timestamp' => 'datetime',
  47. );
  48. }
  49. /**
  50. * Gets the SQL snippet used to declare a VARCHAR column type.
  51. *
  52. * @param array $field
  53. */
  54. protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
  55. {
  56. return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
  57. : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
  58. }
  59. /**
  60. * Gets the SQL snippet used to declare a CLOB column type.
  61. *
  62. * @param array $field
  63. */
  64. public function getClobTypeDeclarationSQL(array $field)
  65. {
  66. // todo clob(n) with $field['length'];
  67. return 'CLOB(1M)';
  68. }
  69. /**
  70. * Gets the name of the platform.
  71. *
  72. * @return string
  73. */
  74. public function getName()
  75. {
  76. return 'db2';
  77. }
  78. /**
  79. * Gets the SQL snippet that declares a boolean column.
  80. *
  81. * @param array $columnDef
  82. * @return string
  83. */
  84. public function getBooleanTypeDeclarationSQL(array $columnDef)
  85. {
  86. return 'SMALLINT';
  87. }
  88. /**
  89. * Gets the SQL snippet that declares a 4 byte integer column.
  90. *
  91. * @param array $columnDef
  92. * @return string
  93. */
  94. public function getIntegerTypeDeclarationSQL(array $columnDef)
  95. {
  96. return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
  97. }
  98. /**
  99. * Gets the SQL snippet that declares an 8 byte integer column.
  100. *
  101. * @param array $columnDef
  102. * @return string
  103. */
  104. public function getBigIntTypeDeclarationSQL(array $columnDef)
  105. {
  106. return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
  107. }
  108. /**
  109. * Gets the SQL snippet that declares a 2 byte integer column.
  110. *
  111. * @param array $columnDef
  112. * @return string
  113. */
  114. public function getSmallIntTypeDeclarationSQL(array $columnDef)
  115. {
  116. return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
  117. }
  118. /**
  119. * Gets the SQL snippet that declares common properties of an integer column.
  120. *
  121. * @param array $columnDef
  122. * @return string
  123. */
  124. protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
  125. {
  126. $autoinc = '';
  127. if ( ! empty($columnDef['autoincrement'])) {
  128. $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
  129. }
  130. return $autoinc;
  131. }
  132. /**
  133. * Obtain DBMS specific SQL to be used to create datetime fields in
  134. * statements like CREATE TABLE
  135. *
  136. * @param array $fieldDeclaration
  137. * @return string
  138. */
  139. public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
  140. {
  141. if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
  142. return "TIMESTAMP(0) WITH DEFAULT";
  143. }
  144. return 'TIMESTAMP(0)';
  145. }
  146. /**
  147. * Obtain DBMS specific SQL to be used to create date fields in statements
  148. * like CREATE TABLE.
  149. *
  150. * @param array $fieldDeclaration
  151. * @return string
  152. */
  153. public function getDateTypeDeclarationSQL(array $fieldDeclaration)
  154. {
  155. return 'DATE';
  156. }
  157. /**
  158. * Obtain DBMS specific SQL to be used to create time fields in statements
  159. * like CREATE TABLE.
  160. *
  161. * @param array $fieldDeclaration
  162. * @return string
  163. */
  164. public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
  165. {
  166. return 'TIME';
  167. }
  168. public function getListDatabasesSQL()
  169. {
  170. throw DBALException::notSupported(__METHOD__);
  171. }
  172. public function getListSequencesSQL($database)
  173. {
  174. throw DBALException::notSupported(__METHOD__);
  175. }
  176. public function getListTableConstraintsSQL($table)
  177. {
  178. throw DBALException::notSupported(__METHOD__);
  179. }
  180. /**
  181. * This code fragment is originally from the Zend_Db_Adapter_Db2 class.
  182. *
  183. * @license New BSD License
  184. * @param string $table
  185. * @return string
  186. */
  187. public function getListTableColumnsSQL($table, $database = null)
  188. {
  189. return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno,
  190. c.typename, c.default, c.nulls, c.length, c.scale,
  191. c.identity, tc.type AS tabconsttype, k.colseq
  192. FROM syscat.columns c
  193. LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
  194. ON (k.tabschema = tc.tabschema
  195. AND k.tabname = tc.tabname
  196. AND tc.type = 'P'))
  197. ON (c.tabschema = k.tabschema
  198. AND c.tabname = k.tabname
  199. AND c.colname = k.colname)
  200. WHERE UPPER(c.tabname) = UPPER('" . $table . "') ORDER BY c.colno";
  201. }
  202. public function getListTablesSQL()
  203. {
  204. return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
  205. }
  206. public function getListUsersSQL()
  207. {
  208. throw DBALException::notSupported(__METHOD__);
  209. }
  210. /**
  211. * Get the SQL to list all views of a database or user.
  212. *
  213. * @param string $database
  214. * @return string
  215. */
  216. public function getListViewsSQL($database)
  217. {
  218. return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS";
  219. }
  220. public function getListTableIndexesSQL($table, $currentDatabase = null)
  221. {
  222. return "SELECT NAME, COLNAMES, UNIQUERULE FROM SYSIBM.SYSINDEXES WHERE TBNAME = UPPER('" . $table . "')";
  223. }
  224. public function getListTableForeignKeysSQL($table)
  225. {
  226. return "SELECT TBNAME, RELNAME, REFTBNAME, DELETERULE, UPDATERULE, FKCOLNAMES, PKCOLNAMES ".
  227. "FROM SYSIBM.SYSRELS WHERE TBNAME = UPPER('".$table."')";
  228. }
  229. public function getCreateViewSQL($name, $sql)
  230. {
  231. return "CREATE VIEW ".$name." AS ".$sql;
  232. }
  233. public function getDropViewSQL($name)
  234. {
  235. return "DROP VIEW ".$name;
  236. }
  237. public function getDropSequenceSQL($sequence)
  238. {
  239. throw DBALException::notSupported(__METHOD__);
  240. }
  241. public function getSequenceNextValSQL($sequenceName)
  242. {
  243. throw DBALException::notSupported(__METHOD__);
  244. }
  245. public function getCreateDatabaseSQL($database)
  246. {
  247. return "CREATE DATABASE ".$database;
  248. }
  249. public function getDropDatabaseSQL($database)
  250. {
  251. return "DROP DATABASE ".$database.";";
  252. }
  253. public function supportsCreateDropDatabase()
  254. {
  255. return false;
  256. }
  257. /**
  258. * Whether the platform supports releasing savepoints.
  259. *
  260. * @return boolean
  261. */
  262. public function supportsReleaseSavepoints()
  263. {
  264. return false;
  265. }
  266. /**
  267. * Gets the SQL specific for the platform to get the current date.
  268. *
  269. * @return string
  270. */
  271. public function getCurrentDateSQL()
  272. {
  273. return 'VALUES CURRENT DATE';
  274. }
  275. /**
  276. * Gets the SQL specific for the platform to get the current time.
  277. *
  278. * @return string
  279. */
  280. public function getCurrentTimeSQL()
  281. {
  282. return 'VALUES CURRENT TIME';
  283. }
  284. /**
  285. * Gets the SQL specific for the platform to get the current timestamp
  286. *
  287. * @return string
  288. */
  289. public function getCurrentTimestampSQL()
  290. {
  291. return "VALUES CURRENT TIMESTAMP";
  292. }
  293. /**
  294. * Obtain DBMS specific SQL code portion needed to set an index
  295. * declaration to be used in statements like CREATE TABLE.
  296. *
  297. * @param string $name name of the index
  298. * @param Index $index index definition
  299. * @return string DBMS specific SQL code portion needed to set an index
  300. */
  301. public function getIndexDeclarationSQL($name, Index $index)
  302. {
  303. return $this->getUniqueConstraintDeclarationSQL($name, $index);
  304. }
  305. /**
  306. * @param string $tableName
  307. * @param array $columns
  308. * @param array $options
  309. * @return array
  310. */
  311. protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
  312. {
  313. $indexes = array();
  314. if (isset($options['indexes'])) {
  315. $indexes = $options['indexes'];
  316. }
  317. $options['indexes'] = array();
  318. $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
  319. foreach ($indexes as $index => $definition) {
  320. $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
  321. }
  322. return $sqls;
  323. }
  324. /**
  325. * Gets the SQL to alter an existing table.
  326. *
  327. * @param TableDiff $diff
  328. * @return array
  329. */
  330. public function getAlterTableSQL(TableDiff $diff)
  331. {
  332. $sql = array();
  333. $columnSql = array();
  334. $queryParts = array();
  335. foreach ($diff->addedColumns AS $fieldName => $column) {
  336. if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
  337. continue;
  338. }
  339. $queryParts[] = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
  340. }
  341. foreach ($diff->removedColumns AS $column) {
  342. if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
  343. continue;
  344. }
  345. $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this);
  346. }
  347. foreach ($diff->changedColumns AS $columnDiff) {
  348. if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
  349. continue;
  350. }
  351. /* @var $columnDiff Doctrine\DBAL\Schema\ColumnDiff */
  352. $column = $columnDiff->column;
  353. $queryParts[] = 'ALTER ' . ($columnDiff->oldColumnName) . ' '
  354. . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
  355. }
  356. foreach ($diff->renamedColumns AS $oldColumnName => $column) {
  357. if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
  358. continue;
  359. }
  360. $queryParts[] = 'RENAME ' . $oldColumnName . ' TO ' . $column->getQuotedName($this);
  361. }
  362. $tableSql = array();
  363. if (!$this->onSchemaAlterTable($diff, $tableSql)) {
  364. if (count($queryParts) > 0) {
  365. $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(" ", $queryParts);
  366. }
  367. $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff));
  368. if ($diff->newName !== false) {
  369. $sql[] = 'RENAME TABLE TO ' . $diff->newName;
  370. }
  371. }
  372. return array_merge($sql, $tableSql, $columnSql);
  373. }
  374. public function getDefaultValueDeclarationSQL($field)
  375. {
  376. if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) {
  377. if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) {
  378. $field['default'] = 0;
  379. } else if((string)$field['type'] == "DateTime") {
  380. $field['default'] = "00-00-00 00:00:00";
  381. } else if ((string)$field['type'] == "Date") {
  382. $field['default'] = "00-00-00";
  383. } else if((string)$field['type'] == "Time") {
  384. $field['default'] = "00:00:00";
  385. } else {
  386. $field['default'] = '';
  387. }
  388. }
  389. unset($field['default']); // @todo this needs fixing
  390. if (isset($field['version']) && $field['version']) {
  391. if ((string)$field['type'] != "DateTime") {
  392. $field['default'] = "1";
  393. }
  394. }
  395. return parent::getDefaultValueDeclarationSQL($field);
  396. }
  397. /**
  398. * Get the insert sql for an empty insert statement
  399. *
  400. * @param string $tableName
  401. * @param string $identifierColumnName
  402. * @return string $sql
  403. */
  404. public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
  405. {
  406. return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
  407. }
  408. public function getCreateTemporaryTableSnippetSQL()
  409. {
  410. return "DECLARE GLOBAL TEMPORARY TABLE";
  411. }
  412. /**
  413. * DB2 automatically moves temporary tables into the SESSION. schema.
  414. *
  415. * @param string $tableName
  416. * @return string
  417. */
  418. public function getTemporaryTableName($tableName)
  419. {
  420. return "SESSION." . $tableName;
  421. }
  422. protected function doModifyLimitQuery($query, $limit, $offset = null)
  423. {
  424. if ($limit === null && $offset === null) {
  425. return $query;
  426. }
  427. $limit = (int)$limit;
  428. $offset = (int)(($offset)?:0);
  429. // Todo OVER() needs ORDER BY data!
  430. $sql = 'SELECT db22.* FROM (SELECT ROW_NUMBER() OVER() AS DC_ROWNUM, db21.* '.
  431. 'FROM (' . $query . ') db21) db22 WHERE db22.DC_ROWNUM BETWEEN ' . ($offset+1) .' AND ' . ($offset+$limit);
  432. return $sql;
  433. }
  434. /**
  435. * returns the position of the first occurrence of substring $substr in string $str
  436. *
  437. * @param string $substr literal string to find
  438. * @param string $str literal string
  439. * @param int $pos position to start at, beginning of string by default
  440. * @return integer
  441. */
  442. public function getLocateExpression($str, $substr, $startPos = false)
  443. {
  444. if ($startPos == false) {
  445. return 'LOCATE(' . $substr . ', ' . $str . ')';
  446. } else {
  447. return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
  448. }
  449. }
  450. /**
  451. * return string to call a function to get a substring inside an SQL statement
  452. *
  453. * Note: Not SQL92, but common functionality.
  454. *
  455. * SQLite only supports the 2 parameter variant of this function
  456. *
  457. * @param string $value an sql string literal or column name/alias
  458. * @param integer $from where to start the substring portion
  459. * @param integer $len the substring portion length
  460. * @return string
  461. */
  462. public function getSubstringExpression($value, $from, $len = null)
  463. {
  464. if ($len === null)
  465. return 'SUBSTR(' . $value . ', ' . $from . ')';
  466. else {
  467. return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')';
  468. }
  469. }
  470. public function supportsIdentityColumns()
  471. {
  472. return true;
  473. }
  474. public function prefersIdentityColumns()
  475. {
  476. return true;
  477. }
  478. /**
  479. * Gets the character casing of a column in an SQL result set of this platform.
  480. *
  481. * DB2 returns all column names in SQL result sets in uppercase.
  482. *
  483. * @param string $column The column name for which to get the correct character casing.
  484. * @return string The column name in the character casing used in SQL result sets.
  485. */
  486. public function getSQLResultCasing($column)
  487. {
  488. return strtoupper($column);
  489. }
  490. public function getForUpdateSQL()
  491. {
  492. return ' WITH RR USE AND KEEP UPDATE LOCKS';
  493. }
  494. public function getDummySelectSQL()
  495. {
  496. return 'SELECT 1 FROM sysibm.sysdummy1';
  497. }
  498. /**
  499. * DB2 supports savepoints, but they work semantically different than on other vendor platforms.
  500. *
  501. * TODO: We have to investigate how to get DB2 up and running with savepoints.
  502. *
  503. * @return bool
  504. */
  505. public function supportsSavepoints()
  506. {
  507. return false;
  508. }
  509. protected function getReservedKeywordsClass()
  510. {
  511. return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords';
  512. }
  513. }