Skip to content

Commit 068dc8e

Browse files
committed
Merge branch 'develop'
* develop: specify next release add unique constraint fix values collision
2 parents 8ee1464 + ac9a77b commit 068dc8e

5 files changed

Lines changed: 163 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 2.15.0 - 2024-02-10
4+
5+
### Added
6+
7+
- `Formal\AccessLayer\Query\CreateTable::unique()`
8+
- `Formal\AccessLayer\Query\Constraint\Unique`
9+
310
## 2.14.0 - 2024-02-10
411

512
### Added

proofs/connection/pdo.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,50 @@ static function($assert) use ($connection) {
536536
},
537537
);
538538

539+
yield proof(
540+
'Unique constraint',
541+
given(Set\Integers::between(0, 1_000_000)),
542+
static function($assert, $int) use ($connection) {
543+
$table = Table\Name::of('test_unique');
544+
$connection(CreateTable::ifNotExists(
545+
$table,
546+
Column::of(
547+
Column\Name::of('id'),
548+
Column\Type::int(),
549+
),
550+
Column::of(
551+
Column\Name::of('other'),
552+
Column\Type::varchar(3),
553+
),
554+
)->unique(Column\Name::of('id'), Column\Name::of('other')));
555+
556+
$connection(Insert::into(
557+
$table,
558+
Row::of([
559+
'id' => $int,
560+
'other' => 'foo',
561+
]),
562+
));
563+
$connection(Insert::into(
564+
$table,
565+
Row::of([
566+
'id' => $int,
567+
'other' => 'bar',
568+
]),
569+
));
570+
571+
$assert->throws(fn() => $connection(Insert::into(
572+
$table,
573+
Row::of([
574+
'id' => $int,
575+
'other' => 'foo',
576+
]),
577+
)));
578+
579+
$connection(DropTable::named($table));
580+
},
581+
);
582+
539583
yield properties(
540584
'PDO properties',
541585
Properties::any(),

properties/Connection/SelectWhereInQuery.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,13 @@ public function __construct(
3939
string $uuid2,
4040
string $username,
4141
int $number,
42-
string $value1,
43-
string $value2,
42+
array $values,
4443
) {
4544
$this->uuid1 = $uuid1;
4645
$this->uuid2 = $uuid2;
4746
$this->username = $username;
4847
$this->number = $number;
49-
$this->value1 = $value1;
50-
$this->value2 = $value2;
48+
[$this->value1, $this->value2] = $values;
5149
}
5250

5351
public static function any(): Set
@@ -58,8 +56,10 @@ public static function any(): Set
5856
Set\Uuid::any(),
5957
Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255),
6058
Set\Integers::any(),
61-
Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255),
62-
Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255),
59+
Set\MutuallyExclusive::of(
60+
Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255),
61+
Set\Strings::madeOf(Set\Chars::ascii())->between(0, 255),
62+
),
6363
);
6464
}
6565

src/Query/Constraint/Unique.php

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace Formal\AccessLayer\Query\Constraint;
5+
6+
use Formal\AccessLayer\Table\{
7+
Name,
8+
Column,
9+
};
10+
use Innmind\Immutable\{
11+
Maybe,
12+
Sequence,
13+
Str,
14+
Monoid\Concat,
15+
};
16+
17+
/**
18+
* @psalm-immutable
19+
*/
20+
final class Unique
21+
{
22+
private Column\Name $column;
23+
/** @var Sequence<Column\Name> */
24+
private Sequence $columns;
25+
/** @var Maybe<non-empty-string> */
26+
private Maybe $name;
27+
28+
/**
29+
* @param Sequence<Column\Name> $columns
30+
* @param Maybe<non-empty-string> $name
31+
*/
32+
private function __construct(
33+
Column\Name $column,
34+
Sequence $columns,
35+
Maybe $name,
36+
) {
37+
$this->column = $column;
38+
$this->columns = $columns;
39+
$this->name = $name;
40+
}
41+
42+
/**
43+
* @psalm-pure
44+
* @no-named-arguments
45+
*/
46+
public static function of(
47+
Column\Name $column,
48+
Column\Name ...$columns,
49+
): self {
50+
/** @var Maybe<non-empty-string> */
51+
$name = Maybe::nothing();
52+
53+
return new self($column, Sequence::of(...$columns), $name);
54+
}
55+
56+
/**
57+
* @param non-empty-string $name
58+
*/
59+
public function named(string $name): self
60+
{
61+
return new self(
62+
$this->column,
63+
$this->columns,
64+
Maybe::just($name),
65+
);
66+
}
67+
68+
/**
69+
* @return non-empty-string
70+
*/
71+
public function sql(): string
72+
{
73+
$columns = $this
74+
->columns
75+
->map(static fn($column) => ', '.$column->sql())
76+
->map(Str::of(...))
77+
->fold(new Concat)
78+
->toString();
79+
80+
return $this->name->match(
81+
fn($name) => \sprintf(
82+
'CONSTRAINT UC_%s UNIQUE (%s%s)',
83+
$name,
84+
$this->column->sql(),
85+
$columns,
86+
),
87+
fn() => \sprintf(
88+
'UNIQUE (%s%s)',
89+
$this->column->sql(),
90+
$columns,
91+
),
92+
);
93+
}
94+
}

src/Query/CreateTable.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Query,
88
Query\Constraint\PrimaryKey,
99
Query\Constraint\ForeignKey,
10+
Query\Constraint\Unique,
1011
Table\Name,
1112
Table\Column,
1213
};
@@ -23,13 +24,13 @@ final class CreateTable implements Query
2324
private Name $name;
2425
/** @var Sequence<Column> */
2526
private Sequence $columns;
26-
/** @var Sequence<PrimaryKey|ForeignKey> */
27+
/** @var Sequence<PrimaryKey|ForeignKey|Unique> */
2728
private Sequence $constraints;
2829
private bool $ifNotExists;
2930

3031
/**
3132
* @param Sequence<Column> $columns
32-
* @param Sequence<PrimaryKey|ForeignKey> $constraints
33+
* @param Sequence<PrimaryKey|ForeignKey|Unique> $constraints
3334
*/
3435
private function __construct(
3536
bool $ifNotExists,
@@ -81,7 +82,15 @@ public function foreignKey(Column\Name $column, Name $target, Column\Name $refer
8182
return $this->constraint(ForeignKey::of($column, $target, $reference));
8283
}
8384

84-
public function constraint(PrimaryKey|ForeignKey $constraint): self
85+
/**
86+
* @no-named-arguments
87+
*/
88+
public function unique(Column\Name $column, Column\Name ...$columns): self
89+
{
90+
return $this->constraint(Unique::of($column, ...$columns));
91+
}
92+
93+
public function constraint(PrimaryKey|ForeignKey|Unique $constraint): self
8594
{
8695
return new self(
8796
$this->ifNotExists,

0 commit comments

Comments
 (0)