Skip to content

Commit b40e849

Browse files
authored
Merge pull request #138 from simon-mundy/ddl-improvements
This small enhancement allows:- - Better handling of string literals for default values in DDL - Adds Double, Json and SmallInteger column types - Adds modifiers to CREATE/DROP tables with IF/IF NOT EXISTS
2 parents c461b1b + e520970 commit b40e849

File tree

16 files changed

+464
-9
lines changed

16 files changed

+464
-9
lines changed

src/Sql/Ddl/Column/Column.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
use PhpDb\Sql\Argument\Identifier;
99
use PhpDb\Sql\Argument\Literal;
1010
use PhpDb\Sql\Argument\Value;
11+
use PhpDb\Sql\ArgumentInterface;
1112
use PhpDb\Sql\Ddl\Constraint\ConstraintInterface;
1213

1314
use function implode;
1415

1516
class Column implements ColumnInterface
1617
{
17-
protected string|int|null $default;
18+
protected string|int|float|bool|Literal|Value|null $default;
1819

1920
protected bool $isNullable = false;
2021

@@ -65,14 +66,14 @@ public function isNullable(): bool
6566
return $this->isNullable;
6667
}
6768

68-
public function setDefault(string|int|null $default): static
69+
public function setDefault(string|int|float|bool|Literal|Value|null $default): static
6970
{
7071
$this->default = $default;
7172
return $this;
7273
}
7374

7475
#[Override]
75-
public function getDefault(): string|int|null
76+
public function getDefault(): string|int|float|bool|Literal|Value|null
7677
{
7778
return $this->default;
7879
}
@@ -118,7 +119,9 @@ public function getExpressionData(): array
118119

119120
if ($this->default !== null) {
120121
$specParts[] = 'DEFAULT %s';
121-
$values[] = new Value($this->default);
122+
$values[] = $this->default instanceof ArgumentInterface
123+
? $this->default
124+
: new Value($this->default);
122125
}
123126

124127
foreach ($this->constraints as $constraint) {

src/Sql/Ddl/Column/ColumnInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace PhpDb\Sql\Ddl\Column;
66

7+
use PhpDb\Sql\Argument\Literal;
8+
use PhpDb\Sql\Argument\Value;
79
use PhpDb\Sql\ExpressionInterface;
810

911
/**
@@ -15,7 +17,7 @@ public function getName(): string;
1517

1618
public function isNullable(): bool;
1719

18-
public function getDefault(): string|int|null;
20+
public function getDefault(): string|int|float|bool|Literal|Value|null;
1921

2022
public function getOptions(): array;
2123
}

src/Sql/Ddl/Column/Double.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDb\Sql\Ddl\Column;
6+
7+
class Double extends AbstractPrecisionColumn
8+
{
9+
protected string $type = 'DOUBLE';
10+
}

src/Sql/Ddl/Column/Json.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDb\Sql\Ddl\Column;
6+
7+
class Json extends Column
8+
{
9+
protected string $type = 'JSON';
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDb\Sql\Ddl\Column;
6+
7+
class SmallInteger extends Integer
8+
{
9+
protected string $type = 'SMALLINT';
10+
}

src/Sql/Ddl/CreateTable.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ class CreateTable extends AbstractSql
2222

2323
protected array $constraints = [];
2424

25+
protected bool $ifNotExists = false;
26+
2527
protected bool $isTemporary = false;
2628

2729
/**
2830
* {@inheritDoc}
2931
*/
3032
protected array $specifications = [
31-
self::TABLE => 'CREATE %1$sTABLE %2$s (',
33+
self::TABLE => 'CREATE %1$sTABLE %2$s%3$s (',
3234
self::COLUMNS => [
3335
"\n %1\$s" => [
3436
[1 => '%1$s', 'combinedby' => ",\n "],
@@ -51,6 +53,17 @@ public function __construct(string|TableIdentifier $table = '', bool $isTemporar
5153
$this->setTemporary($isTemporary);
5254
}
5355

56+
public function ifNotExists(bool $ifNotExists = true): static
57+
{
58+
$this->ifNotExists = $ifNotExists;
59+
return $this;
60+
}
61+
62+
public function getIfNotExists(): bool
63+
{
64+
return $this->ifNotExists;
65+
}
66+
5467
public function setTemporary(string|int|bool $temporary): static
5568
{
5669
$this->isTemporary = (bool) $temporary;
@@ -102,6 +115,7 @@ protected function processTable(?PlatformInterface $adapterPlatform = null): arr
102115
{
103116
return [
104117
$this->isTemporary ? 'TEMPORARY ' : '',
118+
$this->ifNotExists ? 'IF NOT EXISTS ' : '',
105119
$this->resolveTable($this->table, $adapterPlatform),
106120
];
107121
}

src/Sql/Ddl/DropTable.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ class DropTable extends AbstractSql
1212
{
1313
final public const TABLE = 'table';
1414

15+
protected bool $ifExists = false;
16+
1517
protected array $specifications = [
16-
self::TABLE => 'DROP TABLE %1$s',
18+
self::TABLE => 'DROP TABLE %1$s%2$s',
1719
];
1820

1921
protected string|TableIdentifier $table = '';
@@ -23,9 +25,23 @@ public function __construct(string|TableIdentifier $table = '')
2325
$this->table = $table;
2426
}
2527

28+
public function ifExists(bool $ifExists = true): static
29+
{
30+
$this->ifExists = $ifExists;
31+
return $this;
32+
}
33+
34+
public function getIfExists(): bool
35+
{
36+
return $this->ifExists;
37+
}
38+
2639
/** @return string[] */
2740
protected function processTable(?PlatformInterface $adapterPlatform = null): array
2841
{
29-
return [$this->resolveTable($this->table, $adapterPlatform)];
42+
return [
43+
$this->ifExists ? 'IF EXISTS ' : '',
44+
$this->resolveTable($this->table, $adapterPlatform),
45+
];
3046
}
3147
}

src/Sql/Ddl/Index/Index.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Override;
88
use PhpDb\Sql\Argument\Identifier;
9+
use PhpDb\Sql\Argument\Literal;
910

1011
use function count;
1112
use function implode;
@@ -17,13 +18,26 @@ class Index extends AbstractIndex
1718

1819
protected array $lengths;
1920

21+
protected ?string $type = null;
22+
2023
public function __construct(null|array|string $columns, ?string $name = null, array $lengths = [])
2124
{
2225
parent::__construct($columns, $name);
2326

2427
$this->lengths = $lengths;
2528
}
2629

30+
public function setType(string $type): static
31+
{
32+
$this->type = $type;
33+
return $this;
34+
}
35+
36+
public function getType(): ?string
37+
{
38+
return $this->type;
39+
}
40+
2741
/** @inheritDoc */
2842
#[Override]
2943
public function getExpressionData(): array
@@ -43,8 +57,15 @@ public function getExpressionData(): array
4357
$specParts[] = $specPart;
4458
}
4559

60+
$spec = str_replace('...', implode(', ', $specParts), $this->specification);
61+
62+
if ($this->type !== null) {
63+
$spec .= ' USING %s';
64+
$values[] = new Literal($this->type);
65+
}
66+
4667
return [
47-
'spec' => str_replace('...', implode(', ', $specParts), $this->specification),
68+
'spec' => $spec,
4869
'values' => $values,
4970
];
5071
}

test/unit/Sql/Ddl/Column/ColumnTest.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace PhpDbTest\Sql\Ddl\Column;
66

77
use PhpDb\Sql\Argument;
8+
use PhpDb\Sql\Argument\Literal;
9+
use PhpDb\Sql\Argument\Value;
810
use PhpDb\Sql\Ddl\Column\Column;
911
use PHPUnit\Framework\Attributes\CoversMethod;
1012
use PHPUnit\Framework\TestCase;
@@ -166,4 +168,114 @@ public function testGetExpressionData(): void
166168
Argument::value('bar'),
167169
], $expressionData['values']);
168170
}
171+
172+
public function testSetDefaultWithLiteral(): void
173+
{
174+
$column = new Column();
175+
$column->setName('created_at');
176+
177+
$literal = new Literal('CURRENT_TIMESTAMP');
178+
$result = $column->setDefault($literal);
179+
180+
self::assertSame($column, $result);
181+
self::assertSame($literal, $column->getDefault());
182+
}
183+
184+
public function testGetExpressionDataWithLiteralDefault(): void
185+
{
186+
$column = new Column();
187+
$column->setName('created_at');
188+
$column->setDefault(new Literal('CURRENT_TIMESTAMP'));
189+
190+
$expressionData = $column->getExpressionData();
191+
192+
self::assertEquals('%s %s NOT NULL DEFAULT %s', $expressionData['spec']);
193+
self::assertEquals([
194+
Argument::identifier('created_at'),
195+
Argument::literal('INTEGER'),
196+
Argument::literal('CURRENT_TIMESTAMP'),
197+
], $expressionData['values']);
198+
}
199+
200+
public function testSetDefaultWithValue(): void
201+
{
202+
$column = new Column();
203+
$column->setName('score');
204+
205+
$value = new Value(99);
206+
$result = $column->setDefault($value);
207+
208+
self::assertSame($column, $result);
209+
self::assertSame($value, $column->getDefault());
210+
}
211+
212+
public function testGetExpressionDataWithValueDefault(): void
213+
{
214+
$column = new Column();
215+
$column->setName('score');
216+
$column->setDefault(new Value(42));
217+
218+
$expressionData = $column->getExpressionData();
219+
220+
self::assertEquals('%s %s NOT NULL DEFAULT %s', $expressionData['spec']);
221+
self::assertEquals([
222+
Argument::identifier('score'),
223+
Argument::literal('INTEGER'),
224+
Argument::value(42),
225+
], $expressionData['values']);
226+
}
227+
228+
public function testSetDefaultWithFloat(): void
229+
{
230+
$column = new Column();
231+
$column->setName('rate');
232+
233+
$result = $column->setDefault(3.14);
234+
235+
self::assertSame($column, $result);
236+
self::assertSame(3.14, $column->getDefault());
237+
}
238+
239+
public function testGetExpressionDataWithFloatDefault(): void
240+
{
241+
$column = new Column();
242+
$column->setName('rate');
243+
$column->setDefault(9.99);
244+
245+
$expressionData = $column->getExpressionData();
246+
247+
self::assertEquals('%s %s NOT NULL DEFAULT %s', $expressionData['spec']);
248+
self::assertEquals([
249+
Argument::identifier('rate'),
250+
Argument::literal('INTEGER'),
251+
Argument::value(9.99),
252+
], $expressionData['values']);
253+
}
254+
255+
public function testSetDefaultWithBool(): void
256+
{
257+
$column = new Column();
258+
$column->setName('is_active');
259+
260+
$result = $column->setDefault(true);
261+
262+
self::assertSame($column, $result);
263+
self::assertTrue($column->getDefault());
264+
}
265+
266+
public function testGetExpressionDataWithBoolDefault(): void
267+
{
268+
$column = new Column();
269+
$column->setName('is_active');
270+
$column->setDefault(false);
271+
272+
$expressionData = $column->getExpressionData();
273+
274+
self::assertEquals('%s %s NOT NULL DEFAULT %s', $expressionData['spec']);
275+
self::assertEquals([
276+
Argument::identifier('is_active'),
277+
Argument::literal('INTEGER'),
278+
Argument::value(false),
279+
], $expressionData['values']);
280+
}
169281
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpDbTest\Sql\Ddl\Column;
6+
7+
use PhpDb\Sql\Argument;
8+
use PhpDb\Sql\Ddl\Column\Double;
9+
use PHPUnit\Framework\Attributes\CoversMethod;
10+
use PHPUnit\Framework\TestCase;
11+
12+
#[CoversMethod(Double::class, 'getExpressionData')]
13+
final class DoubleTest extends TestCase
14+
{
15+
public function testGetExpressionData(): void
16+
{
17+
$column = new Double('foo', 10, 5);
18+
19+
$expressionData = $column->getExpressionData();
20+
21+
self::assertEquals('%s %s(%s) NOT NULL', $expressionData['spec']);
22+
self::assertEquals([
23+
Argument::identifier('foo'),
24+
Argument::literal('DOUBLE'),
25+
Argument::literal('10,5'),
26+
], $expressionData['values']);
27+
}
28+
}

0 commit comments

Comments
 (0)