Skip to content

Commit ba6b01b

Browse files
committed
Flatten YEAR cast nested subquery for Turso
The YEAR cast in cast_value_for_saving wraps the integer-cast value in a derived table to alias it as `value` so the CASE branches can reference it once: (SELECT CASE WHEN value IS NULL THEN NULL ... END FROM (SELECT CAST(<column-ref> AS INTEGER) AS value)) In an INSERT-SELECT-FROM-VALUES context the <column-ref> resolves two levels up — past the inner SELECT and past the outer per-column projection — and Turso fails the binding with "Parse error: no such column: columnN", breaking six tests that exercise YEAR via INSERT...VALUES: testNonStrictModeTypeCasting (column17) testColumnInfoForDateAndTimeDataTypes (column5) testCastValuesOnInsert (column1) testCastValuesOnInsertInNonStrictMode (column1) testCastValuesOnUpdate (column1) testCastValuesOnUpdateInNonStrictMode (column1) A direct CI probe of one-level FROM-VALUES references (with and without CAST/backticks/INSERT context) showed every shape passes on Turso. The failure is specific to the YEAR cast's *nested* derived FROM. Other date/time types use a flat CASE and work. Flatten the YEAR cast to inline `CAST(... AS INTEGER)` at every WHEN — CAST is idempotent so the rewrite is semantically identical. All 667 mysql-on-sqlite tests pass with the patch on real SQLite.
1 parent ce22919 commit ba6b01b

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

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

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,6 +1662,77 @@ jobs:
16621662
# pager fragility is fixed upstream.
16631663
open(path, 'w').write(src)
16641664
print('patched AUTO_INCREMENT correlated-subquery -> JOIN form (SHOW TABLE STATUS only)')
1665+
1666+
# 18. The YEAR type cast in cast_value_for_saving uses a nested
1667+
# subquery to alias the integer cast as `value`:
1668+
#
1669+
# (SELECT CASE WHEN value IS NULL THEN NULL ... END
1670+
# FROM (SELECT CAST(<column-ref> AS INTEGER) AS value))
1671+
#
1672+
# where <column-ref> is `column1`, `column17`, etc. — a
1673+
# reference into the outer FROM (VALUES (...)) of the
1674+
# enclosing INSERT-SELECT. Turso fails to resolve the
1675+
# two-level-correlated reference and reports
1676+
# "Parse error: no such column: columnN", which breaks 6
1677+
# tests (testNonStrictModeTypeCasting,
1678+
# testColumnInfoForDateAndTimeDataTypes, the four
1679+
# testCastValues* — all of which insert into a YEAR column
1680+
# via INSERT...VALUES). Single-level correlated references
1681+
# (probed in step 17/probe) and the flat CASE forms used
1682+
# by date/time/datetime/timestamp work fine.
1683+
#
1684+
# Flatten the YEAR cast to inline the `CAST(... AS INTEGER)`
1685+
# at every WHEN, removing the inner derived FROM. CAST is
1686+
# idempotent so this is semantically identical and avoids
1687+
# the two-level binding.
1688+
path = 'src/sqlite/class-wp-pdo-mysql-on-sqlite.php'
1689+
src = open(path).read()
1690+
old_year = (
1691+
"\t\t\t\t\treturn sprintf(\n"
1692+
"\t\t\t\t\t\t\"(\n"
1693+
"\t\t\t\t\t\t\tSELECT CASE\n"
1694+
"\t\t\t\t\t\t\t\tWHEN value IS NULL THEN NULL\n"
1695+
"\t\t\t\t\t\t\t\tWHEN value = 0 THEN '0000'\n"
1696+
"\t\t\t\t\t\t\t\tWHEN value BETWEEN 1901 AND 2155 THEN value\n"
1697+
"\t\t\t\t\t\t\t\tWHEN value BETWEEN 1 AND 69 THEN 2000 + value\n"
1698+
"\t\t\t\t\t\t\t\tWHEN value BETWEEN 70 AND 99 THEN 1900 + value\n"
1699+
"\t\t\t\t\t\t\t\tELSE %s\n"
1700+
"\t\t\t\t\t\t\tEND\n"
1701+
"\t\t\t\t\t\t\tFROM (SELECT CAST(%s AS INTEGER) AS value)\n"
1702+
"\t\t\t\t\t\t)\",\n"
1703+
"\t\t\t\t\t\t$is_strict_mode\n"
1704+
"\t\t\t\t\t\t\t? sprintf( \"THROW('Out of range value: ''' || %s || '''')\", $translated_value )\n"
1705+
"\t\t\t\t\t\t\t: \"'0000'\",\n"
1706+
"\t\t\t\t\t\t$translated_value\n"
1707+
"\t\t\t\t\t);\n"
1708+
)
1709+
new_year = (
1710+
"\t\t\t\t\treturn sprintf(\n"
1711+
"\t\t\t\t\t\t\"CASE\n"
1712+
"\t\t\t\t\t\t\tWHEN CAST(%s AS INTEGER) IS NULL THEN NULL\n"
1713+
"\t\t\t\t\t\t\tWHEN CAST(%s AS INTEGER) = 0 THEN '0000'\n"
1714+
"\t\t\t\t\t\t\tWHEN CAST(%s AS INTEGER) BETWEEN 1901 AND 2155 THEN CAST(%s AS INTEGER)\n"
1715+
"\t\t\t\t\t\t\tWHEN CAST(%s AS INTEGER) BETWEEN 1 AND 69 THEN 2000 + CAST(%s AS INTEGER)\n"
1716+
"\t\t\t\t\t\t\tWHEN CAST(%s AS INTEGER) BETWEEN 70 AND 99 THEN 1900 + CAST(%s AS INTEGER)\n"
1717+
"\t\t\t\t\t\t\tELSE %s\n"
1718+
"\t\t\t\t\t\tEND\",\n"
1719+
"\t\t\t\t\t\t$translated_value,\n"
1720+
"\t\t\t\t\t\t$translated_value,\n"
1721+
"\t\t\t\t\t\t$translated_value,\n"
1722+
"\t\t\t\t\t\t$translated_value,\n"
1723+
"\t\t\t\t\t\t$translated_value,\n"
1724+
"\t\t\t\t\t\t$translated_value,\n"
1725+
"\t\t\t\t\t\t$translated_value,\n"
1726+
"\t\t\t\t\t\t$translated_value,\n"
1727+
"\t\t\t\t\t\t$is_strict_mode\n"
1728+
"\t\t\t\t\t\t\t? sprintf( \"THROW('Out of range value: ''' || %s || '''')\", $translated_value )\n"
1729+
"\t\t\t\t\t\t\t: \"'0000'\"\n"
1730+
"\t\t\t\t\t);\n"
1731+
)
1732+
assert old_year in src, 'YEAR cast nested-subquery block not found'
1733+
src = src.replace(old_year, new_year, 1)
1734+
open(path, 'w').write(src)
1735+
print('patched YEAR cast nested-subquery -> flat CASE form')
16651736
PY
16661737
16671738
- name: Run PHPUnit tests against Turso DB

0 commit comments

Comments
 (0)