@@ -1286,14 +1286,6 @@ private function execute_update_statement( WP_Parser_Node $node ): void {
1286
1286
* SET_SYMBOL updateList whereClause? orderClause? simpleLimitClause?
1287
1287
*/
1288
1288
1289
- // Translate WITH clause.
1290
- $ with = $ this ->translate ( $ node ->get_first_child_node ( 'withClause ' ) );
1291
-
1292
- // Translate "UPDATE IGNORE" to "UPDATE OR IGNORE".
1293
- $ or_ignore = $ node ->has_child_token ( WP_MySQL_Lexer::IGNORE_SYMBOL )
1294
- ? 'OR IGNORE '
1295
- : null ;
1296
-
1297
1289
// Collect all tables used in the UPDATE clause (e.g, UPDATE t1, t2 JOIN t3).
1298
1290
$ table_alias_map = $ this ->create_table_reference_map (
1299
1291
$ node ->get_first_child_node ( 'tableReferenceList ' )
@@ -1316,7 +1308,7 @@ private function execute_update_statement( WP_Parser_Node $node ): void {
1316
1308
if ( null === $ table_or_alias ) {
1317
1309
$ persistent_table_names = array ();
1318
1310
$ temporary_table_names = array ();
1319
- foreach ( array_column ( $ table_alias_map , 'table_name ' ) as $ table_name ) {
1311
+ foreach ( array_filter ( array_column ( $ table_alias_map , 'table_name ' ) ) as $ table_name ) {
1320
1312
$ is_temporary = $ this ->information_schema_builder ->temporary_table_exists ( $ table_name );
1321
1313
$ quoted_table_name = $ this ->connection ->quote ( $ table_name );
1322
1314
if ( $ is_temporary ) {
@@ -1387,18 +1379,42 @@ private function execute_update_statement( WP_Parser_Node $node ): void {
1387
1379
throw $ this ->new_not_supported_exception ( 'UPDATE statement modifying multiple tables ' );
1388
1380
}
1389
1381
1382
+ // Translate WITH clause.
1383
+ $ with = $ this ->translate ( $ node ->get_first_child_node ( 'withClause ' ) );
1384
+
1385
+ // Translate "UPDATE IGNORE" to "UPDATE OR IGNORE".
1386
+ $ or_ignore = $ node ->has_child_token ( WP_MySQL_Lexer::IGNORE_SYMBOL )
1387
+ ? 'OR IGNORE '
1388
+ : null ;
1389
+
1390
+ // Compose the update target clause.
1391
+ $ update_target_table = $ table_alias_map [ $ update_target ]['table_name ' ] ?? $ update_target ;
1392
+ $ update_target_clause = $ this ->quote_sqlite_identifier ( $ update_target_table );
1393
+ if ( $ update_target !== $ update_target_table ) {
1394
+ $ update_target_clause .= ' AS ' . $ this ->quote_sqlite_identifier ( $ update_target );
1395
+ }
1396
+
1390
1397
// Compose the FROM clause using all tables except the one being updated.
1391
1398
// UPDATE with FROM in SQLite is equivalent to UPDATE with JOIN in MySQL.
1392
1399
$ from_items = array ();
1393
1400
foreach ( $ table_alias_map as $ alias => $ data ) {
1394
- $ table_name = $ data ['table_name ' ];
1395
1401
if ( $ alias === $ update_target ) {
1396
1402
continue ;
1397
1403
}
1398
1404
1399
- $ from_item = $ this ->quote_sqlite_identifier ( $ alias );
1405
+ $ table_name = $ data ['table_name ' ];
1406
+
1407
+ // Derived table.
1408
+ if ( null === $ table_name ) {
1409
+ $ from_item = $ data ['table_expr ' ] . ' AS ' . $ this ->quote_sqlite_identifier ( $ alias );
1410
+ $ from_items [] = $ from_item ;
1411
+ continue ;
1412
+ }
1413
+
1414
+ // Regular table.
1415
+ $ from_item = $ this ->quote_sqlite_identifier ( $ table_name );
1400
1416
if ( $ alias !== $ table_name ) {
1401
- $ from_item .= ' AS ' . $ this ->quote_sqlite_identifier ( $ table_name );
1417
+ $ from_item .= ' AS ' . $ this ->quote_sqlite_identifier ( $ alias );
1402
1418
}
1403
1419
$ from_items [] = $ from_item ;
1404
1420
}
@@ -1449,7 +1465,7 @@ private function execute_update_statement( WP_Parser_Node $node ): void {
1449
1465
$ with ,
1450
1466
'UPDATE ' ,
1451
1467
$ or_ignore ,
1452
- $ this -> quote_sqlite_identifier ( $ update_target ) ,
1468
+ $ update_target_clause ,
1453
1469
'SET ' ,
1454
1470
$ update_list ,
1455
1471
$ from ,
@@ -2825,6 +2841,10 @@ private function translate( $node ): ?string {
2825
2841
case 'indexHint ' :
2826
2842
case 'indexHintList ' :
2827
2843
return null ;
2844
+ case 'lockingClause ' :
2845
+ // SQLite doesn't support locking clauses (SELECT ... FOR UPDATE).
2846
+ // They are not needed in SQLite due to the database file locking.
2847
+ return null ;
2828
2848
default :
2829
2849
return $ this ->translate_sequence ( $ node ->get_children () );
2830
2850
}
@@ -4107,8 +4127,9 @@ private function create_select_item_disambiguation_map( WP_Parser_Node $select_i
4107
4127
* The returned array maps table aliases to table names and additional data:
4108
4128
* - key: table alias, or name if no alias is used
4109
4129
* - value: an array of table data
4110
- * - table_name: the real name of the table
4111
- * - join_expr: the join expression used for the table
4130
+ * - table_name: the real name of the table (null for derived tables)
4131
+ * - table_expr: the table expression for a derived table (null for regular tables)
4132
+ * - join_expr: the join expression used for the table (null when no join is used)
4112
4133
*
4113
4134
* MySQL has a non-stand ardsyntax extension where a comma-separated list of
4114
4135
* table references is allowed as a table reference in itself, for instance:
@@ -4145,11 +4166,24 @@ private function create_table_reference_map( WP_Parser_Node $node ): array {
4145
4166
4146
4167
if ( 'singleTable ' === $ child ->rule_name ) {
4147
4168
// Extract data from the "singleTable" node.
4148
- $ name = $ this ->translate ( $ child ->get_first_child_node ( 'tableRef ' ) );
4149
- $ alias = $ this ->translate ( $ child ->get_first_child_node ( 'tableAlias ' ) );
4169
+ $ name = $ this ->translate ( $ child ->get_first_child_node ( 'tableRef ' ) );
4170
+ $ alias_node = $ child ->get_first_child_node ( 'tableAlias ' );
4171
+ $ alias = $ alias_node ? $ this ->translate ( $ alias_node ->get_first_child_node ( 'identifier ' ) ) : null ;
4150
4172
4151
4173
$ table_map [ $ this ->unquote_sqlite_identifier ( $ alias ?? $ name ) ] = array (
4152
4174
'table_name ' => $ this ->unquote_sqlite_identifier ( $ name ),
4175
+ 'table_expr ' => null ,
4176
+ 'join_expr ' => $ this ->translate ( $ join_expr ),
4177
+ );
4178
+ } elseif ( 'derivedTable ' === $ child ->rule_name ) {
4179
+ // Extract data from the "derivedTable" node.
4180
+ $ subquery = $ child ->get_first_descendant_node ( 'subquery ' );
4181
+ $ alias_node = $ child ->get_first_child_node ( 'tableAlias ' );
4182
+ $ alias = $ alias_node ? $ this ->translate ( $ alias_node ->get_first_child_node ( 'identifier ' ) ) : null ;
4183
+
4184
+ $ table_map [ $ this ->unquote_sqlite_identifier ( $ alias ) ] = array (
4185
+ 'table_name ' => null ,
4186
+ 'table_expr ' => $ this ->translate ( $ subquery ),
4153
4187
'join_expr ' => $ this ->translate ( $ join_expr ),
4154
4188
);
4155
4189
} elseif ( 'tableReferenceListParens ' === $ child ->rule_name ) {
0 commit comments