testModifyLimitQuery()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Tests\Platforms;
6
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Exception\ColumnLengthRequired;
9
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
10
use Doctrine\DBAL\Schema\Column;
11
use Doctrine\DBAL\Schema\ColumnDiff;
12
use Doctrine\DBAL\Schema\Index;
13
use Doctrine\DBAL\Schema\Sequence;
14
use Doctrine\DBAL\Schema\Table;
15
use Doctrine\DBAL\Schema\TableDiff;
16
use Doctrine\DBAL\TransactionIsolationLevel;
17
use Doctrine\DBAL\Types\Type;
18
use function assert;
19
20
abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCase
21
{
22
    public function getGenerateTableSql() : string
23
    {
24
        return 'CREATE TABLE test (id INT IDENTITY NOT NULL, test NVARCHAR(255), PRIMARY KEY (id))';
25
    }
26
27
    /**
28
     * {@inheritDoc}
29
     */
30
    public function getGenerateTableWithMultiColumnUniqueIndexSql() : array
31
    {
32
        return [
33
            'CREATE TABLE test (foo NVARCHAR(255), bar NVARCHAR(255))',
34
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar) WHERE foo IS NOT NULL AND bar IS NOT NULL',
35
        ];
36
    }
37
38
    /**
39
     * {@inheritDoc}
40
     */
41
    public function getGenerateAlterTableSql() : array
42
    {
43
        return [
44
            'ALTER TABLE mytable ADD quota INT',
45
            'ALTER TABLE mytable DROP COLUMN foo',
46
            'ALTER TABLE mytable ALTER COLUMN baz NVARCHAR(255) NOT NULL',
47
            "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_78240498 DEFAULT 'def' FOR baz",
48
            'ALTER TABLE mytable ALTER COLUMN bloo BIT NOT NULL',
49
            "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_CECED971 DEFAULT '0' FOR bloo",
50
            "sp_RENAME 'mytable', 'userlist'",
51
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
52
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " .
53
            "+ REPLACE(dc.name, '6B2BD609', 'E2B58069') + ''', ''OBJECT'';' " .
54
            'FROM sys.default_constraints dc ' .
55
            'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' .
56
            "WHERE tbl.name = 'userlist';" .
57
            'EXEC sp_executesql @sql',
58
        ];
59
    }
60
61
    public function testDoesNotSupportRegexp() : void
62
    {
63
        $this->expectException(DBALException::class);
64
65
        $this->platform->getRegexpExpression();
66
    }
67
68
    public function testGeneratesSqlSnippets() : void
69
    {
70
        self::assertEquals('CONVERT(date, GETDATE())', $this->platform->getCurrentDateSQL());
71
        self::assertEquals('CONVERT(time, GETDATE())', $this->platform->getCurrentTimeSQL());
72
        self::assertEquals('CURRENT_TIMESTAMP', $this->platform->getCurrentTimestampSQL());
73
        self::assertEquals('"', $this->platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
74
        self::assertEquals('(column1 + column2 + column3)', $this->platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct');
75
    }
76
77
    public function testGeneratesTransactionsCommands() : void
78
    {
79
        self::assertEquals(
80
            'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED',
81
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
82
        );
83
        self::assertEquals(
84
            'SET TRANSACTION ISOLATION LEVEL READ COMMITTED',
85
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
86
        );
87
        self::assertEquals(
88
            'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ',
89
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
90
        );
91
        self::assertEquals(
92
            'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE',
93
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
94
        );
95
    }
96
97
    public function testGeneratesDDLSnippets() : void
98
    {
99
        $dropDatabaseExpectation = 'DROP DATABASE foobar';
100
101
        self::assertEquals('SELECT * FROM sys.databases', $this->platform->getListDatabasesSQL());
102
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
103
        self::assertEquals($dropDatabaseExpectation, $this->platform->getDropDatabaseSQL('foobar'));
104
        self::assertEquals('DROP TABLE foobar', $this->platform->getDropTableSQL('foobar'));
105
    }
106
107
    public function testGeneratesTypeDeclarationForIntegers() : void
108
    {
109
        self::assertEquals(
110
            'INT',
111
            $this->platform->getIntegerTypeDeclarationSQL([])
112
        );
113
        self::assertEquals(
114
            'INT IDENTITY',
115
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
116
        );
117
        self::assertEquals(
118
            'INT IDENTITY',
119
            $this->platform->getIntegerTypeDeclarationSQL(
120
                ['autoincrement' => true, 'primary' => true]
121
            )
122
        );
123
    }
124
125
    public function testGeneratesTypeDeclarationsForStrings() : void
126
    {
127
        self::assertSame('VARCHAR(MAX)', $this->platform->getClobTypeDeclarationSQL([]));
128
        self::assertSame(
129
            'VARCHAR(MAX)',
130
            $this->platform->getClobTypeDeclarationSQL(['length' => 5, 'fixed' => true])
131
        );
132
    }
133
134
    public function testPrefersIdentityColumns() : void
135
    {
136
        self::assertTrue($this->platform->prefersIdentityColumns());
137
    }
138
139
    public function testSupportsIdentityColumns() : void
140
    {
141
        self::assertTrue($this->platform->supportsIdentityColumns());
142
    }
143
144
    public function testSupportsCreateDropDatabase() : void
145
    {
146
        self::assertTrue($this->platform->supportsCreateDropDatabase());
147
    }
148
149
    public function testSupportsSchemas() : void
150
    {
151
        self::assertTrue($this->platform->supportsSchemas());
152
    }
153
154
    public function testDoesNotSupportSavePoints() : void
155
    {
156
        self::assertTrue($this->platform->supportsSavepoints());
157
    }
158
159
    public function getGenerateIndexSql() : string
160
    {
161
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
162
    }
163
164
    public function getGenerateUniqueIndexSql() : string
165
    {
166
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2) WHERE test IS NOT NULL AND test2 IS NOT NULL';
167
    }
168
169
    public function getGenerateForeignKeySql() : string
170
    {
171
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
172
    }
173
174
    public function testModifyLimitQuery() : void
175
    {
176
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10, 0);
177
        self::assertEquals('SELECT * FROM user ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
178
    }
179
180
    public function testModifyLimitQueryWithEmptyOffset() : void
181
    {
182
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user', 10);
183
        self::assertEquals('SELECT * FROM user ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
184
    }
185
186
    public function testModifyLimitQueryWithOffset() : void
187
    {
188
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10, 5);
189
        self::assertEquals('SELECT * FROM user ORDER BY username DESC OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
190
    }
191
192
    public function testModifyLimitQueryWithAscOrderBy() : void
193
    {
194
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username ASC', 10);
195
        self::assertEquals('SELECT * FROM user ORDER BY username ASC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
196
    }
197
198
    public function testModifyLimitQueryWithLowercaseOrderBy() : void
199
    {
200
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user order by username', 10);
201
        self::assertEquals('SELECT * FROM user order by username OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
202
    }
203
204
    public function testModifyLimitQueryWithDescOrderBy() : void
205
    {
206
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC', 10);
207
        self::assertEquals('SELECT * FROM user ORDER BY username DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
208
    }
209
210
    public function testModifyLimitQueryWithMultipleOrderBy() : void
211
    {
212
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM user ORDER BY username DESC, usereamil ASC', 10);
213
        self::assertEquals('SELECT * FROM user ORDER BY username DESC, usereamil ASC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
214
    }
215
216
    public function testModifyLimitQueryWithSubSelect() : void
217
    {
218
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result', 10);
219
        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);
220
    }
221
222
    public function testModifyLimitQueryWithSubSelectAndOrder() : void
223
    {
224
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result ORDER BY uname DESC', 10);
225
        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);
226
227
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC', 10);
228
        self::assertEquals('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
229
    }
230
231
    public function testModifyLimitQueryWithSubSelectAndMultipleOrder() : void
232
    {
233
        $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);
234
        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);
235
236
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id uid, u.name uname) dctrn_result ORDER BY uname DESC, uid ASC', 10, 5);
237
        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);
238
239
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM (SELECT u.id, u.name) dctrn_result ORDER BY name DESC, id ASC', 10, 5);
240
        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);
241
    }
242
243
    public function testModifyLimitQueryWithFromColumnNames() : void
244
    {
245
        $sql = $this->platform->modifyLimitQuery('SELECT a.fromFoo, fromBar FROM foo', 10);
246
        self::assertEquals('SELECT a.fromFoo, fromBar FROM foo ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY', $sql);
247
    }
248
249
    /**
250
     * @group DBAL-927
251
     */
252
    public function testModifyLimitQueryWithExtraLongQuery() : void
253
    {
254
        $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 ';
255
        $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) ';
256
        $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) ';
257
        $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)';
258
259
        $sql = $this->platform->modifyLimitQuery($query, 10);
260
261
        $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 ';
262
        $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) ';
263
        $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) ';
264
        $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) ';
265
        $expected .= 'ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
266
267
        self::assertEquals($expected, $sql);
268
    }
269
270
    /**
271
     * @group DDC-2470
272
     */
273
    public function testModifyLimitQueryWithOrderByClause() : void
274
    {
275
        $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';
276
        $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';
277
        $actual   = $this->platform->modifyLimitQuery($sql, 10, 5);
278
279
        self::assertEquals($expected, $actual);
280
    }
281
282
    /**
283
     * @group DBAL-713
284
     */
285
    public function testModifyLimitQueryWithSubSelectInSelectList() : void
286
    {
287
        $sql = $this->platform->modifyLimitQuery(
288
            'SELECT ' .
289
            'u.id, ' .
290
            '(u.foo/2) foodiv, ' .
291
            'CONCAT(u.bar, u.baz) barbaz, ' .
292
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
293
            'FROM user u ' .
294
            "WHERE u.status = 'disabled'",
295
            10
296
        );
297
298
        self::assertEquals(
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
            'ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
307
            $sql
308
        );
309
    }
310
311
    /**
312
     * @group DBAL-713
313
     */
314
    public function testModifyLimitQueryWithSubSelectInSelectListAndOrderByClause() : void
315
    {
316
        $sql = $this->platform->modifyLimitQuery(
317
            'SELECT ' .
318
            'u.id, ' .
319
            '(u.foo/2) foodiv, ' .
320
            'CONCAT(u.bar, u.baz) barbaz, ' .
321
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
322
            'FROM user u ' .
323
            "WHERE u.status = 'disabled' " .
324
            'ORDER BY u.username DESC',
325
            10,
326
            5
327
        );
328
329
        self::assertEquals(
330
            'SELECT ' .
331
            'u.id, ' .
332
            '(u.foo/2) foodiv, ' .
333
            'CONCAT(u.bar, u.baz) barbaz, ' .
334
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
335
            'FROM user u ' .
336
            "WHERE u.status = 'disabled' " .
337
            'ORDER BY u.username DESC ' .
338
            'OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY',
339
            $sql
340
        );
341
    }
342
343
    /**
344
     * @group DBAL-834
345
     */
346
    public function testModifyLimitQueryWithAggregateFunctionInOrderByClause() : void
347
    {
348
        $sql = $this->platform->modifyLimitQuery(
349
            'SELECT ' .
350
            'MAX(heading_id) aliased, ' .
351
            'code ' .
352
            'FROM operator_model_operator ' .
353
            'GROUP BY code ' .
354
            'ORDER BY MAX(heading_id) DESC',
355
            1,
356
            0
357
        );
358
359
        self::assertEquals(
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
            'OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
367
            $sql
368
        );
369
    }
370
371
    /**
372
     * @throws DBALException
373
     */
374
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBaseTable() : void
375
    {
376
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
377
            . 'FROM ('
378
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
379
            . 'FROM table_parent t1 '
380
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
381
            . ') dctrn_result '
382
            . 'ORDER BY id_0 ASC';
383
        $alteredSql = 'SELECT DISTINCT id_0, name_1 '
384
            . 'FROM ('
385
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
386
            . 'FROM table_parent t1 '
387
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
388
            . ') dctrn_result '
389
            . 'ORDER BY id_0 ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
390
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
391
        self::assertEquals($alteredSql, $sql);
392
    }
393
394
    /**
395
     * @throws DBALException
396
     */
397
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoinTable() : void
398
    {
399
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
400
            . 'FROM ('
401
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
402
            . 'FROM table_parent t1 '
403
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
404
            . ') dctrn_result '
405
            . 'ORDER BY name_1 ASC';
406
        $alteredSql = 'SELECT DISTINCT id_0, name_1 '
407
            . 'FROM ('
408
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
409
            . 'FROM table_parent t1 '
410
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
411
            . ') dctrn_result '
412
            . 'ORDER BY name_1 ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
413
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
414
        self::assertEquals($alteredSql, $sql);
415
    }
416
417
    /**
418
     * @throws DBALException
419
     */
420
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnsFromBothTables() : void
421
    {
422
        $querySql   = 'SELECT DISTINCT id_0, name_1, foo_2 '
423
            . 'FROM ('
424
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
425
            . 'FROM table_parent t1 '
426
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
427
            . ') dctrn_result '
428
            . 'ORDER BY name_1 ASC, foo_2 DESC';
429
        $alteredSql = 'SELECT DISTINCT id_0, name_1, foo_2 '
430
            . 'FROM ('
431
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
432
            . 'FROM table_parent t1 '
433
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
434
            . ') dctrn_result '
435
            . 'ORDER BY name_1 ASC, foo_2 DESC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
436
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
437
        self::assertEquals($alteredSql, $sql);
438
    }
439
440
    public function testModifyLimitSubquerySimple() : void
441
    {
442
        $querySql   = 'SELECT DISTINCT id_0 FROM '
443
            . '(SELECT k0_.id AS id_0, k0_.field AS field_1 '
444
            . 'FROM key_table k0_ WHERE (k0_.where_field IN (1))) dctrn_result';
445
        $alteredSql = 'SELECT DISTINCT id_0 FROM (SELECT k0_.id AS id_0, k0_.field AS field_1 '
446
            . 'FROM key_table k0_ WHERE (k0_.where_field IN (1))) dctrn_result ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY';
447
        $sql        = $this->platform->modifyLimitQuery($querySql, 20);
448
        self::assertEquals($alteredSql, $sql);
449
    }
450
451
    /**
452
     * @group DDC-1360
453
     */
454
    public function testQuoteIdentifier() : void
455
    {
456
        self::assertEquals('[fo][o]', $this->platform->quoteIdentifier('fo]o'));
457
        self::assertEquals('[test]', $this->platform->quoteIdentifier('test'));
458
        self::assertEquals('[test].[test]', $this->platform->quoteIdentifier('test.test'));
459
    }
460
461
    /**
462
     * @group DDC-1360
463
     */
464
    public function testQuoteSingleIdentifier() : void
465
    {
466
        self::assertEquals('[fo][o]', $this->platform->quoteSingleIdentifier('fo]o'));
467
        self::assertEquals('[test]', $this->platform->quoteSingleIdentifier('test'));
468
        self::assertEquals('[test.test]', $this->platform->quoteSingleIdentifier('test.test'));
469
    }
470
471
    /**
472
     * @group DBAL-220
473
     */
474
    public function testCreateClusteredIndex() : void
475
    {
476
        $idx = new Index('idx', ['id']);
477
        $idx->addFlag('clustered');
478
        self::assertEquals('CREATE CLUSTERED INDEX idx ON tbl (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
479
    }
480
481
    /**
482
     * @group DBAL-220
483
     */
484
    public function testCreateNonClusteredPrimaryKeyInTable() : void
485
    {
486
        $table = new Table('tbl');
487
        $table->addColumn('id', 'integer');
488
        $table->setPrimaryKey(['id']);
489
        $table->getIndex('primary')->addFlag('nonclustered');
490
491
        self::assertEquals(['CREATE TABLE tbl (id INT NOT NULL, PRIMARY KEY NONCLUSTERED (id))'], $this->platform->getCreateTableSQL($table));
492
    }
493
494
    /**
495
     * @group DBAL-220
496
     */
497
    public function testCreateNonClusteredPrimaryKey() : void
498
    {
499
        $idx = new Index('idx', ['id'], false, true);
500
        $idx->addFlag('nonclustered');
501
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY NONCLUSTERED (id)', $this->platform->getCreatePrimaryKeySQL($idx, 'tbl'));
502
    }
503
504
    public function testAlterAddPrimaryKey() : void
505
    {
506
        $idx = new Index('idx', ['id'], false, true);
507
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
508
    }
509
510
    /**
511
     * {@inheritDoc}
512
     */
513
    protected function getQuotedColumnInPrimaryKeySQL() : array
514
    {
515
        return ['CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, PRIMARY KEY ([create]))'];
516
    }
517
518
    /**
519
     * {@inheritDoc}
520
     */
521
    protected function getQuotedColumnInIndexSQL() : array
522
    {
523
        return [
524
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL)',
525
            'CREATE INDEX IDX_22660D028FD6E0FB ON [quoted] ([create])',
526
        ];
527
    }
528
529
    /**
530
     * {@inheritDoc}
531
     */
532
    protected function getQuotedNameInIndexSQL() : array
533
    {
534
        return [
535
            'CREATE TABLE test (column1 NVARCHAR(255) NOT NULL)',
536
            'CREATE INDEX [key] ON test (column1)',
537
        ];
538
    }
539
540
    /**
541
     * {@inheritDoc}
542
     */
543
    protected function getQuotedColumnInForeignKeySQL() : array
544
    {
545
        return [
546
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, foo NVARCHAR(255) NOT NULL, [bar] NVARCHAR(255) NOT NULL)',
547
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES [foreign] ([create], bar, [foo-bar])',
548
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES foo ([create], bar, [foo-bar])',
549
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ([create], foo, [bar]) REFERENCES [foo-bar] ([create], bar, [foo-bar])',
550
        ];
551
    }
552
553
    public function testGetCreateSchemaSQL() : void
554
    {
555
        $schemaName = 'schema';
556
        $sql        = $this->platform->getCreateSchemaSQL($schemaName);
557
        self::assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
558
    }
559
560
    public function testCreateTableWithSchemaColumnComments() : void
561
    {
562
        $table = new Table('testschema.test');
563
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
564
        $table->setPrimaryKey(['id']);
565
566
        $expectedSql = [
567
            'CREATE TABLE testschema.test (id INT NOT NULL, PRIMARY KEY (id))',
568
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'testschema', N'TABLE', 'test', N'COLUMN', id",
569
        ];
570
571
        self::assertEquals($expectedSql, $this->platform->getCreateTableSQL($table));
572
    }
573
574
    public function testAlterTableWithSchemaColumnComments() : void
575
    {
576
        $tableDiff                        = new TableDiff('testschema.mytable');
577
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
578
579
        $expectedSql = [
580
            'ALTER TABLE testschema.mytable ADD quota INT NOT NULL',
581
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota",
582
        ];
583
584
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
585
    }
586
587
    public function testAlterTableWithSchemaDropColumnComments() : void
588
    {
589
        $tableDiff                          = new TableDiff('testschema.mytable');
590
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
591
            'quota',
592
            new Column('quota', Type::getType('integer'), []),
593
            ['comment'],
594
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
595
        );
596
597
        $expectedSql = ["EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];
598
599
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
600
    }
601
602
    public function testAlterTableWithSchemaUpdateColumnComments() : void
603
    {
604
        $tableDiff                          = new TableDiff('testschema.mytable');
605
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
606
            'quota',
607
            new Column('quota', Type::getType('integer'), ['comment' => 'B comment']),
608
            ['comment'],
609
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
610
        );
611
612
        $expectedSql = ["EXEC sp_updateextendedproperty N'MS_Description', N'B comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];
613
614
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
615
    }
616
617
    /**
618
     * {@inheritDoc}
619
     *
620
     * @group DBAL-543
621
     */
622
    public function getCreateTableColumnCommentsSQL() : array
623
    {
624
        return [
625
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY (id))',
626
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', id",
627
        ];
628
    }
629
630
    /**
631
     * {@inheritDoc}
632
     *
633
     * @group DBAL-543
634
     */
635
    public function getAlterTableColumnCommentsSQL() : array
636
    {
637
        return [
638
            'ALTER TABLE mytable ADD quota INT NOT NULL',
639
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', quota",
640
            // todo
641
            //"EXEC sp_addextendedproperty N'MS_Description', N'B comment', N'SCHEMA', dbo, N'TABLE', mytable, N'COLUMN', baz",
642
        ];
643
    }
644
645
    /**
646
     * {@inheritDoc}
647
     *
648
     * @group DBAL-543
649
     */
650
    public function getCreateTableColumnTypeCommentsSQL() : array
651
    {
652
        return [
653
            'CREATE TABLE test (id INT NOT NULL, data VARCHAR(MAX) NOT NULL, PRIMARY KEY (id))',
654
            "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', data",
655
        ];
656
    }
657
658
    /**
659
     * @group DBAL-543
660
     */
661
    public function testGeneratesCreateTableSQLWithColumnComments() : void
662
    {
663
        $table = new Table('mytable');
664
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
665
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
666
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
667
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
668
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
669
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
670
        $table->addColumn('commented_type', 'object');
671
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
672
        $table->addColumn('comment_with_string_literal_char', 'string', [
673
            'length' => 255,
674
            'comment' => "O'Reilly",
675
        ]);
676
        $table->setPrimaryKey(['id']);
677
678
        self::assertEquals(
679
            [
680
                'CREATE TABLE mytable (id INT IDENTITY NOT NULL, comment_empty_string 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))',
681
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
682
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz you!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
683
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for explicitly quoted columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
684
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for reserved keyword columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
685
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
686
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine array type.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
687
                "EXEC sp_addextendedproperty N'MS_Description', N'O''Reilly', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
688
            ],
689
            $this->platform->getCreateTableSQL($table)
690
        );
691
    }
692
693
    /**
694
     * @group DBAL-543
695
     * @group DBAL-1011
696
     */
697
    public function testGeneratesAlterTableSQLWithColumnComments() : void
698
    {
699
        $table = new Table('mytable');
700
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
701
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
702
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
703
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
704
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
705
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
706
        $table->addColumn('commented_type', 'object');
707
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
708
        $table->addColumn('comment_with_string_literal_quote_char', 'array', [
709
            'length' => 255,
710
            'comment' => "O'Reilly",
711
        ]);
712
        $table->setPrimaryKey(['id']);
713
714
        $tableDiff                                                         = new TableDiff('mytable');
715
        $tableDiff->fromTable                                              = $table;
716
        $tableDiff->addedColumns['added_comment_none']                     = new Column('added_comment_none', Type::getType('integer'));
717
        $tableDiff->addedColumns['added_comment_empty_string']             = new Column('added_comment_empty_string', Type::getType('integer'), ['comment' => '']);
718
        $tableDiff->addedColumns['added_comment_string_0']                 = new Column('added_comment_string_0', Type::getType('integer'), ['comment' => '0']);
719
        $tableDiff->addedColumns['added_comment']                          = new Column('added_comment', Type::getType('integer'), ['comment' => 'Doctrine']);
720
        $tableDiff->addedColumns['`added_comment_quoted`']                 = new Column('`added_comment_quoted`', Type::getType('integer'), ['comment' => 'rulez']);
721
        $tableDiff->addedColumns['select']                                 = new Column('select', Type::getType('integer'), ['comment' => '666']);
722
        $tableDiff->addedColumns['added_commented_type']                   = new Column('added_commented_type', Type::getType('object'));
723
        $tableDiff->addedColumns['added_commented_type_with_comment']      = new Column('added_commented_type_with_comment', Type::getType('array'), ['comment' => '666']);
724
        $tableDiff->addedColumns['added_comment_with_string_literal_char'] = new Column('added_comment_with_string_literal_char', Type::getType('string'), [
725
            'length' => 255,
726
            'comment' => "''",
727
        ]);
728
729
        // Add comment to non-commented column.
730
        $tableDiff->changedColumns['id'] = new ColumnDiff(
731
            'id',
732
            new Column('id', Type::getType('integer'), ['autoincrement' => true, 'comment' => 'primary']),
733
            ['comment'],
734
            new Column('id', Type::getType('integer'), ['autoincrement' => true])
735
        );
736
737
        // Change type to custom type from empty string commented column.
738
        $tableDiff->changedColumns['comment_empty_string'] = new ColumnDiff(
739
            'comment_empty_string',
740
            new Column('comment_empty_string', Type::getType('object')),
741
            ['type'],
742
            new Column('comment_empty_string', Type::getType('integer'), ['comment' => ''])
743
        );
744
745
        // Change comment to empty comment from zero-string commented column.
746
        $tableDiff->changedColumns['comment_string_0'] = new ColumnDiff(
747
            'comment_string_0',
748
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '']),
749
            ['comment'],
750
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '0'])
751
        );
752
753
        // Remove comment from regular commented column.
754
        $tableDiff->changedColumns['comment'] = new ColumnDiff(
755
            'comment',
756
            new Column('comment', Type::getType('integer')),
757
            ['comment'],
758
            new Column('comment', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
759
        );
760
761
        // Change comment and change type to custom type from regular commented column.
762
        $tableDiff->changedColumns['`comment_quoted`'] = new ColumnDiff(
763
            '`comment_quoted`',
764
            new Column('`comment_quoted`', Type::getType('array'), ['comment' => 'Doctrine array.']),
765
            ['comment', 'type'],
766
            new Column('`comment_quoted`', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
767
        );
768
769
        // Remove comment and change type to custom type from regular commented column.
770
        $tableDiff->changedColumns['create'] = new ColumnDiff(
771
            'create',
772
            new Column('create', Type::getType('object')),
773
            ['comment', 'type'],
774
            new Column('create', Type::getType('integer'), ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!'])
775
        );
776
777
        // Add comment and change custom type to regular type from non-commented column.
778
        $tableDiff->changedColumns['commented_type'] = new ColumnDiff(
779
            'commented_type',
780
            new Column('commented_type', Type::getType('integer'), ['comment' => 'foo']),
781
            ['comment', 'type'],
782
            new Column('commented_type', Type::getType('object'))
783
        );
784
785
        // Remove comment from commented custom type column.
786
        $tableDiff->changedColumns['commented_type_with_comment'] = new ColumnDiff(
787
            'commented_type_with_comment',
788
            new Column('commented_type_with_comment', Type::getType('array')),
789
            ['comment'],
790
            new Column('commented_type_with_comment', Type::getType('array'), ['comment' => 'Doctrine array type.'])
791
        );
792
793
        // Change comment from comment with string literal char column.
794
        $tableDiff->changedColumns['comment_with_string_literal_char'] = new ColumnDiff(
795
            'comment_with_string_literal_char',
796
            new Column('comment_with_string_literal_char', Type::getType('string'), ['comment' => "'"]),
797
            ['comment'],
798
            new Column('comment_with_string_literal_char', Type::getType('array'), ['comment' => "O'Reilly"])
799
        );
800
801
        self::assertEquals(
802
            [
803
                // Added columns.
804
                'ALTER TABLE mytable ADD added_comment_none INT NOT NULL',
805
                'ALTER TABLE mytable ADD added_comment_empty_string INT NOT NULL',
806
                'ALTER TABLE mytable ADD added_comment_string_0 INT NOT NULL',
807
                'ALTER TABLE mytable ADD added_comment INT NOT NULL',
808
                'ALTER TABLE mytable ADD [added_comment_quoted] INT NOT NULL',
809
                'ALTER TABLE mytable ADD [select] INT NOT NULL',
810
                'ALTER TABLE mytable ADD added_commented_type VARCHAR(MAX) NOT NULL',
811
                'ALTER TABLE mytable ADD added_commented_type_with_comment VARCHAR(MAX) NOT NULL',
812
                'ALTER TABLE mytable ADD added_comment_with_string_literal_char NVARCHAR(255) NOT NULL',
813
                'ALTER TABLE mytable ALTER COLUMN comment_empty_string VARCHAR(MAX) NOT NULL',
814
                'ALTER TABLE mytable ALTER COLUMN [comment_quoted] VARCHAR(MAX) NOT NULL',
815
                'ALTER TABLE mytable ALTER COLUMN [create] VARCHAR(MAX) NOT NULL',
816
                'ALTER TABLE mytable ALTER COLUMN commented_type INT NOT NULL',
817
818
                // Added columns.
819
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_string_0",
820
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment",
821
                "EXEC sp_addextendedproperty N'MS_Description', N'rulez', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [added_comment_quoted]",
822
                "EXEC sp_addextendedproperty N'MS_Description', N'666', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [select]",
823
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type",
824
                "EXEC sp_addextendedproperty N'MS_Description', N'666(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type_with_comment",
825
                "EXEC sp_addextendedproperty N'MS_Description', N'''''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_with_string_literal_char",
826
827
                // Changed columns.
828
                "EXEC sp_addextendedproperty N'MS_Description', N'primary', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', id",
829
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_empty_string",
830
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
831
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
832
                "EXEC sp_updateextendedproperty N'MS_Description', N'Doctrine array.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
833
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
834
                "EXEC sp_updateextendedproperty N'MS_Description', N'foo', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
835
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
836
                "EXEC sp_updateextendedproperty N'MS_Description', N'''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
837
            ],
838
            $this->platform->getAlterTableSQL($tableDiff)
839
        );
840
    }
841
842
    /**
843
     * @group DBAL-122
844
     */
845
    public function testInitializesDoctrineTypeMappings() : void
846
    {
847
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bigint'));
848
        self::assertSame('bigint', $this->platform->getDoctrineTypeMapping('bigint'));
849
850
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('numeric'));
851
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('numeric'));
852
853
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bit'));
854
        self::assertSame('boolean', $this->platform->getDoctrineTypeMapping('bit'));
855
856
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallint'));
857
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('smallint'));
858
859
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('decimal'));
860
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('decimal'));
861
862
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallmoney'));
863
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('smallmoney'));
864
865
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('int'));
866
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('int'));
867
868
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tinyint'));
869
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('tinyint'));
870
871
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('money'));
872
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('money'));
873
874
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('float'));
875
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('float'));
876
877
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real'));
878
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('real'));
879
880
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double'));
881
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double'));
882
883
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double precision'));
884
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double precision'));
885
886
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smalldatetime'));
887
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('smalldatetime'));
888
889
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('datetime'));
890
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('datetime'));
891
892
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('char'));
893
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('char'));
894
895
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varchar'));
896
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('varchar'));
897
898
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('text'));
899
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('text'));
900
901
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nchar'));
902
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nchar'));
903
904
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nvarchar'));
905
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nvarchar'));
906
907
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('ntext'));
908
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('ntext'));
909
910
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('binary'));
911
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('binary'));
912
913
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varbinary'));
914
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('varbinary'));
915
916
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('image'));
917
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('image'));
918
919
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('uniqueidentifier'));
920
        self::assertSame('guid', $this->platform->getDoctrineTypeMapping('uniqueidentifier'));
921
    }
922
923
    protected function getExpectedFixedLengthStringTypeDeclarationSQLNoLength() : string
924
    {
925
        return 'NCHAR';
926
    }
927
928
    protected function getExpectedFixedLengthStringTypeDeclarationSQLWithLength() : string
929
    {
930
        return 'NCHAR(16)';
931
    }
932
933
    public function testGetVariableLengthStringTypeDeclarationSQLNoLength() : void
934
    {
935
        $this->expectException(ColumnLengthRequired::class);
936
937
        parent::testGetVariableLengthStringTypeDeclarationSQLNoLength();
938
    }
939
940
    protected function getExpectedVariableLengthStringTypeDeclarationSQLWithLength() : string
941
    {
942
        return 'NVARCHAR(16)';
943
    }
944
945
    public function testGetVariableLengthBinaryTypeDeclarationSQLNoLength() : void
946
    {
947
        $this->expectException(ColumnLengthRequired::class);
948
949
        parent::testGetVariableLengthBinaryTypeDeclarationSQLNoLength();
950
    }
951
952
    /**
953
     * {@inheritDoc}
954
     *
955
     * @group DBAL-234
956
     */
957
    protected function getAlterTableRenameIndexSQL() : array
958
    {
959
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_bar', N'INDEX'"];
960
    }
961
962
    /**
963
     * {@inheritDoc}
964
     *
965
     * @group DBAL-234
966
     */
967
    protected function getQuotedAlterTableRenameIndexSQL() : array
968
    {
969
        return [
970
            "EXEC sp_RENAME N'[table].[create]', N'[select]', N'INDEX'",
971
            "EXEC sp_RENAME N'[table].[foo]', N'[bar]', N'INDEX'",
972
        ];
973
    }
974
975
    /**
976
     * @group DBAL-825
977
     */
978
    public function testChangeColumnsTypeWithDefaultValue() : void
979
    {
980
        $tableName = 'column_def_change_type';
981
        $table     = new Table($tableName);
982
983
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
984
        $table->addColumn('col_string', 'string', ['default' => 'foo']);
985
986
        $tableDiff                            = new TableDiff($tableName);
987
        $tableDiff->fromTable                 = $table;
988
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
989
            'col_int',
990
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
991
            ['type'],
992
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
993
        );
994
995
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
996
            'col_string',
997
            new Column('col_string', Type::getType('string'), [
998
                'length' => 255,
999
                'fixed' => true,
1000
                'default' => 'foo',
1001
            ]),
1002
            ['fixed'],
1003
            new Column('col_string', Type::getType('string'), ['default' => 'foo'])
1004
        );
1005
1006
        self::assertSame(
1007
            [
1008
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_FA2CB292',
1009
                'ALTER TABLE column_def_change_type ALTER COLUMN col_int INT NOT NULL',
1010
                'ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_FA2CB292 DEFAULT 666 FOR col_int',
1011
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_2725A6D0',
1012
                'ALTER TABLE column_def_change_type ALTER COLUMN col_string NCHAR(255) NOT NULL',
1013
                "ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_2725A6D0 DEFAULT 'foo' FOR col_string",
1014
            ],
1015
            $this->platform->getAlterTableSQL($tableDiff)
1016
        );
1017
    }
1018
1019
    /**
1020
     * {@inheritdoc}
1021
     */
1022
    protected function getQuotedAlterTableRenameColumnSQL() : array
1023
    {
1024
        return [
1025
            "sp_RENAME 'mytable.unquoted1', 'unquoted', 'COLUMN'",
1026
            "sp_RENAME 'mytable.unquoted2', '[where]', 'COLUMN'",
1027
            "sp_RENAME 'mytable.unquoted3', '[foo]', 'COLUMN'",
1028
            "sp_RENAME 'mytable.[create]', 'reserved_keyword', 'COLUMN'",
1029
            "sp_RENAME 'mytable.[table]', '[from]', 'COLUMN'",
1030
            "sp_RENAME 'mytable.[select]', '[bar]', 'COLUMN'",
1031
            "sp_RENAME 'mytable.quoted1', 'quoted', 'COLUMN'",
1032
            "sp_RENAME 'mytable.quoted2', '[and]', 'COLUMN'",
1033
            "sp_RENAME 'mytable.quoted3', '[baz]', 'COLUMN'",
1034
        ];
1035
    }
1036
1037
    /**
1038
     * {@inheritdoc}
1039
     */
1040
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
1041
    {
1042
        self::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...
1043
    }
1044
1045
    /**
1046
     * {@inheritDoc}
1047
     *
1048
     * @group DBAL-807
1049
     */
1050
    protected function getAlterTableRenameIndexInSchemaSQL() : array
1051
    {
1052
        return ["EXEC sp_RENAME N'myschema.mytable.idx_foo', N'idx_bar', N'INDEX'"];
1053
    }
1054
1055
    /**
1056
     * {@inheritDoc}
1057
     *
1058
     * @group DBAL-807
1059
     */
1060
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
1061
    {
1062
        return [
1063
            "EXEC sp_RENAME N'[schema].[table].[create]', N'[select]', N'INDEX'",
1064
            "EXEC sp_RENAME N'[schema].[table].[foo]', N'[bar]', N'INDEX'",
1065
        ];
1066
    }
1067
1068
    protected function getQuotesDropForeignKeySQL() : string
1069
    {
1070
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
1071
    }
1072
1073
    protected function getQuotesDropConstraintSQL() : string
1074
    {
1075
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
1076
    }
1077
1078
    /**
1079
     * @param mixed[] $column
1080
     *
1081
     * @dataProvider getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL
1082
     * @group DBAL-830
1083
     */
1084
    public function testGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL(string $table, array $column, string $expectedSql) : void
1085
    {
1086
        assert($this->platform instanceof SQLServer2012Platform);
1087
        self::assertSame($expectedSql, $this->platform->getDefaultConstraintDeclarationSQL($table, $column));
1088
    }
1089
1090
    /**
1091
     * @return mixed[][]
1092
     */
1093
    public static function getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL() : iterable
1094
    {
1095
        return [
1096
            // Unquoted identifiers non-reserved keywords.
1097
            ['mytable', ['name' => 'mycolumn', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn"],
1098
            // Quoted identifiers non-reserved keywords.
1099
            ['`mytable`', ['name' => '`mycolumn`', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]"],
1100
            // Unquoted identifiers reserved keywords.
1101
            ['table', ['name' => 'select', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1102
            // Quoted identifiers reserved keywords.
1103
            ['`table`', ['name' => '`select`', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1104
        ];
1105
    }
1106
1107
    /**
1108
     * @param string[] $expectedSql
1109
     *
1110
     * @dataProvider getGeneratesIdentifierNamesInCreateTableSQL
1111
     * @group DBAL-830
1112
     */
1113
    public function testGeneratesIdentifierNamesInCreateTableSQL(Table $table, array $expectedSql) : void
1114
    {
1115
        self::assertSame($expectedSql, $this->platform->getCreateTableSQL($table));
1116
    }
1117
1118
    /**
1119
     * @return mixed[][]
1120
     */
1121
    public static function getGeneratesIdentifierNamesInCreateTableSQL() : iterable
1122
    {
1123
        return [
1124
            // Unquoted identifiers non-reserved keywords.
1125
            [
1126
                new Table('mytable', [
1127
                    new Column('mycolumn', Type::getType('string'), [
1128
                        'length' => 255,
1129
                        'default' => 'foo',
1130
                    ]),
1131
                ]),
1132
                [
1133
                    'CREATE TABLE mytable (mycolumn NVARCHAR(255) NOT NULL)',
1134
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn",
1135
                ],
1136
            ],
1137
            // Quoted identifiers reserved keywords.
1138
            [
1139
                new Table('`mytable`', [
1140
                    new Column('`mycolumn`', Type::getType('string'), [
1141
                        'length' => 255,
1142
                        'default' => 'foo',
1143
                    ]),
1144
                ]),
1145
                [
1146
                    'CREATE TABLE [mytable] ([mycolumn] NVARCHAR(255) NOT NULL)',
1147
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]",
1148
                ],
1149
            ],
1150
            // Unquoted identifiers reserved keywords.
1151
            [
1152
                new Table('table', [
1153
                    new Column('select', Type::getType('string'), [
1154
                        'length' => 255,
1155
                        'default' => 'foo',
1156
                    ]),
1157
                ]),
1158
                [
1159
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
1160
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
1161
                ],
1162
            ],
1163
            // Quoted identifiers reserved keywords.
1164
            [
1165
                new Table('`table`', [
1166
                    new Column('`select`', Type::getType('string'), [
1167
                        'length' => 255,
1168
                        'default' => 'foo',
1169
                    ]),
1170
                ]),
1171
                [
1172
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
1173
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
1174
                ],
1175
            ],
1176
        ];
1177
    }
1178
1179
    /**
1180
     * @param string[] $expectedSql
1181
     *
1182
     * @dataProvider getGeneratesIdentifierNamesInAlterTableSQL
1183
     * @group DBAL-830
1184
     */
1185
    public function testGeneratesIdentifierNamesInAlterTableSQL(TableDiff $tableDiff, array $expectedSql) : void
1186
    {
1187
        self::assertSame($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
1188
    }
1189
1190
    /**
1191
     * @return mixed[][]
1192
     */
1193
    public static function getGeneratesIdentifierNamesInAlterTableSQL() : iterable
1194
    {
1195
        return [
1196
            // Unquoted identifiers non-reserved keywords.
1197
            [
1198
                new TableDiff(
1199
                    'mytable',
1200
                    [
1201
                        'addcolumn' => new Column('addcolumn', Type::getType('string'), [
1202
                            'length' => 255,
1203
                            'default' => 'foo',
1204
                        ]),
1205
                    ],
1206
                    [
1207
                        'mycolumn' => new ColumnDiff(
1208
                            'mycolumn',
1209
                            new Column('mycolumn', Type::getType('string'), [
1210
                                'length' => 255,
1211
                                'default' => 'bar',
1212
                            ]),
1213
                            ['default'],
1214
                            new Column('mycolumn', Type::getType('string'), [
1215
                                'length' => 255,
1216
                                'default' => 'foo',
1217
                            ])
1218
                        ),
1219
                    ],
1220
                    [
1221
                        'removecolumn' => new Column('removecolumn', Type::getType('string'), [
1222
                            'length' => 255,
1223
                            'default' => 'foo',
1224
                        ]),
1225
                    ]
1226
                ),
1227
                [
1228
                    'ALTER TABLE mytable ADD addcolumn NVARCHAR(255) NOT NULL',
1229
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR addcolumn",
1230
                    'ALTER TABLE mytable DROP COLUMN removecolumn',
1231
                    'ALTER TABLE mytable DROP CONSTRAINT DF_6B2BD609_9BADD926',
1232
                    'ALTER TABLE mytable ALTER COLUMN mycolumn NVARCHAR(255) NOT NULL',
1233
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR mycolumn",
1234
                ],
1235
            ],
1236
            // Quoted identifiers non-reserved keywords.
1237
            [
1238
                new TableDiff(
1239
                    '`mytable`',
1240
                    [
1241
                        'addcolumn' => new Column('`addcolumn`', Type::getType('string'), [
1242
                            'length' => 255,
1243
                            'default' => 'foo',
1244
                        ]),
1245
                    ],
1246
                    [
1247
                        'mycolumn' => new ColumnDiff(
1248
                            '`mycolumn`',
1249
                            new Column('`mycolumn`', Type::getType('string'), [
1250
                                'length' => 255,
1251
                                'default' => 'bar',
1252
                            ]),
1253
                            ['default'],
1254
                            new Column('`mycolumn`', Type::getType('string'), [
1255
                                'length' => 255,
1256
                                'default' => 'foo',
1257
                            ])
1258
                        ),
1259
                    ],
1260
                    [
1261
                        'removecolumn' => new Column('`removecolumn`', Type::getType('string'), [
1262
                            'length' => 255,
1263
                            'default' => 'foo',
1264
                        ]),
1265
                    ]
1266
                ),
1267
                [
1268
                    'ALTER TABLE [mytable] ADD [addcolumn] NVARCHAR(255) NOT NULL',
1269
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR [addcolumn]",
1270
                    'ALTER TABLE [mytable] DROP COLUMN [removecolumn]',
1271
                    'ALTER TABLE [mytable] DROP CONSTRAINT DF_6B2BD609_9BADD926',
1272
                    'ALTER TABLE [mytable] ALTER COLUMN [mycolumn] NVARCHAR(255) NOT NULL',
1273
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR [mycolumn]",
1274
                ],
1275
            ],
1276
            // Unquoted identifiers reserved keywords.
1277
            [
1278
                new TableDiff(
1279
                    'table',
1280
                    [
1281
                        'add' => new Column('add', Type::getType('string'), [
1282
                            'length' => 255,
1283
                            'default' => 'foo',
1284
                        ]),
1285
                    ],
1286
                    [
1287
                        'select' => new ColumnDiff(
1288
                            'select',
1289
                            new Column('select', Type::getType('string'), [
1290
                                'length' => 255,
1291
                                'default' => 'bar',
1292
                            ]),
1293
                            ['default'],
1294
                            new Column('select', Type::getType('string'), [
1295
                                'length' => 255,
1296
                                'default' => 'foo',
1297
                            ])
1298
                        ),
1299
                    ],
1300
                    [
1301
                        'drop' => new Column('drop', Type::getType('string'), [
1302
                            'length' => 255,
1303
                            'default' => 'foo',
1304
                        ]),
1305
                    ]
1306
                ),
1307
                [
1308
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
1309
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
1310
                    'ALTER TABLE [table] DROP COLUMN [drop]',
1311
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
1312
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
1313
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
1314
                ],
1315
            ],
1316
            // Quoted identifiers reserved keywords.
1317
            [
1318
                new TableDiff(
1319
                    '`table`',
1320
                    [
1321
                        'add' => new Column('`add`', Type::getType('string'), [
1322
                            'length' => 255,
1323
                            'default' => 'foo',
1324
                        ]),
1325
                    ],
1326
                    [
1327
                        'select' => new ColumnDiff(
1328
                            '`select`',
1329
                            new Column('`select`', Type::getType('string'), [
1330
                                'length' => 255,
1331
                                'default' => 'bar',
1332
                            ]),
1333
                            ['default'],
1334
                            new Column('`select`', Type::getType('string'), [
1335
                                'length' => 255,
1336
                                'default' => 'foo',
1337
                            ])
1338
                        ),
1339
                    ],
1340
                    [
1341
                        'drop' => new Column('`drop`', Type::getType('string'), [
1342
                            'length' => 255,
1343
                            'default' => 'foo',
1344
                        ]),
1345
                    ]
1346
                ),
1347
                [
1348
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
1349
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
1350
                    'ALTER TABLE [table] DROP COLUMN [drop]',
1351
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
1352
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
1353
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
1354
                ],
1355
            ],
1356
        ];
1357
    }
1358
1359
    /**
1360
     * @group DBAL-423
1361
     */
1362
    public function testReturnsGuidTypeDeclarationSQL() : void
1363
    {
1364
        self::assertSame('UNIQUEIDENTIFIER', $this->platform->getGuidTypeDeclarationSQL([]));
1365
    }
1366
1367
    /**
1368
     * {@inheritdoc}
1369
     */
1370
    public function getAlterTableRenameColumnSQL() : array
1371
    {
1372
        return [
1373
            "sp_RENAME 'foo.bar', 'baz', 'COLUMN'",
1374
            'ALTER TABLE foo DROP CONSTRAINT DF_8C736521_76FF8CAA',
1375
            'ALTER TABLE foo ADD CONSTRAINT DF_8C736521_78240498 DEFAULT 666 FOR baz',
1376
        ];
1377
    }
1378
1379
    /**
1380
     * {@inheritdoc}
1381
     */
1382
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
1383
    {
1384
        return [
1385
            'ALTER TABLE [foo] DROP CONSTRAINT fk1',
1386
            'ALTER TABLE [foo] DROP CONSTRAINT fk2',
1387
            "sp_RENAME '[foo].id', 'war', 'COLUMN'",
1388
            'ALTER TABLE [foo] ADD bloo INT NOT NULL',
1389
            'ALTER TABLE [foo] DROP COLUMN baz',
1390
            'ALTER TABLE [foo] ALTER COLUMN bar INT',
1391
            "sp_RENAME '[foo]', 'table'",
1392
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
1393
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' + REPLACE(dc.name, '8C736521', 'F6298F46') + ''', " .
1394
            "''OBJECT'';' FROM sys.default_constraints dc JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " .
1395
            "WHERE tbl.name = 'table';EXEC sp_executesql @sql",
1396
            'ALTER TABLE [table] ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id)',
1397
            'ALTER TABLE [table] ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id)',
1398
        ];
1399
    }
1400
1401
    /**
1402
     * {@inheritdoc}
1403
     */
1404
    protected function getCommentOnColumnSQL() : array
1405
    {
1406
        return [
1407
            "COMMENT ON COLUMN foo.bar IS 'comment'",
1408
            "COMMENT ON COLUMN [Foo].[BAR] IS 'comment'",
1409
            "COMMENT ON COLUMN [select].[from] IS 'comment'",
1410
        ];
1411
    }
1412
1413
    /**
1414
     * {@inheritdoc}
1415
     */
1416
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
1417
    {
1418
        return [
1419
            ['CASCADE', 'CASCADE'],
1420
            ['SET NULL', 'SET NULL'],
1421
            ['NO ACTION', 'NO ACTION'],
1422
            ['RESTRICT', 'NO ACTION'],
1423
            ['SET DEFAULT', 'SET DEFAULT'],
1424
            ['CaScAdE', 'CASCADE'],
1425
        ];
1426
    }
1427
1428
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
1429
    {
1430
        return 'CONSTRAINT [select] UNIQUE (foo)';
1431
    }
1432
1433
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
1434
    {
1435
        return 'INDEX [select] (foo)';
1436
    }
1437
1438
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
1439
    {
1440
        return 'TRUNCATE TABLE [select]';
1441
    }
1442
1443
    /**
1444
     * {@inheritdoc}
1445
     */
1446
    protected function getAlterStringToFixedStringSQL() : array
1447
    {
1448
        return ['ALTER TABLE mytable ALTER COLUMN name NCHAR(2) NOT NULL'];
1449
    }
1450
1451
    /**
1452
     * {@inheritdoc}
1453
     */
1454
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
1455
    {
1456
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_foo_renamed', N'INDEX'"];
1457
    }
1458
1459
    public function testModifyLimitQueryWithTopNSubQueryWithOrderBy() : void
1460
    {
1461
        $querySql    = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC)';
1462
        $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';
1463
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1464
        self::assertEquals($expectedSql, $sql);
1465
1466
        $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';
1467
        $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';
1468
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1469
        self::assertEquals($expectedSql, $sql);
1470
    }
1471
1472
    public function testModifyLimitQueryWithFromSubquery() : void
1473
    {
1474
        $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);
1475
1476
        $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';
1477
1478
        self::assertEquals($sql, $expected);
1479
    }
1480
1481
    public function testModifyLimitQueryWithFromSubqueryAndOrder() : void
1482
    {
1483
        $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);
1484
1485
        $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';
1486
1487
        self::assertEquals($sql, $expected);
1488
    }
1489
1490
    public function testModifyLimitQueryWithComplexOrderByExpression() : void
1491
    {
1492
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM table ORDER BY (table.x * table.y) DESC', 10);
1493
1494
        $expected = 'SELECT * FROM table ORDER BY (table.x * table.y) DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1495
1496
        self::assertEquals($sql, $expected);
1497
    }
1498
1499
    public function testModifyLimitQueryWithNewlineBeforeOrderBy() : void
1500
    {
1501
        $querySql    = "SELECT * FROM test\nORDER BY col DESC";
1502
        $expectedSql = "SELECT * FROM test\nORDER BY col DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY";
1503
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1504
        self::assertEquals($expectedSql, $sql);
1505
    }
1506
1507
    /**
1508
     * @group DBAL-2436
1509
     */
1510
    public function testQuotesTableNameInListTableColumnsSQL() : void
1511
    {
1512
        self::assertStringContainsStringIgnoringCase(
1513
            "'Foo''Bar\\'",
1514
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
1515
        );
1516
    }
1517
1518
    /**
1519
     * @group DBAL-2436
1520
     */
1521
    public function testQuotesSchemaNameInListTableColumnsSQL() : void
1522
    {
1523
        self::assertStringContainsStringIgnoringCase(
1524
            "'Foo''Bar\\'",
1525
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1526
        );
1527
    }
1528
1529
    /**
1530
     * @group DBAL-2436
1531
     */
1532
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
1533
    {
1534
        self::assertStringContainsStringIgnoringCase(
1535
            "'Foo''Bar\\'",
1536
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
1537
        );
1538
    }
1539
1540
    /**
1541
     * @group DBAL-2436
1542
     */
1543
    public function testQuotesSchemaNameInListTableForeignKeysSQL() : void
1544
    {
1545
        self::assertStringContainsStringIgnoringCase(
1546
            "'Foo''Bar\\'",
1547
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
1548
        );
1549
    }
1550
1551
    /**
1552
     * @group DBAL-2436
1553
     */
1554
    public function testQuotesTableNameInListTableIndexesSQL() : void
1555
    {
1556
        self::assertStringContainsStringIgnoringCase(
1557
            "'Foo''Bar\\'",
1558
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
1559
        );
1560
    }
1561
1562
    /**
1563
     * @group DBAL-2436
1564
     */
1565
    public function testQuotesSchemaNameInListTableIndexesSQL() : void
1566
    {
1567
        self::assertStringContainsStringIgnoringCase(
1568
            "'Foo''Bar\\'",
1569
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
1570
        );
1571
    }
1572
1573
    /**
1574
     * @group 2859
1575
     */
1576
    public function testGetDefaultValueDeclarationSQLForDateType() : void
1577
    {
1578
        $currentDateSql = $this->platform->getCurrentDateSQL();
1579
        foreach (['date', 'date_immutable'] as $type) {
1580
            $field = [
1581
                'type' => Type::getType($type),
1582
                'default' => $currentDateSql,
1583
            ];
1584
1585
            self::assertSame(
1586
                ' DEFAULT CONVERT(date, GETDATE())',
1587
                $this->platform->getDefaultValueDeclarationSQL($field)
1588
            );
1589
        }
1590
    }
1591
1592
    public function testSupportsColumnCollation() : void
1593
    {
1594
        self::assertTrue($this->platform->supportsColumnCollation());
1595
    }
1596
1597
    public function testColumnCollationDeclarationSQL() : void
1598
    {
1599
        self::assertSame(
1600
            'COLLATE Latin1_General_CS_AS_KS_WS',
1601
            $this->platform->getColumnCollationDeclarationSQL('Latin1_General_CS_AS_KS_WS')
1602
        );
1603
    }
1604
1605
    public function testGetCreateTableSQLWithColumnCollation() : void
1606
    {
1607
        $table = new Table('foo');
1608
        $table->addColumn('no_collation', 'string', ['length' => 255]);
1609
        $table->addColumn('column_collation', 'string', ['length' => 255])->setPlatformOption('collation', 'Latin1_General_CS_AS_KS_WS');
1610
1611
        self::assertSame(
1612
            ['CREATE TABLE foo (no_collation NVARCHAR(255) NOT NULL, column_collation NVARCHAR(255) COLLATE Latin1_General_CS_AS_KS_WS NOT NULL)'],
1613
            $this->platform->getCreateTableSQL($table),
1614
            'Column "no_collation" will use the default collation from the table/database and "column_collation" overwrites the collation on this column'
1615
        );
1616
    }
1617
1618
    public function testSupportsSequences() : void
1619
    {
1620
        self::assertTrue($this->platform->supportsSequences());
1621
    }
1622
1623
    public function testDoesNotPreferSequences() : void
1624
    {
1625
        self::assertFalse($this->platform->prefersSequences());
1626
    }
1627
1628
    public function testGeneratesSequenceSqlCommands() : void
1629
    {
1630
        $sequence = new Sequence('myseq', 20, 1);
1631
        self::assertEquals(
1632
            'CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 20 MINVALUE 1',
1633
            $this->platform->getCreateSequenceSQL($sequence)
1634
        );
1635
        self::assertEquals(
1636
            'ALTER SEQUENCE myseq INCREMENT BY 20',
1637
            $this->platform->getAlterSequenceSQL($sequence)
1638
        );
1639
        self::assertEquals(
1640
            'DROP SEQUENCE myseq',
1641
            $this->platform->getDropSequenceSQL('myseq')
1642
        );
1643
        self::assertEquals(
1644
            'SELECT NEXT VALUE FOR myseq',
1645
            $this->platform->getSequenceNextValSQL('myseq')
1646
        );
1647
    }
1648
}
1649