AbstractSQLServerPlatformTestCase.php 71.3 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Platforms;

Sergei Morozov's avatar
Sergei Morozov committed
5
use Doctrine\DBAL\DBALException;
Grégoire Paris's avatar
Grégoire Paris committed
6
use Doctrine\DBAL\Platforms\SQLServerPlatform;
7 8
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ColumnDiff;
Sergei Morozov's avatar
Sergei Morozov committed
9
use Doctrine\DBAL\Schema\Index;
10 11
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Schema\TableDiff;
12
use Doctrine\DBAL\TransactionIsolationLevel;
13
use Doctrine\DBAL\Types\Type;
14

15
use function sprintf;
16 17 18

abstract class AbstractSQLServerPlatformTestCase extends AbstractPlatformTestCase
{
Grégoire Paris's avatar
Grégoire Paris committed
19 20 21
    /** @var SQLServerPlatform */
    protected $platform;

22
    public function getGenerateTableSql(): string
23 24 25 26
    {
        return 'CREATE TABLE test (id INT IDENTITY NOT NULL, test NVARCHAR(255), PRIMARY KEY (id))';
    }

27 28 29
    /**
     * {@inheritDoc}
     */
30
    public function getGenerateTableWithMultiColumnUniqueIndexSql(): array
31
    {
Sergei Morozov's avatar
Sergei Morozov committed
32
        return [
33
            'CREATE TABLE test (foo NVARCHAR(255), bar NVARCHAR(255))',
Sergei Morozov's avatar
Sergei Morozov committed
34 35
            'CREATE UNIQUE INDEX UNIQ_D87F7E0C8C73652176FF8CAA ON test (foo, bar) WHERE foo IS NOT NULL AND bar IS NOT NULL',
        ];
36 37
    }

38 39 40
    /**
     * {@inheritDoc}
     */
41
    public function getGenerateAlterTableSql(): array
42
    {
Sergei Morozov's avatar
Sergei Morozov committed
43
        return [
44 45 46 47 48 49 50 51 52 53
            'ALTER TABLE mytable ADD quota INT',
            'ALTER TABLE mytable DROP COLUMN foo',
            'ALTER TABLE mytable ALTER COLUMN baz NVARCHAR(255) NOT NULL',
            "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_78240498 DEFAULT 'def' FOR baz",
            'ALTER TABLE mytable ALTER COLUMN bloo BIT NOT NULL',
            "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_CECED971 DEFAULT '0' FOR bloo",
            "sp_RENAME 'mytable', 'userlist'",
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' " .
            "+ REPLACE(dc.name, '6B2BD609', 'E2B58069') + ''', ''OBJECT'';' " .
Sergei Morozov's avatar
Sergei Morozov committed
54 55
            'FROM sys.default_constraints dc ' .
            'JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id ' .
56
            "WHERE tbl.name = 'userlist';" .
Sergei Morozov's avatar
Sergei Morozov committed
57 58
            'EXEC sp_executesql @sql',
        ];
59 60
    }

61
    public function testDoesNotSupportRegexp(): void
62
    {
63 64
        $this->expectException(DBALException::class);

Sergei Morozov's avatar
Sergei Morozov committed
65
        $this->platform->getRegexpExpression();
66 67
    }

68
    public function testGeneratesSqlSnippets(): void
69
    {
Sergei Morozov's avatar
Sergei Morozov committed
70 71 72 73 74
        self::assertEquals('CONVERT(date, GETDATE())', $this->platform->getCurrentDateSQL());
        self::assertEquals('CONVERT(time, GETDATE())', $this->platform->getCurrentTimeSQL());
        self::assertEquals('CURRENT_TIMESTAMP', $this->platform->getCurrentTimestampSQL());
        self::assertEquals('"', $this->platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct');
        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',
Sergei Morozov's avatar
Sergei Morozov committed
81
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_UNCOMMITTED)
82
        );
83
        self::assertEquals(
84
            'SET TRANSACTION ISOLATION LEVEL READ COMMITTED',
Sergei Morozov's avatar
Sergei Morozov committed
85
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::READ_COMMITTED)
86
        );
87
        self::assertEquals(
88
            'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ',
Sergei Morozov's avatar
Sergei Morozov committed
89
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::REPEATABLE_READ)
90
        );
91
        self::assertEquals(
92
            'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE',
Sergei Morozov's avatar
Sergei Morozov committed
93
            $this->platform->getSetTransactionIsolationSQL(TransactionIsolationLevel::SERIALIZABLE)
94 95 96
        );
    }

97
    public function testGeneratesDDLSnippets(): void
98 99 100
    {
        $dropDatabaseExpectation = 'DROP DATABASE foobar';

Sergei Morozov's avatar
Sergei Morozov committed
101 102 103 104
        self::assertEquals('SELECT * FROM sys.databases', $this->platform->getListDatabasesSQL());
        self::assertEquals('CREATE DATABASE foobar', $this->platform->getCreateDatabaseSQL('foobar'));
        self::assertEquals($dropDatabaseExpectation, $this->platform->getDropDatabaseSQL('foobar'));
        self::assertEquals('DROP TABLE foobar', $this->platform->getDropTableSQL('foobar'));
105 106
    }

107
    public function testGeneratesTypeDeclarationForIntegers(): void
108
    {
109
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
110
            'INT',
Sergei Morozov's avatar
Sergei Morozov committed
111
            $this->platform->getIntegerTypeDeclarationSQL([])
112
        );
113
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
114
            'INT IDENTITY',
Sergei Morozov's avatar
Sergei Morozov committed
115
            $this->platform->getIntegerTypeDeclarationSQL(['autoincrement' => true])
Sergei Morozov's avatar
Sergei Morozov committed
116
        );
117
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
118
            'INT IDENTITY',
Sergei Morozov's avatar
Sergei Morozov committed
119
            $this->platform->getIntegerTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
120 121 122
                ['autoincrement' => true, 'primary' => true]
            )
        );
123 124
    }

125
    public function testGeneratesTypeDeclarationsForStrings(): void
126
    {
127
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
128
            'NCHAR(10)',
Sergei Morozov's avatar
Sergei Morozov committed
129
            $this->platform->getVarcharTypeDeclarationSQL(
Sergei Morozov's avatar
Sergei Morozov committed
130 131 132
                ['length' => 10, 'fixed' => true]
            )
        );
133
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
134
            'NVARCHAR(50)',
Sergei Morozov's avatar
Sergei Morozov committed
135
            $this->platform->getVarcharTypeDeclarationSQL(['length' => 50]),
Sergei Morozov's avatar
Sergei Morozov committed
136
            'Variable string declaration is not correct'
137
        );
138
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
139
            'NVARCHAR(255)',
Sergei Morozov's avatar
Sergei Morozov committed
140
            $this->platform->getVarcharTypeDeclarationSQL([]),
Sergei Morozov's avatar
Sergei Morozov committed
141
            'Long string declaration is not correct'
142
        );
Sergei Morozov's avatar
Sergei Morozov committed
143
        self::assertSame('VARCHAR(MAX)', $this->platform->getClobTypeDeclarationSQL([]));
144
        self::assertSame(
145
            'VARCHAR(MAX)',
Sergei Morozov's avatar
Sergei Morozov committed
146
            $this->platform->getClobTypeDeclarationSQL(['length' => 5, 'fixed' => true])
147 148 149
        );
    }

150
    public function testPrefersIdentityColumns(): void
151
    {
Sergei Morozov's avatar
Sergei Morozov committed
152
        self::assertTrue($this->platform->prefersIdentityColumns());
153 154
    }

155
    public function testSupportsIdentityColumns(): void
156
    {
Sergei Morozov's avatar
Sergei Morozov committed
157
        self::assertTrue($this->platform->supportsIdentityColumns());
158 159
    }

160
    public function testSupportsCreateDropDatabase(): void
161
    {
Sergei Morozov's avatar
Sergei Morozov committed
162
        self::assertTrue($this->platform->supportsCreateDropDatabase());
163 164
    }

165
    public function testSupportsSchemas(): void
166
    {
Sergei Morozov's avatar
Sergei Morozov committed
167
        self::assertTrue($this->platform->supportsSchemas());
168 169
    }

170
    public function testDoesNotSupportSavePoints(): void
171
    {
Sergei Morozov's avatar
Sergei Morozov committed
172
        self::assertTrue($this->platform->supportsSavepoints());
173 174
    }

175
    public function getGenerateIndexSql(): string
176 177 178 179
    {
        return 'CREATE INDEX my_idx ON mytable (user_name, last_login)';
    }

180
    public function getGenerateUniqueIndexSql(): string
181 182 183 184
    {
        return 'CREATE UNIQUE INDEX index_name ON test (test, test2) WHERE test IS NOT NULL AND test2 IS NOT NULL';
    }

185
    protected function getGenerateForeignKeySql(): string
186 187 188 189
    {
        return 'ALTER TABLE test ADD FOREIGN KEY (fk_name_id) REFERENCES other_table (id)';
    }

190
    public function testModifyLimitQuery(): void
191
    {
Sergei Morozov's avatar
Sergei Morozov committed
192
        $querySql   = 'SELECT * FROM user';
193
        $alteredSql = 'SELECT TOP 10 * FROM user';
Sergei Morozov's avatar
Sergei Morozov committed
194
        $sql        = $this->platform->modifyLimitQuery($querySql, 10, 0);
195
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
196 197
    }

198
    public function testModifyLimitQueryWithEmptyOffset(): void
199
    {
Sergei Morozov's avatar
Sergei Morozov committed
200
        $querySql   = 'SELECT * FROM user';
201
        $alteredSql = 'SELECT TOP 10 * FROM user';
Sergei Morozov's avatar
Sergei Morozov committed
202
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
203
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
204 205
    }

206
    public function testModifyLimitQueryWithOffset(): void
207
    {
Sergei Morozov's avatar
Sergei Morozov committed
208 209
        if (! $this->platform->supportsLimitOffset()) {
            $this->markTestSkipped(sprintf('Platform "%s" does not support offsets in result limiting.', $this->platform->getName()));
210 211
        }

Sergei Morozov's avatar
Sergei Morozov committed
212
        $querySql   = 'SELECT * FROM user ORDER BY username DESC';
213
        $alteredSql = 'SELECT TOP 15 * FROM user ORDER BY username DESC';
Sergei Morozov's avatar
Sergei Morozov committed
214
        $sql        = $this->platform->modifyLimitQuery($querySql, 10, 5);
215

216
        $this->expectCteWithMinAndMaxRowNums($alteredSql, 6, 15, $sql);
217 218
    }

219
    public function testModifyLimitQueryWithAscOrderBy(): void
220
    {
Sergei Morozov's avatar
Sergei Morozov committed
221
        $querySql   = 'SELECT * FROM user ORDER BY username ASC';
222
        $alteredSql = 'SELECT TOP 10 * FROM user ORDER BY username ASC';
Sergei Morozov's avatar
Sergei Morozov committed
223
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
224

225
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
226
    }
227

228
    public function testModifyLimitQueryWithLowercaseOrderBy(): void
229
    {
Sergei Morozov's avatar
Sergei Morozov committed
230
        $querySql   = 'SELECT * FROM user order by username';
231
        $alteredSql = 'SELECT TOP 10 * FROM user order by username';
Sergei Morozov's avatar
Sergei Morozov committed
232
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
233
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
234
    }
235

236
    public function testModifyLimitQueryWithDescOrderBy(): void
237
    {
Sergei Morozov's avatar
Sergei Morozov committed
238
        $querySql   = 'SELECT * FROM user ORDER BY username DESC';
239
        $alteredSql = 'SELECT TOP 10 * FROM user ORDER BY username DESC';
Sergei Morozov's avatar
Sergei Morozov committed
240
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
241
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
242 243
    }

244
    public function testModifyLimitQueryWithMultipleOrderBy(): void
245
    {
Sergei Morozov's avatar
Sergei Morozov committed
246
        $querySql   = 'SELECT * FROM user ORDER BY username DESC, usereamil ASC';
247
        $alteredSql = 'SELECT TOP 10 * FROM user ORDER BY username DESC, usereamil ASC';
Sergei Morozov's avatar
Sergei Morozov committed
248
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
249
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
250 251
    }

252
    public function testModifyLimitQueryWithSubSelect(): void
253
    {
Sergei Morozov's avatar
Sergei Morozov committed
254
        $querySql   = 'SELECT * FROM (SELECT u.id as uid, u.name as uname) dctrn_result';
255
        $alteredSql = 'SELECT TOP 10 * FROM (SELECT u.id as uid, u.name as uname) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
256
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
257
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
258 259
    }

260
    public function testModifyLimitQueryWithSubSelectAndOrder(): void
261
    {
Sergei Morozov's avatar
Sergei Morozov committed
262
        $querySql   = 'SELECT * FROM (SELECT u.id as uid, u.name as uname ORDER BY u.name DESC) dctrn_result';
263
        $alteredSql = 'SELECT TOP 10 * FROM (SELECT u.id as uid, u.name as uname) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
264
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
265
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
266

Sergei Morozov's avatar
Sergei Morozov committed
267
        $querySql   = 'SELECT * FROM (SELECT u.id, u.name ORDER BY u.name DESC) dctrn_result';
268
        $alteredSql = 'SELECT TOP 10 * FROM (SELECT u.id, u.name) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
269
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
270
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
271 272
    }

273
    public function testModifyLimitQueryWithSubSelectAndMultipleOrder(): void
274
    {
Sergei Morozov's avatar
Sergei Morozov committed
275 276
        if (! $this->platform->supportsLimitOffset()) {
            $this->markTestSkipped(sprintf('Platform "%s" does not support offsets in result limiting.', $this->platform->getName()));
277 278
        }

Sergei Morozov's avatar
Sergei Morozov committed
279
        $querySql   = 'SELECT * FROM (SELECT u.id as uid, u.name as uname ORDER BY u.name DESC, id ASC) dctrn_result';
280
        $alteredSql = 'SELECT TOP 15 * FROM (SELECT u.id as uid, u.name as uname) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
281
        $sql        = $this->platform->modifyLimitQuery($querySql, 10, 5);
282
        $this->expectCteWithMinAndMaxRowNums($alteredSql, 6, 15, $sql);
283

Sergei Morozov's avatar
Sergei Morozov committed
284
        $querySql   = 'SELECT * FROM (SELECT u.id uid, u.name uname ORDER BY u.name DESC, id ASC) dctrn_result';
285
        $alteredSql = 'SELECT TOP 15 * FROM (SELECT u.id uid, u.name uname) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
286
        $sql        = $this->platform->modifyLimitQuery($querySql, 10, 5);
287
        $this->expectCteWithMinAndMaxRowNums($alteredSql, 6, 15, $sql);
288

Sergei Morozov's avatar
Sergei Morozov committed
289
        $querySql   = 'SELECT * FROM (SELECT u.id, u.name ORDER BY u.name DESC, id ASC) dctrn_result';
290
        $alteredSql = 'SELECT TOP 15 * FROM (SELECT u.id, u.name) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
291
        $sql        = $this->platform->modifyLimitQuery($querySql, 10, 5);
292
        $this->expectCteWithMinAndMaxRowNums($alteredSql, 6, 15, $sql);
293 294
    }

295
    public function testModifyLimitQueryWithFromColumnNames(): void
296
    {
Sergei Morozov's avatar
Sergei Morozov committed
297
        $querySql   = 'SELECT a.fromFoo, fromBar FROM foo';
298
        $alteredSql = 'SELECT TOP 10 a.fromFoo, fromBar FROM foo';
Sergei Morozov's avatar
Sergei Morozov committed
299
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
300
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
301 302
    }

303
    public function testModifyLimitQueryWithExtraLongQuery(): void
304
    {
Sergei Morozov's avatar
Sergei Morozov committed
305 306 307 308
        $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 ';
        $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) ';
        $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) ';
        $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)';
309

Sergei Morozov's avatar
Sergei Morozov committed
310 311 312 313
        $alteredSql  = 'SELECT TOP 10 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 ';
        $alteredSql .= '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) ';
        $alteredSql .= '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) ';
        $alteredSql .= '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)';
314

Sergei Morozov's avatar
Sergei Morozov committed
315
        $sql = $this->platform->modifyLimitQuery($query, 10);
316
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
317 318
    }

319
    public function testModifyLimitQueryWithOrderByClause(): void
320
    {
Sergei Morozov's avatar
Sergei Morozov committed
321 322
        if (! $this->platform->supportsLimitOffset()) {
            $this->markTestSkipped(sprintf('Platform "%s" does not support offsets in result limiting.', $this->platform->getName()));
323 324
        }

325 326
        $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';
        $alteredSql = 'SELECT TOP 15 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';
Sergei Morozov's avatar
Sergei Morozov committed
327
        $actual     = $this->platform->modifyLimitQuery($sql, 10, 5);
328

329
        $this->expectCteWithMinAndMaxRowNums($alteredSql, 6, 15, $actual);
330 331
    }

332
    public function testModifyLimitQueryWithSubSelectInSelectList(): void
333
    {
Sergei Morozov's avatar
Sergei Morozov committed
334 335 336 337 338 339
        $querySql   = 'SELECT ' .
            'u.id, ' .
            '(u.foo/2) foodiv, ' .
            'CONCAT(u.bar, u.baz) barbaz, ' .
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
            'FROM user u ' .
340
            "WHERE u.status = 'disabled'";
Sergei Morozov's avatar
Sergei Morozov committed
341 342 343 344 345 346
        $alteredSql = 'SELECT TOP 10 ' .
            'u.id, ' .
            '(u.foo/2) foodiv, ' .
            'CONCAT(u.bar, u.baz) barbaz, ' .
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
            'FROM user u ' .
347
            "WHERE u.status = 'disabled'";
Sergei Morozov's avatar
Sergei Morozov committed
348
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
349

350
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
351 352
    }

353
    public function testModifyLimitQueryWithSubSelectInSelectListAndOrderByClause(): void
354
    {
Sergei Morozov's avatar
Sergei Morozov committed
355 356
        if (! $this->platform->supportsLimitOffset()) {
            $this->markTestSkipped(sprintf('Platform "%s" does not support offsets in result limiting.', $this->platform->getName()));
357 358
        }

Sergei Morozov's avatar
Sergei Morozov committed
359 360 361 362 363 364
        $querySql   = 'SELECT ' .
            'u.id, ' .
            '(u.foo/2) foodiv, ' .
            'CONCAT(u.bar, u.baz) barbaz, ' .
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
            'FROM user u ' .
365
            "WHERE u.status = 'disabled' " .
Sergei Morozov's avatar
Sergei Morozov committed
366 367 368 369 370 371 372
            'ORDER BY u.username DESC';
        $alteredSql = 'SELECT TOP 15 ' .
            'u.id, ' .
            '(u.foo/2) foodiv, ' .
            'CONCAT(u.bar, u.baz) barbaz, ' .
            '(SELECT (SELECT COUNT(*) FROM login l WHERE l.profile_id = p.id) FROM profile p WHERE p.user_id = u.id) login_count ' .
            'FROM user u ' .
373
            "WHERE u.status = 'disabled' " .
Sergei Morozov's avatar
Sergei Morozov committed
374
            'ORDER BY u.username DESC';
Sergei Morozov's avatar
Sergei Morozov committed
375
        $sql        = $this->platform->modifyLimitQuery($querySql, 10, 5);
376
        $this->expectCteWithMinAndMaxRowNums($alteredSql, 6, 15, $sql);
377 378
    }

379
    public function testModifyLimitQueryWithAggregateFunctionInOrderByClause(): void
380
    {
Sergei Morozov's avatar
Sergei Morozov committed
381 382 383 384 385 386 387 388 389 390 391 392
        $querySql   = 'SELECT ' .
            'MAX(heading_id) aliased, ' .
            'code ' .
            'FROM operator_model_operator ' .
            'GROUP BY code ' .
            'ORDER BY MAX(heading_id) DESC';
        $alteredSql = 'SELECT TOP 1 ' .
            'MAX(heading_id) aliased, ' .
            'code ' .
            'FROM operator_model_operator ' .
            'GROUP BY code ' .
            'ORDER BY MAX(heading_id) DESC';
Sergei Morozov's avatar
Sergei Morozov committed
393
        $sql        = $this->platform->modifyLimitQuery($querySql, 1, 0);
394
        $this->expectCteWithMaxRowNum($alteredSql, 1, $sql);
395 396 397
    }

    /**
Sergei Morozov's avatar
Sergei Morozov committed
398
     * @throws DBALException
399
     */
400
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromBaseTable(): void
401
    {
Sergei Morozov's avatar
Sergei Morozov committed
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
            . 'FROM ('
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
            . 'FROM table_parent t1 '
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id '
            . 'ORDER BY t1.id ASC'
            . ') dctrn_result '
            . 'ORDER BY id_0 ASC';
        $alteredSql = 'SELECT DISTINCT TOP 5 id_0, name_1 '
            . 'FROM ('
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
            . 'FROM table_parent t1 '
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
            . ') dctrn_result '
            . 'ORDER BY id_0 ASC';
Sergei Morozov's avatar
Sergei Morozov committed
417
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
418
        $this->expectCteWithMaxRowNum($alteredSql, 5, $sql);
419 420 421
    }

    /**
Sergei Morozov's avatar
Sergei Morozov committed
422
     * @throws DBALException
423
     */
424
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnFromJoinTable(): void
425
    {
Sergei Morozov's avatar
Sergei Morozov committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
        $querySql   = 'SELECT DISTINCT id_0, name_1 '
            . 'FROM ('
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
            . 'FROM table_parent t1 '
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id '
            . 'ORDER BY t2.name ASC'
            . ') dctrn_result '
            . 'ORDER BY name_1 ASC';
        $alteredSql = 'SELECT DISTINCT TOP 5 id_0, name_1 '
            . 'FROM ('
            . 'SELECT t1.id AS id_0, t2.name AS name_1 '
            . 'FROM table_parent t1 '
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
            . ') dctrn_result '
            . 'ORDER BY name_1 ASC';
Sergei Morozov's avatar
Sergei Morozov committed
441
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
442
        $this->expectCteWithMaxRowNum($alteredSql, 5, $sql);
443 444 445
    }

    /**
Sergei Morozov's avatar
Sergei Morozov committed
446
     * @throws DBALException
447
     */
448
    public function testModifyLimitSubqueryWithJoinAndSubqueryOrderedByColumnsFromBothTables(): void
449
    {
Sergei Morozov's avatar
Sergei Morozov committed
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
        $querySql   = 'SELECT DISTINCT id_0, name_1, foo_2 '
            . 'FROM ('
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
            . 'FROM table_parent t1 '
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id '
            . 'ORDER BY t2.name ASC, t2.foo DESC'
            . ') dctrn_result '
            . 'ORDER BY name_1 ASC, foo_2 DESC';
        $alteredSql = 'SELECT DISTINCT TOP 5 id_0, name_1, foo_2 '
            . 'FROM ('
            . 'SELECT t1.id AS id_0, t2.name AS name_1, t2.foo AS foo_2 '
            . 'FROM table_parent t1 '
            . 'LEFT JOIN join_table t2 ON t1.id = t2.table_id'
            . ') dctrn_result '
            . 'ORDER BY name_1 ASC, foo_2 DESC';
Sergei Morozov's avatar
Sergei Morozov committed
465
        $sql        = $this->platform->modifyLimitQuery($querySql, 5);
466
        $this->expectCteWithMaxRowNum($alteredSql, 5, $sql);
467 468
    }

469
    public function testModifyLimitSubquerySimple(): void
470
    {
Sergei Morozov's avatar
Sergei Morozov committed
471
        $querySql   = 'SELECT DISTINCT id_0 FROM '
472 473 474 475
            . '(SELECT k0_.id AS id_0, k0_.column AS column_1 '
            . 'FROM key_table k0_ WHERE (k0_.where_column IN (1))) dctrn_result';
        $alteredSql = 'SELECT DISTINCT TOP 20 id_0 FROM (SELECT k0_.id AS id_0, k0_.column AS column_1 '
            . 'FROM key_table k0_ WHERE (k0_.where_column IN (1))) dctrn_result';
Sergei Morozov's avatar
Sergei Morozov committed
476
        $sql        = $this->platform->modifyLimitQuery($querySql, 20);
477
        $this->expectCteWithMaxRowNum($alteredSql, 20, $sql);
478 479
    }

480
    public function testQuoteIdentifier(): void
481
    {
Sergei Morozov's avatar
Sergei Morozov committed
482 483 484
        self::assertEquals('[fo][o]', $this->platform->quoteIdentifier('fo]o'));
        self::assertEquals('[test]', $this->platform->quoteIdentifier('test'));
        self::assertEquals('[test].[test]', $this->platform->quoteIdentifier('test.test'));
485 486
    }

487
    public function testQuoteSingleIdentifier(): void
488
    {
Sergei Morozov's avatar
Sergei Morozov committed
489 490 491
        self::assertEquals('[fo][o]', $this->platform->quoteSingleIdentifier('fo]o'));
        self::assertEquals('[test]', $this->platform->quoteSingleIdentifier('test'));
        self::assertEquals('[test.test]', $this->platform->quoteSingleIdentifier('test.test'));
492 493
    }

494
    public function testCreateClusteredIndex(): void
495
    {
Sergei Morozov's avatar
Sergei Morozov committed
496
        $idx = new Index('idx', ['id']);
497
        $idx->addFlag('clustered');
Sergei Morozov's avatar
Sergei Morozov committed
498
        self::assertEquals('CREATE CLUSTERED INDEX idx ON tbl (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
499 500
    }

501
    public function testCreateNonClusteredPrimaryKeyInTable(): void
502
    {
Sergei Morozov's avatar
Sergei Morozov committed
503 504 505
        $table = new Table('tbl');
        $table->addColumn('id', 'integer');
        $table->setPrimaryKey(['id']);
506 507
        $table->getIndex('primary')->addFlag('nonclustered');

Sergei Morozov's avatar
Sergei Morozov committed
508
        self::assertEquals(['CREATE TABLE tbl (id INT NOT NULL, PRIMARY KEY NONCLUSTERED (id))'], $this->platform->getCreateTableSQL($table));
509 510
    }

511
    public function testCreateNonClusteredPrimaryKey(): void
512
    {
Sergei Morozov's avatar
Sergei Morozov committed
513
        $idx = new Index('idx', ['id'], false, true);
514
        $idx->addFlag('nonclustered');
Sergei Morozov's avatar
Sergei Morozov committed
515
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY NONCLUSTERED (id)', $this->platform->getCreatePrimaryKeySQL($idx, 'tbl'));
516 517
    }

518
    public function testAlterAddPrimaryKey(): void
519
    {
Sergei Morozov's avatar
Sergei Morozov committed
520
        $idx = new Index('idx', ['id'], false, true);
Sergei Morozov's avatar
Sergei Morozov committed
521
        self::assertEquals('ALTER TABLE tbl ADD PRIMARY KEY (id)', $this->platform->getCreateIndexSQL($idx, 'tbl'));
522 523
    }

524 525 526
    /**
     * {@inheritDoc}
     */
527
    protected function getQuotedColumnInPrimaryKeySQL(): array
528
    {
Sergei Morozov's avatar
Sergei Morozov committed
529
        return ['CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, PRIMARY KEY ([create]))'];
530 531
    }

532 533 534
    /**
     * {@inheritDoc}
     */
535
    protected function getQuotedColumnInIndexSQL(): array
536
    {
Sergei Morozov's avatar
Sergei Morozov committed
537
        return [
538 539
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL)',
            'CREATE INDEX IDX_22660D028FD6E0FB ON [quoted] ([create])',
Sergei Morozov's avatar
Sergei Morozov committed
540
        ];
541 542
    }

543 544 545
    /**
     * {@inheritDoc}
     */
546
    protected function getQuotedNameInIndexSQL(): array
Markus Fasselt's avatar
Markus Fasselt committed
547
    {
Sergei Morozov's avatar
Sergei Morozov committed
548
        return [
Markus Fasselt's avatar
Markus Fasselt committed
549 550
            'CREATE TABLE test (column1 NVARCHAR(255) NOT NULL)',
            'CREATE INDEX [key] ON test (column1)',
Sergei Morozov's avatar
Sergei Morozov committed
551
        ];
Markus Fasselt's avatar
Markus Fasselt committed
552 553
    }

554 555 556
    /**
     * {@inheritDoc}
     */
557
    protected function getQuotedColumnInForeignKeySQL(): array
558
    {
Sergei Morozov's avatar
Sergei Morozov committed
559
        return [
560 561 562 563
            'CREATE TABLE [quoted] ([create] NVARCHAR(255) NOT NULL, foo NVARCHAR(255) NOT NULL, [bar] NVARCHAR(255) NOT NULL)',
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES [foreign] ([create], bar, [foo-bar])',
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_NON_RESERVED_KEYWORD FOREIGN KEY ([create], foo, [bar]) REFERENCES foo ([create], bar, [foo-bar])',
            'ALTER TABLE [quoted] ADD CONSTRAINT FK_WITH_INTENDED_QUOTATION FOREIGN KEY ([create], foo, [bar]) REFERENCES [foo-bar] ([create], bar, [foo-bar])',
Sergei Morozov's avatar
Sergei Morozov committed
564
        ];
565 566
    }

567
    public function testGetCreateSchemaSQL(): void
568 569
    {
        $schemaName = 'schema';
Sergei Morozov's avatar
Sergei Morozov committed
570
        $sql        = $this->platform->getCreateSchemaSQL($schemaName);
571
        self::assertEquals('CREATE SCHEMA ' . $schemaName, $sql);
572 573
    }

574
    public function testCreateTableWithSchemaColumnComments(): void
575 576 577 578 579 580 581 582 583 584
    {
        $table = new Table('testschema.test');
        $table->addColumn('id', 'integer', ['comment' => 'This is a comment']);
        $table->setPrimaryKey(['id']);

        $expectedSql = [
            'CREATE TABLE testschema.test (id INT NOT NULL, PRIMARY KEY (id))',
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'testschema', N'TABLE', 'test', N'COLUMN', id",
        ];

Sergei Morozov's avatar
Sergei Morozov committed
585
        self::assertEquals($expectedSql, $this->platform->getCreateTableSQL($table));
586 587
    }

588
    public function testAlterTableWithSchemaColumnComments(): void
589 590 591 592 593 594 595 596 597
    {
        $tableDiff                        = new TableDiff('testschema.mytable');
        $tableDiff->addedColumns['quota'] = new Column('quota', Type::getType('integer'), ['comment' => 'A comment']);

        $expectedSql = [
            'ALTER TABLE testschema.mytable ADD quota INT NOT NULL',
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota",
        ];

Sergei Morozov's avatar
Sergei Morozov committed
598
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
599 600
    }

601
    public function testAlterTableWithSchemaDropColumnComments(): void
602 603 604 605 606 607 608 609 610 611 612
    {
        $tableDiff                          = new TableDiff('testschema.mytable');
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
            'quota',
            new Column('quota', Type::getType('integer'), []),
            ['comment'],
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
        );

        $expectedSql = ["EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];

Sergei Morozov's avatar
Sergei Morozov committed
613
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
614 615
    }

616
    public function testAlterTableWithSchemaUpdateColumnComments(): void
617 618 619 620 621 622 623 624 625 626 627
    {
        $tableDiff                          = new TableDiff('testschema.mytable');
        $tableDiff->changedColumns['quota'] = new ColumnDiff(
            'quota',
            new Column('quota', Type::getType('integer'), ['comment' => 'B comment']),
            ['comment'],
            new Column('quota', Type::getType('integer'), ['comment' => 'A comment'])
        );

        $expectedSql = ["EXEC sp_updateextendedproperty N'MS_Description', N'B comment', N'SCHEMA', 'testschema', N'TABLE', 'mytable', N'COLUMN', quota"];

Sergei Morozov's avatar
Sergei Morozov committed
628
        self::assertEquals($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
629 630
    }

631
    /**
632
     * {@inheritDoc}
633
     */
634
    public function getCreateTableColumnCommentsSQL(): array
635
    {
Sergei Morozov's avatar
Sergei Morozov committed
636 637
        return [
            'CREATE TABLE test (id INT NOT NULL, PRIMARY KEY (id))',
638
            "EXEC sp_addextendedproperty N'MS_Description', N'This is a comment', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', id",
Sergei Morozov's avatar
Sergei Morozov committed
639
        ];
640 641 642
    }

    /**
643
     * {@inheritDoc}
644
     */
645
    public function getAlterTableColumnCommentsSQL(): array
646
    {
Sergei Morozov's avatar
Sergei Morozov committed
647 648
        return [
            'ALTER TABLE mytable ADD quota INT NOT NULL',
649
            "EXEC sp_addextendedproperty N'MS_Description', N'A comment', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', quota",
650 651
            // todo
            //"EXEC sp_addextendedproperty N'MS_Description', N'B comment', N'SCHEMA', dbo, N'TABLE', mytable, N'COLUMN', baz",
Sergei Morozov's avatar
Sergei Morozov committed
652
        ];
653 654 655
    }

    /**
656
     * {@inheritDoc}
657
     */
658
    public function getCreateTableColumnTypeCommentsSQL(): array
659
    {
Sergei Morozov's avatar
Sergei Morozov committed
660 661
        return [
            'CREATE TABLE test (id INT NOT NULL, data VARCHAR(MAX) NOT NULL, PRIMARY KEY (id))',
662
            "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'test', N'COLUMN', data",
Sergei Morozov's avatar
Sergei Morozov committed
663
        ];
664 665
    }

666
    public function testGeneratesCreateTableSQLWithColumnComments(): void
667 668
    {
        $table = new Table('mytable');
Sergei Morozov's avatar
Sergei Morozov committed
669 670 671 672 673 674 675 676 677 678
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
        $table->addColumn('comment_false', 'integer', ['comment' => false]);
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
        $table->addColumn('comment_integer_0', 'integer', ['comment' => 0]);
        $table->addColumn('comment_float_0', 'integer', ['comment' => 0.0]);
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
679
        $table->addColumn('commented_type', 'object');
Sergei Morozov's avatar
Sergei Morozov committed
680 681 682
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
        $table->addColumn('comment_with_string_literal_char', 'string', ['comment' => "O'Reilly"]);
        $table->setPrimaryKey(['id']);
683

684
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
685 686
            [
                'CREATE TABLE mytable (id INT IDENTITY NOT NULL, comment_null INT NOT NULL, comment_false INT NOT NULL, comment_empty_string INT NOT NULL, comment_integer_0 INT NOT NULL, comment_float_0 INT NOT NULL, comment_string_0 INT NOT NULL, comment INT NOT NULL, [comment_quoted] INT NOT NULL, [create] INT NOT NULL, commented_type VARCHAR(MAX) NOT NULL, commented_type_with_comment VARCHAR(MAX) NOT NULL, comment_with_string_literal_char NVARCHAR(255) NOT NULL, PRIMARY KEY (id))',
687 688 689 690 691 692 693 694 695
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_integer_0",
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_float_0",
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz you!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for explicitly quoted columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine 0wnz comments for reserved keyword columns!', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine array type.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
                "EXEC sp_addextendedproperty N'MS_Description', N'O''Reilly', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
Sergei Morozov's avatar
Sergei Morozov committed
696
            ],
Sergei Morozov's avatar
Sergei Morozov committed
697
            $this->platform->getCreateTableSQL($table)
698 699 700
        );
    }

701
    public function testGeneratesAlterTableSQLWithColumnComments(): void
702 703
    {
        $table = new Table('mytable');
Sergei Morozov's avatar
Sergei Morozov committed
704 705 706 707 708 709 710 711 712 713
        $table->addColumn('id', 'integer', ['autoincrement' => true]);
        $table->addColumn('comment_null', 'integer', ['comment' => null]);
        $table->addColumn('comment_false', 'integer', ['comment' => false]);
        $table->addColumn('comment_empty_string', 'integer', ['comment' => '']);
        $table->addColumn('comment_integer_0', 'integer', ['comment' => 0]);
        $table->addColumn('comment_float_0', 'integer', ['comment' => 0.0]);
        $table->addColumn('comment_string_0', 'integer', ['comment' => '0']);
        $table->addColumn('comment', 'integer', ['comment' => 'Doctrine 0wnz you!']);
        $table->addColumn('`comment_quoted`', 'integer', ['comment' => 'Doctrine 0wnz comments for explicitly quoted columns!']);
        $table->addColumn('create', 'integer', ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!']);
714
        $table->addColumn('commented_type', 'object');
Sergei Morozov's avatar
Sergei Morozov committed
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
        $table->addColumn('commented_type_with_comment', 'array', ['comment' => 'Doctrine array type.']);
        $table->addColumn('comment_with_string_literal_quote_char', 'array', ['comment' => "O'Reilly"]);
        $table->setPrimaryKey(['id']);

        $tableDiff                                                         = new TableDiff('mytable');
        $tableDiff->fromTable                                              = $table;
        $tableDiff->addedColumns['added_comment_none']                     = new Column('added_comment_none', Type::getType('integer'));
        $tableDiff->addedColumns['added_comment_null']                     = new Column('added_comment_null', Type::getType('integer'), ['comment' => null]);
        $tableDiff->addedColumns['added_comment_false']                    = new Column('added_comment_false', Type::getType('integer'), ['comment' => false]);
        $tableDiff->addedColumns['added_comment_empty_string']             = new Column('added_comment_empty_string', Type::getType('integer'), ['comment' => '']);
        $tableDiff->addedColumns['added_comment_integer_0']                = new Column('added_comment_integer_0', Type::getType('integer'), ['comment' => 0]);
        $tableDiff->addedColumns['added_comment_float_0']                  = new Column('added_comment_float_0', Type::getType('integer'), ['comment' => 0.0]);
        $tableDiff->addedColumns['added_comment_string_0']                 = new Column('added_comment_string_0', Type::getType('integer'), ['comment' => '0']);
        $tableDiff->addedColumns['added_comment']                          = new Column('added_comment', Type::getType('integer'), ['comment' => 'Doctrine']);
        $tableDiff->addedColumns['`added_comment_quoted`']                 = new Column('`added_comment_quoted`', Type::getType('integer'), ['comment' => 'rulez']);
        $tableDiff->addedColumns['select']                                 = new Column('select', Type::getType('integer'), ['comment' => '666']);
        $tableDiff->addedColumns['added_commented_type']                   = new Column('added_commented_type', Type::getType('object'));
        $tableDiff->addedColumns['added_commented_type_with_comment']      = new Column('added_commented_type_with_comment', Type::getType('array'), ['comment' => '666']);
        $tableDiff->addedColumns['added_comment_with_string_literal_char'] = new Column('added_comment_with_string_literal_char', Type::getType('string'), ['comment' => "''"]);

        $tableDiff->renamedColumns['comment_float_0'] = new Column('comment_double_0', Type::getType('decimal'), ['comment' => 'Double for real!']);
736 737 738 739

        // Add comment to non-commented column.
        $tableDiff->changedColumns['id'] = new ColumnDiff(
            'id',
Sergei Morozov's avatar
Sergei Morozov committed
740 741 742
            new Column('id', Type::getType('integer'), ['autoincrement' => true, 'comment' => 'primary']),
            ['comment'],
            new Column('id', Type::getType('integer'), ['autoincrement' => true])
743 744 745 746 747 748
        );

        // Remove comment from null-commented column.
        $tableDiff->changedColumns['comment_null'] = new ColumnDiff(
            'comment_null',
            new Column('comment_null', Type::getType('string')),
Sergei Morozov's avatar
Sergei Morozov committed
749 750
            ['type'],
            new Column('comment_null', Type::getType('integer'), ['comment' => null])
751 752 753 754 755
        );

        // Add comment to false-commented column.
        $tableDiff->changedColumns['comment_false'] = new ColumnDiff(
            'comment_false',
Sergei Morozov's avatar
Sergei Morozov committed
756 757 758
            new Column('comment_false', Type::getType('integer'), ['comment' => 'false']),
            ['comment'],
            new Column('comment_false', Type::getType('integer'), ['comment' => false])
759 760 761 762 763 764
        );

        // Change type to custom type from empty string commented column.
        $tableDiff->changedColumns['comment_empty_string'] = new ColumnDiff(
            'comment_empty_string',
            new Column('comment_empty_string', Type::getType('object')),
Sergei Morozov's avatar
Sergei Morozov committed
765 766
            ['type'],
            new Column('comment_empty_string', Type::getType('integer'), ['comment' => ''])
767 768 769 770 771
        );

        // Change comment to false-comment from zero-string commented column.
        $tableDiff->changedColumns['comment_string_0'] = new ColumnDiff(
            'comment_string_0',
Sergei Morozov's avatar
Sergei Morozov committed
772 773 774
            new Column('comment_string_0', Type::getType('integer'), ['comment' => false]),
            ['comment'],
            new Column('comment_string_0', Type::getType('integer'), ['comment' => '0'])
775 776 777 778 779 780
        );

        // Remove comment from regular commented column.
        $tableDiff->changedColumns['comment'] = new ColumnDiff(
            'comment',
            new Column('comment', Type::getType('integer')),
Sergei Morozov's avatar
Sergei Morozov committed
781 782
            ['comment'],
            new Column('comment', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
783 784 785 786 787
        );

        // Change comment and change type to custom type from regular commented column.
        $tableDiff->changedColumns['`comment_quoted`'] = new ColumnDiff(
            '`comment_quoted`',
Sergei Morozov's avatar
Sergei Morozov committed
788 789 790
            new Column('`comment_quoted`', Type::getType('array'), ['comment' => 'Doctrine array.']),
            ['comment', 'type'],
            new Column('`comment_quoted`', Type::getType('integer'), ['comment' => 'Doctrine 0wnz you!'])
791 792 793 794 795 796
        );

        // Remove comment and change type to custom type from regular commented column.
        $tableDiff->changedColumns['create'] = new ColumnDiff(
            'create',
            new Column('create', Type::getType('object')),
Sergei Morozov's avatar
Sergei Morozov committed
797 798
            ['comment', 'type'],
            new Column('create', Type::getType('integer'), ['comment' => 'Doctrine 0wnz comments for reserved keyword columns!'])
799 800 801 802 803
        );

        // Add comment and change custom type to regular type from non-commented column.
        $tableDiff->changedColumns['commented_type'] = new ColumnDiff(
            'commented_type',
Sergei Morozov's avatar
Sergei Morozov committed
804 805
            new Column('commented_type', Type::getType('integer'), ['comment' => 'foo']),
            ['comment', 'type'],
806 807 808 809 810 811 812
            new Column('commented_type', Type::getType('object'))
        );

        // Remove comment from commented custom type column.
        $tableDiff->changedColumns['commented_type_with_comment'] = new ColumnDiff(
            'commented_type_with_comment',
            new Column('commented_type_with_comment', Type::getType('array')),
Sergei Morozov's avatar
Sergei Morozov committed
813 814
            ['comment'],
            new Column('commented_type_with_comment', Type::getType('array'), ['comment' => 'Doctrine array type.'])
815 816
        );

817 818 819
        // Change comment from comment with string literal char column.
        $tableDiff->changedColumns['comment_with_string_literal_char'] = new ColumnDiff(
            'comment_with_string_literal_char',
Sergei Morozov's avatar
Sergei Morozov committed
820 821 822
            new Column('comment_with_string_literal_char', Type::getType('string'), ['comment' => "'"]),
            ['comment'],
            new Column('comment_with_string_literal_char', Type::getType('array'), ['comment' => "O'Reilly"])
823 824
        );

Sergei Morozov's avatar
Sergei Morozov committed
825
        $tableDiff->removedColumns['comment_integer_0'] = new Column('comment_integer_0', Type::getType('integer'), ['comment' => 0]);
826

827
        self::assertEquals(
Sergei Morozov's avatar
Sergei Morozov committed
828
            [
829 830 831 832
                // Renamed columns.
                "sp_RENAME 'mytable.comment_float_0', 'comment_double_0', 'COLUMN'",

                // Added columns.
Sergei Morozov's avatar
Sergei Morozov committed
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
                'ALTER TABLE mytable ADD added_comment_none INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment_null INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment_false INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment_empty_string INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment_integer_0 INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment_float_0 INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment_string_0 INT NOT NULL',
                'ALTER TABLE mytable ADD added_comment INT NOT NULL',
                'ALTER TABLE mytable ADD [added_comment_quoted] INT NOT NULL',
                'ALTER TABLE mytable ADD [select] INT NOT NULL',
                'ALTER TABLE mytable ADD added_commented_type VARCHAR(MAX) NOT NULL',
                'ALTER TABLE mytable ADD added_commented_type_with_comment VARCHAR(MAX) NOT NULL',
                'ALTER TABLE mytable ADD added_comment_with_string_literal_char NVARCHAR(255) NOT NULL',
                'ALTER TABLE mytable DROP COLUMN comment_integer_0',
                'ALTER TABLE mytable ALTER COLUMN comment_null NVARCHAR(255) NOT NULL',
                'ALTER TABLE mytable ALTER COLUMN comment_empty_string VARCHAR(MAX) NOT NULL',
                'ALTER TABLE mytable ALTER COLUMN [comment_quoted] VARCHAR(MAX) NOT NULL',
                'ALTER TABLE mytable ALTER COLUMN [create] VARCHAR(MAX) NOT NULL',
                'ALTER TABLE mytable ALTER COLUMN commented_type INT NOT NULL',
852 853

                // Added columns.
854 855 856 857 858 859 860 861 862
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_integer_0",
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_float_0",
                "EXEC sp_addextendedproperty N'MS_Description', N'0', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_string_0",
                "EXEC sp_addextendedproperty N'MS_Description', N'Doctrine', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment",
                "EXEC sp_addextendedproperty N'MS_Description', N'rulez', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [added_comment_quoted]",
                "EXEC sp_addextendedproperty N'MS_Description', N'666', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [select]",
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type",
                "EXEC sp_addextendedproperty N'MS_Description', N'666(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_commented_type_with_comment",
                "EXEC sp_addextendedproperty N'MS_Description', N'''''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', added_comment_with_string_literal_char",
863 864

                // Changed columns.
865 866 867 868 869 870 871 872 873 874
                "EXEC sp_addextendedproperty N'MS_Description', N'primary', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', id",
                "EXEC sp_addextendedproperty N'MS_Description', N'false', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_false",
                "EXEC sp_addextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_empty_string",
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_string_0",
                "EXEC sp_dropextendedproperty N'MS_Description', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment",
                "EXEC sp_updateextendedproperty N'MS_Description', N'Doctrine array.(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [comment_quoted]",
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:object)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', [create]",
                "EXEC sp_updateextendedproperty N'MS_Description', N'foo', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type",
                "EXEC sp_updateextendedproperty N'MS_Description', N'(DC2Type:array)', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', commented_type_with_comment",
                "EXEC sp_updateextendedproperty N'MS_Description', N'''', N'SCHEMA', 'dbo', N'TABLE', 'mytable', N'COLUMN', comment_with_string_literal_char",
Sergei Morozov's avatar
Sergei Morozov committed
875
            ],
Sergei Morozov's avatar
Sergei Morozov committed
876
            $this->platform->getAlterTableSQL($tableDiff)
877 878 879
        );
    }

880
    public function testInitializesDoctrineTypeMappings(): void
881
    {
Sergei Morozov's avatar
Sergei Morozov committed
882 883
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bigint'));
        self::assertSame('bigint', $this->platform->getDoctrineTypeMapping('bigint'));
884

Sergei Morozov's avatar
Sergei Morozov committed
885 886
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('numeric'));
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('numeric'));
887

Sergei Morozov's avatar
Sergei Morozov committed
888 889
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('bit'));
        self::assertSame('boolean', $this->platform->getDoctrineTypeMapping('bit'));
890

Sergei Morozov's avatar
Sergei Morozov committed
891 892
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallint'));
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('smallint'));
893

Sergei Morozov's avatar
Sergei Morozov committed
894 895
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('decimal'));
        self::assertSame('decimal', $this->platform->getDoctrineTypeMapping('decimal'));
896

Sergei Morozov's avatar
Sergei Morozov committed
897 898
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smallmoney'));
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('smallmoney'));
899

Sergei Morozov's avatar
Sergei Morozov committed
900 901
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('int'));
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('int'));
902

Sergei Morozov's avatar
Sergei Morozov committed
903 904
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('tinyint'));
        self::assertSame('smallint', $this->platform->getDoctrineTypeMapping('tinyint'));
905

Sergei Morozov's avatar
Sergei Morozov committed
906 907
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('money'));
        self::assertSame('integer', $this->platform->getDoctrineTypeMapping('money'));
908

Sergei Morozov's avatar
Sergei Morozov committed
909 910
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('float'));
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('float'));
911

Sergei Morozov's avatar
Sergei Morozov committed
912 913
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('real'));
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('real'));
914

Sergei Morozov's avatar
Sergei Morozov committed
915 916
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double'));
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double'));
917

Sergei Morozov's avatar
Sergei Morozov committed
918 919
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('double precision'));
        self::assertSame('float', $this->platform->getDoctrineTypeMapping('double precision'));
920

Sergei Morozov's avatar
Sergei Morozov committed
921 922
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('smalldatetime'));
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('smalldatetime'));
923

Sergei Morozov's avatar
Sergei Morozov committed
924 925
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('datetime'));
        self::assertSame('datetime', $this->platform->getDoctrineTypeMapping('datetime'));
926

Sergei Morozov's avatar
Sergei Morozov committed
927 928
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('char'));
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('char'));
929

Sergei Morozov's avatar
Sergei Morozov committed
930 931
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varchar'));
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('varchar'));
932

Sergei Morozov's avatar
Sergei Morozov committed
933 934
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('text'));
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('text'));
935

Sergei Morozov's avatar
Sergei Morozov committed
936 937
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nchar'));
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nchar'));
938

Sergei Morozov's avatar
Sergei Morozov committed
939 940
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('nvarchar'));
        self::assertSame('string', $this->platform->getDoctrineTypeMapping('nvarchar'));
941

Sergei Morozov's avatar
Sergei Morozov committed
942 943
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('ntext'));
        self::assertSame('text', $this->platform->getDoctrineTypeMapping('ntext'));
944

Sergei Morozov's avatar
Sergei Morozov committed
945 946
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('binary'));
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('binary'));
947

Sergei Morozov's avatar
Sergei Morozov committed
948 949
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('varbinary'));
        self::assertSame('binary', $this->platform->getDoctrineTypeMapping('varbinary'));
950

Sergei Morozov's avatar
Sergei Morozov committed
951 952
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('image'));
        self::assertSame('blob', $this->platform->getDoctrineTypeMapping('image'));
953

Sergei Morozov's avatar
Sergei Morozov committed
954 955
        self::assertTrue($this->platform->hasDoctrineTypeMappingFor('uniqueidentifier'));
        self::assertSame('guid', $this->platform->getDoctrineTypeMapping('uniqueidentifier'));
956 957
    }

958
    protected function getBinaryMaxLength(): int
959 960 961 962
    {
        return 8000;
    }

963
    public function testReturnsBinaryTypeDeclarationSQL(): void
964
    {
Sergei Morozov's avatar
Sergei Morozov committed
965 966 967
        self::assertSame('VARBINARY(255)', $this->platform->getBinaryTypeDeclarationSQL([]));
        self::assertSame('VARBINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 0]));
        self::assertSame('VARBINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 8000]));
968

Sergei Morozov's avatar
Sergei Morozov committed
969 970 971
        self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true]));
        self::assertSame('BINARY(255)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 0]));
        self::assertSame('BINARY(8000)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 8000]));
972 973
    }

974
    public function testReturnsBinaryTypeLongerThanMaxDeclarationSQL(): void
975
    {
Sergei Morozov's avatar
Sergei Morozov committed
976 977
        self::assertSame('VARBINARY(MAX)', $this->platform->getBinaryTypeDeclarationSQL(['length' => 8001]));
        self::assertSame('VARBINARY(MAX)', $this->platform->getBinaryTypeDeclarationSQL(['fixed' => true, 'length' => 8001]));
978 979 980
    }

    /**
981
     * {@inheritDoc}
982
     */
983
    protected function getAlterTableRenameIndexSQL(): array
984
    {
Sergei Morozov's avatar
Sergei Morozov committed
985
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_bar', N'INDEX'"];
986 987 988
    }

    /**
989
     * {@inheritDoc}
990
     */
991
    protected function getQuotedAlterTableRenameIndexSQL(): array
992
    {
Sergei Morozov's avatar
Sergei Morozov committed
993
        return [
994 995
            "EXEC sp_RENAME N'[table].[create]', N'[select]', N'INDEX'",
            "EXEC sp_RENAME N'[table].[foo]', N'[bar]', N'INDEX'",
Sergei Morozov's avatar
Sergei Morozov committed
996
        ];
997
    }
998

999
    public function testChangeColumnsTypeWithDefaultValue(): void
1000 1001 1002 1003
    {
        $tableName = 'column_def_change_type';
        $table     = new Table($tableName);

Sergei Morozov's avatar
Sergei Morozov committed
1004 1005
        $table->addColumn('col_int', 'smallint', ['default' => 666]);
        $table->addColumn('col_string', 'string', ['default' => 'foo']);
1006

Sergei Morozov's avatar
Sergei Morozov committed
1007 1008
        $tableDiff                            = new TableDiff($tableName);
        $tableDiff->fromTable                 = $table;
1009 1010
        $tableDiff->changedColumns['col_int'] = new ColumnDiff(
            'col_int',
Sergei Morozov's avatar
Sergei Morozov committed
1011 1012 1013
            new Column('col_int', Type::getType('integer'), ['default' => 666]),
            ['type'],
            new Column('col_int', Type::getType('smallint'), ['default' => 666])
1014 1015 1016 1017
        );

        $tableDiff->changedColumns['col_string'] = new ColumnDiff(
            'col_string',
Sergei Morozov's avatar
Sergei Morozov committed
1018 1019 1020
            new Column('col_string', Type::getType('string'), ['default' => 666, 'fixed' => true]),
            ['fixed'],
            new Column('col_string', Type::getType('string'), ['default' => 666])
1021 1022
        );

Sergei Morozov's avatar
Sergei Morozov committed
1023
        $expected = $this->platform->getAlterTableSQL($tableDiff);
1024

1025
        self::assertSame(
1026
            $expected,
Sergei Morozov's avatar
Sergei Morozov committed
1027
            [
1028 1029 1030 1031 1032 1033
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_FA2CB292',
                'ALTER TABLE column_def_change_type ALTER COLUMN col_int INT NOT NULL',
                'ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_FA2CB292 DEFAULT 666 FOR col_int',
                'ALTER TABLE column_def_change_type DROP CONSTRAINT DF_829302E0_2725A6D0',
                'ALTER TABLE column_def_change_type ALTER COLUMN col_string NCHAR(255) NOT NULL',
                "ALTER TABLE column_def_change_type ADD CONSTRAINT DF_829302E0_2725A6D0 DEFAULT '666' FOR col_string",
Sergei Morozov's avatar
Sergei Morozov committed
1034
            ]
1035 1036
        );
    }
1037 1038 1039 1040

    /**
     * {@inheritdoc}
     */
1041
    protected function getQuotedAlterTableRenameColumnSQL(): array
1042
    {
Sergei Morozov's avatar
Sergei Morozov committed
1043
        return [
1044 1045 1046 1047 1048 1049 1050 1051 1052
            "sp_RENAME 'mytable.unquoted1', 'unquoted', 'COLUMN'",
            "sp_RENAME 'mytable.unquoted2', '[where]', 'COLUMN'",
            "sp_RENAME 'mytable.unquoted3', '[foo]', 'COLUMN'",
            "sp_RENAME 'mytable.[create]', 'reserved_keyword', 'COLUMN'",
            "sp_RENAME 'mytable.[table]', '[from]', 'COLUMN'",
            "sp_RENAME 'mytable.[select]', '[bar]', 'COLUMN'",
            "sp_RENAME 'mytable.quoted1', 'quoted', 'COLUMN'",
            "sp_RENAME 'mytable.quoted2', '[and]', 'COLUMN'",
            "sp_RENAME 'mytable.quoted3', '[baz]', 'COLUMN'",
Sergei Morozov's avatar
Sergei Morozov committed
1053
        ];
1054
    }
1055

1056 1057 1058
    /**
     * {@inheritdoc}
     */
1059
    protected function getQuotedAlterTableChangeColumnLengthSQL(): array
1060 1061 1062 1063
    {
        $this->markTestIncomplete('Not implemented yet');
    }

1064
    /**
1065
     * {@inheritDoc}
1066
     */
1067
    protected function getAlterTableRenameIndexInSchemaSQL(): array
1068
    {
Sergei Morozov's avatar
Sergei Morozov committed
1069
        return ["EXEC sp_RENAME N'myschema.mytable.idx_foo', N'idx_bar', N'INDEX'"];
1070 1071 1072
    }

    /**
1073
     * {@inheritDoc}
1074
     */
1075
    protected function getQuotedAlterTableRenameIndexInSchemaSQL(): array
1076
    {
Sergei Morozov's avatar
Sergei Morozov committed
1077
        return [
1078 1079
            "EXEC sp_RENAME N'[schema].[table].[create]', N'[select]', N'INDEX'",
            "EXEC sp_RENAME N'[schema].[table].[foo]', N'[bar]', N'INDEX'",
Sergei Morozov's avatar
Sergei Morozov committed
1080
        ];
1081
    }
1082

1083
    protected function getQuotesDropForeignKeySQL(): string
1084 1085 1086 1087
    {
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
    }

1088
    protected function getQuotesDropConstraintSQL(): string
1089 1090 1091 1092
    {
        return 'ALTER TABLE [table] DROP CONSTRAINT [select]';
    }

1093
    /**
1094 1095
     * @param mixed[] $column
     *
1096 1097
     * @dataProvider getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL
     */
1098
    public function testGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL(string $table, array $column, string $expectedSql): void
1099
    {
Sergei Morozov's avatar
Sergei Morozov committed
1100
        self::assertSame($expectedSql, $this->platform->getDefaultConstraintDeclarationSQL($table, $column));
1101 1102
    }

1103 1104 1105
    /**
     * @return mixed[][]
     */
1106
    public static function getGeneratesIdentifierNamesInDefaultConstraintDeclarationSQL(): iterable
1107
    {
Sergei Morozov's avatar
Sergei Morozov committed
1108
        return [
1109
            // Unquoted identifiers non-reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1110
            ['mytable', ['name' => 'mycolumn', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn"],
1111
            // Quoted identifiers non-reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1112
            ['`mytable`', ['name' => '`mycolumn`', 'default' => 'foo'], " CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]"],
1113
            // Unquoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1114
            ['table', ['name' => 'select', 'default' => 'foo'], " CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]"],
1115
            // Quoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1116 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
    public function testGeneratesIdentifierNamesInCreateTableSQL(Table $table, array $expectedSql): void
1126
    {
Sergei Morozov's avatar
Sergei Morozov committed
1127
        self::assertSame($expectedSql, $this->platform->getCreateTableSQL($table));
1128 1129
    }

1130 1131 1132
    /**
     * @return mixed[][]
     */
1133
    public static function getGeneratesIdentifierNamesInCreateTableSQL(): iterable
1134
    {
Sergei Morozov's avatar
Sergei Morozov committed
1135
        return [
1136
            // Unquoted identifiers non-reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1137 1138 1139
            [
                new Table('mytable', [new Column('mycolumn', Type::getType('string'), ['default' => 'foo'])]),
                [
1140
                    'CREATE TABLE mytable (mycolumn NVARCHAR(255) NOT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
1141 1142 1143
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR mycolumn",
                ],
            ],
1144
            // Quoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1145 1146 1147
            [
                new Table('`mytable`', [new Column('`mycolumn`', Type::getType('string'), ['default' => 'foo'])]),
                [
1148
                    'CREATE TABLE [mytable] ([mycolumn] NVARCHAR(255) NOT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
1149 1150 1151
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'foo' FOR [mycolumn]",
                ],
            ],
1152
            // Unquoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1153 1154 1155
            [
                new Table('table', [new Column('select', Type::getType('string'), ['default' => 'foo'])]),
                [
1156
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
1157 1158 1159
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
                ],
            ],
1160
            // Quoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1161 1162 1163
            [
                new Table('`table`', [new Column('`select`', Type::getType('string'), ['default' => 'foo'])]),
                [
1164
                    'CREATE TABLE [table] ([select] NVARCHAR(255) NOT NULL)',
Sergei Morozov's avatar
Sergei Morozov committed
1165 1166 1167 1168
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'foo' FOR [select]",
                ],
            ],
        ];
1169 1170 1171
    }

    /**
1172 1173
     * @param string[] $expectedSql
     *
1174 1175
     * @dataProvider getGeneratesIdentifierNamesInAlterTableSQL
     */
1176
    public function testGeneratesIdentifierNamesInAlterTableSQL(TableDiff $tableDiff, array $expectedSql): void
1177
    {
Sergei Morozov's avatar
Sergei Morozov committed
1178
        self::assertSame($expectedSql, $this->platform->getAlterTableSQL($tableDiff));
1179 1180
    }

1181 1182 1183
    /**
     * @return mixed[][]
     */
1184
    public static function getGeneratesIdentifierNamesInAlterTableSQL(): iterable
1185
    {
Sergei Morozov's avatar
Sergei Morozov committed
1186
        return [
1187
            // Unquoted identifiers non-reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1188
            [
1189 1190
                new TableDiff(
                    'mytable',
Sergei Morozov's avatar
Sergei Morozov committed
1191 1192
                    [new Column('addcolumn', Type::getType('string'), ['default' => 'foo'])],
                    [
1193 1194
                        'mycolumn' => new ColumnDiff(
                            'mycolumn',
Sergei Morozov's avatar
Sergei Morozov committed
1195 1196 1197 1198 1199 1200
                            new Column('mycolumn', Type::getType('string'), ['default' => 'bar']),
                            ['default'],
                            new Column('mycolumn', Type::getType('string'), ['default' => 'foo'])
                        ),
                    ],
                    [new Column('removecolumn', Type::getType('string'), ['default' => 'foo'])]
1201
                ),
Sergei Morozov's avatar
Sergei Morozov committed
1202
                [
1203 1204 1205 1206 1207
                    'ALTER TABLE mytable ADD addcolumn NVARCHAR(255) NOT NULL',
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR addcolumn",
                    'ALTER TABLE mytable DROP COLUMN removecolumn',
                    'ALTER TABLE mytable DROP CONSTRAINT DF_6B2BD609_9BADD926',
                    'ALTER TABLE mytable ALTER COLUMN mycolumn NVARCHAR(255) NOT NULL',
Sergei Morozov's avatar
Sergei Morozov committed
1208 1209 1210
                    "ALTER TABLE mytable ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR mycolumn",
                ],
            ],
1211
            // Quoted identifiers non-reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1212
            [
1213 1214
                new TableDiff(
                    '`mytable`',
Sergei Morozov's avatar
Sergei Morozov committed
1215 1216
                    [new Column('`addcolumn`', Type::getType('string'), ['default' => 'foo'])],
                    [
1217 1218
                        'mycolumn' => new ColumnDiff(
                            '`mycolumn`',
Sergei Morozov's avatar
Sergei Morozov committed
1219 1220 1221 1222 1223 1224
                            new Column('`mycolumn`', Type::getType('string'), ['default' => 'bar']),
                            ['default'],
                            new Column('`mycolumn`', Type::getType('string'), ['default' => 'foo'])
                        ),
                    ],
                    [new Column('`removecolumn`', Type::getType('string'), ['default' => 'foo'])]
1225
                ),
Sergei Morozov's avatar
Sergei Morozov committed
1226
                [
1227 1228 1229 1230 1231
                    'ALTER TABLE [mytable] ADD [addcolumn] NVARCHAR(255) NOT NULL',
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_4AD86123 DEFAULT 'foo' FOR [addcolumn]",
                    'ALTER TABLE [mytable] DROP COLUMN [removecolumn]',
                    'ALTER TABLE [mytable] DROP CONSTRAINT DF_6B2BD609_9BADD926',
                    'ALTER TABLE [mytable] ALTER COLUMN [mycolumn] NVARCHAR(255) NOT NULL',
Sergei Morozov's avatar
Sergei Morozov committed
1232 1233 1234
                    "ALTER TABLE [mytable] ADD CONSTRAINT DF_6B2BD609_9BADD926 DEFAULT 'bar' FOR [mycolumn]",
                ],
            ],
1235
            // Unquoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1236
            [
1237 1238
                new TableDiff(
                    'table',
Sergei Morozov's avatar
Sergei Morozov committed
1239 1240
                    [new Column('add', Type::getType('string'), ['default' => 'foo'])],
                    [
1241 1242
                        'select' => new ColumnDiff(
                            'select',
Sergei Morozov's avatar
Sergei Morozov committed
1243 1244 1245 1246 1247 1248
                            new Column('select', Type::getType('string'), ['default' => 'bar']),
                            ['default'],
                            new Column('select', Type::getType('string'), ['default' => 'foo'])
                        ),
                    ],
                    [new Column('drop', Type::getType('string'), ['default' => 'foo'])]
1249
                ),
Sergei Morozov's avatar
Sergei Morozov committed
1250
                [
1251 1252 1253 1254 1255
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
                    'ALTER TABLE [table] DROP COLUMN [drop]',
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
Sergei Morozov's avatar
Sergei Morozov committed
1256 1257 1258
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
                ],
            ],
1259
            // Quoted identifiers reserved keywords.
Sergei Morozov's avatar
Sergei Morozov committed
1260
            [
1261 1262
                new TableDiff(
                    '`table`',
Sergei Morozov's avatar
Sergei Morozov committed
1263 1264
                    [new Column('`add`', Type::getType('string'), ['default' => 'foo'])],
                    [
1265 1266
                        'select' => new ColumnDiff(
                            '`select`',
Sergei Morozov's avatar
Sergei Morozov committed
1267 1268 1269 1270 1271 1272
                            new Column('`select`', Type::getType('string'), ['default' => 'bar']),
                            ['default'],
                            new Column('`select`', Type::getType('string'), ['default' => 'foo'])
                        ),
                    ],
                    [new Column('`drop`', Type::getType('string'), ['default' => 'foo'])]
1273
                ),
Sergei Morozov's avatar
Sergei Morozov committed
1274
                [
1275 1276 1277 1278 1279
                    'ALTER TABLE [table] ADD [add] NVARCHAR(255) NOT NULL',
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_FD1A73E7 DEFAULT 'foo' FOR [add]",
                    'ALTER TABLE [table] DROP COLUMN [drop]',
                    'ALTER TABLE [table] DROP CONSTRAINT DF_F6298F46_4BF2EAC0',
                    'ALTER TABLE [table] ALTER COLUMN [select] NVARCHAR(255) NOT NULL',
Sergei Morozov's avatar
Sergei Morozov committed
1280 1281 1282 1283
                    "ALTER TABLE [table] ADD CONSTRAINT DF_F6298F46_4BF2EAC0 DEFAULT 'bar' FOR [select]",
                ],
            ],
        ];
1284
    }
1285

1286
    public function testReturnsGuidTypeDeclarationSQL(): void
1287
    {
Sergei Morozov's avatar
Sergei Morozov committed
1288
        self::assertSame('UNIQUEIDENTIFIER', $this->platform->getGuidTypeDeclarationSQL([]));
1289
    }
1290 1291 1292 1293

    /**
     * {@inheritdoc}
     */
1294
    public function getAlterTableRenameColumnSQL(): array
1295
    {
Sergei Morozov's avatar
Sergei Morozov committed
1296
        return [
1297 1298 1299
            "sp_RENAME 'foo.bar', 'baz', 'COLUMN'",
            'ALTER TABLE foo DROP CONSTRAINT DF_8C736521_76FF8CAA',
            'ALTER TABLE foo ADD CONSTRAINT DF_8C736521_78240498 DEFAULT 666 FOR baz',
Sergei Morozov's avatar
Sergei Morozov committed
1300
        ];
1301
    }
1302 1303 1304 1305

    /**
     * {@inheritdoc}
     */
1306
    protected function getQuotesTableIdentifiersInAlterTableSQL(): array
1307
    {
Sergei Morozov's avatar
Sergei Morozov committed
1308
        return [
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
            'ALTER TABLE [foo] DROP CONSTRAINT fk1',
            'ALTER TABLE [foo] DROP CONSTRAINT fk2',
            "sp_RENAME '[foo].id', 'war', 'COLUMN'",
            'ALTER TABLE [foo] ADD bloo INT NOT NULL',
            'ALTER TABLE [foo] DROP COLUMN baz',
            'ALTER TABLE [foo] ALTER COLUMN bar INT',
            "sp_RENAME '[foo]', 'table'",
            "DECLARE @sql NVARCHAR(MAX) = N''; " .
            "SELECT @sql += N'EXEC sp_rename N''' + dc.name + ''', N''' + REPLACE(dc.name, '8C736521', 'F6298F46') + ''', " .
            "''OBJECT'';' FROM sys.default_constraints dc JOIN sys.tables tbl ON dc.parent_object_id = tbl.object_id " .
            "WHERE tbl.name = 'table';EXEC sp_executesql @sql",
            'ALTER TABLE [table] ADD CONSTRAINT fk_add FOREIGN KEY (fk3) REFERENCES fk_table (id)',
            'ALTER TABLE [table] ADD CONSTRAINT fk2 FOREIGN KEY (fk2) REFERENCES fk_table2 (id)',
Sergei Morozov's avatar
Sergei Morozov committed
1322
        ];
1323
    }
1324 1325 1326 1327

    /**
     * {@inheritdoc}
     */
1328
    protected function getCommentOnColumnSQL(): array
1329
    {
Sergei Morozov's avatar
Sergei Morozov committed
1330
        return [
1331 1332 1333
            "COMMENT ON COLUMN foo.bar IS 'comment'",
            "COMMENT ON COLUMN [Foo].[BAR] IS 'comment'",
            "COMMENT ON COLUMN [select].[from] IS 'comment'",
Sergei Morozov's avatar
Sergei Morozov committed
1334
        ];
1335
    }
1336 1337 1338 1339

    /**
     * {@inheritdoc}
     */
1340
    public static function getReturnsForeignKeyReferentialActionSQL(): iterable
1341
    {
Sergei Morozov's avatar
Sergei Morozov committed
1342 1343 1344 1345 1346 1347 1348 1349
        return [
            ['CASCADE', 'CASCADE'],
            ['SET NULL', 'SET NULL'],
            ['NO ACTION', 'NO ACTION'],
            ['RESTRICT', 'NO ACTION'],
            ['SET DEFAULT', 'SET DEFAULT'],
            ['CaScAdE', 'CASCADE'],
        ];
1350
    }
1351

1352
    protected function getQuotesReservedKeywordInUniqueConstraintDeclarationSQL(): string
1353 1354 1355 1356
    {
        return 'CONSTRAINT [select] UNIQUE (foo) WHERE foo IS NOT NULL';
    }

1357
    protected function getQuotesReservedKeywordInIndexDeclarationSQL(): string
1358 1359 1360
    {
        return 'INDEX [select] (foo)';
    }
1361

1362
    protected function getQuotesReservedKeywordInTruncateTableSQL(): string
1363 1364 1365 1366
    {
        return 'TRUNCATE TABLE [select]';
    }

1367 1368 1369
    /**
     * {@inheritdoc}
     */
1370
    protected function getAlterStringToFixedStringSQL(): array
1371
    {
Sergei Morozov's avatar
Sergei Morozov committed
1372
        return ['ALTER TABLE mytable ALTER COLUMN name NCHAR(2) NOT NULL'];
1373
    }
1374 1375 1376 1377

    /**
     * {@inheritdoc}
     */
1378
    protected function getGeneratesAlterTableRenameIndexUsedByForeignKeySQL(): array
1379
    {
Sergei Morozov's avatar
Sergei Morozov committed
1380
        return ["EXEC sp_RENAME N'mytable.idx_foo', N'idx_foo_renamed', N'INDEX'"];
1381
    }
1382

1383
    public function testModifyLimitQueryWithTopNSubQueryWithOrderBy(): void
1384
    {
Sergei Morozov's avatar
Sergei Morozov committed
1385
        $querySql   = 'SELECT * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC)';
1386
        $alteredSql = 'SELECT TOP 10 * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC)';
Sergei Morozov's avatar
Sergei Morozov committed
1387
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
1388
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
1389

Sergei Morozov's avatar
Sergei Morozov committed
1390
        $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';
1391
        $alteredSql = 'SELECT TOP 10 * FROM test t WHERE t.id = (SELECT TOP 1 t2.id FROM test t2 ORDER BY t2.data DESC) ORDER BY t.data2 DESC';
Sergei Morozov's avatar
Sergei Morozov committed
1392
        $sql        = $this->platform->modifyLimitQuery($querySql, 10);
1393
        $this->expectCteWithMaxRowNum($alteredSql, 10, $sql);
1394
    }
1395

1396
    public function testQuotesTableNameInListTableColumnsSQL(): void
1397
    {
1398 1399 1400 1401
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableColumnsSQL("Foo'Bar\\")
        );
1402 1403
    }

1404
    public function testQuotesSchemaNameInListTableColumnsSQL(): void
1405
    {
1406
        self::assertStringContainsStringIgnoringCase(
1407
            "'Foo''Bar\\'",
1408
            $this->platform->getListTableColumnsSQL("Foo'Bar\\.baz_table")
1409 1410 1411
        );
    }

1412
    public function testQuotesTableNameInListTableForeignKeysSQL(): void
1413
    {
1414 1415 1416 1417
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\")
        );
1418 1419
    }

1420
    public function testQuotesSchemaNameInListTableForeignKeysSQL(): void
1421
    {
1422
        self::assertStringContainsStringIgnoringCase(
1423
            "'Foo''Bar\\'",
1424
            $this->platform->getListTableForeignKeysSQL("Foo'Bar\\.baz_table")
1425 1426 1427
        );
    }

1428
    public function testQuotesTableNameInListTableIndexesSQL(): void
1429
    {
1430 1431 1432 1433
        self::assertStringContainsStringIgnoringCase(
            "'Foo''Bar\\'",
            $this->platform->getListTableIndexesSQL("Foo'Bar\\")
        );
1434 1435
    }

1436
    public function testQuotesSchemaNameInListTableIndexesSQL(): void
1437
    {
1438
        self::assertStringContainsStringIgnoringCase(
1439
            "'Foo''Bar\\'",
1440
            $this->platform->getListTableIndexesSQL("Foo'Bar\\.baz_table")
1441 1442
        );
    }
1443

1444
    public function testGetDefaultValueDeclarationSQLForDateType(): void
1445
    {
Sergei Morozov's avatar
Sergei Morozov committed
1446
        $currentDateSql = $this->platform->getCurrentDateSQL();
1447 1448
        foreach (['date', 'date_immutable'] as $type) {
            self::assertSame(
1449
                ' DEFAULT CONVERT(date, GETDATE())',
1450 1451 1452 1453
                $this->platform->getDefaultValueDeclarationSQL([
                    'type' => Type::getType($type),
                    'default' => $currentDateSql,
                ])
1454 1455 1456
            );
        }
    }
1457

1458
    public function testSupportsColumnCollation(): void
1459
    {
Sergei Morozov's avatar
Sergei Morozov committed
1460
        self::assertTrue($this->platform->supportsColumnCollation());
1461 1462
    }

1463
    public function testColumnCollationDeclarationSQL(): void
1464 1465 1466
    {
        self::assertSame(
            'COLLATE Latin1_General_CS_AS_KS_WS',
Sergei Morozov's avatar
Sergei Morozov committed
1467
            $this->platform->getColumnCollationDeclarationSQL('Latin1_General_CS_AS_KS_WS')
1468 1469 1470
        );
    }

1471
    public function testGetCreateTableSQLWithColumnCollation(): void
1472 1473 1474 1475 1476 1477 1478
    {
        $table = new Table('foo');
        $table->addColumn('no_collation', 'string');
        $table->addColumn('column_collation', 'string')->setPlatformOption('collation', 'Latin1_General_CS_AS_KS_WS');

        self::assertSame(
            ['CREATE TABLE foo (no_collation NVARCHAR(255) NOT NULL, column_collation NVARCHAR(255) COLLATE Latin1_General_CS_AS_KS_WS NOT NULL)'],
Sergei Morozov's avatar
Sergei Morozov committed
1479
            $this->platform->getCreateTableSQL($table),
1480 1481 1482 1483
            'Column "no_collation" will use the default collation from the table/database and "column_collation" overwrites the collation on this column'
        );
    }

1484
    private function expectCteWithMaxRowNum(string $expectedSql, int $expectedMax, string $sql): void
1485 1486 1487 1488 1489
    {
        $pattern = 'WITH dctrn_cte AS (%s) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum <= %d ORDER BY doctrine_rownum ASC';
        self::assertEquals(sprintf($pattern, $expectedSql, $expectedMax), $sql);
    }

1490
    private function expectCteWithMinAndMaxRowNums(string $expectedSql, int $expectedMin, int $expectedMax, string $sql): void
1491 1492 1493 1494
    {
        $pattern = 'WITH dctrn_cte AS (%s) SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS doctrine_rownum FROM dctrn_cte) AS doctrine_tbl WHERE doctrine_rownum >= %d AND doctrine_rownum <= %d ORDER BY doctrine_rownum ASC';
        self::assertEquals(sprintf($pattern, $expectedSql, $expectedMin, $expectedMax), $sql);
    }
1495
}