SqlitePlatformTest.php 29.2 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Platforms;

jeroendedauw's avatar
jeroendedauw committed
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Platforms\AbstractPlatform;
jeroendedauw's avatar
jeroendedauw committed
7
use Doctrine\DBAL\Platforms\SqlitePlatform;
8
use Doctrine\DBAL\Schema\Column;
jeroendedauw's avatar
jeroendedauw committed
9
use Doctrine\DBAL\Schema\Table;
10
use Doctrine\DBAL\Schema\TableDiff;
11
use Doctrine\DBAL\TransactionIsolationLevel;
12
use Doctrine\DBAL\Types\Type;
13

14
class SqlitePlatformTest extends AbstractPlatformTestCase
15
{
16
    public function createPlatform() : AbstractPlatform
17
    {
Sergei Morozov's avatar
Sergei Morozov committed
18
        return new SqlitePlatform();
19 20
    }

21
    public function getGenerateTableSql() : string
22
    {
Timo Bakx's avatar
Timo Bakx committed
23
        return 'CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, test VARCHAR(255) DEFAULT NULL)';
24 25
    }

26 27 28 29
    /**
     * {@inheritDoc}
     */
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
30
    {
Sergei Morozov's avatar
Sergei Morozov committed
31
        return [
32
            'CREATE TABLE test (foo VARCHAR(255) DEFAULT NULL, bar VARCHAR(255) DEFAULT NULL)',
33
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar)',
Sergei Morozov's avatar
Sergei Morozov committed
34
        ];
35 36
    }

37
    public function testGeneratesSqlSnippets() : void
38
    {
Sergei Morozov's avatar
Sergei Morozov committed
39 40 41
        self::assertEquals('REGEXP', $this->platform->getRegexpExpression(), 'Regular expression operator is not correct');
        self::assertEquals('SUBSTR(column, 5, LENGTH(column))', $this->platform->getSubstringExpression('column', 5), 'Substring expression without length is not correct');
        self::assertEquals('SUBSTR(column, 0, 5)', $this->platform->getSubstringExpression('column', 0, 5), 'Substring expression with length is not correct');
42 43
    }

44
    public function testGeneratesTransactionCommands() : void
45
    {
46
        self::assertEquals(
47
            'PRAGMA read_uncommitted = 0',
Sergei Morozov's avatar
Sergei Morozov committed
48
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
49
        );
50
        self::assertEquals(
51
            'PRAGMA read_uncommitted = 1',
Sergei Morozov's avatar
Sergei Morozov committed
52
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
53
        );
54
        self::assertEquals(
55
            'PRAGMA read_uncommitted = 1',
Sergei Morozov's avatar
Sergei Morozov committed
56
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
57
        );
58
        self::assertEquals(
59
            'PRAGMA read_uncommitted = 1',
Sergei Morozov's avatar
Sergei Morozov committed
60
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
61
        );
62 63
    }

64
    public function testPrefersIdentityColumns() : void
65
    {
Sergei Morozov's avatar
Sergei Morozov committed
66
        self::assertTrue($this->platform->prefersIdentityColumns());
67 68
    }

69
    public function testIgnoresUnsignedIntegerDeclarationForAutoIncrementalIntegers() : void
70
    {
71
        self::assertSame(
Timo Bakx's avatar
Timo Bakx committed
72
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
73
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true, 'unsigned' => true])
74 75 76
        );
    }

77 78
    /**
     * @group DBAL-752
79
     * @group DBAL-924
80
     */
81
    public function testGeneratesTypeDeclarationForTinyIntegers() : void
82
    {
83
        self::assertEquals(
84
            'TINYINT',
Sergei Morozov's avatar
Sergei Morozov committed
85
            $this->platform->getTinyIntTypeDeclarationSQL([])
86
        );
87
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
88
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
89
            $this->platform->getTinyIntTypeDeclarationSQL(['autoincrement' => true])
90
        );
91
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
92
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
93
            $this->platform->getTinyIntTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
94 95
                ['autoincrement' => true, 'primary' => true]
            )
96
        );
97
        self::assertEquals(
98
            'TINYINT',
Sergei Morozov's avatar
Sergei Morozov committed
99
            $this->platform->getTinyIntTypeDeclarationSQL(['unsigned' => false])
100
        );
101
        self::assertEquals(
102
            'TINYINT UNSIGNED',
Sergei Morozov's avatar
Sergei Morozov committed
103
            $this->platform->getTinyIntTypeDeclarationSQL(['unsigned' => true])
104 105 106 107 108
        );
    }

    /**
     * @group DBAL-752
109
     * @group DBAL-924
110
     */
111
    public function testGeneratesTypeDeclarationForSmallIntegers() : void
112
    {
113
        self::assertEquals(
114
            'SMALLINT',
Sergei Morozov's avatar
Sergei Morozov committed
115
            $this->platform->getSmallIntTypeDeclarationSQL([])
116
        );
117
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
118
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
119
            $this->platform->getSmallIntTypeDeclarationSQL(['autoincrement' => true])
120
        );
121
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
122
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
123
            $this->platform->getTinyIntTypeDeclarationSQL(['autoincrement' => true, 'unsigned' => true])
124
        );
125
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
126
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
127
            $this->platform->getSmallIntTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
128 129
                ['autoincrement' => true, 'primary' => true]
            )
130
        );
131
        self::assertEquals(
132
            'SMALLINT',
Sergei Morozov's avatar
Sergei Morozov committed
133
            $this->platform->getSmallIntTypeDeclarationSQL(['unsigned' => false])
134
        );
135
        self::assertEquals(
136
            'SMALLINT UNSIGNED',
Sergei Morozov's avatar
Sergei Morozov committed
137
            $this->platform->getSmallIntTypeDeclarationSQL(['unsigned' => true])
138 139 140 141 142
        );
    }

    /**
     * @group DBAL-752
143
     * @group DBAL-924
144
     */
145
    public function testGeneratesTypeDeclarationForMediumIntegers() : void
146
    {
147
        self::assertEquals(
148
            'MEDIUMINT',
Sergei Morozov's avatar
Sergei Morozov committed
149
            $this->platform->getMediumIntTypeDeclarationSQL([])
150
        );
151
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
152
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
153
            $this->platform->getMediumIntTypeDeclarationSQL(['autoincrement' => true])
154
        );
155
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
156
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
157
            $this->platform->getMediumIntTypeDeclarationSQL(['autoincrement' => true, 'unsigned' => true])
158
        );
159
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
160
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
161
            $this->platform->getMediumIntTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
162 163
                ['autoincrement' => true, 'primary' => true]
            )
164
        );
165
        self::assertEquals(
166
            'MEDIUMINT',
Sergei Morozov's avatar
Sergei Morozov committed
167
            $this->platform->getMediumIntTypeDeclarationSQL(['unsigned' => false])
168
        );
169
        self::assertEquals(
170
            'MEDIUMINT UNSIGNED',
Sergei Morozov's avatar
Sergei Morozov committed
171
            $this->platform->getMediumIntTypeDeclarationSQL(['unsigned' => true])
172 173 174
        );
    }

175
    public function testGeneratesTypeDeclarationForIntegers() : void
176
    {
177
        self::assertEquals(
178
            'INTEGER',
Sergei Morozov's avatar
Sergei Morozov committed
179
            $this->platform->getIntegerTypeDeclarationSQL([])
180
        );
181
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
182
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
183
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
184
        );
185
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
186
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
187
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true, 'unsigned' => true])
188
        );
189
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
190
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
191
            $this->platform->getIntegerTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
192 193
                ['autoincrement' => true, 'primary' => true]
            )
194
        );
195
        self::assertEquals(
196
            'INTEGER',
Sergei Morozov's avatar
Sergei Morozov committed
197
            $this->platform->getIntegerTypeDeclarationSQL(['unsigned' => false])
198
        );
199
        self::assertEquals(
200
            'INTEGER UNSIGNED',
Sergei Morozov's avatar
Sergei Morozov committed
201
            $this->platform->getIntegerTypeDeclarationSQL(['unsigned' => true])
202 203 204 205 206
        );
    }

    /**
     * @group DBAL-752
207
     * @group DBAL-924
208
     */
209
    public function testGeneratesTypeDeclarationForBigIntegers() : void
210
    {
211
        self::assertEquals(
212
            'BIGINT',
Sergei Morozov's avatar
Sergei Morozov committed
213
            $this->platform->getBigIntTypeDeclarationSQL([])
214
        );
215
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
216
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
217
            $this->platform->getBigIntTypeDeclarationSQL(['autoincrement' => true])
218
        );
219
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
220
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
221
            $this->platform->getBigIntTypeDeclarationSQL(['autoincrement' => true, 'unsigned' => true])
222
        );
223
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
224
            'INTEGER PRIMARY KEY AUTOINCREMENT',
Sergei Morozov's avatar
Sergei Morozov committed
225
            $this->platform->getBigIntTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
226 227
                ['autoincrement' => true, 'primary' => true]
            )
228
        );
229
        self::assertEquals(
230
            'BIGINT',
Sergei Morozov's avatar
Sergei Morozov committed
231
            $this->platform->getBigIntTypeDeclarationSQL(['unsigned' => false])
232
        );
233
        self::assertEquals(
234
            'BIGINT UNSIGNED',
Sergei Morozov's avatar
Sergei Morozov committed
235
            $this->platform->getBigIntTypeDeclarationSQL(['unsigned' => true])
236
        );
237 238
    }

239
    public function testGeneratesTypeDeclarationForStrings() : void
240
    {
241
        self::assertEquals(
242
            'CHAR(10)',
Sergei Morozov's avatar
Sergei Morozov committed
243
            $this->platform->getVarcharTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
244 245
                ['length' => 10, 'fixed' => true]
            )
246
        );
247
        self::assertEquals(
248
            'VARCHAR(50)',
Sergei Morozov's avatar
Sergei Morozov committed
249
            $this->platform->getVarcharTypeDeclarationSQL(['length' => 50]),
250
            'Variable string declaration is not correct'
251
        );
252
        self::assertEquals(
253
            'VARCHAR(255)',
Sergei Morozov's avatar
Sergei Morozov committed
254
            $this->platform->getVarcharTypeDeclarationSQL([]),
255
            'Long string declaration is not correct'
256
        );
257
    }
258

259
    public function getGenerateIndexSql() : string
260 261 262 263
    {
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
    }

264
    public function getGenerateUniqueIndexSql() : string
265
    {
266
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2)';
267 268
    }

269
    public function testGeneratesForeignKeyCreationSql() : void
270
    {
271 272
        $this->expectException(DBALException::class);

273 274 275
        parent::testGeneratesForeignKeyCreationSql();
    }

276
    public function testGeneratesConstraintCreationSql() : void
277
    {
278 279
        $this->expectException(DBALException::class);

280 281 282
        parent::testGeneratesConstraintCreationSql();
    }

283
    public function getGenerateForeignKeySql() : string
284
    {
285
        return null;
286
    }
287

288
    public function testModifyLimitQuery() : void
289
    {
Sergei Morozov's avatar
Sergei Morozov committed
290
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
291
        self::assertEquals('SELECT * FROM user LIMIT 10', $sql);
292 293
    }

294
    public function testModifyLimitQueryWithEmptyOffset() : void
295
    {
Sergei Morozov's avatar
Sergei Morozov committed
296
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10);
297
        self::assertEquals('SELECT * FROM user LIMIT 10', $sql);
298
    }
299

300
    public function testModifyLimitQueryWithOffsetAndEmptyLimit() : void
301
    {
Sergei Morozov's avatar
Sergei Morozov committed
302
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', null, 10);
303
        self::assertEquals('SELECT * FROM user LIMIT -1 OFFSET 10', $sql);
304 305
    }

306 307 308 309
    /**
     * {@inheritDoc}
     */
    public function getGenerateAlterTableSql() : array
310
    {
Sergei Morozov's avatar
Sergei Morozov committed
311 312 313
        return [
            'CREATE TEMPORARY TABLE __temp__mytable AS SELECT id, bar, bloo FROM mytable',
            'DROP TABLE mytable',
Timo Bakx's avatar
Timo Bakx committed
314
            "CREATE TABLE mytable (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, baz VARCHAR(255) DEFAULT 'def' NOT NULL, bloo BOOLEAN DEFAULT '0' NOT NULL, quota INTEGER DEFAULT NULL)",
Sergei Morozov's avatar
Sergei Morozov committed
315 316 317 318
            'INSERT INTO mytable (id, baz, bloo) SELECT id, bar, bloo FROM __temp__mytable',
            'DROP TABLE __temp__mytable',
            'ALTER TABLE mytable RENAME TO userlist',
        ];
319
    }
Fabio B. Silva's avatar
Fabio B. Silva committed
320 321 322 323

    /**
     * @group DDC-1845
     */
324
    public function testGenerateTableSqlShouldNotAutoQuotePrimaryKey() : void
Fabio B. Silva's avatar
Fabio B. Silva committed
325
    {
Sergei Morozov's avatar
Sergei Morozov committed
326 327 328
        $table = new Table('test');
        $table->addColumn('"like"', 'integer', ['notnull' => true, 'autoincrement' => true]);
        $table->setPrimaryKey(['"like"']);
Fabio B. Silva's avatar
Fabio B. Silva committed
329

Sergei Morozov's avatar
Sergei Morozov committed
330
        $createTableSQL = $this->platform->getCreateTableSQL($table);
331
        self::assertEquals(
Timo Bakx's avatar
Timo Bakx committed
332
            'CREATE TABLE test ("like" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)',
Fabio B. Silva's avatar
Fabio B. Silva committed
333 334 335
            $createTableSQL[0]
        );
    }
336

337
    public function testAlterTableAddColumns() : void
338
    {
Sergei Morozov's avatar
Sergei Morozov committed
339 340 341
        $diff                        = new TableDiff('user');
        $diff->addedColumns['foo']   = new Column('foo', Type::getType('string'));
        $diff->addedColumns['count'] = new Column('count', Type::getType('integer'), ['notnull' => false, 'default' => 1]);
342

Sergei Morozov's avatar
Sergei Morozov committed
343
        $expected = [
344 345
            'ALTER TABLE user ADD COLUMN foo VARCHAR(255) NOT NULL',
            'ALTER TABLE user ADD COLUMN count INTEGER DEFAULT 1',
Sergei Morozov's avatar
Sergei Morozov committed
346
        ];
347

Sergei Morozov's avatar
Sergei Morozov committed
348
        self::assertEquals($expected, $this->platform->getAlterTableSQL($diff));
349 350
    }

Luís Cobucci's avatar
Luís Cobucci committed
351 352 353 354
    /**
     * @dataProvider complexDiffProvider
     */
    public function testAlterTableAddComplexColumns(TableDiff $diff) : void
355
    {
Luís Cobucci's avatar
Luís Cobucci committed
356 357
        $this->expectException(DBALException::class);

Sergei Morozov's avatar
Sergei Morozov committed
358
        $this->platform->getAlterTableSQL($diff);
Luís Cobucci's avatar
Luís Cobucci committed
359 360
    }

361 362 363 364
    /**
     * @return mixed[][]
     */
    public static function complexDiffProvider() : iterable
Luís Cobucci's avatar
Luís Cobucci committed
365
    {
Sergei Morozov's avatar
Sergei Morozov committed
366 367
        $date                       = new TableDiff('user');
        $date->addedColumns['time'] = new Column('time', Type::getType('date'), ['default' => 'CURRENT_DATE']);
368

Sergei Morozov's avatar
Sergei Morozov committed
369 370
        $id                     = new TableDiff('user');
        $id->addedColumns['id'] = new Column('id', Type::getType('integer'), ['autoincrement' => true]);
371

Luís Cobucci's avatar
Luís Cobucci committed
372 373 374 375
        return [
            'date column with default value' => [$date],
            'id column with auto increment'  => [$id],
        ];
376 377
    }

378
    public function testCreateTableWithDeferredForeignKeys() : void
379 380 381 382 383 384
    {
        $table = new Table('user');
        $table->addColumn('id', 'integer');
        $table->addColumn('article', 'integer');
        $table->addColumn('post', 'integer');
        $table->addColumn('parent', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
385 386 387 388
        $table->setPrimaryKey(['id']);
        $table->addForeignKeyConstraint('article', ['article'], ['id'], ['deferrable' => true]);
        $table->addForeignKeyConstraint('post', ['post'], ['id'], ['deferred' => true]);
        $table->addForeignKeyConstraint('user', ['parent'], ['id'], ['deferrable' => true, 'deferred' => true]);
389

Sergei Morozov's avatar
Sergei Morozov committed
390
        $sql = [
391
            'CREATE TABLE user ('
392 393
                . 'id INTEGER NOT NULL, article INTEGER NOT NULL, post INTEGER NOT NULL, parent INTEGER NOT NULL'
                . ', PRIMARY KEY(id)'
394 395 396 397 398 399 400
                . ', CONSTRAINT FK_8D93D64923A0E66 FOREIGN KEY (article) REFERENCES article (id) DEFERRABLE INITIALLY IMMEDIATE'
                . ', CONSTRAINT FK_8D93D6495A8A6C8D FOREIGN KEY (post) REFERENCES post (id) NOT DEFERRABLE INITIALLY DEFERRED'
                . ', CONSTRAINT FK_8D93D6493D8E604F FOREIGN KEY (parent) REFERENCES user (id) DEFERRABLE INITIALLY DEFERRED'
                . ')',
            'CREATE INDEX IDX_8D93D64923A0E66 ON user (article)',
            'CREATE INDEX IDX_8D93D6495A8A6C8D ON user (post)',
            'CREATE INDEX IDX_8D93D6493D8E604F ON user (parent)',
Sergei Morozov's avatar
Sergei Morozov committed
401
        ];
402

Sergei Morozov's avatar
Sergei Morozov committed
403
        self::assertEquals($sql, $this->platform->getCreateTableSQL($table));
404 405
    }

406
    public function testAlterTable() : void
407 408 409 410 411 412
    {
        $table = new Table('user');
        $table->addColumn('id', 'integer');
        $table->addColumn('article', 'integer');
        $table->addColumn('post', 'integer');
        $table->addColumn('parent', 'integer');
Sergei Morozov's avatar
Sergei Morozov committed
413 414 415 416 417 418 419 420 421 422 423 424
        $table->setPrimaryKey(['id']);
        $table->addForeignKeyConstraint('article', ['article'], ['id'], ['deferrable' => true]);
        $table->addForeignKeyConstraint('post', ['post'], ['id'], ['deferred' => true]);
        $table->addForeignKeyConstraint('user', ['parent'], ['id'], ['deferrable' => true, 'deferred' => true]);
        $table->addIndex(['article', 'post'], 'index1');

        $diff                           = new TableDiff('user');
        $diff->fromTable                = $table;
        $diff->newName                  = 'client';
        $diff->renamedColumns['id']     = new Column('key', Type::getType('integer'), []);
        $diff->renamedColumns['post']   = new Column('comment', Type::getType('integer'), []);
        $diff->removedColumns['parent'] = new Column('comment', Type::getType('integer'), []);
425 426
        $diff->removedIndexes['index1'] = $table->getIndex('index1');

Sergei Morozov's avatar
Sergei Morozov committed
427
        $sql = [
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
            'DROP INDEX IDX_8D93D64923A0E66',
            'DROP INDEX IDX_8D93D6495A8A6C8D',
            'DROP INDEX IDX_8D93D6493D8E604F',
            'DROP INDEX index1',
            'CREATE TEMPORARY TABLE __temp__user AS SELECT id, article, post FROM user',
            'DROP TABLE user',
            'CREATE TABLE user ('
                . '"key" INTEGER NOT NULL, article INTEGER NOT NULL, comment INTEGER NOT NULL'
                . ', PRIMARY KEY("key")'
                . ', CONSTRAINT FK_8D93D64923A0E66 FOREIGN KEY (article) REFERENCES article (id) DEFERRABLE INITIALLY IMMEDIATE'
                . ', CONSTRAINT FK_8D93D6495A8A6C8D FOREIGN KEY (comment) REFERENCES post (id) NOT DEFERRABLE INITIALLY DEFERRED'
                . ')',
            'INSERT INTO user ("key", article, comment) SELECT id, article, post FROM __temp__user',
            'DROP TABLE __temp__user',
            'ALTER TABLE user RENAME TO client',
            'CREATE INDEX IDX_8D93D64923A0E66 ON client (article)',
            'CREATE INDEX IDX_8D93D6495A8A6C8D ON client (comment)',
Sergei Morozov's avatar
Sergei Morozov committed
445
        ];
446

Sergei Morozov's avatar
Sergei Morozov committed
447
        self::assertEquals($sql, $this->platform->getAlterTableSQL($diff));
448 449
    }

450 451 452 453
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInPrimaryKeySQL() : array
454
    {
Sergei Morozov's avatar
Sergei Morozov committed
455
        return ['CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL, PRIMARY KEY("create"))'];
456 457
    }

458 459 460 461
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInIndexSQL() : array
462
    {
Sergei Morozov's avatar
Sergei Morozov committed
463
        return [
464 465
            'CREATE TABLE "quoted" ("create" VARCHAR(255) NOT NULL)',
            'CREATE INDEX IDX_22660D028FD6E0FB ON "quoted" ("create")',
Sergei Morozov's avatar
Sergei Morozov committed
466
        ];
467 468
    }

469 470 471 472
    /**
     * {@inheritDoc}
     */
    protected function getQuotedNameInIndexSQL() : array
Markus Fasselt's avatar
Markus Fasselt committed
473
    {
Sergei Morozov's avatar
Sergei Morozov committed
474
        return [
Markus Fasselt's avatar
Markus Fasselt committed
475 476
            'CREATE TABLE test (column1 VARCHAR(255) NOT NULL)',
            'CREATE INDEX "key" ON test (column1)',
Sergei Morozov's avatar
Sergei Morozov committed
477
        ];
Markus Fasselt's avatar
Markus Fasselt committed
478 479
    }

480 481 482 483
    /**
     * {@inheritDoc}
     */
    protected function getQuotedColumnInForeignKeySQL() : array
484
    {
Sergei Morozov's avatar
Sergei Morozov committed
485
        return [
486 487 488 489 490
            '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") NOT DEFERRABLE INITIALLY IMMEDIATE, ' .
            'CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES foo ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE, ' .
            'CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ("create", foo, "bar") REFERENCES "foo-bar" ("create", bar, "foo-bar") NOT DEFERRABLE INITIALLY IMMEDIATE)',
Sergei Morozov's avatar
Sergei Morozov committed
491
        ];
492
    }
Steve Müller's avatar
Steve Müller committed
493

494
    protected function getBinaryDefaultLength() : int
Steve Müller's avatar
Steve Müller committed
495 496 497 498
    {
        return 0;
    }

499
    protected function getBinaryMaxLength() : int
Steve Müller's avatar
Steve Müller committed
500 501 502 503
    {
        return 0;
    }

504
    public function testReturnsBinaryTypeDeclarationSQL() : void
Steve Müller's avatar
Steve Müller committed
505
    {
Sergei Morozov's avatar
Sergei Morozov committed
506 507 508
        self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL([]));
        self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0]));
        self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['length' => 9999999]));
Steve Müller's avatar
Steve Müller committed
509

Sergei Morozov's avatar
Sergei Morozov committed
510 511 512
        self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true]));
        self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0]));
        self::assertSame('BLOB', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 9999999]));
Steve Müller's avatar
Steve Müller committed
513
    }
514 515

    /**
516 517
     * {@inheritDoc}
     *
518 519
     * @group DBAL-234
     */
520
    protected function getAlterTableRenameIndexSQL() : array
521
    {
Sergei Morozov's avatar
Sergei Morozov committed
522
        return [
523 524 525 526 527 528
            'CREATE TEMPORARY TABLE __temp__mytable AS SELECT id FROM mytable',
            'DROP TABLE mytable',
            'CREATE TABLE mytable (id INTEGER NOT NULL, PRIMARY KEY(id))',
            'INSERT INTO mytable (id) SELECT id FROM __temp__mytable',
            'DROP TABLE __temp__mytable',
            'CREATE INDEX idx_bar ON mytable (id)',
Sergei Morozov's avatar
Sergei Morozov committed
529
        ];
530 531 532
    }

    /**
533 534
     * {@inheritDoc}
     *
535 536
     * @group DBAL-234
     */
537
    protected function getQuotedAlterTableRenameIndexSQL() : array
538
    {
Sergei Morozov's avatar
Sergei Morozov committed
539
        return [
540 541 542 543 544
            'CREATE TEMPORARY TABLE __temp__table AS SELECT id FROM "table"',
            'DROP TABLE "table"',
            'CREATE TABLE "table" (id INTEGER NOT NULL, PRIMARY KEY(id))',
            'INSERT INTO "table" (id) SELECT id FROM __temp__table',
            'DROP TABLE __temp__table',
545 546
            'CREATE INDEX "select" ON "table" (id)',
            'CREATE INDEX "bar" ON "table" (id)',
Sergei Morozov's avatar
Sergei Morozov committed
547
        ];
548
    }
549 550 551 552

    /**
     * {@inheritdoc}
     */
553
    protected function getQuotedAlterTableRenameColumnSQL() : array
554
    {
Sergei Morozov's avatar
Sergei Morozov committed
555
        return [
556 557
            'CREATE TEMPORARY TABLE __temp__mytable AS SELECT unquoted1, unquoted2, unquoted3, "create", "table", "select", "quoted1", "quoted2", "quoted3" FROM mytable',
            'DROP TABLE mytable',
558 559 560 561 562 563 564 565 566 567
            'CREATE TABLE mytable (unquoted INTEGER NOT NULL --Unquoted 1
, "where" INTEGER NOT NULL --Unquoted 2
, "foo" INTEGER NOT NULL --Unquoted 3
, reserved_keyword INTEGER NOT NULL --Reserved keyword 1
, "from" INTEGER NOT NULL --Reserved keyword 2
, "bar" INTEGER NOT NULL --Reserved keyword 3
, quoted INTEGER NOT NULL --Quoted 1
, "and" INTEGER NOT NULL --Quoted 2
, "baz" INTEGER NOT NULL --Quoted 3
)',
568 569
            'INSERT INTO mytable (unquoted, "where", "foo", reserved_keyword, "from", "bar", quoted, "and", "baz") SELECT unquoted1, unquoted2, unquoted3, "create", "table", "select", "quoted1", "quoted2", "quoted3" FROM __temp__mytable',
            'DROP TABLE __temp__mytable',
Sergei Morozov's avatar
Sergei Morozov committed
570
        ];
571
    }
572 573 574 575

    /**
     * {@inheritdoc}
     */
576
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
577
    {
Sergei Morozov's avatar
Sergei Morozov committed
578
        return [
579 580
            'CREATE TEMPORARY TABLE __temp__mytable AS SELECT unquoted1, unquoted2, unquoted3, "create", "table", "select" FROM mytable',
            'DROP TABLE mytable',
581 582 583 584 585 586 587
            'CREATE TABLE mytable (unquoted1 VARCHAR(255) NOT NULL --Unquoted 1
, unquoted2 VARCHAR(255) NOT NULL --Unquoted 2
, unquoted3 VARCHAR(255) NOT NULL --Unquoted 3
, "create" VARCHAR(255) NOT NULL --Reserved keyword 1
, "table" VARCHAR(255) NOT NULL --Reserved keyword 2
, "select" VARCHAR(255) NOT NULL --Reserved keyword 3
)',
588 589
            'INSERT INTO mytable (unquoted1, unquoted2, unquoted3, "create", "table", "select") SELECT unquoted1, unquoted2, unquoted3, "create", "table", "select" FROM __temp__mytable',
            'DROP TABLE __temp__mytable',
Sergei Morozov's avatar
Sergei Morozov committed
590
        ];
591
    }
592 593 594 595

    /**
     * @group DBAL-807
     */
596
    public function testAlterTableRenameIndexInSchema() : void
597 598 599 600 601 602 603 604 605 606
    {
        $this->markTestIncomplete(
            'Test currently produces broken SQL due to SQLLitePlatform::getAlterTable being broken ' .
            'when used with schemas.'
        );
    }

    /**
     * @group DBAL-807
     */
607
    public function testQuotesAlterTableRenameIndexInSchema() : void
608 609 610 611 612 613
    {
        $this->markTestIncomplete(
            'Test currently produces broken SQL due to SQLLitePlatform::getAlterTable being broken ' .
            'when used with schemas.'
        );
    }
614 615 616 617

    /**
     * @group DBAL-423
     */
618
    public function testReturnsGuidTypeDeclarationSQL() : void
619
    {
Sergei Morozov's avatar
Sergei Morozov committed
620
        self::assertSame('CHAR(36)', $this->platform->getGuidTypeDeclarationSQL([]));
621
    }
622 623 624 625

    /**
     * {@inheritdoc}
     */
626
    public function getAlterTableRenameColumnSQL() : array
627
    {
Sergei Morozov's avatar
Sergei Morozov committed
628
        return [
629 630
            'CREATE TEMPORARY TABLE __temp__foo AS SELECT bar FROM foo',
            'DROP TABLE foo',
631 632
            'CREATE TABLE foo (baz INTEGER DEFAULT 666 NOT NULL --rename test
)',
633 634
            'INSERT INTO foo (baz) SELECT bar FROM __temp__foo',
            'DROP TABLE __temp__foo',
Sergei Morozov's avatar
Sergei Morozov committed
635
        ];
636
    }
637 638 639 640

    /**
     * {@inheritdoc}
     */
641
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
642
    {
Sergei Morozov's avatar
Sergei Morozov committed
643
        return [
644 645 646 647 648 649 650 651 652 653 654 655 656
            'DROP INDEX IDX_8C736521A81E660E',
            'DROP INDEX IDX_8C736521FDC58D6C',
            'CREATE TEMPORARY TABLE __temp__foo AS SELECT fk, fk2, id, fk3, bar FROM "foo"',
            'DROP TABLE "foo"',
            'CREATE TABLE "foo" (fk2 INTEGER NOT NULL, fk3 INTEGER NOT NULL, fk INTEGER NOT NULL, war INTEGER NOT NULL, ' .
            'bar INTEGER DEFAULT NULL, bloo INTEGER NOT NULL, ' .
            'CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id) NOT DEFERRABLE INITIALLY IMMEDIATE, ' .
            'CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE)',
            'INSERT INTO "foo" (fk, fk2, war, fk3, bar) SELECT fk, fk2, id, fk3, bar FROM __temp__foo',
            'DROP TABLE __temp__foo',
            'ALTER TABLE "foo" RENAME TO "table"',
            'CREATE INDEX IDX_8C736521A81E660E ON "table" (fk)',
            'CREATE INDEX IDX_8C736521FDC58D6C ON "table" (fk2)',
Sergei Morozov's avatar
Sergei Morozov committed
657
        ];
658
    }
659 660 661 662

    /**
     * {@inheritdoc}
     */
663
    protected function getCommentOnColumnSQL() : array
664
    {
Sergei Morozov's avatar
Sergei Morozov committed
665
        return [
666 667 668
            '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
669
        ];
670
    }
671

672
    protected static function getInlineColumnCommentDelimiter() : string
673 674 675 676
    {
        return "\n";
    }

677
    protected static function getInlineColumnRegularCommentSQL() : string
678 679 680 681
    {
        return "--Regular comment\n";
    }

682
    protected static function getInlineColumnCommentRequiringEscapingSQL() : string
683 684 685 686
    {
        return "--Using inline comment delimiter \n-- works\n";
    }

687
    protected static function getInlineColumnEmptyCommentSQL() : string
688 689 690 691
    {
        return "--\n";
    }

692 693 694
    /**
     * {@inheritdoc}
     */
695
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
696 697 698 699 700 701 702
    {
        return 'CONSTRAINT "select" UNIQUE (foo)';
    }

    /**
     * {@inheritdoc}
     */
703
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
704 705 706
    {
        return 'INDEX "select" (foo)';
    }
707

708 709 710
    /**
     * {@inheritdoc}
     */
711
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
712 713 714 715
    {
        return 'DELETE FROM "select"';
    }

716 717 718
    /**
     * {@inheritdoc}
     */
719
    protected function getAlterStringToFixedStringSQL() : array
720
    {
Sergei Morozov's avatar
Sergei Morozov committed
721
        return [
722 723 724 725 726
            'CREATE TEMPORARY TABLE __temp__mytable AS SELECT name FROM mytable',
            'DROP TABLE mytable',
            'CREATE TABLE mytable (name CHAR(2) NOT NULL)',
            'INSERT INTO mytable (name) SELECT name FROM __temp__mytable',
            'DROP TABLE __temp__mytable',
Sergei Morozov's avatar
Sergei Morozov committed
727
        ];
728
    }
729 730 731 732

    /**
     * {@inheritdoc}
     */
733
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
734
    {
Sergei Morozov's avatar
Sergei Morozov committed
735
        return [
736 737 738 739 740 741 742 743 744
            'DROP INDEX idx_foo',
            'DROP INDEX idx_bar',
            'CREATE TEMPORARY TABLE __temp__mytable AS SELECT foo, bar, baz FROM mytable',
            'DROP TABLE mytable',
            'CREATE TABLE mytable (foo INTEGER NOT NULL, bar INTEGER NOT NULL, baz INTEGER NOT NULL, CONSTRAINT fk_foo FOREIGN KEY (foo) REFERENCES foreign_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT fk_bar FOREIGN KEY (bar) REFERENCES foreign_table (id) NOT DEFERRABLE INITIALLY IMMEDIATE)',
            'INSERT INTO mytable (foo, bar, baz) SELECT foo, bar, baz FROM __temp__mytable',
            'DROP TABLE __temp__mytable',
            'CREATE INDEX idx_bar ON mytable (bar)',
            'CREATE INDEX idx_foo_renamed ON mytable (foo)',
Sergei Morozov's avatar
Sergei Morozov committed
745
        ];
746
    }
747 748 749 750

    /**
     * @group DBAL-2436
     */
751
    public function testQuotesTableNameInListTableConstraintsSQL() : void
752
    {
753 754 755 756
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableConstraintsSQL("Foo'Bar\\")
        );
757 758 759 760 761
    }

    /**
     * @group DBAL-2436
     */
762
    public function testQuotesTableNameInListTableColumnsSQL() : void
763
    {
764 765 766 767
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
        );
768 769 770 771 772
    }

    /**
     * @group DBAL-2436
     */
773
    public function testQuotesTableNameInListTableIndexesSQL() : void
774
    {
775 776 777 778
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
        );
779 780 781 782 783
    }

    /**
     * @group DBAL-2436
     */
784
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
785
    {
786 787 788 789
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
        );
790
    }
791

792
    public function testDateAddStaticNumberOfDays() : void
793
    {
Sergei Morozov's avatar
Sergei Morozov committed
794
        self::assertSame("DATE(rentalBeginsOn,'+12 DAY')", $this->platform->getDateAddDaysExpression('rentalBeginsOn', 12));
795 796
    }

797
    public function testDateAddNumberOfDaysFromColumn() : void
798
    {
Sergei Morozov's avatar
Sergei Morozov committed
799
        self::assertSame("DATE(rentalBeginsOn,'+' || duration || ' DAY')", $this->platform->getDateAddDaysExpression('rentalBeginsOn', 'duration'));
800
    }
801 802 803

    public function testSupportsColumnCollation() : void
    {
Sergei Morozov's avatar
Sergei Morozov committed
804
        self::assertTrue($this->platform->supportsColumnCollation());
805 806 807 808 809 810
    }

    public function testColumnCollationDeclarationSQL() : void
    {
        self::assertSame(
            'COLLATE NOCASE',
Sergei Morozov's avatar
Sergei Morozov committed
811
            $this->platform->getColumnCollationDeclarationSQL('NOCASE')
812 813 814 815 816 817 818 819 820 821 822
        );
    }

    public function testGetCreateTableSQLWithColumnCollation() : void
    {
        $table = new Table('foo');
        $table->addColumn('no_collation', 'string');
        $table->addColumn('column_collation', 'string')->setPlatformOption('collation', 'NOCASE');

        self::assertSame(
            ['CREATE TABLE foo (no_collation VARCHAR(255) NOT NULL, column_collation VARCHAR(255) NOT NULL COLLATE NOCASE)'],
Sergei Morozov's avatar
Sergei Morozov committed
823
            $this->platform->getCreateTableSQL($table),
824 825 826
            'Column "no_collation" will use the default collation (BINARY) and "column_collation" overwrites the collation on this column'
        );
    }
827
}