@@ -4430,7 +4430,7 @@ private function translate_insert_or_replace_body_in_non_strict_mode(
44304430 // This method is always used with the main database.
44314431 $ database = $ this ->get_saved_db_name ( $ this ->main_db_name );
44324432
4433- // 1. Get column metadata from information schema.
4433+ // Get column metadata for the target table from the information schema.
44344434 $ is_temporary = $ this ->information_schema_builder ->temporary_table_exists ( $ table_name );
44354435 $ columns_table = $ this ->information_schema_builder ->get_table_name ( $ is_temporary , 'columns ' );
44364436 $ columns = $ this ->execute_sqlite_query (
@@ -4444,36 +4444,41 @@ private function translate_insert_or_replace_body_in_non_strict_mode(
44444444 array ( $ database , $ table_name )
44454445 )->fetchAll ( PDO ::FETCH_ASSOC );
44464446
4447- // 2. Get the list of fields explicitly defined in the INSERT statement.
4447+ // Get a list of columns that are targeted by the INSERT or REPLACE query.
4448+ // This is either an explicit column list, or all columns of the table.
44484449 $ insert_list = array ();
44494450 $ fields_node = $ node ->get_first_child_node ( 'fields ' );
44504451 if ( $ fields_node ) {
4451- // This is the optional "INSERT INTO ... (field1, field2 , ...)" list.
4452+ // "INSERT INTO ... (column1, column2 , ...)"
44524453 foreach ( $ fields_node ->get_child_nodes () as $ field ) {
44534454 $ column_name = $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ field ) );
44544455 $ insert_list [] = strtolower ( $ column_name );
44554456 }
44564457 } elseif ( 'updateList ' === $ node ->rule_name ) {
4457- // This is the "INSERT INTO ... SET c1 = v1, c2 = v2 , ... " syntax.
4458+ // "INSERT INTO ... SET column1 = value1, column2 = value2 , ..."
44584459 foreach ( $ node ->get_child_nodes ( 'updateElement ' ) as $ update_element ) {
44594460 $ column_ref = $ update_element ->get_first_child_node ( 'columnRef ' );
44604461 $ column_name = $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ column_ref ) );
44614462 $ insert_list [] = strtolower ( $ column_name );
44624463 }
44634464 } else {
4464- // When no explicit field list is provided, all columns are required.
4465+ // "INSERT INTO ... VALUES(...)" or "INSERT INTO ... SELECT ..."
4466+ // No explicit column list is provided; we need to list all columns.
44654467 foreach ( array_column ( $ columns , 'COLUMN_NAME ' ) as $ column_name ) {
44664468 $ insert_list [] = strtolower ( $ column_name );
44674469 }
44684470 }
44694471
4470- // 3. Filter out omitted columns that will get a value from the SQLite engine.
4471- // That is, nullable columns, columns with defaults, and generated columns.
4472+ // Prepare a helper map of columns that are included in the INSERT list.
4473+ $ insert_map = array_combine ( $ insert_list , $ insert_list );
4474+
4475+ // Filter out omitted columns that will get a value from the SQLite engine.
4476+ // That is, nullable columns, columns with defaults, and generated columns.
44724477 $ columns = array_values (
44734478 array_filter (
44744479 $ columns ,
4475- function ( $ column ) use ( $ insert_list ) {
4476- $ is_omitted = ! in_array ( $ column ['COLUMN_NAME ' ], $ insert_list , true );
4480+ function ( $ column ) use ( $ insert_map ) {
4481+ $ is_omitted = ! isset ( $ insert_map [ $ column ['COLUMN_NAME ' ] ] );
44774482 if ( ! $ is_omitted ) {
44784483 return true ;
44794484 }
@@ -4485,11 +4490,17 @@ function ( $column ) use ( $insert_list ) {
44854490 )
44864491 );
44874492
4488- // 4. Get the list of column names returned by VALUES or SELECT clause.
4493+ /*
4494+ * Get a list of column names for the INSERT or REPLACE values clause.
4495+ * These are the columns that will be used in a SELECT statement when
4496+ * the values clause is wrapped in a subquery:
4497+ *
4498+ * INSERT INTO ... SELECT <select-list> FROM (<values-from-original-query>)
4499+ */
44894500 $ select_list = array ();
44904501 if ( 'insertQueryExpression ' === $ node ->rule_name ) {
44914502 // When inserting from a SELECT query, we don't know the column names.
4492- // Let's wrap the query with a SELECT (...) LIMIT 0 to get obtain them.
4503+ // Let's wrap the query with a " SELECT (...) LIMIT 0" to obtain them.
44934504 $ expr = $ node ->get_first_child_node ( 'queryExpressionOrParens ' );
44944505 $ stmt = $ this ->execute_sqlite_query (
44954506 'SELECT * FROM ( ' . $ this ->translate ( $ expr ) . ') LIMIT 1 '
@@ -4500,24 +4511,25 @@ function ( $column ) use ( $insert_list ) {
45004511 $ select_list [] = $ stmt ->getColumnMeta ( $ i )['name ' ];
45014512 }
45024513 } else {
4503- // When inserting from a VALUES list, SQLite uses "columnN" naming.
4514+ // When inserting from a VALUES list, SQLite uses a "columnN" naming.
4515+ // This also applies to the SET syntax, which is converted to VALUES.
45044516 foreach ( array_keys ( $ insert_list ) as $ position ) {
45054517 $ select_list [] = 'column ' . ( $ position + 1 );
45064518 }
45074519 }
45084520
4509- // 5. Compose a new INSERT field list with all columns from the table.
4521+ // Compose a new INSERT column list with all columns from the table.
45104522 $ fragment = '( ' ;
45114523 foreach ( $ columns as $ i => $ column ) {
45124524 $ fragment .= $ i > 0 ? ', ' : '' ;
45134525 $ fragment .= $ this ->quote_sqlite_identifier ( $ column ['COLUMN_NAME ' ] );
45144526 }
45154527 $ fragment .= ') ' ;
45164528
4517- // 6. Compose a wrapper SELECT statement emulating IMPLICIT DEFAULT values.
4529+ // Compose a wrapper SELECT statement emulating IMPLICIT DEFAULT values.
45184530 $ fragment .= ' SELECT ' ;
45194531 foreach ( $ columns as $ i => $ column ) {
4520- $ is_omitted = ! in_array ( $ column ['COLUMN_NAME ' ], $ insert_list , true );
4532+ $ is_omitted = ! isset ( $ insert_map [ $ column ['COLUMN_NAME ' ] ] );
45214533 $ fragment .= $ i > 0 ? ', ' : '' ;
45224534 if ( $ is_omitted ) {
45234535 /*
@@ -4540,7 +4552,7 @@ function ( $column ) use ( $insert_list ) {
45404552 }
45414553 }
45424554
4543- // 6. Wrap the original insert VALUES, SELECT, or SET list in a FROM clause.
4555+ // Wrap the original insert VALUES, SELECT, or SET list in a FROM clause.
45444556 if ( 'insertFromConstructor ' === $ node ->rule_name ) {
45454557 // VALUES (...)
45464558 $ from = $ this ->translate (
0 commit comments