1+ use crate :: translate:: expr:: { walk_expr, walk_expr_mut, WalkControl } ;
12use std:: sync:: Arc ;
23use turso_parser:: {
34 ast:: { self , TableInternalId } ,
@@ -9,7 +10,6 @@ use crate::{
910 schema:: { Column , ForeignKey , Table , RESERVED_TABLE_PREFIXES } ,
1011 translate:: {
1112 emitter:: Resolver ,
12- expr:: { walk_expr, WalkControl } ,
1313 plan:: { ColumnUsedMask , OuterQueryReference , TableReferences } ,
1414 } ,
1515 util:: normalize_ident,
@@ -2511,8 +2511,14 @@ fn rewrite_expr_column_ref(
25112511 )
25122512}
25132513
2514- /// Walk an Upsert clause and rewrite table references for table rename.
2515- /// Handles conflict targets, target WHERE, DO SET expressions, DO WHERE, and chained upserts.
2514+ /// Walk an Upsert clause and rewrite qualified table references for table rename.
2515+ ///
2516+ /// Handles ON CONFLICT clauses that may contain table-qualified column references:
2517+ /// - Conflict targets: Expressions in `ON CONFLICT(expr, ...)` that may reference the table
2518+ /// - Target WHERE: The optional `WHERE` clause after conflict targets
2519+ /// - DO UPDATE SET: Expressions in `SET col = expr` assignments (e.g., `val = t1.val || 'x'`)
2520+ /// - DO UPDATE WHERE: The optional `WHERE` clause of the DO UPDATE
2521+ /// - Chained upserts: Recursively handles multiple ON CONFLICT clauses
25162522pub fn walk_upsert_for_table_rename (
25172523 upsert : & mut ast:: Upsert ,
25182524 old_table_norm : & str ,
@@ -2555,135 +2561,76 @@ pub fn walk_upsert_for_table_rename(
25552561 changed
25562562}
25572563
2564+ /// Rewrite qualified table references in an expression tree for table rename.
2565+ ///
2566+ /// Handles table-qualified column references in trigger expressions:
2567+ /// - Qualified column refs (e.g., `t1.col`): Rewrites to `"t1_new".col`
2568+ /// - Doubly-qualified (e.g., `main.t1.col`): Rewrites to `main."t1_new".col`
2569+ /// - InTable expression (e.g., `x IN t1`): Rewrites to `x IN "t1_new"`
2570+ /// - Subqueries and nested expressions: Recursively walks all child expressions
2571+ ///
2572+ /// `old_table_norm`: Normalized (lowercase) name of the table being renamed.
2573+ /// `new_table_name`: New name for the table (will be double-quoted in output).
2574+ ///
2575+ /// This function uses `walk_expr_mut` from `expr.rs` to traverse all expression
2576+ /// variants automatically, including WINDOW clauses and frame bounds. Only the
2577+ /// special cases (table-qualified names) are handled in the callback.
25582578pub fn rewrite_expr_table_refs_for_rename (
25592579 expr : & mut Box < ast:: Expr > ,
25602580 old_table_norm : & str ,
25612581 new_table_name : & str ,
25622582) -> bool {
25632583 let mut changed = false ;
25642584
2565- match expr. as_mut ( ) {
2566- ast:: Expr :: Qualified ( ref mut tbl_name, _) => {
2567- if normalize_ident ( tbl_name. as_str ( ) ) == old_table_norm {
2568- * tbl_name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
2569- changed = true ;
2570- }
2571- }
2572-
2573- ast:: Expr :: DoublyQualified ( _, ref mut tbl_name, _) => {
2574- if normalize_ident ( tbl_name. as_str ( ) ) == old_table_norm {
2575- * tbl_name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
2576- changed = true ;
2577- }
2578- }
2579- ast:: Expr :: Between {
2580- lhs, start, end, ..
2581- } => {
2582- changed |= rewrite_expr_table_refs_for_rename ( lhs, old_table_norm, new_table_name) ;
2583- changed |= rewrite_expr_table_refs_for_rename ( start, old_table_norm, new_table_name) ;
2584- changed |= rewrite_expr_table_refs_for_rename ( end, old_table_norm, new_table_name) ;
2585- }
2586- ast:: Expr :: Binary ( lhs, _, rhs) => {
2587- changed |= rewrite_expr_table_refs_for_rename ( lhs, old_table_norm, new_table_name) ;
2588- changed |= rewrite_expr_table_refs_for_rename ( rhs, old_table_norm, new_table_name) ;
2589- }
2590- ast:: Expr :: Case {
2591- base,
2592- when_then_pairs,
2593- else_expr,
2594- ..
2595- } => {
2596- if let Some ( b) = base {
2597- changed |= rewrite_expr_table_refs_for_rename ( b, old_table_norm, new_table_name) ;
2598- }
2599- for ( when_expr, then_expr) in when_then_pairs {
2600- changed |=
2601- rewrite_expr_table_refs_for_rename ( when_expr, old_table_norm, new_table_name) ;
2602- changed |=
2603- rewrite_expr_table_refs_for_rename ( then_expr, old_table_norm, new_table_name) ;
2585+ // Use walk_expr_mut to traverse ALL expression types automatically.
2586+ // This handles WINDOW clauses, frame bounds, and all other nested expressions.
2587+ let _ = walk_expr_mut ( expr, & mut |e : & mut ast:: Expr | -> Result < WalkControl > {
2588+ match e {
2589+ // Handle table-qualified column references: tbl.col -> "new_tbl".col
2590+ ast:: Expr :: Qualified ( ref mut tbl_name, _) => {
2591+ if normalize_ident ( tbl_name. as_str ( ) ) == old_table_norm {
2592+ * tbl_name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
2593+ changed = true ;
2594+ }
26042595 }
2605- if let Some ( else_e) = else_expr {
2606- changed |=
2607- rewrite_expr_table_refs_for_rename ( else_e, old_table_norm, new_table_name) ;
2596+ // Handle doubly-qualified references: db.tbl.col -> db."new_tbl".col
2597+ ast:: Expr :: DoublyQualified ( _, ref mut tbl_name, _) => {
2598+ if normalize_ident ( tbl_name. as_str ( ) ) == old_table_norm {
2599+ * tbl_name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
2600+ changed = true ;
2601+ }
26082602 }
2609- }
2610- ast:: Expr :: Cast { expr : inner, .. } => {
2611- changed |= rewrite_expr_table_refs_for_rename ( inner, old_table_norm, new_table_name) ;
2612- }
2613- ast:: Expr :: Collate ( inner, _) => {
2614- changed |= rewrite_expr_table_refs_for_rename ( inner, old_table_norm, new_table_name) ;
2615- }
2616- ast:: Expr :: FunctionCall {
2617- args, filter_over, ..
2618- } => {
2619- for arg in args {
2620- changed |= rewrite_expr_table_refs_for_rename ( arg, old_table_norm, new_table_name) ;
2603+ // Handle `x IN tbl` - the table reference is not a sub-expression
2604+ ast:: Expr :: InTable { rhs, .. } => {
2605+ if normalize_ident ( rhs. name . as_str ( ) ) == old_table_norm {
2606+ rhs. name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
2607+ changed = true ;
2608+ }
26212609 }
2622- if let Some ( ref mut filter_expr) = filter_over. filter_clause {
2610+ // Handle subqueries - walk_expr_mut doesn't descend into SELECT statements
2611+ ast:: Expr :: InSelect { rhs, .. } => {
26232612 changed |=
2624- rewrite_expr_table_refs_for_rename ( filter_expr , old_table_norm, new_table_name) ;
2613+ rewrite_select_table_refs_for_rename ( rhs , old_table_norm, new_table_name) ;
26252614 }
2626- }
2627- ast:: Expr :: FunctionCallStar { filter_over, .. } => {
2628- if let Some ( ref mut filter_expr) = filter_over. filter_clause {
2615+ ast:: Expr :: Subquery ( select) | ast:: Expr :: Exists ( select) => {
26292616 changed |=
2630- rewrite_expr_table_refs_for_rename ( filter_expr, old_table_norm, new_table_name) ;
2631- }
2632- }
2633- ast:: Expr :: InList { lhs, rhs, .. } => {
2634- changed |= rewrite_expr_table_refs_for_rename ( lhs, old_table_norm, new_table_name) ;
2635- for item in rhs {
2636- changed |= rewrite_expr_table_refs_for_rename ( item, old_table_norm, new_table_name) ;
2637- }
2638- }
2639- ast:: Expr :: InSelect { lhs, rhs, .. } => {
2640- changed |= rewrite_expr_table_refs_for_rename ( lhs, old_table_norm, new_table_name) ;
2641- changed |= rewrite_select_table_refs_for_rename ( rhs, old_table_norm, new_table_name) ;
2642- }
2643- ast:: Expr :: InTable { lhs, rhs, .. } => {
2644- changed |= rewrite_expr_table_refs_for_rename ( lhs, old_table_norm, new_table_name) ;
2645- if normalize_ident ( rhs. name . as_str ( ) ) == old_table_norm {
2646- rhs. name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
2647- changed = true ;
2617+ rewrite_select_table_refs_for_rename ( select, old_table_norm, new_table_name) ;
26482618 }
2619+ _ => { }
26492620 }
2650- ast:: Expr :: IsNull ( inner) | ast:: Expr :: NotNull ( inner) => {
2651- changed |= rewrite_expr_table_refs_for_rename ( inner, old_table_norm, new_table_name) ;
2652- }
2653- ast:: Expr :: Like {
2654- lhs, rhs, escape, ..
2655- } => {
2656- changed |= rewrite_expr_table_refs_for_rename ( lhs, old_table_norm, new_table_name) ;
2657- changed |= rewrite_expr_table_refs_for_rename ( rhs, old_table_norm, new_table_name) ;
2658- if let Some ( ref mut esc) = escape {
2659- changed |= rewrite_expr_table_refs_for_rename ( esc, old_table_norm, new_table_name) ;
2660- }
2661- }
2662- ast:: Expr :: Parenthesized ( inner) => {
2663- for e in inner {
2664- changed |= rewrite_expr_table_refs_for_rename ( e, old_table_norm, new_table_name) ;
2665- }
2666- }
2667- ast:: Expr :: Subquery ( select) | ast:: Expr :: Exists ( select) => {
2668- changed |= rewrite_select_table_refs_for_rename ( select, old_table_norm, new_table_name) ;
2669- }
2670- ast:: Expr :: Unary ( _, inner) => {
2671- changed |= rewrite_expr_table_refs_for_rename ( inner, old_table_norm, new_table_name) ;
2672- }
2673- ast:: Expr :: Id ( _)
2674- | ast:: Expr :: Literal ( _)
2675- | ast:: Expr :: Name ( _)
2676- | ast:: Expr :: Variable ( _)
2677- | ast:: Expr :: Raise ( _, _)
2678- | ast:: Expr :: Register ( _)
2679- | ast:: Expr :: Column { .. }
2680- | ast:: Expr :: RowId { .. }
2681- | ast:: Expr :: SubqueryResult { .. } => { }
2682- }
2621+ Ok ( WalkControl :: Continue )
2622+ } ) ;
26832623
26842624 changed
26852625}
26862626
2627+ /// Rewrite table references in a SELECT statement for table rename.
2628+ ///
2629+ /// Handles the complete SELECT structure used in trigger commands:
2630+ /// - WITH clause (CTEs): Recursively rewrites CTE subqueries
2631+ /// - SELECT body: Main query and UNION/EXCEPT/INTERSECT compounds
2632+ /// - ORDER BY: Expressions in ORDER BY clause that may contain table refs
2633+ /// - LIMIT/OFFSET: Expressions in LIMIT and OFFSET clauses
26872634pub fn rewrite_select_table_refs_for_rename (
26882635 select : & mut ast:: Select ,
26892636 old_table_norm : & str ,
@@ -2723,6 +2670,11 @@ pub fn rewrite_select_table_refs_for_rename(
27232670 changed
27242671}
27252672
2673+ /// Rewrite table references in a SelectBody (main SELECT and compound queries).
2674+ ///
2675+ /// Handles:
2676+ /// - Main SELECT: The primary `OneSelect` (SELECT or VALUES terms)
2677+ /// - Compound queries: Recursively walks chained UNION, EXCEPT, or INTERSECT SELECTs
27262678fn rewrite_select_body_table_refs_for_rename (
27272679 body : & mut ast:: SelectBody ,
27282680 old_table_norm : & str ,
@@ -2744,6 +2696,14 @@ fn rewrite_select_body_table_refs_for_rename(
27442696 changed
27452697}
27462698
2699+ /// Rewrite table references in a OneSelect (single SELECT or VALUES clause).
2700+ ///
2701+ /// Handles:
2702+ /// - Result columns: Including table-qualified stars like `t1.*` → `"t1_new".*`
2703+ /// - FROM clause: Table references and subqueries in the FROM
2704+ /// - WHERE clause: Expressions that may reference the renamed table
2705+ /// - GROUP BY/HAVING: Grouping and filter expressions
2706+ /// - VALUES clause: All value expressions in VALUES(...) rows
27472707fn rewrite_one_select_table_refs_for_rename (
27482708 one_select : & mut ast:: OneSelect ,
27492709 old_table_norm : & str ,
@@ -2769,12 +2729,13 @@ fn rewrite_one_select_table_refs_for_rename(
27692729 ) ;
27702730 }
27712731 ast:: ResultColumn :: TableStar ( ref mut name) => {
2732+ // Handle `t1.*` -> `"t1_new".*`
27722733 if normalize_ident ( name. as_str ( ) ) == old_table_norm {
27732734 * name = ast:: Name :: from_string ( format ! ( "\" {new_table_name}\" " ) ) ;
27742735 changed = true ;
27752736 }
27762737 }
2777- ast:: ResultColumn :: Star => { }
2738+ ast:: ResultColumn :: Star => { } // `*` has no table qualifier
27782739 }
27792740 }
27802741
@@ -2815,6 +2776,12 @@ fn rewrite_one_select_table_refs_for_rename(
28152776 changed
28162777}
28172778
2779+ /// Rewrite table references in a FROM clause including JOINs.
2780+ ///
2781+ /// Handles:
2782+ /// - Main table entry: The primary table/subquery in the FROM
2783+ /// - JOIN tables: All joined tables referenced in the clause
2784+ /// - ON conditions: Expressions in JOIN ON clauses that may contain table refs
28182785fn rewrite_from_clause_table_refs_for_rename (
28192786 from : & mut ast:: FromClause ,
28202787 old_table_norm : & str ,
@@ -2842,6 +2809,13 @@ fn rewrite_from_clause_table_refs_for_rename(
28422809 changed
28432810}
28442811
2812+ /// Rewrite table references in a SelectTable entry.
2813+ ///
2814+ /// Handles different table source types:
2815+ /// - Table: Direct table reference `FROM t1` → `FROM "t1_new"`
2816+ /// - TableCall: Table-valued function `FROM func(args)` - walks arguments
2817+ /// - Select: Subquery `FROM (SELECT ...)` - recursively rewrites
2818+ /// - Sub: Subquery with FROM clause (derived table)
28452819fn rewrite_select_table_entry_refs_for_rename (
28462820 select_table : & mut Box < ast:: SelectTable > ,
28472821 old_table_norm : & str ,
0 commit comments