Skip to content

Commit b28e194

Browse files
committed
add spaces and fix tests
dont allow table refs in when adjust spaces adjust spaces
1 parent 1b42dac commit b28e194

File tree

4 files changed

+97
-17
lines changed

4 files changed

+97
-17
lines changed

core/translate/alter.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,6 +2644,51 @@ pub fn rewrite_expr_table_refs_for_rename(
26442644
changed
26452645
}
26462646

2647+
/// Check if a trigger WHEN clause contains table-qualified column refs that would become
2648+
/// invalid after a table rename. SQLite rejects ALTER TABLE RENAME if the WHEN clause
2649+
/// contains refs like `t1.col` (other than NEW.col or OLD.col) where t1 is the table
2650+
/// being renamed.
2651+
pub fn check_when_clause_for_invalid_table_refs(
2652+
expr: &ast::Expr,
2653+
table_being_renamed: &str,
2654+
) -> Option<(String, String)> {
2655+
let mut invalid_ref: Option<(String, String)> = None;
2656+
2657+
let _ = walk_expr(expr, &mut |e: &ast::Expr| -> Result<WalkControl> {
2658+
// If we already found an invalid ref, skip the rest
2659+
if invalid_ref.is_some() {
2660+
return Ok(WalkControl::SkipChildren);
2661+
}
2662+
match e {
2663+
ast::Expr::Qualified(tbl_name, col_name) => {
2664+
let tbl_norm = normalize_ident(tbl_name.as_str());
2665+
// NEW and OLD are valid pseudo-table references in triggers
2666+
if !tbl_norm.eq_ignore_ascii_case("new")
2667+
&& !tbl_norm.eq_ignore_ascii_case("old")
2668+
&& tbl_norm == table_being_renamed
2669+
{
2670+
invalid_ref = Some((tbl_name.to_string(), col_name.to_string()));
2671+
return Ok(WalkControl::SkipChildren);
2672+
}
2673+
}
2674+
ast::Expr::DoublyQualified(_, tbl_name, col_name) => {
2675+
let tbl_norm = normalize_ident(tbl_name.as_str());
2676+
if !tbl_norm.eq_ignore_ascii_case("new")
2677+
&& !tbl_norm.eq_ignore_ascii_case("old")
2678+
&& tbl_norm == table_being_renamed
2679+
{
2680+
invalid_ref = Some((tbl_name.to_string(), col_name.to_string()));
2681+
return Ok(WalkControl::SkipChildren);
2682+
}
2683+
}
2684+
_ => {}
2685+
}
2686+
Ok(WalkControl::Continue)
2687+
});
2688+
2689+
invalid_ref
2690+
}
2691+
26472692
/// Rewrite table references in a SELECT statement for table rename.
26482693
///
26492694
/// Handles the complete SELECT structure used in trigger commands:

core/vdbe/execute.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use crate::storage::page_cache::PageCache;
1414
use crate::storage::pager::{default_page1, CreateBTreeFlags};
1515
use crate::storage::sqlite3_ondisk::{read_varint_fast, DatabaseHeader, PageSize, RawVersion};
1616
use crate::translate::alter::{
17-
rewrite_expr_table_refs_for_rename, rewrite_select_table_refs_for_rename,
18-
walk_upsert_for_table_rename,
17+
check_when_clause_for_invalid_table_refs, rewrite_expr_table_refs_for_rename,
18+
rewrite_select_table_refs_for_rename, walk_upsert_for_table_rename,
1919
};
2020
use crate::translate::collate::CollationSeq;
2121
use crate::types::{
@@ -5856,6 +5856,17 @@ pub fn op_function(
58565856
}
58575857

58585858
if let Some(ref mut when_expr) = when_clause {
5859+
if let Some((tbl, col)) = check_when_clause_for_invalid_table_refs(
5860+
when_expr,
5861+
&rename_from,
5862+
) {
5863+
return Err(LimboError::Constraint(format!(
5864+
"error in trigger {}: no such column: {}.{}",
5865+
trigger_name.name.as_str(),
5866+
tbl,
5867+
col
5868+
)));
5869+
}
58595870
changed |= rewrite_expr_table_refs_for_rename(
58605871
when_expr,
58615872
&rename_from,

parser/src/ast/fmt.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,9 +398,11 @@ impl ToTokens for Stmt {
398398
}
399399
s.append(TK_BEGIN, Some("\n"))?;
400400
for command in commands {
401+
s.append(TK_ANY, Some(" "))?;
401402
command.to_tokens(s, context)?;
402403
s.append(TK_SEMI, Some("\n"))?;
403404
}
405+
s.append(TK_ANY, Some(" "))?;
404406
s.append(TK_END, None)
405407
}
406408
Self::CreateView {

testing/trigger.test

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -825,43 +825,52 @@ do_execsql_test_on_specific_db {:memory:} trigger-rename-table-on-clause {
825825
ALTER TABLE t1 RENAME TO t1_new;
826826
SELECT sql FROM sqlite_schema WHERE type='trigger' AND name='trg1';
827827
} {{CREATE TRIGGER trg1 AFTER INSERT ON "t1_new" BEGIN
828-
INSERT INTO "t1_new" VALUES (100, 'triggered');
829-
END}}
828+
INSERT INTO "t1_new" VALUES (100, 'triggered');
829+
END}}
830830

831-
# table-qualified refs in WHERE clause get rewritten
832-
do_execsql_test_on_specific_db {:memory:} trigger-rename-table-qualified-refs {
831+
# table-qualified refs in WHEN clause cause error on rename (SQLite behavior)
832+
do_execsql_test_in_memory_any_error trigger-rename-table-qualified-refs-when-error {
833833
CREATE TABLE t1 (id INT, val TEXT);
834834
CREATE TRIGGER trg2 AFTER UPDATE ON t1 WHEN t1.id > 5 BEGIN
835835
UPDATE t1 SET val = 'updated' WHERE t1.id = NEW.id;
836836
END;
837837
ALTER TABLE t1 RENAME TO t1_new;
838+
}
839+
840+
# table-qualified refs in trigger body (not WHEN) get rewritten
841+
do_execsql_test_on_specific_db {:memory:} trigger-rename-table-qualified-refs-body {
842+
CREATE TABLE t1 (id INT, val TEXT);
843+
CREATE TRIGGER trg2 AFTER UPDATE ON t1 BEGIN
844+
UPDATE t1 SET val = 'updated' WHERE t1.id = NEW.id;
845+
END;
846+
ALTER TABLE t1 RENAME TO t1_new;
838847
SELECT sql FROM sqlite_schema WHERE type='trigger' AND name='trg2';
839-
} {{CREATE TRIGGER trg2 AFTER UPDATE ON "t1_new" WHEN "t1_new".id > 5 BEGIN
840-
UPDATE "t1_new" SET val = 'updated' WHERE "t1_new".id = NEW.id;
841-
END}}
848+
} {{CREATE TRIGGER trg2 AFTER UPDATE ON "t1_new" BEGIN
849+
UPDATE "t1_new" SET val = 'updated' WHERE "t1_new".id = NEW.id;
850+
END}}
842851

843852
do_execsql_test_on_specific_db {:memory:} trigger-rename-upsert-clause {
844853
CREATE TABLE t1 (id INT PRIMARY KEY, val TEXT);
845854
CREATE TRIGGER trg3 AFTER INSERT ON t1 BEGIN
846-
INSERT INTO t1 VALUES (100, 'x') ON CONFLICT(id) DO UPDATE SET val = t1.val || 'conflict';
855+
INSERT INTO t1 VALUES (100, 'x') ON CONFLICT (id) DO UPDATE SET val = t1.val || 'conflict';
847856
END;
848857
ALTER TABLE t1 RENAME TO t1_new;
849858
SELECT sql FROM sqlite_schema WHERE type='trigger' AND name='trg3';
850859
} {{CREATE TRIGGER trg3 AFTER INSERT ON "t1_new" BEGIN
851-
INSERT INTO "t1_new" VALUES (100, 'x') ON CONFLICT (id) DO UPDATE SET val = "t1_new".val || 'conflict';
852-
END}}
860+
INSERT INTO "t1_new" VALUES (100, 'x') ON CONFLICT (id) DO UPDATE SET val = "t1_new".val || 'conflict';
861+
END}}
853862

854863
do_execsql_test_on_specific_db {:memory:} trigger-rename-insert-select-body {
855864
CREATE TABLE t1 (id INT, val TEXT);
856865
CREATE TABLE log (src TEXT, cnt INT);
857866
CREATE TRIGGER trg4 AFTER INSERT ON t1 BEGIN
858-
INSERT INTO log SELECT 'inserted', count(*) FROM t1;
867+
INSERT INTO log SELECT 'inserted', count (*) FROM t1;
859868
END;
860869
ALTER TABLE t1 RENAME TO t1_new;
861870
SELECT sql FROM sqlite_schema WHERE type='trigger' AND name='trg4';
862871
} {{CREATE TRIGGER trg4 AFTER INSERT ON "t1_new" BEGIN
863-
INSERT INTO log SELECT 'inserted', count (*) FROM "t1_new";
864-
END}}
872+
INSERT INTO log SELECT 'inserted', count (*) FROM "t1_new";
873+
END}}
865874

866875
do_execsql_test_on_specific_db {:memory:} trigger-rename-select-cmd {
867876
CREATE TABLE t1 (id INT);
@@ -871,8 +880,8 @@ do_execsql_test_on_specific_db {:memory:} trigger-rename-select-cmd {
871880
ALTER TABLE t1 RENAME TO t1_new;
872881
SELECT sql FROM sqlite_schema WHERE type='trigger' AND name='trg5';
873882
} {{CREATE TRIGGER trg5 AFTER INSERT ON "t1_new" BEGIN
874-
SELECT count (*) FROM "t1_new" WHERE "t1_new".id > 0;
875-
END}}
883+
SELECT count (*) FROM "t1_new" WHERE "t1_new".id > 0;
884+
END}}
876885

877886
# SQLite disallows RETURNING clause inside triggers
878887
do_execsql_test_in_memory_any_error trigger-returning-not-allowed {
@@ -916,3 +925,16 @@ do_execsql_test_on_specific_db {:memory:} trigger-fires-after-table-rename {
916925
SELECT count(*) FROM t2;
917926
} {2
918927
4}
928+
929+
# table-qualified refs in WHEN clause subquery get rewritten (SQLite behavior)
930+
do_execsql_test_on_specific_db {:memory:} trigger-rename-table-qualified-refs-when-subquery {
931+
CREATE TABLE t1 (id INT, val TEXT);
932+
CREATE TRIGGER trg2 AFTER UPDATE ON t1 WHEN (SELECT id FROM t1 WHERE t1.id = NEW.id) > 5 BEGIN
933+
SELECT 1;
934+
END;
935+
ALTER TABLE t1 RENAME TO t1_new;
936+
SELECT sql FROM sqlite_schema WHERE type='trigger' AND name='trg2';
937+
} {{CREATE TRIGGER trg2 AFTER UPDATE ON "t1_new" WHEN (SELECT id FROM "t1_new" WHERE "t1_new".id = NEW.id) > 5 BEGIN
938+
SELECT 1;
939+
END}}
940+

0 commit comments

Comments
 (0)