Skip to content

Commit 6ae46dc

Browse files
committed
test: add MSSQL-specific DDL builder tests
- Add tests for buildDropColumnSql with default constraint removal (cursor-based) - Add tests for sp_rename usage in rename operations (column, index, foreign key, table) - Add tests for buildCreateTableSql with ON clause (filegroup) - Add tests for formatColumnDefinition conversions (TEXT->NVARCHAR(MAX), BOOLEAN->BIT) - Add tests for NVARCHAR/VARCHAR without length (defaults to MAX) - Add tests for buildCreateIndexSql with INCLUDE columns - Add tests for buildCreateIndexSql with WHERE clause (filtered indexes) - Add tests for buildTableExistsSql with and without schema - Add tests for buildAlterColumnSql using ALTER COLUMN syntax All 17 MSSQL-specific tests pass successfully.
1 parent ff02a78 commit 6ae46dc

1 file changed

Lines changed: 359 additions & 0 deletions

File tree

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace tommyknocker\pdodb\tests\mssql;
6+
7+
use tommyknocker\pdodb\query\schema\ColumnSchema;
8+
9+
/**
10+
* MSSQL-specific DDL Builder tests.
11+
*
12+
* These tests verify MSSQL-specific DDL builder methods and SQL generation.
13+
*/
14+
final class MSSQLDdlBuilderTests extends BaseMSSQLTestCase
15+
{
16+
/**
17+
* Get MSSQL DDL builder instance.
18+
*
19+
* @return \tommyknocker\pdodb\dialects\mssql\MSSQLDdlBuilder
20+
*/
21+
protected function getDdlBuilder(): \tommyknocker\pdodb\dialects\mssql\MSSQLDdlBuilder
22+
{
23+
$db = self::$db;
24+
$dialect = $db->schema()->getDialect();
25+
26+
// Get ddlBuilder from dialect via reflection
27+
$reflection = new \ReflectionClass($dialect);
28+
$builderProperty = $reflection->getProperty('ddlBuilder');
29+
$builderProperty->setAccessible(true);
30+
return $builderProperty->getValue($dialect);
31+
}
32+
33+
/**
34+
* Test buildDropColumnSql generates SQL with default constraint removal.
35+
*/
36+
public function testBuildDropColumnSqlWithDefaultConstraints(): void
37+
{
38+
$builder = $this->getDdlBuilder();
39+
40+
$sql = $builder->buildDropColumnSql('[dbo].[test_table]', 'test_column');
41+
42+
$this->assertIsString($sql);
43+
$this->assertStringContainsString('DECLARE @constraintName', $sql);
44+
$this->assertStringContainsString('sys.default_constraints', $sql);
45+
$this->assertStringContainsString('OBJECT_ID', $sql);
46+
$this->assertStringContainsString('COLUMNPROPERTY', $sql);
47+
$this->assertStringContainsString('DROP CONSTRAINT', $sql);
48+
$this->assertStringContainsString('DROP COLUMN', $sql);
49+
$this->assertStringContainsString('[test_table]', $sql);
50+
$this->assertStringContainsString('[test_column]', $sql);
51+
}
52+
53+
/**
54+
* Test buildRenameColumnSql uses sp_rename.
55+
*/
56+
public function testBuildRenameColumnSqlUsesSpRename(): void
57+
{
58+
$builder = $this->getDdlBuilder();
59+
60+
$sql = $builder->buildRenameColumnSql('[dbo].[test_table]', 'old_column', 'new_column');
61+
62+
$this->assertIsString($sql);
63+
$this->assertStringContainsString('EXEC sp_rename', $sql);
64+
$this->assertStringContainsString("'COLUMN'", $sql);
65+
$this->assertStringContainsString('old_column', $sql);
66+
$this->assertStringContainsString('new_column', $sql);
67+
// sp_rename requires unquoted names
68+
$this->assertStringNotContainsString('[old_column]', $sql);
69+
$this->assertStringNotContainsString('[new_column]', $sql);
70+
}
71+
72+
/**
73+
* Test buildRenameIndexSql uses sp_rename.
74+
*/
75+
public function testBuildRenameIndexSqlUsesSpRename(): void
76+
{
77+
$builder = $this->getDdlBuilder();
78+
79+
$sql = $builder->buildRenameIndexSql('old_idx', '[dbo].[test_table]', 'new_idx');
80+
81+
$this->assertIsString($sql);
82+
$this->assertStringContainsString('EXEC sp_rename', $sql);
83+
$this->assertStringContainsString("'INDEX'", $sql);
84+
$this->assertStringContainsString('old_idx', $sql);
85+
$this->assertStringContainsString('new_idx', $sql);
86+
// sp_rename requires unquoted names
87+
$this->assertStringNotContainsString('[old_idx]', $sql);
88+
$this->assertStringNotContainsString('[new_idx]', $sql);
89+
}
90+
91+
/**
92+
* Test buildRenameForeignKeySql uses sp_rename.
93+
*/
94+
public function testBuildRenameForeignKeySqlUsesSpRename(): void
95+
{
96+
$builder = $this->getDdlBuilder();
97+
98+
$sql = $builder->buildRenameForeignKeySql('old_fk', '[dbo].[test_table]', 'new_fk');
99+
100+
$this->assertIsString($sql);
101+
$this->assertStringContainsString('EXEC sp_rename', $sql);
102+
$this->assertStringContainsString("'OBJECT'", $sql);
103+
$this->assertStringContainsString('old_fk', $sql);
104+
$this->assertStringContainsString('new_fk', $sql);
105+
// sp_rename requires unquoted names
106+
$this->assertStringNotContainsString('[old_fk]', $sql);
107+
$this->assertStringNotContainsString('[new_fk]', $sql);
108+
}
109+
110+
/**
111+
* Test buildRenameTableSql uses sp_rename.
112+
*/
113+
public function testBuildRenameTableSqlUsesSpRename(): void
114+
{
115+
$builder = $this->getDdlBuilder();
116+
117+
$sql = $builder->buildRenameTableSql('[dbo].[old_table]', 'new_table');
118+
119+
$this->assertIsString($sql);
120+
$this->assertStringContainsString('EXEC sp_rename', $sql);
121+
$this->assertStringContainsString('old_table', $sql);
122+
$this->assertStringContainsString('new_table', $sql);
123+
// sp_rename doesn't need brackets in new name
124+
$this->assertStringNotContainsString('[new_table]', $sql);
125+
}
126+
127+
/**
128+
* Test buildCreateTableSql with ON clause (filegroup).
129+
*/
130+
public function testBuildCreateTableSqlWithOnClause(): void
131+
{
132+
$builder = $this->getDdlBuilder();
133+
$schema = self::$db->schema();
134+
135+
$columns = [
136+
'id' => $schema->primaryKey(),
137+
'name' => $schema->string(100)->notNull(),
138+
];
139+
140+
$options = ['on' => '[PRIMARY]'];
141+
$sql = $builder->buildCreateTableSql('[dbo].[test_table]', $columns, $options);
142+
143+
$this->assertIsString($sql);
144+
$this->assertStringContainsString('CREATE TABLE', $sql);
145+
$this->assertStringContainsString('[test_table]', $sql);
146+
$this->assertStringContainsString('ON [PRIMARY]', $sql);
147+
}
148+
149+
/**
150+
* Test formatColumnDefinition converts TEXT to NVARCHAR(MAX).
151+
*/
152+
public function testFormatColumnDefinitionTextToNvarcharMax(): void
153+
{
154+
$builder = $this->getDdlBuilder();
155+
$schema = self::$db->schema();
156+
157+
$columnSchema = $schema->text();
158+
$sql = $builder->formatColumnDefinition('test_column', $columnSchema);
159+
160+
$this->assertIsString($sql);
161+
$this->assertStringContainsString('test_column', $sql);
162+
$this->assertStringContainsString('NVARCHAR(MAX)', $sql);
163+
$this->assertStringNotContainsString('TEXT', $sql);
164+
}
165+
166+
/**
167+
* Test formatColumnDefinition converts BOOLEAN to BIT.
168+
*/
169+
public function testFormatColumnDefinitionBooleanToBit(): void
170+
{
171+
$builder = $this->getDdlBuilder();
172+
173+
// parseColumnDefinition converts BOOLEAN to BIT
174+
$reflection = new \ReflectionClass($builder);
175+
$parseMethod = $reflection->getMethod('parseColumnDefinition');
176+
$parseMethod->setAccessible(true);
177+
178+
$columnSchema = $parseMethod->invoke($builder, ['type' => 'BOOLEAN']);
179+
$sql = $builder->formatColumnDefinition('test_column', $columnSchema);
180+
181+
$this->assertIsString($sql);
182+
$this->assertStringContainsString('test_column', $sql);
183+
$this->assertStringContainsString('BIT', $sql);
184+
$this->assertStringNotContainsString('BOOLEAN', $sql);
185+
}
186+
187+
/**
188+
* Test formatColumnDefinition uses NVARCHAR for string types.
189+
*/
190+
public function testFormatColumnDefinitionUsesNvarchar(): void
191+
{
192+
$builder = $this->getDdlBuilder();
193+
$schema = self::$db->schema();
194+
195+
$columnSchema = $schema->string(100);
196+
$sql = $builder->formatColumnDefinition('test_column', $columnSchema);
197+
198+
$this->assertIsString($sql);
199+
$this->assertStringContainsString('test_column', $sql);
200+
$this->assertStringContainsString('NVARCHAR', $sql);
201+
$this->assertStringContainsString('100', $sql);
202+
}
203+
204+
/**
205+
* Test formatColumnDefinition handles NVARCHAR without length as NVARCHAR(MAX).
206+
*/
207+
public function testFormatColumnDefinitionNvarcharWithoutLength(): void
208+
{
209+
$builder = $this->getDdlBuilder();
210+
211+
// Create column schema with NVARCHAR without length
212+
$columnSchema = new ColumnSchema('NVARCHAR');
213+
$sql = $builder->formatColumnDefinition('test_column', $columnSchema);
214+
215+
$this->assertIsString($sql);
216+
$this->assertStringContainsString('test_column', $sql);
217+
$this->assertStringContainsString('NVARCHAR(MAX)', $sql);
218+
}
219+
220+
/**
221+
* Test formatColumnDefinition handles VARCHAR without length as VARCHAR(MAX).
222+
*/
223+
public function testFormatColumnDefinitionVarcharWithoutLength(): void
224+
{
225+
$builder = $this->getDdlBuilder();
226+
227+
// Create column schema with VARCHAR without length
228+
$columnSchema = new ColumnSchema('VARCHAR');
229+
$sql = $builder->formatColumnDefinition('test_column', $columnSchema);
230+
231+
$this->assertIsString($sql);
232+
$this->assertStringContainsString('test_column', $sql);
233+
$this->assertStringContainsString('VARCHAR(MAX)', $sql);
234+
}
235+
236+
/**
237+
* Test buildCreateIndexSql with INCLUDE columns.
238+
*/
239+
public function testBuildCreateIndexSqlWithInclude(): void
240+
{
241+
$builder = $this->getDdlBuilder();
242+
243+
$sql = $builder->buildCreateIndexSql(
244+
'idx_test',
245+
'[dbo].[test_table]',
246+
['name'],
247+
false,
248+
null,
249+
['price', 'stock']
250+
);
251+
252+
$this->assertIsString($sql);
253+
$this->assertStringContainsString('CREATE INDEX', $sql);
254+
$this->assertStringContainsString('[idx_test]', $sql);
255+
$this->assertStringContainsString('[test_table]', $sql);
256+
$this->assertStringContainsString('INCLUDE', $sql);
257+
$this->assertStringContainsString('[price]', $sql);
258+
$this->assertStringContainsString('[stock]', $sql);
259+
}
260+
261+
/**
262+
* Test buildCreateIndexSql with WHERE clause (filtered index).
263+
*/
264+
public function testBuildCreateIndexSqlWithWhereClause(): void
265+
{
266+
$builder = $this->getDdlBuilder();
267+
268+
$sql = $builder->buildCreateIndexSql(
269+
'idx_test',
270+
'[dbo].[test_table]',
271+
['status'],
272+
false,
273+
'status = 1'
274+
);
275+
276+
$this->assertIsString($sql);
277+
$this->assertStringContainsString('CREATE INDEX', $sql);
278+
$this->assertStringContainsString('[idx_test]', $sql);
279+
$this->assertStringContainsString('WHERE', $sql);
280+
$this->assertStringContainsString('status = 1', $sql);
281+
}
282+
283+
/**
284+
* Test buildCreateIndexSql with both WHERE and INCLUDE.
285+
*/
286+
public function testBuildCreateIndexSqlWithWhereAndInclude(): void
287+
{
288+
$builder = $this->getDdlBuilder();
289+
290+
$sql = $builder->buildCreateIndexSql(
291+
'idx_test',
292+
'[dbo].[test_table]',
293+
['status'],
294+
false,
295+
'status = 1',
296+
['name', 'value']
297+
);
298+
299+
$this->assertIsString($sql);
300+
$this->assertStringContainsString('CREATE INDEX', $sql);
301+
$this->assertStringContainsString('WHERE', $sql);
302+
$this->assertStringContainsString('status = 1', $sql);
303+
$this->assertStringContainsString('INCLUDE', $sql);
304+
$this->assertStringContainsString('[name]', $sql);
305+
$this->assertStringContainsString('[value]', $sql);
306+
}
307+
308+
/**
309+
* Test buildTableExistsSql with schema-qualified table name.
310+
*/
311+
public function testBuildTableExistsSqlWithSchema(): void
312+
{
313+
$builder = $this->getDdlBuilder();
314+
315+
$sql = $builder->buildTableExistsSql('dbo.test_table');
316+
317+
$this->assertIsString($sql);
318+
$this->assertStringContainsString('INFORMATION_SCHEMA.TABLES', $sql);
319+
$this->assertStringContainsString('TABLE_SCHEMA', $sql);
320+
$this->assertStringContainsString('TABLE_NAME', $sql);
321+
$this->assertStringContainsString('dbo', $sql);
322+
$this->assertStringContainsString('test_table', $sql);
323+
}
324+
325+
/**
326+
* Test buildTableExistsSql without schema.
327+
*/
328+
public function testBuildTableExistsSqlWithoutSchema(): void
329+
{
330+
$builder = $this->getDdlBuilder();
331+
332+
$sql = $builder->buildTableExistsSql('test_table');
333+
334+
$this->assertIsString($sql);
335+
$this->assertStringContainsString('INFORMATION_SCHEMA.TABLES', $sql);
336+
$this->assertStringContainsString('TABLE_NAME', $sql);
337+
$this->assertStringContainsString('test_table', $sql);
338+
// Should not check TABLE_SCHEMA when schema is not provided
339+
$this->assertStringNotContainsString('TABLE_SCHEMA', $sql);
340+
}
341+
342+
/**
343+
* Test buildAlterColumnSql uses ALTER COLUMN syntax.
344+
*/
345+
public function testBuildAlterColumnSqlUsesAlterColumn(): void
346+
{
347+
$builder = $this->getDdlBuilder();
348+
$schema = self::$db->schema();
349+
350+
$columnSchema = $schema->string(200)->notNull();
351+
$sql = $builder->buildAlterColumnSql('[dbo].[test_table]', 'test_column', $columnSchema);
352+
353+
$this->assertIsString($sql);
354+
$this->assertStringContainsString('ALTER TABLE', $sql);
355+
$this->assertStringContainsString('[test_table]', $sql);
356+
$this->assertStringContainsString('ALTER COLUMN', $sql);
357+
$this->assertStringContainsString('[test_column]', $sql);
358+
}
359+
}

0 commit comments

Comments
 (0)