SQLAnywhere16PlatformTest.php 45.3 KB
Newer Older
1 2
<?php

3
namespace Doctrine\DBAL\Tests\Platforms;
4

Sergei Morozov's avatar
Sergei Morozov committed
5
use Doctrine\DBAL\DBALException;
6 7
use Doctrine\DBAL\LockMode;
use Doctrine\DBAL\Platforms\AbstractPlatform;
8
use Doctrine\DBAL\Platforms\SQLAnywhere16Platform;
9
use Doctrine\DBAL\Platforms\TrimMode;
10 11
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
12
use Doctrine\DBAL\Schema\Comparator;
Sergei Morozov's avatar
Sergei Morozov committed
13
use Doctrine\DBAL\Schema\Constraint;
14 15
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Index;
16
use Doctrine\DBAL\Schema\Sequence;
17 18
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
19
use Doctrine\DBAL\TransactionIsolationLevel;
20
use Doctrine\DBAL\Types\Type;
Sergei Morozov's avatar
Sergei Morozov committed
21
use InvalidArgumentException;
22 23 24
use function mt_rand;
use function strlen;
use function substr;
25

26
class SQLAnywhere16PlatformTest extends AbstractPlatformTestCase
27
{
28
    /** @var SQLAnywhere16Platform */
Sergei Morozov's avatar
Sergei Morozov committed
29
    protected $platform;
30

31
    public function createPlatform() : AbstractPlatform
32
    {
33
        return new SQLAnywhere16Platform();
34 35
    }

36 37 38 39
    /**
     * {@inheritDoc}
     */
    public function getGenerateAlterTableSql() : array
40
    {
Sergei Morozov's avatar
Sergei Morozov committed
41
        return [
42
            "ALTER TABLE mytable ADD quota INT DEFAULT NULL, DROP foo, ALTER baz VARCHAR(1) DEFAULT 'def' NOT NULL, ALTER bloo BIT DEFAULT '0' NOT NULL",
Sergei Morozov's avatar
Sergei Morozov committed
43 44
            'ALTER TABLE mytable RENAME userlist',
        ];
45 46
    }

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

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

57
    public function getGenerateTableSql() : string
58 59 60 61
    {
        return 'CREATE TABLE test (id INT IDENTITY NOT NULL, test VARCHAR(255) DEFAULT NULL, PRIMARY KEY (id))';
    }

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

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

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

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

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

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

116 117 118 119
    /**
     * {@inheritDoc}
     */
    public function getCreateTableColumnCommentsSQL() : array
120
    {
Sergei Morozov's avatar
Sergei Morozov committed
121 122
        return [
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY (id))',
123
            "COMMENT ON COLUMN test.id IS 'This is a comment'",
Sergei Morozov's avatar
Sergei Morozov committed
124
        ];
125 126
    }

127 128 129 130
    /**
     * {@inheritDoc}
     */
    public function getAlterTableColumnCommentsSQL() : array
131
    {
Sergei Morozov's avatar
Sergei Morozov committed
132 133
        return [
            'ALTER TABLE mytable ADD quota INT NOT NULL',
134
            "COMMENT ON COLUMN mytable.quota IS 'A comment'",
Sergei Morozov's avatar
Sergei Morozov committed
135
            'COMMENT ON COLUMN mytable.foo IS NULL',
136
            "COMMENT ON COLUMN mytable.baz IS 'B comment'",
Sergei Morozov's avatar
Sergei Morozov committed
137
        ];
138 139
    }

140 141 142 143
    /**
     * {@inheritDoc}
     */
    public function getCreateTableColumnTypeCommentsSQL() : array
144
    {
Sergei Morozov's avatar
Sergei Morozov committed
145 146 147 148
        return [
            'CREATE TABLE test (id INT NOT NULL, data TEXT NOT NULL, PRIMARY KEY (id))',
            "COMMENT ON COLUMN test.data IS '(DC2Type:array)'",
        ];
149 150
    }

151
    public function testHasCorrectPlatformName() : void
152
    {
Sergei Morozov's avatar
Sergei Morozov committed
153
        self::assertEquals('sqlanywhere', $this->platform->getName());
154 155
    }

156
    public function testGeneratesCreateTableSQLWithCommonIndexes() : void
157 158 159
    {
        $table = new Table('test');
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
160 161 162 163
        $table->addColumn('name', 'string', ['length' => 50]);
        $table->setPrimaryKey(['id']);
        $table->addIndex(['name']);
        $table->addIndex(['id', 'name'], 'composite_idx');
164

165
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
166
            [
167 168
                'CREATE TABLE test (id INT NOT NULL, name VARCHAR(50) NOT NULL, PRIMARY KEY (id))',
                'CREATE INDEX IDX_D87F7E0C5E237E06 ON test (name)',
Sergei Morozov's avatar
Sergei Morozov committed
169 170
                'CREATE INDEX composite_idx ON test (id, name)',
            ],
Sergei Morozov's avatar
Sergei Morozov committed
171
            $this->platform->getCreateTableSQL($table)
172 173 174
        );
    }

175
    public function testGeneratesCreateTableSQLWithForeignKeyConstraints() : void
176 177 178 179 180
    {
        $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
181 182
        $table->setPrimaryKey(['id']);
        $table->addForeignKeyConstraint('foreign_table', ['fk_1', 'fk_2'], ['pk_1', 'pk_2']);
183 184
        $table->addForeignKeyConstraint(
            'foreign_table2',
Sergei Morozov's avatar
Sergei Morozov committed
185 186 187
            ['fk_1', 'fk_2'],
            ['pk_1', 'pk_2'],
            [],
188 189 190
            'named_fk'
        );

191
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
192
            ['CREATE TABLE test (id INT NOT NULL, fk_1 INT NOT NULL, fk_2 INT NOT NULL, ' .
193
                'CONSTRAINT FK_D87F7E0C177612A38E7F4319 FOREIGN KEY (fk_1, fk_2) REFERENCES foreign_table (pk_1, pk_2), ' .
Sergei Morozov's avatar
Sergei Morozov committed
194 195
                'CONSTRAINT named_fk FOREIGN KEY (fk_1, fk_2) REFERENCES foreign_table2 (pk_1, pk_2))',
            ],
Sergei Morozov's avatar
Sergei Morozov committed
196
            $this->platform->getCreateTableSQL($table, AbstractPlatform::CREATE_FOREIGNKEYS)
197 198 199
        );
    }

200
    public function testGeneratesCreateTableSQLWithCheckConstraints() : void
201 202 203
    {
        $table = new Table('test');
        $table->addColumn('id', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
204 205 206
        $table->addColumn('check_max', 'integer', ['platformOptions' => ['max' => 10]]);
        $table->addColumn('check_min', 'integer', ['platformOptions' => ['min' => 10]]);
        $table->setPrimaryKey(['id']);
207

208
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
209
            ['CREATE TABLE test (id INT NOT NULL, check_max INT NOT NULL, check_min INT NOT NULL, PRIMARY KEY (id), CHECK (check_max <= 10), CHECK (check_min >= 10))'],
Sergei Morozov's avatar
Sergei Morozov committed
210
            $this->platform->getCreateTableSQL($table)
211 212 213
        );
    }

214
    public function testGeneratesTableAlterationWithRemovedColumnCommentSql() : void
215 216
    {
        $table = new Table('mytable');
Sergei Morozov's avatar
Sergei Morozov committed
217
        $table->addColumn('foo', 'string', ['comment' => 'foo comment']);
218

Sergei Morozov's avatar
Sergei Morozov committed
219 220
        $tableDiff                        = new TableDiff('mytable');
        $tableDiff->fromTable             = $table;
221 222 223
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
            'foo',
            new Column('foo', Type::getType('string')),
Sergei Morozov's avatar
Sergei Morozov committed
224
            ['comment']
225 226
        );

227
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
228
            ['COMMENT ON COLUMN mytable.foo IS NULL'],
Sergei Morozov's avatar
Sergei Morozov committed
229
            $this->platform->getAlterTableSQL($tableDiff)
230 231 232
        );
    }

233
    /**
234 235
     * @param int|bool|null $lockMode
     *
236 237
     * @dataProvider getLockHints
     */
238
    public function testAppendsLockHint($lockMode, string $lockHint) : void
239
    {
Sergei Morozov's avatar
Sergei Morozov committed
240
        $fromClause     = 'FROM users';
241
        $expectedResult = $fromClause . $lockHint;
242

Sergei Morozov's avatar
Sergei Morozov committed
243
        self::assertSame($expectedResult, $this->platform->appendLockHint($fromClause, $lockMode));
244 245
    }

246 247 248 249
    /**
     * @return mixed[][]
     */
    public static function getLockHints() : iterable
250
    {
Sergei Morozov's avatar
Sergei Morozov committed
251 252 253 254 255 256 257 258 259
        return [
            [null, ''],
            [false, ''],
            [true, ''],
            [LockMode::NONE, ' WITH (NOLOCK)'],
            [LockMode::OPTIMISTIC, ''],
            [LockMode::PESSIMISTIC_READ, ' WITH (UPDLOCK)'],
            [LockMode::PESSIMISTIC_WRITE, ' WITH (XLOCK)'],
        ];
260 261
    }

262
    public function testHasCorrectMaxIdentifierLength() : void
263
    {
Sergei Morozov's avatar
Sergei Morozov committed
264
        self::assertEquals(128, $this->platform->getMaxIdentifierLength());
265 266
    }

267
    public function testFixesSchemaElementNames() : void
268
    {
Sergei Morozov's avatar
Sergei Morozov committed
269
        $maxIdentifierLength = $this->platform->getMaxIdentifierLength();
Sergei Morozov's avatar
Sergei Morozov committed
270 271
        $characters          = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $schemaElementName   = '';
272 273 274 275 276 277 278

        for ($i = 0; $i < $maxIdentifierLength + 100; $i++) {
            $schemaElementName .= $characters[mt_rand(0, strlen($characters) - 1)];
        }

        $fixedSchemaElementName = substr($schemaElementName, 0, $maxIdentifierLength);

279
        self::assertEquals(
280
            $fixedSchemaElementName,
Sergei Morozov's avatar
Sergei Morozov committed
281
            $this->platform->fixSchemaElementName($schemaElementName)
282
        );
283
        self::assertEquals(
284
            $fixedSchemaElementName,
Sergei Morozov's avatar
Sergei Morozov committed
285
            $this->platform->fixSchemaElementName($fixedSchemaElementName)
286 287 288
        );
    }

289
    public function testGeneratesColumnTypesDeclarationSQL() : void
290
    {
Sergei Morozov's avatar
Sergei Morozov committed
291
        $fullColumnDef = [
292 293 294
            'length' => 10,
            'fixed' => true,
            'unsigned' => true,
Sergei Morozov's avatar
Sergei Morozov committed
295 296
            'autoincrement' => true,
        ];
297

Sergei Morozov's avatar
Sergei Morozov committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
        self::assertEquals('SMALLINT', $this->platform->getSmallIntTypeDeclarationSQL([]));
        self::assertEquals('UNSIGNED SMALLINT', $this->platform->getSmallIntTypeDeclarationSQL(['unsigned' => true]));
        self::assertEquals('UNSIGNED SMALLINT IDENTITY', $this->platform->getSmallIntTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('INT', $this->platform->getIntegerTypeDeclarationSQL([]));
        self::assertEquals('UNSIGNED INT', $this->platform->getIntegerTypeDeclarationSQL(['unsigned' => true]));
        self::assertEquals('UNSIGNED INT IDENTITY', $this->platform->getIntegerTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('BIGINT', $this->platform->getBigIntTypeDeclarationSQL([]));
        self::assertEquals('UNSIGNED BIGINT', $this->platform->getBigIntTypeDeclarationSQL(['unsigned' => true]));
        self::assertEquals('UNSIGNED BIGINT IDENTITY', $this->platform->getBigIntTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('LONG BINARY', $this->platform->getBlobTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('BIT', $this->platform->getBooleanTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('TEXT', $this->platform->getClobTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('DATE', $this->platform->getDateTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('DATETIME', $this->platform->getDateTimeTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('TIME', $this->platform->getTimeTypeDeclarationSQL($fullColumnDef));
        self::assertEquals('UNIQUEIDENTIFIER', $this->platform->getGuidTypeDeclarationSQL($fullColumnDef));

        self::assertEquals(1, $this->platform->getVarcharDefaultLength());
        self::assertEquals(32767, $this->platform->getVarcharMaxLength());
317 318
    }

319
    public function testHasNativeGuidType() : void
320
    {
Sergei Morozov's avatar
Sergei Morozov committed
321
        self::assertTrue($this->platform->hasNativeGuidType());
322 323
    }

324
    public function testGeneratesDDLSnippets() : void
325
    {
Sergei Morozov's avatar
Sergei Morozov committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
        self::assertEquals("CREATE DATABASE 'foobar'", $this->platform->getCreateDatabaseSQL('foobar'));
        self::assertEquals("CREATE DATABASE 'foobar'", $this->platform->getCreateDatabaseSQL('"foobar"'));
        self::assertEquals("CREATE DATABASE 'create'", $this->platform->getCreateDatabaseSQL('create'));
        self::assertEquals("DROP DATABASE 'foobar'", $this->platform->getDropDatabaseSQL('foobar'));
        self::assertEquals("DROP DATABASE 'foobar'", $this->platform->getDropDatabaseSQL('"foobar"'));
        self::assertEquals("DROP DATABASE 'create'", $this->platform->getDropDatabaseSQL('create'));
        self::assertEquals('CREATE GLOBAL TEMPORARY TABLE', $this->platform->getCreateTemporaryTableSnippetSQL());
        self::assertEquals("START DATABASE 'foobar' AUTOSTOP OFF", $this->platform->getStartDatabaseSQL('foobar'));
        self::assertEquals("START DATABASE 'foobar' AUTOSTOP OFF", $this->platform->getStartDatabaseSQL('"foobar"'));
        self::assertEquals("START DATABASE 'create' AUTOSTOP OFF", $this->platform->getStartDatabaseSQL('create'));
        self::assertEquals('STOP DATABASE "foobar" UNCONDITIONALLY', $this->platform->getStopDatabaseSQL('foobar'));
        self::assertEquals('STOP DATABASE "foobar" UNCONDITIONALLY', $this->platform->getStopDatabaseSQL('"foobar"'));
        self::assertEquals('STOP DATABASE "create" UNCONDITIONALLY', $this->platform->getStopDatabaseSQL('create'));
        self::assertEquals('TRUNCATE TABLE foobar', $this->platform->getTruncateTableSQL('foobar'));
        self::assertEquals('TRUNCATE TABLE foobar', $this->platform->getTruncateTableSQL('foobar'), true);
341 342

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

347
    public function testGeneratesPrimaryKeyDeclarationSQL() : void
348
    {
349
        self::assertEquals(
350
            'CONSTRAINT pk PRIMARY KEY CLUSTERED (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
351
            $this->platform->getPrimaryKeyDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
352
                new Index(null, ['a', 'b'], true, true, ['clustered']),
353 354 355
                'pk'
            )
        );
356
        self::assertEquals(
357
            'PRIMARY KEY (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
358
            $this->platform->getPrimaryKeyDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
359
                new Index(null, ['a', 'b'], true, true)
360 361 362 363
            )
        );
    }

364
    public function testCannotGeneratePrimaryKeyDeclarationSQLWithEmptyColumns() : void
365
    {
Sergei Morozov's avatar
Sergei Morozov committed
366
        $this->expectException(InvalidArgumentException::class);
367

Sergei Morozov's avatar
Sergei Morozov committed
368
        $this->platform->getPrimaryKeyDeclarationSQL(new Index('pk', [], true, true));
369 370
    }

371
    public function testGeneratesCreateUnnamedPrimaryKeySQL() : void
372
    {
373
        self::assertEquals(
374
            'ALTER TABLE foo ADD PRIMARY KEY CLUSTERED (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
375
            $this->platform->getCreatePrimaryKeySQL(
Sergei Morozov's avatar
Sergei Morozov committed
376
                new Index('pk', ['a', 'b'], true, true, ['clustered']),
377 378 379
                'foo'
            )
        );
380
        self::assertEquals(
381
            'ALTER TABLE foo ADD PRIMARY KEY (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
382
            $this->platform->getCreatePrimaryKeySQL(
Sergei Morozov's avatar
Sergei Morozov committed
383
                new Index('any_pk_name', ['a', 'b'], true, true),
384 385 386 387 388
                new Table('foo')
            )
        );
    }

389
    public function testGeneratesUniqueConstraintDeclarationSQL() : void
390
    {
391
        self::assertEquals(
392
            'CONSTRAINT unique_constraint UNIQUE CLUSTERED (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
393
            $this->platform->getUniqueConstraintDeclarationSQL(
394
                'unique_constraint',
Sergei Morozov's avatar
Sergei Morozov committed
395
                new Index(null, ['a', 'b'], true, false, ['clustered'])
396 397
            )
        );
398
        self::assertEquals(
399
            'UNIQUE (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
400
            $this->platform->getUniqueConstraintDeclarationSQL(null, new Index(null, ['a', 'b'], true, false))
401 402 403
        );
    }

404
    public function testCannotGenerateUniqueConstraintDeclarationSQLWithEmptyColumns() : void
405
    {
Sergei Morozov's avatar
Sergei Morozov committed
406
        $this->expectException(InvalidArgumentException::class);
407

Sergei Morozov's avatar
Sergei Morozov committed
408
        $this->platform->getUniqueConstraintDeclarationSQL('constr', new Index('constr', [], true));
409 410
    }

411
    public function testGeneratesForeignKeyConstraintsWithAdvancedPlatformOptionsSQL() : void
412
    {
413
        self::assertEquals(
414 415 416 417
            'CONSTRAINT fk ' .
                'NOT NULL FOREIGN KEY (a, b) ' .
                'REFERENCES foreign_table (c, d) ' .
                'MATCH UNIQUE SIMPLE ON UPDATE CASCADE ON DELETE SET NULL CHECK ON COMMIT CLUSTERED FOR OLAP WORKLOAD',
Sergei Morozov's avatar
Sergei Morozov committed
418
            $this->platform->getForeignKeyDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
419
                new ForeignKeyConstraint(['a', 'b'], 'foreign_table', ['c', 'd'], 'fk', [
420
                    'notnull' => true,
421
                    'match' => SQLAnywhere16Platform::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE,
422 423 424 425
                    'onUpdate' => 'CASCADE',
                    'onDelete' => 'SET NULL',
                    'check_on_commit' => true,
                    'clustered' => true,
Sergei Morozov's avatar
Sergei Morozov committed
426 427
                    'for_olap_workload' => true,
                ])
428 429
            )
        );
430
        self::assertEquals(
431
            'FOREIGN KEY (a, b) REFERENCES foreign_table (c, d)',
Sergei Morozov's avatar
Sergei Morozov committed
432
            $this->platform->getForeignKeyDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
433
                new ForeignKeyConstraint(['a', 'b'], 'foreign_table', ['c', 'd'])
434 435 436 437
            )
        );
    }

438
    public function testGeneratesForeignKeyMatchClausesSQL() : void
439
    {
Sergei Morozov's avatar
Sergei Morozov committed
440 441 442 443
        self::assertEquals('SIMPLE', $this->platform->getForeignKeyMatchClauseSQL(1));
        self::assertEquals('FULL', $this->platform->getForeignKeyMatchClauseSQL(2));
        self::assertEquals('UNIQUE SIMPLE', $this->platform->getForeignKeyMatchClauseSQL(129));
        self::assertEquals('UNIQUE FULL', $this->platform->getForeignKeyMatchClauseSQL(130));
444 445
    }

446
    public function testCannotGenerateInvalidForeignKeyMatchClauseSQL() : void
447
    {
Sergei Morozov's avatar
Sergei Morozov committed
448
        $this->expectException(InvalidArgumentException::class);
449

Sergei Morozov's avatar
Sergei Morozov committed
450
        $this->platform->getForeignKeyMatchCLauseSQL(3);
451 452
    }

453
    public function testCannotGenerateForeignKeyConstraintSQLWithEmptyLocalColumns() : void
454
    {
Sergei Morozov's avatar
Sergei Morozov committed
455 456
        $this->expectException(InvalidArgumentException::class);
        $this->platform->getForeignKeyDeclarationSQL(new ForeignKeyConstraint([], 'foreign_tbl', ['c', 'd']));
457 458
    }

459
    public function testCannotGenerateForeignKeyConstraintSQLWithEmptyForeignColumns() : void
460
    {
Sergei Morozov's avatar
Sergei Morozov committed
461 462
        $this->expectException(InvalidArgumentException::class);
        $this->platform->getForeignKeyDeclarationSQL(new ForeignKeyConstraint(['a', 'b'], 'foreign_tbl', []));
463 464
    }

465
    public function testCannotGenerateForeignKeyConstraintSQLWithEmptyForeignTableName() : void
466
    {
Sergei Morozov's avatar
Sergei Morozov committed
467 468
        $this->expectException(InvalidArgumentException::class);
        $this->platform->getForeignKeyDeclarationSQL(new ForeignKeyConstraint(['a', 'b'], '', ['c', 'd']));
469 470
    }

471
    public function testCannotGenerateCommonIndexWithCreateConstraintSQL() : void
472
    {
Sergei Morozov's avatar
Sergei Morozov committed
473
        $this->expectException(InvalidArgumentException::class);
474

Sergei Morozov's avatar
Sergei Morozov committed
475
        $this->platform->getCreateConstraintSQL(new Index('fooindex', []), new Table('footable'));
476 477
    }

478
    public function testCannotGenerateCustomConstraintWithCreateConstraintSQL() : void
479
    {
Sergei Morozov's avatar
Sergei Morozov committed
480
        $this->expectException(InvalidArgumentException::class);
481

Sergei Morozov's avatar
Sergei Morozov committed
482
        $this->platform->getCreateConstraintSQL($this->createMock(Constraint::class), 'footable');
483 484
    }

485
    public function testGeneratesCreateIndexWithAdvancedPlatformOptionsSQL() : void
486
    {
487
        self::assertEquals(
488
            'CREATE UNIQUE INDEX fooindex ON footable (a, b) WITH NULLS DISTINCT',
Sergei Morozov's avatar
Sergei Morozov committed
489
            $this->platform->getCreateIndexSQL(
490 491
                new Index(
                    'fooindex',
Sergei Morozov's avatar
Sergei Morozov committed
492
                    ['a', 'b'],
493 494
                    true,
                    false,
495
                    ['with_nulls_distinct']
496 497 498 499
                ),
                'footable'
            )
        );
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602

        // WITH NULLS DISTINCT clause not available on primary indexes.
        self::assertEquals(
            'ALTER TABLE footable ADD PRIMARY KEY (a, b)',
            $this->platform->getCreateIndexSQL(
                new Index(
                    'fooindex',
                    ['a', 'b'],
                    false,
                    true,
                    ['with_nulls_distinct']
                ),
                'footable'
            )
        );

        // WITH NULLS DISTINCT clause not available on non-unique indexes.
        self::assertEquals(
            'CREATE INDEX fooindex ON footable (a, b)',
            $this->platform->getCreateIndexSQL(
                new Index(
                    'fooindex',
                    ['a', 'b'],
                    false,
                    false,
                    ['with_nulls_distinct']
                ),
                'footable'
            )
        );

        self::assertEquals(
            'CREATE VIRTUAL UNIQUE CLUSTERED INDEX fooindex ON footable (a, b) WITH NULLS NOT DISTINCT FOR OLAP WORKLOAD',
            $this->platform->getCreateIndexSQL(
                new Index(
                    'fooindex',
                    ['a', 'b'],
                    true,
                    false,
                    ['virtual', 'clustered', 'with_nulls_not_distinct', 'for_olap_workload']
                ),
                'footable'
            )
        );
        self::assertEquals(
            'CREATE VIRTUAL CLUSTERED INDEX fooindex ON footable (a, b) FOR OLAP WORKLOAD',
            $this->platform->getCreateIndexSQL(
                new Index(
                    'fooindex',
                    ['a', 'b'],
                    false,
                    false,
                    ['virtual', 'clustered', 'with_nulls_not_distinct', 'for_olap_workload']
                ),
                'footable'
            )
        );

        // WITH NULLS NOT DISTINCT clause not available on primary indexes.
        self::assertEquals(
            'ALTER TABLE footable ADD PRIMARY KEY (a, b)',
            $this->platform->getCreateIndexSQL(
                new Index(
                    'fooindex',
                    ['a', 'b'],
                    false,
                    true,
                    ['with_nulls_not_distinct']
                ),
                'footable'
            )
        );

        // WITH NULLS NOT DISTINCT clause not available on non-unique indexes.
        self::assertEquals(
            'CREATE INDEX fooindex ON footable (a, b)',
            $this->platform->getCreateIndexSQL(
                new Index(
                    'fooindex',
                    ['a', 'b'],
                    false,
                    false,
                    ['with_nulls_not_distinct']
                ),
                'footable'
            )
        );
    }

    public function testThrowsExceptionOnInvalidWithNullsNotDistinctIndexOptions() : void
    {
        $this->expectException('UnexpectedValueException');

        $this->platform->getCreateIndexSQL(
            new Index(
                'fooindex',
                ['a', 'b'],
                false,
                false,
                ['with_nulls_distinct', 'with_nulls_not_distinct']
            ),
            'footable'
        );
603 604
    }

605
    public function testDoesNotSupportIndexDeclarationInCreateAlterTableStatements() : void
606
    {
Sergei Morozov's avatar
Sergei Morozov committed
607
        $this->expectException(DBALException::class);
608

Sergei Morozov's avatar
Sergei Morozov committed
609
        $this->platform->getIndexDeclarationSQL('index', new Index('index', []));
610 611
    }

612
    public function testGeneratesDropIndexSQL() : void
613
    {
Sergei Morozov's avatar
Sergei Morozov committed
614
        $index = new Index('fooindex', []);
615

Sergei Morozov's avatar
Sergei Morozov committed
616 617 618
        self::assertEquals('DROP INDEX fooindex', $this->platform->getDropIndexSQL($index));
        self::assertEquals('DROP INDEX footable.fooindex', $this->platform->getDropIndexSQL($index, 'footable'));
        self::assertEquals('DROP INDEX footable.fooindex', $this->platform->getDropIndexSQL(
619 620 621 622 623
            $index,
            new Table('footable')
        ));
    }

624
    public function testCannotGenerateDropIndexSQLWithInvalidIndexParameter() : void
625
    {
Sergei Morozov's avatar
Sergei Morozov committed
626
        $this->expectException(InvalidArgumentException::class);
627

Sergei Morozov's avatar
Sergei Morozov committed
628
        $this->platform->getDropIndexSQL(['index'], 'table');
629 630
    }

631
    public function testCannotGenerateDropIndexSQLWithInvalidTableParameter() : void
632
    {
Sergei Morozov's avatar
Sergei Morozov committed
633
        $this->expectException(InvalidArgumentException::class);
634

Sergei Morozov's avatar
Sergei Morozov committed
635
        $this->platform->getDropIndexSQL('index', ['table']);
636 637
    }

638
    public function testGeneratesSQLSnippets() : void
639
    {
Sergei Morozov's avatar
Sergei Morozov committed
640
        self::assertEquals('STRING(column1, "string1", column2, "string2")', $this->platform->getConcatExpression(
641 642 643 644 645
            'column1',
            '"string1"',
            'column2',
            '"string2"'
        ));
Sergei Morozov's avatar
Sergei Morozov committed
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
        self::assertEquals('CURRENT DATE', $this->platform->getCurrentDateSQL());
        self::assertEquals('CURRENT TIME', $this->platform->getCurrentTimeSQL());
        self::assertEquals('CURRENT TIMESTAMP', $this->platform->getCurrentTimestampSQL());
        self::assertEquals("DATEADD(DAY, 4, '1987/05/02')", $this->platform->getDateAddDaysExpression("'1987/05/02'", 4));
        self::assertEquals("DATEADD(HOUR, 12, '1987/05/02')", $this->platform->getDateAddHourExpression("'1987/05/02'", 12));
        self::assertEquals("DATEADD(MINUTE, 2, '1987/05/02')", $this->platform->getDateAddMinutesExpression("'1987/05/02'", 2));
        self::assertEquals("DATEADD(MONTH, 102, '1987/05/02')", $this->platform->getDateAddMonthExpression("'1987/05/02'", 102));
        self::assertEquals("DATEADD(QUARTER, 5, '1987/05/02')", $this->platform->getDateAddQuartersExpression("'1987/05/02'", 5));
        self::assertEquals("DATEADD(SECOND, 1, '1987/05/02')", $this->platform->getDateAddSecondsExpression("'1987/05/02'", 1));
        self::assertEquals("DATEADD(WEEK, 3, '1987/05/02')", $this->platform->getDateAddWeeksExpression("'1987/05/02'", 3));
        self::assertEquals("DATEADD(YEAR, 10, '1987/05/02')", $this->platform->getDateAddYearsExpression("'1987/05/02'", 10));
        self::assertEquals("DATEDIFF(day, '1987/04/01', '1987/05/02')", $this->platform->getDateDiffExpression("'1987/05/02'", "'1987/04/01'"));
        self::assertEquals("DATEADD(DAY, -1 * 4, '1987/05/02')", $this->platform->getDateSubDaysExpression("'1987/05/02'", 4));
        self::assertEquals("DATEADD(HOUR, -1 * 12, '1987/05/02')", $this->platform->getDateSubHourExpression("'1987/05/02'", 12));
        self::assertEquals("DATEADD(MINUTE, -1 * 2, '1987/05/02')", $this->platform->getDateSubMinutesExpression("'1987/05/02'", 2));
        self::assertEquals("DATEADD(MONTH, -1 * 102, '1987/05/02')", $this->platform->getDateSubMonthExpression("'1987/05/02'", 102));
        self::assertEquals("DATEADD(QUARTER, -1 * 5, '1987/05/02')", $this->platform->getDateSubQuartersExpression("'1987/05/02'", 5));
        self::assertEquals("DATEADD(SECOND, -1 * 1, '1987/05/02')", $this->platform->getDateSubSecondsExpression("'1987/05/02'", 1));
        self::assertEquals("DATEADD(WEEK, -1 * 3, '1987/05/02')", $this->platform->getDateSubWeeksExpression("'1987/05/02'", 3));
        self::assertEquals("DATEADD(YEAR, -1 * 10, '1987/05/02')", $this->platform->getDateSubYearsExpression("'1987/05/02'", 10));
        self::assertEquals('Y-m-d H:i:s.u', $this->platform->getDateTimeFormatString());
        self::assertEquals('H:i:s.u', $this->platform->getTimeFormatString());
        self::assertEquals('', $this->platform->getForUpdateSQL());
        self::assertEquals('NEWID()', $this->platform->getGuidExpression());
        self::assertEquals('LOCATE(string_column, substring_column)', $this->platform->getLocateExpression('string_column', 'substring_column'));
        self::assertEquals('LOCATE(string_column, substring_column, 1)', $this->platform->getLocateExpression('string_column', 'substring_column', 1));
        self::assertEquals("HASH(column, 'MD5')", $this->platform->getMd5Expression('column'));
        self::assertEquals('SUBSTRING(column, 5)', $this->platform->getSubstringExpression('column', 5));
        self::assertEquals('SUBSTRING(column, 5, 2)', $this->platform->getSubstringExpression('column', 5, 2));
        self::assertEquals('GLOBAL TEMPORARY', $this->platform->getTemporaryTableSQL());
676
        self::assertEquals(
677
            'LTRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
678
            $this->platform->getTrimExpression('column', TrimMode::LEADING)
679
        );
680
        self::assertEquals(
681
            'RTRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
682
            $this->platform->getTrimExpression('column', TrimMode::TRAILING)
683
        );
684
        self::assertEquals(
685
            'TRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
686
            $this->platform->getTrimExpression('column')
687
        );
688
        self::assertEquals(
689
            'TRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
690
            $this->platform->getTrimExpression('column', TrimMode::UNSPECIFIED)
691
        );
692
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
693
            "SUBSTR(column, PATINDEX('%[^' + c + ']%', column))",
Sergei Morozov's avatar
Sergei Morozov committed
694
            $this->platform->getTrimExpression('column', TrimMode::LEADING, 'c')
695
        );
696
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
697
            "REVERSE(SUBSTR(REVERSE(column), PATINDEX('%[^' + c + ']%', REVERSE(column))))",
Sergei Morozov's avatar
Sergei Morozov committed
698
            $this->platform->getTrimExpression('column', TrimMode::TRAILING, 'c')
699
        );
700
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
701 702
            "REVERSE(SUBSTR(REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))), PATINDEX('%[^' + c + ']%', " .
            "REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))))))",
Sergei Morozov's avatar
Sergei Morozov committed
703
            $this->platform->getTrimExpression('column', null, 'c')
704
        );
705
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
706 707
            "REVERSE(SUBSTR(REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))), PATINDEX('%[^' + c + ']%', " .
            "REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))))))",
Sergei Morozov's avatar
Sergei Morozov committed
708
            $this->platform->getTrimExpression('column', TrimMode::UNSPECIFIED, 'c')
709 710 711
        );
    }

712
    public function testHasCorrectDateTimeTzFormatString() : void
713
    {
714 715
        self::assertEquals('Y-m-d H:i:s.uP', $this->platform->getDateTimeTzFormatString());
    }
716

717 718 719 720 721 722 723 724 725 726 727
    public function testGeneratesDateTimeTzColumnTypeDeclarationSQL() : void
    {
        self::assertEquals(
            'TIMESTAMP WITH TIME ZONE',
            $this->platform->getDateTimeTzTypeDeclarationSQL([
                'length' => 10,
                'fixed' => true,
                'unsigned' => true,
                'autoincrement' => true,
            ])
        );
728 729
    }

730
    public function testInitializesDateTimeTzTypeMapping() : void
731
    {
732 733
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('timestamp with time zone'));
        self::assertEquals('datetime', $this->platform->getDoctrineTypeMapping('timestamp with time zone'));
734 735
    }

736
    public function testHasCorrectDefaultTransactionIsolationLevel() : void
737
    {
738
        self::assertEquals(
739
            TransactionIsolationLevel::READ_UNCOMMITTED,
Sergei Morozov's avatar
Sergei Morozov committed
740
            $this->platform->getDefaultTransactionIsolationLevel()
741 742 743
        );
    }

744
    public function testGeneratesTransactionsCommands() : void
745
    {
746
        self::assertEquals(
747
            'SET TEMPORARY OPTION isolation_level = 0',
Sergei Morozov's avatar
Sergei Morozov committed
748
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
749
        );
750
        self::assertEquals(
751
            'SET TEMPORARY OPTION isolation_level = 1',
Sergei Morozov's avatar
Sergei Morozov committed
752
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
753
        );
754
        self::assertEquals(
755
            'SET TEMPORARY OPTION isolation_level = 2',
Sergei Morozov's avatar
Sergei Morozov committed
756
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
757
        );
758
        self::assertEquals(
759
            'SET TEMPORARY OPTION isolation_level = 3',
Sergei Morozov's avatar
Sergei Morozov committed
760
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
761 762 763
        );
    }

764
    public function testCannotGenerateTransactionCommandWithInvalidIsolationLevel() : void
765
    {
Sergei Morozov's avatar
Sergei Morozov committed
766
        $this->expectException(InvalidArgumentException::class);
767

Sergei Morozov's avatar
Sergei Morozov committed
768
        $this->platform->getSetTransactionIsolationSQL('invalid_transaction_isolation_level');
769 770
    }

771
    public function testModifiesLimitQuery() : void
772
    {
773
        self::assertEquals(
774
            'SELECT TOP 10 * FROM user',
Sergei Morozov's avatar
Sergei Morozov committed
775
            $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0)
776 777 778
        );
    }

779
    public function testModifiesLimitQueryWithEmptyOffset() : void
780
    {
781
        self::assertEquals(
782
            'SELECT TOP 10 * FROM user',
Sergei Morozov's avatar
Sergei Morozov committed
783
            $this->platform->modifyLimitQuery('SELECT * FROM user', 10)
784 785 786
        );
    }

787
    public function testModifiesLimitQueryWithOffset() : void
788
    {
789
        self::assertEquals(
790
            'SELECT TOP 10 START AT 6 * FROM user',
Sergei Morozov's avatar
Sergei Morozov committed
791
            $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 5)
792
        );
793
        self::assertEquals(
794
            'SELECT TOP 0 START AT 6 * FROM user',
Sergei Morozov's avatar
Sergei Morozov committed
795
            $this->platform->modifyLimitQuery('SELECT * FROM user', 0, 5)
796 797 798
        );
    }

799
    public function testModifiesLimitQueryWithSubSelect() : void
800
    {
801
        self::assertEquals(
802
            'SELECT TOP 10 * FROM (SELECT u.id as uid, u.name as uname FROM user) AS doctrine_tbl',
Sergei Morozov's avatar
Sergei Morozov committed
803
            $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname FROM user) AS doctrine_tbl', 10)
804 805 806
        );
    }

807
    public function testModifiesLimitQueryWithoutLimit() : void
808 809 810 811 812 813 814
    {
        self::assertEquals(
            'SELECT TOP ALL START AT 11 n FROM Foo',
            $this->platform->modifyLimitQuery('SELECT n FROM Foo', null, 10)
        );
    }

815
    public function testPrefersIdentityColumns() : void
816
    {
Sergei Morozov's avatar
Sergei Morozov committed
817
        self::assertTrue($this->platform->prefersIdentityColumns());
818 819
    }

820
    public function testDoesNotPreferSequences() : void
821
    {
Sergei Morozov's avatar
Sergei Morozov committed
822
        self::assertFalse($this->platform->prefersSequences());
823 824
    }

825
    public function testSupportsIdentityColumns() : void
826
    {
Sergei Morozov's avatar
Sergei Morozov committed
827
        self::assertTrue($this->platform->supportsIdentityColumns());
828 829
    }

830
    public function testSupportsPrimaryConstraints() : void
831
    {
Sergei Morozov's avatar
Sergei Morozov committed
832
        self::assertTrue($this->platform->supportsPrimaryConstraints());
833 834
    }

835
    public function testSupportsForeignKeyConstraints() : void
836
    {
Sergei Morozov's avatar
Sergei Morozov committed
837
        self::assertTrue($this->platform->supportsForeignKeyConstraints());
838 839
    }

840
    public function testSupportsForeignKeyOnUpdate() : void
841
    {
Sergei Morozov's avatar
Sergei Morozov committed
842
        self::assertTrue($this->platform->supportsForeignKeyOnUpdate());
843 844
    }

845
    public function testSupportsAlterTable() : void
846
    {
Sergei Morozov's avatar
Sergei Morozov committed
847
        self::assertTrue($this->platform->supportsAlterTable());
848 849
    }

850
    public function testSupportsTransactions() : void
851
    {
Sergei Morozov's avatar
Sergei Morozov committed
852
        self::assertTrue($this->platform->supportsTransactions());
853 854
    }

855
    public function testSupportsSchemas() : void
856
    {
Sergei Morozov's avatar
Sergei Morozov committed
857
        self::assertFalse($this->platform->supportsSchemas());
858 859
    }

860
    public function testSupportsIndexes() : void
861
    {
Sergei Morozov's avatar
Sergei Morozov committed
862
        self::assertTrue($this->platform->supportsIndexes());
863 864
    }

865
    public function testSupportsCommentOnStatement() : void
866
    {
Sergei Morozov's avatar
Sergei Morozov committed
867
        self::assertTrue($this->platform->supportsCommentOnStatement());
868 869
    }

870
    public function testSupportsSavePoints() : void
871
    {
Sergei Morozov's avatar
Sergei Morozov committed
872
        self::assertTrue($this->platform->supportsSavepoints());
873 874
    }

875
    public function testSupportsReleasePoints() : void
876
    {
Sergei Morozov's avatar
Sergei Morozov committed
877
        self::assertTrue($this->platform->supportsReleaseSavepoints());
878 879
    }

880
    public function testSupportsCreateDropDatabase() : void
881
    {
Sergei Morozov's avatar
Sergei Morozov committed
882
        self::assertTrue($this->platform->supportsCreateDropDatabase());
883 884
    }

885
    public function testSupportsGettingAffectedRows() : void
886
    {
Sergei Morozov's avatar
Sergei Morozov committed
887
        self::assertTrue($this->platform->supportsGettingAffectedRows());
888 889
    }

890
    public function testDoesNotSupportSequences() : void
891
    {
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
        self::markTestSkipped('This version of the platform now supports sequences.');
    }

    public function testSupportsSequences() : void
    {
        self::assertTrue($this->platform->supportsSequences());
    }

    public function testGeneratesSequenceSqlCommands() : void
    {
        $sequence = new Sequence('myseq', 20, 1);
        self::assertEquals(
            'CREATE SEQUENCE myseq INCREMENT BY 20 START WITH 1 MINVALUE 1',
            $this->platform->getCreateSequenceSQL($sequence)
        );
        self::assertEquals(
            'ALTER SEQUENCE myseq INCREMENT BY 20',
            $this->platform->getAlterSequenceSQL($sequence)
        );
        self::assertEquals(
            'DROP SEQUENCE myseq',
            $this->platform->getDropSequenceSQL('myseq')
        );
        self::assertEquals(
            'DROP SEQUENCE myseq',
            $this->platform->getDropSequenceSQL($sequence)
        );
        self::assertEquals(
            'SELECT myseq.NEXTVAL',
            $this->platform->getSequenceNextValSQL('myseq')
        );
        self::assertEquals(
            'SELECT sequence_name, increment_by, start_with, min_value FROM SYS.SYSSEQUENCE',
            $this->platform->getListSequencesSQL(null)
        );
927 928
    }

929
    public function testDoesNotSupportInlineColumnComments() : void
930
    {
Sergei Morozov's avatar
Sergei Morozov committed
931
        self::assertFalse($this->platform->supportsInlineColumnComments());
932 933
    }

934
    public function testCannotEmulateSchemas() : void
935
    {
Sergei Morozov's avatar
Sergei Morozov committed
936
        self::assertFalse($this->platform->canEmulateSchemas());
937
    }
Steve Müller's avatar
Steve Müller committed
938

939
    public function testInitializesDoctrineTypeMappings() : void
Steve Müller's avatar
Steve Müller committed
940
    {
Sergei Morozov's avatar
Sergei Morozov committed
941 942
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('integer'));
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('integer'));
943

Sergei Morozov's avatar
Sergei Morozov committed
944 945
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('binary'));
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('binary'));
Steve Müller's avatar
Steve Müller committed
946

Sergei Morozov's avatar
Sergei Morozov committed
947 948
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varbinary'));
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('varbinary'));
Steve Müller's avatar
Steve Müller committed
949 950
    }

951
    protected function getBinaryDefaultLength() : int
Steve Müller's avatar
Steve Müller committed
952 953 954 955
    {
        return 1;
    }

956
    protected function getBinaryMaxLength() : int
Steve Müller's avatar
Steve Müller committed
957 958 959 960
    {
        return 32767;
    }

961
    public function testReturnsBinaryTypeDeclarationSQL() : void
Steve Müller's avatar
Steve Müller committed
962
    {
Sergei Morozov's avatar
Sergei Morozov committed
963 964 965
        self::assertSame('VARBINARY(1)', $this->platform->getBinaryTypeDeclarationSQL([]));
        self::assertSame('VARBINARY(1)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0]));
        self::assertSame('VARBINARY(32767)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32767]));
Steve Müller's avatar
Steve Müller committed
966

Sergei Morozov's avatar
Sergei Morozov committed
967 968 969
        self::assertSame('BINARY(1)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true]));
        self::assertSame('BINARY(1)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0]));
        self::assertSame('BINARY(32767)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32767]));
970 971 972 973
    }

    /**
     * @group legacy
974
     * @expectedDeprecation Binary field length 32768 is greater than supported by the platform (32767). Reduce the field length or use a BLOB field instead.
975
     */
976
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
977
    {
Sergei Morozov's avatar
Sergei Morozov committed
978 979
        self::assertSame('LONG BINARY', $this->platform->getBinaryTypeDeclarationSQL(['length' => 32768]));
        self::assertSame('LONG BINARY', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 32768]));
Steve Müller's avatar
Steve Müller committed
980
    }
981 982

    /**
983 984
     * {@inheritDoc}
     *
985 986
     * @group DBAL-234
     */
987
    protected function getAlterTableRenameIndexSQL() : array
988
    {
Sergei Morozov's avatar
Sergei Morozov committed
989
        return ['ALTER INDEX idx_foo ON mytable RENAME TO idx_bar'];
990 991 992
    }

    /**
993 994
     * {@inheritDoc}
     *
995 996
     * @group DBAL-234
     */
997
    protected function getQuotedAlterTableRenameIndexSQL() : array
998
    {
Sergei Morozov's avatar
Sergei Morozov committed
999
        return [
1000 1001
            'ALTER INDEX "create" ON "table" RENAME TO "select"',
            'ALTER INDEX "foo" ON "table" RENAME TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
1002
        ];
1003
    }
1004 1005 1006 1007

    /**
     * {@inheritdoc}
     */
1008
    protected function getQuotedAlterTableRenameColumnSQL() : array
1009
    {
Sergei Morozov's avatar
Sergei Morozov committed
1010
        return [
1011 1012 1013 1014 1015 1016 1017 1018 1019
            'ALTER TABLE mytable RENAME unquoted1 TO unquoted',
            'ALTER TABLE mytable RENAME unquoted2 TO "where"',
            'ALTER TABLE mytable RENAME unquoted3 TO "foo"',
            'ALTER TABLE mytable RENAME "create" TO reserved_keyword',
            'ALTER TABLE mytable RENAME "table" TO "from"',
            'ALTER TABLE mytable RENAME "select" TO "bar"',
            'ALTER TABLE mytable RENAME quoted1 TO quoted',
            'ALTER TABLE mytable RENAME quoted2 TO "and"',
            'ALTER TABLE mytable RENAME quoted3 TO "baz"',
Sergei Morozov's avatar
Sergei Morozov committed
1020
        ];
1021
    }
1022

1023 1024 1025
    /**
     * {@inheritdoc}
     */
1026
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
1027 1028 1029 1030
    {
        $this->markTestIncomplete('Not implemented yet');
    }

1031
    /**
1032 1033
     * {@inheritDoc}
     *
1034 1035
     * @group DBAL-807
     */
1036
    protected function getAlterTableRenameIndexInSchemaSQL() : array
1037
    {
Sergei Morozov's avatar
Sergei Morozov committed
1038
        return ['ALTER INDEX idx_foo ON myschema.mytable RENAME TO idx_bar'];
1039 1040 1041
    }

    /**
1042 1043
     * {@inheritDoc}
     *
1044 1045
     * @group DBAL-807
     */
1046
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
1047
    {
Sergei Morozov's avatar
Sergei Morozov committed
1048
        return [
1049 1050
            'ALTER INDEX "create" ON "schema"."table" RENAME TO "select"',
            'ALTER INDEX "foo" ON "schema"."table" RENAME TO "bar"',
Sergei Morozov's avatar
Sergei Morozov committed
1051
        ];
1052
    }
1053 1054 1055 1056

    /**
     * @group DBAL-423
     */
1057
    public function testReturnsGuidTypeDeclarationSQL() : void
1058
    {
Sergei Morozov's avatar
Sergei Morozov committed
1059
        self::assertSame('UNIQUEIDENTIFIER', $this->platform->getGuidTypeDeclarationSQL([]));
1060
    }
1061 1062 1063 1064

    /**
     * {@inheritdoc}
     */
1065
    public function getAlterTableRenameColumnSQL() : array
1066
    {
Sergei Morozov's avatar
Sergei Morozov committed
1067
        return ['ALTER TABLE foo RENAME bar TO baz'];
1068
    }
1069 1070 1071 1072

    /**
     * {@inheritdoc}
     */
1073
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
1074
    {
Sergei Morozov's avatar
Sergei Morozov committed
1075
        return [
1076 1077 1078 1079 1080 1081 1082
            'ALTER TABLE "foo" DROP FOREIGN KEY fk1',
            'ALTER TABLE "foo" DROP FOREIGN KEY fk2',
            'ALTER TABLE "foo" RENAME id TO war',
            'ALTER TABLE "foo" ADD bloo INT NOT NULL, DROP baz, ALTER bar INT DEFAULT NULL',
            'ALTER TABLE "foo" RENAME "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
1083
        ];
1084
    }
1085 1086 1087 1088

    /**
     * {@inheritdoc}
     */
1089
    protected function getCommentOnColumnSQL() : array
1090
    {
Sergei Morozov's avatar
Sergei Morozov committed
1091
        return [
1092 1093 1094
            '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
1095
        ];
1096 1097 1098 1099 1100
    }

    /**
     * @group DBAL-1004
     */
1101
    public function testAltersTableColumnCommentWithExplicitlyQuotedIdentifiers() : void
1102
    {
Sergei Morozov's avatar
Sergei Morozov committed
1103 1104
        $table1 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'))]);
        $table2 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'), ['comment' => 'baz'])]);
1105 1106 1107 1108 1109

        $comparator = new Comparator();

        $tableDiff = $comparator->diffTable($table1, $table2);

Sergei Morozov's avatar
Sergei Morozov committed
1110
        self::assertInstanceOf(TableDiff::class, $tableDiff);
1111
        self::assertSame(
Sergei Morozov's avatar
Sergei Morozov committed
1112
            ['COMMENT ON COLUMN "foo"."bar" IS \'baz\''],
Sergei Morozov's avatar
Sergei Morozov committed
1113
            $this->platform->getAlterTableSQL($tableDiff)
1114 1115
        );
    }
1116 1117 1118 1119

    /**
     * {@inheritdoc}
     */
1120
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
1121
    {
Sergei Morozov's avatar
Sergei Morozov committed
1122 1123 1124 1125 1126 1127 1128 1129
        return [
            ['CASCADE', 'CASCADE'],
            ['SET NULL', 'SET NULL'],
            ['NO ACTION', 'RESTRICT'],
            ['RESTRICT', 'RESTRICT'],
            ['SET DEFAULT', 'SET DEFAULT'],
            ['CaScAdE', 'CASCADE'],
        ];
1130
    }
1131 1132 1133 1134

    /**
     * {@inheritdoc}
     */
1135
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
1136 1137 1138 1139 1140 1141 1142
    {
        return 'CONSTRAINT "select" UNIQUE (foo)';
    }

    /**
     * {@inheritdoc}
     */
1143
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
1144 1145 1146 1147
    {
        return ''; // not supported by this platform
    }

1148 1149 1150
    /**
     * {@inheritdoc}
     */
1151
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
1152 1153 1154 1155
    {
        return 'TRUNCATE TABLE "select"';
    }

1156 1157 1158
    /**
     * {@inheritdoc}
     */
1159
    protected function supportsInlineIndexDeclaration() : bool
1160 1161 1162
    {
        return false;
    }
1163 1164 1165 1166

    /**
     * {@inheritdoc}
     */
1167
    protected function getAlterStringToFixedStringSQL() : array
1168
    {
Sergei Morozov's avatar
Sergei Morozov committed
1169
        return ['ALTER TABLE mytable ALTER name CHAR(2) NOT NULL'];
1170
    }
1171 1172 1173 1174

    /**
     * {@inheritdoc}
     */
1175
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
1176
    {
Sergei Morozov's avatar
Sergei Morozov committed
1177
        return ['ALTER INDEX idx_foo ON mytable RENAME TO idx_foo_renamed'];
1178
    }
1179 1180 1181 1182

    /**
     * @group DBAL-2436
     */
1183
    public function testQuotesSchemaNameInListTableColumnsSQL() : void
1184
    {
1185
        self::assertStringContainsStringIgnoringCase(
1186
            "'Foo''Bar\\'",
1187
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1188 1189 1190 1191 1192 1193
        );
    }

    /**
     * @group DBAL-2436
     */
1194
    public function testQuotesTableNameInListTableConstraintsSQL() : void
1195
    {
1196
        self::assertStringContainsStringIgnoringCase("'Foo''Bar\\'", $this->platform->getListTableConstraintsSQL("Foo'Bar\\"), '', true);
1197 1198 1199 1200 1201
    }

    /**
     * @group DBAL-2436
     */
1202
    public function testQuotesSchemaNameInListTableConstraintsSQL() : void
1203
    {
1204
        self::assertStringContainsStringIgnoringCase(
1205
            "'Foo''Bar\\'",
1206
            $this->platform->getListTableConstraintsSQL("Foo'Bar\\.baz_table")
1207 1208 1209 1210 1211 1212
        );
    }

    /**
     * @group DBAL-2436
     */
1213
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
1214
    {
1215 1216 1217 1218
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
        );
1219 1220 1221 1222 1223
    }

    /**
     * @group DBAL-2436
     */
1224
    public function testQuotesSchemaNameInListTableForeignKeysSQL() : void
1225
    {
1226
        self::assertStringContainsStringIgnoringCase(
1227
            "'Foo''Bar\\'",
1228
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
1229 1230 1231 1232 1233 1234
        );
    }

    /**
     * @group DBAL-2436
     */
1235
    public function testQuotesTableNameInListTableIndexesSQL() : void
1236
    {
1237 1238 1239 1240
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
        );
1241 1242 1243 1244 1245
    }

    /**
     * @group DBAL-2436
     */
1246
    public function testQuotesSchemaNameInListTableIndexesSQL() : void
1247
    {
1248
        self::assertStringContainsStringIgnoringCase(
1249
            "'Foo''Bar\\'",
1250
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
1251 1252
        );
    }
1253
}