Completed
Pull Request — master (#3756)
by Sergei
60:59
created

testDoesNotPreferSequences()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\DBAL\Platforms;
6
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Exception\ColumnLengthRequired;
9
use Doctrine\DBAL\Platforms\SQLServerPlatform;
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
300
            'SELECT ' .
301
            'u.id, ' .
302
            '(u.foo/2) foodiv, ' .
303
            'CONCAT(u.bar, u.baz) barbaz, ' .
304
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
305
            'FROM user u ' .
306
            "WHERE u.status = 'disabled' " .
307
            'ORDER BY (SELECT 0) OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY',
308
            $sql
309
        );
310
    }
311
312
    /**
313
     * @group DBAL-713
314
     */
315
    public function testModifyLimitQueryWithSubSelectInSelectListAndOrderByClause() : void
316
    {
317
        $sql = $this->platform->modifyLimitQuery(
318
            'SELECT ' .
319
            'u.id, ' .
320
            '(u.foo/2) foodiv, ' .
321
            'CONCAT(u.bar, u.baz) barbaz, ' .
322
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
323
            'FROM user u ' .
324
            "WHERE u.status = 'disabled' " .
325
            'ORDER BY u.username DESC',
326
            10,
327
            5
328
        );
329
330
        self::assertEquals(
331
            'SELECT ' .
332
            'u.id, ' .
333
            '(u.foo/2) foodiv, ' .
334
            'CONCAT(u.bar, u.baz) barbaz, ' .
335
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
336
            'FROM user u ' .
337
            "WHERE u.status = 'disabled' " .
338
            'ORDER BY u.username DESC ' .
339
            'OFFSET 5 ROWS FETCH NEXT 10 ROWS ONLY',
340
            $sql
341
        );
342
    }
343
344
    /**
345
     * @group DBAL-834
346
     */
347
    public function testModifyLimitQueryWithAggregateFunctionInOrderByClause() : void
348
    {
349
        $sql = $this->platform->modifyLimitQuery(
350
            'SELECT ' .
351
            'MAX(heading_id) aliased, ' .
352
            'code ' .
353
            'FROM operator_model_operator ' .
354
            'GROUP BY code ' .
355
            'ORDER BY MAX(heading_id) DESC',
356
            1,
357
            0
358
        );
359
360
        self::assertEquals(
361
            'SELECT ' .
362
            'MAX(heading_id) aliased, ' .
363
            'code ' .
364
            'FROM operator_model_operator ' .
365
            'GROUP BY code ' .
366
            'ORDER BY MAX(heading_id) DESC ' .
367
            'OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
368
            $sql
369
        );
370
    }
371
372
    /**
373
     * @throws DBALException
374
     */
375
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBaseTable() : void
376
    {
377
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
378
            . 'FROM ('
379
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
380
            . 'FROM table_parent t1 '
381
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
382
            . ') dctrn_result '
383
            . 'ORDER BY id_0 ASC';
384
        $alteredSql = 'SELECT DISTINCT id_0, name_1 '
385
            . 'FROM ('
386
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
387
            . 'FROM table_parent t1 '
388
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
389
            . ') dctrn_result '
390
            . 'ORDER BY id_0 ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
391
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
392
        self::assertEquals($alteredSql, $sql);
393
    }
394
395
    /**
396
     * @throws DBALException
397
     */
398
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoinTable() : void
399
    {
400
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
401
            . 'FROM ('
402
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
403
            . 'FROM table_parent t1 '
404
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
405
            . ') dctrn_result '
406
            . 'ORDER BY name_1 ASC';
407
        $alteredSql = 'SELECT DISTINCT id_0, name_1 '
408
            . 'FROM ('
409
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
410
            . 'FROM table_parent t1 '
411
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
412
            . ') dctrn_result '
413
            . 'ORDER BY name_1 ASC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
414
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
415
        self::assertEquals($alteredSql, $sql);
416
    }
417
418
    /**
419
     * @throws DBALException
420
     */
421
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnsFromBothTables() : void
422
    {
423
        $querySql   = 'SELECT DISTINCT id_0, name_1, foo_2 '
424
            . 'FROM ('
425
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
426
            . 'FROM table_parent t1 '
427
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
428
            . ') dctrn_result '
429
            . 'ORDER BY name_1 ASC, foo_2 DESC';
430
        $alteredSql = 'SELECT DISTINCT id_0, name_1, foo_2 '
431
            . 'FROM ('
432
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
433
            . 'FROM table_parent t1 '
434
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
435
            . ') dctrn_result '
436
            . 'ORDER BY name_1 ASC, foo_2 DESC OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY';
437
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
438
        self::assertEquals($alteredSql, $sql);
439
    }
440
441
    public function testModifyLimitSubquerySimple() : void
442
    {
443
        $querySql   = 'SELECT DISTINCT id_0 FROM '
444
            . '(SELECT k0_.id AS id_0, k0_.field AS field_1 '
445
            . 'FROM key_table k0_ WHERE (k0_.where_field IN (1))) dctrn_result';
446
        $alteredSql = 'SELECT DISTINCT id_0 FROM (SELECT k0_.id AS id_0, k0_.field AS field_1 '
447
            . 'FROM key_table k0_ WHERE (k0_.where_field IN (1))) dctrn_result ORDER BY 1 OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY';
448
        $sql        = $this->platform->modifyLimitQuery($querySql, 20);
449
        self::assertEquals($alteredSql, $sql);
450
    }
451
452
    /**
453
     * @group DDC-1360
454
     */
455
    public function testQuoteIdentifier() : void
456
    {
457
        self::assertEquals('[fo][o]', $this->platform->quoteIdentifier('fo]o'));
458
        self::assertEquals('[test]', $this->platform->quoteIdentifier('test'));
459
        self::assertEquals('[test].[test]', $this->platform->quoteIdentifier('test.test'));
460
    }
461
462
    /**
463
     * @group DDC-1360
464
     */
465
    public function testQuoteSingleIdentifier() : void
466
    {
467
        self::assertEquals('[fo][o]', $this->platform->quoteSingleIdentifier('fo]o'));
468
        self::assertEquals('[test]', $this->platform->quoteSingleIdentifier('test'));
469
        self::assertEquals('[test.test]', $this->platform->quoteSingleIdentifier('test.test'));
470
    }
471
472
    /**
473
     * @group DBAL-220
474
     */
475
    public function testCreateClusteredIndex() : void
476
    {
477
        $idx = new Index('idx', ['id']);
478
        $idx->addFlag('clustered');
479
        self::assertEquals('CREATE CLUSTERED INDEX idx ON tbl (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
480
    }
481
482
    /**
483
     * @group DBAL-220
484
     */
485
    public function testCreateNonClusteredPrimaryKeyInTable() : void
486
    {
487
        $table = new Table('tbl');
488
        $table->addColumn('id', 'integer');
489
        $table->setPrimaryKey(['id']);
490
        $table->getIndex('primary')->addFlag('nonclustered');
491
492
        self::assertEquals(['CREATE TABLE tbl (id INT NOT NULL, PRIMARY KEY NONCLUSTERED (id))'], $this->platform->getCreateTableSQL($table));
493
    }
494
495
    /**
496
     * @group DBAL-220
497
     */
498
    public function testCreateNonClusteredPrimaryKey() : void
499
    {
500
        $idx = new Index('idx', ['id'], false, true);
501
        $idx->addFlag('nonclustered');
502
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY NONCLUSTERED (id)', $this->platform->getCreatePrimaryKeySQL($idx, 'tbl'));
503
    }
504
505
    public function testAlterAddPrimaryKey() : void
506
    {
507
        $idx = new Index('idx', ['id'], false, true);
508
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
509
    }
510
511
    /**
512
     * {@inheritDoc}
513
     */
514
    protected function getQuotedColumnInPrimaryKeySQL() : array
515
    {
516
        return ['CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, PRIMARY KEY ([create]))'];
517
    }
518
519
    /**
520
     * {@inheritDoc}
521
     */
522
    protected function getQuotedColumnInIndexSQL() : array
523
    {
524
        return [
525
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL)',
526
            'CREATE INDEX IDX_22660D028FD6E0FB ON [quoted] ([create])',
527
        ];
528
    }
529
530
    /**
531
     * {@inheritDoc}
532
     */
533
    protected function getQuotedNameInIndexSQL() : array
534
    {
535
        return [
536
            'CREATE TABLE test (column1 NVARCHAR(255) NOT NULL)',
537
            'CREATE INDEX [key] ON test (column1)',
538
        ];
539
    }
540
541
    /**
542
     * {@inheritDoc}
543
     */
544
    protected function getQuotedColumnInForeignKeySQL() : array
545
    {
546
        return [
547
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, foo NVARCHAR(255) NOT NULL, [bar] NVARCHAR(255) NOT NULL)',
548
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES [foreign] ([create], bar, [foo-bar])',
549
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES foo ([create], bar, [foo-bar])',
550
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ([create], foo, [bar]) REFERENCES [foo-bar] ([create], bar, [foo-bar])',
551
        ];
552
    }
553
554
    public function testGetCreateSchemaSQL() : void
555
    {
556
        $schemaName = 'schema';
557
        $sql        = $this->platform->getCreateSchemaSQL($schemaName);
558
        self::assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
559
    }
560
561
    public function testCreateTableWithSchemaColumnComments() : void
562
    {
563
        $table = new Table('testschema.test');
564
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
565
        $table->setPrimaryKey(['id']);
566
567
        $expectedSql = [
568
            'CREATE TABLE testschema.test (id INT NOT NULL, PRIMARY KEY (id))',
569
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'testschema', N'TABLE', 'test', N'COLUMN', id",
570
        ];
571
572
        self::assertEquals($expectedSql, $this->platform->getCreateTableSQL($table));
573
    }
574
575
    public function testAlterTableWithSchemaColumnComments() : void
576
    {
577
        $tableDiff                        = new TableDiff('testschema.mytable');
578
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);
579
580
        $expectedSql = [
581
            'ALTER TABLE testschema.mytable ADD quota INT NOT NULL',
582
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota",
583
        ];
584
585
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
586
    }
587
588
    public function testAlterTableWithSchemaDropColumnComments() : void
589
    {
590
        $tableDiff                          = new TableDiff('testschema.mytable');
591
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
592
            'quota',
593
            new Column('quota', Type::getType('integer'), []),
594
            ['comment'],
595
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
596
        );
597
598
        $expectedSql = ["EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];
599
600
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
601
    }
602
603
    public function testAlterTableWithSchemaUpdateColumnComments() : void
604
    {
605
        $tableDiff                          = new TableDiff('testschema.mytable');
606
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
607
            'quota',
608
            new Column('quota', Type::getType('integer'), ['comment' => 'B comment']),
609
            ['comment'],
610
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
611
        );
612
613
        $expectedSql = ["EXEC sp_updateextendedproperty N'MS_Description', N'B comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];
614
615
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
616
    }
617
618
    /**
619
     * {@inheritDoc}
620
     *
621
     * @group DBAL-543
622
     */
623
    public function getCreateTableColumnCommentsSQL() : array
624
    {
625
        return [
626
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY (id))',
627
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', id",
628
        ];
629
    }
630
631
    /**
632
     * {@inheritDoc}
633
     *
634
     * @group DBAL-543
635
     */
636
    public function getAlterTableColumnCommentsSQL() : array
637
    {
638
        return [
639
            'ALTER TABLE mytable ADD quota INT NOT NULL',
640
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', quota",
641
            // todo
642
            //"EXEC sp_addextendedproperty N'MS_Description', N'B comment', N'SCHEMA', dbo, N'TABLE', mytable, N'COLUMN', baz",
643
        ];
644
    }
645
646
    /**
647
     * {@inheritDoc}
648
     *
649
     * @group DBAL-543
650
     */
651
    public function getCreateTableColumnTypeCommentsSQL() : array
652
    {
653
        return [
654
            'CREATE TABLE test (id INT NOT NULL, data VARCHAR(MAX) NOT NULL, PRIMARY KEY (id))',
655
            "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', data",
656
        ];
657
    }
658
659
    /**
660
     * @group DBAL-543
661
     */
662
    public function testGeneratesCreateTableSQLWithColumnComments() : void
663
    {
664
        $table = new Table('mytable');
665
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
666
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
667
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
668
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
669
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
670
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
671
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
672
        $table->addColumn('commented_type', 'object');
673
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
674
        $table->addColumn('comment_with_string_literal_char', 'string', [
675
            'length' => 255,
676
            'comment' => "O'Reilly",
677
        ]);
678
        $table->setPrimaryKey(['id']);
679
680
        self::assertEquals(
681
            [
682
                '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))',
683
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
684
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz you!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
685
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for explicitly quoted columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
686
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for reserved keyword columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
687
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
688
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine array type.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
689
                "EXEC sp_addextendedproperty N'MS_Description', N'O''Reilly', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
690
            ],
691
            $this->platform->getCreateTableSQL($table)
692
        );
693
    }
694
695
    /**
696
     * @group DBAL-543
697
     * @group DBAL-1011
698
     */
699
    public function testGeneratesAlterTableSQLWithColumnComments() : void
700
    {
701
        $table = new Table('mytable');
702
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
703
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
704
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
705
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
706
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
707
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
708
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
709
        $table->addColumn('commented_type', 'object');
710
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
711
        $table->addColumn('comment_with_string_literal_quote_char', 'array', [
712
            'length' => 255,
713
            'comment' => "O'Reilly",
714
        ]);
715
        $table->setPrimaryKey(['id']);
716
717
        $tableDiff                                                         = new TableDiff('mytable');
718
        $tableDiff->fromTable                                              = $table;
719
        $tableDiff->addedColumns['added_comment_none']                     = new Column('added_comment_none', Type::getType('integer'));
720
        $tableDiff->addedColumns['added_comment_null']                     = new Column('added_comment_null', Type::getType('integer'), ['comment' => null]);
721
        $tableDiff->addedColumns['added_comment_empty_string']             = new Column('added_comment_empty_string', Type::getType('integer'), ['comment' => '']);
722
        $tableDiff->addedColumns['added_comment_string_0']                 = new Column('added_comment_string_0', Type::getType('integer'), ['comment' => '0']);
723
        $tableDiff->addedColumns['added_comment']                          = new Column('added_comment', Type::getType('integer'), ['comment' => 'Doctrine']);
724
        $tableDiff->addedColumns['`added_comment_quoted`']                 = new Column('`added_comment_quoted`', Type::getType('integer'), ['comment' => 'rulez']);
725
        $tableDiff->addedColumns['select']                                 = new Column('select', Type::getType('integer'), ['comment' => '666']);
726
        $tableDiff->addedColumns['added_commented_type']                   = new Column('added_commented_type', Type::getType('object'));
727
        $tableDiff->addedColumns['added_commented_type_with_comment']      = new Column('added_commented_type_with_comment', Type::getType('array'), ['comment' => '666']);
728
        $tableDiff->addedColumns['added_comment_with_string_literal_char'] = new Column('added_comment_with_string_literal_char', Type::getType('string'), [
729
            'length' => 255,
730
            'comment' => "''",
731
        ]);
732
733
        // Add comment to non-commented column.
734
        $tableDiff->changedColumns['id'] = new ColumnDiff(
735
            'id',
736
            new Column('id', Type::getType('integer'), ['autoincrement' => true, 'comment' => 'primary']),
737
            ['comment'],
738
            new Column('id', Type::getType('integer'), ['autoincrement' => true])
739
        );
740
741
        // Remove comment from null-commented column.
742
        $tableDiff->changedColumns['comment_null'] = new ColumnDiff(
743
            'comment_null',
744
            new Column('comment_null', Type::getType('string'), ['length' => 255]),
745
            ['type'],
746
            new Column('comment_null', Type::getType('integer'), ['comment' => null])
747
        );
748
749
        // Change type to custom type from empty string commented column.
750
        $tableDiff->changedColumns['comment_empty_string'] = new ColumnDiff(
751
            'comment_empty_string',
752
            new Column('comment_empty_string', Type::getType('object')),
753
            ['type'],
754
            new Column('comment_empty_string', Type::getType('integer'), ['comment' => ''])
755
        );
756
757
        // Change comment to empty comment from zero-string commented column.
758
        $tableDiff->changedColumns['comment_string_0'] = new ColumnDiff(
759
            'comment_string_0',
760
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '']),
761
            ['comment'],
762
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '0'])
763
        );
764
765
        // Remove comment from regular commented column.
766
        $tableDiff->changedColumns['comment'] = new ColumnDiff(
767
            'comment',
768
            new Column('comment', Type::getType('integer')),
769
            ['comment'],
770
            new Column('comment', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
771
        );
772
773
        // Change comment and change type to custom type from regular commented column.
774
        $tableDiff->changedColumns['`comment_quoted`'] = new ColumnDiff(
775
            '`comment_quoted`',
776
            new Column('`comment_quoted`', Type::getType('array'), ['comment' => 'Doctrine array.']),
777
            ['comment', 'type'],
778
            new Column('`comment_quoted`', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
779
        );
780
781
        // Remove comment and change type to custom type from regular commented column.
782
        $tableDiff->changedColumns['create'] = new ColumnDiff(
783
            'create',
784
            new Column('create', Type::getType('object')),
785
            ['comment', 'type'],
786
            new Column('create', Type::getType('integer'), ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!'])
787
        );
788
789
        // Add comment and change custom type to regular type from non-commented column.
790
        $tableDiff->changedColumns['commented_type'] = new ColumnDiff(
791
            'commented_type',
792
            new Column('commented_type', Type::getType('integer'), ['comment' => 'foo']),
793
            ['comment', 'type'],
794
            new Column('commented_type', Type::getType('object'))
795
        );
796
797
        // Remove comment from commented custom type column.
798
        $tableDiff->changedColumns['commented_type_with_comment'] = new ColumnDiff(
799
            'commented_type_with_comment',
800
            new Column('commented_type_with_comment', Type::getType('array')),
801
            ['comment'],
802
            new Column('commented_type_with_comment', Type::getType('array'), ['comment' => 'Doctrine array type.'])
803
        );
804
805
        // Change comment from comment with string literal char column.
806
        $tableDiff->changedColumns['comment_with_string_literal_char'] = new ColumnDiff(
807
            'comment_with_string_literal_char',
808
            new Column('comment_with_string_literal_char', Type::getType('string'), ['comment' => "'"]),
809
            ['comment'],
810
            new Column('comment_with_string_literal_char', Type::getType('array'), ['comment' => "O'Reilly"])
811
        );
812
813
        self::assertEquals(
814
            [
815
                // Added columns.
816
                'ALTER TABLE mytable ADD added_comment_none INT NOT NULL',
817
                'ALTER TABLE mytable ADD added_comment_null INT NOT NULL',
818
                'ALTER TABLE mytable ADD added_comment_empty_string INT NOT NULL',
819
                'ALTER TABLE mytable ADD added_comment_string_0 INT NOT NULL',
820
                'ALTER TABLE mytable ADD added_comment INT NOT NULL',
821
                'ALTER TABLE mytable ADD [added_comment_quoted] INT NOT NULL',
822
                'ALTER TABLE mytable ADD [select] INT NOT NULL',
823
                'ALTER TABLE mytable ADD added_commented_type VARCHAR(MAX) NOT NULL',
824
                'ALTER TABLE mytable ADD added_commented_type_with_comment VARCHAR(MAX) NOT NULL',
825
                'ALTER TABLE mytable ADD added_comment_with_string_literal_char NVARCHAR(255) NOT NULL',
826
                'ALTER TABLE mytable ALTER COLUMN comment_null NVARCHAR(255) NOT NULL',
827
                'ALTER TABLE mytable ALTER COLUMN comment_empty_string VARCHAR(MAX) NOT NULL',
828
                'ALTER TABLE mytable ALTER COLUMN [comment_quoted] VARCHAR(MAX) NOT NULL',
829
                'ALTER TABLE mytable ALTER COLUMN [create] VARCHAR(MAX) NOT NULL',
830
                'ALTER TABLE mytable ALTER COLUMN commented_type INT NOT NULL',
831
832
                // Added columns.
833
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_string_0",
834
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment",
835
                "EXEC sp_addextendedproperty N'MS_Description', N'rulez', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [added_comment_quoted]",
836
                "EXEC sp_addextendedproperty N'MS_Description', N'666', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [select]",
837
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type",
838
                "EXEC sp_addextendedproperty N'MS_Description', N'666(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type_with_comment",
839
                "EXEC sp_addextendedproperty N'MS_Description', N'''''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_with_string_literal_char",
840
841
                // Changed columns.
842
                "EXEC sp_addextendedproperty N'MS_Description', N'primary', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', id",
843
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_empty_string",
844
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
845
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
846
                "EXEC sp_updateextendedproperty N'MS_Description', N'Doctrine array.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
847
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
848
                "EXEC sp_updateextendedproperty N'MS_Description', N'foo', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
849
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
850
                "EXEC sp_updateextendedproperty N'MS_Description', N'''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
851
            ],
852
            $this->platform->getAlterTableSQL($tableDiff)
853
        );
854
    }
855
856
    /**
857
     * @group DBAL-122
858
     */
859
    public function testInitializesDoctrineTypeMappings() : void
860
    {
861
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bigint'));
862
        self::assertSame('bigint', $this->platform->getDoctrineTypeMapping('bigint'));
863
864
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('numeric'));
865
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('numeric'));
866
867
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bit'));
868
        self::assertSame('boolean', $this->platform->getDoctrineTypeMapping('bit'));
869
870
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallint'));
871
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('smallint'));
872
873
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('decimal'));
874
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('decimal'));
875
876
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallmoney'));
877
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('smallmoney'));
878
879
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('int'));
880
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('int'));
881
882
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tinyint'));
883
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('tinyint'));
884
885
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('money'));
886
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('money'));
887
888
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('float'));
889
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('float'));
890
891
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real'));
892
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('real'));
893
894
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double'));
895
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double'));
896
897
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double precision'));
898
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double precision'));
899
900
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smalldatetime'));
901
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('smalldatetime'));
902
903
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('datetime'));
904
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('datetime'));
905
906
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('char'));
907
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('char'));
908
909
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varchar'));
910
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('varchar'));
911
912
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('text'));
913
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('text'));
914
915
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nchar'));
916
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nchar'));
917
918
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nvarchar'));
919
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nvarchar'));
920
921
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('ntext'));
922
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('ntext'));
923
924
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('binary'));
925
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('binary'));
926
927
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varbinary'));
928
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('varbinary'));
929
930
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('image'));
931
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('image'));
932
933
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('uniqueidentifier'));
934
        self::assertSame('guid', $this->platform->getDoctrineTypeMapping('uniqueidentifier'));
935
    }
936
937
    protected function getExpectedFixedLengthStringTypeDeclarationSQLNoLength() : string
938
    {
939
        return 'NCHAR';
940
    }
941
942
    protected function getExpectedFixedLengthStringTypeDeclarationSQLWithLength() : string
943
    {
944
        return 'NCHAR(16)';
945
    }
946
947
    public function testGetVariableLengthStringTypeDeclarationSQLNoLength() : void
948
    {
949
        $this->expectException(ColumnLengthRequired::class);
950
951
        parent::testGetVariableLengthStringTypeDeclarationSQLNoLength();
952
    }
953
954
    protected function getExpectedVariableLengthStringTypeDeclarationSQLWithLength() : string
955
    {
956
        return 'NVARCHAR(16)';
957
    }
958
959
    public function testGetVariableLengthBinaryTypeDeclarationSQLNoLength() : void
960
    {
961
        $this->expectException(ColumnLengthRequired::class);
962
963
        parent::testGetVariableLengthBinaryTypeDeclarationSQLNoLength();
964
    }
965
966
    /**
967
     * {@inheritDoc}
968
     *
969
     * @group DBAL-234
970
     */
971
    protected function getAlterTableRenameIndexSQL() : array
972
    {
973
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_bar', N'INDEX'"];
974
    }
975
976
    /**
977
     * {@inheritDoc}
978
     *
979
     * @group DBAL-234
980
     */
981
    protected function getQuotedAlterTableRenameIndexSQL() : array
982
    {
983
        return [
984
            "EXEC sp_RENAME N'[table].[create]', N'[select]', N'INDEX'",
985
            "EXEC sp_RENAME N'[table].[foo]', N'[bar]', N'INDEX'",
986
        ];
987
    }
988
989
    /**
990
     * @group DBAL-825
991
     */
992
    public function testChangeColumnsTypeWithDefaultValue() : void
993
    {
994
        $tableName = 'column_def_change_type';
995
        $table     = new Table($tableName);
996
997
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
998
        $table->addColumn('col_string', 'string', ['default' => 'foo']);
999
1000
        $tableDiff                            = new TableDiff($tableName);
1001
        $tableDiff->fromTable                 = $table;
1002
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
1003
            'col_int',
1004
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
1005
            ['type'],
1006
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
1007
        );
1008
1009
        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
1010
            'col_string',
1011
            new Column('col_string', Type::getType('string'), [
1012
                'length' => 255,
1013
                'fixed' => true,
1014
                'default' => 'foo',
1015
            ]),
1016
            ['fixed'],
1017
            new Column('col_string', Type::getType('string'), ['default' => 'foo'])
1018
        );
1019
1020
        self::assertSame(
1021
            [
1022
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_FA2CB292',
1023
                'ALTER TABLE column_def_change_type ALTER COLUMN col_int INT NOT NULL',
1024
                'ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_FA2CB292 DEFAULT 666 FOR col_int',
1025
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_2725A6D0',
1026
                'ALTER TABLE column_def_change_type ALTER COLUMN col_string NCHAR(255) NOT NULL',
1027
                "ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_2725A6D0 DEFAULT 'foo' FOR col_string",
1028
            ],
1029
            $this->platform->getAlterTableSQL($tableDiff)
1030
        );
1031
    }
1032
1033
    /**
1034
     * {@inheritdoc}
1035
     */
1036
    protected function getQuotedAlterTableRenameColumnSQL() : array
1037
    {
1038
        return [
1039
            "sp_RENAME 'mytable.unquoted1', 'unquoted', 'COLUMN'",
1040
            "sp_RENAME 'mytable.unquoted2', '[where]', 'COLUMN'",
1041
            "sp_RENAME 'mytable.unquoted3', '[foo]', 'COLUMN'",
1042
            "sp_RENAME 'mytable.[create]', 'reserved_keyword', 'COLUMN'",
1043
            "sp_RENAME 'mytable.[table]', '[from]', 'COLUMN'",
1044
            "sp_RENAME 'mytable.[select]', '[bar]', 'COLUMN'",
1045
            "sp_RENAME 'mytable.quoted1', 'quoted', 'COLUMN'",
1046
            "sp_RENAME 'mytable.quoted2', '[and]', 'COLUMN'",
1047
            "sp_RENAME 'mytable.quoted3', '[baz]', 'COLUMN'",
1048
        ];
1049
    }
1050
1051
    /**
1052
     * {@inheritdoc}
1053
     */
1054
    protected function getQuotedAlterTableChangeColumnLengthSQL() : array
1055
    {
1056
        $this->markTestIncomplete('Not implemented yet');
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return array. Consider adding a return statement or allowing null as return value.

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

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

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