DB2PlatformTest.php 27.5 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
    /**
     * {@inheritDoc}
     */
28
    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
    protected 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
    /**
     * {@inheritDoc}
     */
63
    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
    /**
     * {@inheritDoc}
     */
79
    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
    /**
     * {@inheritDoc}
     */
92
    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
    /**
     * {@inheritDoc}
     */
103
    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
    /**
     * {@inheritDoc}
     */
114
    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
    /**
     * {@inheritDoc}
     */
132
    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
    /**
     * {@inheritDoc}
     */
143
    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
    /**
     * {@inheritDoc}
     */
158
    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
    /**
     * {@inheritDoc}
     */
309
    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
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL(): void
450
    {
Sergei Morozov's avatar
Sergei Morozov committed
451 452
        self::assertSame('BLOB(1M)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32705]));
        self::assertSame('BLOB(1M)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32705]));
453
    }
454 455

    /**
456
     * {@inheritDoc}
457
     */
458
    protected function getAlterTableRenameIndexSQL(): array
459
    {
Sergei Morozov's avatar
Sergei Morozov committed
460
        return ['RENAME INDEX idx_foo TO idx_bar'];
461 462 463
    }

    /**
464
     * {@inheritDoc}
465
     */
466
    protected function getQuotedAlterTableRenameIndexSQL(): array
467
    {
Sergei Morozov's avatar
Sergei Morozov committed
468
        return [
469 470
            'RENAME INDEX "create" TO "select"',
            'RENAME INDEX "foo" TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
471
        ];
472
    }
473 474 475 476

    /**
     * {@inheritdoc}
     */
477
    protected function getQuotedAlterTableRenameColumnSQL(): array
478
    {
Sergei Morozov's avatar
Sergei Morozov committed
479
        return ['ALTER TABLE mytable ' .
480 481 482 483 484 485 486 487
            '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
488 489
            'RENAME COLUMN quoted3 TO "baz"',
        ];
490
    }
491

492 493 494
    /**
     * {@inheritdoc}
     */
495
    protected function getQuotedAlterTableChangeColumnLengthSQL(): array
496 497 498 499
    {
        $this->markTestIncomplete('Not implemented yet');
    }

500
    /**
501
     * {@inheritDoc}
502
     */
503
    protected function getAlterTableRenameIndexInSchemaSQL(): array
504
    {
Sergei Morozov's avatar
Sergei Morozov committed
505
        return ['RENAME INDEX myschema.idx_foo TO idx_bar'];
506 507 508
    }

    /**
509
     * {@inheritDoc}
510
     */
511
    protected function getQuotedAlterTableRenameIndexInSchemaSQL(): array
512
    {
Sergei Morozov's avatar
Sergei Morozov committed
513
        return [
514 515
            'RENAME INDEX "schema"."create" TO "select"',
            'RENAME INDEX "schema"."foo" TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
516
        ];
517
    }
518

519
    public function testReturnsGuidTypeDeclarationSQL(): void
520
    {
Sergei Morozov's avatar
Sergei Morozov committed
521
        self::assertSame('CHAR(36)', $this->platform->getGuidTypeDeclarationSQL([]));
522
    }
523 524 525 526

    /**
     * {@inheritdoc}
     */
527
    public function getAlterTableRenameColumnSQL(): array
528
    {
Sergei Morozov's avatar
Sergei Morozov committed
529
        return ['ALTER TABLE foo RENAME COLUMN bar TO baz'];
530
    }
531 532 533 534

    /**
     * {@inheritdoc}
     */
535
    protected function getQuotesTableIdentifiersInAlterTableSQL(): array
536
    {
Sergei Morozov's avatar
Sergei Morozov committed
537
        return [
538 539
            'ALTER TABLE "foo" DROP FOREIGN KEY fk1',
            'ALTER TABLE "foo" DROP FOREIGN KEY fk2',
540 541 542 543 544
            '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',
545 546 547 548
            '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
549
        ];
550
    }
551 552 553 554

    /**
     * {@inheritdoc}
     */
555
    protected function getCommentOnColumnSQL(): array
556
    {
Sergei Morozov's avatar
Sergei Morozov committed
557
        return [
558 559 560
            '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
561
        ];
562
    }
563 564 565 566

    /**
     * @dataProvider getGeneratesAlterColumnSQL
     */
567
    public function testGeneratesAlterColumnSQL(string $changedProperty, Column $column, ?string $expectedSQLClause = null): void
568
    {
Sergei Morozov's avatar
Sergei Morozov committed
569 570 571
        $tableDiff                        = new TableDiff('foo');
        $tableDiff->fromTable             = new Table('foo');
        $tableDiff->changedColumns['bar'] = new ColumnDiff('bar', $column, [$changedProperty]);
572

Sergei Morozov's avatar
Sergei Morozov committed
573
        $expectedSQL = [];
574

Sergei Morozov's avatar
Sergei Morozov committed
575
        if ($expectedSQLClause !== null) {
576 577 578 579 580
            $expectedSQL[] = 'ALTER TABLE foo ALTER COLUMN bar ' . $expectedSQLClause;
        }

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

Sergei Morozov's avatar
Sergei Morozov committed
581
        self::assertSame($expectedSQL, $this->platform->getAlterTableSQL($tableDiff));
582 583 584
    }

    /**
585
     * @return mixed[][]
586
     */
587
    public static function getGeneratesAlterColumnSQL(): iterable
588
    {
Sergei Morozov's avatar
Sergei Morozov committed
589 590
        return [
            [
591
                'columnDefinition',
Sergei Morozov's avatar
Sergei Morozov committed
592 593 594 595
                new Column('bar', Type::getType('decimal'), ['columnDefinition' => 'MONEY NOT NULL']),
                'MONEY NOT NULL',
            ],
            [
596 597
                'type',
                new Column('bar', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
598 599 600
                'SET DATA TYPE INTEGER',
            ],
            [
601
                'length',
Sergei Morozov's avatar
Sergei Morozov committed
602 603 604 605
                new Column('bar', Type::getType('string'), ['length' => 100]),
                'SET DATA TYPE VARCHAR(100)',
            ],
            [
606
                'precision',
Sergei Morozov's avatar
Sergei Morozov committed
607 608 609 610
                new Column('bar', Type::getType('decimal'), ['precision' => 10, 'scale' => 2]),
                'SET DATA TYPE NUMERIC(10, 2)',
            ],
            [
611
                'scale',
Sergei Morozov's avatar
Sergei Morozov committed
612 613 614 615
                new Column('bar', Type::getType('decimal'), ['precision' => 5, 'scale' => 4]),
                'SET DATA TYPE NUMERIC(5, 4)',
            ],
            [
616
                'fixed',
Sergei Morozov's avatar
Sergei Morozov committed
617 618 619 620
                new Column('bar', Type::getType('string'), ['length' => 20, 'fixed' => true]),
                'SET DATA TYPE CHAR(20)',
            ],
            [
621
                'notnull',
Sergei Morozov's avatar
Sergei Morozov committed
622 623 624 625
                new Column('bar', Type::getType('string'), ['notnull' => true]),
                'SET NOT NULL',
            ],
            [
626
                'notnull',
Sergei Morozov's avatar
Sergei Morozov committed
627 628 629 630
                new Column('bar', Type::getType('string'), ['notnull' => false]),
                'DROP NOT NULL',
            ],
            [
631
                'default',
Sergei Morozov's avatar
Sergei Morozov committed
632 633 634 635
                new Column('bar', Type::getType('string'), ['default' => 'foo']),
                "SET DEFAULT 'foo'",
            ],
            [
636
                'default',
Sergei Morozov's avatar
Sergei Morozov committed
637 638 639 640
                new Column('bar', Type::getType('integer'), ['autoincrement' => true, 'default' => 666]),
                null,
            ],
            [
641 642
                'default',
                new Column('bar', Type::getType('string')),
Sergei Morozov's avatar
Sergei Morozov committed
643 644 645
                'DROP DEFAULT',
            ],
        ];
646
    }
647

648
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(): string
649 650 651 652
    {
        return 'CONSTRAINT "select" UNIQUE (foo)';
    }

653
    protected function getQuotesReservedKeywordInIndexDeclarationSQL(): string
654 655 656 657
    {
        return ''; // not supported by this platform
    }

658
    protected function getQuotesReservedKeywordInTruncateTableSQL(): string
659 660 661 662
    {
        return 'TRUNCATE "select" IMMEDIATE';
    }

663
    protected function supportsInlineIndexDeclaration(): bool
664 665 666
    {
        return false;
    }
667

668
    protected function supportsCommentOnStatement(): bool
669 670 671 672
    {
        return true;
    }

673 674 675
    /**
     * {@inheritdoc}
     */
676
    protected function getAlterStringToFixedStringSQL(): array
677
    {
Sergei Morozov's avatar
Sergei Morozov committed
678
        return [
679 680
            '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
681
        ];
682
    }
683 684 685 686

    /**
     * {@inheritdoc}
     */
687
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL(): array
688
    {
Sergei Morozov's avatar
Sergei Morozov committed
689
        return ['RENAME INDEX idx_foo TO idx_foo_renamed'];
690
    }
691

692
    public function testQuotesTableNameInListTableColumnsSQL(): void
693
    {
694 695 696 697
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
        );
698 699
    }

700
    public function testQuotesTableNameInListTableIndexesSQL(): void
701
    {
702 703 704 705
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
        );
706 707
    }

708
    public function testQuotesTableNameInListTableForeignKeysSQL(): void
709
    {
710 711 712 713
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
        );
714
    }
715
}