updated composer
[openemr.git] / vendor / doctrine / dbal / lib / Doctrine / DBAL / Platforms / DB2Platform.php
blobda0d77ced38b47b2f3060aa325597d053e86af91
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.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\DBAL\Platforms;
22 use Doctrine\DBAL\DBALException;
23 use Doctrine\DBAL\Schema\Column;
24 use Doctrine\DBAL\Schema\ColumnDiff;
25 use Doctrine\DBAL\Schema\Identifier;
26 use Doctrine\DBAL\Schema\Index;
27 use Doctrine\DBAL\Schema\Table;
28 use Doctrine\DBAL\Schema\TableDiff;
29 use Doctrine\DBAL\Types\Type;
31 class DB2Platform extends AbstractPlatform
33 /**
34 * {@inheritdoc}
36 public function getBinaryMaxLength()
38 return 32704;
41 /**
42 * {@inheritdoc}
44 public function getBinaryDefaultLength()
46 return 1;
49 /**
50 * {@inheritDoc}
52 public function getBlobTypeDeclarationSQL(array $field)
54 // todo blob(n) with $field['length'];
55 return 'BLOB(1M)';
58 /**
59 * {@inheritDoc}
61 public function initializeDoctrineTypeMappings()
63 $this->doctrineTypeMapping = array(
64 'smallint' => 'smallint',
65 'bigint' => 'bigint',
66 'integer' => 'integer',
67 'time' => 'time',
68 'date' => 'date',
69 'varchar' => 'string',
70 'character' => 'string',
71 'varbinary' => 'binary',
72 'binary' => 'binary',
73 'clob' => 'text',
74 'blob' => 'blob',
75 'decimal' => 'decimal',
76 'double' => 'float',
77 'real' => 'float',
78 'timestamp' => 'datetime',
82 /**
83 * {@inheritdoc}
85 public function isCommentedDoctrineType(Type $doctrineType)
87 if ($doctrineType->getName() === Type::BOOLEAN) {
88 // We require a commented boolean type in order to distinguish between boolean and smallint
89 // as both (have to) map to the same native type.
90 return true;
93 return parent::isCommentedDoctrineType($doctrineType);
96 /**
97 * {@inheritDoc}
99 protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
101 return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
102 : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
106 * {@inheritdoc}
108 protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed)
110 return $fixed ? 'BINARY(' . ($length ?: 255) . ')' : 'VARBINARY(' . ($length ?: 255) . ')';
114 * {@inheritDoc}
116 public function getClobTypeDeclarationSQL(array $field)
118 // todo clob(n) with $field['length'];
119 return 'CLOB(1M)';
123 * {@inheritDoc}
125 public function getName()
127 return 'db2';
131 * {@inheritDoc}
133 public function getBooleanTypeDeclarationSQL(array $columnDef)
135 return 'SMALLINT';
139 * {@inheritDoc}
141 public function getIntegerTypeDeclarationSQL(array $columnDef)
143 return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
147 * {@inheritDoc}
149 public function getBigIntTypeDeclarationSQL(array $columnDef)
151 return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
155 * {@inheritDoc}
157 public function getSmallIntTypeDeclarationSQL(array $columnDef)
159 return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
163 * {@inheritDoc}
165 protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
167 $autoinc = '';
168 if ( ! empty($columnDef['autoincrement'])) {
169 $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
172 return $autoinc;
176 * {@inheritdoc}
178 public function getBitAndComparisonExpression($value1, $value2)
180 return 'BITAND(' . $value1 . ', ' . $value2 . ')';
184 * {@inheritdoc}
186 public function getBitOrComparisonExpression($value1, $value2)
188 return 'BITOR(' . $value1 . ', ' . $value2 . ')';
192 * {@inheritdoc}
194 protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit)
196 switch ($unit) {
197 case self::DATE_INTERVAL_UNIT_WEEK:
198 $interval *= 7;
199 $unit = self::DATE_INTERVAL_UNIT_DAY;
200 break;
202 case self::DATE_INTERVAL_UNIT_QUARTER:
203 $interval *= 3;
204 $unit = self::DATE_INTERVAL_UNIT_MONTH;
205 break;
208 return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;
212 * {@inheritdoc}
214 public function getDateDiffExpression($date1, $date2)
216 return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';
220 * {@inheritDoc}
222 public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
224 if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
225 return "TIMESTAMP(0) WITH DEFAULT";
228 return 'TIMESTAMP(0)';
232 * {@inheritDoc}
234 public function getDateTypeDeclarationSQL(array $fieldDeclaration)
236 return 'DATE';
240 * {@inheritDoc}
242 public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
244 return 'TIME';
248 * {@inheritdoc}
250 public function getTruncateTableSQL($tableName, $cascade = false)
252 $tableIdentifier = new Identifier($tableName);
254 return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';
258 * This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.
260 * @license New BSD License
262 * @param string $table
263 * @param string $database
265 * @return string
267 public function getListTableColumnsSQL($table, $database = null)
269 $table = $this->quoteStringLiteral($table);
271 // We do the funky subquery and join syscat.columns.default this crazy way because
272 // as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,
273 // it wants shorter stuff like a varchar.
274 return "
275 SELECT
276 cols.default,
277 subq.*
278 FROM (
279 SELECT DISTINCT
280 c.tabschema,
281 c.tabname,
282 c.colname,
283 c.colno,
284 c.typename,
285 c.nulls,
286 c.length,
287 c.scale,
288 c.identity,
289 tc.type AS tabconsttype,
290 c.remarks AS comment,
291 k.colseq,
292 CASE
293 WHEN c.generated = 'D' THEN 1
294 ELSE 0
295 END AS autoincrement
296 FROM syscat.columns c
297 LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
298 ON (k.tabschema = tc.tabschema
299 AND k.tabname = tc.tabname
300 AND tc.type = 'P'))
301 ON (c.tabschema = k.tabschema
302 AND c.tabname = k.tabname
303 AND c.colname = k.colname)
304 WHERE UPPER(c.tabname) = UPPER(" . $table . ")
305 ORDER BY c.colno
306 ) subq
307 JOIN syscat.columns cols
308 ON subq.tabschema = cols.tabschema
309 AND subq.tabname = cols.tabname
310 AND subq.colno = cols.colno
311 ORDER BY subq.colno
316 * {@inheritDoc}
318 public function getListTablesSQL()
320 return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
324 * {@inheritDoc}
326 public function getListViewsSQL($database)
328 return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS";
332 * {@inheritDoc}
334 public function getListTableIndexesSQL($table, $currentDatabase = null)
336 $table = $this->quoteStringLiteral($table);
338 return "SELECT idx.INDNAME AS key_name,
339 idxcol.COLNAME AS column_name,
340 CASE
341 WHEN idx.UNIQUERULE = 'P' THEN 1
342 ELSE 0
343 END AS primary,
344 CASE
345 WHEN idx.UNIQUERULE = 'D' THEN 1
346 ELSE 0
347 END AS non_unique
348 FROM SYSCAT.INDEXES AS idx
349 JOIN SYSCAT.INDEXCOLUSE AS idxcol
350 ON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAME
351 WHERE idx.TABNAME = UPPER(" . $table . ")
352 ORDER BY idxcol.COLSEQ ASC";
356 * {@inheritDoc}
358 public function getListTableForeignKeysSQL($table)
360 $table = $this->quoteStringLiteral($table);
362 return "SELECT fkcol.COLNAME AS local_column,
363 fk.REFTABNAME AS foreign_table,
364 pkcol.COLNAME AS foreign_column,
365 fk.CONSTNAME AS index_name,
366 CASE
367 WHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'
368 ELSE NULL
369 END AS on_update,
370 CASE
371 WHEN fk.DELETERULE = 'C' THEN 'CASCADE'
372 WHEN fk.DELETERULE = 'N' THEN 'SET NULL'
373 WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'
374 ELSE NULL
375 END AS on_delete
376 FROM SYSCAT.REFERENCES AS fk
377 JOIN SYSCAT.KEYCOLUSE AS fkcol
378 ON fk.CONSTNAME = fkcol.CONSTNAME
379 AND fk.TABSCHEMA = fkcol.TABSCHEMA
380 AND fk.TABNAME = fkcol.TABNAME
381 JOIN SYSCAT.KEYCOLUSE AS pkcol
382 ON fk.REFKEYNAME = pkcol.CONSTNAME
383 AND fk.REFTABSCHEMA = pkcol.TABSCHEMA
384 AND fk.REFTABNAME = pkcol.TABNAME
385 WHERE fk.TABNAME = UPPER(" . $table . ")
386 ORDER BY fkcol.COLSEQ ASC";
390 * {@inheritDoc}
392 public function getCreateViewSQL($name, $sql)
394 return "CREATE VIEW ".$name." AS ".$sql;
398 * {@inheritDoc}
400 public function getDropViewSQL($name)
402 return "DROP VIEW ".$name;
406 * {@inheritDoc}
408 public function getCreateDatabaseSQL($database)
410 return "CREATE DATABASE ".$database;
414 * {@inheritDoc}
416 public function getDropDatabaseSQL($database)
418 return "DROP DATABASE " . $database;
422 * {@inheritDoc}
424 public function supportsCreateDropDatabase()
426 return false;
430 * {@inheritDoc}
432 public function supportsReleaseSavepoints()
434 return false;
438 * {@inheritdoc}
440 public function supportsCommentOnStatement()
442 return true;
446 * {@inheritDoc}
448 public function getCurrentDateSQL()
450 return 'CURRENT DATE';
454 * {@inheritDoc}
456 public function getCurrentTimeSQL()
458 return 'CURRENT TIME';
462 * {@inheritDoc}
464 public function getCurrentTimestampSQL()
466 return "CURRENT TIMESTAMP";
470 * {@inheritDoc}
472 public function getIndexDeclarationSQL($name, Index $index)
474 // Index declaration in statements like CREATE TABLE is not supported.
475 throw DBALException::notSupported(__METHOD__);
479 * {@inheritDoc}
481 protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
483 $indexes = array();
484 if (isset($options['indexes'])) {
485 $indexes = $options['indexes'];
487 $options['indexes'] = array();
489 $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);
491 foreach ($indexes as $definition) {
492 $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
494 return $sqls;
498 * {@inheritDoc}
500 public function getAlterTableSQL(TableDiff $diff)
502 $sql = array();
503 $columnSql = array();
504 $commentsSQL = array();
506 $queryParts = array();
507 foreach ($diff->addedColumns as $column) {
508 if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
509 continue;
512 $columnDef = $column->toArray();
513 $queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);
515 // Adding non-nullable columns to a table requires a default value to be specified.
516 if ( ! empty($columnDef['notnull']) &&
517 ! isset($columnDef['default']) &&
518 empty($columnDef['autoincrement'])
520 $queryPart .= ' WITH DEFAULT';
523 $queryParts[] = $queryPart;
525 $comment = $this->getColumnComment($column);
527 if (null !== $comment && '' !== $comment) {
528 $commentsSQL[] = $this->getCommentOnColumnSQL(
529 $diff->getName($this)->getQuotedName($this),
530 $column->getQuotedName($this),
531 $comment
536 foreach ($diff->removedColumns as $column) {
537 if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
538 continue;
541 $queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this);
544 foreach ($diff->changedColumns as $columnDiff) {
545 if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
546 continue;
549 if ($columnDiff->hasChanged('comment')) {
550 $commentsSQL[] = $this->getCommentOnColumnSQL(
551 $diff->getName($this)->getQuotedName($this),
552 $columnDiff->column->getQuotedName($this),
553 $this->getColumnComment($columnDiff->column)
556 if (count($columnDiff->changedProperties) === 1) {
557 continue;
561 $this->gatherAlterColumnSQL($diff->fromTable, $columnDiff, $sql, $queryParts);
564 foreach ($diff->renamedColumns as $oldColumnName => $column) {
565 if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
566 continue;
569 $oldColumnName = new Identifier($oldColumnName);
571 $queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .
572 ' TO ' . $column->getQuotedName($this);
575 $tableSql = array();
577 if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
578 if (count($queryParts) > 0) {
579 $sql[] = 'ALTER TABLE ' . $diff->getName($this)->getQuotedName($this) . ' ' . implode(" ", $queryParts);
582 // Some table alteration operations require a table reorganization.
583 if ( ! empty($diff->removedColumns) || ! empty($diff->changedColumns)) {
584 $sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $diff->getName($this)->getQuotedName($this) . "')";
587 $sql = array_merge($sql, $commentsSQL);
589 if ($diff->newName !== false) {
590 $sql[] = 'RENAME TABLE ' . $diff->getName($this)->getQuotedName($this) . ' TO ' . $diff->getNewName()->getQuotedName($this);
593 $sql = array_merge(
594 $this->getPreAlterTableIndexForeignKeySQL($diff),
595 $sql,
596 $this->getPostAlterTableIndexForeignKeySQL($diff)
600 return array_merge($sql, $tableSql, $columnSql);
604 * Gathers the table alteration SQL for a given column diff.
606 * @param Table $table The table to gather the SQL for.
607 * @param ColumnDiff $columnDiff The column diff to evaluate.
608 * @param array $sql The sequence of table alteration statements to fill.
609 * @param array $queryParts The sequence of column alteration clauses to fill.
611 private function gatherAlterColumnSQL(Table $table, ColumnDiff $columnDiff, array &$sql, array &$queryParts)
613 $alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);
615 if (empty($alterColumnClauses)) {
616 return;
619 // If we have a single column alteration, we can append the clause to the main query.
620 if (count($alterColumnClauses) === 1) {
621 $queryParts[] = current($alterColumnClauses);
623 return;
626 // We have multiple alterations for the same column,
627 // so we need to trigger a complete ALTER TABLE statement
628 // for each ALTER COLUMN clause.
629 foreach ($alterColumnClauses as $alterColumnClause) {
630 $sql[] = 'ALTER TABLE ' . $table->getQuotedName($this) . ' ' . $alterColumnClause;
635 * Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.
637 * @param ColumnDiff $columnDiff The column diff to evaluate.
639 * @return array
641 private function getAlterColumnClausesSQL(ColumnDiff $columnDiff)
643 $column = $columnDiff->column->toArray();
645 $alterClause = 'ALTER COLUMN ' . $columnDiff->column->getQuotedName($this);
647 if ($column['columnDefinition']) {
648 return array($alterClause . ' ' . $column['columnDefinition']);
651 $clauses = array();
653 if ($columnDiff->hasChanged('type') ||
654 $columnDiff->hasChanged('length') ||
655 $columnDiff->hasChanged('precision') ||
656 $columnDiff->hasChanged('scale') ||
657 $columnDiff->hasChanged('fixed')
659 $clauses[] = $alterClause . ' SET DATA TYPE ' . $column['type']->getSQLDeclaration($column, $this);
662 if ($columnDiff->hasChanged('notnull')) {
663 $clauses[] = $column['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';
666 if ($columnDiff->hasChanged('default')) {
667 if (isset($column['default'])) {
668 $defaultClause = $this->getDefaultValueDeclarationSQL($column);
670 if ($defaultClause) {
671 $clauses[] = $alterClause . ' SET' . $defaultClause;
673 } else {
674 $clauses[] = $alterClause . ' DROP DEFAULT';
678 return $clauses;
682 * {@inheritDoc}
684 protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff)
686 $sql = array();
687 $table = $diff->getName($this)->getQuotedName($this);
689 foreach ($diff->removedIndexes as $remKey => $remIndex) {
690 foreach ($diff->addedIndexes as $addKey => $addIndex) {
691 if ($remIndex->getColumns() == $addIndex->getColumns()) {
692 if ($remIndex->isPrimary()) {
693 $sql[] = 'ALTER TABLE ' . $table . ' DROP PRIMARY KEY';
694 } elseif ($remIndex->isUnique()) {
695 $sql[] = 'ALTER TABLE ' . $table . ' DROP UNIQUE ' . $remIndex->getQuotedName($this);
696 } else {
697 $sql[] = $this->getDropIndexSQL($remIndex, $table);
700 $sql[] = $this->getCreateIndexSQL($addIndex, $table);
702 unset($diff->removedIndexes[$remKey]);
703 unset($diff->addedIndexes[$addKey]);
705 break;
710 $sql = array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));
712 return $sql;
716 * {@inheritdoc}
718 protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName)
720 if (strpos($tableName, '.') !== false) {
721 list($schema) = explode('.', $tableName);
722 $oldIndexName = $schema . '.' . $oldIndexName;
725 return array('RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this));
729 * {@inheritDoc}
731 public function getDefaultValueDeclarationSQL($field)
733 if ( ! empty($field['autoincrement'])) {
734 return '';
737 if (isset($field['version']) && $field['version']) {
738 if ((string) $field['type'] != "DateTime") {
739 $field['default'] = "1";
743 return parent::getDefaultValueDeclarationSQL($field);
747 * {@inheritDoc}
749 public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
751 return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
755 * {@inheritDoc}
757 public function getCreateTemporaryTableSnippetSQL()
759 return "DECLARE GLOBAL TEMPORARY TABLE";
763 * {@inheritDoc}
765 public function getTemporaryTableName($tableName)
767 return "SESSION." . $tableName;
771 * {@inheritDoc}
773 protected function doModifyLimitQuery($query, $limit, $offset = null)
775 $where = array();
777 if ($offset > 0) {
778 $where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);
781 if ($limit !== null) {
782 $where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);
785 if (empty($where)) {
786 return $query;
789 // Todo OVER() needs ORDER BY data!
790 return sprintf(
791 'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',
792 $query,
793 implode(' AND ', $where)
798 * {@inheritDoc}
800 public function getLocateExpression($str, $substr, $startPos = false)
802 if ($startPos == false) {
803 return 'LOCATE(' . $substr . ', ' . $str . ')';
806 return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
810 * {@inheritDoc}
812 public function getSubstringExpression($value, $from, $length = null)
814 if ($length === null) {
815 return 'SUBSTR(' . $value . ', ' . $from . ')';
818 return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
822 * {@inheritDoc}
824 public function supportsIdentityColumns()
826 return true;
830 * {@inheritDoc}
832 public function prefersIdentityColumns()
834 return true;
838 * {@inheritDoc}
840 * DB2 returns all column names in SQL result sets in uppercase.
842 public function getSQLResultCasing($column)
844 return strtoupper($column);
848 * {@inheritDoc}
850 public function getForUpdateSQL()
852 return ' WITH RR USE AND KEEP UPDATE LOCKS';
856 * {@inheritDoc}
858 public function getDummySelectSQL()
860 return 'SELECT 1 FROM sysibm.sysdummy1';
864 * {@inheritDoc}
866 * DB2 supports savepoints, but they work semantically different than on other vendor platforms.
868 * TODO: We have to investigate how to get DB2 up and running with savepoints.
870 public function supportsSavepoints()
872 return false;
876 * {@inheritDoc}
878 protected function getReservedKeywordsClass()
880 return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords';