DB2Platform.php 14.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php
/*
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
Benjamin Eberlei's avatar
Benjamin Eberlei committed
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
Benjamin Morel's avatar
Benjamin Morel committed
18
 */
19 20 21

namespace Doctrine\DBAL\Platforms;

22 23 24 25
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\TableDiff;

26
class DB2Platform extends AbstractPlatform
27
{
28
    /**
29
     * {@inheritDoc}
30 31 32 33 34 35
     */
    public function getBlobTypeDeclarationSQL(array $field)
    {
        throw DBALException::notSupported(__METHOD__);
    }

36 37 38
    /**
     * {@inheritDoc}
     */
39 40 41 42 43 44 45 46 47 48 49 50
    public function initializeDoctrineTypeMappings()
    {
        $this->doctrineTypeMapping = array(
            'smallint'      => 'smallint',
            'bigint'        => 'bigint',
            'integer'       => 'integer',
            'time'          => 'time',
            'date'          => 'date',
            'varchar'       => 'string',
            'character'     => 'string',
            'clob'          => 'text',
            'decimal'       => 'decimal',
51 52
            'double'        => 'float',
            'real'          => 'float',
53 54 55 56
            'timestamp'     => 'datetime',
        );
    }

57
    /**
58
     * {@inheritDoc}
59
     */
60
    protected function getVarcharTypeDeclarationSQLSnippet($length, $fixed)
61 62 63 64 65 66
    {
        return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
                : ($length ? 'VARCHAR(' . $length . ')' : 'VARCHAR(255)');
    }

    /**
67
     * {@inheritDoc}
68 69 70 71 72 73 74 75
     */
    public function getClobTypeDeclarationSQL(array $field)
    {
        // todo clob(n) with $field['length'];
        return 'CLOB(1M)';
    }

    /**
76
     * {@inheritDoc}
77 78 79 80 81 82 83
     */
    public function getName()
    {
        return 'db2';
    }

    /**
84
     * {@inheritDoc}
85 86 87 88 89 90 91
     */
    public function getBooleanTypeDeclarationSQL(array $columnDef)
    {
        return 'SMALLINT';
    }

    /**
92
     * {@inheritDoc}
93 94 95
     */
    public function getIntegerTypeDeclarationSQL(array $columnDef)
    {
96
        return 'INTEGER' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
97 98 99
    }

    /**
100
     * {@inheritDoc}
101 102 103
     */
    public function getBigIntTypeDeclarationSQL(array $columnDef)
    {
104
        return 'BIGINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
105 106 107
    }

    /**
108
     * {@inheritDoc}
109 110 111
     */
    public function getSmallIntTypeDeclarationSQL(array $columnDef)
    {
112
        return 'SMALLINT' . $this->_getCommonIntegerTypeDeclarationSQL($columnDef);
113 114 115
    }

    /**
116
     * {@inheritDoc}
117 118 119
     */
    protected function _getCommonIntegerTypeDeclarationSQL(array $columnDef)
    {
120 121 122 123
        $autoinc = '';
        if ( ! empty($columnDef['autoincrement'])) {
            $autoinc = ' GENERATED BY DEFAULT AS IDENTITY';
        }
124

125
        return $autoinc;
126 127
    }

128
    /**
129
     * {@inheritDoc}
130 131 132
     */
    public function getDateTimeTypeDeclarationSQL(array $fieldDeclaration)
    {
133 134 135 136
        if (isset($fieldDeclaration['version']) && $fieldDeclaration['version'] == true) {
            return "TIMESTAMP(0) WITH DEFAULT";
        }

137 138 139 140
        return 'TIMESTAMP(0)';
    }

    /**
141
     * {@inheritDoc}
142 143 144 145 146 147 148
     */
    public function getDateTypeDeclarationSQL(array $fieldDeclaration)
    {
        return 'DATE';
    }

    /**
149
     * {@inheritDoc}
150 151 152 153 154 155
     */
    public function getTimeTypeDeclarationSQL(array $fieldDeclaration)
    {
        return 'TIME';
    }

Benjamin Morel's avatar
Benjamin Morel committed
156 157 158
    /**
     * {@inheritDoc}
     */
159 160 161 162 163
    public function getListDatabasesSQL()
    {
        throw DBALException::notSupported(__METHOD__);
    }

Benjamin Morel's avatar
Benjamin Morel committed
164 165 166
    /**
     * {@inheritDoc}
     */
167 168 169 170 171
    public function getListSequencesSQL($database)
    {
        throw DBALException::notSupported(__METHOD__);
    }

Benjamin Morel's avatar
Benjamin Morel committed
172 173 174
    /**
     * {@inheritDoc}
     */
175 176 177 178 179
    public function getListTableConstraintsSQL($table)
    {
        throw DBALException::notSupported(__METHOD__);
    }

180 181 182 183
    /**
     * This code fragment is originally from the Zend_Db_Adapter_Db2 class.
     *
     * @license New BSD License
Benjamin Morel's avatar
Benjamin Morel committed
184 185
     *
     * @param string $table
Christophe Coevoet's avatar
Christophe Coevoet committed
186
     * @param string $database
Benjamin Morel's avatar
Benjamin Morel committed
187
     *
188 189
     * @return string
     */
190
    public function getListTableColumnsSQL($table, $database = null)
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
    {
        return "SELECT DISTINCT c.tabschema, c.tabname, c.colname, c.colno,
                c.typename, c.default, c.nulls, c.length, c.scale,
                c.identity, tc.type AS tabconsttype, k.colseq
                FROM syscat.columns c
                LEFT JOIN (syscat.keycoluse k JOIN syscat.tabconst tc
                ON (k.tabschema = tc.tabschema
                    AND k.tabname = tc.tabname
                    AND tc.type = 'P'))
                ON (c.tabschema = k.tabschema
                    AND c.tabname = k.tabname
                    AND c.colname = k.colname)
                WHERE UPPER(c.tabname) = UPPER('" . $table . "') ORDER BY c.colno";
    }

Benjamin Morel's avatar
Benjamin Morel committed
206 207 208
    /**
     * {@inheritDoc}
     */
209 210
    public function getListTablesSQL()
    {
211
        return "SELECT NAME FROM SYSIBM.SYSTABLES WHERE TYPE = 'T'";
212 213
    }

Benjamin Morel's avatar
Benjamin Morel committed
214 215 216
    /**
     * {@inheritDoc}
     */
217 218 219 220 221 222
    public function getListUsersSQL()
    {
        throw DBALException::notSupported(__METHOD__);
    }

    /**
223
     * {@inheritDoc}
224 225 226 227 228 229
     */
    public function getListViewsSQL($database)
    {
        return "SELECT NAME, TEXT FROM SYSIBM.SYSVIEWS";
    }

230 231 232
    /**
     * {@inheritDoc}
     */
233
    public function getListTableIndexesSQL($table, $currentDatabase = null)
234
    {
235
        return "SELECT NAME, COLNAMES, UNIQUERULE FROM SYSIBM.SYSINDEXES WHERE TBNAME = UPPER('" . $table . "')";
236 237
    }

Benjamin Morel's avatar
Benjamin Morel committed
238 239 240
    /**
     * {@inheritDoc}
     */
241 242
    public function getListTableForeignKeysSQL($table)
    {
243 244
        return "SELECT TBNAME, RELNAME, REFTBNAME, DELETERULE, UPDATERULE, FKCOLNAMES, PKCOLNAMES ".
               "FROM SYSIBM.SYSRELS WHERE TBNAME = UPPER('".$table."')";
245 246
    }

Benjamin Morel's avatar
Benjamin Morel committed
247 248 249
    /**
     * {@inheritDoc}
     */
250 251 252 253 254
    public function getCreateViewSQL($name, $sql)
    {
        return "CREATE VIEW ".$name." AS ".$sql;
    }

Benjamin Morel's avatar
Benjamin Morel committed
255 256 257
    /**
     * {@inheritDoc}
     */
258 259 260 261 262
    public function getDropViewSQL($name)
    {
        return "DROP VIEW ".$name;
    }

263 264 265
    /**
     * {@inheritDoc}
     */
266 267 268 269 270
    public function getDropSequenceSQL($sequence)
    {
        throw DBALException::notSupported(__METHOD__);
    }

Benjamin Morel's avatar
Benjamin Morel committed
271 272 273
    /**
     * {@inheritDoc}
     */
274 275 276 277 278
    public function getSequenceNextValSQL($sequenceName)
    {
        throw DBALException::notSupported(__METHOD__);
    }

279 280 281
    /**
     * {@inheritDoc}
     */
282 283 284 285 286
    public function getCreateDatabaseSQL($database)
    {
        return "CREATE DATABASE ".$database;
    }

287 288 289
    /**
     * {@inheritDoc}
     */
290 291 292 293 294
    public function getDropDatabaseSQL($database)
    {
        return "DROP DATABASE ".$database.";";
    }

295 296 297
    /**
     * {@inheritDoc}
     */
298 299 300 301
    public function supportsCreateDropDatabase()
    {
        return false;
    }
302

303
    /**
304
     * {@inheritDoc}
305 306 307 308 309 310
     */
    public function supportsReleaseSavepoints()
    {
        return false;
    }

311
    /**
312
     * {@inheritDoc}
313 314 315
     */
    public function getCurrentDateSQL()
    {
316
        return 'VALUES CURRENT DATE';
317 318 319
    }

    /**
320
     * {@inheritDoc}
321 322 323
     */
    public function getCurrentTimeSQL()
    {
324
        return 'VALUES CURRENT TIME';
325 326 327
    }

    /**
328
     * {@inheritDoc}
329
     */
330
    public function getCurrentTimestampSQL()
331
    {
332 333
        return "VALUES CURRENT TIMESTAMP";
    }
334 335

    /**
336
     * {@inheritDoc}
337 338 339 340 341 342 343
     */
    public function getIndexDeclarationSQL($name, Index $index)
    {
        return $this->getUniqueConstraintDeclarationSQL($name, $index);
    }

    /**
344
     * {@inheritDoc}
345 346 347 348 349 350 351 352
     */
    protected function _getCreateTableSQL($tableName, array $columns, array $options = array())
    {
        $indexes = array();
        if (isset($options['indexes'])) {
            $indexes = $options['indexes'];
        }
        $options['indexes'] = array();
353

354 355
        $sqls = parent::_getCreateTableSQL($tableName, $columns, $options);

356
        foreach ($indexes as $definition) {
357 358 359 360 361 362
            $sqls[] = $this->getCreateIndexSQL($definition, $tableName);
        }
        return $sqls;
    }

    /**
363
     * {@inheritDoc}
364 365 366 367
     */
    public function getAlterTableSQL(TableDiff $diff)
    {
        $sql = array();
368
        $columnSql = array();
369 370

        $queryParts = array();
371
        foreach ($diff->addedColumns as $column) {
372 373
            if ($this->onSchemaAlterTableAddColumn($column, $diff, $columnSql)) {
                continue;
374 375
            }

376
            $queryParts[] = 'ADD COLUMN ' . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
377 378
        }

379
        foreach ($diff->removedColumns as $column) {
380 381
            if ($this->onSchemaAlterTableRemoveColumn($column, $diff, $columnSql)) {
                continue;
382 383
            }

384
            $queryParts[] =  'DROP COLUMN ' . $column->getQuotedName($this);
385 386
        }

387
        foreach ($diff->changedColumns as $columnDiff) {
388 389
            if ($this->onSchemaAlterTableChangeColumn($columnDiff, $diff, $columnSql)) {
                continue;
390 391
            }

392
            /* @var $columnDiff \Doctrine\DBAL\Schema\ColumnDiff */
393 394
            $column = $columnDiff->column;
            $queryParts[] =  'ALTER ' . ($columnDiff->oldColumnName) . ' '
395
                    . $this->getColumnDeclarationSQL($column->getQuotedName($this), $column->toArray());
396 397
        }

398
        foreach ($diff->renamedColumns as $oldColumnName => $column) {
399 400
            if ($this->onSchemaAlterTableRenameColumn($oldColumnName, $column, $diff, $columnSql)) {
                continue;
401 402
            }

403
            $queryParts[] =  'RENAME ' . $oldColumnName . ' TO ' . $column->getQuotedName($this);
404 405
        }

406 407
        $tableSql = array();

408
        if ( ! $this->onSchemaAlterTable($diff, $tableSql)) {
409 410 411
            if (count($queryParts) > 0) {
                $sql[] = 'ALTER TABLE ' . $diff->name . ' ' . implode(" ", $queryParts);
            }
412

413
            $sql = array_merge($sql, $this->_getAlterTableIndexForeignKeySQL($diff));
414

415 416 417
            if ($diff->newName !== false) {
                $sql[] =  'RENAME TABLE TO ' . $diff->newName;
            }
418 419
        }

420
        return array_merge($sql, $tableSql, $columnSql);
421 422
    }

423 424 425
    /**
     * {@inheritDoc}
     */
426 427 428 429 430
    public function getDefaultValueDeclarationSQL($field)
    {
        if (isset($field['notnull']) && $field['notnull'] && !isset($field['default'])) {
            if (in_array((string)$field['type'], array("Integer", "BigInteger", "SmallInteger"))) {
                $field['default'] = 0;
431 432 433 434 435 436
            } else if((string)$field['type'] == "DateTime") {
                $field['default'] = "00-00-00 00:00:00";
            } else if ((string)$field['type'] == "Date") {
                $field['default'] = "00-00-00";
            } else if((string)$field['type'] == "Time") {
                $field['default'] = "00:00:00";
437 438 439 440 441
            } else {
                $field['default'] = '';
            }
        }

442 443 444 445 446 447 448
        unset($field['default']); // @todo this needs fixing
        if (isset($field['version']) && $field['version']) {
            if ((string)$field['type'] != "DateTime") {
                $field['default'] = "1";
            }
        }

449 450 451
        return parent::getDefaultValueDeclarationSQL($field);
    }

452
    /**
453
     * {@inheritDoc}
454 455 456 457 458 459
     */
    public function getEmptyIdentityInsertSQL($tableName, $identifierColumnName)
    {
        return 'INSERT INTO ' . $tableName . ' (' . $identifierColumnName . ') VALUES (DEFAULT)';
    }

Benjamin Morel's avatar
Benjamin Morel committed
460 461 462
    /**
     * {@inheritDoc}
     */
463 464 465 466 467 468
    public function getCreateTemporaryTableSnippetSQL()
    {
        return "DECLARE GLOBAL TEMPORARY TABLE";
    }

    /**
469
     * {@inheritDoc}
470 471 472 473 474 475
     */
    public function getTemporaryTableName($tableName)
    {
        return "SESSION." . $tableName;
    }

476 477 478
    /**
     * {@inheritDoc}
     */
479
    protected function doModifyLimitQuery($query, $limit, $offset = null)
480 481 482 483 484 485 486 487 488 489 490
    {
        if ($limit === null && $offset === null) {
            return $query;
        }

        $limit = (int)$limit;
        $offset = (int)(($offset)?:0);

        // Todo OVER() needs ORDER BY data!
        $sql = 'SELECT db22.* FROM (SELECT ROW_NUMBER() OVER() AS DC_ROWNUM, db21.* '.
               'FROM (' . $query . ') db21) db22 WHERE db22.DC_ROWNUM BETWEEN ' . ($offset+1) .' AND ' . ($offset+$limit);
491

492 493 494 495
        return $sql;
    }

    /**
496
     * {@inheritDoc}
497 498 499 500 501 502
     */
    public function getLocateExpression($str, $substr, $startPos = false)
    {
        if ($startPos == false) {
            return 'LOCATE(' . $substr . ', ' . $str . ')';
        }
503 504

        return 'LOCATE(' . $substr . ', ' . $str . ', '.$startPos.')';
505 506 507
    }

    /**
508
     * {@inheritDoc}
509
     */
510
    public function getSubstringExpression($value, $from, $length = null)
511
    {
512
        if ($length === null) {
513 514
            return 'SUBSTR(' . $value . ', ' . $from . ')';
        }
515 516

        return 'SUBSTR(' . $value . ', ' . $from . ', ' . $length . ')';
517 518
    }

519 520 521
    /**
     * {@inheritDoc}
     */
522 523 524 525 526
    public function supportsIdentityColumns()
    {
        return true;
    }

527 528 529
    /**
     * {@inheritDoc}
     */
530 531 532 533
    public function prefersIdentityColumns()
    {
        return true;
    }
534 535

    /**
536
     * {@inheritDoc}
537 538 539 540 541 542 543
     *
     * DB2 returns all column names in SQL result sets in uppercase.
     */
    public function getSQLResultCasing($column)
    {
        return strtoupper($column);
    }
544

Benjamin Morel's avatar
Benjamin Morel committed
545 546 547
    /**
     * {@inheritDoc}
     */
548 549 550 551
    public function getForUpdateSQL()
    {
        return ' WITH RR USE AND KEEP UPDATE LOCKS';
    }
552

553 554 555
    /**
     * {@inheritDoc}
     */
556 557 558 559
    public function getDummySelectSQL()
    {
        return 'SELECT 1 FROM sysibm.sysdummy1';
    }
560 561

    /**
562 563
     * {@inheritDoc}
     *
564 565 566 567 568 569 570 571
     * 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;
    }
572

573 574 575
    /**
     * {@inheritDoc}
     */
576 577 578 579
    protected function getReservedKeywordsClass()
    {
        return 'Doctrine\DBAL\Platforms\Keywords\DB2Keywords';
    }
580
}