Skip to content

Commit aaa380c

Browse files
committed
Revert "Inline single-row INSERT VALUES into outer SELECT (correct order)"
This reverts commit 909fd69.
1 parent 403d319 commit aaa380c

1 file changed

Lines changed: 0 additions & 128 deletions

File tree

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

Lines changed: 0 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,134 +1407,6 @@ jobs:
14071407
open(path, 'w').write(src)
14081408
print('patched UPDATE multi-table to rowid-IN subquery')
14091409
1410-
# 17. For single-row INSERT ... VALUES without DEFAULT tokens,
1411-
# inline each value expression directly into the wrapping
1412-
# SELECT instead of using a FROM (VALUES ...) subquery with
1413-
# columnN aliases. Turso can't resolve the outer columnN
1414-
# reference through an INSERT...SELECT...FROM subquery even
1415-
# when the aliases are explicit. The inline form bypasses
1416-
# the columnN indirection entirely and is equivalent SQL on
1417-
# real SQLite.
1418-
src = open(path).read()
1419-
1420-
# Step 1: intercept select_list initialization for single-row
1421-
# VALUES without DEFAULT, set select_list to pre-translated
1422-
# expressions (wrapped in parens so CAST composes correctly),
1423-
# and set $inline_values = true.
1424-
old_init = (
1425-
"\t\t} else {\n"
1426-
"\t\t\t// When inserting from a VALUES list, SQLite uses a \"columnN\" naming.\n"
1427-
"\t\t\t// This also applies to the SET syntax, which is converted to VALUES.\n"
1428-
"\t\t\tforeach ( array_keys( $insert_list ) as $position ) {\n"
1429-
"\t\t\t\t$select_list[] = 'column' . ( $position + 1 );\n"
1430-
"\t\t\t}\n"
1431-
"\t\t}"
1432-
)
1433-
new_init = (
1434-
"\t\t} else {\n"
1435-
"\t\t\t$inline_values = false;\n"
1436-
"\t\t\tif ( 'insertFromConstructor' === $node->rule_name ) {\n"
1437-
"\t\t\t\t$insert_values = $node->get_first_child_node( 'insertValues' );\n"
1438-
"\t\t\t\t$value_list = $insert_values->get_first_child_node( 'valueList' );\n"
1439-
"\t\t\t\t$values_nodes = $value_list->get_child_nodes( 'values' );\n"
1440-
"\t\t\t\t$has_default = count( $values_nodes ) === 1\n"
1441-
"\t\t\t\t\t&& count( $values_nodes[0]->get_child_nodes( WP_MySQL_Lexer::DEFAULT_SYMBOL ) ) > 0;\n"
1442-
"\t\t\t\tif ( count( $values_nodes ) === 1 && ! $has_default ) {\n"
1443-
"\t\t\t\t\t$inline_values = true;\n"
1444-
"\t\t\t\t\tforeach ( $values_nodes[0]->get_child_nodes( 'expr' ) as $expr_node ) {\n"
1445-
"\t\t\t\t\t\t$select_list[] = '(' . $this->translate( $expr_node ) . ')';\n"
1446-
"\t\t\t\t\t}\n"
1447-
"\t\t\t\t}\n"
1448-
"\t\t\t}\n"
1449-
"\t\t\tif ( ! $inline_values ) {\n"
1450-
"\t\t\t\t// When inserting from a VALUES list, SQLite uses a \"columnN\" naming.\n"
1451-
"\t\t\t\t// This also applies to the SET syntax, which is converted to VALUES.\n"
1452-
"\t\t\t\tforeach ( array_keys( $insert_list ) as $position ) {\n"
1453-
"\t\t\t\t\t$select_list[] = 'column' . ( $position + 1 );\n"
1454-
"\t\t\t\t}\n"
1455-
"\t\t\t}\n"
1456-
"\t\t}"
1457-
)
1458-
assert old_init in src, 'select_list else-branch not found'
1459-
src = src.replace(old_init, new_init, 1)
1460-
1461-
# Step 2: CAST loop — use raw expression when $inline_values.
1462-
old_cast = (
1463-
"\t\t\t\t$position = array_search( $column['COLUMN_NAME'], $insert_list, true );\n"
1464-
"\t\t\t\t$identifier = $this->quote_sqlite_identifier( $select_list[ $position ] );\n"
1465-
"\t\t\t\t$value = $this->cast_value_for_saving( $column['DATA_TYPE'], $identifier );\n"
1466-
)
1467-
new_cast = (
1468-
"\t\t\t\t$position = array_search( $column['COLUMN_NAME'], $insert_list, true );\n"
1469-
"\t\t\t\t$identifier = ( isset( $inline_values ) && $inline_values )\n"
1470-
"\t\t\t\t\t? $select_list[ $position ]\n"
1471-
"\t\t\t\t\t: $this->quote_sqlite_identifier( $select_list[ $position ] );\n"
1472-
"\t\t\t\t$value = $this->cast_value_for_saving( $column['DATA_TYPE'], $identifier );\n"
1473-
)
1474-
assert old_cast in src, 'CAST identifier lookup not found'
1475-
src = src.replace(old_cast, new_cast, 1)
1476-
1477-
# Step 3: FROM wrapping — skip for inline case.
1478-
old_from_block = (
1479-
"\t\t// Wrap the original insert VALUES, SELECT, or SET list in a FROM clause.\n"
1480-
"\t\tif ( 'insertFromConstructor' === $node->rule_name ) {\n"
1481-
"\t\t\t// VALUES (...)\n"
1482-
"\t\t\t$insert_values = $node->get_first_child_node( 'insertValues' );\n"
1483-
"\t\t\t$from = $this->translate( $insert_values );\n"
1484-
)
1485-
new_from_block = (
1486-
"\t\t// Wrap the original insert VALUES, SELECT, or SET list in a FROM clause.\n"
1487-
"\t\tif ( isset( $inline_values ) && $inline_values ) {\n"
1488-
"\t\t\t$from = null;\n"
1489-
"\t\t} elseif ( 'insertFromConstructor' === $node->rule_name ) {\n"
1490-
"\t\t\t// VALUES (...)\n"
1491-
"\t\t\t$insert_values = $node->get_first_child_node( 'insertValues' );\n"
1492-
"\t\t\t$from = $this->translate( $insert_values );\n"
1493-
)
1494-
assert old_from_block in src, 'FROM wrapping block not found'
1495-
src = src.replace(old_from_block, new_from_block, 1)
1496-
1497-
# Step 4: final emission — omit FROM when $from is null.
1498-
old_emit = "\t\t$fragment .= ' FROM (' . $from . ') WHERE true';"
1499-
new_emit = (
1500-
"\t\tif ( null === $from ) {\n"
1501-
"\t\t\t$fragment .= ' WHERE true';\n"
1502-
"\t\t} else {\n"
1503-
"\t\t\t$fragment .= ' FROM (' . $from . ') WHERE true';\n"
1504-
"\t\t}"
1505-
)
1506-
assert old_emit in src, 'final FROM emission not found'
1507-
src = src.replace(old_emit, new_emit, 1)
1508-
open(path, 'w').write(src)
1509-
print('patched single-row VALUES to inline into outer SELECT (no FROM subquery)')
1510-
1511-
# Update Translation_Tests INSERT/REPLACE expectations for the inline form.
1512-
import re
1513-
path_tt = 'tests/WP_SQLite_Driver_Translation_Tests.php'
1514-
src_tt = open(path_tt).read()
1515-
srow_pat = re.compile(
1516-
r"SELECT (?P<cols>`column\d+`(?:, `column\d+`)*) FROM \(VALUES \( (?P<vals>[^()]+) \)\) WHERE true"
1517-
)
1518-
def rewrite_single_row(m):
1519-
cols = re.findall(r"`column\d+`", m.group('cols'))
1520-
vals = [v.strip() for v in m.group('vals').split(',')]
1521-
if len(cols) != len(vals):
1522-
return m.group(0)
1523-
return "SELECT " + ", ".join(f"({v})" for v in vals) + " WHERE true"
1524-
src_tt2, n1 = srow_pat.subn(rewrite_single_row, src_tt)
1525-
scast_pat = re.compile(
1526-
r"SELECT (?P<casts>CAST\(`column\d+` AS \w+\)(?:, CAST\(`column\d+` AS \w+\))*) FROM \(VALUES \( (?P<vals>[^()]+) \)\) WHERE true"
1527-
)
1528-
def rewrite_cast_row(m):
1529-
types = re.findall(r"CAST\(`column\d+` AS (\w+)\)", m.group('casts'))
1530-
vals = [v.strip() for v in m.group('vals').split(',')]
1531-
if len(types) != len(vals):
1532-
return m.group(0)
1533-
return "SELECT " + ", ".join(f"CAST(({v}) AS {t})" for v, t in zip(vals, types)) + " WHERE true"
1534-
src_tt2, n2 = scast_pat.subn(rewrite_cast_row, src_tt2)
1535-
open(path_tt, 'w').write(src_tt2)
1536-
print(f'patched Translation_Tests VALUES expectations: {n1} plain + {n2} CAST')
1537-
15381410
# 14. Update Translation_Tests::testHexadecimalLiterals to match
15391411
# the hex-literal alias force patch (which needs to stay so
15401412
# Turso doesn't mangle x'417a' into 17a' at runtime).

0 commit comments

Comments
 (0)