Failed Conditions
Push — master ( ac0e13...24dbc4 )
by Sergei
22s queued 15s
created

Platforms/AbstractSQLServerPlatformTestCase.php (1 issue)

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_null', 'integer', ['comment' => null]);
666
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
667
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
668
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
669
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
670
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
671
        $table->addColumn('commented_type', 'object');
672
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
673
        $table->addColumn('comment_with_string_literal_char', 'string', [
674
            'length' => 255,
675
            'comment' => "O'Reilly",
676
        ]);
677
        $table->setPrimaryKey(['id']);
678
679
        self::assertEquals(
680
            [
681
                'CREATE TABLE mytable (id INT IDENTITY NOT NULL, comment_null INT 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))',
682
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
683
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz you!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
684
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for explicitly quoted columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
685
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for reserved keyword columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
686
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
687
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine array type.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
688
                "EXEC sp_addextendedproperty N'MS_Description', N'O''Reilly', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
689
            ],
690
            $this->platform->getCreateTableSQL($table)
691
        );
692
    }
693
694
    /**
695
     * @group DBAL-543
696
     * @group DBAL-1011
697
     */
698
    public function testGeneratesAlterTableSQLWithColumnComments() : void
699
    {
700
        $table = new Table('mytable');
701
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
702
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
703
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
704
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
705
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
706
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
707
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
708
        $table->addColumn('commented_type', 'object');
709
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
710
        $table->addColumn('comment_with_string_literal_quote_char', 'array', [
711
            'length' => 255,
712
            'comment' => "O'Reilly",
713
        ]);
714
        $table->setPrimaryKey(['id']);
715
716
        $tableDiff                                                         = new TableDiff('mytable');
717
        $tableDiff->fromTable                                              = $table;
718
        $tableDiff->addedColumns['added_comment_none']                     = new Column('added_comment_none', Type::getType('integer'));
719
        $tableDiff->addedColumns['added_comment_null']                     = new Column('added_comment_null', Type::getType('integer'), ['comment' => null]);
720
        $tableDiff->addedColumns['added_comment_empty_string']             = new Column('added_comment_empty_string', Type::getType('integer'), ['comment' => '']);
721
        $tableDiff->addedColumns['added_comment_string_0']                 = new Column('added_comment_string_0', Type::getType('integer'), ['comment' => '0']);
722
        $tableDiff->addedColumns['added_comment']                          = new Column('added_comment', Type::getType('integer'), ['comment' => 'Doctrine']);
723
        $tableDiff->addedColumns['`added_comment_quoted`']                 = new Column('`added_comment_quoted`', Type::getType('integer'), ['comment' => 'rulez']);
724
        $tableDiff->addedColumns['select']                                 = new Column('select', Type::getType('integer'), ['comment' => '666']);
725
        $tableDiff->addedColumns['added_commented_type']                   = new Column('added_commented_type', Type::getType('object'));
726
        $tableDiff->addedColumns['added_commented_type_with_comment']      = new Column('added_commented_type_with_comment', Type::getType('array'), ['comment' => '666']);
727
        $tableDiff->addedColumns['added_comment_with_string_literal_char'] = new Column('added_comment_with_string_literal_char', Type::getType('string'), [
728
            'length' => 255,
729
            'comment' => "''",
730
        ]);
731
732
        // Add comment to non-commented column.
733
        $tableDiff->changedColumns['id'] = new ColumnDiff(
734
            'id',
735
            new Column('id', Type::getType('integer'), ['autoincrement' => true, 'comment' => 'primary']),
736
            ['comment'],
737
            new Column('id', Type::getType('integer'), ['autoincrement' => true])
738
        );
739
740
        // Remove comment from null-commented column.
741
        $tableDiff->changedColumns['comment_null'] = new ColumnDiff(
742
            'comment_null',
743
            new Column('comment_null', Type::getType('string'), ['length' => 255]),
744
            ['type'],
745
            new Column('comment_null', Type::getType('integer'), ['comment' => null])
746
        );
747
748
        // Change type to custom type from empty string commented column.
749
        $tableDiff->changedColumns['comment_empty_string'] = new ColumnDiff(
750
            'comment_empty_string',
751
            new Column('comment_empty_string', Type::getType('object')),
752
            ['type'],
753
            new Column('comment_empty_string', Type::getType('integer'), ['comment' => ''])
754
        );
755
756
        // Change comment to empty comment from zero-string commented column.
757
        $tableDiff->changedColumns['comment_string_0'] = new ColumnDiff(
758
            'comment_string_0',
759
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '']),
760
            ['comment'],
761
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '0'])
762
        );
763
764
        // Remove comment from regular commented column.
765
        $tableDiff->changedColumns['comment'] = new ColumnDiff(
766
            'comment',
767
            new Column('comment', Type::getType('integer')),
768
            ['comment'],
769
            new Column('comment', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
770
        );
771
772
        // Change comment and change type to custom type from regular commented column.
773
        $tableDiff->changedColumns['`comment_quoted`'] = new ColumnDiff(
774
            '`comment_quoted`',
775
            new Column('`comment_quoted`', Type::getType('array'), ['comment' => 'Doctrine array.']),
776
            ['comment', 'type'],
777
            new Column('`comment_quoted`', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
778
        );
779
780
        // Remove comment and change type to custom type from regular commented column.
781
        $tableDiff->changedColumns['create'] = new ColumnDiff(
782
            'create',
783
            new Column('create', Type::getType('object')),
784
            ['comment', 'type'],
785
            new Column('create', Type::getType('integer'), ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!'])
786
        );
787
788
        // Add comment and change custom type to regular type from non-commented column.
789
        $tableDiff->changedColumns['commented_type'] = new ColumnDiff(
790
            'commented_type',
791
            new Column('commented_type', Type::getType('integer'), ['comment' => 'foo']),
792
            ['comment', 'type'],
793
            new Column('commented_type', Type::getType('object'))
794
        );
795
796
        // Remove comment from commented custom type column.
797
        $tableDiff->changedColumns['commented_type_with_comment'] = new ColumnDiff(
798
            'commented_type_with_comment',
799
            new Column('commented_type_with_comment', Type::getType('array')),
800
            ['comment'],
801
            new Column('commented_type_with_comment', Type::getType('array'), ['comment' => 'Doctrine array type.'])
802
        );
803
804
        // Change comment from comment with string literal char column.
805
        $tableDiff->changedColumns['comment_with_string_literal_char'] = new ColumnDiff(
806
            'comment_with_string_literal_char',
807
            new Column('comment_with_string_literal_char', Type::getType('string'), ['comment' => "'"]),
808
            ['comment'],
809
            new Column('comment_with_string_literal_char', Type::getType('array'), ['comment' => "O'Reilly"])
810
        );
811
812
        self::assertEquals(
813
            [
814
                // Added columns.
815
                'ALTER TABLE mytable ADD added_comment_none INT NOT NULL',
816
                'ALTER TABLE mytable ADD added_comment_null INT NOT NULL',
817
                'ALTER TABLE mytable ADD added_comment_empty_string INT NOT NULL',
818
                'ALTER TABLE mytable ADD added_comment_string_0 INT NOT NULL',
819
                'ALTER TABLE mytable ADD added_comment INT NOT NULL',
820
                'ALTER TABLE mytable ADD [added_comment_quoted] INT NOT NULL',
821
                'ALTER TABLE mytable ADD [select] INT NOT NULL',
822
                'ALTER TABLE mytable ADD added_commented_type VARCHAR(MAX) NOT NULL',
823
                'ALTER TABLE mytable ADD added_commented_type_with_comment VARCHAR(MAX) NOT NULL',
824
                'ALTER TABLE mytable ADD added_comment_with_string_literal_char NVARCHAR(255) NOT NULL',
825
                'ALTER TABLE mytable ALTER COLUMN comment_null NVARCHAR(255) NOT NULL',
826
                'ALTER TABLE mytable ALTER COLUMN comment_empty_string VARCHAR(MAX) NOT NULL',
827
                'ALTER TABLE mytable ALTER COLUMN [comment_quoted] VARCHAR(MAX) NOT NULL',
828
                'ALTER TABLE mytable ALTER COLUMN [create] VARCHAR(MAX) NOT NULL',
829
                'ALTER TABLE mytable ALTER COLUMN commented_type INT NOT NULL',
830
831
                // Added columns.
832
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_string_0",
833
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment",
834
                "EXEC sp_addextendedproperty N'MS_Description', N'rulez', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [added_comment_quoted]",
835
                "EXEC sp_addextendedproperty N'MS_Description', N'666', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [select]",
836
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type",
837
                "EXEC sp_addextendedproperty N'MS_Description', N'666(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type_with_comment",
838
                "EXEC sp_addextendedproperty N'MS_Description', N'''''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_with_string_literal_char",
839
840
                // Changed columns.
841
                "EXEC sp_addextendedproperty N'MS_Description', N'primary', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', id",
842
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_empty_string",
843
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
844
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
845
                "EXEC sp_updateextendedproperty N'MS_Description', N'Doctrine array.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
846
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
847
                "EXEC sp_updateextendedproperty N'MS_Description', N'foo', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
848
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
849
                "EXEC sp_updateextendedproperty N'MS_Description', N'''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
850
            ],
851
            $this->platform->getAlterTableSQL($tableDiff)
852
        );
853
    }
854
855
    /**
856
     * @group DBAL-122
857
     */
858
    public function testInitializesDoctrineTypeMappings() : void
859
    {
860
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bigint'));
861
        self::assertSame('bigint', $this->platform->getDoctrineTypeMapping('bigint'));
862
863
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('numeric'));
864
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('numeric'));
865
866
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bit'));
867
        self::assertSame('boolean', $this->platform->getDoctrineTypeMapping('bit'));
868
869
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallint'));
870
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('smallint'));
871
872
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('decimal'));
873
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('decimal'));
874
875
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallmoney'));
876
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('smallmoney'));
877
878
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('int'));
879
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('int'));
880
881
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tinyint'));
882
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('tinyint'));
883
884
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('money'));
885
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('money'));
886
887
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('float'));
888
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('float'));
889
890
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real'));
891
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('real'));
892
893
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double'));
894
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double'));
895
896
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double precision'));
897
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double precision'));
898
899
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smalldatetime'));
900
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('smalldatetime'));
901
902
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('datetime'));
903
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('datetime'));
904
905
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('char'));
906
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('char'));
907
908
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varchar'));
909
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('varchar'));
910
911
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('text'));
912
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('text'));
913
914
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nchar'));
915
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nchar'));
916
917
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nvarchar'));
918
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nvarchar'));
919
920
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('ntext'));
921
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('ntext'));
922
923
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('binary'));
924
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('binary'));
925
926
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varbinary'));
927
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('varbinary'));
928
929
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('image'));
930
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('image'));
931
932
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('uniqueidentifier'));
933
        self::assertSame('guid', $this->platform->getDoctrineTypeMapping('uniqueidentifier'));
934
    }
935
936
    protected function getExpectedFixedLengthStringTypeDeclarationSQLNoLength() : string
937
    {
938
        return 'NCHAR';
939
    }
940
941
    protected function getExpectedFixedLengthStringTypeDeclarationSQLWithLength() : string
942
    {
943
        return 'NCHAR(16)';
944
    }
945
946
    public function testGetVariableLengthStringTypeDeclarationSQLNoLength() : void
947
    {
948
        $this->expectException(ColumnLengthRequired::class);
949
950
        parent::testGetVariableLengthStringTypeDeclarationSQLNoLength();
951
    }
952
953
    protected function getExpectedVariableLengthStringTypeDeclarationSQLWithLength() : string
954
    {
955
        return 'NVARCHAR(16)';
956
    }
957
958
    public function testGetVariableLengthBinaryTypeDeclarationSQLNoLength() : void
959
    {
960
        $this->expectException(ColumnLengthRequired::class);
961
962
        parent::testGetVariableLengthBinaryTypeDeclarationSQLNoLength();
963
    }
964
965
    /**
966
     * {@inheritDoc}
967
     *
968
     * @group DBAL-234
969
     */
970
    protected function getAlterTableRenameIndexSQL() : array
971
    {
972
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_bar', N'INDEX'"];
973
    }
974
975
    /**
976
     * {@inheritDoc}
977
     *
978
     * @group DBAL-234
979
     */
980
    protected function getQuotedAlterTableRenameIndexSQL() : array
981
    {
982
        return [
983
            "EXEC sp_RENAME N'[table].[create]', N'[select]', N'INDEX'",
984
            "EXEC sp_RENAME N'[table].[foo]', N'[bar]', N'INDEX'",
985
        ];
986
    }
987
988
    /**
989
     * @group DBAL-825
990
     */
991
    public function testChangeColumnsTypeWithDefaultValue() : void
992
    {
993
        $tableName = 'column_def_change_type';
994
        $table     = new Table($tableName);
995
996
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
997
        $table->addColumn('col_string', 'string', ['default' => 'foo']);
998
999
        $tableDiff                            = new TableDiff($tableName);
1000
        $tableDiff->fromTable                 = $table;
1001
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
1002
            'col_int',
1003
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
1004
            ['type'],
1005
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
1006
        );
1007
1008
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
1009
            'col_string',
1010
            new Column('col_string', Type::getType('string'), [
1011
                'length' => 255,
1012
                'fixed' => true,
1013
                'default' => 'foo',
1014
            ]),
1015
            ['fixed'],
1016
            new Column('col_string', Type::getType('string'), ['default' => 'foo'])
1017
        );
1018
1019
        self::assertSame(
1020
            [
1021
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_FA2CB292',
1022
                'ALTER TABLE column_def_change_type ALTER COLUMN col_int INT NOT NULL',
1023
                'ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_FA2CB292 DEFAULT 666 FOR col_int',
1024
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_2725A6D0',
1025
                'ALTER TABLE column_def_change_type ALTER COLUMN col_string NCHAR(255) NOT NULL',
1026
                "ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_2725A6D0 DEFAULT 'foo' FOR col_string",
1027
            ],
1028
            $this->platform->getAlterTableSQL($tableDiff)
1029
        );
1030
    }
1031
1032
    /**
1033
     * {@inheritdoc}
1034
     */
1035
    protected function getQuotedAlterTableRenameColumnSQL() : array
1036
    {
1037
        return [
1038
            "sp_RENAME 'mytable.unquoted1', 'unquoted', 'COLUMN'",
1039
            "sp_RENAME 'mytable.unquoted2', '[where]', 'COLUMN'",
1040
            "sp_RENAME 'mytable.unquoted3', '[foo]', 'COLUMN'",
1041
            "sp_RENAME 'mytable.[create]', 'reserved_keyword', 'COLUMN'",
1042
            "sp_RENAME 'mytable.[table]', '[from]', 'COLUMN'",
1043
            "sp_RENAME 'mytable.[select]', '[bar]', 'COLUMN'",
1044
            "sp_RENAME 'mytable.quoted1', 'quoted', 'COLUMN'",
1045
            "sp_RENAME 'mytable.quoted2', '[and]', 'COLUMN'",
1046
            "sp_RENAME 'mytable.quoted3', '[baz]', 'COLUMN'",
1047
        ];
1048
    }
1049
1050
    /**
1051
     * {@inheritdoc}
1052
     */
1053
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
1054
    {
1055
        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...
1056
    }
1057
1058
    /**
1059
     * {@inheritDoc}
1060
     *
1061
     * @group DBAL-807
1062
     */
1063
    protected function getAlterTableRenameIndexInSchemaSQL() : array
1064
    {
1065
        return ["EXEC sp_RENAME N'myschema.mytable.idx_foo', N'idx_bar', N'INDEX'"];
1066
    }
1067
1068
    /**
1069
     * {@inheritDoc}
1070
     *
1071
     * @group DBAL-807
1072
     */
1073
    protected function getQuotedAlterTableRenameIndexInSchemaSQL() : array
1074
    {
1075
        return [
1076
            "EXEC sp_RENAME N'[schema].[table].[create]', N'[select]', N'INDEX'",
1077
            "EXEC sp_RENAME N'[schema].[table].[foo]', N'[bar]', N'INDEX'",
1078
        ];
1079
    }
1080
1081
    protected function getQuotesDropForeignKeySQL() : string
1082
    {
1083
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
1084
    }
1085
1086
    protected function getQuotesDropConstraintSQL() : string
1087
    {
1088
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
1089
    }
1090
1091
    /**
1092
     * @param mixed[] $column
1093
     *
1094
     * @dataProvider getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL
1095
     * @group DBAL-830
1096
     */
1097
    public function testGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL(string $table, array $column, string $expectedSql) : void
1098
    {
1099
        assert($this->platform instanceof SQLServer2012Platform);
1100
        self::assertSame($expectedSql, $this->platform->getDefaultConstraintDeclarationSQL($table, $column));
1101
    }
1102
1103
    /**
1104
     * @return mixed[][]
1105
     */
1106
    public static function getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL() : iterable
1107
    {
1108
        return [
1109
            // Unquoted identifiers non-reserved keywords.
1110
            ['mytable', ['name' => 'mycolumn', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn"],
1111
            // Quoted identifiers non-reserved keywords.
1112
            ['`mytable`', ['name' => '`mycolumn`', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]"],
1113
            // Unquoted identifiers reserved keywords.
1114
            ['table', ['name' => 'select', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1115
            // Quoted identifiers reserved keywords.
1116
            ['`table`', ['name' => '`select`', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1117
        ];
1118
    }
1119
1120
    /**
1121
     * @param string[] $expectedSql
1122
     *
1123
     * @dataProvider getGeneratesIdentifierNamesInCreateTableSQL
1124
     * @group DBAL-830
1125
     */
1126
    public function testGeneratesIdentifierNamesInCreateTableSQL(Table $table, array $expectedSql) : void
1127
    {
1128
        self::assertSame($expectedSql, $this->platform->getCreateTableSQL($table));
1129
    }
1130
1131
    /**
1132
     * @return mixed[][]
1133
     */
1134
    public static function getGeneratesIdentifierNamesInCreateTableSQL() : iterable
1135
    {
1136
        return [
1137
            // Unquoted identifiers non-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
            // Quoted identifiers reserved keywords.
1151
            [
1152
                new Table('`mytable`', [
1153
                    new Column('`mycolumn`', Type::getType('string'), [
1154
                        'length' => 255,
1155
                        'default' => 'foo',
1156
                    ]),
1157
                ]),
1158
                [
1159
                    'CREATE TABLE [mytable] ([mycolumn] NVARCHAR(255) NOT NULL)',
1160
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]",
1161
                ],
1162
            ],
1163
            // Unquoted 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
            // Quoted identifiers reserved keywords.
1177
            [
1178
                new Table('`table`', [
1179
                    new Column('`select`', Type::getType('string'), [
1180
                        'length' => 255,
1181
                        'default' => 'foo',
1182
                    ]),
1183
                ]),
1184
                [
1185
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
1186
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
1187
                ],
1188
            ],
1189
        ];
1190
    }
1191
1192
    /**
1193
     * @param string[] $expectedSql
1194
     *
1195
     * @dataProvider getGeneratesIdentifierNamesInAlterTableSQL
1196
     * @group DBAL-830
1197
     */
1198
    public function testGeneratesIdentifierNamesInAlterTableSQL(TableDiff $tableDiff, array $expectedSql) : void
1199
    {
1200
        self::assertSame($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
1201
    }
1202
1203
    /**
1204
     * @return mixed[][]
1205
     */
1206
    public static function getGeneratesIdentifierNamesInAlterTableSQL() : iterable
1207
    {
1208
        return [
1209
            // Unquoted identifiers non-reserved keywords.
1210
            [
1211
                new TableDiff(
1212
                    'mytable',
1213
                    [
1214
                        'addcolumn' => new Column('addcolumn', Type::getType('string'), [
1215
                            'length' => 255,
1216
                            'default' => 'foo',
1217
                        ]),
1218
                    ],
1219
                    [
1220
                        'mycolumn' => new ColumnDiff(
1221
                            'mycolumn',
1222
                            new Column('mycolumn', Type::getType('string'), [
1223
                                'length' => 255,
1224
                                'default' => 'bar',
1225
                            ]),
1226
                            ['default'],
1227
                            new Column('mycolumn', Type::getType('string'), [
1228
                                'length' => 255,
1229
                                'default' => 'foo',
1230
                            ])
1231
                        ),
1232
                    ],
1233
                    [
1234
                        'removecolumn' => new Column('removecolumn', Type::getType('string'), [
1235
                            'length' => 255,
1236
                            'default' => 'foo',
1237
                        ]),
1238
                    ]
1239
                ),
1240
                [
1241
                    'ALTER TABLE mytable ADD addcolumn NVARCHAR(255) NOT NULL',
1242
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR addcolumn",
1243
                    'ALTER TABLE mytable DROP COLUMN removecolumn',
1244
                    'ALTER TABLE mytable DROP CONSTRAINT DF_6B2BD609_9BADD926',
1245
                    'ALTER TABLE mytable ALTER COLUMN mycolumn NVARCHAR(255) NOT NULL',
1246
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR mycolumn",
1247
                ],
1248
            ],
1249
            // Quoted identifiers non-reserved keywords.
1250
            [
1251
                new TableDiff(
1252
                    '`mytable`',
1253
                    [
1254
                        'addcolumn' => new Column('`addcolumn`', Type::getType('string'), [
1255
                            'length' => 255,
1256
                            'default' => 'foo',
1257
                        ]),
1258
                    ],
1259
                    [
1260
                        'mycolumn' => new ColumnDiff(
1261
                            '`mycolumn`',
1262
                            new Column('`mycolumn`', Type::getType('string'), [
1263
                                'length' => 255,
1264
                                'default' => 'bar',
1265
                            ]),
1266
                            ['default'],
1267
                            new Column('`mycolumn`', Type::getType('string'), [
1268
                                'length' => 255,
1269
                                'default' => 'foo',
1270
                            ])
1271
                        ),
1272
                    ],
1273
                    [
1274
                        'removecolumn' => new Column('`removecolumn`', Type::getType('string'), [
1275
                            'length' => 255,
1276
                            'default' => 'foo',
1277
                        ]),
1278
                    ]
1279
                ),
1280
                [
1281
                    'ALTER TABLE [mytable] ADD [addcolumn] NVARCHAR(255) NOT NULL',
1282
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR [addcolumn]",
1283
                    'ALTER TABLE [mytable] DROP COLUMN [removecolumn]',
1284
                    'ALTER TABLE [mytable] DROP CONSTRAINT DF_6B2BD609_9BADD926',
1285
                    'ALTER TABLE [mytable] ALTER COLUMN [mycolumn] NVARCHAR(255) NOT NULL',
1286
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR [mycolumn]",
1287
                ],
1288
            ],
1289
            // Unquoted identifiers reserved keywords.
1290
            [
1291
                new TableDiff(
1292
                    'table',
1293
                    [
1294
                        'add' => new Column('add', Type::getType('string'), [
1295
                            'length' => 255,
1296
                            'default' => 'foo',
1297
                        ]),
1298
                    ],
1299
                    [
1300
                        'select' => new ColumnDiff(
1301
                            'select',
1302
                            new Column('select', Type::getType('string'), [
1303
                                'length' => 255,
1304
                                'default' => 'bar',
1305
                            ]),
1306
                            ['default'],
1307
                            new Column('select', Type::getType('string'), [
1308
                                'length' => 255,
1309
                                'default' => 'foo',
1310
                            ])
1311
                        ),
1312
                    ],
1313
                    [
1314
                        'drop' => new Column('drop', Type::getType('string'), [
1315
                            'length' => 255,
1316
                            'default' => 'foo',
1317
                        ]),
1318
                    ]
1319
                ),
1320
                [
1321
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
1322
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
1323
                    'ALTER TABLE [table] DROP COLUMN [drop]',
1324
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
1325
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
1326
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
1327
                ],
1328
            ],
1329
            // Quoted identifiers reserved keywords.
1330
            [
1331
                new TableDiff(
1332
                    '`table`',
1333
                    [
1334
                        'add' => new Column('`add`', Type::getType('string'), [
1335
                            'length' => 255,
1336
                            'default' => 'foo',
1337
                        ]),
1338
                    ],
1339
                    [
1340
                        'select' => new ColumnDiff(
1341
                            '`select`',
1342
                            new Column('`select`', Type::getType('string'), [
1343
                                'length' => 255,
1344
                                'default' => 'bar',
1345
                            ]),
1346
                            ['default'],
1347
                            new Column('`select`', Type::getType('string'), [
1348
                                'length' => 255,
1349
                                'default' => 'foo',
1350
                            ])
1351
                        ),
1352
                    ],
1353
                    [
1354
                        'drop' => new Column('`drop`', Type::getType('string'), [
1355
                            'length' => 255,
1356
                            'default' => 'foo',
1357
                        ]),
1358
                    ]
1359
                ),
1360
                [
1361
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
1362
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
1363
                    'ALTER TABLE [table] DROP COLUMN [drop]',
1364
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
1365
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
1366
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
1367
                ],
1368
            ],
1369
        ];
1370
    }
1371
1372
    /**
1373
     * @group DBAL-423
1374
     */
1375
    public function testReturnsGuidTypeDeclarationSQL() : void
1376
    {
1377
        self::assertSame('UNIQUEIDENTIFIER', $this->platform->getGuidTypeDeclarationSQL([]));
1378
    }
1379
1380
    /**
1381
     * {@inheritdoc}
1382
     */
1383
    public function getAlterTableRenameColumnSQL() : array
1384
    {
1385
        return [
1386
            "sp_RENAME 'foo.bar', 'baz', 'COLUMN'",
1387
            'ALTER TABLE foo DROP CONSTRAINT DF_8C736521_76FF8CAA',
1388
            'ALTER TABLE foo ADD CONSTRAINT DF_8C736521_78240498 DEFAULT 666 FOR baz',
1389
        ];
1390
    }
1391
1392
    /**
1393
     * {@inheritdoc}
1394
     */
1395
    protected function getQuotesTableIdentifiersInAlterTableSQL() : array
1396
    {
1397
        return [
1398
            'ALTER TABLE [foo] DROP CONSTRAINT fk1',
1399
            'ALTER TABLE [foo] DROP CONSTRAINT fk2',
1400
            "sp_RENAME '[foo].id', 'war', 'COLUMN'",
1401
            'ALTER TABLE [foo] ADD bloo INT NOT NULL',
1402
            'ALTER TABLE [foo] DROP COLUMN baz',
1403
            'ALTER TABLE [foo] ALTER COLUMN bar INT',
1404
            "sp_RENAME '[foo]', 'table'",
1405
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
1406
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' + REPLACE(dc.name, '8C736521', 'F6298F46') + ''', " .
1407
            "''OBJECT'';' FROM sys.default_constraints dc JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " .
1408
            "WHERE tbl.name = 'table';EXEC sp_executesql @sql",
1409
            'ALTER TABLE [table] ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id)',
1410
            'ALTER TABLE [table] ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id)',
1411
        ];
1412
    }
1413
1414
    /**
1415
     * {@inheritdoc}
1416
     */
1417
    protected function getCommentOnColumnSQL() : array
1418
    {
1419
        return [
1420
            "COMMENT ON COLUMN foo.bar IS 'comment'",
1421
            "COMMENT ON COLUMN [Foo].[BAR] IS 'comment'",
1422
            "COMMENT ON COLUMN [select].[from] IS 'comment'",
1423
        ];
1424
    }
1425
1426
    /**
1427
     * {@inheritdoc}
1428
     */
1429
    public static function getReturnsForeignKeyReferentialActionSQL() : iterable
1430
    {
1431
        return [
1432
            ['CASCADE', 'CASCADE'],
1433
            ['SET NULL', 'SET NULL'],
1434
            ['NO ACTION', 'NO ACTION'],
1435
            ['RESTRICT', 'NO ACTION'],
1436
            ['SET DEFAULT', 'SET DEFAULT'],
1437
            ['CaScAdE', 'CASCADE'],
1438
        ];
1439
    }
1440
1441
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL() : string
1442
    {
1443
        return 'CONSTRAINT [select] UNIQUE (foo)';
1444
    }
1445
1446
    protected function getQuotesReservedKeywordInIndexDeclarationSQL() : string
1447
    {
1448
        return 'INDEX [select] (foo)';
1449
    }
1450
1451
    protected function getQuotesReservedKeywordInTruncateTableSQL() : string
1452
    {
1453
        return 'TRUNCATE TABLE [select]';
1454
    }
1455
1456
    /**
1457
     * {@inheritdoc}
1458
     */
1459
    protected function getAlterStringToFixedStringSQL() : array
1460
    {
1461
        return ['ALTER TABLE mytable ALTER COLUMN name NCHAR(2) NOT NULL'];
1462
    }
1463
1464
    /**
1465
     * {@inheritdoc}
1466
     */
1467
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL() : array
1468
    {
1469
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_foo_renamed', N'INDEX'"];
1470
    }
1471
1472
    public function testModifyLimitQueryWithTopNSubQueryWithOrderBy() : void
1473
    {
1474
        $querySql    = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC)';
1475
        $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';
1476
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1477
        self::assertEquals($expectedSql, $sql);
1478
1479
        $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';
1480
        $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';
1481
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1482
        self::assertEquals($expectedSql, $sql);
1483
    }
1484
1485
    public function testModifyLimitQueryWithFromSubquery() : void
1486
    {
1487
        $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);
1488
1489
        $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';
1490
1491
        self::assertEquals($sql, $expected);
1492
    }
1493
1494
    public function testModifyLimitQueryWithFromSubqueryAndOrder() : void
1495
    {
1496
        $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);
1497
1498
        $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';
1499
1500
        self::assertEquals($sql, $expected);
1501
    }
1502
1503
    public function testModifyLimitQueryWithComplexOrderByExpression() : void
1504
    {
1505
        $sql = $this->platform->modifyLimitQuery('SELECT * FROM table ORDER BY (table.x * table.y) DESC', 10);
1506
1507
        $expected = 'SELECT * FROM table ORDER BY (table.x * table.y) DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY';
1508
1509
        self::assertEquals($sql, $expected);
1510
    }
1511
1512
    public function testModifyLimitQueryWithNewlineBeforeOrderBy() : void
1513
    {
1514
        $querySql    = "SELECT * FROM test\nORDER BY col DESC";
1515
        $expectedSql = "SELECT * FROM test\nORDER BY col DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY";
1516
        $sql         = $this->platform->modifyLimitQuery($querySql, 10);
1517
        self::assertEquals($expectedSql, $sql);
1518
    }
1519
1520
    /**
1521
     * @group DBAL-2436
1522
     */
1523
    public function testQuotesTableNameInListTableColumnsSQL() : void
1524
    {
1525
        self::assertStringContainsStringIgnoringCase(
1526
            "'Foo''Bar\\'",
1527
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
1528
        );
1529
    }
1530
1531
    /**
1532
     * @group DBAL-2436
1533
     */
1534
    public function testQuotesSchemaNameInListTableColumnsSQL() : void
1535
    {
1536
        self::assertStringContainsStringIgnoringCase(
1537
            "'Foo''Bar\\'",
1538
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1539
        );
1540
    }
1541
1542
    /**
1543
     * @group DBAL-2436
1544
     */
1545
    public function testQuotesTableNameInListTableForeignKeysSQL() : void
1546
    {
1547
        self::assertStringContainsStringIgnoringCase(
1548
            "'Foo''Bar\\'",
1549
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
1550
        );
1551
    }
1552
1553
    /**
1554
     * @group DBAL-2436
1555
     */
1556
    public function testQuotesSchemaNameInListTableForeignKeysSQL() : void
1557
    {
1558
        self::assertStringContainsStringIgnoringCase(
1559
            "'Foo''Bar\\'",
1560
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
1561
        );
1562
    }
1563
1564
    /**
1565
     * @group DBAL-2436
1566
     */
1567
    public function testQuotesTableNameInListTableIndexesSQL() : void
1568
    {
1569
        self::assertStringContainsStringIgnoringCase(
1570
            "'Foo''Bar\\'",
1571
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
1572
        );
1573
    }
1574
1575
    /**
1576
     * @group DBAL-2436
1577
     */
1578
    public function testQuotesSchemaNameInListTableIndexesSQL() : void
1579
    {
1580
        self::assertStringContainsStringIgnoringCase(
1581
            "'Foo''Bar\\'",
1582
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
1583
        );
1584
    }
1585
1586
    /**
1587
     * @group 2859
1588
     */
1589
    public function testGetDefaultValueDeclarationSQLForDateType() : void
1590
    {
1591
        $currentDateSql = $this->platform->getCurrentDateSQL();
1592
        foreach (['date', 'date_immutable'] as $type) {
1593
            $field = [
1594
                'type' => Type::getType($type),
1595
                'default' => $currentDateSql,
1596
            ];
1597
1598
            self::assertSame(
1599
                ' DEFAULT CONVERT(date, GETDATE())',
1600
                $this->platform->getDefaultValueDeclarationSQL($field)
1601
            );
1602
        }
1603
    }
1604
1605
    public function testSupportsColumnCollation() : void
1606
    {
1607
        self::assertTrue($this->platform->supportsColumnCollation());
1608
    }
1609
1610
    public function testColumnCollationDeclarationSQL() : void
1611
    {
1612
        self::assertSame(
1613
            'COLLATE Latin1_General_CS_AS_KS_WS',
1614
            $this->platform->getColumnCollationDeclarationSQL('Latin1_General_CS_AS_KS_WS')
1615
        );
1616
    }
1617
1618
    public function testGetCreateTableSQLWithColumnCollation() : void
1619
    {
1620
        $table = new Table('foo');
1621
        $table->addColumn('no_collation', 'string', ['length' => 255]);
1622
        $table->addColumn('column_collation', 'string', ['length' => 255])->setPlatformOption('collation', 'Latin1_General_CS_AS_KS_WS');
1623
1624
        self::assertSame(
1625
            ['CREATE TABLE foo (no_collation NVARCHAR(255) NOT NULL, column_collation NVARCHAR(255) COLLATE Latin1_General_CS_AS_KS_WS NOT NULL)'],
1626
            $this->platform->getCreateTableSQL($table),
1627
            'Column "no_collation" will use the default collation from the table/database and "column_collation" overwrites the collation on this column'
1628
        );
1629
    }
1630
1631
    public function testSupportsSequences() : void
1632
    {
1633
        self::assertTrue($this->platform->supportsSequences());
1634
    }
1635
1636
    public function testDoesNotPreferSequences() : void
1637
    {
1638
        self::assertFalse($this->platform->prefersSequences());
1639
    }
1640
1641
    public function testGeneratesSequenceSqlCommands() : void
1642
    {
1643
        $sequence = new Sequence('myseq', 20, 1);
1644
        self::assertEquals(
1645
            'CREATE SEQUENCE myseq START WITH 1 INCREMENT BY 20 MINVALUE 1',
1646
            $this->platform->getCreateSequenceSQL($sequence)
1647
        );
1648
        self::assertEquals(
1649
            'ALTER SEQUENCE myseq INCREMENT BY 20',
1650
            $this->platform->getAlterSequenceSQL($sequence)
1651
        );
1652
        self::assertEquals(
1653
            'DROP SEQUENCE myseq',
1654
            $this->platform->getDropSequenceSQL('myseq')
1655
        );
1656
        self::assertEquals(
1657
            'SELECT NEXT VALUE FOR myseq',
1658
            $this->platform->getSequenceNextValSQL('myseq')
1659
        );
1660
    }
1661
}
1662