@@ -1037,6 +1037,11 @@ private function execute_update_statement( WP_Parser_Node $node ): void {
10371037 if ( $ child instanceof WP_MySQL_Token && WP_MySQL_Lexer::IGNORE_SYMBOL === $ child ->id ) {
10381038 // Translate "UPDATE IGNORE" to "UPDATE OR IGNORE".
10391039 $ parts [] = 'OR IGNORE ' ;
1040+ } elseif ( $ child instanceof WP_Parser_Node && 'updateList ' === $ child ->rule_name ) {
1041+ $ table_ref = $ node ->get_first_child_node ( 'tableReferenceList ' )->get_first_child_node ( 'tableReference ' );
1042+ $ table_name = $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ table_ref ) );
1043+ // TODO: Use this only in non-strict mode.
1044+ $ parts [] = $ this ->translate_update_list_in_non_strict_mode ( $ table_name , $ child );
10401045 } else {
10411046 $ parts [] = $ this ->translate ( $ child );
10421047 }
@@ -2802,6 +2807,70 @@ private function translate_insert_or_replace_body_in_non_strict_mode(
28022807 return $ fragment ;
28032808 }
28042809
2810+ /**
2811+ * Translate UPDATE list, emulating MySQL implicit defaults in non-strict mode.
2812+ *
2813+ * In MySQL, the behavior of INSERT and UPDATE statements depends on whether
2814+ * the STRICT_TRANS_TABLES (InnoDB) or STRICT_ALL_TABLES SQL mode is enabled.
2815+ *
2816+ * When the strict mode is not enabled, executing an UPDATE statement that
2817+ * sets a NOT NULL column value to NULL saves an IMPLICIT DEFAULT instead.
2818+ *
2819+ * @param string $table_name The name of the target table.
2820+ * @param WP_Parser_Node $node The "updateList" AST node.
2821+ * @return string The translated UPDATE list.
2822+ */
2823+ private function translate_update_list_in_non_strict_mode ( string $ table_name , WP_Parser_Node $ node ): string {
2824+ // 1. Get column metadata from information schema.
2825+ $ is_temporary = $ this ->information_schema_builder ->temporary_table_exists ( $ table_name );
2826+ $ columns_table = $ this ->information_schema_builder ->get_table_name ( $ is_temporary , 'columns ' );
2827+ $ columns = $ this ->execute_sqlite_query (
2828+ "
2829+ SELECT column_name, is_nullable, data_type, column_default
2830+ FROM $ columns_table
2831+ WHERE table_schema = ?
2832+ AND table_name = ?
2833+ " ,
2834+ array ( $ this ->db_name , $ table_name )
2835+ )->fetchAll ( PDO ::FETCH_ASSOC );
2836+ $ column_map = array_combine ( array_column ( $ columns , 'COLUMN_NAME ' ), $ columns );
2837+
2838+ // 2. Translate UPDATE list, emulating implicit defaults for NULLs values.
2839+ $ fragment = '' ;
2840+ foreach ( $ node ->get_child_nodes () as $ i => $ update_element ) {
2841+ $ column_ref = $ update_element ->get_first_child_node ( 'columnRef ' );
2842+ $ expr = $ update_element ->get_first_child_node ( 'expr ' );
2843+
2844+ // Get column info.
2845+ $ column_name = $ this ->unquote_sqlite_identifier ( $ this ->translate ( $ column_ref ) );
2846+ $ column_info = $ column_map [ $ column_name ];
2847+ $ data_type = $ column_info ['DATA_TYPE ' ];
2848+ $ is_nullable = 'YES ' === $ column_info ['IS_NULLABLE ' ];
2849+ $ default = $ column_info ['COLUMN_DEFAULT ' ];
2850+
2851+ // Get the UPDATE value. It's either an expression or a DEFAULT keyword.
2852+ if ( null === $ expr ) {
2853+ // Emulate "column = DEFAULT".
2854+ $ value = null === $ default ? 'NULL ' : $ this ->pdo ->quote ( $ default );
2855+ } else {
2856+ $ value = $ this ->translate ( $ expr );
2857+ }
2858+
2859+ // If the column is NOT NULL, a NULL value resolves to implicit default.
2860+ $ implicit_default = self ::DATA_TYPE_IMPLICIT_DEFAULT_MAP [ $ data_type ] ?? null ;
2861+ if ( ! $ is_nullable && null !== $ implicit_default ) {
2862+ $ value = sprintf ( 'COALESCE(%s, %s) ' , $ value , $ this ->pdo ->quote ( $ implicit_default ) );
2863+ }
2864+
2865+ // Compose the UPDATE list item.
2866+ $ fragment .= $ i > 0 ? ', ' : '' ;
2867+ $ fragment .= $ this ->translate ( $ column_ref );
2868+ $ fragment .= ' = ' ;
2869+ $ fragment .= $ value ;
2870+ }
2871+ return $ fragment ;
2872+ }
2873+
28052874 /**
28062875 * Generate a SQLite CREATE TABLE statement from information schema data.
28072876 *
0 commit comments