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\Schema\UniqueConstraint;
20
use Doctrine\DBAL\TransactionIsolationLevel;
21
use Doctrine\DBAL\Types\Type;
Sergei Morozov's avatar
Sergei Morozov committed
22
use InvalidArgumentException;
23 24 25
use function mt_rand;
use function strlen;
use function substr;
26

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

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

37 38 39 40
    /**
     * {@inheritDoc}
     */
    public function getGenerateAlterTableSql() : array
41
    {
Sergei Morozov's avatar
Sergei Morozov committed
42
        return [
43
            "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
44 45
            'ALTER TABLE mytable RENAME userlist',
        ];
46 47
    }

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

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

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

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

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

79 80 81 82
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInForeignKeySQL() : array
83
    {
Sergei Morozov's avatar
Sergei Morozov committed
84
        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"))'];
85 86
    }

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

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

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

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

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

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

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

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

166
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
167
            [
168 169
                '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
170 171
                'CREATE INDEX composite_idx ON test (id, name)',
            ],
Sergei Morozov's avatar
Sergei Morozov committed
172
            $this->platform->getCreateTableSQL($table)
173 174 175
        );
    }

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

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

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

209
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
210
            ['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
211
            $this->platform->getCreateTableSQL($table)
212 213 214
        );
    }

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

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

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

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

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

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

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

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

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

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

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

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

Sergei Morozov's avatar
Sergei Morozov committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
        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());
318 319
    }

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

325
    public function testGeneratesDDLSnippets() : void
326
    {
Sergei Morozov's avatar
Sergei Morozov committed
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
        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);
342 343

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

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

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

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

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

382
        self::assertEquals(
383
            'ALTER TABLE foo ADD PRIMARY KEY (a, b)',
Sergei Morozov's avatar
Sergei Morozov committed
384
            $this->platform->getCreatePrimaryKeySQL(
Sergei Morozov's avatar
Sergei Morozov committed
385
                new Index('any_pk_name', ['a', 'b'], true, true),
386 387 388 389 390
                new Table('foo')
            )
        );
    }

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

401
        self::assertEquals(
402 403
            'CONSTRAINT UNIQUE (a, b)',
            $this->platform->getUniqueConstraintDeclarationSQL(null, new UniqueConstraint(null, ['a', 'b']))
404 405 406
        );
    }

407
    public function testCannotGenerateUniqueConstraintDeclarationSQLWithEmptyColumns() : void
408
    {
Sergei Morozov's avatar
Sergei Morozov committed
409
        $this->expectException(InvalidArgumentException::class);
410

411
        $this->platform->getUniqueConstraintDeclarationSQL('constr', new UniqueConstraint('constr', []));
412 413
    }

414
    public function testGeneratesForeignKeyConstraintsWithAdvancedPlatformOptionsSQL() : void
415
    {
416
        self::assertEquals(
417 418 419 420
            '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
421
            $this->platform->getForeignKeyDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
422
                new ForeignKeyConstraint(['a', 'b'], 'foreign_table', ['c', 'd'], 'fk', [
423
                    'notnull' => true,
424
                    'match' => SQLAnywhere16Platform::FOREIGN_KEY_MATCH_SIMPLE_UNIQUE,
425 426 427 428
                    'onUpdate' => 'CASCADE',
                    'onDelete' => 'SET NULL',
                    'check_on_commit' => true,
                    'clustered' => true,
Sergei Morozov's avatar
Sergei Morozov committed
429 430
                    'for_olap_workload' => true,
                ])
431 432
            )
        );
433
        self::assertEquals(
434
            'FOREIGN KEY (a, b) REFERENCES foreign_table (c, d)',
Sergei Morozov's avatar
Sergei Morozov committed
435
            $this->platform->getForeignKeyDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
436
                new ForeignKeyConstraint(['a', 'b'], 'foreign_table', ['c', 'd'])
437 438 439 440
            )
        );
    }

441
    public function testGeneratesForeignKeyMatchClausesSQL() : void
442
    {
Sergei Morozov's avatar
Sergei Morozov committed
443 444 445 446
        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));
447 448
    }

449
    public function testCannotGenerateInvalidForeignKeyMatchClauseSQL() : void
450
    {
Sergei Morozov's avatar
Sergei Morozov committed
451
        $this->expectException(InvalidArgumentException::class);
452

453
        $this->platform->getForeignKeyMatchClauseSQL(3);
454 455
    }

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

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

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

474
    public function testCannotGenerateCommonIndexWithCreateConstraintSQL() : void
475
    {
Sergei Morozov's avatar
Sergei Morozov committed
476
        $this->expectException(InvalidArgumentException::class);
477

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

481
    public function testCannotGenerateCustomConstraintWithCreateConstraintSQL() : void
482
    {
Sergei Morozov's avatar
Sergei Morozov committed
483
        $this->expectException(InvalidArgumentException::class);
484

Sergei Morozov's avatar
Sergei Morozov committed
485
        $this->platform->getCreateConstraintSQL($this->createMock(Constraint::class), 'footable');
486 487
    }

488
    public function testGeneratesCreateIndexWithAdvancedPlatformOptionsSQL() : void
489
    {
490
        self::assertEquals(
491
            'CREATE UNIQUE INDEX fooindex ON footable (a, b) WITH NULLS DISTINCT',
Sergei Morozov's avatar
Sergei Morozov committed
492
            $this->platform->getCreateIndexSQL(
493 494
                new Index(
                    'fooindex',
Sergei Morozov's avatar
Sergei Morozov committed
495
                    ['a', 'b'],
496 497
                    true,
                    false,
498
                    ['with_nulls_distinct']
499 500 501 502
                ),
                'footable'
            )
        );
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 603 604 605

        // 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'
        );
606 607
    }

608
    public function testDoesNotSupportIndexDeclarationInCreateAlterTableStatements() : void
609
    {
Sergei Morozov's avatar
Sergei Morozov committed
610
        $this->expectException(DBALException::class);
611

Sergei Morozov's avatar
Sergei Morozov committed
612
        $this->platform->getIndexDeclarationSQL('index', new Index('index', []));
613 614
    }

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

Sergei Morozov's avatar
Sergei Morozov committed
619 620 621
        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(
622 623 624 625 626
            $index,
            new Table('footable')
        ));
    }

627
    public function testCannotGenerateDropIndexSQLWithInvalidIndexParameter() : void
628
    {
Sergei Morozov's avatar
Sergei Morozov committed
629
        $this->expectException(InvalidArgumentException::class);
630

Sergei Morozov's avatar
Sergei Morozov committed
631
        $this->platform->getDropIndexSQL(['index'], 'table');
632 633
    }

634
    public function testCannotGenerateDropIndexSQLWithInvalidTableParameter() : void
635
    {
Sergei Morozov's avatar
Sergei Morozov committed
636
        $this->expectException(InvalidArgumentException::class);
637

Sergei Morozov's avatar
Sergei Morozov committed
638
        $this->platform->getDropIndexSQL('index', ['table']);
639 640
    }

641
    public function testGeneratesSQLSnippets() : void
642
    {
Sergei Morozov's avatar
Sergei Morozov committed
643
        self::assertEquals('STRING(column1, "string1", column2, "string2")', $this->platform->getConcatExpression(
644 645 646 647 648
            'column1',
            '"string1"',
            'column2',
            '"string2"'
        ));
Sergei Morozov's avatar
Sergei Morozov committed
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 676 677 678
        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());
679
        self::assertEquals(
680
            'LTRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
681
            $this->platform->getTrimExpression('column', TrimMode::LEADING)
682
        );
683
        self::assertEquals(
684
            'RTRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
685
            $this->platform->getTrimExpression('column', TrimMode::TRAILING)
686
        );
687
        self::assertEquals(
688
            'TRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
689
            $this->platform->getTrimExpression('column')
690
        );
691
        self::assertEquals(
692
            'TRIM(column)',
Sergei Morozov's avatar
Sergei Morozov committed
693
            $this->platform->getTrimExpression('column', TrimMode::UNSPECIFIED)
694
        );
695
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
696
            "SUBSTR(column, PATINDEX('%[^' + c + ']%', column))",
Sergei Morozov's avatar
Sergei Morozov committed
697
            $this->platform->getTrimExpression('column', TrimMode::LEADING, 'c')
698
        );
699
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
700
            "REVERSE(SUBSTR(REVERSE(column), PATINDEX('%[^' + c + ']%', REVERSE(column))))",
Sergei Morozov's avatar
Sergei Morozov committed
701
            $this->platform->getTrimExpression('column', TrimMode::TRAILING, 'c')
702
        );
703
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
704 705
            "REVERSE(SUBSTR(REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))), PATINDEX('%[^' + c + ']%', " .
            "REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))))))",
Sergei Morozov's avatar
Sergei Morozov committed
706
            $this->platform->getTrimExpression('column', null, 'c')
707
        );
708
        self::assertEquals(
Steve Müller's avatar
Steve Müller committed
709 710
            "REVERSE(SUBSTR(REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))), PATINDEX('%[^' + c + ']%', " .
            "REVERSE(SUBSTR(column, PATINDEX('%[^' + c + ']%', column))))))",
Sergei Morozov's avatar
Sergei Morozov committed
711
            $this->platform->getTrimExpression('column', TrimMode::UNSPECIFIED, 'c')
712 713 714
        );
    }

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

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

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

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

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

767
    public function testCannotGenerateTransactionCommandWithInvalidIsolationLevel() : void
768
    {
Sergei Morozov's avatar
Sergei Morozov committed
769
        $this->expectException(InvalidArgumentException::class);
770

Sergei Morozov's avatar
Sergei Morozov committed
771
        $this->platform->getSetTransactionIsolationSQL('invalid_transaction_isolation_level');
772 773
    }

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

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

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

802
    public function testModifiesLimitQueryWithSubSelect() : void
803
    {
804
        self::assertEquals(
805
            '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
806
            $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname FROM user) AS doctrine_tbl', 10)
807 808 809
        );
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

893
    public function testDoesNotSupportSequences() : void
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 927 928 929
        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)
        );
930 931
    }

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

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

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

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

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

954
    protected function getBinaryDefaultLength() : int
Steve Müller's avatar
Steve Müller committed
955 956 957 958
    {
        return 1;
    }

959
    protected function getBinaryMaxLength() : int
Steve Müller's avatar
Steve Müller committed
960 961 962 963
    {
        return 32767;
    }

964
    public function testReturnsBinaryTypeDeclarationSQL() : void
Steve Müller's avatar
Steve Müller committed
965
    {
Sergei Morozov's avatar
Sergei Morozov committed
966 967 968
        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
969

Sergei Morozov's avatar
Sergei Morozov committed
970 971 972
        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]));
973 974 975 976
    }

    /**
     * @group legacy
977
     * @expectedDeprecation Binary field length 32768 is greater than supported by the platform (32767). Reduce the field length or use a BLOB field instead.
978
     */
979
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
980
    {
Sergei Morozov's avatar
Sergei Morozov committed
981 982
        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
983
    }
984 985

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

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

    /**
     * {@inheritdoc}
     */
1011
    protected function getQuotedAlterTableRenameColumnSQL() : array
1012
    {
Sergei Morozov's avatar
Sergei Morozov committed
1013
        return [
1014 1015 1016 1017 1018 1019 1020 1021 1022
            '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
1023
        ];
1024
    }
1025

1026 1027 1028
    /**
     * {@inheritdoc}
     */
1029
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
1030
    {
1031
        self::markTestIncomplete('Not implemented yet');
1032 1033
    }

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

    /**
1045 1046
     * {@inheritDoc}
     *
1047 1048
     * @group DBAL-807
     */
1049
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
1050
    {
Sergei Morozov's avatar
Sergei Morozov committed
1051
        return [
1052 1053
            '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
1054
        ];
1055
    }
1056 1057 1058 1059

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

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

    /**
     * {@inheritdoc}
     */
1076
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
1077
    {
Sergei Morozov's avatar
Sergei Morozov committed
1078
        return [
1079 1080 1081 1082 1083 1084 1085
            '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
1086
        ];
1087
    }
1088 1089 1090 1091

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

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

        $comparator = new Comparator();

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

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

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

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

    /**
     * {@inheritdoc}
     */
1146
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
1147 1148 1149 1150
    {
        return ''; // not supported by this platform
    }

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

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

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

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

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

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

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

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

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

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

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