Completed
Pull Request — master (#3769)
by Benjamin
65:11 queued 11s
created

testUsesSequenceEmulatedIdentityColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\DBAL\Platforms;
6
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Exception\ColumnLengthRequired;
9
use Doctrine\DBAL\Platforms\AbstractPlatform;
10
use Doctrine\DBAL\Platforms\OraclePlatform;
11
use Doctrine\DBAL\Schema\Column;
12
use Doctrine\DBAL\Schema\ColumnDiff;
13
use Doctrine\DBAL\Schema\Comparator;
14
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
15
use Doctrine\DBAL\Schema\Sequence;
16
use Doctrine\DBAL\Schema\Table;
17
use Doctrine\DBAL\Schema\TableDiff;
18
use Doctrine\DBAL\TransactionIsolationLevel;
19
use Doctrine\DBAL\Types\Type;
20
use function array_walk;
21
use function assert;
22
use function preg_replace;
23
use function sprintf;
24
use function strtoupper;
25
use function uniqid;
26
27
class OraclePlatformTest extends AbstractPlatformTestCase
28
{
29
    /**
30
     * @return mixed[][]
31
     */
32
    public static function dataValidIdentifiers() : iterable
33
    {
34
        return [
35
            ['a'],
36
            ['foo'],
37
            ['Foo'],
38
            ['Foo123'],
39
            ['Foo#bar_baz$'],
40
            ['"a"'],
41
            ['"1"'],
42
            ['"foo_bar"'],
43
            ['"@$%&!"'],
44
        ];
45
    }
46
47
    /**
48
     * @dataProvider dataValidIdentifiers
49
     */
50
    public function testValidIdentifiers(string $identifier) : void
51
    {
52
        OraclePlatform::assertValidIdentifier($identifier);
53
54
        $this->addToAssertionCount(1);
55
    }
56
57
    /**
58
     * @return mixed[][]
59
     */
60
    public static function dataInvalidIdentifiers() : iterable
61
    {
62
        return [
63
            ['1'],
64
            ['abc&'],
65
            ['abc-def'],
66
            ['"'],
67
            ['"foo"bar"'],
68
        ];
69
    }
70
71
    /**
72
     * @dataProvider dataInvalidIdentifiers
73
     */
74
    public function testInvalidIdentifiers(string $identifier) : void
75
    {
76
        $this->expectException(DBALException::class);
77
78
        OraclePlatform::assertValidIdentifier($identifier);
79
    }
80
81
    public function createPlatform() : AbstractPlatform
82
    {
83
        return new OraclePlatform();
84
    }
85
86
    public function getGenerateTableSql() : string
87
    {
88
        return 'CREATE TABLE test (id NUMBER(10) NOT NULL, test VARCHAR2(255) DEFAULT NULL NULL, PRIMARY KEY(id))';
89
    }
90
91
    /**
92
     * @return mixed[]
93
     */
94
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
95
    {
96
        return [
97
            'CREATE TABLE test (foo VARCHAR2(255) DEFAULT NULL NULL, bar VARCHAR2(255) DEFAULT NULL NULL)',
98
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar)',
99
        ];
100
    }
101
102
    /**
103
     * {@inheritDoc}
104
     */
105
    public function getGenerateAlterTableSql() : array
106
    {
107
        return [
108
            'ALTER TABLE mytable ADD (quota NUMBER(10) DEFAULT NULL NULL)',
109
            "ALTER TABLE mytable MODIFY (baz VARCHAR2(255) DEFAULT 'def' NOT NULL, bloo NUMBER(1) DEFAULT '0' NOT NULL)",
110
            'ALTER TABLE mytable DROP (foo)',
111
            'ALTER TABLE mytable RENAME TO userlist',
112
        ];
113
    }
114
115
    public function testRLike() : void
116
    {
117
        $this->expectException(DBALException::class);
118
119
        self::assertEquals('RLIKE', $this->platform->getRegexpExpression(), 'Regular expression operator is not correct');
120
    }
121
122
    public function testGeneratesSqlSnippets() : void
123
    {
124
        self::assertEquals('"', $this->platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
125
        self::assertEquals('column1 || column2 || column3', $this->platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
126
    }
127
128
    public function testGeneratesTransactionsCommands() : void
129
    {
130
        self::assertEquals(
131
            'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
132
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
133
        );
134
        self::assertEquals(
135
            'SET TRANSACTION ISOLATION LEVEL READ COMMITTED',
136
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
137
        );
138
        self::assertEquals(
139
            'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE',
140
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
141
        );
142
        self::assertEquals(
143
            'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE',
144
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
145
        );
146
    }
147
148
    public function testCreateDatabaseThrowsException() : void
149
    {
150
        $this->expectException(DBALException::class);
151
152
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
153
    }
154
155
    public function testDropDatabaseThrowsException() : void
156
    {
157
        self::assertEquals('DROP USER foobar CASCADE', $this->platform->getDropDatabaseSQL('foobar'));
158
    }
159
160
    public function testDropTable() : void
161
    {
162
        self::assertEquals('DROP TABLE foobar', $this->platform->getDropTableSQL('foobar'));
163
    }
164
165
    public function testGeneratesTypeDeclarationForIntegers() : void
166
    {
167
        self::assertEquals(
168
            'NUMBER(10)',
169
            $this->platform->getIntegerTypeDeclarationSQL([])
170
        );
171
        self::assertEquals(
172
            'NUMBER(10)',
173
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
174
        );
175
        self::assertEquals(
176
            'NUMBER(10)',
177
            $this->platform->getIntegerTypeDeclarationSQL(
178
                ['autoincrement' => true, 'primary' => true]
179
            )
180
        );
181
    }
182
183
    public function testPrefersIdentityColumns() : void
184
    {
185
        self::assertFalse($this->platform->prefersIdentityColumns());
186
    }
187
188
    public function testSupportsIdentityColumns() : void
189
    {
190
        self::assertFalse($this->platform->supportsIdentityColumns());
191
    }
192
193
    public function testSupportsSavePoints() : void
194
    {
195
        self::assertTrue($this->platform->supportsSavepoints());
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201
    protected function supportsCommentOnStatement() : bool
202
    {
203
        return true;
204
    }
205
206
    public function getGenerateIndexSql() : string
207
    {
208
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
209
    }
210
211
    public function getGenerateUniqueIndexSql() : string
212
    {
213
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2)';
214
    }
215
216
    public function getGenerateForeignKeySql() : string
217
    {
218
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
219
    }
220
221
    /**
222
     * @param mixed[] $options
223
     *
224
     * @group DBAL-1097
225
     * @dataProvider getGeneratesAdvancedForeignKeyOptionsSQLData
226
     */
227
    public function testGeneratesAdvancedForeignKeyOptionsSQL(array $options, string $expectedSql) : void
228
    {
229
        $foreignKey = new ForeignKeyConstraint(['foo'], 'foreign_table', ['bar'], '', $options);
230
231
        self::assertSame($expectedSql, $this->platform->getAdvancedForeignKeyOptionsSQL($foreignKey));
232
    }
233
234
    /**
235
     * @return mixed[][]
236
     */
237
    public static function getGeneratesAdvancedForeignKeyOptionsSQLData() : iterable
238
    {
239
        return [
240
            [[], ''],
241
            [['onUpdate' => 'CASCADE'], ''],
242
            [['onDelete' => 'CASCADE'], ' ON DELETE CASCADE'],
243
            [['onDelete' => 'NO ACTION'], ''],
244
            [['onDelete' => 'RESTRICT'], ''],
245
            [['onUpdate' => 'SET NULL', 'onDelete' => 'SET NULL'], ' ON DELETE SET NULL'],
246
        ];
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
253
    {
254
        return [
255
            ['CASCADE', 'CASCADE'],
256
            ['SET NULL', 'SET NULL'],
257
            ['NO ACTION', ''],
258
            ['RESTRICT', ''],
259
            ['CaScAdE', 'CASCADE'],
260
        ];
261
    }
262
263
    public function testModifyLimitQuery() : void
264
    {
265
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
266
        self::assertEquals('SELECT a.* FROM (SELECT * FROM user) a WHERE ROWNUM <= 10', $sql);
267
    }
268
269
    public function testModifyLimitQueryWithEmptyOffset() : void
270
    {
271
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10);
272
        self::assertEquals('SELECT a.* FROM (SELECT * FROM user) a WHERE ROWNUM <= 10', $sql);
273
    }
274
275
    public function testModifyLimitQueryWithNonEmptyOffset() : void
276
    {
277
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 10);
278
        self::assertEquals('SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT * FROM user) a WHERE ROWNUM <= 20) WHERE doctrine_rownum >= 11', $sql);
279
    }
280
281
    public function testModifyLimitQueryWithEmptyLimit() : void
282
    {
283
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', null, 10);
284
        self::assertEquals('SELECT * FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT * FROM user) a) WHERE doctrine_rownum >= 11', $sql);
285
    }
286
287
    public function testModifyLimitQueryWithAscOrderBy() : void
288
    {
289
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username ASC', 10);
290
        self::assertEquals('SELECT a.* FROM (SELECT * FROM user ORDER BY username ASC) a WHERE ROWNUM <= 10', $sql);
291
    }
292
293
    public function testModifyLimitQueryWithDescOrderBy() : void
294
    {
295
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10);
296
        self::assertEquals('SELECT a.* FROM (SELECT * FROM user ORDER BY username DESC) a WHERE ROWNUM <= 10', $sql);
297
    }
298
299
    public function testGenerateTableWithAutoincrement() : void
300
    {
301
        $columnName = strtoupper('id' . uniqid());
302
        $tableName  = strtoupper('table' . uniqid());
303
        $table      = new Table($tableName);
304
305
        $column = $table->addColumn($columnName, 'integer');
306
        $column->setAutoincrement(true);
307
        $targets    = [
308
            sprintf('CREATE TABLE %s (%s NUMBER(10) NOT NULL)', $tableName, $columnName),
309
            sprintf(
310
                "DECLARE constraints_Count NUMBER; BEGIN SELECT COUNT(CONSTRAINT_NAME) INTO constraints_Count FROM USER_CONSTRAINTS WHERE TABLE_NAME = '%s' AND CONSTRAINT_TYPE = 'P'; IF constraints_Count = 0 OR constraints_Count = '' THEN EXECUTE IMMEDIATE 'ALTER TABLE %s ADD CONSTRAINT %s_AI_PK PRIMARY KEY (%s)'; END IF; END;",
311
                $tableName,
312
                $tableName,
313
                $tableName,
314
                $columnName
315
            ),
316
            sprintf('CREATE SEQUENCE %s_SEQ START WITH 1 MINVALUE 1 INCREMENT BY 1', $tableName),
317
            sprintf(
318
                "CREATE TRIGGER %s_AI_PK BEFORE INSERT ON %s FOR EACH ROW DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN SELECT %s_SEQ.NEXTVAL INTO :NEW.%s FROM DUAL; IF (:NEW.%s IS NULL OR :NEW.%s = 0) THEN SELECT %s_SEQ.NEXTVAL INTO :NEW.%s FROM DUAL; ELSE SELECT NVL(Last_Number, 0) INTO last_Sequence FROM User_Sequences WHERE Sequence_Name = '%s_SEQ'; SELECT :NEW.%s INTO last_InsertID FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP SELECT %s_SEQ.NEXTVAL INTO last_Sequence FROM DUAL; END LOOP; END IF; END;",
319
                $tableName,
320
                $tableName,
321
                $tableName,
322
                $columnName,
323
                $columnName,
324
                $columnName,
325
                $tableName,
326
                $columnName,
327
                $tableName,
328
                $columnName,
329
                $tableName
330
            ),
331
        ];
332
        $statements = $this->platform->getCreateTableSQL($table);
333
        //strip all the whitespace from the statements
334
        array_walk($statements, static function (&$value) : void {
335
            $value = preg_replace('/\s+/', ' ', $value);
336
        });
337
        foreach ($targets as $key => $sql) {
338
            self::assertArrayHasKey($key, $statements);
339
            self::assertEquals($sql, $statements[$key]);
340
        }
341
    }
342
343
    /**
344
     * {@inheritDoc}
345
     */
346
    public function getCreateTableColumnCommentsSQL() : array
347
    {
348
        return [
349
            'CREATE TABLE test (id NUMBER(10) NOT NULL, PRIMARY KEY(id))',
350
            "COMMENT ON COLUMN test.id IS 'This is a comment'",
351
        ];
352
    }
353
354
    /**
355
     * {@inheritDoc}
356
     */
357
    public function getCreateTableColumnTypeCommentsSQL() : array
358
    {
359
        return [
360
            'CREATE TABLE test (id NUMBER(10) NOT NULL, data CLOB NOT NULL, PRIMARY KEY(id))',
361
            "COMMENT ON COLUMN test.data IS '(DC2Type:array)'",
362
        ];
363
    }
364
365
    /**
366
     * {@inheritDoc}
367
     */
368
    public function getAlterTableColumnCommentsSQL() : array
369
    {
370
        return [
371
            'ALTER TABLE mytable ADD (quota NUMBER(10) NOT NULL)',
372
            "COMMENT ON COLUMN mytable.quota IS 'A comment'",
373
            "COMMENT ON COLUMN mytable.foo IS ''",
374
            "COMMENT ON COLUMN mytable.baz IS 'B comment'",
375
        ];
376
    }
377
378
    public function getBitAndComparisonExpressionSql(string $value1, string $value2) : string
379
    {
380
        return 'BITAND(' . $value1 . ', ' . $value2 . ')';
381
    }
382
383
    public function getBitOrComparisonExpressionSql(string $value1, string $value2) : string
384
    {
385
        return '(' . $value1 . '-' .
386
        $this->getBitAndComparisonExpressionSql($value1, $value2)
387
        . '+' . $value2 . ')';
388
    }
389
390
    /**
391
     * @return mixed[]
392
     */
393
    protected function getQuotedColumnInPrimaryKeySQL() : array
394
    {
395
        return ['CREATE TABLE "quoted" ("create" VARCHAR2(255) NOT NULL, PRIMARY KEY("create"))'];
396
    }
397
398
    /**
399
     * @return mixed[]
400
     */
401
    protected function getQuotedColumnInIndexSQL() : array
402
    {
403
        return [
404
            'CREATE TABLE "quoted" ("create" VARCHAR2(255) NOT NULL)',
405
            'CREATE INDEX IDX_22660D028FD6E0FB ON "quoted" ("create")',
406
        ];
407
    }
408
409
    /**
410
     * @return mixed[]
411
     */
412
    protected function getQuotedNameInIndexSQL() : array
413
    {
414
        return [
415
            'CREATE TABLE test (column1 VARCHAR2(255) NOT NULL)',
416
            'CREATE INDEX "key" ON test (column1)',
417
        ];
418
    }
419
420
    /**
421
     * @return mixed[]
422
     */
423
    protected function getQuotedColumnInForeignKeySQL() : array
424
    {
425
        return [
426
            'CREATE TABLE "quoted" ("create" VARCHAR2(255) NOT NULL, foo VARCHAR2(255) NOT NULL, "bar" VARCHAR2(255) NOT NULL)',
427
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES foreign ("create", bar, "foo-bar")',
428
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ("create", foo, "bar") REFERENCES foo ("create", bar, "foo-bar")',
429
            'ALTER TABLE "quoted" ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ("create", foo, "bar") REFERENCES "foo-bar" ("create", bar, "foo-bar")',
430
        ];
431
    }
432
433
    /**
434
     * @group DBAL-472
435
     * @group DBAL-1001
436
     */
437
    public function testAlterTableNotNULL() : void
438
    {
439
        $tableDiff = new TableDiff('mytable');
440
441
        $tableDiff->changedColumns['foo'] = new ColumnDiff(
442
            'foo',
443
            new Column(
444
                'foo',
445
                Type::getType('string'),
446
                [
447
                    'length' => 255,
448
                    'default' => 'bla',
449
                    'notnull' => true,
450
                ]
451
            ),
452
            ['type']
453
        );
454
455
        $tableDiff->changedColumns['bar'] = new ColumnDiff(
456
            'bar',
457
            new Column(
458
                'baz',
459
                Type::getType('string'),
460
                [
461
                    'length' => 255,
462
                    'default' => 'bla',
463
                    'notnull' => true,
464
                ]
465
            ),
466
            ['type', 'notnull']
467
        );
468
469
        $tableDiff->changedColumns['metar'] = new ColumnDiff(
470
            'metar',
471
            new Column(
472
                'metar',
473
                Type::getType('string'),
474
                [
475
                    'length' => 2000,
476
                    'notnull' => false,
477
                ]
478
            ),
479
            ['notnull']
480
        );
481
482
        $expectedSql = ["ALTER TABLE mytable MODIFY (foo VARCHAR2(255) DEFAULT 'bla', baz VARCHAR2(255) DEFAULT 'bla' NOT NULL, metar VARCHAR2(2000) DEFAULT NULL NULL)"];
483
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
484
    }
485
486
    /**
487
     * @group DBAL-2555
488
     */
489
    public function testInitializesDoctrineTypeMappings() : void
490
    {
491
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('long raw'));
492
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('long raw'));
493
494
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('raw'));
495
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('raw'));
496
497
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('date'));
498
        self::assertSame('date', $this->platform->getDoctrineTypeMapping('date'));
499
    }
500
501
    public function testGetVariableLengthStringTypeDeclarationSQLNoLength() : void
502
    {
503
        $this->expectException(ColumnLengthRequired::class);
504
505
        parent::testGetVariableLengthStringTypeDeclarationSQLNoLength();
506
    }
507
508
    protected function getExpectedVariableLengthStringTypeDeclarationSQLWithLength() : string
509
    {
510
        return 'VARCHAR2(16)';
511
    }
512
513
    public function testGetFixedLengthBinaryTypeDeclarationSQLNoLength() : void
514
    {
515
        $this->expectException(ColumnLengthRequired::class);
516
517
        parent::testGetFixedLengthBinaryTypeDeclarationSQLNoLength();
518
    }
519
520
    public function getExpectedFixedLengthBinaryTypeDeclarationSQLWithLength() : string
521
    {
522
        return 'RAW(16)';
523
    }
524
525
    public function testGetVariableLengthBinaryTypeDeclarationSQLNoLength() : void
526
    {
527
        $this->expectException(ColumnLengthRequired::class);
528
529
        parent::testGetVariableLengthBinaryTypeDeclarationSQLNoLength();
530
    }
531
532
    public function getExpectedVariableLengthBinaryTypeDeclarationSQLWithLength() : string
533
    {
534
        return 'RAW(16)';
535
    }
536
537
    public function testDoesNotPropagateUnnecessaryTableAlterationOnBinaryType() : void
538
    {
539
        $table1 = new Table('mytable');
540
        $table1->addColumn('column_varbinary', 'binary');
541
        $table1->addColumn('column_binary', 'binary', ['fixed' => true]);
542
543
        $table2 = new Table('mytable');
544
        $table2->addColumn('column_varbinary', 'binary', ['fixed' => true]);
545
        $table2->addColumn('column_binary', 'binary');
546
547
        $comparator = new Comparator();
548
549
        $diff = $comparator->diffTable($table1, $table2);
550
551
        self::assertNotNull($diff);
552
553
        // VARBINARY -> BINARY
554
        // BINARY    -> VARBINARY
555
        self::assertEmpty($this->platform->getAlterTableSQL($diff));
556
    }
557
558
    /**
559
     * @group DBAL-563
560
     * @group DBAL-831
561
     */
562
    public function testReturnsIdentitySequenceName() : void
563
    {
564
        self::assertSame('MYTABLE_SEQ', $this->platform->getIdentitySequenceName('mytable', 'mycolumn'));
565
        self::assertSame('"mytable_SEQ"', $this->platform->getIdentitySequenceName('"mytable"', 'mycolumn'));
566
        self::assertSame('MYTABLE_SEQ', $this->platform->getIdentitySequenceName('mytable', '"mycolumn"'));
567
        self::assertSame('"mytable_SEQ"', $this->platform->getIdentitySequenceName('"mytable"', '"mycolumn"'));
568
    }
569
570
    /**
571
     * @dataProvider dataCreateSequenceWithCache
572
     * @group DBAL-139
573
     */
574
    public function testCreateSequenceWithCache(int $cacheSize, string $expectedSql) : void
575
    {
576
        $sequence = new Sequence('foo', 1, 1, $cacheSize);
577
        self::assertStringContainsString($expectedSql, $this->platform->getCreateSequenceSQL($sequence));
578
    }
579
580
    /**
581
     * @return mixed[][]
582
     */
583
    public static function dataCreateSequenceWithCache() : iterable
584
    {
585
        return [
586
            [1, 'NOCACHE'],
587
            [0, 'NOCACHE'],
588
            [3, 'CACHE 3'],
589
        ];
590
    }
591
592
    /**
593
     * {@inheritDoc}
594
     *
595
     * @group DBAL-234
596
     */
597
    protected function getAlterTableRenameIndexSQL() : array
598
    {
599
        return ['ALTER INDEX idx_foo RENAME TO idx_bar'];
600
    }
601
602
    /**
603
     * {@inheritDoc}
604
     *
605
     * @group DBAL-234
606
     */
607
    protected function getQuotedAlterTableRenameIndexSQL() : array
608
    {
609
        return [
610
            'ALTER INDEX "create" RENAME TO "select"',
611
            'ALTER INDEX "foo" RENAME TO "bar"',
612
        ];
613
    }
614
615
    /**
616
     * {@inheritdoc}
617
     */
618
    protected function getQuotedAlterTableRenameColumnSQL() : array
619
    {
620
        return [
621
            'ALTER TABLE mytable RENAME COLUMN unquoted1 TO unquoted',
622
            'ALTER TABLE mytable RENAME COLUMN unquoted2 TO "where"',
623
            'ALTER TABLE mytable RENAME COLUMN unquoted3 TO "foo"',
624
            'ALTER TABLE mytable RENAME COLUMN "create" TO reserved_keyword',
625
            'ALTER TABLE mytable RENAME COLUMN "table" TO "from"',
626
            'ALTER TABLE mytable RENAME COLUMN "select" TO "bar"',
627
            'ALTER TABLE mytable RENAME COLUMN quoted1 TO quoted',
628
            'ALTER TABLE mytable RENAME COLUMN quoted2 TO "and"',
629
            'ALTER TABLE mytable RENAME COLUMN quoted3 TO "baz"',
630
        ];
631
    }
632
633
    /**
634
     * {@inheritdoc}
635
     */
636
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
637
    {
638
        $this->markTestIncomplete('Not implemented yet');
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
639
    }
640
641
    /**
642
     * {@inheritDoc}
643
     *
644
     * @group DBAL-807
645
     */
646
    protected function getAlterTableRenameIndexInSchemaSQL() : array
647
    {
648
        return ['ALTER INDEX myschema.idx_foo RENAME TO idx_bar'];
649
    }
650
651
    /**
652
     * {@inheritDoc}
653
     *
654
     * @group DBAL-807
655
     */
656
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
657
    {
658
        return [
659
            'ALTER INDEX "schema"."create" RENAME TO "select"',
660
            'ALTER INDEX "schema"."foo" RENAME TO "bar"',
661
        ];
662
    }
663
664
    protected function getQuotesDropForeignKeySQL() : string
665
    {
666
        return 'ALTER TABLE "table" DROP CONSTRAINT "select"';
667
    }
668
669
    /**
670
     * @group DBAL-423
671
     */
672
    public function testReturnsGuidTypeDeclarationSQL() : void
673
    {
674
        self::assertSame('CHAR(36)', $this->platform->getGuidTypeDeclarationSQL([]));
675
    }
676
677
    /**
678
     * {@inheritdoc}
679
     */
680
    public function getAlterTableRenameColumnSQL() : array
681
    {
682
        return ['ALTER TABLE foo RENAME COLUMN bar TO baz'];
683
    }
684
685
    /**
686
     * @param string[] $expectedSql
687
     *
688
     * @dataProvider getReturnsDropAutoincrementSQL
689
     * @group DBAL-831
690
     */
691
    public function testReturnsDropAutoincrementSQL(string $table, array $expectedSql) : void
692
    {
693
        assert($this->platform instanceof OraclePlatform);
694
695
        self::assertSame($expectedSql, $this->platform->getDropAutoincrementSql($table));
696
    }
697
698
    /**
699
     * @return mixed[][]
700
     */
701
    public static function getReturnsDropAutoincrementSQL() : iterable
702
    {
703
        return [
704
            [
705
                'myTable',
706
                [
707
                    'DROP TRIGGER MYTABLE_AI_PK',
708
                    'DROP SEQUENCE MYTABLE_SEQ',
709
                    'ALTER TABLE MYTABLE DROP CONSTRAINT MYTABLE_AI_PK',
710
                ],
711
            ],
712
            [
713
                '"myTable"',
714
                [
715
                    'DROP TRIGGER "myTable_AI_PK"',
716
                    'DROP SEQUENCE "myTable_SEQ"',
717
                    'ALTER TABLE "myTable" DROP CONSTRAINT "myTable_AI_PK"',
718
                ],
719
            ],
720
            [
721
                'table',
722
                [
723
                    'DROP TRIGGER TABLE_AI_PK',
724
                    'DROP SEQUENCE TABLE_SEQ',
725
                    'ALTER TABLE "TABLE" DROP CONSTRAINT TABLE_AI_PK',
726
                ],
727
            ],
728
        ];
729
    }
730
731
    /**
732
     * {@inheritdoc}
733
     */
734
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
735
    {
736
        return [
737
            'ALTER TABLE "foo" DROP CONSTRAINT fk1',
738
            'ALTER TABLE "foo" DROP CONSTRAINT fk2',
739
            'ALTER TABLE "foo" ADD (bloo NUMBER(10) NOT NULL)',
740
            'ALTER TABLE "foo" MODIFY (bar NUMBER(10) DEFAULT NULL NULL)',
741
            'ALTER TABLE "foo" RENAME COLUMN id TO war',
742
            'ALTER TABLE "foo" DROP (baz)',
743
            'ALTER TABLE "foo" RENAME TO "table"',
744
            'ALTER TABLE "table" ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id)',
745
            'ALTER TABLE "table" ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id)',
746
        ];
747
    }
748
749
    /**
750
     * {@inheritdoc}
751
     */
752
    protected function getCommentOnColumnSQL() : array
753
    {
754
        return [
755
            'COMMENT ON COLUMN foo.bar IS \'comment\'',
756
            'COMMENT ON COLUMN "Foo"."BAR" IS \'comment\'',
757
            'COMMENT ON COLUMN "select"."from" IS \'comment\'',
758
        ];
759
    }
760
761
    /**
762
     * @group DBAL-1004
763
     */
764
    public function testAltersTableColumnCommentWithExplicitlyQuotedIdentifiers() : void
765
    {
766
        $table1 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'))]);
767
        $table2 = new Table('"foo"', [new Column('"bar"', Type::getType('integer'), ['comment' => 'baz'])]);
768
769
        $comparator = new Comparator();
770
771
        $tableDiff = $comparator->diffTable($table1, $table2);
772
773
        self::assertInstanceOf(TableDiff::class, $tableDiff);
774
        self::assertSame(
775
            ['COMMENT ON COLUMN "foo"."bar" IS \'baz\''],
776
            $this->platform->getAlterTableSQL($tableDiff)
777
        );
778
    }
779
780
    public function testQuotedTableNames() : void
781
    {
782
        $table = new Table('"test"');
783
        $table->addColumn('"id"', 'integer', ['autoincrement' => true]);
784
785
        // assert tabel
786
        self::assertTrue($table->isQuoted());
787
        self::assertEquals('test', $table->getName());
788
        self::assertEquals('"test"', $table->getQuotedName($this->platform));
789
790
        $sql = $this->platform->getCreateTableSQL($table);
791
        self::assertEquals('CREATE TABLE "test" ("id" NUMBER(10) NOT NULL)', $sql[0]);
792
        self::assertEquals('CREATE SEQUENCE "test_SEQ" START WITH 1 MINVALUE 1 INCREMENT BY 1', $sql[2]);
793
        $createTriggerStatement = <<<EOD
794
CREATE TRIGGER "test_AI_PK"
795
   BEFORE INSERT
796
   ON "test"
797
   FOR EACH ROW
798
DECLARE
799
   last_Sequence NUMBER;
800
   last_InsertID NUMBER;
801
BEGIN
802
   SELECT "test_SEQ".NEXTVAL INTO :NEW."id" FROM DUAL;
803
   IF (:NEW."id" IS NULL OR :NEW."id" = 0) THEN
804
      SELECT "test_SEQ".NEXTVAL INTO :NEW."id" FROM DUAL;
805
   ELSE
806
      SELECT NVL(Last_Number, 0) INTO last_Sequence
807
        FROM User_Sequences
808
       WHERE Sequence_Name = 'test_SEQ';
809
      SELECT :NEW."id" INTO last_InsertID FROM DUAL;
810
      WHILE (last_InsertID > last_Sequence) LOOP
811
         SELECT "test_SEQ".NEXTVAL INTO last_Sequence FROM DUAL;
812
      END LOOP;
813
   END IF;
814
END;
815
EOD;
816
817
        self::assertEquals($createTriggerStatement, $sql[3]);
818
    }
819
820
    /**
821
     * @dataProvider getReturnsGetListTableColumnsSQL
822
     * @group DBAL-831
823
     */
824
    public function testReturnsGetListTableColumnsSQL(?string $database, string $expectedSql) : void
825
    {
826
        // note: this assertion is a bit strict, as it compares a full SQL string.
827
        // Should this break in future, then please try to reduce the matching to substring matching while reworking
828
        // the tests
829
        self::assertEquals($expectedSql, $this->platform->getListTableColumnsSQL('"test"', $database));
830
    }
831
832
    /**
833
     * @return mixed[][]
834
     */
835
    public static function getReturnsGetListTableColumnsSQL() : iterable
836
    {
837
        return [
838
            [
839
                null,
840
                <<<'SQL'
841
SELECT   c.*,
842
         (
843
             SELECT d.comments
844
             FROM   user_col_comments d
845
             WHERE  d.TABLE_NAME = c.TABLE_NAME
846
             AND    d.COLUMN_NAME = c.COLUMN_NAME
847
         ) AS comments
848
FROM     user_tab_columns c
849
WHERE    c.table_name = 'test'
850
ORDER BY c.column_id
851
SQL
852
                ,
853
            ],
854
            [
855
                '/',
856
                <<<'SQL'
857
SELECT   c.*,
858
         (
859
             SELECT d.comments
860
             FROM   user_col_comments d
861
             WHERE  d.TABLE_NAME = c.TABLE_NAME
862
             AND    d.COLUMN_NAME = c.COLUMN_NAME
863
         ) AS comments
864
FROM     user_tab_columns c
865
WHERE    c.table_name = 'test'
866
ORDER BY c.column_id
867
SQL
868
                ,
869
            ],
870
            [
871
                'scott',
872
                <<<'SQL'
873
SELECT   c.*,
874
         (
875
             SELECT d.comments
876
             FROM   all_col_comments d
877
             WHERE  d.TABLE_NAME = c.TABLE_NAME AND d.OWNER = c.OWNER
878
             AND    d.COLUMN_NAME = c.COLUMN_NAME
879
         ) AS comments
880
FROM     all_tab_columns c
881
WHERE    c.table_name = 'test' AND c.owner = 'SCOTT'
882
ORDER BY c.column_id
883
SQL
884
                ,
885
            ],
886
        ];
887
    }
888
889
    /**
890
     * {@inheritdoc}
891
     */
892
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
893
    {
894
        return 'CONSTRAINT "select" UNIQUE (foo)';
895
    }
896
897
    /**
898
     * {@inheritdoc}
899
     */
900
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
901
    {
902
        return 'INDEX "select" (foo)';
903
    }
904
905
    /**
906
     * {@inheritdoc}
907
     */
908
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
909
    {
910
        return 'TRUNCATE TABLE "select"';
911
    }
912
913
    /**
914
     * {@inheritdoc}
915
     */
916
    protected function getAlterStringToFixedStringSQL() : array
917
    {
918
        return ['ALTER TABLE mytable MODIFY (name CHAR(2) DEFAULT NULL)'];
919
    }
920
921
    /**
922
     * {@inheritdoc}
923
     */
924
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
925
    {
926
        return ['ALTER INDEX idx_foo RENAME TO idx_foo_renamed'];
927
    }
928
929
    /**
930
     * @group DBAL-2436
931
     */
932
    public function testQuotesDatabaseNameInListSequencesSQL() : void
933
    {
934
        self::assertStringContainsStringIgnoringCase(
935
            "'Foo''Bar\\'",
936
            $this->platform->getListSequencesSQL("Foo'Bar\\")
937
        );
938
    }
939
940
    /**
941
     * @group DBAL-2436
942
     */
943
    public function testQuotesTableNameInListTableIndexesSQL() : void
944
    {
945
        self::assertStringContainsStringIgnoringCase(
946
            "'Foo''Bar\\'",
947
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
948
        );
949
    }
950
951
    /**
952
     * @group DBAL-2436
953
     */
954
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
955
    {
956
        self::assertStringContainsStringIgnoringCase(
957
            "'Foo''Bar\\'",
958
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
959
        );
960
    }
961
962
    /**
963
     * @group DBAL-2436
964
     */
965
    public function testQuotesTableNameInListTableConstraintsSQL() : void
966
    {
967
        self::assertStringContainsStringIgnoringCase(
968
            "'Foo''Bar\\'",
969
            $this->platform->getListTableConstraintsSQL("Foo'Bar\\")
970
        );
971
    }
972
973
    /**
974
     * @group DBAL-2436
975
     */
976
    public function testQuotesTableNameInListTableColumnsSQL() : void
977
    {
978
        self::assertStringContainsStringIgnoringCase(
979
            "'Foo''Bar\\'",
980
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
981
        );
982
    }
983
984
    /**
985
     * @group DBAL-2436
986
     */
987
    public function testQuotesDatabaseNameInListTableColumnsSQL() : void
988
    {
989
        self::assertStringContainsStringIgnoringCase(
990
            "'Foo''Bar\\'",
991
            $this->platform->getListTableColumnsSQL('foo_table', "Foo'Bar\\")
992
        );
993
    }
994
}
995