Skip to content

Commit dd4baee

Browse files
Merge branch '2.x' into feature/upsert
2 parents 9a2285a + 876fbc2 commit dd4baee

File tree

11 files changed

+281
-8
lines changed

11 files changed

+281
-8
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "2.13.0"
2+
".": "2.14.0"
33
}

CHANGELOG.md

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

3+
## [2.14.0](https://github.com/cycle/database/compare/2.13.0...2.14.0) (2025-07-14)
4+
5+
6+
### Features
7+
8+
* add parameters to SubQuery ([74dbd7c](https://github.com/cycle/database/commit/74dbd7c11dc8812f54d4145c0fb3d6c07693d829))
9+
* add SubQuery injection ([8079759](https://github.com/cycle/database/commit/80797599e1d3aff28c76e69904748df0e7d6407b))
10+
* add ULID column type ([#233](https://github.com/cycle/database/issues/233)) ([f8b3a87](https://github.com/cycle/database/commit/f8b3a87222aa94a7ebd9e5776bc61897ad10237e))
11+
* **MySQL:** add `first` and `after` into column declaration ([#226](https://github.com/cycle/database/issues/226)) ([7ad2df1](https://github.com/cycle/database/commit/7ad2df1ccf4498ac1a5067a0abc8ded55c6d06cd))
12+
13+
14+
### Bug Fixes
15+
16+
* **MySQL:** unsigned boolean Columns comparison ([#234](https://github.com/cycle/database/issues/234)) ([82378ea](https://github.com/cycle/database/commit/82378ea94653eaf0b558149e86b7a6ebec94872e))
17+
18+
19+
### Documentation
20+
21+
* cover SubQuery with comments ([8579419](https://github.com/cycle/database/commit/85794199f02e4d2f60f8f66de21f18e7968aafc5))
22+
23+
24+
### Styles
25+
26+
* fix code style; rename `SubQueryInjection` to `SubQuery` ([b9cf08e](https://github.com/cycle/database/commit/b9cf08e1ca3911ee4cb3aab513f597988221b3eb))
27+
* improve code formatting and consistency in Compiler, Jsoner, and SubQuery ([02484a6](https://github.com/cycle/database/commit/02484a66a23bac5f2fe3711944890642f0a4a7a2))
28+
29+
30+
### Code Refactoring
31+
32+
* rename `selectSubQuery` to `subQuery` and update related query logic ([727ca0e](https://github.com/cycle/database/commit/727ca0edccd01cb0ba1133240712c65775cc6bd1))
33+
34+
35+
### Tests
36+
37+
* enhance subquery tests with additional conditions and parameters ([09013c6](https://github.com/cycle/database/commit/09013c670cadcd865529b7c39578db2413001631))
38+
* **MySQL:** Added a test case proving bug with boolean columns ([c2c05d1](https://github.com/cycle/database/commit/c2c05d191d5c4379fb9be58599012158ea3b9db6))
39+
* **MySQL:** enhance boolean column configuration tests with zerofill and nullable checks ([0129e7f](https://github.com/cycle/database/commit/0129e7f0c49e2cefb7d986b0ad0d311bdcdf7738))
40+
341
## [2.13.0](https://github.com/cycle/database/compare/2.12.0...2.13.0) (2025-03-27)
442

543

psalm-baseline.xml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@
296296
<code><![CDATA[$param]]></code>
297297
<code><![CDATA[$param]]></code>
298298
<code><![CDATA[$param]]></code>
299+
<code><![CDATA[$tokens['alias']]]></code>
299300
<code><![CDATA[$tokens['columns']]]></code>
300301
<code><![CDATA[$tokens['columns']]]></code>
301302
<code><![CDATA[$tokens['distinct']]]></code>
@@ -379,6 +380,7 @@
379380
<code><![CDATA[$identifier]]></code>
380381
</MoreSpecificImplementedParamType>
381382
<PossiblyUndefinedStringArrayOffset>
383+
<code><![CDATA[$tokens['alias']]]></code>
382384
<code><![CDATA[$tokens['columns']]]></code>
383385
<code><![CDATA[$tokens['columns']]]></code>
384386
<code><![CDATA[$tokens['distinct']]]></code>
@@ -901,19 +903,20 @@
901903
</MixedAssignment>
902904
<PossiblyUndefinedStringArrayOffset>
903905
<code><![CDATA[$matches['type']]]></code>
906+
<code><![CDATA[$schema['Comment']]]></code>
904907
<code><![CDATA[$schema['Default']]]></code>
905908
<code><![CDATA[$schema['Extra']]]></code>
906909
<code><![CDATA[$schema['Field']]]></code>
907910
<code><![CDATA[$schema['Null']]]></code>
908911
<code><![CDATA[$schema['Type']]]></code>
909-
<code><![CDATA[$schema['Comment']]]></code>
910912
</PossiblyUndefinedStringArrayOffset>
911913
<RiskyTruthyFalsyComparison>
912914
<code><![CDATA[empty($matches['attr'])]]></code>
913915
<code><![CDATA[empty($matches['options'])]]></code>
914916
</RiskyTruthyFalsyComparison>
915917
<UndefinedInterfaceMethod>
916918
<code><![CDATA[identifier]]></code>
919+
<code><![CDATA[identifier]]></code>
917920
</UndefinedInterfaceMethod>
918921
</file>
919922
<file src="src/Driver/MySQL/Schema/MySQLForeignKey.php">
@@ -1303,10 +1306,10 @@
13031306
<code><![CDATA[$identifier]]></code>
13041307
<code><![CDATA[$identifier]]></code>
13051308
<code><![CDATA[$range]]></code>
1309+
<code><![CDATA[$tableName]]></code>
13061310
<code><![CDATA[$value]]></code>
13071311
<code><![CDATA[$value]]></code>
13081312
<code><![CDATA[$value]]></code>
1309-
<code><![CDATA[$tableName]]></code>
13101313
</MixedAssignment>
13111314
<MixedOperand>
13121315
<code><![CDATA[$schema['dtd_identifier']]]></code>
@@ -1321,6 +1324,7 @@
13211324
<code><![CDATA[$schema['column_name']]]></code>
13221325
<code><![CDATA[$schema['data_type']]]></code>
13231326
<code><![CDATA[$schema['datetime_precision']]]></code>
1327+
<code><![CDATA[$schema['description']]]></code>
13241328
<code><![CDATA[$schema['dtd_identifier']]]></code>
13251329
<code><![CDATA[$schema['interval_type']]]></code>
13261330
<code><![CDATA[$schema['is_nullable']]]></code>
@@ -1330,7 +1334,6 @@
13301334
<code><![CDATA[$schema['tableOID']]]></code>
13311335
<code><![CDATA[$schema['typname']]]></code>
13321336
<code><![CDATA[$schema['typtype']]]></code>
1333-
<code><![CDATA[$schema['description']]]></code>
13341337
</PossiblyUndefinedStringArrayOffset>
13351338
<RedundantCast>
13361339
<code><![CDATA[(bool) (
@@ -2424,6 +2427,14 @@
24242427
<code><![CDATA[$value]]></code>
24252428
</PropertyNotSetInConstructor>
24262429
</file>
2430+
<file src="src/Injection/SubQuery.php">
2431+
<ClassMustBeFinal>
2432+
<code><![CDATA[SubQuery]]></code>
2433+
</ClassMustBeFinal>
2434+
<MixedPropertyTypeCoercion>
2435+
<code><![CDATA[$parameters->getParameters()]]></code>
2436+
</MixedPropertyTypeCoercion>
2437+
</file>
24272438
<file src="src/Query/ActiveQuery.php">
24282439
<PossiblyNullArgument>
24292440
<code><![CDATA[$this->prefix]]></code>

src/Driver/MySQL/Schema/MySQLColumn.php

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class MySQLColumn extends AbstractColumn
4242
*/
4343
public const DATETIME_NOW = 'CURRENT_TIMESTAMP';
4444

45-
public const EXCLUDE_FROM_COMPARE = ['size', 'timezone', 'userType', 'attributes', 'first', 'after'];
45+
public const EXCLUDE_FROM_COMPARE = ['size', 'timezone', 'userType', 'attributes', 'first', 'after', 'unknownSize'];
4646
protected const INTEGER_TYPES = ['tinyint', 'smallint', 'mediumint', 'int', 'bigint'];
4747

4848
protected array $mapping = [
@@ -112,6 +112,7 @@ class MySQLColumn extends AbstractColumn
112112

113113
//Additional types
114114
'json' => 'json',
115+
'ulid' => ['type' => 'varchar', 'size' => 26],
115116
'uuid' => ['type' => 'varchar', 'size' => 36],
116117
];
117118
protected array $reverseMapping = [
@@ -161,6 +162,11 @@ class MySQLColumn extends AbstractColumn
161162
)]
162163
protected int $size = 0;
163164

165+
/**
166+
* True if size is not defined in DB schema.
167+
*/
168+
protected bool $unknownSize = false;
169+
164170
/**
165171
* Column is auto incremental.
166172
*/
@@ -250,6 +256,7 @@ public static function createInstance(string $table, array $schema, ?\DateTimeZo
250256

251257
// since 8.0 database does not provide size for some columns
252258
if ($column->size === 0) {
259+
$column->unknownSize = true;
253260
switch ($column->type) {
254261
case 'int':
255262
$column->size = 11;
@@ -290,6 +297,12 @@ public static function createInstance(string $table, array $schema, ?\DateTimeZo
290297
return $column;
291298
}
292299

300+
public function size(int $value): self
301+
{
302+
$this->unknownSize = false;
303+
return parent::__call('size', [$value]);
304+
}
305+
293306
/**
294307
* @psalm-return non-empty-string
295308
*/
@@ -326,10 +339,27 @@ public function sqlStatement(DriverInterface $driver): string
326339

327340
public function compare(AbstractColumn $initial): bool
328341
{
329-
$result = parent::compare($initial);
342+
\assert($initial instanceof self);
343+
$self = $this;
344+
345+
// MySQL 8.0 does not provide size for unsigned integers without zerofill
346+
// so we can get wrong results in comparison of boolean columns
347+
if ($self->unknownSize || $initial->unknownSize) {
348+
// if one of the columns is boolean, we can safely assume that size is 1
349+
if (\in_array($self->userType, ['bool', 'boolean'], true)) {
350+
$initial = clone $initial;
351+
$initial->size = 1;
352+
} elseif (\in_array($initial->userType, ['bool', 'boolean'], true)) {
353+
$self = clone $self;
354+
$self->size = 1;
355+
}
356+
}
357+
358+
$result = \Closure::fromCallable([parent::class, 'compare'])->bindTo($self)($initial);
359+
330360

331-
if ($this->type === 'varchar' || $this->type === 'varbinary') {
332-
return $result && $this->size === $initial->size;
361+
if ($self->type === 'varchar' || $self->type === 'varbinary') {
362+
return $result && $self->size === $initial->size;
333363
}
334364

335365
return $result;

src/Driver/Postgres/Schema/PostgresColumn.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ class PostgresColumn extends AbstractColumn
181181
//Additional types
182182
'json' => 'json',
183183
'jsonb' => 'jsonb',
184+
'ulid' => ['type' => 'character varying', 'size' => 26],
184185
'uuid' => 'uuid',
185186
'point' => 'point',
186187
'line' => 'line',

src/Driver/SQLServer/Schema/SQLServerColumn.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class SQLServerColumn extends AbstractColumn
9898

9999
//Additional types
100100
'json' => ['type' => 'varchar', 'size' => 0],
101+
'ulid' => ['type' => 'varchar', 'size' => 26],
101102
'uuid' => ['type' => 'varchar', 'size' => 36],
102103
];
103104
protected array $reverseMapping = [

src/Driver/SQLite/Schema/SQLiteColumn.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class SQLiteColumn extends AbstractColumn
8787

8888
//Additional types
8989
'json' => 'text',
90+
'ulid' => ['type' => 'varchar', 'size' => 26],
9091
'uuid' => ['type' => 'varchar', 'size' => 36],
9192
];
9293
protected array $reverseMapping = [

src/Schema/AbstractColumn.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
* @method $this|AbstractColumn tinyBinary()
5151
* @method $this|AbstractColumn longBinary()
5252
* @method $this|AbstractColumn json()
53+
* @method $this|AbstractColumn ulid()
5354
* @method $this|AbstractColumn uuid()
5455
*/
5556
abstract class AbstractColumn implements ColumnInterface, ElementInterface

src/Schema/AbstractTable.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* @method AbstractColumn binary($column)
5252
* @method AbstractColumn tinyBinary($column)
5353
* @method AbstractColumn longBinary($column)
54+
* @method AbstractColumn ulid($column)
5455
* @method AbstractColumn uuid($column)
5556
*/
5657
abstract class AbstractTable implements TableInterface, ElementInterface

tests/Database/Functional/Driver/Common/Schema/ConsistencyTest.php

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,92 @@ public function testTime(): void
360360
$this->assertTrue($schema->column('target')->compare($column));
361361
}
362362

363+
public function testUlid(): void
364+
{
365+
$schema = $this->schema('table');
366+
$this->assertFalse($schema->exists());
367+
368+
$column = $schema->ulid('target');
369+
370+
$schema->save();
371+
372+
$schema = $this->schema('table');
373+
$this->assertTrue($schema->exists());
374+
$this->assertTrue($schema->column('target')->compare($column));
375+
$this->assertSame('string', $schema->column('target')->getType());
376+
377+
$this->database->table('table')->insertOne(
378+
[
379+
'target' => '0GWWXY2G84DFMRVWQNJ1SRYCMC',
380+
],
381+
);
382+
383+
$this->assertEquals(
384+
[
385+
'target' => '0GWWXY2G84DFMRVWQNJ1SRYCMC',
386+
],
387+
$this->database->table('table')->select()->fetchAll()[0],
388+
);
389+
}
390+
391+
public function testUlidCallingColumnMethod(): void
392+
{
393+
$schema = $this->schema('table');
394+
$this->assertFalse($schema->exists());
395+
396+
$column = $schema->column('target')->ulid();
397+
398+
$schema->save();
399+
400+
$schema = $this->schema('table');
401+
$this->assertTrue($schema->exists());
402+
$this->assertTrue($schema->column('target')->compare($column));
403+
$this->assertSame('string', $schema->column('target')->getType());
404+
405+
$this->database->table('table')->insertOne(
406+
[
407+
'target' => '0GWWXY2G84DFMRVWQNJ1SRYCMC',
408+
],
409+
);
410+
411+
$this->assertEquals(
412+
[
413+
'target' => '0GWWXY2G84DFMRVWQNJ1SRYCMC',
414+
],
415+
$this->database->table('table')->select()->fetchAll()[0],
416+
);
417+
}
418+
419+
public function testUlidPrimary(): void
420+
{
421+
$schema = $this->schema('table');
422+
$this->assertFalse($schema->exists());
423+
424+
$column = $schema->ulid('target')->nullable(false);
425+
$schema->setPrimaryKeys(['target']);
426+
$schema->save();
427+
428+
$schema = $this->schema('table');
429+
$this->assertTrue($schema->exists());
430+
431+
$this->assertTrue($schema->column('target')->compare($column));
432+
$this->assertSame('string', $schema->column('target')->getType());
433+
$this->assertSame(['target'], $schema->getPrimaryKeys());
434+
435+
$this->database->table('table')->insertOne(
436+
[
437+
'target' => '0GWWXY2G84DFMRVWQNJ1SRYCMC',
438+
],
439+
);
440+
441+
$this->assertEquals(
442+
[
443+
'target' => '0GWWXY2G84DFMRVWQNJ1SRYCMC',
444+
],
445+
$this->database->table('table')->select()->fetchAll()[0],
446+
);
447+
}
448+
363449
public function testUuid(): void
364450
{
365451
$schema = $this->schema('table');
@@ -388,6 +474,34 @@ public function testUuid(): void
388474
);
389475
}
390476

477+
public function testUuidCallingColumnMethod(): void
478+
{
479+
$schema = $this->schema('table');
480+
$this->assertFalse($schema->exists());
481+
482+
$column = $schema->column('target')->uuid();
483+
484+
$schema->save();
485+
486+
$schema = $this->schema('table');
487+
$this->assertTrue($schema->exists());
488+
$this->assertTrue($schema->column('target')->compare($column));
489+
$this->assertSame('string', $schema->column('target')->getType());
490+
491+
$this->database->table('table')->insertOne(
492+
[
493+
'target' => 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
494+
],
495+
);
496+
497+
$this->assertEquals(
498+
[
499+
'target' => 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
500+
],
501+
$this->database->table('table')->select()->fetchAll()[0],
502+
);
503+
}
504+
391505
public function testUuidPrimary(): void
392506
{
393507
$schema = $this->schema('table');

0 commit comments

Comments
 (0)