Blame | Letzte Änderung | Log anzeigen | RSS feed
<?phpnamespace Doctrine\DBAL\Platforms;use Doctrine\DBAL\Connection;use Doctrine\DBAL\Exception;use Doctrine\DBAL\Schema\ColumnDiff;use Doctrine\DBAL\Schema\DB2SchemaManager;use Doctrine\DBAL\Schema\Identifier;use Doctrine\DBAL\Schema\Index;use Doctrine\DBAL\Schema\TableDiff;use Doctrine\DBAL\Types\Type;use Doctrine\DBAL\Types\Types;use Doctrine\Deprecations\Deprecation;use function array_merge;use function count;use function current;use function explode;use function func_get_arg;use function func_num_args;use function implode;use function sprintf;use function strpos;class DB2Platform extends AbstractPlatform{/*** {@inheritdoc}** @deprecated*/public function getCharMaxLength(): int{Deprecation::triggerIfCalledFromOutside('doctrine/dbal','https://github.com/doctrine/dbal/issues/3263','DB2Platform::getCharMaxLength() is deprecated.',);return 254;}/*** {@inheritdoc}** @deprecated*/public function getBinaryMaxLength(){Deprecation::triggerIfCalledFromOutside('doctrine/dbal','https://github.com/doctrine/dbal/issues/3263','DB2Platform::getBinaryMaxLength() is deprecated.',);return 32704;}/*** {@inheritdoc}** @deprecated*/public function getBinaryDefaultLength(){Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/issues/3263','Relying on the default binary column length is deprecated, specify the length explicitly.',);return 1;}/*** {@inheritDoc}*/public function getVarcharTypeDeclarationSQL(array $column){// for IBM DB2, the CHAR max length is less than VARCHAR default lengthif (! isset($column['length']) && ! empty($column['fixed'])) {$column['length'] = $this->getCharMaxLength();}return parent::getVarcharTypeDeclarationSQL($column);}/*** {@inheritDoc}*/public function getBlobTypeDeclarationSQL(array $column){// todo blob(n) with $column['length'];return 'BLOB(1M)';}/*** {@inheritDoc}*/protected function initializeDoctrineTypeMappings(){$this->doctrineTypeMapping = ['bigint' => 'bigint','binary' => 'binary','blob' => 'blob','character' => 'string','clob' => 'text','date' => 'date','decimal' => 'decimal','double' => 'float','integer' => 'integer','real' => 'float','smallint' => 'smallint','time' => 'time','timestamp' => 'datetime','varbinary' => 'binary','varchar' => 'string',];}/*** {@inheritdoc}*/public function isCommentedDoctrineType(Type $doctrineType){Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/pull/5058','%s is deprecated and will be removed in Doctrine DBAL 4.0. Use Type::requiresSQLCommentHint() instead.',__METHOD__,);if ($doctrineType->getName() === Types::BOOLEAN) {// We require a commented boolean type in order to distinguish between boolean and smallint// as both (have to) map to the same native type.return true;}return parent::isCommentedDoctrineType($doctrineType);}/*** {@inheritDoc}*/protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/){if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) {Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/issues/3263','Relying on the default string column length on IBM DB2 is deprecated'. ', specify the length explicitly.',);}return $fixed ? ($length > 0 ? 'CHAR(' . $length . ')' : 'CHAR(254)'): ($length > 0 ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');}/*** {@inheritdoc}*/protected function getBinaryTypeDeclarationSQLSnippet($length, $fixed/*, $lengthOmitted = false*/){if ($length <= 0 || (func_num_args() > 2 && func_get_arg(2))) {Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/issues/3263','Relying on the default binary column length on IBM DB2 is deprecated'. ', specify the length explicitly.',);}return $this->getVarcharTypeDeclarationSQLSnippet($length, $fixed) . ' FOR BIT DATA';}/*** {@inheritDoc}*/public function getClobTypeDeclarationSQL(array $column){// todo clob(n) with $column['length'];return 'CLOB(1M)';}/*** {@inheritDoc}*/public function getName(){Deprecation::triggerIfCalledFromOutside('doctrine/dbal','https://github.com/doctrine/dbal/issues/4749','DB2Platform::getName() is deprecated. Identify platforms by their class.',);return 'db2';}/*** {@inheritDoc}*/public function getBooleanTypeDeclarationSQL(array $column){return 'SMALLINT';}/*** {@inheritDoc}*/public function getIntegerTypeDeclarationSQL(array $column){return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($column);}/*** {@inheritDoc}*/public function getBigIntTypeDeclarationSQL(array $column){return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);}/*** {@inheritDoc}*/public function getSmallIntTypeDeclarationSQL(array $column){return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($column);}/*** {@inheritDoc}*/protected function _getCommonIntegerTypeDeclarationSQL(array $column){$autoinc = '';if (! empty($column['autoincrement'])) {$autoinc = ' GENERATED BY DEFAULT AS IDENTITY';}return $autoinc;}/*** {@inheritdoc}*/public function getBitAndComparisonExpression($value1, $value2){return 'BITAND(' . $value1 . ', ' . $value2 . ')';}/*** {@inheritdoc}*/public function getBitOrComparisonExpression($value1, $value2){return 'BITOR(' . $value1 . ', ' . $value2 . ')';}/*** {@inheritdoc}*/protected function getDateArithmeticIntervalExpression($date, $operator, $interval, $unit){switch ($unit) {case DateIntervalUnit::WEEK:$interval *= 7;$unit = DateIntervalUnit::DAY;break;case DateIntervalUnit::QUARTER:$interval *= 3;$unit = DateIntervalUnit::MONTH;break;}return $date . ' ' . $operator . ' ' . $interval . ' ' . $unit;}/*** {@inheritdoc}*/public function getDateDiffExpression($date1, $date2){return 'DAYS(' . $date1 . ') - DAYS(' . $date2 . ')';}/*** {@inheritDoc}*/public function getDateTimeTypeDeclarationSQL(array $column){if (isset($column['version']) && $column['version'] === true) {return 'TIMESTAMP(0) WITH DEFAULT';}return 'TIMESTAMP(0)';}/*** {@inheritDoc}*/public function getDateTypeDeclarationSQL(array $column){return 'DATE';}/*** {@inheritDoc}*/public function getTimeTypeDeclarationSQL(array $column){return 'TIME';}/*** {@inheritdoc}*/public function getTruncateTableSQL($tableName, $cascade = false){$tableIdentifier = new Identifier($tableName);return 'TRUNCATE ' . $tableIdentifier->getQuotedName($this) . ' IMMEDIATE';}/*** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.** This code fragment is originally from the Zend_Db_Adapter_Db2 class, but has been edited.** @param string $table* @param string $database** @return string*/public function getListTableColumnsSQL($table, $database = null){$table = $this->quoteStringLiteral($table);// We do the funky subquery and join syscat.columns.default this crazy way because// as of db2 v10, the column is CLOB(64k) and the distinct operator won't allow a CLOB,// it wants shorter stuff like a varchar.return "SELECTcols.default,subq.*FROM (SELECT DISTINCTc.tabschema,c.tabname,c.colname,c.colno,c.typename,c.codepage,c.nulls,c.length,c.scale,c.identity,tc.type AS tabconsttype,c.remarks AS comment,k.colseq,CASEWHEN c.generated = 'D' THEN 1ELSE 0END AS autoincrementFROM syscat.columns cLEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tcON (k.tabschema = tc.tabschemaAND k.tabname = tc.tabnameAND tc.type = 'P'))ON (c.tabschema = k.tabschemaAND c.tabname = k.tabnameAND c.colname = k.colname)WHERE UPPER(c.tabname) = UPPER(" . $table . ')ORDER BY c.colno) subqJOIN syscat.columns colsON subq.tabschema = cols.tabschemaAND subq.tabname = cols.tabnameAND subq.colno = cols.colnoORDER BY subq.colno';}/*** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.** {@inheritDoc}*/public function getListTablesSQL(){return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T' AND CREATOR = CURRENT_USER";}/*** {@inheritDoc}** @internal The method should be only used from within the {@see AbstractSchemaManager} class hierarchy.*/public function getListViewsSQL($database){return 'SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS';}/*** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.** {@inheritDoc}*/public function getListTableIndexesSQL($table, $database = null){$table = $this->quoteStringLiteral($table);return "SELECT idx.INDNAME AS key_name,idxcol.COLNAME AS column_name,CASEWHEN idx.UNIQUERULE = 'P' THEN 1ELSE 0END AS primary,CASEWHEN idx.UNIQUERULE = 'D' THEN 1ELSE 0END AS non_uniqueFROM SYSCAT.INDEXES AS idxJOIN SYSCAT.INDEXCOLUSE AS idxcolON idx.INDSCHEMA = idxcol.INDSCHEMA AND idx.INDNAME = idxcol.INDNAMEWHERE idx.TABNAME = UPPER(" . $table . ')ORDER BY idxcol.COLSEQ ASC';}/*** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon.** {@inheritDoc}*/public function getListTableForeignKeysSQL($table){$table = $this->quoteStringLiteral($table);return "SELECT fkcol.COLNAME AS local_column,fk.REFTABNAME AS foreign_table,pkcol.COLNAME AS foreign_column,fk.CONSTNAME AS index_name,CASEWHEN fk.UPDATERULE = 'R' THEN 'RESTRICT'ELSE NULLEND AS on_update,CASEWHEN fk.DELETERULE = 'C' THEN 'CASCADE'WHEN fk.DELETERULE = 'N' THEN 'SET NULL'WHEN fk.DELETERULE = 'R' THEN 'RESTRICT'ELSE NULLEND AS on_deleteFROM SYSCAT.REFERENCES AS fkJOIN SYSCAT.KEYCOLUSE AS fkcolON fk.CONSTNAME = fkcol.CONSTNAMEAND fk.TABSCHEMA = fkcol.TABSCHEMAAND fk.TABNAME = fkcol.TABNAMEJOIN SYSCAT.KEYCOLUSE AS pkcolON fk.REFKEYNAME = pkcol.CONSTNAMEAND fk.REFTABSCHEMA = pkcol.TABSCHEMAAND fk.REFTABNAME = pkcol.TABNAMEWHERE fk.TABNAME = UPPER(" . $table . ')ORDER BY fkcol.COLSEQ ASC';}/*** {@inheritDoc}** @deprecated*/public function supportsCreateDropDatabase(){Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/pull/5513','%s is deprecated.',__METHOD__,);return false;}/*** {@inheritdoc}** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.*/public function supportsCommentOnStatement(){return true;}/*** {@inheritDoc}*/public function getCurrentDateSQL(){return 'CURRENT DATE';}/*** {@inheritDoc}*/public function getCurrentTimeSQL(){return 'CURRENT TIME';}/*** {@inheritDoc}*/public function getCurrentTimestampSQL(){return 'CURRENT TIMESTAMP';}/*** {@inheritDoc}** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.*/public function getIndexDeclarationSQL($name, Index $index){// Index declaration in statements like CREATE TABLE is not supported.throw Exception::notSupported(__METHOD__);}/*** {@inheritDoc}*/protected function _getCreateTableSQL($name, array $columns, array $options = []){$indexes = [];if (isset($options['indexes'])) {$indexes = $options['indexes'];}$options['indexes'] = [];$sqls = parent::_getCreateTableSQL($name, $columns, $options);foreach ($indexes as $definition) {$sqls[] = $this->getCreateIndexSQL($definition, $name);}return $sqls;}/*** {@inheritDoc}*/public function getAlterTableSQL(TableDiff $diff){$sql = [];$columnSql = [];$commentsSQL = [];$tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this);$queryParts = [];foreach ($diff->getAddedColumns() as $column) {if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {continue;}$columnDef = $column->toArray();$queryPart = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $columnDef);// Adding non-nullable columns to a table requires a default value to be specified.if (! empty($columnDef['notnull']) &&! isset($columnDef['default']) &&empty($columnDef['autoincrement'])) {$queryPart .= ' WITH DEFAULT';}$queryParts[] = $queryPart;$comment = $this->getColumnComment($column);if ($comment === null || $comment === '') {continue;}$commentsSQL[] = $this->getCommentOnColumnSQL($tableNameSQL,$column->getQuotedName($this),$comment,);}foreach ($diff->getDroppedColumns() as $column) {if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {continue;}$queryParts[] = 'DROP COLUMN ' . $column->getQuotedName($this);}foreach ($diff->getModifiedColumns() as $columnDiff) {if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {continue;}if ($columnDiff->hasCommentChanged()) {$commentsSQL[] = $this->getCommentOnColumnSQL($tableNameSQL,$columnDiff->getNewColumn()->getQuotedName($this),$this->getColumnComment($columnDiff->getNewColumn()),);}$this->gatherAlterColumnSQL($tableNameSQL,$columnDiff,$sql,$queryParts,);}foreach ($diff->getRenamedColumns() as $oldColumnName => $column) {if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {continue;}$oldColumnName = new Identifier($oldColumnName);$queryParts[] = 'RENAME COLUMN ' . $oldColumnName->getQuotedName($this) .' TO ' . $column->getQuotedName($this);}$tableSql = [];if (! $this->onSchemaAlterTable($diff, $tableSql)) {if (count($queryParts) > 0) {$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' ' . implode(' ', $queryParts);}// Some table alteration operations require a table reorganization.if (count($diff->getDroppedColumns()) > 0 || count($diff->getModifiedColumns()) > 0) {$sql[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE " . $tableNameSQL . "')";}$sql = array_merge($sql, $commentsSQL);$newName = $diff->getNewName();if ($newName !== false) {Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/pull/5663','Generation of "rename table" SQL using %s is deprecated. Use getRenameTableSQL() instead.',__METHOD__,);$sql[] = sprintf('RENAME TABLE %s TO %s',$tableNameSQL,$newName->getQuotedName($this),);}$sql = array_merge($this->getPreAlterTableIndexForeignKeySQL($diff),$sql,$this->getPostAlterTableIndexForeignKeySQL($diff),);}return array_merge($sql, $tableSql, $columnSql);}/*** {@inheritDoc}*/public function getRenameTableSQL(string $oldName, string $newName): array{return [sprintf('RENAME TABLE %s TO %s', $oldName, $newName),];}/*** Gathers the table alteration SQL for a given column diff.** @param string $table The table to gather the SQL for.* @param ColumnDiff $columnDiff The column diff to evaluate.* @param string[] $sql The sequence of table alteration statements to fill.* @param mixed[] $queryParts The sequence of column alteration clauses to fill.*/private function gatherAlterColumnSQL(string $table,ColumnDiff $columnDiff,array &$sql,array &$queryParts): void {$alterColumnClauses = $this->getAlterColumnClausesSQL($columnDiff);if (empty($alterColumnClauses)) {return;}// If we have a single column alteration, we can append the clause to the main query.if (count($alterColumnClauses) === 1) {$queryParts[] = current($alterColumnClauses);return;}// We have multiple alterations for the same column,// so we need to trigger a complete ALTER TABLE statement// for each ALTER COLUMN clause.foreach ($alterColumnClauses as $alterColumnClause) {$sql[] = 'ALTER TABLE ' . $table . ' ' . $alterColumnClause;}}/*** Returns the ALTER COLUMN SQL clauses for altering a column described by the given column diff.** @return string[]*/private function getAlterColumnClausesSQL(ColumnDiff $columnDiff): array{$newColumn = $columnDiff->getNewColumn()->toArray();$alterClause = 'ALTER COLUMN ' . $columnDiff->getNewColumn()->getQuotedName($this);if ($newColumn['columnDefinition'] !== null) {return [$alterClause . ' ' . $newColumn['columnDefinition']];}$clauses = [];if ($columnDiff->hasTypeChanged() ||$columnDiff->hasLengthChanged() ||$columnDiff->hasPrecisionChanged() ||$columnDiff->hasScaleChanged() ||$columnDiff->hasFixedChanged()) {$clauses[] = $alterClause . ' SET DATA TYPE ' . $newColumn['type']->getSQLDeclaration($newColumn, $this);}if ($columnDiff->hasNotNullChanged()) {$clauses[] = $newColumn['notnull'] ? $alterClause . ' SET NOT NULL' : $alterClause . ' DROP NOT NULL';}if ($columnDiff->hasDefaultChanged()) {if (isset($newColumn['default'])) {$defaultClause = $this->getDefaultValueDeclarationSQL($newColumn);if ($defaultClause !== '') {$clauses[] = $alterClause . ' SET' . $defaultClause;}} else {$clauses[] = $alterClause . ' DROP DEFAULT';}}return $clauses;}/*** {@inheritDoc}*/protected function getPreAlterTableIndexForeignKeySQL(TableDiff $diff){$sql = [];$tableNameSQL = ($diff->getOldTable() ?? $diff->getName($this))->getQuotedName($this);foreach ($diff->getDroppedIndexes() as $droppedIndex) {foreach ($diff->getAddedIndexes() as $addedIndex) {if ($droppedIndex->getColumns() !== $addedIndex->getColumns()) {continue;}if ($droppedIndex->isPrimary()) {$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP PRIMARY KEY';} elseif ($droppedIndex->isUnique()) {$sql[] = 'ALTER TABLE ' . $tableNameSQL . ' DROP UNIQUE ' . $droppedIndex->getQuotedName($this);} else {$sql[] = $this->getDropIndexSQL($droppedIndex, $tableNameSQL);}$sql[] = $this->getCreateIndexSQL($addedIndex, $tableNameSQL);$diff->unsetAddedIndex($addedIndex);$diff->unsetDroppedIndex($droppedIndex);break;}}return array_merge($sql, parent::getPreAlterTableIndexForeignKeySQL($diff));}/*** {@inheritdoc}*/protected function getRenameIndexSQL($oldIndexName, Index $index, $tableName){if (strpos($tableName, '.') !== false) {[$schema] = explode('.', $tableName);$oldIndexName = $schema . '.' . $oldIndexName;}return ['RENAME INDEX ' . $oldIndexName . ' TO ' . $index->getQuotedName($this)];}/*** {@inheritDoc}** @internal The method should be only used from within the {@see AbstractPlatform} class hierarchy.*/public function getDefaultValueDeclarationSQL($column){if (! empty($column['autoincrement'])) {return '';}if (! empty($column['version'])) {if ((string) $column['type'] !== 'DateTime') {$column['default'] = '1';}}return parent::getDefaultValueDeclarationSQL($column);}/*** {@inheritDoc}*/public function getEmptyIdentityInsertSQL($quotedTableName, $quotedIdentifierColumnName){return 'INSERT INTO ' . $quotedTableName . ' (' . $quotedIdentifierColumnName . ') VALUES (DEFAULT)';}/*** {@inheritDoc}*/public function getCreateTemporaryTableSnippetSQL(){return 'DECLARE GLOBAL TEMPORARY TABLE';}/*** {@inheritDoc}*/public function getTemporaryTableName($tableName){return 'SESSION.' . $tableName;}/*** {@inheritDoc}*/protected function doModifyLimitQuery($query, $limit, $offset){$where = [];if ($offset > 0) {$where[] = sprintf('db22.DC_ROWNUM >= %d', $offset + 1);}if ($limit !== null) {$where[] = sprintf('db22.DC_ROWNUM <= %d', $offset + $limit);}if (empty($where)) {return $query;}// Todo OVER() needs ORDER BY data!return sprintf('SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (%s) db21) db22 WHERE %s',$query,implode(' AND ', $where),);}/*** {@inheritDoc}*/public function getLocateExpression($str, $substr, $startPos = false){if ($startPos === false) {return 'LOCATE(' . $substr . ', ' . $str . ')';}return 'LOCATE(' . $substr . ', ' . $str . ', ' . $startPos . ')';}/*** {@inheritDoc}*/public function getSubstringExpression($string, $start, $length = null){if ($length === null) {return 'SUBSTR(' . $string . ', ' . $start . ')';}return 'SUBSTR(' . $string . ', ' . $start . ', ' . $length . ')';}/*** {@inheritDoc}*/public function getLengthExpression($column){return 'LENGTH(' . $column . ', CODEUNITS32)';}public function getCurrentDatabaseExpression(): string{return 'CURRENT_USER';}/*** {@inheritDoc}*/public function supportsIdentityColumns(){return true;}/*** {@inheritDoc}** @deprecated*/public function prefersIdentityColumns(){Deprecation::trigger('doctrine/dbal','https://github.com/doctrine/dbal/pull/1519','DB2Platform::prefersIdentityColumns() is deprecated.',);return true;}/*** {@inheritDoc}*/public function getForUpdateSQL(){return ' WITH RR USE AND KEEP UPDATE LOCKS';}/*** {@inheritDoc}*/public function getDummySelectSQL(){$expression = func_num_args() > 0 ? func_get_arg(0) : '1';return sprintf('SELECT %s FROM sysibm.sysdummy1', $expression);}/*** {@inheritDoc}** DB2 supports savepoints, but they work semantically different than on other vendor platforms.** TODO: We have to investigate how to get DB2 up and running with savepoints.*/public function supportsSavepoints(){return false;}/*** {@inheritDoc}** @deprecated Implement {@see createReservedKeywordsList()} instead.*/protected function getReservedKeywordsClass(){Deprecation::triggerIfCalledFromOutside('doctrine/dbal','https://github.com/doctrine/dbal/issues/4510','DB2Platform::getReservedKeywordsClass() is deprecated,'. ' use DB2Platform::createReservedKeywordsList() instead.',);return Keywords\DB2Keywords::class;}/** @deprecated The SQL used for schema introspection is an implementation detail and should not be relied upon. */public function getListTableCommentsSQL(string $table): string{return sprintf(<<<'SQL'SELECT REMARKSFROM SYSIBM.SYSTABLESWHERE NAME = UPPER( %s )SQL,$this->quoteStringLiteral($table),);}public function createSchemaManager(Connection $connection): DB2SchemaManager{return new DB2SchemaManager($connection, $this);}}