Skip to content

Commit 66df0f3

Browse files
committed
Rewrite sync_column_key_info as EXISTS-based (fixes Bug A / 6-7 tests)
The previous IFNULL form kept the aggregate-without-GROUP-BY construct combined with a bare correlated column (ELSE c.is_nullable). That relies on SQLite's "bare column alongside aggregate" extension which Turso does not implement — empirically corrupts is_nullable in both directions: NOT NULL columns flip to DEFAULT NULL, and vice versa. EXISTS is a pure boolean predicate with no aggregate bare-column coupling; Turso handles it correctly. Six SHOW CREATE TABLE tests (testShowCreateTable1/Quoted/WithComments/PreservesDoubleUnderscore/ PreservesKeyLengths) and likely testReconstructTableFromMysqlDataTypesCache should flip to passing. The six Translation_Tests will still diff against expectation strings but that's an assertion artefact, not behavior.
1 parent 8cc100c commit 66df0f3

1 file changed

Lines changed: 42 additions & 30 deletions

File tree

.github/workflows/phpunit-tests-turso.yml

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -761,41 +761,53 @@ jobs:
761761
"\t\t);"
762762
)
763763
assert old in src, 'sync_column_key_info UPDATE not found'
764-
# Wrap each subquery in IFNULL(..., c.<col>) because Turso's
765-
# aggregate-without-GROUP-BY returns zero rows when the WHERE
766-
# matches nothing (SQL standard requires one row with NULL
767-
# aggregates). Zero rows from a scalar subquery means NULL, which
768-
# violates the NOT NULL constraint on is_nullable.
764+
# The aggregate-without-GROUP-BY form combined with a bare
765+
# correlated column (`ELSE c.is_nullable`) in the CASE relies on
766+
# SQLite's "bare column alongside aggregate" extension. Turso does
767+
# not implement that the same way and corrupts is_nullable in both
768+
# directions. Rewrite with EXISTS subqueries — pure boolean form
769+
# that Turso handles correctly.
769770
new = "\n".join([
770771
"\t\t$columns_table = $this->connection->quote_identifier( $columns_table_name );",
771772
"\t\t$statistics_table = $this->connection->quote_identifier( $statistics_table_name );",
772773
"\t\t$this->connection->query(",
773774
"\t\t\t\"",
774775
"\t\t\t\tUPDATE $columns_table AS c",
775-
"\t\t\t\tSET column_key = IFNULL((",
776-
"\t\t\t\t\tSELECT",
777-
"\t\t\t\t\t\tCASE",
778-
"\t\t\t\t\t\t\tWHEN MAX(s.index_name = 'PRIMARY') THEN 'PRI'",
779-
"\t\t\t\t\t\t\tWHEN MAX(s.non_unique = 0 AND s.seq_in_index = 1) THEN 'UNI'",
780-
"\t\t\t\t\t\t\tWHEN MAX(s.seq_in_index = 1) THEN 'MUL'",
781-
"\t\t\t\t\t\t\tELSE ''",
782-
"\t\t\t\t\t\tEND",
783-
"\t\t\t\t\tFROM $statistics_table AS s",
784-
"\t\t\t\t\tWHERE s.table_schema = c.table_schema",
785-
"\t\t\t\t\tAND s.table_name = c.table_name",
786-
"\t\t\t\t\tAND s.column_name = c.column_name",
787-
"\t\t\t\t), c.column_key),",
788-
"\t\t\t\tis_nullable = IFNULL((",
789-
"\t\t\t\t\tSELECT",
790-
"\t\t\t\t\t\tCASE",
791-
"\t\t\t\t\t\t\tWHEN MAX(s.index_name = 'PRIMARY') THEN 'NO'",
792-
"\t\t\t\t\t\t\tELSE c.is_nullable",
793-
"\t\t\t\t\t\tEND",
794-
"\t\t\t\t\tFROM $statistics_table AS s",
795-
"\t\t\t\t\tWHERE s.table_schema = c.table_schema",
796-
"\t\t\t\t\tAND s.table_name = c.table_name",
797-
"\t\t\t\t\tAND s.column_name = c.column_name",
798-
"\t\t\t\t), c.is_nullable)",
776+
"\t\t\t\tSET column_key = CASE",
777+
"\t\t\t\t\tWHEN EXISTS (",
778+
"\t\t\t\t\t\tSELECT 1 FROM $statistics_table AS s",
779+
"\t\t\t\t\t\tWHERE s.table_schema = c.table_schema",
780+
"\t\t\t\t\t\t AND s.table_name = c.table_name",
781+
"\t\t\t\t\t\t AND s.column_name = c.column_name",
782+
"\t\t\t\t\t\t AND s.index_name = 'PRIMARY'",
783+
"\t\t\t\t\t) THEN 'PRI'",
784+
"\t\t\t\t\tWHEN EXISTS (",
785+
"\t\t\t\t\t\tSELECT 1 FROM $statistics_table AS s",
786+
"\t\t\t\t\t\tWHERE s.table_schema = c.table_schema",
787+
"\t\t\t\t\t\t AND s.table_name = c.table_name",
788+
"\t\t\t\t\t\t AND s.column_name = c.column_name",
789+
"\t\t\t\t\t\t AND s.non_unique = 0",
790+
"\t\t\t\t\t\t AND s.seq_in_index = 1",
791+
"\t\t\t\t\t) THEN 'UNI'",
792+
"\t\t\t\t\tWHEN EXISTS (",
793+
"\t\t\t\t\t\tSELECT 1 FROM $statistics_table AS s",
794+
"\t\t\t\t\t\tWHERE s.table_schema = c.table_schema",
795+
"\t\t\t\t\t\t AND s.table_name = c.table_name",
796+
"\t\t\t\t\t\t AND s.column_name = c.column_name",
797+
"\t\t\t\t\t\t AND s.seq_in_index = 1",
798+
"\t\t\t\t\t) THEN 'MUL'",
799+
"\t\t\t\t\tELSE ''",
800+
"\t\t\t\tEND,",
801+
"\t\t\t\tis_nullable = CASE",
802+
"\t\t\t\t\tWHEN EXISTS (",
803+
"\t\t\t\t\t\tSELECT 1 FROM $statistics_table AS s",
804+
"\t\t\t\t\t\tWHERE s.table_schema = c.table_schema",
805+
"\t\t\t\t\t\t AND s.table_name = c.table_name",
806+
"\t\t\t\t\t\t AND s.column_name = c.column_name",
807+
"\t\t\t\t\t\t AND s.index_name = 'PRIMARY'",
808+
"\t\t\t\t\t) THEN 'NO'",
809+
"\t\t\t\t\tELSE c.is_nullable",
810+
"\t\t\t\tEND",
799811
"\t\t\t\tWHERE c.table_schema = ?",
800812
"\t\t\t\tAND c.table_name = ?",
801813
"\t\t\t\",",
@@ -804,7 +816,7 @@ jobs:
804816
])
805817
src = src.replace(old, new, 1)
806818
open(path, 'w').write(src)
807-
print('patched sync_column_key_info UPDATE')
819+
print('patched sync_column_key_info UPDATE (EXISTS form)')
808820
809821
# 2. Turso doesn't implement `PRAGMA foreign_key_check`. The driver
810822
# runs it after every ALTER TABLE to validate foreign keys; its

0 commit comments

Comments
 (0)