Skip to content

Commit 976d86f

Browse files
committed
Translate multi-table UPDATE to rowid-IN subquery under Turso
Turso's translator rejects UPDATE ... FROM. Instead of relying on that SQLite 3.33+ feature, rewrite multi-table UPDATEs so the target rowids are selected via a subquery over the full tableReferenceList (which carries JOIN ON conditions inline), and the outer UPDATE is single-table only. Targets testUpdateWithJoinedTables, testUpdateWithJoinedTablesInNonStrictMode, and testUpdateWithJoinComplexQuery.
1 parent fffa8fc commit 976d86f

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,78 @@ jobs:
12751275
open(path, 'w').write(src.replace(old, new, 1))
12761276
print('patched DEFAULT_GENERATED simple-identifier unwrap')
12771277
1278+
# 16. Turso doesn't implement UPDATE ... FROM. The driver uses it to
1279+
# translate MySQL multi-table UPDATE (UPDATE t1, t2 JOIN t3 SET
1280+
# ... WHERE ...). Rewrite as UPDATE t SET ... WHERE rowid IN
1281+
# (SELECT t.rowid FROM tableRefList WHERE <orig where>).
1282+
old = (
1283+
"\t\t// Compose the FROM clause using all tables except the one being updated.\n"
1284+
"\t\t// UPDATE with FROM in SQLite is equivalent to UPDATE with JOIN in MySQL.\n"
1285+
"\t\t$from_items = array();\n"
1286+
)
1287+
new = (
1288+
"\t\t// For multi-table UPDATE, Turso doesn't support UPDATE ... FROM.\n"
1289+
"\t\t// Build a rowid-IN subquery that selects target rowids via the\n"
1290+
"\t\t// full tableReferenceList, then do a plain single-table UPDATE.\n"
1291+
"\t\tif ( count( $table_alias_map ) > 1 && null === $where_subquery ) {\n"
1292+
"\t\t\t$where_subquery = 'SELECT ' . $this->quote_sqlite_identifier( $update_target ) . '.rowid FROM '\n"
1293+
"\t\t\t\t. $this->translate_sequence(\n"
1294+
"\t\t\t\t\tarray(\n"
1295+
"\t\t\t\t\t\t$node->get_first_child_node( 'tableReferenceList' ),\n"
1296+
"\t\t\t\t\t\t$node->get_first_child_node( 'whereClause' ),\n"
1297+
"\t\t\t\t\t)\n"
1298+
"\t\t\t\t);\n"
1299+
"\t\t}\n"
1300+
"\n"
1301+
"\t\t// Compose the FROM clause using all tables except the one being updated.\n"
1302+
"\t\t// UPDATE with FROM in SQLite is equivalent to UPDATE with JOIN in MySQL.\n"
1303+
"\t\t$from_items = array();\n"
1304+
)
1305+
assert old in src, 'UPDATE from-clause preamble not found'
1306+
src = src.replace(old, new, 1)
1307+
1308+
# Also: when where_subquery is in play (either from ORDER/LIMIT or
1309+
# our multi-table rewrite), skip $from entirely — rowid-IN subquery
1310+
# contains all the JOIN info.
1311+
old2 = (
1312+
"\t\t$from = null;\n"
1313+
"\t\tif ( count( $from_items ) > 0 ) {\n"
1314+
"\t\t\t$from = 'FROM ' . implode( ', ', $from_items );\n"
1315+
"\t\t}\n"
1316+
)
1317+
new2 = (
1318+
"\t\t$from = null;\n"
1319+
"\t\tif ( count( $from_items ) > 0 && null === $where_subquery ) {\n"
1320+
"\t\t\t$from = 'FROM ' . implode( ', ', $from_items );\n"
1321+
"\t\t}\n"
1322+
)
1323+
assert old2 in src, 'UPDATE from-clause block not found'
1324+
src = src.replace(old2, new2, 1)
1325+
1326+
# Also skip the join_exprs re-append when where_subquery is in play:
1327+
# JOIN ON conditions are already inside the rowid-IN subquery.
1328+
old3 = (
1329+
"\t\t// With JOINs, we need to use the JOIN expressions in the WHERE clause.\n"
1330+
"\t\t$join_exprs = array_filter( array_column( $table_alias_map, 'join_expr' ) );\n"
1331+
"\t\tif ( count( $join_exprs ) > 0 ) {\n"
1332+
"\t\t\t$where_clause .= $where_clause ? ' AND ' : ' WHERE ';\n"
1333+
"\t\t\t$where_clause .= implode( ' AND ', $join_exprs );\n"
1334+
"\t\t}\n"
1335+
)
1336+
new3 = (
1337+
"\t\t// With JOINs, we need to use the JOIN expressions in the WHERE clause.\n"
1338+
"\t\t// Skip when $where_subquery holds the conditions already.\n"
1339+
"\t\t$join_exprs = array_filter( array_column( $table_alias_map, 'join_expr' ) );\n"
1340+
"\t\tif ( count( $join_exprs ) > 0 && null === $where_subquery ) {\n"
1341+
"\t\t\t$where_clause .= $where_clause ? ' AND ' : ' WHERE ';\n"
1342+
"\t\t\t$where_clause .= implode( ' AND ', $join_exprs );\n"
1343+
"\t\t}\n"
1344+
)
1345+
assert old3 in src, 'UPDATE join_exprs block not found'
1346+
src = src.replace(old3, new3, 1)
1347+
open(path, 'w').write(src)
1348+
print('patched UPDATE multi-table to rowid-IN subquery')
1349+
12781350
# 14. Update Translation_Tests::testHexadecimalLiterals to match
12791351
# the hex-literal alias force patch (which needs to stay so
12801352
# Turso doesn't mangle x'417a' into 17a' at runtime).

0 commit comments

Comments
 (0)