Completed
Push — 3.0.x ( 0d9a8c...f82f5c )
by Sergei
25s queued 17s
created

testGeneratesSequenceSqlCommands()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 18
rs 9.8333
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\DBAL\Platforms;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Column;
7
use Doctrine\DBAL\Schema\ColumnDiff;
8
use Doctrine\DBAL\Schema\Index;
9
use Doctrine\DBAL\Schema\Sequence;
10
use Doctrine\DBAL\Schema\Table;
11
use Doctrine\DBAL\Schema\TableDiff;
12
use Doctrine\DBAL\TransactionIsolationLevel;
13
use Doctrine\DBAL\Types\Type;
14
15
abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCase
16
{
17
    public function getGenerateTableSql() : string
18
    {
19
        return 'CREATE TABLE test (id INT IDENTITY NOT NULL, test NVARCHAR(255), PRIMARY KEY (id))';
20
    }
21
22
    /**
23
     * {@inheritDoc}
24
     */
25
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
26
    {
27
        return [
28
            'CREATE TABLE test (foo NVARCHAR(255), bar NVARCHAR(255))',
29
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar) WHERE foo IS NOT NULL AND bar IS NOT NULL',
30
        ];
31
    }
32
33
    /**
34
     * {@inheritDoc}
35
     */
36
    public function getGenerateAlterTableSql() : array
37
    {
38
        return [
39
            'ALTER TABLE mytable ADD quota INT',
40
            'ALTER TABLE mytable DROP COLUMN foo',
41
            'ALTER TABLE mytable ALTER COLUMN baz NVARCHAR(255) NOT NULL',
42
            "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_78240498 DEFAULT 'def' FOR baz",
43
            'ALTER TABLE mytable ALTER COLUMN bloo BIT NOT NULL',
44
            "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_CECED971 DEFAULT '0' FOR bloo",
45
            "sp_RENAME 'mytable', 'userlist'",
46
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
47
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " .
48
            "+ REPLACE(dc.name, '6B2BD609', 'E2B58069') + ''', ''OBJECT'';' " .
49
            'FROM sys.default_constraints dc ' .
50
            'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' .
51
            "WHERE tbl.name = 'userlist';" .
52
            'EXEC sp_executesql @sql',
53
        ];
54
    }
55
56
    public function testDoesNotSupportRegexp() : void
57
    {
58
        $this->expectException(DBALException::class);
59
60
        $this->platform->getRegexpExpression();
61
    }
62
63
    public function testGeneratesSqlSnippets() : void
64
    {
65
        self::assertEquals('CONVERT(date, GETDATE())', $this->platform->getCurrentDateSQL());
66
        self::assertEquals('CONVERT(time, GETDATE())', $this->platform->getCurrentTimeSQL());
67
        self::assertEquals('CURRENT_TIMESTAMP', $this->platform->getCurrentTimestampSQL());
68
        self::assertEquals('"', $this->platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
69
        self::assertEquals('(column1 + column2 + column3)', $this->platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
70
    }
71
72
    public function testGeneratesTransactionsCommands() : void
73
    {
74
        self::assertEquals(
75
            'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
76
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
77
        );
78
        self::assertEquals(
79
            'SET TRANSACTION ISOLATION LEVEL READ COMMITTED',
80
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
81
        );
82
        self::assertEquals(
83
            'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ',
84
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
85
        );
86
        self::assertEquals(
87
            'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE',
88
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
89
        );
90
    }
91
92
    public function testGeneratesDDLSnippets() : void
93
    {
94
        $dropDatabaseExpectation = 'DROP DATABASE foobar';
95
96
        self::assertEquals('SELECT * FROM sys.databases', $this->platform->getListDatabasesSQL());
97
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
98
        self::assertEquals($dropDatabaseExpectation, $this->platform->getDropDatabaseSQL('foobar'));
99
        self::assertEquals('DROP TABLE foobar', $this->platform->getDropTableSQL('foobar'));
100
    }
101
102
    public function testGeneratesTypeDeclarationForIntegers() : void
103
    {
104
        self::assertEquals(
105
            'INT',
106
            $this->platform->getIntegerTypeDeclarationSQL([])
107
        );
108
        self::assertEquals(
109
            'INT IDENTITY',
110
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
111
        );
112
        self::assertEquals(
113
            'INT IDENTITY',
114
            $this->platform->getIntegerTypeDeclarationSQL(
115
                ['autoincrement' => true, 'primary' => true]
116
            )
117
        );
118
    }
119
120
    public function testGeneratesTypeDeclarationsForStrings() : void
121
    {
122
        self::assertEquals(
123
            'NCHAR(10)',
124
            $this->platform->getVarcharTypeDeclarationSQL(
125
                ['length' => 10, 'fixed' => true]
126
            )
127
        );
128
        self::assertEquals(
129
            'NVARCHAR(50)',
130
            $this->platform->getVarcharTypeDeclarationSQL(['length' => 50]),
131
            'Variable string declaration is not correct'
132
        );
133
        self::assertEquals(
134
            'NVARCHAR(255)',
135
            $this->platform->getVarcharTypeDeclarationSQL([]),
136
            'Long string declaration is not correct'
137
        );
138
        self::assertSame('VARCHAR(MAX)', $this->platform->getClobTypeDeclarationSQL([]));
139
        self::assertSame(
140
            'VARCHAR(MAX)',
141
            $this->platform->getClobTypeDeclarationSQL(['length' => 5, 'fixed' => true])
142
        );
143
    }
144
145
    public function testPrefersIdentityColumns() : void
146
    {
147
        self::assertTrue($this->platform->prefersIdentityColumns());
148
    }
149
150
    public function testSupportsIdentityColumns() : void
151
    {
152
        self::assertTrue($this->platform->supportsIdentityColumns());
153
    }
154
155
    public function testSupportsCreateDropDatabase() : void
156
    {
157
        self::assertTrue($this->platform->supportsCreateDropDatabase());
158
    }
159
160
    public function testSupportsSchemas() : void
161
    {
162
        self::assertTrue($this->platform->supportsSchemas());
163
    }
164
165
    public function testDoesNotSupportSavePoints() : void
166
    {
167
        self::assertTrue($this->platform->supportsSavepoints());
168
    }
169
170
    public function getGenerateIndexSql() : string
171
    {
172
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
173
    }
174
175
    public function getGenerateUniqueIndexSql() : string
176
    {
177
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2) WHERE test IS NOT NULL AND test2 IS NOT NULL';
178
    }
179
180
    public function getGenerateForeignKeySql() : string
181
    {
182
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
183
    }
184
185
    public function testModifyLimitQuery() : void
186
    {
187
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
188
        self::assertEquals('SELECT * FROM user ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
189
    }
190
191
    public function testModifyLimitQueryWithEmptyOffset() : void
192
    {
193
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10);
194
        self::assertEquals('SELECT * FROM user ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
195
    }
196
197
    public function testModifyLimitQueryWithOffset() : void
198
    {
199
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10, 5);
200
        self::assertEquals('SELECT * FROM user ORDER BY username DESC OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
201
    }
202
203
    public function testModifyLimitQueryWithAscOrderBy() : void
204
    {
205
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username ASC', 10);
206
        self::assertEquals('SELECT * FROM user ORDER BY username ASC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
207
    }
208
209
    public function testModifyLimitQueryWithLowercaseOrderBy() : void
210
    {
211
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user order by username', 10);
212
        self::assertEquals('SELECT * FROM user order by username OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
213
    }
214
215
    public function testModifyLimitQueryWithDescOrderBy() : void
216
    {
217
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10);
218
        self::assertEquals('SELECT * FROM user ORDER BY username DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
219
    }
220
221
    public function testModifyLimitQueryWithMultipleOrderBy() : void
222
    {
223
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC, usereamil ASC', 10);
224
        self::assertEquals('SELECT * FROM user ORDER BY username DESC, usereamil ASC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
225
    }
226
227
    public function testModifyLimitQueryWithSubSelect() : void
228
    {
229
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result', 10);
230
        self::assertEquals('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
231
    }
232
233
    public function testModifyLimitQueryWithSubSelectAndOrder() : void
234
    {
235
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result ORDER BY uname DESC', 10);
236
        self::assertEquals('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result ORDER BY uname DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
237
238
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC', 10);
239
        self::assertEquals('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
240
    }
241
242
    public function testModifyLimitQueryWithSubSelectAndMultipleOrder() : void
243
    {
244
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result ORDER BY uname DESC, uid ASC', 10, 5);
245
        self::assertEquals('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result ORDER BY uname DESC, uid ASC OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
246
247
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id uid, u.name uname) dctrn_result ORDER BY uname DESC, uid ASC', 10, 5);
248
        self::assertEquals('SELECT * FROM (SELECT u.id uid, u.name uname) dctrn_result ORDER BY uname DESC, uid ASC OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
249
250
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC, id ASC', 10, 5);
251
        self::assertEquals('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC, id ASC OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
252
    }
253
254
    public function testModifyLimitQueryWithFromColumnNames() : void
255
    {
256
        $sql = $this->platform->modifyLimitQuery('SELECT a.fromFoo, fromBar FROM foo', 10);
257
        self::assertEquals('SELECT a.fromFoo, fromBar FROM foo ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
258
    }
259
260
    /**
261
     * @group DBAL-927
262
     */
263
    public function testModifyLimitQueryWithExtraLongQuery() : void
264
    {
265
        $query  = 'SELECT table1.column1, table2.column2, table3.column3, table4.column4, table5.column5, table6.column6, table7.column7, table8.column8 FROM table1, table2, table3, table4, table5, table6, table7, table8 ';
266
        $query .= 'WHERE (table1.column1 = table2.column2) AND (table1.column1 = table3.column3) AND (table1.column1 = table4.column4) AND (table1.column1 = table5.column5) AND (table1.column1 = table6.column6) AND (table1.column1 = table7.column7) AND (table1.column1 = table8.column8) AND (table2.column2 = table3.column3) AND (table2.column2 = table4.column4) AND (table2.column2 = table5.column5) AND (table2.column2 = table6.column6) ';
267
        $query .= 'AND (table2.column2 = table7.column7) AND (table2.column2 = table8.column8) AND (table3.column3 = table4.column4) AND (table3.column3 = table5.column5) AND (table3.column3 = table6.column6) AND (table3.column3 = table7.column7) AND (table3.column3 = table8.column8) AND (table4.column4 = table5.column5) AND (table4.column4 = table6.column6) AND (table4.column4 = table7.column7) AND (table4.column4 = table8.column8) ';
268
        $query .= 'AND (table5.column5 = table6.column6) AND (table5.column5 = table7.column7) AND (table5.column5 = table8.column8) AND (table6.column6 = table7.column7) AND (table6.column6 = table8.column8) AND (table7.column7 = table8.column8)';
269
270
        $sql = $this->platform->modifyLimitQuery($query, 10);
271
272
        $expected  = 'SELECT table1.column1, table2.column2, table3.column3, table4.column4, table5.column5, table6.column6, table7.column7, table8.column8 FROM table1, table2, table3, table4, table5, table6, table7, table8 ';
273
        $expected .= 'WHERE (table1.column1 = table2.column2) AND (table1.column1 = table3.column3) AND (table1.column1 = table4.column4) AND (table1.column1 = table5.column5) AND (table1.column1 = table6.column6) AND (table1.column1 = table7.column7) AND (table1.column1 = table8.column8) AND (table2.column2 = table3.column3) AND (table2.column2 = table4.column4) AND (table2.column2 = table5.column5) AND (table2.column2 = table6.column6) ';
274
        $expected .= 'AND (table2.column2 = table7.column7) AND (table2.column2 = table8.column8) AND (table3.column3 = table4.column4) AND (table3.column3 = table5.column5) AND (table3.column3 = table6.column6) AND (table3.column3 = table7.column7) AND (table3.column3 = table8.column8) AND (table4.column4 = table5.column5) AND (table4.column4 = table6.column6) AND (table4.column4 = table7.column7) AND (table4.column4 = table8.column8) ';
275
        $expected .= 'AND (table5.column5 = table6.column6) AND (table5.column5 = table7.column7) AND (table5.column5 = table8.column8) AND (table6.column6 = table7.column7) AND (table6.column6 = table8.column8) AND (table7.column7 = table8.column8) ';
276
        $expected .= 'ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
277
278
        self::assertEquals($expected, $sql);
279
    }
280
281
    /**
282
     * @group DDC-2470
283
     */
284
    public function testModifyLimitQueryWithOrderByClause() : void
285
    {
286
        $sql      = 'SELECT m0_.NOMBRE AS NOMBRE0, m0_.FECHAINICIO AS FECHAINICIO1, m0_.FECHAFIN AS FECHAFIN2 FROM MEDICION m0_ WITH (NOLOCK) INNER JOIN ESTUDIO e1_ ON m0_.ESTUDIO_ID = e1_.ID INNER JOIN CLIENTE c2_ ON e1_.CLIENTE_ID = c2_.ID INNER JOIN USUARIO u3_ ON c2_.ID = u3_.CLIENTE_ID WHERE u3_.ID = ? ORDER BY m0_.FECHAINICIO DESC';
287
        $expected = 'SELECT m0_.NOMBRE AS NOMBRE0, m0_.FECHAINICIO AS FECHAINICIO1, m0_.FECHAFIN AS FECHAFIN2 FROM MEDICION m0_ WITH (NOLOCK) INNER JOIN ESTUDIO e1_ ON m0_.ESTUDIO_ID = e1_.ID INNER JOIN CLIENTE c2_ ON e1_.CLIENTE_ID = c2_.ID INNER JOIN USUARIO u3_ ON c2_.ID = u3_.CLIENTE_ID WHERE u3_.ID = ? ORDER BY m0_.FECHAINICIO DESC OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY';
288
        $actual   = $this->platform->modifyLimitQuery($sql, 10, 5);
289
290
        self::assertEquals($expected, $actual);
291
    }
292
293
    /**
294
     * @group DBAL-713
295
     */
296
    public function testModifyLimitQueryWithSubSelectInSelectList() : void
297
    {
298
        $sql = $this->platform->modifyLimitQuery(
299
            'SELECT ' .
300
            'u.id, ' .
301
            '(u.foo/2) foodiv, ' .
302
            'CONCAT(u.bar, u.baz) barbaz, ' .
303
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
304
            'FROM user u ' .
305
            "WHERE u.status = 'disabled'",
306
            10
307
        );
308
309
        self::assertEquals(
310
            'SELECT ' .
311
            'u.id, ' .
312
            '(u.foo/2) foodiv, ' .
313
            'CONCAT(u.bar, u.baz) barbaz, ' .
314
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
315
            'FROM user u ' .
316
            "WHERE u.status = 'disabled' " .
317
            'ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
318
            $sql
319
        );
320
    }
321
322
    /**
323
     * @group DBAL-713
324
     */
325
    public function testModifyLimitQueryWithSubSelectInSelectListAndOrderByClause() : void
326
    {
327
        $sql = $this->platform->modifyLimitQuery(
328
            'SELECT ' .
329
            'u.id, ' .
330
            '(u.foo/2) foodiv, ' .
331
            'CONCAT(u.bar, u.baz) barbaz, ' .
332
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
333
            'FROM user u ' .
334
            "WHERE u.status = 'disabled' " .
335
            'ORDER BY u.username DESC',
336
            10,
337
            5
338
        );
339
340
        self::assertEquals(
341
            'SELECT ' .
342
            'u.id, ' .
343
            '(u.foo/2) foodiv, ' .
344
            'CONCAT(u.bar, u.baz) barbaz, ' .
345
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
346
            'FROM user u ' .
347
            "WHERE u.status = 'disabled' " .
348
            'ORDER BY u.username DESC ' .
349
            'OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY',
350
            $sql
351
        );
352
    }
353
354
    /**
355
     * @group DBAL-834
356
     */
357
    public function testModifyLimitQueryWithAggregateFunctionInOrderByClause() : void
358
    {
359
        $sql = $this->platform->modifyLimitQuery(
360
            'SELECT ' .
361
            'MAX(heading_id) aliased, ' .
362
            'code ' .
363
            'FROM operator_model_operator ' .
364
            'GROUP BY code ' .
365
            'ORDER BY MAX(heading_id) DESC',
366
            1,
367
            0
368
        );
369
370
        self::assertEquals(
371
            'SELECT ' .
372
            'MAX(heading_id) aliased, ' .
373
            'code ' .
374
            'FROM operator_model_operator ' .
375
            'GROUP BY code ' .
376
            'ORDER BY MAX(heading_id) DESC ' .
377
            'OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
378
            $sql
379
        );
380
    }
381
382
    /**
383
     * @throws DBALException
384
     */
385
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBaseTable() : void
386
    {
387
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
388
            . 'FROM ('
389
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
390
            . 'FROM table_parent t1 '
391
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
392
            . ') dctrn_result '
393
            . 'ORDER BY id_0 ASC';
394
        $alteredSql = 'SELECT DISTINCT id_0, name_1 '
395
            . 'FROM ('
396
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
397
            . 'FROM table_parent t1 '
398
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
399
            . ') dctrn_result '
400
            . 'ORDER BY id_0 ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
401
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
402
        self::assertEquals($alteredSql, $sql);
403
    }
404
405
    /**
406
     * @throws DBALException
407
     */
408
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoinTable() : void
409
    {
410
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
411
            . 'FROM ('
412
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
413
            . 'FROM table_parent t1 '
414
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
415
            . ') dctrn_result '
416
            . 'ORDER BY name_1 ASC';
417
        $alteredSql = 'SELECT DISTINCT id_0, name_1 '
418
            . 'FROM ('
419
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
420
            . 'FROM table_parent t1 '
421
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
422
            . ') dctrn_result '
423
            . 'ORDER BY name_1 ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
424
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
425
        self::assertEquals($alteredSql, $sql);
426
    }
427
428
    /**
429
     * @throws DBALException
430
     */
431
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnsFromBothTables() : void
432
    {
433
        $querySql   = 'SELECT DISTINCT id_0, name_1, foo_2 '
434
            . 'FROM ('
435
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
436
            . 'FROM table_parent t1 '
437
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
438
            . ') dctrn_result '
439
            . 'ORDER BY name_1 ASC, foo_2 DESC';
440
        $alteredSql = 'SELECT DISTINCT id_0, name_1, foo_2 '
441
            . 'FROM ('
442
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
443
            . 'FROM table_parent t1 '
444
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
445
            . ') dctrn_result '
446
            . 'ORDER BY name_1 ASC, foo_2 DESC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
447
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
448
        self::assertEquals($alteredSql, $sql);
449
    }
450
451
    public function testModifyLimitSubquerySimple() : void
452
    {
453
        $querySql   = 'SELECT DISTINCT id_0 FROM '
454
            . '(SELECT k0_.id AS id_0, k0_.field AS field_1 '
455
            . 'FROM key_table k0_ WHERE (k0_.where_field IN (1))) dctrn_result';
456
        $alteredSql = 'SELECT DISTINCT id_0 FROM (SELECT k0_.id AS id_0, k0_.field AS field_1 '
457
            . 'FROM key_table k0_ WHERE (k0_.where_field IN (1))) dctrn_result ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY';
458
        $sql        = $this->platform->modifyLimitQuery($querySql, 20);
459
        self::assertEquals($alteredSql, $sql);
460
    }
461
462
    /**
463
     * @group DDC-1360
464
     */
465
    public function testQuoteIdentifier() : void
466
    {
467
        self::assertEquals('[fo][o]', $this->platform->quoteIdentifier('fo]o'));
468
        self::assertEquals('[test]', $this->platform->quoteIdentifier('test'));
469
        self::assertEquals('[test].[test]', $this->platform->quoteIdentifier('test.test'));
470
    }
471
472
    /**
473
     * @group DDC-1360
474
     */
475
    public function testQuoteSingleIdentifier() : void
476
    {
477
        self::assertEquals('[fo][o]', $this->platform->quoteSingleIdentifier('fo]o'));
478
        self::assertEquals('[test]', $this->platform->quoteSingleIdentifier('test'));
479
        self::assertEquals('[test.test]', $this->platform->quoteSingleIdentifier('test.test'));
480
    }
481
482
    /**
483
     * @group DBAL-220
484
     */
485
    public function testCreateClusteredIndex() : void
486
    {
487
        $idx = new Index('idx', ['id']);
488
        $idx->addFlag('clustered');
489
        self::assertEquals('CREATE CLUSTERED INDEX idx ON tbl (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
490
    }
491
492
    /**
493
     * @group DBAL-220
494
     */
495
    public function testCreateNonClusteredPrimaryKeyInTable() : void
496
    {
497
        $table = new Table('tbl');
498
        $table->addColumn('id', 'integer');
499
        $table->setPrimaryKey(['id']);
500
        $table->getIndex('primary')->addFlag('nonclustered');
501
502
        self::assertEquals(['CREATE TABLE tbl (id INT NOT NULL, PRIMARY KEY NONCLUSTERED (id))'], $this->platform->getCreateTableSQL($table));
503
    }
504
505
    /**
506
     * @group DBAL-220
507
     */
508
    public function testCreateNonClusteredPrimaryKey() : void
509
    {
510
        $idx = new Index('idx', ['id'], false, true);
511
        $idx->addFlag('nonclustered');
512
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY NONCLUSTERED (id)', $this->platform->getCreatePrimaryKeySQL($idx, 'tbl'));
513
    }
514
515
    public function testAlterAddPrimaryKey() : void
516
    {
517
        $idx = new Index('idx', ['id'], false, true);
518
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
519
    }
520
521
    /**
522
     * {@inheritDoc}
523
     */
524
    protected function getQuotedColumnInPrimaryKeySQL() : array
525
    {
526
        return ['CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, PRIMARY KEY ([create]))'];
527
    }
528
529
    /**
530
     * {@inheritDoc}
531
     */
532
    protected function getQuotedColumnInIndexSQL() : array
533
    {
534
        return [
535
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL)',
536
            'CREATE INDEX IDX_22660D028FD6E0FB ON [quoted] ([create])',
537
        ];
538
    }
539
540
    /**
541
     * {@inheritDoc}
542
     */
543
    protected function getQuotedNameInIndexSQL() : array
544
    {
545
        return [
546
            'CREATE TABLE test (column1 NVARCHAR(255) NOT NULL)',
547
            'CREATE INDEX [key] ON test (column1)',
548
        ];
549
    }
550
551
    /**
552
     * {@inheritDoc}
553
     */
554
    protected function getQuotedColumnInForeignKeySQL() : array
555
    {
556
        return [
557
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, foo NVARCHAR(255) NOT NULL, [bar] NVARCHAR(255) NOT NULL)',
558
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES [foreign] ([create], bar, [foo-bar])',
559
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES foo ([create], bar, [foo-bar])',
560
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ([create], foo, [bar]) REFERENCES [foo-bar] ([create], bar, [foo-bar])',
561
        ];
562
    }
563
564
    public function testGetCreateSchemaSQL() : void
565
    {
566
        $schemaName = 'schema';
567
        $sql        = $this->platform->getCreateSchemaSQL($schemaName);
568
        self::assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
569
    }
570
571
    public function testCreateTableWithSchemaColumnComments() : void
572
    {
573
        $table = new Table('testschema.test');
574
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
575
        $table->setPrimaryKey(['id']);
576
577
        $expectedSql = [
578
            'CREATE TABLE testschema.test (id INT NOT NULL, PRIMARY KEY (id))',
579
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'testschema', N'TABLE', 'test', N'COLUMN', id",
580
        ];
581
582
        self::assertEquals($expectedSql, $this->platform->getCreateTableSQL($table));
583
    }
584
585
    public function testAlterTableWithSchemaColumnComments() : void
586
    {
587
        $tableDiff                        = new TableDiff('testschema.mytable');
588
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
589
590
        $expectedSql = [
591
            'ALTER TABLE testschema.mytable ADD quota INT NOT NULL',
592
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota",
593
        ];
594
595
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
596
    }
597
598
    public function testAlterTableWithSchemaDropColumnComments() : void
599
    {
600
        $tableDiff                          = new TableDiff('testschema.mytable');
601
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
602
            'quota',
603
            new Column('quota', Type::getType('integer'), []),
604
            ['comment'],
605
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
606
        );
607
608
        $expectedSql = ["EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];
609
610
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
611
    }
612
613
    public function testAlterTableWithSchemaUpdateColumnComments() : void
614
    {
615
        $tableDiff                          = new TableDiff('testschema.mytable');
616
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
617
            'quota',
618
            new Column('quota', Type::getType('integer'), ['comment' => 'B comment']),
619
            ['comment'],
620
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
621
        );
622
623
        $expectedSql = ["EXEC sp_updateextendedproperty N'MS_Description', N'B comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];
624
625
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
626
    }
627
628
    /**
629
     * {@inheritDoc}
630
     *
631
     * @group DBAL-543
632
     */
633
    public function getCreateTableColumnCommentsSQL() : array
634
    {
635
        return [
636
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY (id))',
637
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', id",
638
        ];
639
    }
640
641
    /**
642
     * {@inheritDoc}
643
     *
644
     * @group DBAL-543
645
     */
646
    public function getAlterTableColumnCommentsSQL() : array
647
    {
648
        return [
649
            'ALTER TABLE mytable ADD quota INT NOT NULL',
650
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', quota",
651
            // todo
652
            //"EXEC sp_addextendedproperty N'MS_Description', N'B comment', N'SCHEMA', dbo, N'TABLE', mytable, N'COLUMN', baz",
653
        ];
654
    }
655
656
    /**
657
     * {@inheritDoc}
658
     *
659
     * @group DBAL-543
660
     */
661
    public function getCreateTableColumnTypeCommentsSQL() : array
662
    {
663
        return [
664
            'CREATE TABLE test (id INT NOT NULL, data VARCHAR(MAX) NOT NULL, PRIMARY KEY (id))',
665
            "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', data",
666
        ];
667
    }
668
669
    /**
670
     * @group DBAL-543
671
     */
672
    public function testGeneratesCreateTableSQLWithColumnComments() : void
673
    {
674
        $table = new Table('mytable');
675
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
676
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
677
        $table->addColumn('comment_false', 'integer', ['comment' => false]);
678
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
679
        $table->addColumn('comment_integer_0', 'integer', ['comment' => 0]);
680
        $table->addColumn('comment_float_0', 'integer', ['comment' => 0.0]);
681
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
682
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
683
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
684
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
685
        $table->addColumn('commented_type', 'object');
686
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
687
        $table->addColumn('comment_with_string_literal_char', 'string', ['comment' => "O'Reilly"]);
688
        $table->setPrimaryKey(['id']);
689
690
        self::assertEquals(
691
            [
692
                'CREATE TABLE mytable (id INT IDENTITY NOT NULL, comment_null INT NOT NULL, comment_false INT NOT NULL, comment_empty_string INT NOT NULL, comment_integer_0 INT NOT NULL, comment_float_0 INT NOT NULL, comment_string_0 INT NOT NULL, comment INT NOT NULL, [comment_quoted] INT NOT NULL, [create] INT NOT NULL, commented_type VARCHAR(MAX) NOT NULL, commented_type_with_comment VARCHAR(MAX) NOT NULL, comment_with_string_literal_char NVARCHAR(255) NOT NULL, PRIMARY KEY (id))',
693
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_integer_0",
694
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_float_0",
695
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
696
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz you!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
697
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for explicitly quoted columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
698
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for reserved keyword columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
699
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
700
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine array type.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
701
                "EXEC sp_addextendedproperty N'MS_Description', N'O''Reilly', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
702
            ],
703
            $this->platform->getCreateTableSQL($table)
704
        );
705
    }
706
707
    /**
708
     * @group DBAL-543
709
     * @group DBAL-1011
710
     */
711
    public function testGeneratesAlterTableSQLWithColumnComments() : void
712
    {
713
        $table = new Table('mytable');
714
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
715
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
716
        $table->addColumn('comment_false', 'integer', ['comment' => false]);
717
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
718
        $table->addColumn('comment_integer_0', 'integer', ['comment' => 0]);
719
        $table->addColumn('comment_float_0', 'integer', ['comment' => 0.0]);
720
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
721
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
722
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
723
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
724
        $table->addColumn('commented_type', 'object');
725
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
726
        $table->addColumn('comment_with_string_literal_quote_char', 'array', ['comment' => "O'Reilly"]);
727
        $table->setPrimaryKey(['id']);
728
729
        $tableDiff                                                         = new TableDiff('mytable');
730
        $tableDiff->fromTable                                              = $table;
731
        $tableDiff->addedColumns['added_comment_none']                     = new Column('added_comment_none', Type::getType('integer'));
732
        $tableDiff->addedColumns['added_comment_null']                     = new Column('added_comment_null', Type::getType('integer'), ['comment' => null]);
733
        $tableDiff->addedColumns['added_comment_false']                    = new Column('added_comment_false', Type::getType('integer'), ['comment' => false]);
734
        $tableDiff->addedColumns['added_comment_empty_string']             = new Column('added_comment_empty_string', Type::getType('integer'), ['comment' => '']);
735
        $tableDiff->addedColumns['added_comment_integer_0']                = new Column('added_comment_integer_0', Type::getType('integer'), ['comment' => 0]);
736
        $tableDiff->addedColumns['added_comment_float_0']                  = new Column('added_comment_float_0', Type::getType('integer'), ['comment' => 0.0]);
737
        $tableDiff->addedColumns['added_comment_string_0']                 = new Column('added_comment_string_0', Type::getType('integer'), ['comment' => '0']);
738
        $tableDiff->addedColumns['added_comment']                          = new Column('added_comment', Type::getType('integer'), ['comment' => 'Doctrine']);
739
        $tableDiff->addedColumns['`added_comment_quoted`']                 = new Column('`added_comment_quoted`', Type::getType('integer'), ['comment' => 'rulez']);
740
        $tableDiff->addedColumns['select']                                 = new Column('select', Type::getType('integer'), ['comment' => '666']);
741
        $tableDiff->addedColumns['added_commented_type']                   = new Column('added_commented_type', Type::getType('object'));
742
        $tableDiff->addedColumns['added_commented_type_with_comment']      = new Column('added_commented_type_with_comment', Type::getType('array'), ['comment' => '666']);
743
        $tableDiff->addedColumns['added_comment_with_string_literal_char'] = new Column('added_comment_with_string_literal_char', Type::getType('string'), ['comment' => "''"]);
744
745
        $tableDiff->renamedColumns['comment_float_0'] = new Column('comment_double_0', Type::getType('decimal'), ['comment' => 'Double for real!']);
746
747
        // Add comment to non-commented column.
748
        $tableDiff->changedColumns['id'] = new ColumnDiff(
749
            'id',
750
            new Column('id', Type::getType('integer'), ['autoincrement' => true, 'comment' => 'primary']),
751
            ['comment'],
752
            new Column('id', Type::getType('integer'), ['autoincrement' => true])
753
        );
754
755
        // Remove comment from null-commented column.
756
        $tableDiff->changedColumns['comment_null'] = new ColumnDiff(
757
            'comment_null',
758
            new Column('comment_null', Type::getType('string')),
759
            ['type'],
760
            new Column('comment_null', Type::getType('integer'), ['comment' => null])
761
        );
762
763
        // Add comment to false-commented column.
764
        $tableDiff->changedColumns['comment_false'] = new ColumnDiff(
765
            'comment_false',
766
            new Column('comment_false', Type::getType('integer'), ['comment' => 'false']),
767
            ['comment'],
768
            new Column('comment_false', Type::getType('integer'), ['comment' => false])
769
        );
770
771
        // Change type to custom type from empty string commented column.
772
        $tableDiff->changedColumns['comment_empty_string'] = new ColumnDiff(
773
            'comment_empty_string',
774
            new Column('comment_empty_string', Type::getType('object')),
775
            ['type'],
776
            new Column('comment_empty_string', Type::getType('integer'), ['comment' => ''])
777
        );
778
779
        // Change comment to false-comment from zero-string commented column.
780
        $tableDiff->changedColumns['comment_string_0'] = new ColumnDiff(
781
            'comment_string_0',
782
            new Column('comment_string_0', Type::getType('integer'), ['comment' => false]),
783
            ['comment'],
784
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '0'])
785
        );
786
787
        // Remove comment from regular commented column.
788
        $tableDiff->changedColumns['comment'] = new ColumnDiff(
789
            'comment',
790
            new Column('comment', Type::getType('integer')),
791
            ['comment'],
792
            new Column('comment', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
793
        );
794
795
        // Change comment and change type to custom type from regular commented column.
796
        $tableDiff->changedColumns['`comment_quoted`'] = new ColumnDiff(
797
            '`comment_quoted`',
798
            new Column('`comment_quoted`', Type::getType('array'), ['comment' => 'Doctrine array.']),
799
            ['comment', 'type'],
800
            new Column('`comment_quoted`', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
801
        );
802
803
        // Remove comment and change type to custom type from regular commented column.
804
        $tableDiff->changedColumns['create'] = new ColumnDiff(
805
            'create',
806
            new Column('create', Type::getType('object')),
807
            ['comment', 'type'],
808
            new Column('create', Type::getType('integer'), ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!'])
809
        );
810
811
        // Add comment and change custom type to regular type from non-commented column.
812
        $tableDiff->changedColumns['commented_type'] = new ColumnDiff(
813
            'commented_type',
814
            new Column('commented_type', Type::getType('integer'), ['comment' => 'foo']),
815
            ['comment', 'type'],
816
            new Column('commented_type', Type::getType('object'))
817
        );
818
819
        // Remove comment from commented custom type column.
820
        $tableDiff->changedColumns['commented_type_with_comment'] = new ColumnDiff(
821
            'commented_type_with_comment',
822
            new Column('commented_type_with_comment', Type::getType('array')),
823
            ['comment'],
824
            new Column('commented_type_with_comment', Type::getType('array'), ['comment' => 'Doctrine array type.'])
825
        );
826
827
        // Change comment from comment with string literal char column.
828
        $tableDiff->changedColumns['comment_with_string_literal_char'] = new ColumnDiff(
829
            'comment_with_string_literal_char',
830
            new Column('comment_with_string_literal_char', Type::getType('string'), ['comment' => "'"]),
831
            ['comment'],
832
            new Column('comment_with_string_literal_char', Type::getType('array'), ['comment' => "O'Reilly"])
833
        );
834
835
        $tableDiff->removedColumns['comment_integer_0'] = new Column('comment_integer_0', Type::getType('integer'), ['comment' => 0]);
836
837
        self::assertEquals(
838
            [
839
                // Renamed columns.
840
                "sp_RENAME 'mytable.comment_float_0', 'comment_double_0', 'COLUMN'",
841
842
                // Added columns.
843
                'ALTER TABLE mytable ADD added_comment_none INT NOT NULL',
844
                'ALTER TABLE mytable ADD added_comment_null INT NOT NULL',
845
                'ALTER TABLE mytable ADD added_comment_false INT NOT NULL',
846
                'ALTER TABLE mytable ADD added_comment_empty_string INT NOT NULL',
847
                'ALTER TABLE mytable ADD added_comment_integer_0 INT NOT NULL',
848
                'ALTER TABLE mytable ADD added_comment_float_0 INT NOT NULL',
849
                'ALTER TABLE mytable ADD added_comment_string_0 INT NOT NULL',
850
                'ALTER TABLE mytable ADD added_comment INT NOT NULL',
851
                'ALTER TABLE mytable ADD [added_comment_quoted] INT NOT NULL',
852
                'ALTER TABLE mytable ADD [select] INT NOT NULL',
853
                'ALTER TABLE mytable ADD added_commented_type VARCHAR(MAX) NOT NULL',
854
                'ALTER TABLE mytable ADD added_commented_type_with_comment VARCHAR(MAX) NOT NULL',
855
                'ALTER TABLE mytable ADD added_comment_with_string_literal_char NVARCHAR(255) NOT NULL',
856
                'ALTER TABLE mytable DROP COLUMN comment_integer_0',
857
                'ALTER TABLE mytable ALTER COLUMN comment_null NVARCHAR(255) NOT NULL',
858
                'ALTER TABLE mytable ALTER COLUMN comment_empty_string VARCHAR(MAX) NOT NULL',
859
                'ALTER TABLE mytable ALTER COLUMN [comment_quoted] VARCHAR(MAX) NOT NULL',
860
                'ALTER TABLE mytable ALTER COLUMN [create] VARCHAR(MAX) NOT NULL',
861
                'ALTER TABLE mytable ALTER COLUMN commented_type INT NOT NULL',
862
863
                // Added columns.
864
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_integer_0",
865
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_float_0",
866
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_string_0",
867
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment",
868
                "EXEC sp_addextendedproperty N'MS_Description', N'rulez', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [added_comment_quoted]",
869
                "EXEC sp_addextendedproperty N'MS_Description', N'666', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [select]",
870
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type",
871
                "EXEC sp_addextendedproperty N'MS_Description', N'666(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type_with_comment",
872
                "EXEC sp_addextendedproperty N'MS_Description', N'''''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_with_string_literal_char",
873
874
                // Changed columns.
875
                "EXEC sp_addextendedproperty N'MS_Description', N'primary', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', id",
876
                "EXEC sp_addextendedproperty N'MS_Description', N'false', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_false",
877
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_empty_string",
878
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
879
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
880
                "EXEC sp_updateextendedproperty N'MS_Description', N'Doctrine array.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
881
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
882
                "EXEC sp_updateextendedproperty N'MS_Description', N'foo', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
883
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
884
                "EXEC sp_updateextendedproperty N'MS_Description', N'''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
885
            ],
886
            $this->platform->getAlterTableSQL($tableDiff)
887
        );
888
    }
889
890
    /**
891
     * @group DBAL-122
892
     */
893
    public function testInitializesDoctrineTypeMappings() : void
894
    {
895
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bigint'));
896
        self::assertSame('bigint', $this->platform->getDoctrineTypeMapping('bigint'));
897
898
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('numeric'));
899
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('numeric'));
900
901
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bit'));
902
        self::assertSame('boolean', $this->platform->getDoctrineTypeMapping('bit'));
903
904
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallint'));
905
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('smallint'));
906
907
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('decimal'));
908
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('decimal'));
909
910
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallmoney'));
911
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('smallmoney'));
912
913
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('int'));
914
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('int'));
915
916
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tinyint'));
917
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('tinyint'));
918
919
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('money'));
920
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('money'));
921
922
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('float'));
923
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('float'));
924
925
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real'));
926
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('real'));
927
928
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double'));
929
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double'));
930
931
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double precision'));
932
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double precision'));
933
934
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smalldatetime'));
935
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('smalldatetime'));
936
937
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('datetime'));
938
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('datetime'));
939
940
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('char'));
941
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('char'));
942
943
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varchar'));
944
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('varchar'));
945
946
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('text'));
947
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('text'));
948
949
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nchar'));
950
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nchar'));
951
952
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nvarchar'));
953
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nvarchar'));
954
955
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('ntext'));
956
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('ntext'));
957
958
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('binary'));
959
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('binary'));
960
961
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varbinary'));
962
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('varbinary'));
963
964
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('image'));
965
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('image'));
966
967
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('uniqueidentifier'));
968
        self::assertSame('guid', $this->platform->getDoctrineTypeMapping('uniqueidentifier'));
969
    }
970
971
    protected function getBinaryMaxLength() : int
972
    {
973
        return 8000;
974
    }
975
976
    public function testReturnsBinaryTypeDeclarationSQL() : void
977
    {
978
        self::assertSame('VARBINARY(255)', $this->platform->getBinaryTypeDeclarationSQL([]));
979
        self::assertSame('VARBINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0]));
980
        self::assertSame('VARBINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 8000]));
981
982
        self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true]));
983
        self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0]));
984
        self::assertSame('BINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 8000]));
985
    }
986
987
    /**
988
     * @group legacy
989
     * @expectedDeprecation Binary field length 8001 is greater than supported by the platform (8000). Reduce the field length or use a BLOB field instead.
990
     */
991
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL() : void
992
    {
993
        self::assertSame('VARBINARY(MAX)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 8001]));
994
        self::assertSame('VARBINARY(MAX)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 8001]));
995
    }
996
997
    /**
998
     * {@inheritDoc}
999
     *
1000
     * @group DBAL-234
1001
     */
1002
    protected function getAlterTableRenameIndexSQL() : array
1003
    {
1004
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_bar', N'INDEX'"];
1005
    }
1006
1007
    /**
1008
     * {@inheritDoc}
1009
     *
1010
     * @group DBAL-234
1011
     */
1012
    protected function getQuotedAlterTableRenameIndexSQL() : array
1013
    {
1014
        return [
1015
            "EXEC sp_RENAME N'[table].[create]', N'[select]', N'INDEX'",
1016
            "EXEC sp_RENAME N'[table].[foo]', N'[bar]', N'INDEX'",
1017
        ];
1018
    }
1019
1020
    /**
1021
     * @group DBAL-825
1022
     */
1023
    public function testChangeColumnsTypeWithDefaultValue() : void
1024
    {
1025
        $tableName = 'column_def_change_type';
1026
        $table     = new Table($tableName);
1027
1028
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
1029
        $table->addColumn('col_string', 'string', ['default' => 'foo']);
1030
1031
        $tableDiff                            = new TableDiff($tableName);
1032
        $tableDiff->fromTable                 = $table;
1033
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
1034
            'col_int',
1035
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
1036
            ['type'],
1037
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
1038
        );
1039
1040
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
1041
            'col_string',
1042
            new Column('col_string', Type::getType('string'), ['default' => 666, 'fixed' => true]),
1043
            ['fixed'],
1044
            new Column('col_string', Type::getType('string'), ['default' => 666])
1045
        );
1046
1047
        $expected = $this->platform->getAlterTableSQL($tableDiff);
1048
1049
        self::assertSame(
1050
            $expected,
1051
            [
1052
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_FA2CB292',
1053
                'ALTER TABLE column_def_change_type ALTER COLUMN col_int INT NOT NULL',
1054
                'ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_FA2CB292 DEFAULT 666 FOR col_int',
1055
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_2725A6D0',
1056
                'ALTER TABLE column_def_change_type ALTER COLUMN col_string NCHAR(255) NOT NULL',
1057
                "ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_2725A6D0 DEFAULT '666' FOR col_string",
1058
            ]
1059
        );
1060
    }
1061
1062
    /**
1063
     * {@inheritdoc}
1064
     */
1065
    protected function getQuotedAlterTableRenameColumnSQL() : array
1066
    {
1067
        return [
1068
            "sp_RENAME 'mytable.unquoted1', 'unquoted', 'COLUMN'",
1069
            "sp_RENAME 'mytable.unquoted2', '[where]', 'COLUMN'",
1070
            "sp_RENAME 'mytable.unquoted3', '[foo]', 'COLUMN'",
1071
            "sp_RENAME 'mytable.[create]', 'reserved_keyword', 'COLUMN'",
1072
            "sp_RENAME 'mytable.[table]', '[from]', 'COLUMN'",
1073
            "sp_RENAME 'mytable.[select]', '[bar]', 'COLUMN'",
1074
            "sp_RENAME 'mytable.quoted1', 'quoted', 'COLUMN'",
1075
            "sp_RENAME 'mytable.quoted2', '[and]', 'COLUMN'",
1076
            "sp_RENAME 'mytable.quoted3', '[baz]', 'COLUMN'",
1077
        ];
1078
    }
1079
1080
    /**
1081
     * {@inheritdoc}
1082
     */
1083
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
1084
    {
1085
        $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...
1086
    }
1087
1088
    /**
1089
     * {@inheritDoc}
1090
     *
1091
     * @group DBAL-807
1092
     */
1093
    protected function getAlterTableRenameIndexInSchemaSQL() : array
1094
    {
1095
        return ["EXEC sp_RENAME N'myschema.mytable.idx_foo', N'idx_bar', N'INDEX'"];
1096
    }
1097
1098
    /**
1099
     * {@inheritDoc}
1100
     *
1101
     * @group DBAL-807
1102
     */
1103
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
1104
    {
1105
        return [
1106
            "EXEC sp_RENAME N'[schema].[table].[create]', N'[select]', N'INDEX'",
1107
            "EXEC sp_RENAME N'[schema].[table].[foo]', N'[bar]', N'INDEX'",
1108
        ];
1109
    }
1110
1111
    protected function getQuotesDropForeignKeySQL() : string
1112
    {
1113
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
1114
    }
1115
1116
    protected function getQuotesDropConstraintSQL() : string
1117
    {
1118
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
1119
    }
1120
1121
    /**
1122
     * @param mixed[] $column
1123
     *
1124
     * @dataProvider getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL
1125
     * @group DBAL-830
1126
     */
1127
    public function testGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL(string $table, array $column, string $expectedSql) : void
1128
    {
1129
        self::assertSame($expectedSql, $this->platform->getDefaultConstraintDeclarationSQL($table, $column));
0 ignored issues
show
Bug introduced by
The method getDefaultConstraintDeclarationSQL() does not exist on Doctrine\DBAL\Platforms\AbstractPlatform. It seems like you code against a sub-type of Doctrine\DBAL\Platforms\AbstractPlatform such as Doctrine\DBAL\Platforms\SQLServerPlatform. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1129
        self::assertSame($expectedSql, $this->platform->/** @scrutinizer ignore-call */ getDefaultConstraintDeclarationSQL($table, $column));
Loading history...
1130
    }
1131
1132
    /**
1133
     * @return mixed[][]
1134
     */
1135
    public static function getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL() : iterable
1136
    {
1137
        return [
1138
            // Unquoted identifiers non-reserved keywords.
1139
            ['mytable', ['name' => 'mycolumn', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn"],
1140
            // Quoted identifiers non-reserved keywords.
1141
            ['`mytable`', ['name' => '`mycolumn`', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]"],
1142
            // Unquoted identifiers reserved keywords.
1143
            ['table', ['name' => 'select', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1144
            // Quoted identifiers reserved keywords.
1145
            ['`table`', ['name' => '`select`', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1146
        ];
1147
    }
1148
1149
    /**
1150
     * @param string[] $expectedSql
1151
     *
1152
     * @dataProvider getGeneratesIdentifierNamesInCreateTableSQL
1153
     * @group DBAL-830
1154
     */
1155
    public function testGeneratesIdentifierNamesInCreateTableSQL(Table $table, array $expectedSql) : void
1156
    {
1157
        self::assertSame($expectedSql, $this->platform->getCreateTableSQL($table));
1158
    }
1159
1160
    /**
1161
     * @return mixed[][]
1162
     */
1163
    public static function getGeneratesIdentifierNamesInCreateTableSQL() : iterable
1164
    {
1165
        return [
1166
            // Unquoted identifiers non-reserved keywords.
1167
            [
1168
                new Table('mytable', [new Column('mycolumn', Type::getType('string'), ['default' => 'foo'])]),
1169
                [
1170
                    'CREATE TABLE mytable (mycolumn NVARCHAR(255) NOT NULL)',
1171
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn",
1172
                ],
1173
            ],
1174
            // Quoted identifiers reserved keywords.
1175
            [
1176
                new Table('`mytable`', [new Column('`mycolumn`', Type::getType('string'), ['default' => 'foo'])]),
1177
                [
1178
                    'CREATE TABLE [mytable] ([mycolumn] NVARCHAR(255) NOT NULL)',
1179
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]",
1180
                ],
1181
            ],
1182
            // Unquoted identifiers reserved keywords.
1183
            [
1184
                new Table('table', [new Column('select', Type::getType('string'), ['default' => 'foo'])]),
1185
                [
1186
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
1187
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
1188
                ],
1189
            ],
1190
            // Quoted identifiers reserved keywords.
1191
            [
1192
                new Table('`table`', [new Column('`select`', Type::getType('string'), ['default' => 'foo'])]),
1193
                [
1194
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
1195
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
1196
                ],
1197
            ],
1198
        ];
1199
    }
1200
1201
    /**
1202
     * @param string[] $expectedSql
1203
     *
1204
     * @dataProvider getGeneratesIdentifierNamesInAlterTableSQL
1205
     * @group DBAL-830
1206
     */
1207
    public function testGeneratesIdentifierNamesInAlterTableSQL(TableDiff $tableDiff, array $expectedSql) : void
1208
    {
1209
        self::assertSame($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
1210
    }
1211
1212
    /**
1213
     * @return mixed[][]
1214
     */
1215
    public static function getGeneratesIdentifierNamesInAlterTableSQL() : iterable
1216
    {
1217
        return [
1218
            // Unquoted identifiers non-reserved keywords.
1219
            [
1220
                new TableDiff(
1221
                    'mytable',
1222
                    [new Column('addcolumn', Type::getType('string'), ['default' => 'foo'])],
1223
                    [
1224
                        'mycolumn' => new ColumnDiff(
1225
                            'mycolumn',
1226
                            new Column('mycolumn', Type::getType('string'), ['default' => 'bar']),
1227
                            ['default'],
1228
                            new Column('mycolumn', Type::getType('string'), ['default' => 'foo'])
1229
                        ),
1230
                    ],
1231
                    [new Column('removecolumn', Type::getType('string'), ['default' => 'foo'])]
1232
                ),
1233
                [
1234
                    'ALTER TABLE mytable ADD addcolumn NVARCHAR(255) NOT NULL',
1235
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR addcolumn",
1236
                    'ALTER TABLE mytable DROP COLUMN removecolumn',
1237
                    'ALTER TABLE mytable DROP CONSTRAINT DF_6B2BD609_9BADD926',
1238
                    'ALTER TABLE mytable ALTER COLUMN mycolumn NVARCHAR(255) NOT NULL',
1239
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR mycolumn",
1240
                ],
1241
            ],
1242
            // Quoted identifiers non-reserved keywords.
1243
            [
1244
                new TableDiff(
1245
                    '`mytable`',
1246
                    [new Column('`addcolumn`', Type::getType('string'), ['default' => 'foo'])],
1247
                    [
1248
                        'mycolumn' => new ColumnDiff(
1249
                            '`mycolumn`',
1250
                            new Column('`mycolumn`', Type::getType('string'), ['default' => 'bar']),
1251
                            ['default'],
1252
                            new Column('`mycolumn`', Type::getType('string'), ['default' => 'foo'])
1253
                        ),
1254
                    ],
1255
                    [new Column('`removecolumn`', Type::getType('string'), ['default' => 'foo'])]
1256
                ),
1257
                [
1258
                    'ALTER TABLE [mytable] ADD [addcolumn] NVARCHAR(255) NOT NULL',
1259
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR [addcolumn]",
1260
                    'ALTER TABLE [mytable] DROP COLUMN [removecolumn]',
1261
                    'ALTER TABLE [mytable] DROP CONSTRAINT DF_6B2BD609_9BADD926',
1262
                    'ALTER TABLE [mytable] ALTER COLUMN [mycolumn] NVARCHAR(255) NOT NULL',
1263
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR [mycolumn]",
1264
                ],
1265
            ],
1266
            // Unquoted identifiers reserved keywords.
1267
            [
1268
                new TableDiff(
1269
                    'table',
1270
                    [new Column('add', Type::getType('string'), ['default' => 'foo'])],
1271
                    [
1272
                        'select' => new ColumnDiff(
1273
                            'select',
1274
                            new Column('select', Type::getType('string'), ['default' => 'bar']),
1275
                            ['default'],
1276
                            new Column('select', Type::getType('string'), ['default' => 'foo'])
1277
                        ),
1278
                    ],
1279
                    [new Column('drop', Type::getType('string'), ['default' => 'foo'])]
1280
                ),
1281
                [
1282
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
1283
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
1284
                    'ALTER TABLE [table] DROP COLUMN [drop]',
1285
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
1286
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
1287
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
1288
                ],
1289
            ],
1290
            // Quoted identifiers reserved keywords.
1291
            [
1292
                new TableDiff(
1293
                    '`table`',
1294
                    [new Column('`add`', Type::getType('string'), ['default' => 'foo'])],
1295
                    [
1296
                        'select' => new ColumnDiff(
1297
                            '`select`',
1298
                            new Column('`select`', Type::getType('string'), ['default' => 'bar']),
1299
                            ['default'],
1300
                            new Column('`select`', Type::getType('string'), ['default' => 'foo'])
1301
                        ),
1302
                    ],
1303
                    [new Column('`drop`', Type::getType('string'), ['default' => 'foo'])]
1304
                ),
1305
                [
1306
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
1307
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
1308
                    'ALTER TABLE [table] DROP COLUMN [drop]',
1309
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
1310
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
1311
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
1312
                ],
1313
            ],
1314
        ];
1315
    }
1316
1317
    /**
1318
     * @group DBAL-423
1319
     */
1320
    public function testReturnsGuidTypeDeclarationSQL() : void
1321
    {
1322
        self::assertSame('UNIQUEIDENTIFIER', $this->platform->getGuidTypeDeclarationSQL([]));
1323
    }
1324
1325
    /**
1326
     * {@inheritdoc}
1327
     */
1328
    public function getAlterTableRenameColumnSQL() : array
1329
    {
1330
        return [
1331
            "sp_RENAME 'foo.bar', 'baz', 'COLUMN'",
1332
            'ALTER TABLE foo DROP CONSTRAINT DF_8C736521_76FF8CAA',
1333
            'ALTER TABLE foo ADD CONSTRAINT DF_8C736521_78240498 DEFAULT 666 FOR baz',
1334
        ];
1335
    }
1336
1337
    /**
1338
     * {@inheritdoc}
1339
     */
1340
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
1341
    {
1342
        return [
1343
            'ALTER TABLE [foo] DROP CONSTRAINT fk1',
1344
            'ALTER TABLE [foo] DROP CONSTRAINT fk2',
1345
            "sp_RENAME '[foo].id', 'war', 'COLUMN'",
1346
            'ALTER TABLE [foo] ADD bloo INT NOT NULL',
1347
            'ALTER TABLE [foo] DROP COLUMN baz',
1348
            'ALTER TABLE [foo] ALTER COLUMN bar INT',
1349
            "sp_RENAME '[foo]', 'table'",
1350
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
1351
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' + REPLACE(dc.name, '8C736521', 'F6298F46') + ''', " .
1352
            "''OBJECT'';' FROM sys.default_constraints dc JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " .
1353
            "WHERE tbl.name = 'table';EXEC sp_executesql @sql",
1354
            'ALTER TABLE [table] ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id)',
1355
            'ALTER TABLE [table] ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id)',
1356
        ];
1357
    }
1358
1359
    /**
1360
     * {@inheritdoc}
1361
     */
1362
    protected function getCommentOnColumnSQL() : array
1363
    {
1364
        return [
1365
            "COMMENT ON COLUMN foo.bar IS 'comment'",
1366
            "COMMENT ON COLUMN [Foo].[BAR] IS 'comment'",
1367
            "COMMENT ON COLUMN [select].[from] IS 'comment'",
1368
        ];
1369
    }
1370
1371
    /**
1372
     * {@inheritdoc}
1373
     */
1374
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
1375
    {
1376
        return [
1377
            ['CASCADE', 'CASCADE'],
1378
            ['SET NULL', 'SET NULL'],
1379
            ['NO ACTION', 'NO ACTION'],
1380
            ['RESTRICT', 'NO ACTION'],
1381
            ['SET DEFAULT', 'SET DEFAULT'],
1382
            ['CaScAdE', 'CASCADE'],
1383
        ];
1384
    }
1385
1386
    /**
1387
     * {@inheritdoc}
1388
     */
1389
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
1390
    {
1391
        return 'CONSTRAINT [select] UNIQUE (foo) WHERE foo IS NOT NULL';
1392
    }
1393
1394
    /**
1395
     * {@inheritdoc}
1396
     */
1397
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
1398
    {
1399
        return 'INDEX [select] (foo)';
1400
    }
1401
1402
    /**
1403
     * {@inheritdoc}
1404
     */
1405
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
1406
    {
1407
        return 'TRUNCATE TABLE [select]';
1408
    }
1409
1410
    /**
1411
     * {@inheritdoc}
1412
     */
1413
    protected function getAlterStringToFixedStringSQL() : array
1414
    {
1415
        return ['ALTER TABLE mytable ALTER COLUMN name NCHAR(2) NOT NULL'];
1416
    }
1417
1418
    /**
1419
     * {@inheritdoc}
1420
     */
1421
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
1422
    {
1423
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_foo_renamed', N'INDEX'"];
1424
    }
1425
1426
    public function testModifyLimitQueryWithTopNSubQueryWithOrderBy() : void
1427
    {
1428
        $querySql    = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC)';
1429
        $expectedSql = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC) ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1430
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1431
        self::assertEquals($expectedSql, $sql);
1432
1433
        $querySql    = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC) ORDER BY t.data2 DESC';
1434
        $expectedSql = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC) ORDER BY t.data2 DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1435
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1436
        self::assertEquals($expectedSql, $sql);
1437
    }
1438
1439
    public function testModifyLimitQueryWithFromSubquery() : void
1440
    {
1441
        $sql = $this->platform->modifyLimitQuery('SELECT DISTINCT id_0 FROM (SELECT k0_.id AS id_0 FROM key_measure k0_ WHERE (k0_.id_zone in(2))) dctrn_result', 10);
1442
1443
        $expected = 'SELECT DISTINCT id_0 FROM (SELECT k0_.id AS id_0 FROM key_measure k0_ WHERE (k0_.id_zone in(2))) dctrn_result ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1444
1445
        self::assertEquals($sql, $expected);
1446
    }
1447
1448
    public function testModifyLimitQueryWithFromSubqueryAndOrder() : void
1449
    {
1450
        $sql = $this->platform->modifyLimitQuery('SELECT DISTINCT id_0, value_1 FROM (SELECT k0_.id AS id_0, k0_.value AS value_1 FROM key_measure k0_ WHERE (k0_.id_zone in(2))) dctrn_result ORDER BY value_1 DESC', 10);
1451
1452
        $expected = 'SELECT DISTINCT id_0, value_1 FROM (SELECT k0_.id AS id_0, k0_.value AS value_1 FROM key_measure k0_ WHERE (k0_.id_zone in(2))) dctrn_result ORDER BY value_1 DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1453
1454
        self::assertEquals($sql, $expected);
1455
    }
1456
1457
    public function testModifyLimitQueryWithComplexOrderByExpression() : void
1458
    {
1459
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM table ORDER BY (table.x * table.y) DESC', 10);
1460
1461
        $expected = 'SELECT * FROM table ORDER BY (table.x * table.y) DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1462
1463
        self::assertEquals($sql, $expected);
1464
    }
1465
1466
    public function testModifyLimitQueryWithNewlineBeforeOrderBy() : void
1467
    {
1468
        $querySql    = "SELECT * FROM test\nORDER BY col DESC";
1469
        $expectedSql = "SELECT * FROM test\nORDER BY col DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY";
1470
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1471
        self::assertEquals($expectedSql, $sql);
1472
    }
1473
1474
    /**
1475
     * @group DBAL-2436
1476
     */
1477
    public function testQuotesTableNameInListTableColumnsSQL() : void
1478
    {
1479
        self::assertStringContainsStringIgnoringCase(
1480
            "'Foo''Bar\\'",
1481
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
1482
        );
1483
    }
1484
1485
    /**
1486
     * @group DBAL-2436
1487
     */
1488
    public function testQuotesSchemaNameInListTableColumnsSQL() : void
1489
    {
1490
        self::assertStringContainsStringIgnoringCase(
1491
            "'Foo''Bar\\'",
1492
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1493
        );
1494
    }
1495
1496
    /**
1497
     * @group DBAL-2436
1498
     */
1499
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
1500
    {
1501
        self::assertStringContainsStringIgnoringCase(
1502
            "'Foo''Bar\\'",
1503
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
1504
        );
1505
    }
1506
1507
    /**
1508
     * @group DBAL-2436
1509
     */
1510
    public function testQuotesSchemaNameInListTableForeignKeysSQL() : void
1511
    {
1512
        self::assertStringContainsStringIgnoringCase(
1513
            "'Foo''Bar\\'",
1514
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
1515
        );
1516
    }
1517
1518
    /**
1519
     * @group DBAL-2436
1520
     */
1521
    public function testQuotesTableNameInListTableIndexesSQL() : void
1522
    {
1523
        self::assertStringContainsStringIgnoringCase(
1524
            "'Foo''Bar\\'",
1525
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
1526
        );
1527
    }
1528
1529
    /**
1530
     * @group DBAL-2436
1531
     */
1532
    public function testQuotesSchemaNameInListTableIndexesSQL() : void
1533
    {
1534
        self::assertStringContainsStringIgnoringCase(
1535
            "'Foo''Bar\\'",
1536
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
1537
        );
1538
    }
1539
1540
    /**
1541
     * @group 2859
1542
     */
1543
    public function testGetDefaultValueDeclarationSQLForDateType() : void
1544
    {
1545
        $currentDateSql = $this->platform->getCurrentDateSQL();
1546
        foreach (['date', 'date_immutable'] as $type) {
1547
            $field = [
1548
                'type' => Type::getType($type),
1549
                'default' => $currentDateSql,
1550
            ];
1551
1552
            self::assertSame(
1553
                ' DEFAULT CONVERT(date, GETDATE())',
1554
                $this->platform->getDefaultValueDeclarationSQL($field)
1555
            );
1556
        }
1557
    }
1558
1559
    public function testSupportsColumnCollation() : void
1560
    {
1561
        self::assertTrue($this->platform->supportsColumnCollation());
1562
    }
1563
1564
    public function testColumnCollationDeclarationSQL() : void
1565
    {
1566
        self::assertSame(
1567
            'COLLATE Latin1_General_CS_AS_KS_WS',
1568
            $this->platform->getColumnCollationDeclarationSQL('Latin1_General_CS_AS_KS_WS')
1569
        );
1570
    }
1571
1572
    public function testGetCreateTableSQLWithColumnCollation() : void
1573
    {
1574
        $table = new Table('foo');
1575
        $table->addColumn('no_collation', 'string');
1576
        $table->addColumn('column_collation', 'string')->setPlatformOption('collation', 'Latin1_General_CS_AS_KS_WS');
1577
1578
        self::assertSame(
1579
            ['CREATE TABLE foo (no_collation NVARCHAR(255) NOT NULL, column_collation NVARCHAR(255) COLLATE Latin1_General_CS_AS_KS_WS NOT NULL)'],
1580
            $this->platform->getCreateTableSQL($table),
1581
            'Column "no_collation" will use the default collation from the table/database and "column_collation" overwrites the collation on this column'
1582
        );
1583
    }
1584
1585
    public function testSupportsSequences() : void
1586
    {
1587
        self::assertTrue($this->platform->supportsSequences());
1588
    }
1589
1590
    public function testDoesNotPreferSequences() : void
1591
    {
1592
        self::assertFalse($this->platform->prefersSequences());
1593
    }
1594
1595
    public function testGeneratesSequenceSqlCommands() : void
1596
    {
1597
        $sequence = new Sequence('myseq', 20, 1);
1598
        self::assertEquals(
1599
            'CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 20 MINVALUE 1',
1600
            $this->platform->getCreateSequenceSQL($sequence)
1601
        );
1602
        self::assertEquals(
1603
            'ALTER SEQUENCE myseq INCREMENT BY 20',
1604
            $this->platform->getAlterSequenceSQL($sequence)
1605
        );
1606
        self::assertEquals(
1607
            'DROP SEQUENCE myseq',
1608
            $this->platform->getDropSequenceSQL('myseq')
1609
        );
1610
        self::assertEquals(
1611
            'SELECT NEXT VALUE FOR myseq',
1612
            $this->platform->getSequenceNextValSQL('myseq')
1613
        );
1614
    }
1615
}
1616