@@ -1057,113 +1057,10 @@ jobs:
10571057 open(path, 'w').write(src.replace(old, new, 1))
10581058 print('patched CHECK TABLE missing-table check')
10591059
1060- # 12. Turso doesn't propagate VALUES row-column aliases (column1,
1061- # column2, ...) through `INSERT ... SELECT ... FROM (VALUES ...)`
1062- # subqueries — outer reference to `column1` fails with
1063- # "no such column". Replace the VALUES subquery with a
1064- # UNION ALL of SELECTs carrying explicit `expr AS columnN`
1065- # aliases (single-row case collapses to a single SELECT).
1066- path = 'src/sqlite/class-wp-pdo-mysql-on-sqlite.php'
1067- src = open(path).read()
1068- old = (
1069- "\t\tif ( 'insertFromConstructor' === $node->rule_name ) {\n"
1070- "\t\t\t// VALUES (...)\n"
1071- "\t\t\t$insert_values = $node->get_first_child_node( 'insertValues' );\n"
1072- "\t\t\t$from = $this->translate( $insert_values );\n"
1073- "\n"
1074- "\t\t\t/**\n"
1075- "\t\t\t * The automatic \"columnN\" naming for VALUES lists is supported only\n"
1076- "\t\t\t * from SQLite 3.33.0. For older versions, we need to emulate it by\n"
1077- "\t\t\t * prepending a dummy VALUES list header via the UNION ALL operator:\n"
1078- "\t\t\t *\n"
1079- "\t\t\t * SELECT\n"
1080- "\t\t\t * NULL AS `column1`, NULL AS `column2`, ... WHERE FALSE\n"
1081- "\t\t\t * UNION ALL\n"
1082- "\t\t\t * VALUES (value1, value2, ...)\n"
1083- "\t\t\t */\n"
1084- "\t\t\t$is_values_naming_supported = version_compare( $this->get_sqlite_version(), '3.33.0', '>=' );\n"
1085- "\t\t\tif ( ! $is_values_naming_supported ) {\n"
1086- "\t\t\t\t$values_list = $insert_values->get_first_child_node( 'valueList' );\n"
1087- "\t\t\t\t$values = $values_list->get_first_child_node( 'values' );\n"
1088- "\t\t\t\t$value_count = (\n"
1089- "\t\t\t\t\tcount( $values->get_child_nodes( 'expr' ) )\n"
1090- "\t\t\t\t\t+ count( $values->get_child_nodes( WP_MySQL_Lexer::DEFAULT_SYMBOL ) )\n"
1091- "\t\t\t\t);\n"
1092- "\n"
1093- "\t\t\t\t$columns_list = '';\n"
1094- "\t\t\t\tfor ( $i = 1; $i <= $value_count; $i++ ) {\n"
1095- "\t\t\t\t\t$columns_list .= $i > 1 ? ', ' : '';\n"
1096- "\t\t\t\t\t$columns_list .= 'NULL AS ' . $this->quote_sqlite_identifier( 'column' . $i );\n"
1097- "\t\t\t\t}\n"
1098- "\t\t\t\t$from = 'SELECT ' . $columns_list . ' WHERE FALSE UNION ALL ' . $from;\n"
1099- "\t\t\t}\n"
1100- "\t\t}"
1101- )
1102- new = (
1103- "\t\tif ( 'insertFromConstructor' === $node->rule_name ) {\n"
1104- "\t\t\t// Build a UNION ALL of SELECT rows with explicit `expr AS columnN`\n"
1105- "\t\t\t// aliases. Turso doesn't propagate implicit VALUES column names\n"
1106- "\t\t\t// through INSERT...SELECT subqueries.\n"
1107- "\t\t\t$insert_values = $node->get_first_child_node( 'insertValues' );\n"
1108- "\t\t\t$value_list = $insert_values->get_first_child_node( 'valueList' );\n"
1109- "\t\t\t$values_nodes = $value_list->get_child_nodes( 'values' );\n"
1110- "\t\t\t$select_rows = array();\n"
1111- "\t\t\tforeach ( $values_nodes as $values_node ) {\n"
1112- "\t\t\t\t$position = 0;\n"
1113- "\t\t\t\t$row_parts = array();\n"
1114- "\t\t\t\tforeach ( $values_node->get_children() as $child ) {\n"
1115- "\t\t\t\t\tif ( $child instanceof WP_Parser_Node && 'expr' === $child->rule_name ) {\n"
1116- "\t\t\t\t\t\t$expr_sql = $this->translate( $child );\n"
1117- "\t\t\t\t\t\t$row_parts[] = $expr_sql . ' AS ' . $this->quote_sqlite_identifier( 'column' . ( $position + 1 ) );\n"
1118- "\t\t\t\t\t\t$position++;\n"
1119- "\t\t\t\t\t} elseif ( $child instanceof WP_Parser_Token && WP_MySQL_Lexer::DEFAULT_SYMBOL === $child->id ) {\n"
1120- "\t\t\t\t\t\t$row_parts[] = 'NULL AS ' . $this->quote_sqlite_identifier( 'column' . ( $position + 1 ) );\n"
1121- "\t\t\t\t\t\t$position++;\n"
1122- "\t\t\t\t\t}\n"
1123- "\t\t\t\t}\n"
1124- "\t\t\t\t$select_rows[] = 'SELECT ' . implode( ', ', $row_parts );\n"
1125- "\t\t\t}\n"
1126- "\t\t\t$from = implode( ' UNION ALL ', $select_rows );\n"
1127- "\t\t}"
1128- )
1129- assert old in src, 'insertFromConstructor VALUES block not found'
1130- open(path, 'w').write(src.replace(old, new, 1))
1131- print('patched insertFromConstructor to emit SELECT-with-aliases form')
1132-
1133- # 13. Update Translation_Tests expectations to match the new
1134- # SELECT-AS-columnN form emitted by insertFromConstructor.
1135- import re
1136- path = 'tests/WP_SQLite_Driver_Translation_Tests.php'
1137- src = open(path).read()
1138- # Match `FROM (VALUES ( v1 [, v2 ...] ) [, ( ... ) ...])`
1139- # where values are simple literals (no nested parens or strings
1140- # containing parens — good enough for all hits in this file).
1141- values_pattern = re.compile(
1142- r"\(VALUES "
1143- r"\( ([^()]+) \)"
1144- r"((?: , \( [^()]+ \))*)"
1145- r"\)"
1146- )
1147- def rewrite_values(m):
1148- first_row = m.group(1).strip()
1149- extra_rows_text = m.group(2)
1150- rows = [first_row]
1151- for row_match in re.finditer(r"\( ([^()]+) \)", extra_rows_text):
1152- rows.append(row_match.group(1).strip())
1153- selects = []
1154- for row in rows:
1155- values = [v.strip() for v in row.split(',')]
1156- parts = [f"{v} AS `column{i+1}`" for i, v in enumerate(values)]
1157- selects.append("SELECT " + ", ".join(parts))
1158- return "(" + " UNION ALL ".join(selects) + ")"
1159- new_src, n = values_pattern.subn(rewrite_values, src)
1160- assert n >= 10, f'expected 10+ VALUES expectations, matched {n}'
1161- open(path, 'w').write(new_src)
1162- print(f'patched {n} Translation_Tests VALUES expectations to SELECT form')
1163-
11641060 # 14. Update Translation_Tests::testHexadecimalLiterals to match
11651061 # the hex-literal alias force patch (which needs to stay so
11661062 # Turso doesn't mangle x'417a' into 17a' at runtime).
1063+ path = 'tests/WP_SQLite_Driver_Translation_Tests.php'
11671064 src = open(path).read()
11681065 for old_q, new_q in [
11691066 ("\"SELECT x'417a'\",\n\t\t\t\"SELECT x'417a'\"",
0 commit comments