DB2PlatformTest.php 28.1 KB
Newer Older
1 2 3 4 5 6
<?php

namespace Doctrine\Tests\DBAL\Platforms;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\DB2Platform;
7 8
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
9 10
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Table;
11 12
use Doctrine\DBAL\Schema\TableDiff;
use Doctrine\DBAL\Types\Type;
13
use Doctrine\DBAL\Types\Types;
14 15 16

class DB2PlatformTest extends AbstractPlatformTestCase
{
Sergei Morozov's avatar
Sergei Morozov committed
17
    /** @var DB2Platform */
Sergei Morozov's avatar
Sergei Morozov committed
18
    protected $platform;
19

20
    public function createPlatform() : AbstractPlatform
21 22 23 24
    {
        return new DB2Platform();
    }

25 26 27 28
    /**
     * {@inheritDoc}
     */
    public function getGenerateAlterTableSql() : array
29
    {
Sergei Morozov's avatar
Sergei Morozov committed
30 31 32
        return [
            'ALTER TABLE mytable ALTER COLUMN baz SET DATA TYPE VARCHAR(255)',
            'ALTER TABLE mytable ALTER COLUMN baz SET NOT NULL',
33
            "ALTER TABLE mytable ALTER COLUMN baz SET DEFAULT 'def'",
Sergei Morozov's avatar
Sergei Morozov committed
34 35
            'ALTER TABLE mytable ALTER COLUMN bloo SET DATA TYPE SMALLINT',
            'ALTER TABLE mytable ALTER COLUMN bloo SET NOT NULL',
36
            "ALTER TABLE mytable ALTER COLUMN bloo SET DEFAULT '0'",
Sergei Morozov's avatar
Sergei Morozov committed
37 38 39
            'ALTER TABLE mytable ' .
            'ADD COLUMN quota INTEGER DEFAULT NULL ' .
            'DROP COLUMN foo',
40 41
            "CALL SYSPROC.ADMIN_CMD ('REORG TABLE mytable')",
            'RENAME TABLE mytable TO userlist',
Sergei Morozov's avatar
Sergei Morozov committed
42
        ];
43 44
    }

45
    public function getGenerateForeignKeySql() : string
46 47 48 49
    {
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
    }

50
    public function getGenerateIndexSql() : string
51 52 53 54
    {
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
    }

55
    public function getGenerateTableSql() : string
56 57 58 59
    {
        return 'CREATE TABLE test (id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))';
    }

60 61 62 63
    /**
     * {@inheritDoc}
     */
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
64
    {
Sergei Morozov's avatar
Sergei Morozov committed
65
        return [
66
            'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
67 68
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar)',
        ];
69 70
    }

71
    public function getGenerateUniqueIndexSql() : string
72 73 74 75
    {
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2)';
    }

76 77 78 79
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInForeignKeySQL() : array
80
    {
Sergei Morozov's avatar
Sergei Morozov committed
81
        return [
82 83 84 85
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, foo VARCHAR(255) NOT NULL, "bar" VARCHAR(255) NOT NULL)',
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES "foreign" ("create", bar, "foo-bar")',
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES foo ("create", bar, "foo-bar")',
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ("create", foo, "bar") REFERENCES "foo-bar" ("create", bar, "foo-bar")',
Sergei Morozov's avatar
Sergei Morozov committed
86
        ];
87 88
    }

89 90 91 92
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInIndexSQL() : array
93
    {
Sergei Morozov's avatar
Sergei Morozov committed
94
        return [
95
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
96 97
            'CREATE INDEX IDX_22660D028FD6E0FB ON "quoted" ("create")',
        ];
98 99
    }

100 101 102 103
    /**
     * {@inheritDoc}
     */
    protected function getQuotedNameInIndexSQL() : array
Markus Fasselt's avatar
Markus Fasselt committed
104
    {
Sergei Morozov's avatar
Sergei Morozov committed
105
        return [
Markus Fasselt's avatar
Markus Fasselt committed
106 107
            'CREATE TABLE test (column1 VARCHAR(255) NOT NULL)',
            'CREATE INDEX "key" ON test (column1)',
Sergei Morozov's avatar
Sergei Morozov committed
108
        ];
Markus Fasselt's avatar
Markus Fasselt committed
109 110
    }

111 112 113 114
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInPrimaryKeySQL() : array
115
    {
Sergei Morozov's avatar
Sergei Morozov committed
116
        return ['CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, PRIMARY KEY("create"))'];
117 118
    }

119
    protected function getBitAndComparisonExpressionSql(string $value1, string $value2) : string
120 121 122 123
    {
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
    }

124
    protected function getBitOrComparisonExpressionSql(string $value1, string $value2) : string
125 126 127 128
    {
        return 'BITOR(' . $value1 . ', ' . $value2 . ')';
    }

129 130 131 132
    /**
     * {@inheritDoc}
     */
    public function getCreateTableColumnCommentsSQL() : array
133
    {
Sergei Morozov's avatar
Sergei Morozov committed
134 135
        return [
            'CREATE TABLE test (id INTEGER NOT NULL, PRIMARY KEY(id))',
136
            "COMMENT ON COLUMN test.id IS 'This is a comment'",
Sergei Morozov's avatar
Sergei Morozov committed
137
        ];
138 139
    }

140 141 142 143
    /**
     * {@inheritDoc}
     */
    public function getAlterTableColumnCommentsSQL() : array
144
    {
Sergei Morozov's avatar
Sergei Morozov committed
145 146 147
        return [
            'ALTER TABLE mytable ' .
            'ADD COLUMN quota INTEGER NOT NULL WITH DEFAULT',
148 149 150 151
            "CALL SYSPROC.ADMIN_CMD ('REORG TABLE mytable')",
            "COMMENT ON COLUMN mytable.quota IS 'A comment'",
            "COMMENT ON COLUMN mytable.foo IS ''",
            "COMMENT ON COLUMN mytable.baz IS 'B comment'",
Sergei Morozov's avatar
Sergei Morozov committed
152
        ];
153 154
    }

155 156 157 158
    /**
     * {@inheritDoc}
     */
    public function getCreateTableColumnTypeCommentsSQL() : array
159
    {
Sergei Morozov's avatar
Sergei Morozov committed
160
        return [
161
            'CREATE TABLE test (id INTEGER NOT NULL, "data" CLOB(1M) NOT NULL, PRIMARY KEY(id))',
162
            'COMMENT ON COLUMN test."data" IS \'(DC2Type:array)\'',
Sergei Morozov's avatar
Sergei Morozov committed
163
        ];
164 165
    }

166
    public function testHasCorrectPlatformName() : void
167
    {
Sergei Morozov's avatar
Sergei Morozov committed
168
        self::assertEquals('db2', $this->platform->getName());
169 170
    }

171
    public function testGeneratesCreateTableSQLWithCommonIndexes() : void
172 173 174
    {
        $table = new Table('test');
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
175 176 177 178
        $table->addColumn('name', 'string', ['length' => 50]);
        $table->setPrimaryKey(['id']);
        $table->addIndex(['name']);
        $table->addIndex(['id', 'name'], 'composite_idx');
179

180
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
181
            [
182 183
                'CREATE TABLE test (id INTEGER NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY(id))',
                'CREATE INDEX IDX_D87F7E0C5E237E06 ON test (name)',
Sergei Morozov's avatar
Sergei Morozov committed
184 185
                'CREATE INDEX composite_idx ON test (id, name)',
            ],
Sergei Morozov's avatar
Sergei Morozov committed
186
            $this->platform->getCreateTableSQL($table)
187 188 189
        );
    }

190
    public function testGeneratesCreateTableSQLWithForeignKeyConstraints() : void
191 192 193 194 195
    {
        $table = new Table('test');
        $table->addColumn('id', 'integer');
        $table->addColumn('fk_1', 'integer');
        $table->addColumn('fk_2', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
196 197
        $table->setPrimaryKey(['id']);
        $table->addForeignKeyConstraint('foreign_table', ['fk_1', 'fk_2'], ['pk_1', 'pk_2']);
198 199
        $table->addForeignKeyConstraint(
            'foreign_table2',
Sergei Morozov's avatar
Sergei Morozov committed
200 201 202
            ['fk_1', 'fk_2'],
            ['pk_1', 'pk_2'],
            [],
203 204 205
            'named_fk'
        );

206
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
207
            [
208 209 210
                'CREATE TABLE test (id INTEGER NOT NULL, fk_1 INTEGER NOT NULL, fk_2 INTEGER NOT NULL)',
                'ALTER TABLE test ADD CONSTRAINT FK_D87F7E0C177612A38E7F4319 FOREIGN KEY (fk_1, fk_2) REFERENCES foreign_table (pk_1, pk_2)',
                'ALTER TABLE test ADD CONSTRAINT named_fk FOREIGN KEY (fk_1, fk_2) REFERENCES foreign_table2 (pk_1, pk_2)',
Sergei Morozov's avatar
Sergei Morozov committed
211
            ],
Sergei Morozov's avatar
Sergei Morozov committed
212
            $this->platform->getCreateTableSQL($table, AbstractPlatform::CREATE_FOREIGNKEYS)
213 214 215
        );
    }

216
    public function testGeneratesCreateTableSQLWithCheckConstraints() : void
217 218 219
    {
        $table = new Table('test');
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
220 221 222
        $table->addColumn('check_max', 'integer', ['platformOptions' => ['max' => 10]]);
        $table->addColumn('check_min', 'integer', ['platformOptions' => ['min' => 10]]);
        $table->setPrimaryKey(['id']);
223

224
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
225
            ['CREATE TABLE test (id INTEGER NOT NULL, check_max INTEGER NOT NULL, check_min INTEGER NOT NULL, PRIMARY KEY(id), CHECK (check_max <= 10), CHECK (check_min >= 10))'],
Sergei Morozov's avatar
Sergei Morozov committed
226
            $this->platform->getCreateTableSQL($table)
227 228 229
        );
    }

230
    public function testGeneratesColumnTypesDeclarationSQL() : void
231
    {
Sergei Morozov's avatar
Sergei Morozov committed
232
        $fullColumnDef = [
233 234 235
            'length' => 10,
            'fixed' => true,
            'unsigned' => true,
Sergei Morozov's avatar
Sergei Morozov committed
236 237
            'autoincrement' => true,
        ];
238

Sergei Morozov's avatar
Sergei Morozov committed
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
        self::assertEquals('VARCHAR(255)', $this->platform->getVarcharTypeDeclarationSQL([]));
        self::assertEquals('VARCHAR(10)', $this->platform->getVarcharTypeDeclarationSQL(['length' => 10]));
        self::assertEquals('CHAR(254)', $this->platform->getVarcharTypeDeclarationSQL(['fixed' => true]));
        self::assertEquals('CHAR(10)', $this->platform->getVarcharTypeDeclarationSQL($fullColumnDef));

        self::assertEquals('SMALLINT', $this->platform->getSmallIntTypeDeclarationSQL([]));
        self::assertEquals('SMALLINT', $this->platform->getSmallIntTypeDeclarationSQL(['unsigned' => true]));
        self::assertEquals('SMALLINT GENERATED BY DEFAULT AS IDENTITY', $this->platform->getSmallIntTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('INTEGER', $this->platform->getIntegerTypeDeclarationSQL([]));
        self::assertEquals('INTEGER', $this->platform->getIntegerTypeDeclarationSQL(['unsigned' => true]));
        self::assertEquals('INTEGER GENERATED BY DEFAULT AS IDENTITY', $this->platform->getIntegerTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('BIGINT', $this->platform->getBigIntTypeDeclarationSQL([]));
        self::assertEquals('BIGINT', $this->platform->getBigIntTypeDeclarationSQL(['unsigned' => true]));
        self::assertEquals('BIGINT GENERATED BY DEFAULT AS IDENTITY', $this->platform->getBigIntTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('BLOB(1M)', $this->platform->getBlobTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('SMALLINT', $this->platform->getBooleanTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('CLOB(1M)', $this->platform->getClobTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('DATE', $this->platform->getDateTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('TIMESTAMP(0) WITH DEFAULT', $this->platform->getDateTimeTypeDeclarationSQL(['version' => true]));
        self::assertEquals('TIMESTAMP(0)', $this->platform->getDateTimeTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('TIME', $this->platform->getTimeTypeDeclarationSQL($fullColumnDef));
260 261
    }

262
    public function testInitializesDoctrineTypeMappings() : void
263
    {
Sergei Morozov's avatar
Sergei Morozov committed
264
        $this->platform->initializeDoctrineTypeMappings();
265

Sergei Morozov's avatar
Sergei Morozov committed
266 267
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallint'));
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('smallint'));
268

Sergei Morozov's avatar
Sergei Morozov committed
269 270
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bigint'));
        self::assertSame('bigint', $this->platform->getDoctrineTypeMapping('bigint'));
271

Sergei Morozov's avatar
Sergei Morozov committed
272 273
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('integer'));
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('integer'));
274

Sergei Morozov's avatar
Sergei Morozov committed
275 276
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('time'));
        self::assertSame('time', $this->platform->getDoctrineTypeMapping('time'));
277

Sergei Morozov's avatar
Sergei Morozov committed
278 279
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('date'));
        self::assertSame('date', $this->platform->getDoctrineTypeMapping('date'));
280

Sergei Morozov's avatar
Sergei Morozov committed
281 282
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varchar'));
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('varchar'));
283

Sergei Morozov's avatar
Sergei Morozov committed
284 285
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('character'));
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('character'));
286

Sergei Morozov's avatar
Sergei Morozov committed
287 288
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('clob'));
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('clob'));
289

Sergei Morozov's avatar
Sergei Morozov committed
290 291
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('blob'));
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('blob'));
292

Sergei Morozov's avatar
Sergei Morozov committed
293 294
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('decimal'));
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('decimal'));
295

Sergei Morozov's avatar
Sergei Morozov committed
296 297
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double'));
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double'));
298

Sergei Morozov's avatar
Sergei Morozov committed
299 300
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real'));
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('real'));
301

Sergei Morozov's avatar
Sergei Morozov committed
302 303
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('timestamp'));
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('timestamp'));
304 305
    }

306 307 308 309
    /**
     * {@inheritDoc}
     */
    public function getIsCommentedDoctrineType() : array
310 311 312
    {
        $data = parent::getIsCommentedDoctrineType();

313
        $data[Types::BOOLEAN] = [Type::getType(Types::BOOLEAN), true];
314 315 316 317

        return $data;
    }

318
    public function testGeneratesDDLSnippets() : void
319
    {
Sergei Morozov's avatar
Sergei Morozov committed
320 321 322 323 324
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
        self::assertEquals('DROP DATABASE foobar', $this->platform->getDropDatabaseSQL('foobar'));
        self::assertEquals('DECLARE GLOBAL TEMPORARY TABLE', $this->platform->getCreateTemporaryTableSnippetSQL());
        self::assertEquals('TRUNCATE foobar IMMEDIATE', $this->platform->getTruncateTableSQL('foobar'));
        self::assertEquals('TRUNCATE foobar IMMEDIATE', $this->platform->getTruncateTableSQL('foobar'), true);
325 326

        $viewSql = 'SELECT * FROM footable';
Sergei Morozov's avatar
Sergei Morozov committed
327 328
        self::assertEquals('CREATE VIEW fooview AS ' . $viewSql, $this->platform->getCreateViewSQL('fooview', $viewSql));
        self::assertEquals('DROP VIEW fooview', $this->platform->getDropViewSQL('fooview'));
329 330
    }

331
    public function testGeneratesCreateUnnamedPrimaryKeySQL() : void
332
    {
333
        self::assertEquals(
334
            'ALTER TABLE foo ADD PRIMARY KEY (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
335
            $this->platform->getCreatePrimaryKeySQL(
Sergei Morozov's avatar
Sergei Morozov committed
336
                new Index('any_pk_name', ['a', 'b'], true, true),
337 338 339 340 341
                'foo'
            )
        );
    }

342
    public function testGeneratesSQLSnippets() : void
343
    {
Sergei Morozov's avatar
Sergei Morozov committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
        self::assertEquals('CURRENT DATE', $this->platform->getCurrentDateSQL());
        self::assertEquals('CURRENT TIME', $this->platform->getCurrentTimeSQL());
        self::assertEquals('CURRENT TIMESTAMP', $this->platform->getCurrentTimestampSQL());
        self::assertEquals("'1987/05/02' + 4 DAY", $this->platform->getDateAddDaysExpression("'1987/05/02'", 4));
        self::assertEquals("'1987/05/02' + 12 HOUR", $this->platform->getDateAddHourExpression("'1987/05/02'", 12));
        self::assertEquals("'1987/05/02' + 2 MINUTE", $this->platform->getDateAddMinutesExpression("'1987/05/02'", 2));
        self::assertEquals("'1987/05/02' + 102 MONTH", $this->platform->getDateAddMonthExpression("'1987/05/02'", 102));
        self::assertEquals("'1987/05/02' + 15 MONTH", $this->platform->getDateAddQuartersExpression("'1987/05/02'", 5));
        self::assertEquals("'1987/05/02' + 1 SECOND", $this->platform->getDateAddSecondsExpression("'1987/05/02'", 1));
        self::assertEquals("'1987/05/02' + 21 DAY", $this->platform->getDateAddWeeksExpression("'1987/05/02'", 3));
        self::assertEquals("'1987/05/02' + 10 YEAR", $this->platform->getDateAddYearsExpression("'1987/05/02'", 10));
        self::assertEquals("DAYS('1987/05/02') - DAYS('1987/04/01')", $this->platform->getDateDiffExpression("'1987/05/02'", "'1987/04/01'"));
        self::assertEquals("'1987/05/02' - 4 DAY", $this->platform->getDateSubDaysExpression("'1987/05/02'", 4));
        self::assertEquals("'1987/05/02' - 12 HOUR", $this->platform->getDateSubHourExpression("'1987/05/02'", 12));
        self::assertEquals("'1987/05/02' - 2 MINUTE", $this->platform->getDateSubMinutesExpression("'1987/05/02'", 2));
        self::assertEquals("'1987/05/02' - 102 MONTH", $this->platform->getDateSubMonthExpression("'1987/05/02'", 102));
        self::assertEquals("'1987/05/02' - 15 MONTH", $this->platform->getDateSubQuartersExpression("'1987/05/02'", 5));
        self::assertEquals("'1987/05/02' - 1 SECOND", $this->platform->getDateSubSecondsExpression("'1987/05/02'", 1));
        self::assertEquals("'1987/05/02' - 21 DAY", $this->platform->getDateSubWeeksExpression("'1987/05/02'", 3));
        self::assertEquals("'1987/05/02' - 10 YEAR", $this->platform->getDateSubYearsExpression("'1987/05/02'", 10));
        self::assertEquals(' WITH RR USE AND KEEP UPDATE LOCKS', $this->platform->getForUpdateSQL());
        self::assertEquals('LOCATE(substring_column, string_column)', $this->platform->getLocateExpression('string_column', 'substring_column'));
        self::assertEquals('LOCATE(substring_column, string_column)', $this->platform->getLocateExpression('string_column', 'substring_column'));
        self::assertEquals('LOCATE(substring_column, string_column, 1)', $this->platform->getLocateExpression('string_column', 'substring_column', 1));
        self::assertEquals('SUBSTR(column, 5)', $this->platform->getSubstringExpression('column', 5));
        self::assertEquals('SUBSTR(column, 5, 2)', $this->platform->getSubstringExpression('column', 5, 2));
370 371
    }

372
    public function testModifiesLimitQuery() : void
373
    {
374
        self::assertEquals(
375
            'SELECT * FROM user',
Sergei Morozov's avatar
Sergei Morozov committed
376
            $this->platform->modifyLimitQuery('SELECT * FROM user', null, null)
377 378
        );

379
        self::assertEquals(
380
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (SELECT * FROM user) db21) db22 WHERE db22.DC_ROWNUM <= 10',
Sergei Morozov's avatar
Sergei Morozov committed
381
            $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0)
382 383
        );

384
        self::assertEquals(
385
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (SELECT * FROM user) db21) db22 WHERE db22.DC_ROWNUM <= 10',
Sergei Morozov's avatar
Sergei Morozov committed
386
            $this->platform->modifyLimitQuery('SELECT * FROM user', 10)
387 388
        );

389
        self::assertEquals(
390
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (SELECT * FROM user) db21) db22 WHERE db22.DC_ROWNUM >= 6 AND db22.DC_ROWNUM <= 15',
Sergei Morozov's avatar
Sergei Morozov committed
391
            $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 5)
392
        );
393
        self::assertEquals(
394
            'SELECT db22.* FROM (SELECT db21.*, ROW_NUMBER() OVER() AS DC_ROWNUM FROM (SELECT * FROM user) db21) db22 WHERE db22.DC_ROWNUM >= 6 AND db22.DC_ROWNUM <= 5',
Sergei Morozov's avatar
Sergei Morozov committed
395
            $this->platform->modifyLimitQuery('SELECT * FROM user', 0, 5)
396 397 398
        );
    }

399
    public function testPrefersIdentityColumns() : void
400
    {
Sergei Morozov's avatar
Sergei Morozov committed
401
        self::assertTrue($this->platform->prefersIdentityColumns());
402 403
    }

404
    public function testSupportsIdentityColumns() : void
405
    {
Sergei Morozov's avatar
Sergei Morozov committed
406
        self::assertTrue($this->platform->supportsIdentityColumns());
407 408
    }

409
    public function testDoesNotSupportSavePoints() : void
410
    {
Sergei Morozov's avatar
Sergei Morozov committed
411
        self::assertFalse($this->platform->supportsSavepoints());
412 413
    }

414
    public function testDoesNotSupportReleasePoints() : void
415
    {
Sergei Morozov's avatar
Sergei Morozov committed
416
        self::assertFalse($this->platform->supportsReleaseSavepoints());
417 418
    }

419
    public function testDoesNotSupportCreateDropDatabase() : void
420
    {
Sergei Morozov's avatar
Sergei Morozov committed
421
        self::assertFalse($this->platform->supportsCreateDropDatabase());
422 423
    }

424
    public function testReturnsSQLResultCasing() : void
425
    {
Sergei Morozov's avatar
Sergei Morozov committed
426
        self::assertSame('COL', $this->platform->getSQLResultCasing('cOl'));
427
    }
428

429
    protected function getBinaryDefaultLength() : int
430 431 432 433
    {
        return 1;
    }

434
    protected function getBinaryMaxLength() : int
435 436 437 438
    {
        return 32704;
    }

439
    public function testReturnsBinaryTypeDeclarationSQL() : void
440
    {
Sergei Morozov's avatar
Sergei Morozov committed
441 442 443
        self::assertSame('VARCHAR(1) FOR BIT DATA', $this->platform->getBinaryTypeDeclarationSQL([]));
        self::assertSame('VARCHAR(255) FOR BIT DATA', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0]));
        self::assertSame('VARCHAR(32704) FOR BIT DATA', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32704]));
444

Sergei Morozov's avatar
Sergei Morozov committed
445 446
        self::assertSame('CHAR(1) FOR BIT DATA', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true]));
        self::assertSame('CHAR(254) FOR BIT DATA', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0]));
447 448 449 450
    }

    /**
     * @group legacy
451
     * @expectedDeprecation Binary field length 32705 is greater than supported by the platform (32704). Reduce the field length or use a BLOB field instead.
452
     */
453
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
454
    {
Sergei Morozov's avatar
Sergei Morozov committed
455 456
        self::assertSame('BLOB(1M)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32705]));
        self::assertSame('BLOB(1M)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32705]));
457
    }
458 459

    /**
460 461
     * {@inheritDoc}
     *
462 463
     * @group DBAL-234
     */
464
    protected function getAlterTableRenameIndexSQL() : array
465
    {
Sergei Morozov's avatar
Sergei Morozov committed
466
        return ['RENAME INDEX idx_foo TO idx_bar'];
467 468 469
    }

    /**
470 471
     * {@inheritDoc}
     *
472 473
     * @group DBAL-234
     */
474
    protected function getQuotedAlterTableRenameIndexSQL() : array
475
    {
Sergei Morozov's avatar
Sergei Morozov committed
476
        return [
477 478
            'RENAME INDEX "create" TO "select"',
            'RENAME INDEX "foo" TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
479
        ];
480
    }
481 482 483 484

    /**
     * {@inheritdoc}
     */
485
    protected function getQuotedAlterTableRenameColumnSQL() : array
486
    {
Sergei Morozov's avatar
Sergei Morozov committed
487
        return ['ALTER TABLE mytable ' .
488 489 490 491 492 493 494 495
            'RENAME COLUMN unquoted1 TO unquoted ' .
            'RENAME COLUMN unquoted2 TO "where" ' .
            'RENAME COLUMN unquoted3 TO "foo" ' .
            'RENAME COLUMN "create" TO reserved_keyword ' .
            'RENAME COLUMN "table" TO "from" ' .
            'RENAME COLUMN "select" TO "bar" ' .
            'RENAME COLUMN quoted1 TO quoted ' .
            'RENAME COLUMN quoted2 TO "and" ' .
Sergei Morozov's avatar
Sergei Morozov committed
496 497
            'RENAME COLUMN quoted3 TO "baz"',
        ];
498
    }
499

500 501 502
    /**
     * {@inheritdoc}
     */
503
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
504 505 506 507
    {
        $this->markTestIncomplete('Not implemented yet');
    }

508
    /**
509 510
     * {@inheritDoc}
     *
511 512
     * @group DBAL-807
     */
513
    protected function getAlterTableRenameIndexInSchemaSQL() : array
514
    {
Sergei Morozov's avatar
Sergei Morozov committed
515
        return ['RENAME INDEX myschema.idx_foo TO idx_bar'];
516 517 518
    }

    /**
519 520
     * {@inheritDoc}
     *
521 522
     * @group DBAL-807
     */
523
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
524
    {
Sergei Morozov's avatar
Sergei Morozov committed
525
        return [
526 527
            'RENAME INDEX "schema"."create" TO "select"',
            'RENAME INDEX "schema"."foo" TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
528
        ];
529
    }
530 531 532 533

    /**
     * @group DBAL-423
     */
534
    public function testReturnsGuidTypeDeclarationSQL() : void
535
    {
Sergei Morozov's avatar
Sergei Morozov committed
536
        self::assertSame('CHAR(36)', $this->platform->getGuidTypeDeclarationSQL([]));
537
    }
538 539 540 541

    /**
     * {@inheritdoc}
     */
542
    public function getAlterTableRenameColumnSQL() : array
543
    {
Sergei Morozov's avatar
Sergei Morozov committed
544
        return ['ALTER TABLE foo RENAME COLUMN bar TO baz'];
545
    }
546 547 548 549

    /**
     * {@inheritdoc}
     */
550
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
551
    {
Sergei Morozov's avatar
Sergei Morozov committed
552
        return [
553 554
            'ALTER TABLE "foo" DROP FOREIGN KEY fk1',
            'ALTER TABLE "foo" DROP FOREIGN KEY fk2',
555 556 557 558 559
            'ALTER TABLE "foo" ' .
            'ADD COLUMN bloo INTEGER NOT NULL WITH DEFAULT ' .
            'DROP COLUMN baz ' .
            'ALTER COLUMN bar DROP NOT NULL ' .
            'RENAME COLUMN id TO war',
560 561 562 563
            'CALL SYSPROC.ADMIN_CMD (\'REORG TABLE "foo"\')',
            'RENAME TABLE "foo" TO "table"',
            'ALTER TABLE "table" ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id)',
            'ALTER TABLE "table" ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id)',
Sergei Morozov's avatar
Sergei Morozov committed
564
        ];
565
    }
566 567 568 569

    /**
     * {@inheritdoc}
     */
570
    protected function getCommentOnColumnSQL() : array
571
    {
Sergei Morozov's avatar
Sergei Morozov committed
572
        return [
573 574 575
            'COMMENT ON COLUMN foo.bar IS \'comment\'',
            'COMMENT ON COLUMN "Foo"."BAR" IS \'comment\'',
            'COMMENT ON COLUMN "select"."from" IS \'comment\'',
Sergei Morozov's avatar
Sergei Morozov committed
576
        ];
577
    }
578 579 580 581 582

    /**
     * @group DBAL-944
     * @dataProvider getGeneratesAlterColumnSQL
     */
583
    public function testGeneratesAlterColumnSQL(string $changedProperty, Column $column, ?string $expectedSQLClause = null) : void
584
    {
Sergei Morozov's avatar
Sergei Morozov committed
585 586 587
        $tableDiff                        = new TableDiff('foo');
        $tableDiff->fromTable             = new Table('foo');
        $tableDiff->changedColumns['bar'] = new ColumnDiff('bar', $column, [$changedProperty]);
588

Sergei Morozov's avatar
Sergei Morozov committed
589
        $expectedSQL = [];
590

Sergei Morozov's avatar
Sergei Morozov committed
591
        if ($expectedSQLClause !== null) {
592 593 594 595 596
            $expectedSQL[] = 'ALTER TABLE foo ALTER COLUMN bar ' . $expectedSQLClause;
        }

        $expectedSQL[] = "CALL SYSPROC.ADMIN_CMD ('REORG TABLE foo')";

Sergei Morozov's avatar
Sergei Morozov committed
597
        self::assertSame($expectedSQL, $this->platform->getAlterTableSQL($tableDiff));
598 599 600
    }

    /**
601
     * @return mixed[][]
602
     */
603
    public static function getGeneratesAlterColumnSQL() : iterable
604
    {
Sergei Morozov's avatar
Sergei Morozov committed
605 606
        return [
            [
607
                'columnDefinition',
Sergei Morozov's avatar
Sergei Morozov committed
608 609 610 611
                new Column('bar', Type::getType('decimal'), ['columnDefinition' => 'MONEY NOT NULL']),
                'MONEY NOT NULL',
            ],
            [
612 613
                'type',
                new Column('bar', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
614 615 616
                'SET DATA TYPE INTEGER',
            ],
            [
617
                'length',
Sergei Morozov's avatar
Sergei Morozov committed
618 619 620 621
                new Column('bar', Type::getType('string'), ['length' => 100]),
                'SET DATA TYPE VARCHAR(100)',
            ],
            [
622
                'precision',
Sergei Morozov's avatar
Sergei Morozov committed
623 624 625 626
                new Column('bar', Type::getType('decimal'), ['precision' => 10, 'scale' => 2]),
                'SET DATA TYPE NUMERIC(10, 2)',
            ],
            [
627
                'scale',
Sergei Morozov's avatar
Sergei Morozov committed
628 629 630 631
                new Column('bar', Type::getType('decimal'), ['precision' => 5, 'scale' => 4]),
                'SET DATA TYPE NUMERIC(5, 4)',
            ],
            [
632
                'fixed',
Sergei Morozov's avatar
Sergei Morozov committed
633 634 635 636
                new Column('bar', Type::getType('string'), ['length' => 20, 'fixed' => true]),
                'SET DATA TYPE CHAR(20)',
            ],
            [
637
                'notnull',
Sergei Morozov's avatar
Sergei Morozov committed
638 639 640 641
                new Column('bar', Type::getType('string'), ['notnull' => true]),
                'SET NOT NULL',
            ],
            [
642
                'notnull',
Sergei Morozov's avatar
Sergei Morozov committed
643 644 645 646
                new Column('bar', Type::getType('string'), ['notnull' => false]),
                'DROP NOT NULL',
            ],
            [
647
                'default',
Sergei Morozov's avatar
Sergei Morozov committed
648 649 650 651
                new Column('bar', Type::getType('string'), ['default' => 'foo']),
                "SET DEFAULT 'foo'",
            ],
            [
652
                'default',
Sergei Morozov's avatar
Sergei Morozov committed
653 654 655 656
                new Column('bar', Type::getType('integer'), ['autoincrement' => true, 'default' => 666]),
                null,
            ],
            [
657 658
                'default',
                new Column('bar', Type::getType('string')),
Sergei Morozov's avatar
Sergei Morozov committed
659 660 661
                'DROP DEFAULT',
            ],
        ];
662
    }
663

664
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
665 666 667 668
    {
        return 'CONSTRAINT "select" UNIQUE (foo)';
    }

669
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
670 671 672 673
    {
        return ''; // not supported by this platform
    }

674
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
675 676 677 678
    {
        return 'TRUNCATE "select" IMMEDIATE';
    }

679
    protected function supportsInlineIndexDeclaration() : bool
680 681 682
    {
        return false;
    }
683

684
    protected function supportsCommentOnStatement() : bool
685 686 687 688
    {
        return true;
    }

689 690 691
    /**
     * {@inheritdoc}
     */
692
    protected function getAlterStringToFixedStringSQL() : array
693
    {
Sergei Morozov's avatar
Sergei Morozov committed
694
        return [
695 696
            'ALTER TABLE mytable ALTER COLUMN name SET DATA TYPE CHAR(2)',
            'CALL SYSPROC.ADMIN_CMD (\'REORG TABLE mytable\')',
Sergei Morozov's avatar
Sergei Morozov committed
697
        ];
698
    }
699 700 701 702

    /**
     * {@inheritdoc}
     */
703
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
704
    {
Sergei Morozov's avatar
Sergei Morozov committed
705
        return ['RENAME INDEX idx_foo TO idx_foo_renamed'];
706
    }
707 708 709 710

    /**
     * @group DBAL-2436
     */
711
    public function testQuotesTableNameInListTableColumnsSQL() : void
712
    {
713 714 715 716
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
        );
717 718 719 720 721
    }

    /**
     * @group DBAL-2436
     */
722
    public function testQuotesTableNameInListTableIndexesSQL() : void
723
    {
724 725 726 727
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
        );
728 729 730 731 732
    }

    /**
     * @group DBAL-2436
     */
733
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
734
    {
735 736 737 738
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
        );
739
    }
740
}