1use crate::alloc::TursoIteratorExt;
2use crate::numeric::StrToF64;
3use crate::schema::ColDef;
4use crate::translate::emitter::TransactionMode;
5use crate::translate::expr::{walk_expr, walk_expr_mut, WalkControl};
6use crate::translate::plan::{BitSet, JoinedTable, TableReferences};
7use crate::translate::planner::{parse_row_id, TableMask};
8use crate::types::IOResult;
9use crate::IO;
10use crate::{
11 schema::{Column, Schema, Table, Type},
12 types::{Value, ValueType},
13 LimboError, OpenFlags, Result, Statement, SymbolTable,
14};
15use either::Either;
16use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
17use std::future::Future;
18use tracing::{instrument, Level};
19use turso_macros::match_ignore_ascii_case;
20use turso_parser::ast::{self, CreateTableBody, Expr, Literal, UnaryOperator};
21use turso_parser::parser::Parser;
22
23#[macro_export]
24macro_rules! io_yield_one {
25 ($c:expr) => {
26 return Ok(IOResult::IO(IOCompletions::Single($c)));
27 };
28}
29
30#[macro_export]
31macro_rules! eq_ignore_ascii_case {
32 ( $var:expr, $value:literal ) => {{
33 match_ignore_ascii_case!(match $var {
34 $value => true,
35 _ => false,
36 })
37 }};
38}
39
40#[macro_export]
41macro_rules! contains_ignore_ascii_case {
42 ( $var:expr, $value:literal ) => {{
43 let compare_to_idx = $var.len().saturating_sub($value.len());
44 if $var.len() < $value.len() {
45 false
46 } else {
47 let mut result = false;
48 for i in 0..=compare_to_idx {
49 if eq_ignore_ascii_case!(&$var[i..i + $value.len()], $value) {
50 result = true;
51 break;
52 }
53 }
54
55 result
56 }
57 }};
58}
59
60#[macro_export]
61macro_rules! starts_with_ignore_ascii_case {
62 ( $var:expr, $value:literal ) => {{
63 if $var.len() < $value.len() {
64 false
65 } else {
66 eq_ignore_ascii_case!(&$var[..$value.len()], $value)
67 }
68 }};
69}
70
71#[macro_export]
72macro_rules! ends_with_ignore_ascii_case {
73 ( $var:expr, $value:literal ) => {{
74 if $var.len() < $value.len() {
75 false
76 } else {
77 eq_ignore_ascii_case!(&$var[$var.len() - $value.len()..], $value)
78 }
79 }};
80}
81
82pub trait IOExt {
83 fn block<T>(&self, f: impl FnMut() -> Result<IOResult<T>>) -> Result<T>;
84 fn wait<T, F>(&self, f: F) -> impl Future<Output = Result<T>> + Send
85 where
86 F: FnMut() -> Result<IOResult<T>> + Send,
87 T: Send;
88}
89
90impl<I: ?Sized + IO> IOExt for I {
91 fn block<T>(&self, mut f: impl FnMut() -> Result<IOResult<T>>) -> Result<T> {
92 Ok(loop {
93 match f()? {
94 IOResult::Done(v) => break v,
95 IOResult::IO(io) => io.wait(self)?,
96 }
97 })
98 }
99
100 async fn wait<T, F>(&self, mut f: F) -> Result<T>
101 where
102 F: FnMut() -> Result<IOResult<T>> + Send,
103 T: Send,
104 {
105 Ok(loop {
106 match f()? {
107 IOResult::Done(v) => break v,
108 IOResult::IO(io) => io.wait_async(self).await?,
109 }
110 })
111 }
112}
113
114const QUOTE_PAIRS: &[(char, char)] = &[
116 ('"', '"'),
117 ('[', ']'),
118 ('`', '`'),
119 ('\'', '\''), ];
121
122pub fn normalize_ident(identifier: &str) -> String {
123 identifier.to_ascii_lowercase()
126}
127
128pub fn escape_sql_string_literal(literal: &str) -> String {
130 literal.replace('\'', "''")
131}
132
133pub fn quote_identifier(name: &str) -> String {
136 let needs_quoting = name.is_empty()
137 || name.as_bytes()[0].is_ascii_digit()
138 || !name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_')
139 || turso_parser::lexer::is_quotable_keyword(name.as_bytes());
140 if needs_quoting {
141 let escaped = name.replace('"', "\"\"");
142 format!("\"{escaped}\"")
143 } else {
144 name.to_string()
145 }
146}
147
148pub const PRIMARY_KEY_AUTOMATIC_INDEX_NAME_PREFIX: &str = "sqlite_autoindex_";
149
150pub struct UnparsedFromSqlIndex {
154 pub table_name: String,
155 pub root_page: i64,
156 pub sql: String,
157}
158
159#[derive(Default)]
165pub struct ParseSchemaRowsState {
166 inner: Option<ParseSchemaRowsInner>,
167}
168
169struct ParseSchemaRowsInner {
170 rows: Statement,
171 from_sql_indexes: crate::alloc::Vec<UnparsedFromSqlIndex>,
172 automatic_indices: HashMap<String, crate::alloc::Vec<(String, i64)>>,
173 dbsp_state_roots: HashMap<String, i64>,
174 dbsp_state_index_roots: HashMap<String, i64>,
175 materialized_view_info: HashMap<String, (String, i64)>,
176}
177
178impl ParseSchemaRowsState {
179 pub fn new(mut rows: Statement, mv_tx: Option<(u64, TransactionMode)>) -> Self {
182 rows.set_mv_tx(mv_tx);
183 Self {
184 inner: Some(ParseSchemaRowsInner {
185 rows,
186 from_sql_indexes: <crate::alloc::Vec<_> as crate::alloc::TursoTryWithCapacityExt>::try_with_capacity_ext(10).expect(crate::alloc::ALLOC_ERR_MSG),
187 automatic_indices: HashMap::with_capacity_and_hasher(10, Default::default()),
188 dbsp_state_roots: HashMap::default(),
189 dbsp_state_index_roots: HashMap::default(),
190 materialized_view_info: HashMap::default(),
191 }),
192 }
193 }
194}
195
196#[instrument(skip_all, level = Level::DEBUG)]
201pub fn parse_schema_rows(
202 state: &mut ParseSchemaRowsState,
203 schema: &mut Schema,
204 syms: &SymbolTable,
205 resolve_attached_db: &dyn Fn(&str) -> Option<usize>,
206) -> Result<IOResult<()>> {
207 {
208 let inner = state
209 .inner
210 .as_mut()
211 .expect("ParseSchemaRowsState not initialized");
212 let ParseSchemaRowsInner {
215 rows,
216 from_sql_indexes,
217 automatic_indices,
218 dbsp_state_roots,
219 dbsp_state_index_roots,
220 materialized_view_info,
221 } = inner;
222 crate::return_if_io!(rows.run_with_row_callback_nonblock(|row| {
223 let ty = row.get::<&str>(0)?;
224 let name = row.get::<&str>(1)?;
225 let table_name = row.get::<&str>(2)?;
226 let root_page = row.get::<i64>(3)?;
227 let sql = row.get::<&str>(4).ok();
228 schema.handle_schema_row(
229 ty,
230 name,
231 table_name,
232 root_page,
233 sql,
234 syms,
235 from_sql_indexes,
236 automatic_indices,
237 dbsp_state_roots,
238 dbsp_state_index_roots,
239 materialized_view_info,
240 resolve_attached_db,
241 )
242 }));
243 }
244
245 let inner = state
247 .inner
248 .take()
249 .expect("ParseSchemaRowsState not initialized");
250 let has_mv_store = inner.rows.mv_store().is_some();
251 schema.populate_indices(
252 syms,
253 inner.from_sql_indexes,
254 inner.automatic_indices,
255 has_mv_store,
256 )?;
257 schema.populate_materialized_views(
258 inner.materialized_view_info,
259 inner.dbsp_state_roots,
260 inner.dbsp_state_index_roots,
261 )?;
262
263 Ok(IOResult::Done(()))
264}
265
266fn cmp_numeric_strings(num_str: &str, other: &str) -> bool {
267 fn parse(s: &str) -> Option<Either<i64, f64>> {
268 if let Ok(i) = s.parse::<i64>() {
269 Some(Either::Left(i))
270 } else if let Ok(f) = s.parse::<f64>() {
271 Some(Either::Right(f))
272 } else {
273 None
274 }
275 }
276
277 match (parse(num_str), parse(other)) {
278 (Some(Either::Left(i1)), Some(Either::Left(i2))) => i1 == i2,
279 (Some(Either::Right(f1)), Some(Either::Right(f2))) => f1 == f2,
280 (Some(Either::Left(_)), Some(Either::Right(_)))
283 | (Some(Either::Right(_)), Some(Either::Left(_))) => false,
284 _ => num_str == other,
285 }
286}
287
288pub fn check_ident_equivalency(ident1: &str, ident2: &str) -> bool {
289 fn strip_quotes(identifier: &str) -> &str {
290 for &(start, end) in QUOTE_PAIRS {
291 if identifier.starts_with(start) && identifier.ends_with(end) {
292 return &identifier[1..identifier.len() - 1];
293 }
294 }
295 identifier
296 }
297 strip_quotes(ident1).eq_ignore_ascii_case(strip_quotes(ident2))
298}
299
300pub fn sql_is_create_virtual_table(sql: &str) -> bool {
308 matches!(
309 Parser::new(sql.as_bytes()).next_cmd(),
310 Ok(Some(ast::Cmd::Stmt(ast::Stmt::CreateVirtualTable(_))))
311 )
312}
313
314pub fn module_name_from_sql(sql: &str) -> Result<&str> {
315 if let Some(start) = sql.find("USING") {
316 let start = start + 6;
317 let end = sql[start..]
319 .find(|c: char| c.is_whitespace() || c == ';' || c == '(')
320 .unwrap_or(sql.len() - start)
321 + start;
322 Ok(sql[start..end].trim())
323 } else {
324 Err(LimboError::InvalidArgument(
325 "Expected 'USING' in module name".to_string(),
326 ))
327 }
328}
329
330pub fn module_args_from_sql(sql: &str) -> Result<Vec<turso_ext::Value>> {
333 if !sql.contains('(') {
334 return Ok(vec![]);
335 }
336 let start = sql.find('(').ok_or_else(|| {
337 LimboError::InvalidArgument("Expected '(' in module argument list".to_string())
338 })? + 1;
339 let end = sql.rfind(')').ok_or_else(|| {
340 LimboError::InvalidArgument("Expected ')' in module argument list".to_string())
341 })?;
342
343 let mut args = Vec::new();
344 let mut current_arg = String::new();
345 let mut chars = sql[start..end].chars().peekable();
346 let mut in_quotes = false;
347
348 while let Some(c) = chars.next() {
349 match c {
350 '\'' => {
351 if in_quotes {
352 if chars.peek() == Some(&'\'') {
353 current_arg.push('\'');
355 chars.next();
356 } else {
357 in_quotes = false;
358 args.push(turso_ext::Value::from_text(current_arg.trim().to_string()));
359 current_arg.clear();
360 while let Some(&nc) = chars.peek() {
362 if nc == ',' {
363 chars.next(); break;
365 } else if nc.is_whitespace() {
366 chars.next();
367 } else {
368 return Err(LimboError::InvalidArgument(
369 "Unexpected characters after quoted argument".to_string(),
370 ));
371 }
372 }
373 }
374 } else {
375 in_quotes = true;
376 }
377 }
378 ',' => {
379 if !in_quotes {
380 if !current_arg.trim().is_empty() {
381 args.push(turso_ext::Value::from_text(current_arg.trim().to_string()));
382 current_arg.clear();
383 }
384 } else {
385 current_arg.push(c);
386 }
387 }
388 _ => {
389 current_arg.push(c);
390 }
391 }
392 }
393
394 if !current_arg.trim().is_empty() && !in_quotes {
395 args.push(turso_ext::Value::from_text(current_arg.trim().to_string()));
396 }
397
398 if in_quotes {
399 return Err(LimboError::InvalidArgument(
400 "Unterminated string literal in module arguments".to_string(),
401 ));
402 }
403
404 Ok(args)
405}
406
407pub fn check_literal_equivalency(lhs: &Literal, rhs: &Literal) -> bool {
408 match (lhs, rhs) {
409 (Literal::Numeric(n1), Literal::Numeric(n2)) => cmp_numeric_strings(n1, n2),
410 (Literal::String(s1), Literal::String(s2)) => s1 == s2,
411 (Literal::Blob(b1), Literal::Blob(b2)) => b1 == b2,
412 (Literal::Keyword(k1), Literal::Keyword(k2)) => check_ident_equivalency(k1, k2),
413 (Literal::Null, Literal::Null) => true,
414 (Literal::True, Literal::True) => true,
415 (Literal::False, Literal::False) => true,
416 (Literal::CurrentDate, Literal::CurrentDate) => true,
417 (Literal::CurrentTime, Literal::CurrentTime) => true,
418 (Literal::CurrentTimestamp, Literal::CurrentTimestamp) => true,
419 _ => false,
420 }
421}
422
423pub(crate) fn expr_tables_subset_of(
426 expr: &Expr,
427 table_references: &TableReferences,
428 allowed: &TableMask,
429) -> bool {
430 let mut ok = true;
431 let _ = walk_expr(expr, &mut |e: &Expr| -> Result<WalkControl> {
432 match e {
433 Expr::Column { table, .. } | Expr::RowId { table, .. } => {
434 if let Some(idx) = table_references
435 .joined_tables()
436 .iter()
437 .position(|t| t.internal_id == *table)
438 {
439 if !allowed.get(idx) {
440 ok = false;
441 return Ok(WalkControl::SkipChildren);
442 }
443 }
444 }
446 _ => {}
447 }
448 Ok(WalkControl::Continue)
449 });
450 ok
451}
452
453pub fn simple_bind_expr(
455 joined_table: &JoinedTable,
456 result_columns: &[ast::ResultColumn],
457 expr: &mut ast::Expr,
458) -> Result<()> {
459 let internal_id = joined_table.internal_id;
460 walk_expr_mut(expr, &mut |expr: &mut ast::Expr| -> Result<WalkControl> {
461 #[allow(clippy::single_match)]
462 match expr {
463 Expr::Id(id) => {
464 for result_column in result_columns.iter() {
465 if let ast::ResultColumn::Expr(result, Some(ast::As::As(alias))) = result_column
466 {
467 if alias.as_str().eq_ignore_ascii_case(id.as_str()) {
468 *expr = *result.clone();
469 return Ok(WalkControl::Continue);
470 }
471 }
472 }
473 let col_idx = joined_table.columns().iter().position(|c| {
474 c.name
475 .as_ref()
476 .is_some_and(|name| name.eq_ignore_ascii_case(id.as_str()))
477 });
478 if let Some(col_idx) = col_idx {
479 let col = joined_table.table.columns().get(col_idx).unwrap();
480 *expr = ast::Expr::Column {
481 database: None,
482 table: internal_id,
483 column: col_idx,
484 is_rowid_alias: col.is_rowid_alias(),
485 };
486 } else {
487 let is_btree_table = matches!(joined_table.table, Table::BTree(_));
489 if is_btree_table {
490 if let Some(rowid) =
491 parse_row_id(&normalize_ident(id.as_str()), internal_id, || false)?
492 {
493 *expr = rowid;
494 }
495 }
496 }
497 }
498 _ => {}
499 }
500 Ok(WalkControl::Continue)
501 })?;
502 Ok(())
503}
504
505pub fn try_substitute_parameters(
506 pattern: &Expr,
507 parameters: &HashMap<i32, Expr>,
508) -> Option<Box<Expr>> {
509 match pattern {
510 Expr::FunctionCall {
511 name,
512 distinctness,
513 args,
514 order_by,
515 within_group,
516 filter_over,
517 } => {
518 let mut substituted = Vec::new();
519 for arg in args {
520 substituted.push(try_substitute_parameters(arg, parameters)?);
521 }
522 Some(Box::new(Expr::FunctionCall {
523 args: substituted,
524 distinctness: *distinctness,
525 name: name.clone(),
526 order_by: order_by.clone(),
527 within_group: within_group.clone(),
528 filter_over: filter_over.clone(),
529 }))
530 }
531 Expr::Variable(var) => {
532 if var.name.is_some() {
533 return None;
534 }
535 let Ok(var) = i32::try_from(var.index.get()) else {
536 return None;
537 };
538 Some(Box::new(parameters.get(&var)?.clone()))
539 }
540 _ => Some(Box::new(pattern.clone())),
541 }
542}
543
544pub fn try_capture_parameters(pattern: &Expr, query: &Expr) -> Option<HashMap<i32, Expr>> {
545 let mut captured = HashMap::default();
546 match (pattern, query) {
547 (
548 Expr::FunctionCall {
549 name: name1,
550 distinctness: distinct1,
551 args: args1,
552 order_by: order1,
553 within_group: within1,
554 filter_over: filter1,
555 },
556 Expr::FunctionCall {
557 name: name2,
558 distinctness: distinct2,
559 args: args2,
560 order_by: order2,
561 within_group: within2,
562 filter_over: filter2,
563 },
564 ) => {
565 if !name1.as_str().eq_ignore_ascii_case(name2.as_str()) {
566 return None;
567 }
568 if distinct1.is_some() || distinct2.is_some() {
569 return None;
570 }
571 if !order1.is_empty() || !order2.is_empty() {
572 return None;
573 }
574 if !within1.is_empty() || !within2.is_empty() {
575 return None;
576 }
577 if filter1.filter_clause.is_some() || filter1.over_clause.is_some() {
578 return None;
579 }
580 if filter2.filter_clause.is_some() || filter2.over_clause.is_some() {
581 return None;
582 }
583 for (arg1, arg2) in args1.iter().zip(args2.iter()) {
584 let result = try_capture_parameters(arg1, arg2)?;
585 captured.extend(result);
586 }
587 Some(captured)
588 }
589 (Expr::Variable(var), expr) => {
590 if var.name.is_some() {
591 return None;
592 }
593 let Ok(var) = i32::try_from(var.index.get()) else {
594 return None;
595 };
596 captured.insert(var, expr.clone());
597 Some(captured)
598 }
599 (
600 Expr::Id(_) | Expr::Name(_) | Expr::Column { .. },
601 Expr::Id(_) | Expr::Name(_) | Expr::Column { .. },
602 ) => {
603 if pattern == query {
604 Some(captured)
605 } else {
606 None
607 }
608 }
609 (_, _) => None,
610 }
611}
612
613pub fn count_fts_column_args(expr: &Expr) -> usize {
623 match expr {
624 Expr::FunctionCall { name, args, .. } => {
625 let name_lower = name.as_str().to_lowercase();
626 match name_lower.as_str() {
627 "fts_match" | "fts_score" => args.len().saturating_sub(1),
628 "fts_highlight" => args.len().saturating_sub(3),
629 _ => 0,
630 }
631 }
632 _ => 0,
633 }
634}
635
636pub fn try_capture_parameters_column_agnostic(
644 pattern: &Expr, query: &Expr, num_column_args: usize, ) -> Option<HashMap<i32, Expr>> {
648 if num_column_args == 0 {
650 return try_capture_parameters(pattern, query);
651 }
652
653 let (
654 Expr::FunctionCall {
655 name: pattern_name,
656 distinctness: pattern_distinct,
657 args: pattern_args,
658 order_by: pattern_order,
659 within_group: pattern_within,
660 filter_over: pattern_filter,
661 },
662 Expr::FunctionCall {
663 name: query_name,
664 distinctness: query_distinct,
665 args: query_args,
666 order_by: query_order,
667 within_group: query_within,
668 filter_over: query_filter,
669 },
670 ) = (pattern, query)
671 else {
672 return try_capture_parameters(pattern, query);
673 };
674 if !pattern_name
676 .as_str()
677 .eq_ignore_ascii_case(query_name.as_str())
678 {
679 return None;
680 }
681
682 if pattern_args.len() != query_args.len() {
684 return None;
685 }
686 if pattern_distinct.is_some() || query_distinct.is_some() {
688 return None;
689 }
690 if !pattern_order.is_empty() || !query_order.is_empty() {
692 return None;
693 }
694 if !pattern_within.is_empty() || !query_within.is_empty() {
696 return None;
697 }
698
699 if pattern_filter.filter_clause.is_some() || pattern_filter.over_clause.is_some() {
701 return None;
702 }
703 if query_filter.filter_clause.is_some() || query_filter.over_clause.is_some() {
704 return None;
705 }
706
707 let mut captured = HashMap::default();
708
709 let pattern_col_args = &pattern_args[..num_column_args];
711 let query_col_args = &query_args[..num_column_args];
712 let pattern_rest = &pattern_args[num_column_args..];
713 let query_rest = &query_args[num_column_args..];
714
715 let mut matched_pattern_indices = BitSet::default();
718
719 for query_col in query_col_args {
720 let mut found_match = false;
721 for (i, pattern_col) in pattern_col_args.iter().enumerate() {
722 if matched_pattern_indices.get(i) {
723 continue;
724 }
725 if exprs_are_equivalent(pattern_col, query_col) {
726 matched_pattern_indices.set(i).expect("TODO: alloc error");
727 found_match = true;
728 break;
729 }
730 }
731 if !found_match {
732 return None;
733 }
734 }
735 if matched_pattern_indices.count() != pattern_col_args.len() {
737 return None;
738 }
739 for (pattern_arg, query_arg) in pattern_rest.iter().zip(query_rest.iter()) {
741 let result = try_capture_parameters(pattern_arg, query_arg)?;
742 captured.extend(result);
743 }
744
745 Some(captured)
746}
747
748pub fn exprs_are_equivalent(expr1: &Expr, expr2: &Expr) -> bool {
752 match (expr1, expr2) {
753 (
754 Expr::Between {
755 lhs: lhs1,
756 not: not1,
757 start: start1,
758 end: end1,
759 },
760 Expr::Between {
761 lhs: lhs2,
762 not: not2,
763 start: start2,
764 end: end2,
765 },
766 ) => {
767 not1 == not2
768 && exprs_are_equivalent(lhs1, lhs2)
769 && exprs_are_equivalent(start1, start2)
770 && exprs_are_equivalent(end1, end2)
771 }
772 (Expr::Binary(lhs1, op1, rhs1), Expr::Binary(lhs2, op2, rhs2)) => {
773 op1 == op2
774 && ((exprs_are_equivalent(lhs1, lhs2) && exprs_are_equivalent(rhs1, rhs2))
775 || (op1.is_commutative()
776 && exprs_are_equivalent(lhs1, rhs2)
777 && exprs_are_equivalent(rhs1, lhs2)))
778 }
779 (
780 Expr::Case {
781 base: base1,
782 when_then_pairs: pairs1,
783 else_expr: else1,
784 },
785 Expr::Case {
786 base: base2,
787 when_then_pairs: pairs2,
788 else_expr: else2,
789 },
790 ) => {
791 base1 == base2
792 && pairs1.len() == pairs2.len()
793 && pairs1.iter().zip(pairs2).all(|((w1, t1), (w2, t2))| {
794 exprs_are_equivalent(w1, w2) && exprs_are_equivalent(t1, t2)
795 })
796 && else1 == else2
797 }
798 (
799 Expr::Cast {
800 expr: expr1,
801 type_name: type1,
802 },
803 Expr::Cast {
804 expr: expr2,
805 type_name: type2,
806 },
807 ) => {
808 exprs_are_equivalent(expr1, expr2)
809 && match (type1, type2) {
810 (Some(t1), Some(t2)) => t1.name.eq_ignore_ascii_case(&t2.name),
811 _ => false,
812 }
813 }
814 (Expr::Collate(expr1, collation1), Expr::Collate(expr2, collation2)) => {
815 exprs_are_equivalent(expr1, expr2)
817 && collation1
818 .as_str()
819 .eq_ignore_ascii_case(collation2.as_str())
820 }
821 (
822 Expr::FunctionCall {
823 name: name1,
824 distinctness: distinct1,
825 args: args1,
826 order_by: order1,
827 within_group: within1,
828 filter_over: filter1,
829 },
830 Expr::FunctionCall {
831 name: name2,
832 distinctness: distinct2,
833 args: args2,
834 order_by: order2,
835 within_group: within2,
836 filter_over: filter2,
837 },
838 ) => {
839 name1.as_str().eq_ignore_ascii_case(name2.as_str())
840 && distinct1 == distinct2
841 && args1 == args2
842 && order1 == order2
843 && within1 == within2
844 && filter1 == filter2
845 }
846 (
847 Expr::FunctionCallStar {
848 name: name1,
849 filter_over: filter1,
850 },
851 Expr::FunctionCallStar {
852 name: name2,
853 filter_over: filter2,
854 },
855 ) => {
856 name1.as_str().eq_ignore_ascii_case(name2.as_str())
857 && match (&filter1.filter_clause, &filter2.filter_clause) {
858 (Some(expr1), Some(expr2)) => exprs_are_equivalent(expr1, expr2),
859 (None, None) => true,
860 _ => false,
861 }
862 && filter1.over_clause == filter2.over_clause
863 }
864 (Expr::NotNull(expr1), Expr::NotNull(expr2)) => exprs_are_equivalent(expr1, expr2),
865 (Expr::IsNull(expr1), Expr::IsNull(expr2)) => exprs_are_equivalent(expr1, expr2),
866 (Expr::Literal(lit1), Expr::Literal(lit2)) => check_literal_equivalency(lit1, lit2),
867 (Expr::Id(id1), Expr::Id(id2)) => check_ident_equivalency(id1.as_str(), id2.as_str()),
868 (Expr::Unary(op1, expr1), Expr::Unary(op2, expr2)) => {
869 op1 == op2 && exprs_are_equivalent(expr1, expr2)
870 }
871 (Expr::Variable(val), Expr::Variable(val2)) => val == val2,
872 (Expr::Parenthesized(exprs1), Expr::Parenthesized(exprs2)) => {
873 exprs1.len() == exprs2.len()
874 && exprs1
875 .iter()
876 .zip(exprs2)
877 .all(|(e1, e2)| exprs_are_equivalent(e1, e2))
878 }
879 (Expr::Parenthesized(exprs1), exprs2) | (exprs2, Expr::Parenthesized(exprs1)) => {
880 exprs1.len() == 1 && exprs_are_equivalent(&exprs1[0], exprs2)
881 }
882 (Expr::Qualified(tn1, cn1), Expr::Qualified(tn2, cn2)) => {
883 check_ident_equivalency(tn1.as_str(), tn2.as_str())
884 && check_ident_equivalency(cn1.as_str(), cn2.as_str())
885 }
886 (Expr::DoublyQualified(sn1, tn1, cn1), Expr::DoublyQualified(sn2, tn2, cn2)) => {
887 check_ident_equivalency(sn1.as_str(), sn2.as_str())
888 && check_ident_equivalency(tn1.as_str(), tn2.as_str())
889 && check_ident_equivalency(cn1.as_str(), cn2.as_str())
890 }
891 (
892 Expr::InList {
893 lhs: lhs1,
894 not: not1,
895 rhs: rhs1,
896 },
897 Expr::InList {
898 lhs: lhs2,
899 not: not2,
900 rhs: rhs2,
901 },
902 ) => {
903 *not1 == *not2
904 && exprs_are_equivalent(lhs1, lhs2)
905 && rhs1.len() == rhs2.len()
906 && rhs1
907 .iter()
908 .zip(rhs2.iter())
909 .all(|(a, b)| exprs_are_equivalent(a, b))
910 }
911 (
912 Expr::Column {
913 database: db1,
914 is_rowid_alias: r1,
915 table: tbl_1,
916 column: col_1,
917 },
918 Expr::Column {
919 database: db2,
920 is_rowid_alias: r2,
921 table: tbl_2,
922 column: col_2,
923 },
924 ) => tbl_1 == tbl_2 && col_1 == col_2 && db1 == db2 && r1 == r2,
925 _ => expr1 == expr2,
927 }
928}
929
930pub(crate) fn expr_contains_null(expr: &ast::Expr) -> bool {
935 let mut contains_null = false;
936 let _ = walk_expr(expr, &mut |expr: &ast::Expr| -> Result<WalkControl> {
937 if let ast::Expr::Literal(ast::Literal::Null) = expr {
938 contains_null = true;
939 return Ok(WalkControl::SkipChildren);
940 }
941 Ok(WalkControl::Continue)
942 }); contains_null
944}
945
946pub(crate) fn type_from_name(type_name: &str) -> (Type, bool) {
949 let type_name = type_name.as_bytes();
950 if type_name.is_empty() {
951 return (Type::Blob, false);
952 }
953
954 if eq_ignore_ascii_case!(type_name, b"INTEGER") {
955 return (Type::Integer, true);
956 }
957
958 if contains_ignore_ascii_case!(type_name, b"INT") {
959 return (Type::Integer, false);
960 }
961
962 if let Some(ty) = type_name.windows(4).find_map(|s| {
963 match_ignore_ascii_case!(match s {
964 b"CHAR" | b"CLOB" | b"TEXT" => Some(Type::Text),
965 b"BLOB" => Some(Type::Blob),
966 b"REAL" | b"FLOA" | b"DOUB" => Some(Type::Real),
967 _ => None,
968 })
969 }) {
970 return (ty, false);
971 }
972
973 (Type::Numeric, false)
974}
975
976pub fn columns_from_create_table_body(
977 body: &turso_parser::ast::CreateTableBody,
978) -> crate::Result<Vec<Column>> {
979 let CreateTableBody::ColumnsAndConstraints { columns, .. } = body else {
980 return Err(crate::LimboError::ParseError(
981 "CREATE TABLE body must contain columns and constraints".to_string(),
982 ));
983 };
984
985 columns
986 .iter()
987 .map(Column::try_from)
988 .collect::<crate::Result<Vec<Column>>>()
989}
990
991#[derive(Debug, Default, PartialEq)]
992pub struct OpenOptions<'a> {
993 pub authority: Option<&'a str>,
995 pub path: String,
997 pub vfs: Option<String>,
999 pub mode: OpenMode,
1001 pub modeof: Option<String>,
1003 pub cache: CacheMode,
1005 pub immutable: bool,
1007 pub cipher: Option<String>,
1009 pub hexkey: Option<String>,
1011}
1012
1013pub const MEMORY_PATH: &str = ":memory:";
1014
1015#[derive(Clone, Default, Debug, Copy, PartialEq)]
1016pub enum OpenMode {
1017 ReadOnly,
1018 ReadWrite,
1019 Memory,
1020 #[default]
1021 ReadWriteCreate,
1022}
1023
1024#[derive(Debug, Default, Clone, Copy, PartialEq)]
1025pub enum CacheMode {
1026 #[default]
1027 Private,
1028 Shared,
1029}
1030
1031impl From<&str> for CacheMode {
1032 fn from(s: &str) -> Self {
1033 match s {
1034 "private" => CacheMode::Private,
1035 "shared" => CacheMode::Shared,
1036 _ => CacheMode::Private,
1037 }
1038 }
1039}
1040
1041impl OpenMode {
1042 pub fn from_str(s: &str) -> Result<Self> {
1043 let s_bytes = s.trim().as_bytes();
1044 match_ignore_ascii_case!(match s_bytes {
1045 b"ro" => Ok(OpenMode::ReadOnly),
1046 b"rw" => Ok(OpenMode::ReadWrite),
1047 b"memory" => Ok(OpenMode::Memory),
1048 b"rwc" => Ok(OpenMode::ReadWriteCreate),
1049 _ => Err(LimboError::InvalidArgument(format!(
1050 "Invalid mode: '{s}'. Expected one of 'ro', 'rw', 'memory', 'rwc'"
1051 ))),
1052 })
1053 }
1054}
1055
1056fn is_windows_path(path: &str) -> bool {
1057 path.len() >= 3
1058 && path.chars().nth(1) == Some(':')
1059 && (path.chars().nth(2) == Some('/') || path.chars().nth(2) == Some('\\'))
1060}
1061
1062fn normalize_windows_path(path: &str) -> String {
1064 let mut normalized = path.replace("\\", "/");
1065
1066 while normalized.contains("//") {
1068 normalized = normalized.replace("//", "/");
1069 }
1070
1071 if normalized.len() >= 3
1073 && !normalized.starts_with('/')
1074 && normalized.chars().nth(1) == Some(':')
1075 && normalized.chars().nth(2) == Some('/')
1076 {
1077 normalized.insert(0, '/');
1078 }
1079 normalized
1080}
1081
1082impl<'a> OpenOptions<'a> {
1083 pub fn parse(uri: &'a str) -> Result<OpenOptions<'a>> {
1085 if !uri.starts_with("file:") {
1086 return Ok(OpenOptions {
1087 path: uri.to_string(),
1088 ..Default::default()
1089 });
1090 }
1091
1092 let mut opts = OpenOptions::default();
1093 let without_scheme = &uri[5..];
1094
1095 let (without_fragment, _) = without_scheme
1096 .split_once('#')
1097 .unwrap_or((without_scheme, ""));
1098
1099 let (without_query, query) = without_fragment
1100 .split_once('?')
1101 .unwrap_or((without_fragment, ""));
1102 parse_query_params(query, &mut opts)?;
1103
1104 if let Some(after_slashes) = without_query.strip_prefix("//") {
1106 let (authority, path) = after_slashes.split_once('/').unwrap_or((after_slashes, ""));
1107
1108 if !(authority.is_empty() || authority == "localhost") {
1110 return Err(LimboError::InvalidArgument(format!(
1111 "Invalid authority '{authority}'. Only '' or 'localhost' allowed."
1112 )));
1113 }
1114 opts.authority = if authority.is_empty() {
1115 None
1116 } else {
1117 Some(authority)
1118 };
1119
1120 if is_windows_path(path) {
1121 opts.path = normalize_windows_path(&decode_percent(path));
1122 } else if !path.is_empty() {
1123 opts.path = format!("/{}", decode_percent(path));
1124 } else {
1125 opts.path = String::new();
1126 }
1127 } else {
1128 opts.path = decode_percent(without_query);
1130 }
1131
1132 Ok(opts)
1133 }
1134
1135 pub fn get_flags(&self) -> Result<OpenFlags> {
1136 if self.mode != OpenMode::ReadWriteCreate && self.modeof.is_some() {
1138 return Err(LimboError::InvalidArgument(
1139 "modeof is not applicable without mode=rwc".to_string(),
1140 ));
1141 }
1142 Ok(match self.mode {
1144 OpenMode::ReadWriteCreate => OpenFlags::Create,
1145 OpenMode::ReadOnly => OpenFlags::ReadOnly,
1146 _ => OpenFlags::default(),
1147 })
1148 }
1149}
1150
1151fn parse_query_params(query: &str, opts: &mut OpenOptions) -> Result<()> {
1153 for param in query.split('&') {
1154 if let Some((key, value)) = param.split_once('=') {
1155 let decoded_value = decode_percent(value);
1156 match key {
1157 "mode" => opts.mode = OpenMode::from_str(value)?,
1158 "modeof" => opts.modeof = Some(decoded_value),
1159 "cache" => opts.cache = decoded_value.as_str().into(),
1160 "immutable" => opts.immutable = decoded_value == "1",
1161 "vfs" => opts.vfs = Some(decoded_value),
1162 "cipher" => opts.cipher = Some(decoded_value),
1163 "hexkey" => opts.hexkey = Some(decoded_value),
1164 _ => {}
1165 }
1166 }
1167 }
1168 Ok(())
1169}
1170
1171pub fn decode_percent(uri: &str) -> String {
1174 let from_hex_digit = |digit: u8| -> Option<u8> {
1175 match digit {
1176 b'0'..=b'9' => Some(digit - b'0'),
1177 b'A'..=b'F' => Some(digit - b'A' + 10),
1178 b'a'..=b'f' => Some(digit - b'a' + 10),
1179 _ => None,
1180 }
1181 };
1182
1183 let offset = uri.chars().take_while(|&c| c != '%').count();
1184
1185 if offset >= uri.len() {
1186 return uri.to_string();
1187 }
1188
1189 let mut decoded: Vec<u8> = Vec::with_capacity(uri.len());
1190 let (ascii, mut data) = uri.as_bytes().split_at(offset);
1191 decoded.extend_from_slice(ascii);
1192
1193 loop {
1194 let mut parts = data.splitn(2, |&c| c == b'%');
1195 let non_escaped_part = parts.next().unwrap();
1196 let rest = parts.next();
1197 if rest.is_none() && decoded.is_empty() {
1198 return String::from_utf8_lossy(data).to_string();
1199 }
1200 decoded.extend_from_slice(non_escaped_part);
1201 match rest {
1202 Some(rest) => match rest.get(0..2) {
1203 Some([first, second]) => match from_hex_digit(*first) {
1204 Some(first_val) => match from_hex_digit(*second) {
1205 Some(second_val) => {
1206 decoded.push((first_val << 4) | second_val);
1207 data = &rest[2..];
1208 }
1209 None => {
1210 decoded.extend_from_slice(&[b'%', *first]);
1211 data = &rest[1..];
1212 }
1213 },
1214 None => {
1215 decoded.push(b'%');
1216 data = rest;
1217 }
1218 },
1219 _ => {
1220 decoded.push(b'%');
1221 decoded.extend_from_slice(rest);
1222 break;
1223 }
1224 },
1225 None => break,
1226 }
1227 }
1228 String::from_utf8_lossy(&decoded).to_string()
1229}
1230
1231pub fn trim_ascii_whitespace(s: &str) -> &str {
1232 let bytes = s.as_bytes();
1233 let start = bytes
1234 .iter()
1235 .position(|&b| !b.is_ascii_whitespace())
1236 .unwrap_or(bytes.len());
1237 let end = bytes
1238 .iter()
1239 .rposition(|&b| !b.is_ascii_whitespace())
1240 .map(|i| i + 1)
1241 .unwrap_or(0);
1242 if start <= end {
1243 &s[start..end]
1244 } else {
1245 ""
1246 }
1247}
1248
1249pub fn checked_cast_text_to_numeric(text: &str, lossless: bool) -> std::result::Result<Value, ()> {
1262 let original_len = text.trim().len();
1266 let (kind, text) = parse_numeric_str(text)?;
1267
1268 if original_len != text.len() && lossless {
1269 return Err(());
1270 }
1271
1272 match kind {
1273 ValueType::Integer => match text.parse::<i64>() {
1274 Ok(i) => Ok(Value::from_i64(i)),
1275 Err(e) => {
1276 if matches!(
1277 e.kind(),
1278 std::num::IntErrorKind::PosOverflow | std::num::IntErrorKind::NegOverflow
1279 ) {
1280 let value = text.parse::<f64>().unwrap_or_default();
1283 let factor = 10f64.powi(15 - value.abs().log10().ceil() as i32);
1284 Ok(Value::from_f64((value * factor).round() / factor))
1285 } else {
1286 Err(())
1287 }
1288 }
1289 },
1290 ValueType::Float => {
1291 let value = text.parse::<f64>().unwrap_or(0.0);
1292 Ok(real_to_numeric_value(value))
1293 }
1294 _ => unreachable!(),
1295 }
1296}
1297
1298fn real_to_numeric_value(value: f64) -> Value {
1305 const INT51_MAX: i64 = 1 << 51;
1306 if value == 0.0 {
1307 return Value::from_i64(0);
1308 }
1309 if value.is_finite() {
1310 let i = value as i64;
1311 if (i as f64) == value && (-INT51_MAX..INT51_MAX).contains(&i) {
1312 return Value::from_i64(i);
1313 }
1314 }
1315 Value::from_f64(value)
1316}
1317
1318fn parse_numeric_str(text: &str) -> Result<(ValueType, &str), ()> {
1319 let text = text.trim();
1320 let bytes = text.as_bytes();
1321
1322 if matches!(
1323 bytes,
1324 [] | [b'e', ..] | [b'E', ..] | [b'.', b'e' | b'E', ..]
1325 ) {
1326 return Err(());
1327 }
1328
1329 let mut end = 0;
1330 let mut has_decimal = false;
1331 let mut has_exponent = false;
1332 if bytes[0] == b'-' || bytes[0] == b'+' {
1333 end = 1;
1334 }
1335 while end < bytes.len() {
1336 match bytes[end] {
1337 b'0'..=b'9' => end += 1,
1338 b'.' if !has_decimal && !has_exponent => {
1339 has_decimal = true;
1340 end += 1;
1341 }
1342 b'e' | b'E' if !has_exponent => {
1343 has_exponent = true;
1344 end += 1;
1345 if end < bytes.len() && (bytes[end] == b'+' || bytes[end] == b'-') {
1347 end += 1;
1348 }
1349 }
1350 _ => break,
1351 }
1352 }
1353 if end == 0 || (end == 1 && (bytes[0] == b'-' || bytes[0] == b'+')) {
1354 return Err(());
1355 }
1356 let last = bytes[end - 1];
1358 if last.eq_ignore_ascii_case(&b'e') {
1359 return Ok((ValueType::Float, &text[0..end - 1]));
1360 } else if has_exponent && (last == b'-' || last == b'+') {
1362 return Ok((ValueType::Float, &text[0..end - 2]));
1363 }
1364 Ok((
1365 if !has_decimal && !has_exponent {
1366 ValueType::Integer
1367 } else {
1368 ValueType::Float
1369 },
1370 &text[..end],
1371 ))
1372}
1373
1374pub fn cast_real_to_integer(float: f64) -> std::result::Result<i64, ()> {
1381 if !float.is_finite() || float.trunc() != float {
1383 return Err(());
1384 }
1385
1386 let int_val = float as i64;
1389
1390 if int_val == i64::MIN || int_val == i64::MAX {
1393 return Err(());
1394 }
1395
1396 if (int_val as f64) != float {
1399 return Err(());
1400 }
1401
1402 Ok(int_val)
1403}
1404
1405pub fn parse_numeric_literal(text: &str) -> Result<Value> {
1407 let text = if text.contains('_') {
1409 std::borrow::Cow::Owned(text.replace('_', ""))
1410 } else {
1411 std::borrow::Cow::Borrowed(text)
1412 };
1413
1414 if text.starts_with("0x") || text.starts_with("0X") {
1415 let value = u64::from_str_radix(&text[2..], 16)? as i64;
1416 return Ok(Value::from_i64(value));
1417 } else if text.starts_with("-0x") || text.starts_with("-0X") {
1418 let value = u64::from_str_radix(&text[3..], 16)? as i64;
1419 if value == i64::MIN {
1420 return Err(LimboError::IntegerOverflow);
1421 }
1422 return Ok(Value::from_i64(-value));
1423 }
1424
1425 if let Ok(int_value) = text.parse::<i64>() {
1426 return Ok(Value::from_i64(int_value));
1427 }
1428
1429 let Some(StrToF64::Fractional(float) | StrToF64::Decimal(float)) =
1430 crate::numeric::str_to_f64(text)
1431 else {
1432 unreachable!();
1433 };
1434 Ok(Value::Numeric(crate::numeric::Numeric::Float(float)))
1435}
1436
1437pub fn parse_signed_number(expr: &Expr) -> Result<Value> {
1438 match expr {
1439 Expr::Literal(Literal::Numeric(num)) => parse_numeric_literal(num),
1440 Expr::Unary(op, expr) => match (op, expr.as_ref()) {
1441 (UnaryOperator::Negative, Expr::Literal(Literal::Numeric(num))) => {
1442 let data = "-".to_owned() + &num.to_string();
1443 parse_numeric_literal(&data)
1444 }
1445 (UnaryOperator::Positive, Expr::Literal(Literal::Numeric(num))) => {
1446 parse_numeric_literal(num)
1447 }
1448 _ => Err(LimboError::InvalidArgument(
1449 "signed-number must follow the format: ([+|-] numeric-literal)".to_string(),
1450 )),
1451 },
1452 _ => Err(LimboError::InvalidArgument(
1453 "signed-number must follow the format: ([+|-] numeric-literal)".to_string(),
1454 )),
1455 }
1456}
1457
1458pub fn parse_string(expr: &Expr) -> Result<String> {
1459 match expr {
1460 Expr::Name(name) if name.quoted_with('\'') => Ok(name.as_str().to_string()),
1461 _ => Err(LimboError::InvalidArgument(format!(
1462 "string parameter expected, got {expr:?} instead"
1463 ))),
1464 }
1465}
1466
1467#[allow(unused)]
1468pub fn parse_pragma_bool(expr: &Expr) -> Result<bool> {
1469 const TRUE_VALUES: &[&str] = &["yes", "true", "on"];
1470 const FALSE_VALUES: &[&str] = &["no", "false", "off"];
1471 if let Ok(number) = parse_signed_number(expr) {
1472 if let Value::Numeric(crate::numeric::Numeric::Integer(x @ (0 | 1))) = number {
1473 return Ok(x != 0);
1474 }
1475 } else if let Expr::Name(name) = expr {
1476 let ident = normalize_ident(name.as_str());
1477 if TRUE_VALUES.contains(&ident.as_str()) {
1478 return Ok(true);
1479 }
1480 if FALSE_VALUES.contains(&ident.as_str()) {
1481 return Ok(false);
1482 }
1483 }
1484 Err(LimboError::InvalidArgument(
1485 "boolean pragma value must be either 0|1 integer or yes|true|on|no|false|off token"
1486 .to_string(),
1487 ))
1488}
1489
1490pub fn extract_column_name_from_expr(expr: impl AsRef<ast::Expr>) -> Option<String> {
1492 match expr.as_ref() {
1493 ast::Expr::Id(name) => Some(name.as_str().to_string()),
1494 ast::Expr::DoublyQualified(_, _, name) | ast::Expr::Qualified(_, name) => {
1495 Some(normalize_ident(name.as_str()))
1496 }
1497 _ => None,
1498 }
1499}
1500
1501#[derive(Debug, Clone)]
1503pub struct ViewTable {
1504 pub name: String,
1506 pub db_name: Option<String>,
1508 pub alias: Option<String>,
1510}
1511
1512#[derive(Debug, Clone)]
1514pub struct ViewColumn {
1515 pub table_index: usize,
1518 pub column: Column,
1520}
1521
1522#[derive(Debug, Clone)]
1524pub struct ViewColumnSchema {
1525 pub tables: Vec<ViewTable>,
1527 pub columns: Vec<ViewColumn>,
1529}
1530
1531impl ViewColumnSchema {
1532 pub fn flat_columns(&self) -> crate::alloc::Vec<Column> {
1534 self.columns
1535 .iter()
1536 .map(|vc| vc.column.clone())
1537 .try_collect()
1538 .expect(crate::alloc::ALLOC_ERR_MSG)
1539 }
1540
1541 pub fn table_columns(&self, table_index: usize) -> Vec<Column> {
1543 self.columns
1544 .iter()
1545 .filter(|vc| vc.table_index == table_index)
1546 .map(|vc| vc.column.clone())
1547 .collect()
1548 }
1549}
1550
1551pub fn walk_select_expressions<F>(select: &ast::Select, func: &mut F) -> Result<()>
1553where
1554 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1555{
1556 walk_select_expressions_inner(select, func)
1557}
1558
1559fn walk_select_expressions_inner<F>(select: &ast::Select, func: &mut F) -> Result<()>
1560where
1561 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1562{
1563 if let Some(with_clause) = &select.with {
1564 for cte in &with_clause.ctes {
1565 walk_select_expressions_inner(&cte.select, func)?;
1566 }
1567 }
1568
1569 walk_one_select_expressions(&select.body.select, func)?;
1570 for compound in &select.body.compounds {
1571 walk_one_select_expressions(&compound.select, func)?;
1572 }
1573
1574 for sorted_col in &select.order_by {
1575 walk_expr_with_subqueries(&sorted_col.expr, func)?;
1576 }
1577
1578 if let Some(limit) = &select.limit {
1579 walk_expr_with_subqueries(&limit.expr, func)?;
1580 if let Some(offset) = &limit.offset {
1581 walk_expr_with_subqueries(offset, func)?;
1582 }
1583 }
1584
1585 Ok(())
1586}
1587
1588fn walk_one_select_expressions<F>(one_select: &ast::OneSelect, func: &mut F) -> Result<()>
1589where
1590 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1591{
1592 match one_select {
1593 ast::OneSelect::Select {
1594 columns,
1595 from,
1596 where_clause,
1597 group_by,
1598 window_clause,
1599 ..
1600 } => {
1601 for col in columns {
1602 if let ast::ResultColumn::Expr(expr, _) = col {
1603 walk_expr_with_subqueries(expr, func)?;
1604 }
1605 }
1606
1607 if let Some(from_clause) = from {
1608 walk_from_clause_expressions(from_clause, func)?;
1609 }
1610
1611 if let Some(where_expr) = where_clause {
1612 walk_expr_with_subqueries(where_expr, func)?;
1613 }
1614
1615 if let Some(group_by) = group_by {
1616 for expr in &group_by.exprs {
1617 walk_expr_with_subqueries(expr, func)?;
1618 }
1619 if let Some(having_expr) = &group_by.having {
1620 walk_expr_with_subqueries(having_expr, func)?;
1621 }
1622 }
1623
1624 for window_def in window_clause {
1625 walk_window_expressions(&window_def.window, func)?;
1626 }
1627 }
1628 ast::OneSelect::Values(values) => {
1629 for row in values {
1630 for expr in row {
1631 walk_expr_with_subqueries(expr, func)?;
1632 }
1633 }
1634 }
1635 }
1636
1637 Ok(())
1638}
1639
1640fn walk_from_clause_expressions<F>(from_clause: &ast::FromClause, func: &mut F) -> Result<()>
1641where
1642 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1643{
1644 walk_select_table_expressions(&from_clause.select, func)?;
1645
1646 for join in &from_clause.joins {
1647 walk_select_table_expressions(&join.table, func)?;
1648
1649 if let Some(ast::JoinConstraint::On(expr)) = &join.constraint {
1650 walk_expr_with_subqueries(expr, func)?;
1651 }
1652 }
1653
1654 Ok(())
1655}
1656
1657fn walk_select_table_expressions<F>(select_table: &ast::SelectTable, func: &mut F) -> Result<()>
1658where
1659 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1660{
1661 match select_table {
1662 ast::SelectTable::Select(select, _) => walk_select_expressions_inner(select, func),
1663 ast::SelectTable::Sub(from_clause, _) => walk_from_clause_expressions(from_clause, func),
1664 ast::SelectTable::TableCall(_, args, _) => {
1665 for arg in args {
1666 walk_expr_with_subqueries(arg, func)?;
1667 }
1668 Ok(())
1669 }
1670 ast::SelectTable::Table(_, _, _) => Ok(()),
1671 }
1672}
1673
1674fn walk_window_expressions<F>(window: &ast::Window, func: &mut F) -> Result<()>
1675where
1676 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1677{
1678 for expr in &window.partition_by {
1679 walk_expr_with_subqueries(expr, func)?;
1680 }
1681
1682 for sorted_col in &window.order_by {
1683 walk_expr_with_subqueries(&sorted_col.expr, func)?;
1684 }
1685
1686 if let Some(frame_clause) = &window.frame_clause {
1687 walk_frame_bound_expressions(&frame_clause.start, func)?;
1688 if let Some(end_bound) = &frame_clause.end {
1689 walk_frame_bound_expressions(end_bound, func)?;
1690 }
1691 }
1692
1693 Ok(())
1694}
1695
1696fn walk_frame_bound_expressions<F>(bound: &ast::FrameBound, func: &mut F) -> Result<()>
1697where
1698 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1699{
1700 match bound {
1701 ast::FrameBound::Following(expr) | ast::FrameBound::Preceding(expr) => {
1702 walk_expr_with_subqueries(expr, func)
1703 }
1704 ast::FrameBound::CurrentRow
1705 | ast::FrameBound::UnboundedFollowing
1706 | ast::FrameBound::UnboundedPreceding => Ok(()),
1707 }
1708}
1709
1710pub fn walk_expr_with_subqueries<F>(expr: &ast::Expr, func: &mut F) -> Result<()>
1711where
1712 F: FnMut(&ast::Expr) -> Result<WalkControl>,
1713{
1714 walk_expr(expr, &mut |e| {
1715 let control = func(e)?;
1716 if matches!(control, WalkControl::Continue) {
1717 match e {
1718 ast::Expr::Subquery(select) | ast::Expr::Exists(select) => {
1719 walk_select_expressions_inner(select, func)?;
1720 }
1721 ast::Expr::InSelect { rhs, .. } => {
1722 walk_select_expressions_inner(rhs, func)?;
1723 }
1724 _ => {}
1725 }
1726 }
1727 Ok(control)
1728 })?;
1729 Ok(())
1730}
1731
1732fn validate_no_cross_db_references(
1733 select_stmt: &ast::Select,
1734 view_db_name: Option<&ast::Name>,
1735) -> Result<()> {
1736 if let Some(with_clause) = &select_stmt.with {
1737 for cte in &with_clause.ctes {
1738 validate_no_cross_db_references(&cte.select, view_db_name)?;
1739 }
1740 }
1741
1742 validate_one_select_no_cross_db(&select_stmt.body.select, view_db_name)?;
1743
1744 for compound in &select_stmt.body.compounds {
1745 validate_one_select_no_cross_db(&compound.select, view_db_name)?;
1746 }
1747
1748 Ok(())
1749}
1750
1751fn validate_one_select_no_cross_db(
1752 one_select: &ast::OneSelect,
1753 view_db_name: Option<&ast::Name>,
1754) -> Result<()> {
1755 match one_select {
1756 ast::OneSelect::Select { from, .. } => {
1757 if let Some(from_clause) = from {
1758 validate_from_clause_no_cross_db(from_clause, view_db_name)?;
1759 }
1760 }
1761 ast::OneSelect::Values(_) => {}
1762 }
1763 Ok(())
1764}
1765
1766fn validate_from_clause_no_cross_db(
1767 from_clause: &ast::FromClause,
1768 view_db_name: Option<&ast::Name>,
1769) -> Result<()> {
1770 validate_select_table_no_cross_db(&from_clause.select, view_db_name)?;
1771 for join in &from_clause.joins {
1772 validate_select_table_no_cross_db(&join.table, view_db_name)?;
1773 }
1774 Ok(())
1775}
1776
1777fn reject_cross_db_qualified_name(
1778 qualified_name: &ast::QualifiedName,
1779 view_db_name: Option<&ast::Name>,
1780) -> Result<()> {
1781 if let Some(table_db_name) = &qualified_name.db_name {
1782 let is_cross_db = match view_db_name {
1783 Some(view_db) => !view_db
1784 .as_str()
1785 .eq_ignore_ascii_case(table_db_name.as_str()),
1786 None => !table_db_name.as_str().eq_ignore_ascii_case("main"),
1787 };
1788 if is_cross_db {
1789 return Err(crate::LimboError::ParseError(format!(
1790 "view cannot reference table in attached database: {qualified_name}"
1791 )));
1792 }
1793 }
1794 Ok(())
1795}
1796
1797fn validate_select_table_no_cross_db(
1798 select_table: &ast::SelectTable,
1799 view_db_name: Option<&ast::Name>,
1800) -> Result<()> {
1801 match select_table {
1802 ast::SelectTable::Table(name, _, _) | ast::SelectTable::TableCall(name, _, _) => {
1803 reject_cross_db_qualified_name(name, view_db_name)?;
1804 }
1805 ast::SelectTable::Select(select, _) => {
1806 validate_no_cross_db_references(select, view_db_name)?;
1807 }
1808 ast::SelectTable::Sub(from_clause, _) => {
1809 validate_from_clause_no_cross_db(from_clause, view_db_name)?;
1810 }
1811 }
1812 Ok(())
1813}
1814
1815pub fn validate_select_for_unsupported_features(select_stmt: &ast::Select) -> Result<()> {
1816 walk_select_expressions(select_stmt, &mut |expr| {
1817 if let ast::Expr::FunctionCall { order_by, .. } = expr {
1818 if !order_by.is_empty() {
1819 crate::bail_parse_error!(
1820 "ORDER BY clause is not supported yet in aggregate functions"
1821 );
1822 }
1823 }
1824 Ok(WalkControl::Continue)
1825 })
1826}
1827
1828pub fn validate_select_for_views(
1829 select_stmt: &ast::Select,
1830 view_db_name: Option<&ast::Name>,
1831) -> Result<()> {
1832 validate_select_for_unsupported_features(select_stmt)?;
1833
1834 validate_no_cross_db_references(select_stmt, view_db_name)?;
1835
1836 walk_select_expressions(select_stmt, &mut |expr| {
1837 match expr {
1838 ast::Expr::Subquery(subquery_select) | ast::Expr::Exists(subquery_select) => {
1839 validate_no_cross_db_references(subquery_select, view_db_name)?;
1840 }
1841 ast::Expr::InSelect { rhs, .. } => {
1842 validate_no_cross_db_references(rhs, view_db_name)?;
1843 }
1844 _ => {}
1845 }
1846
1847 Ok(WalkControl::Continue)
1848 })?;
1849
1850 Ok(())
1851}
1852
1853pub fn extract_view_columns(
1855 select_stmt: &ast::Select,
1856 schema: &Schema,
1857) -> Result<ViewColumnSchema> {
1858 let mut tables = Vec::new();
1859 let mut columns = Vec::new();
1860 let mut column_name_counts: HashMap<String, usize> = HashMap::default();
1861
1862 if let ast::OneSelect::Select {
1864 ref from,
1865 columns: select_columns,
1866 ..
1867 } = &select_stmt.body.select
1868 {
1869 if let Some(from) = from {
1871 match from.select.as_ref() {
1873 ast::SelectTable::Table(qualified_name, alias, _) => {
1874 let table_name = normalize_ident(qualified_name.name.as_str());
1875 let db_name = qualified_name
1876 .db_name
1877 .as_ref()
1878 .map(|db| normalize_ident(db.as_str()));
1879 tables.push(ViewTable {
1880 name: table_name,
1881 db_name,
1882 alias: alias.as_ref().map(|a| normalize_ident(a.name().as_str())),
1883 });
1884 }
1885 _ => {
1886 }
1888 }
1889
1890 for join in &from.joins {
1892 match join.table.as_ref() {
1893 ast::SelectTable::Table(qualified_name, alias, _) => {
1894 let table_name = normalize_ident(qualified_name.name.as_str());
1895 let db_name = qualified_name
1896 .db_name
1897 .as_ref()
1898 .map(|db| normalize_ident(db.as_str()));
1899 tables.push(ViewTable {
1900 name: table_name,
1901 db_name,
1902 alias: alias.as_ref().map(|a| normalize_ident(a.name().as_str())),
1903 });
1904 }
1905 _ => {
1906 }
1908 }
1909 }
1910 }
1911
1912 let find_table_index = |name: &str| -> Option<usize> {
1914 tables.iter().position(|t| {
1915 t.name.eq_ignore_ascii_case(name)
1916 || t.alias
1917 .as_ref()
1918 .is_some_and(|a| a.eq_ignore_ascii_case(name))
1919 })
1920 };
1921
1922 for result_col in select_columns.iter() {
1924 match result_col {
1925 ast::ResultColumn::Expr(expr, alias) => {
1926 let table_index = match expr.as_ref() {
1928 ast::Expr::Qualified(table_ref, _col_name) => {
1929 find_table_index(table_ref.as_str())
1931 }
1932 ast::Expr::Id(_col_name) => {
1933 if !tables.is_empty() {
1936 Some(0)
1937 } else {
1938 None
1939 }
1940 }
1941 _ => None, };
1943
1944 let col_name = alias
1945 .as_ref()
1946 .filter(|a| !matches!(a, ast::As::ImplicitColumnName(_)))
1949 .map(|a| a.name().as_str().to_string())
1950 .or_else(|| extract_column_name_from_expr(expr))
1951 .unwrap_or_else(|| {
1952 expr.to_string()
1954 });
1955
1956 columns.push(ViewColumn {
1957 table_index: table_index.unwrap_or(usize::MAX),
1958 column: Column::new_default_text(Some(col_name), "TEXT".to_string(), None),
1959 });
1960 }
1961 ast::ResultColumn::Star => {
1962 for (table_idx, table) in tables.iter().enumerate() {
1964 if let Some(table_obj) = schema.get_table(&table.name) {
1965 for table_column in table_obj.columns() {
1966 let col_name =
1967 table_column.name.clone().unwrap_or_else(|| "?".to_string());
1968
1969 let final_name =
1971 if let Some(count) = column_name_counts.get_mut(&col_name) {
1972 *count += 1;
1973 format!("{}:{}", col_name, *count - 1)
1974 } else {
1975 column_name_counts.insert(col_name.clone(), 1);
1976 col_name.clone()
1977 };
1978
1979 columns.push(ViewColumn {
1980 table_index: table_idx,
1981 column: Column::new(
1982 Some(final_name),
1983 table_column.ty_str.clone(),
1984 None,
1985 None,
1986 table_column.ty(),
1987 table_column.collation_opt(),
1988 ColDef::default(),
1989 ),
1990 });
1991 }
1992 }
1993 }
1994
1995 if tables.is_empty() {
1997 columns.push(ViewColumn {
1998 table_index: usize::MAX,
1999 column: Column::new_default_text(
2000 Some("*".to_string()),
2001 "TEXT".to_string(),
2002 None,
2003 ),
2004 });
2005 }
2006 }
2007 ast::ResultColumn::TableStar(table_ref) => {
2008 let table_name_str = normalize_ident(table_ref.as_str());
2010 if let Some(table_idx) = find_table_index(&table_name_str) {
2011 if let Some(table) = schema.get_table(&tables[table_idx].name) {
2012 for table_column in table.columns() {
2013 let col_name =
2014 table_column.name.clone().unwrap_or_else(|| "?".to_string());
2015
2016 let final_name =
2018 if let Some(count) = column_name_counts.get_mut(&col_name) {
2019 *count += 1;
2020 format!("{}:{}", col_name, *count - 1)
2021 } else {
2022 column_name_counts.insert(col_name.clone(), 1);
2023 col_name.clone()
2024 };
2025
2026 columns.push(ViewColumn {
2027 table_index: table_idx,
2028 column: Column::new(
2029 Some(final_name),
2030 table_column.ty_str.clone(),
2031 None,
2032 None,
2033 table_column.ty(),
2034 table_column.collation_opt(),
2035 ColDef::default(),
2036 ),
2037 });
2038 }
2039 } else {
2040 columns.push(ViewColumn {
2042 table_index: usize::MAX,
2043 column: Column::new_default_text(
2044 Some(format!("{table_name_str}.*")),
2045 "TEXT".to_string(),
2046 None,
2047 ),
2048 });
2049 }
2050 }
2051 }
2052 }
2053 }
2054 }
2055
2056 Ok(ViewColumnSchema { tables, columns })
2057}
2058
2059pub fn rewrite_fk_parent_cols_if_self_ref(
2060 clause: &mut ast::ForeignKeyClause,
2061 table: &str,
2062 from: &str,
2063 to: &str,
2064) {
2065 if clause.tbl_name.as_str().eq_ignore_ascii_case(table) {
2066 for c in &mut clause.columns {
2067 if c.col_name.as_str().eq_ignore_ascii_case(from) {
2068 c.col_name = ast::Name::exact(to.to_owned());
2069 }
2070 }
2071 }
2072}
2073
2074pub fn check_expr_references_column(expr: &ast::Expr, col_name_normalized: &str) -> bool {
2077 let mut found = false;
2078 let _ = walk_expr(expr, &mut |e| {
2080 if found {
2081 return Ok(WalkControl::SkipChildren);
2082 }
2083 match e {
2084 ast::Expr::Id(name) | ast::Expr::Name(name) => {
2085 if name.as_str().eq_ignore_ascii_case(col_name_normalized) {
2086 found = true;
2087 return Ok(WalkControl::SkipChildren);
2088 }
2089 }
2090 ast::Expr::Qualified(_, col) | ast::Expr::DoublyQualified(_, _, col) => {
2091 if col.as_str().eq_ignore_ascii_case(col_name_normalized) {
2092 found = true;
2093 return Ok(WalkControl::SkipChildren);
2094 }
2095 }
2096 _ => {}
2097 }
2098 Ok(WalkControl::Continue)
2099 });
2100 found
2101}
2102
2103pub fn rename_identifiers(expr: &mut ast::Expr, from: &str, to: &str) {
2108 let _ = walk_expr_mut(
2110 expr,
2111 &mut |e: &mut ast::Expr| -> crate::Result<WalkControl> {
2112 match e {
2113 ast::Expr::Id(ref name) | ast::Expr::Name(ref name)
2114 if name.as_str().eq_ignore_ascii_case(from) =>
2115 {
2116 *e = ast::Expr::Id(ast::Name::exact(to.to_owned()));
2117 }
2118 ast::Expr::Qualified(ref tbl, ref col_name)
2119 if col_name.as_str().eq_ignore_ascii_case(from) =>
2120 {
2121 let tbl = tbl.clone();
2122 *e = ast::Expr::Qualified(tbl, ast::Name::exact(to.to_owned()));
2123 }
2124 _ => {}
2125 }
2126 Ok(WalkControl::Continue)
2127 },
2128 );
2129}
2130
2131pub fn rename_identifiers_scoped(
2137 expr: &mut ast::Expr,
2138 target_table: &str,
2139 trigger_table: &str,
2140 from: &str,
2141 to: &str,
2142) {
2143 rename_identifiers_scoped_inner(expr, target_table, trigger_table, from, to, true, None);
2144}
2145
2146pub fn rename_identifiers_scoped_when_clause(
2150 expr: &mut ast::Expr,
2151 target_table: &str,
2152 trigger_table: &str,
2153 from: &str,
2154 to: &str,
2155) {
2156 rename_identifiers_scoped_inner(expr, target_table, trigger_table, from, to, false, None);
2157}
2158
2159fn rename_identifiers_scoped_inner(
2163 expr: &mut ast::Expr,
2164 target_table: &str,
2165 trigger_table: &str,
2166 from: &str,
2167 to: &str,
2168 rename_unqualified: bool,
2169 target_qualifiers: Option<&[String]>,
2170) {
2171 let is_renaming_trigger_table = target_table.eq_ignore_ascii_case(trigger_table);
2172 let _ = walk_expr_mut(
2173 expr,
2174 &mut |e: &mut ast::Expr| -> crate::Result<WalkControl> {
2175 match e {
2176 ast::Expr::Subquery(select) | ast::Expr::Exists(select) => {
2177 let mut quals = target_qualifiers.unwrap_or(&[]).to_vec();
2178 rewrite_select_column_refs_scoped(
2179 select,
2180 target_table,
2181 trigger_table,
2182 from,
2183 to,
2184 &mut quals,
2185 );
2186 }
2187 ast::Expr::InSelect { rhs, .. } => {
2188 let mut quals = target_qualifiers.unwrap_or(&[]).to_vec();
2189 rewrite_select_column_refs_scoped(
2190 rhs,
2191 target_table,
2192 trigger_table,
2193 from,
2194 to,
2195 &mut quals,
2196 );
2197 }
2199 ast::Expr::Id(ref name) | ast::Expr::Name(ref name)
2200 if rename_unqualified && name.as_str().eq_ignore_ascii_case(from) =>
2201 {
2202 *e = ast::Expr::Id(ast::Name::exact(to.to_owned()));
2203 }
2204 ast::Expr::Qualified(ref tbl, ref col_name)
2205 if col_name.as_str().eq_ignore_ascii_case(from) =>
2206 {
2207 let tbl_norm = normalize_ident(tbl.as_str());
2208 let should_rename = if tbl_norm == "new" || tbl_norm == "old" {
2209 is_renaming_trigger_table
2210 } else {
2211 target_qualifiers.is_some_and(|qualifiers| qualifiers.contains(&tbl_norm))
2212 || tbl_norm.eq_ignore_ascii_case(target_table)
2213 };
2214 if should_rename {
2215 let tbl = tbl.clone();
2216 *e = ast::Expr::Qualified(tbl, ast::Name::exact(to.to_owned()));
2217 }
2218 }
2219 _ => {}
2220 }
2221 Ok(WalkControl::Continue)
2222 },
2223 );
2224}
2225
2226mod rename_column_view {
2227 use super::*;
2228
2229 #[derive(Debug, Clone)]
2230 pub struct RewrittenView {
2231 pub sql: String,
2232 pub select_stmt: ast::Select,
2233 pub columns: crate::alloc::Vec<Column>,
2234 }
2235
2236 pub fn rewrite_view_sql_for_column_rename(
2237 view_sql: &str,
2238 schema: &Schema,
2239 target_table: &str,
2240 target_db_name: &str,
2241 old_column: &str,
2242 new_column: &str,
2243 ) -> Result<Option<RewrittenView>> {
2244 let mut visiting_views = HashSet::default();
2245 rewrite_view_sql_for_column_rename_inner(
2246 view_sql,
2247 schema,
2248 target_table,
2249 target_db_name,
2250 old_column,
2251 new_column,
2252 &mut visiting_views,
2253 )
2254 }
2255
2256 fn rewrite_view_sql_for_column_rename_inner(
2257 view_sql: &str,
2258 schema: &Schema,
2259 target_table: &str,
2260 target_db_name: &str,
2261 old_column: &str,
2262 new_column: &str,
2263 visiting_views: &mut HashSet<String>,
2264 ) -> Result<Option<RewrittenView>> {
2265 let mut parser = Parser::new(view_sql.as_bytes());
2266 let cmd = parser
2267 .next_cmd()
2268 .map_err(|e| LimboError::ParseError(format!("failed to parse view SQL: {e}")))?;
2269 let Some(ast::Cmd::Stmt(ast::Stmt::CreateView {
2270 temporary,
2271 if_not_exists,
2272 view_name,
2273 columns: view_columns,
2274 mut select,
2275 })) = cmd
2276 else {
2277 return Ok(None);
2278 };
2279
2280 let current_view_name = normalize_ident(view_name.name.as_str());
2281 if !visiting_views.insert(current_view_name.clone()) {
2282 return Err(LimboError::ParseError(format!(
2283 "view {current_view_name} is circularly defined"
2284 )));
2285 }
2286
2287 let rewrite_result = (|| -> Result<Option<RewrittenView>> {
2288 let original_select = select.clone();
2289 let original_columns =
2290 view_columns_from_select(&original_select, schema, &view_columns)?;
2291
2292 let ctx =
2293 ViewRewriteCtx::new(schema, target_table, target_db_name, old_column, new_column);
2294 let sql_changed =
2295 rewrite_view_select_for_column_rename(&mut select, &ctx, &[], visiting_views)?;
2296
2297 let view_column_schema = extract_view_columns(&select, schema)?;
2298 let mut final_columns = apply_view_column_rename(view_column_schema, &ctx);
2299
2300 for (i, indexed_col) in view_columns.iter().enumerate() {
2301 if let Some(col) = final_columns.get_mut(i) {
2302 col.name = Some(indexed_col.col_name.as_str().to_string());
2303 }
2304 }
2305
2306 let columns_changed = !columns_equivalent(&original_columns, &final_columns);
2307
2308 if !sql_changed && !columns_changed {
2309 return Ok(None);
2310 }
2311
2312 let new_sql = if sql_changed {
2313 let new_stmt = ast::Stmt::CreateView {
2314 temporary,
2315 if_not_exists,
2316 view_name,
2317 columns: view_columns,
2318 select: select.clone(),
2319 };
2320 new_stmt.to_string()
2321 } else {
2322 view_sql.to_string()
2323 };
2324
2325 Ok(Some(RewrittenView {
2326 sql: new_sql,
2327 select_stmt: select,
2328 columns: final_columns,
2329 }))
2330 })();
2331 visiting_views.remove(¤t_view_name);
2332 rewrite_result
2333 }
2334
2335 fn apply_view_column_rename(
2336 view_columns: ViewColumnSchema,
2337 ctx: &ViewRewriteCtx,
2338 ) -> crate::alloc::Vec<Column> {
2339 let target_norm = ctx.target_table_norm.as_str();
2340 let mut columns = view_columns.columns;
2341
2342 for view_column in &mut columns {
2343 if view_column.table_index == usize::MAX {
2344 continue;
2345 }
2346 let table = &view_columns.tables[view_column.table_index];
2347 if table_name_matches_target(
2348 &table.name,
2349 table.db_name.as_deref(),
2350 target_norm,
2351 &ctx.target_db_norm,
2352 ) {
2353 if let Some(ref mut name) = view_column.column.name {
2354 if name.as_str().eq_ignore_ascii_case(ctx.old_column) {
2355 *name = ctx.new_column.to_string();
2356 }
2357 }
2358 }
2359 }
2360
2361 columns
2362 .into_iter()
2363 .map(|vc| vc.column)
2364 .try_collect()
2365 .expect(crate::alloc::ALLOC_ERR_MSG)
2366 }
2367
2368 fn view_columns_from_select(
2369 select: &ast::Select,
2370 schema: &Schema,
2371 explicit: &[ast::IndexedColumn],
2372 ) -> Result<crate::alloc::Vec<Column>> {
2373 let view_column_schema = extract_view_columns(select, schema)?;
2374 let mut columns = view_column_schema.flat_columns();
2375 for (i, indexed_col) in explicit.iter().enumerate() {
2376 if let Some(col) = columns.get_mut(i) {
2377 col.name = Some(indexed_col.col_name.as_str().to_string());
2378 }
2379 }
2380 Ok(columns)
2381 }
2382
2383 fn columns_equivalent(left: &[Column], right: &[Column]) -> bool {
2384 if left.len() != right.len() {
2385 return false;
2386 }
2387 left.iter().zip(right.iter()).all(|(l, r)| {
2388 let l_name = l.name.as_deref().unwrap_or("");
2389 let r_name = r.name.as_deref().unwrap_or("");
2390 l_name.eq_ignore_ascii_case(r_name)
2391 })
2392 }
2393
2394 #[derive(Clone)]
2395 struct ViewSourceInfo {
2396 qualifiers: Vec<String>,
2397 columns_before: HashSet<String>,
2398 rename_map: HashMap<String, String>,
2399 is_target_table: bool,
2400 db_name: Option<String>,
2401 }
2402
2403 impl ViewSourceInfo {
2404 fn matches_qualifier(&self, qualifier: &str) -> bool {
2405 self.qualifiers.iter().any(|q| q == qualifier)
2406 }
2407 }
2408
2409 fn alias_name(alias: &ast::As) -> &str {
2410 alias.name().as_str()
2411 }
2412
2413 #[derive(Clone)]
2414 struct CteInfo {
2415 columns_before: HashSet<String>,
2416 rename_map: HashMap<String, String>,
2417 }
2418
2419 struct ViewRewriteCtx<'a> {
2420 schema: &'a Schema,
2421 target_table: &'a str,
2422 target_table_norm: String,
2423 target_db_norm: String,
2424 old_column: &'a str,
2425 old_column_norm: String,
2426 new_column: &'a str,
2427 }
2428
2429 impl<'a> ViewRewriteCtx<'a> {
2430 fn new(
2431 schema: &'a Schema,
2432 target_table: &'a str,
2433 target_db_name: &'a str,
2434 old_column: &'a str,
2435 new_column: &'a str,
2436 ) -> Self {
2437 Self {
2438 schema,
2439 target_table,
2440 target_table_norm: normalize_ident(target_table),
2441 target_db_norm: normalize_ident(target_db_name),
2442 old_column,
2443 old_column_norm: normalize_ident(old_column),
2444 new_column,
2445 }
2446 }
2447 }
2448
2449 fn rewrite_view_select_for_column_rename(
2450 select: &mut ast::Select,
2451 ctx: &ViewRewriteCtx,
2452 outer_scopes: &[&[ViewSourceInfo]],
2453 visiting_views: &mut HashSet<String>,
2454 ) -> Result<bool> {
2455 let mut changed = false;
2456
2457 let mut ctes: HashMap<String, CteInfo> = HashMap::default();
2458 if let Some(ref mut with_clause) = select.with {
2459 for cte in &mut with_clause.ctes {
2460 let mut before_cols = select_output_columns(&cte.select, ctx, false)?;
2461 apply_explicit_column_names(&mut before_cols, &cte.columns);
2462 let cte_changed = rewrite_view_select_for_column_rename(
2463 &mut cte.select,
2464 ctx,
2465 &[],
2466 visiting_views,
2467 )?;
2468 changed |= cte_changed;
2469 let mut after_cols = select_output_columns(&cte.select, ctx, true)?;
2470 apply_explicit_column_names(&mut after_cols, &cte.columns);
2471 let rename_map = build_rename_map(&before_cols, &after_cols, &ctx.old_column_norm);
2472 ctes.insert(
2473 normalize_ident(cte.tbl_name.as_str()),
2474 CteInfo {
2475 columns_before: before_cols
2476 .into_iter()
2477 .map(|c| normalize_ident(&c))
2478 .collect(),
2479 rename_map,
2480 },
2481 );
2482 }
2483 }
2484
2485 let mut scope_sources = rewrite_one_select_for_column_rename(
2486 &mut select.body.select,
2487 ctx,
2488 &ctes,
2489 outer_scopes,
2490 &mut changed,
2491 visiting_views,
2492 )?;
2493
2494 for compound in &mut select.body.compounds {
2495 let compound_sources = rewrite_one_select_for_column_rename(
2496 &mut compound.select,
2497 ctx,
2498 &ctes,
2499 outer_scopes,
2500 &mut changed,
2501 visiting_views,
2502 )?;
2503 if scope_sources.is_none() {
2504 scope_sources = compound_sources;
2505 }
2506 }
2507
2508 if let Some(ref sources) = scope_sources {
2509 for sorted_col in &mut select.order_by {
2510 rewrite_expr_in_scope(
2511 &mut sorted_col.expr,
2512 sources,
2513 outer_scopes,
2514 ctx,
2515 &mut changed,
2516 visiting_views,
2517 )?;
2518 }
2519 if let Some(ref mut limit) = select.limit {
2520 rewrite_expr_in_scope(
2521 &mut limit.expr,
2522 sources,
2523 outer_scopes,
2524 ctx,
2525 &mut changed,
2526 visiting_views,
2527 )?;
2528 if let Some(ref mut offset) = limit.offset {
2529 rewrite_expr_in_scope(
2530 offset,
2531 sources,
2532 outer_scopes,
2533 ctx,
2534 &mut changed,
2535 visiting_views,
2536 )?;
2537 }
2538 }
2539 }
2540
2541 Ok(changed)
2542 }
2543
2544 fn rewrite_one_select_for_column_rename(
2545 one_select: &mut ast::OneSelect,
2546 ctx: &ViewRewriteCtx,
2547 ctes: &HashMap<String, CteInfo>,
2548 outer_scopes: &[&[ViewSourceInfo]],
2549 changed: &mut bool,
2550 visiting_views: &mut HashSet<String>,
2551 ) -> Result<Option<Vec<ViewSourceInfo>>> {
2552 match one_select {
2553 ast::OneSelect::Select {
2554 columns,
2555 from,
2556 where_clause,
2557 group_by,
2558 window_clause,
2559 ..
2560 } => {
2561 let sources = if let Some(ref mut from_clause) = from {
2562 rewrite_from_clause_for_column_rename(
2563 from_clause,
2564 ctx,
2565 ctes,
2566 outer_scopes,
2567 changed,
2568 visiting_views,
2569 )?
2570 } else {
2571 Vec::new()
2572 };
2573
2574 for col in columns {
2575 if let ast::ResultColumn::Expr(expr, _) = col {
2576 rewrite_expr_in_scope(
2577 expr,
2578 &sources,
2579 outer_scopes,
2580 ctx,
2581 changed,
2582 visiting_views,
2583 )?;
2584 }
2585 }
2586
2587 if let Some(ref mut where_expr) = where_clause {
2588 rewrite_expr_in_scope(
2589 where_expr,
2590 &sources,
2591 outer_scopes,
2592 ctx,
2593 changed,
2594 visiting_views,
2595 )?;
2596 }
2597
2598 if let Some(ref mut group_by) = group_by {
2599 for expr in &mut group_by.exprs {
2600 rewrite_expr_in_scope(
2601 expr,
2602 &sources,
2603 outer_scopes,
2604 ctx,
2605 changed,
2606 visiting_views,
2607 )?;
2608 }
2609 if let Some(ref mut having_expr) = group_by.having {
2610 rewrite_expr_in_scope(
2611 having_expr,
2612 &sources,
2613 outer_scopes,
2614 ctx,
2615 changed,
2616 visiting_views,
2617 )?;
2618 }
2619 }
2620
2621 for window_def in window_clause {
2622 for expr in &mut window_def.window.partition_by {
2623 rewrite_expr_in_scope(
2624 expr,
2625 &sources,
2626 outer_scopes,
2627 ctx,
2628 changed,
2629 visiting_views,
2630 )?;
2631 }
2632 for sorted in &mut window_def.window.order_by {
2633 rewrite_expr_in_scope(
2634 &mut sorted.expr,
2635 &sources,
2636 outer_scopes,
2637 ctx,
2638 changed,
2639 visiting_views,
2640 )?;
2641 }
2642 }
2643
2644 Ok(Some(sources))
2645 }
2646 ast::OneSelect::Values(values) => {
2647 for row in values {
2648 for expr in row {
2649 rewrite_expr_in_scope(
2650 expr,
2651 &[],
2652 outer_scopes,
2653 ctx,
2654 changed,
2655 visiting_views,
2656 )?;
2657 }
2658 }
2659 Ok(None)
2660 }
2661 }
2662 }
2663
2664 fn rewrite_from_clause_for_column_rename(
2665 from_clause: &mut ast::FromClause,
2666 ctx: &ViewRewriteCtx,
2667 ctes: &HashMap<String, CteInfo>,
2668 outer_scopes: &[&[ViewSourceInfo]],
2669 changed: &mut bool,
2670 visiting_views: &mut HashSet<String>,
2671 ) -> Result<Vec<ViewSourceInfo>> {
2672 let mut sources = Vec::new();
2673 let first_source = rewrite_select_table_for_column_rename(
2674 &mut from_clause.select,
2675 &[],
2676 ctx,
2677 ctes,
2678 outer_scopes,
2679 changed,
2680 visiting_views,
2681 )?;
2682 sources.push(first_source);
2683
2684 for join in &mut from_clause.joins {
2685 let right_source = rewrite_select_table_for_column_rename(
2686 &mut join.table,
2687 &sources,
2688 ctx,
2689 ctes,
2690 outer_scopes,
2691 changed,
2692 visiting_views,
2693 )?;
2694 sources.push(right_source);
2695 let (right_source, left_sources) = sources
2696 .split_last()
2697 .expect("sources should include the right-hand side join source");
2698 if let Some(ref mut constraint) = join.constraint {
2699 match constraint {
2700 ast::JoinConstraint::On(expr) => {
2701 rewrite_expr_in_scope(
2702 expr,
2703 &sources,
2704 outer_scopes,
2705 ctx,
2706 changed,
2707 visiting_views,
2708 )?;
2709 }
2710 ast::JoinConstraint::Using(cols) => {
2711 *changed |= rewrite_using_columns(
2712 cols,
2713 left_sources,
2714 right_source,
2715 &ctx.old_column_norm,
2716 ctx.new_column,
2717 );
2718 }
2719 }
2720 }
2721 }
2722
2723 Ok(sources)
2724 }
2725
2726 fn rewrite_select_table_for_column_rename(
2727 select_table: &mut ast::SelectTable,
2728 visible_sources: &[ViewSourceInfo],
2729 ctx: &ViewRewriteCtx,
2730 ctes: &HashMap<String, CteInfo>,
2731 outer_scopes: &[&[ViewSourceInfo]],
2732 changed: &mut bool,
2733 visiting_views: &mut HashSet<String>,
2734 ) -> Result<ViewSourceInfo> {
2735 match select_table {
2736 ast::SelectTable::Table(tbl_name, alias, _) => {
2737 let table_name_norm = normalize_ident(tbl_name.name.as_str());
2738 let table_db_norm = tbl_name
2739 .db_name
2740 .as_ref()
2741 .map(|db| normalize_ident(db.as_str()));
2742 let mut qualifiers = Vec::new();
2743 qualifiers.push(table_name_norm.clone());
2744 if let Some(ref alias) = alias {
2745 qualifiers.push(normalize_ident(alias_name(alias)));
2746 }
2747 if table_db_norm.is_none() {
2748 if let Some(cte) = ctes.get(&table_name_norm) {
2749 return Ok(ViewSourceInfo {
2750 qualifiers,
2751 columns_before: cte.columns_before.clone(),
2752 rename_map: cte.rename_map.clone(),
2753 is_target_table: false,
2754 db_name: None,
2755 });
2756 }
2757 }
2758
2759 let is_local = table_db_norm
2760 .as_deref()
2761 .is_none_or(|db| db == ctx.target_db_norm);
2762
2763 if is_local {
2764 if let Some(view) = ctx.schema.views.get(&table_name_norm) {
2765 let columns_before = view
2766 .columns
2767 .iter()
2768 .filter_map(|col| col.name.clone())
2769 .map(|name| normalize_ident(&name))
2770 .collect();
2771
2772 let mut rename_map = HashMap::default();
2773 if let Some(rewritten) = rewrite_view_sql_for_column_rename_inner(
2774 &view.sql,
2775 ctx.schema,
2776 ctx.target_table,
2777 &ctx.target_db_norm,
2778 ctx.old_column,
2779 ctx.new_column,
2780 visiting_views,
2781 )? {
2782 rename_map = build_rename_map_from_columns(
2783 &view.columns,
2784 &rewritten.columns,
2785 &ctx.old_column_norm,
2786 );
2787 }
2788
2789 return Ok(ViewSourceInfo {
2790 qualifiers,
2791 columns_before,
2792 rename_map,
2793 is_target_table: false,
2794 db_name: table_db_norm,
2795 });
2796 }
2797 }
2798 let is_target = table_name_matches_target(
2799 &table_name_norm,
2800 table_db_norm.as_deref(),
2801 &ctx.target_table_norm,
2802 &ctx.target_db_norm,
2803 );
2804 let columns_before = if is_local {
2805 table_source_columns(ctx.schema, &table_name_norm)
2806 .unwrap_or_default()
2807 .into_iter()
2808 .map(|c| normalize_ident(&c))
2809 .collect()
2810 } else {
2811 HashSet::default()
2812 };
2813
2814 Ok(ViewSourceInfo {
2815 qualifiers,
2816 columns_before,
2817 rename_map: HashMap::default(),
2818 is_target_table: is_target,
2819 db_name: table_db_norm,
2820 })
2821 }
2822 ast::SelectTable::Select(select, alias) => {
2823 let before_cols = select_output_columns(select, ctx, false)?;
2824 *changed |=
2825 rewrite_view_select_for_column_rename(select, ctx, &[], visiting_views)?;
2826 let after_cols = select_output_columns(select, ctx, true)?;
2827 let rename_map = build_rename_map(&before_cols, &after_cols, &ctx.old_column_norm);
2828 let qualifiers = alias
2829 .as_ref()
2830 .map(|alias| vec![normalize_ident(alias_name(alias))])
2831 .unwrap_or_default();
2832 Ok(ViewSourceInfo {
2833 qualifiers,
2834 columns_before: before_cols
2835 .into_iter()
2836 .map(|c| normalize_ident(&c))
2837 .collect(),
2838 rename_map,
2839 is_target_table: false,
2840 db_name: None,
2841 })
2842 }
2843 ast::SelectTable::Sub(from_clause, alias) => {
2844 let before_cols = from_clause_output_columns(from_clause, ctx, false)?;
2845 let _ = rewrite_from_clause_for_column_rename(
2846 from_clause,
2847 ctx,
2848 ctes,
2849 outer_scopes,
2850 changed,
2851 visiting_views,
2852 )?;
2853 let after_cols = from_clause_output_columns(from_clause, ctx, true)?;
2854 let rename_map = build_rename_map(&before_cols, &after_cols, &ctx.old_column_norm);
2855 let qualifiers = alias
2856 .as_ref()
2857 .map(|alias| vec![normalize_ident(alias_name(alias))])
2858 .unwrap_or_default();
2859 Ok(ViewSourceInfo {
2860 qualifiers,
2861 columns_before: before_cols
2862 .into_iter()
2863 .map(|c| normalize_ident(&c))
2864 .collect(),
2865 rename_map,
2866 is_target_table: false,
2867 db_name: None,
2868 })
2869 }
2870 ast::SelectTable::TableCall(_, args, alias) => {
2871 for arg in args {
2872 rewrite_expr_in_scope(
2873 arg,
2874 visible_sources,
2875 outer_scopes,
2876 ctx,
2877 changed,
2878 visiting_views,
2879 )?;
2880 }
2881 let qualifiers = alias
2882 .as_ref()
2883 .map(|alias| vec![normalize_ident(alias_name(alias))])
2884 .unwrap_or_default();
2885 Ok(ViewSourceInfo {
2886 qualifiers,
2887 columns_before: HashSet::default(),
2888 rename_map: HashMap::default(),
2889 is_target_table: false,
2890 db_name: None,
2891 })
2892 }
2893 }
2894 }
2895
2896 fn rewrite_expr_in_scope(
2897 expr: &mut ast::Expr,
2898 sources: &[ViewSourceInfo],
2899 outer_scopes: &[&[ViewSourceInfo]],
2900 ctx: &ViewRewriteCtx,
2901 changed: &mut bool,
2902 visiting_views: &mut HashSet<String>,
2903 ) -> Result<()> {
2904 let mut outer_scopes_for_subqueries: Vec<&[ViewSourceInfo]> =
2905 Vec::with_capacity(outer_scopes.len() + 1);
2906 if !sources.is_empty() {
2907 outer_scopes_for_subqueries.push(sources);
2908 }
2909 outer_scopes_for_subqueries.extend_from_slice(outer_scopes);
2910 walk_expr_mut(expr, &mut |e: &mut ast::Expr| -> Result<WalkControl> {
2911 if rewrite_expr_column_ref_view(
2912 e,
2913 sources,
2914 outer_scopes,
2915 &ctx.target_db_norm,
2916 &ctx.old_column_norm,
2917 ctx.new_column,
2918 ) {
2919 *changed = true;
2920 }
2921 match e {
2922 ast::Expr::Subquery(select) | ast::Expr::Exists(select) => {
2923 if rewrite_view_select_for_column_rename(
2924 select,
2925 ctx,
2926 outer_scopes_for_subqueries.as_slice(),
2927 visiting_views,
2928 )? {
2929 *changed = true;
2930 }
2931 }
2932 ast::Expr::InSelect { rhs, .. } => {
2933 if rewrite_view_select_for_column_rename(
2934 rhs,
2935 ctx,
2936 outer_scopes_for_subqueries.as_slice(),
2937 visiting_views,
2938 )? {
2939 *changed = true;
2940 }
2941 }
2942 _ => {}
2943 }
2944 Ok(WalkControl::Continue)
2945 })?;
2946 Ok(())
2947 }
2948
2949 fn rewrite_expr_column_ref_view(
2950 expr: &mut ast::Expr,
2951 sources: &[ViewSourceInfo],
2952 outer_scopes: &[&[ViewSourceInfo]],
2953 target_db_norm: &str,
2954 old_column_norm: &str,
2955 new_column: &str,
2956 ) -> bool {
2957 let apply_rename = |source: &ViewSourceInfo, set_name: &mut dyn FnMut(String)| {
2958 if source.is_target_table {
2959 set_name(new_column.to_string());
2960 return true;
2961 }
2962 if let Some(mapped) = source.rename_map.get(old_column_norm) {
2963 set_name(mapped.to_string());
2964 return true;
2965 }
2966 false
2967 };
2968
2969 match expr {
2970 ast::Expr::Qualified(ns, col) => {
2971 let ns_norm = normalize_ident(ns.as_str());
2972 if !col.as_str().eq_ignore_ascii_case(old_column_norm) {
2973 return false;
2974 }
2975 let (source, local_ambiguous) =
2976 resolve_qualified(sources, &ns_norm, target_db_norm);
2977 if let Some(source) = source {
2978 return apply_rename(source, &mut |name| {
2979 *col = ast::Name::exact(name);
2980 });
2981 }
2982 if local_ambiguous {
2983 return false;
2984 }
2985 for scope in outer_scopes {
2986 let (source, ambiguous) = resolve_qualified(scope, &ns_norm, target_db_norm);
2987 if let Some(source) = source {
2988 return apply_rename(source, &mut |name| {
2989 *col = ast::Name::exact(name);
2990 });
2991 }
2992 if ambiguous {
2993 return false;
2994 }
2995 }
2996 }
2997 ast::Expr::DoublyQualified(schema, ns, col) => {
2998 let schema_norm = normalize_ident(schema.as_str());
2999 if schema_norm != target_db_norm {
3000 return false;
3001 }
3002 let ns_norm = normalize_ident(ns.as_str());
3003 if !col.as_str().eq_ignore_ascii_case(old_column_norm) {
3004 return false;
3005 }
3006 let (source, local_ambiguous) = resolve_qualified(sources, &ns_norm, &schema_norm);
3007 if let Some(source) = source {
3008 return apply_rename(source, &mut |name| {
3009 *col = ast::Name::exact(name);
3010 });
3011 }
3012 if local_ambiguous {
3013 return false;
3014 }
3015 for scope in outer_scopes {
3016 let (source, ambiguous) = resolve_qualified(scope, &ns_norm, &schema_norm);
3017 if let Some(source) = source {
3018 return apply_rename(source, &mut |name| {
3019 *col = ast::Name::exact(name);
3020 });
3021 }
3022 if ambiguous {
3023 return false;
3024 }
3025 }
3026 }
3027 ast::Expr::Id(col) | ast::Expr::Name(col) => {
3028 if !col.as_str().eq_ignore_ascii_case(old_column_norm) {
3029 return false;
3030 }
3031 let col_norm = normalize_ident(col.as_str());
3032 let (source, local_ambiguous) = resolve_unqualified(sources, &col_norm);
3033 if let Some(source) = source {
3034 return apply_rename(source, &mut |name| {
3035 *expr = ast::Expr::Id(ast::Name::exact(name));
3036 });
3037 }
3038 if local_ambiguous {
3039 return false;
3040 }
3041 for scope in outer_scopes {
3042 let (source, ambiguous) = resolve_unqualified(scope, &col_norm);
3043 if let Some(source) = source {
3044 return apply_rename(source, &mut |name| {
3045 *expr = ast::Expr::Id(ast::Name::exact(name));
3046 });
3047 }
3048 if ambiguous {
3049 return false;
3050 }
3051 }
3052 }
3053 _ => {}
3054 }
3055 false
3056 }
3057
3058 fn resolve_unqualified<'a>(
3059 candidates: &'a [ViewSourceInfo],
3060 old_column_norm: &str,
3061 ) -> (Option<&'a ViewSourceInfo>, bool) {
3062 let mut matches = candidates
3063 .iter()
3064 .filter(|s| s.columns_before.contains(old_column_norm));
3065 let Some(first) = matches.next() else {
3066 return (None, false);
3067 };
3068 if matches.next().is_some() {
3069 return (None, true);
3070 }
3071 (Some(first), false)
3072 }
3073
3074 fn resolve_qualified<'a>(
3075 candidates: &'a [ViewSourceInfo],
3076 qualifier: &str,
3077 target_db_norm: &str,
3078 ) -> (Option<&'a ViewSourceInfo>, bool) {
3079 let mut matches = candidates.iter().filter(|s| {
3080 s.matches_qualifier(qualifier)
3081 && s.db_name.as_deref().is_none_or(|db| db == target_db_norm)
3082 });
3083 let Some(first) = matches.next() else {
3084 return (None, false);
3085 };
3086 if matches.next().is_some() {
3087 return (None, true);
3088 }
3089 (Some(first), false)
3090 }
3091
3092 fn rewrite_using_columns(
3093 cols: &mut [ast::Name],
3094 left_sources: &[ViewSourceInfo],
3095 right: &ViewSourceInfo,
3096 old_column_norm: &str,
3097 new_column: &str,
3098 ) -> bool {
3099 let mut changed = false;
3100 let left_map = left_sources
3101 .iter()
3102 .find_map(|source| source.rename_map.get(old_column_norm));
3103 let left_has_target = left_sources.iter().any(|source| source.is_target_table);
3104 let right_map = right.rename_map.get(old_column_norm);
3105 let should_rename =
3106 left_has_target || right.is_target_table || left_map.is_some() || right_map.is_some();
3107 if !should_rename {
3108 return false;
3109 }
3110 let replacement = left_map
3111 .or(right_map)
3112 .map(|s| s.as_str())
3113 .unwrap_or(new_column);
3114
3115 for col in cols {
3116 if col.as_str().eq_ignore_ascii_case(old_column_norm) {
3117 *col = ast::Name::exact(replacement.to_string());
3118 changed = true;
3119 }
3120 }
3121 changed
3122 }
3123
3124 fn select_output_columns(
3125 select: &ast::Select,
3126 ctx: &ViewRewriteCtx,
3127 apply_rename: bool,
3128 ) -> Result<Vec<String>> {
3129 let view_columns = extract_view_columns(select, ctx.schema)?;
3130 let mut columns = view_columns.columns;
3131 if apply_rename {
3132 let target_norm = ctx.target_table_norm.as_str();
3133 for view_column in &mut columns {
3134 if view_column.table_index == usize::MAX {
3135 continue;
3136 }
3137 let table = &view_columns.tables[view_column.table_index];
3138 if table_name_matches_target(
3139 &table.name,
3140 table.db_name.as_deref(),
3141 target_norm,
3142 &ctx.target_db_norm,
3143 ) {
3144 if let Some(ref mut name) = view_column.column.name {
3145 if name.as_str().eq_ignore_ascii_case(ctx.old_column) {
3146 *name = ctx.new_column.to_string();
3147 }
3148 }
3149 }
3150 }
3151 }
3152
3153 Ok(columns
3154 .into_iter()
3155 .map(|vc| vc.column.name.unwrap_or_else(|| "?".to_string()))
3156 .collect())
3157 }
3158
3159 fn apply_explicit_column_names(columns: &mut [String], explicit: &[ast::IndexedColumn]) {
3160 for (i, indexed_col) in explicit.iter().enumerate() {
3161 if let Some(col) = columns.get_mut(i) {
3162 *col = indexed_col.col_name.as_str().to_string();
3163 }
3164 }
3165 }
3166
3167 fn from_clause_output_columns(
3168 from_clause: &ast::FromClause,
3169 ctx: &ViewRewriteCtx,
3170 apply_rename: bool,
3171 ) -> Result<Vec<String>> {
3172 let dummy_select = ast::Select {
3173 with: None,
3174 body: ast::SelectBody {
3175 select: ast::OneSelect::Select {
3176 distinctness: None,
3177 columns: vec![ast::ResultColumn::Star],
3178 from: Some(from_clause.clone()),
3179 where_clause: None,
3180 group_by: None,
3181 window_clause: Vec::new(),
3182 },
3183 compounds: Vec::new(),
3184 },
3185 order_by: Vec::new(),
3186 limit: None,
3187 };
3188 select_output_columns(&dummy_select, ctx, apply_rename)
3189 }
3190
3191 fn build_rename_map(
3192 before_cols: &[String],
3193 after_cols: &[String],
3194 old_column_norm: &str,
3195 ) -> HashMap<String, String> {
3196 let mut map = HashMap::default();
3197 for (before, after) in before_cols.iter().zip(after_cols.iter()) {
3198 if before.as_str().eq_ignore_ascii_case(old_column_norm)
3199 && !after.as_str().eq_ignore_ascii_case(before.as_str())
3200 {
3201 map.insert(old_column_norm.to_string(), after.to_string());
3202 }
3203 }
3204 map
3205 }
3206
3207 fn build_rename_map_from_columns(
3208 before_cols: &[Column],
3209 after_cols: &[Column],
3210 old_column_norm: &str,
3211 ) -> HashMap<String, String> {
3212 if before_cols.len() != after_cols.len() {
3213 return HashMap::default();
3214 }
3215 let mut map = HashMap::default();
3216 for (before, after) in before_cols.iter().zip(after_cols.iter()) {
3217 let Some(before_name) = before.name.as_ref() else {
3218 continue;
3219 };
3220 let Some(after_name) = after.name.as_ref() else {
3221 continue;
3222 };
3223 if before_name.as_str().eq_ignore_ascii_case(old_column_norm)
3224 && !after_name
3225 .as_str()
3226 .eq_ignore_ascii_case(before_name.as_str())
3227 {
3228 map.insert(old_column_norm.to_string(), after_name.to_string());
3229 }
3230 }
3231 map
3232 }
3233
3234 fn table_name_matches_target(
3235 table_name: &str,
3236 table_db: Option<&str>,
3237 target_table_norm: &str,
3238 target_db_norm: &str,
3239 ) -> bool {
3240 if !table_name.eq_ignore_ascii_case(target_table_norm) {
3241 return false;
3242 }
3243 match table_db {
3244 None => true,
3245 Some(db) => db.eq_ignore_ascii_case(target_db_norm),
3246 }
3247 }
3248
3249 fn table_source_columns(schema: &Schema, table_name: &str) -> Option<Vec<String>> {
3250 if let Some(table) = schema.get_table(table_name) {
3251 return Some(
3252 table
3253 .columns()
3254 .iter()
3255 .filter_map(|col| col.name.clone())
3256 .collect(),
3257 );
3258 }
3259 let table_norm = normalize_ident(table_name);
3260 if let Some(view) = schema.views.get(&table_norm) {
3261 return Some(
3262 view.columns
3263 .iter()
3264 .filter_map(|col| col.name.clone())
3265 .collect(),
3266 );
3267 }
3268 None
3269 }
3270}
3271
3272pub use rename_column_view::{rewrite_view_sql_for_column_rename, RewrittenView};
3273
3274pub fn rewrite_check_expr_table_refs(expr: &mut ast::Expr, from: &str, to: &str) {
3279 let _ = walk_expr_mut(
3280 expr,
3281 &mut |e: &mut ast::Expr| -> crate::Result<WalkControl> {
3282 match e {
3283 ast::Expr::Qualified(tbl, col) => {
3284 if tbl.as_str().eq_ignore_ascii_case(from) {
3285 let col = col.clone();
3286 *e = ast::Expr::Qualified(ast::Name::exact(to.to_owned()), col);
3287 }
3288 }
3289 ast::Expr::Exists(select) | ast::Expr::Subquery(select) => {
3290 rewrite_select_table_refs(select, from, to);
3291 }
3292 ast::Expr::InSelect { rhs, .. } => {
3293 rewrite_select_table_refs(rhs, from, to);
3294 }
3295 ast::Expr::InTable { rhs, .. } => {
3296 if rhs.name.as_str().eq_ignore_ascii_case(from) {
3297 rhs.name = ast::Name::exact(to.to_owned());
3298 }
3299 }
3300 _ => {}
3301 }
3302 Ok(WalkControl::Continue)
3303 },
3304 );
3305}
3306
3307pub fn rewrite_column_references_if_needed(
3309 col: &mut ast::ColumnDefinition,
3310 table: &str,
3311 from: &str,
3312 to: &str,
3313) -> Result<()> {
3314 for cc in &mut col.constraints {
3315 match &mut cc.constraint {
3316 ast::ColumnConstraint::ForeignKey { clause, .. } => {
3317 rewrite_fk_parent_cols_if_self_ref(clause, table, from, to);
3318 }
3319 ast::ColumnConstraint::Check(expr) => {
3320 rename_identifiers(expr, from, to);
3321 }
3322 ast::ColumnConstraint::Generated { expr, .. } => {
3323 rename_identifiers(expr, from, to);
3324 }
3325 _ => {}
3326 }
3327 }
3328 Ok(())
3329}
3330
3331pub fn rewrite_column_level_fk_parent_columns_if_needed(
3335 col: &mut ast::ColumnDefinition,
3336 table: &str,
3337 from: &str,
3338 to: &str,
3339) {
3340 for cc in &mut col.constraints {
3341 if let ast::ColumnConstraint::ForeignKey { clause, .. } = &mut cc.constraint {
3342 rewrite_fk_parent_cols_if_self_ref(clause, table, from, to);
3343 }
3344 }
3345}
3346
3347pub fn rewrite_fk_parent_table_if_needed(
3349 clause: &mut ast::ForeignKeyClause,
3350 old_tbl: &str,
3351 new_tbl: &str,
3352) -> bool {
3353 if clause.tbl_name.as_str().eq_ignore_ascii_case(old_tbl) {
3354 clause.tbl_name = ast::Name::exact(new_tbl.to_owned());
3355 return true;
3356 }
3357 false
3358}
3359
3360pub fn rewrite_inline_col_fk_target_if_needed(
3362 col: &mut ast::ColumnDefinition,
3363 old_tbl: &str,
3364 new_tbl: &str,
3365) -> bool {
3366 let mut changed = false;
3367 for cc in &mut col.constraints {
3368 if let ast::NamedColumnConstraint {
3369 constraint: ast::ColumnConstraint::ForeignKey { clause, .. },
3370 ..
3371 } = cc
3372 {
3373 changed |= rewrite_fk_parent_table_if_needed(clause, old_tbl, new_tbl);
3374 }
3375 }
3376 changed
3377}
3378
3379pub fn rewrite_trigger_cmd_table_refs(cmd: &mut ast::TriggerCmd, old_tbl: &str, new_tbl: &str) {
3383 match cmd {
3384 ast::TriggerCmd::Update {
3385 tbl_name,
3386 sets,
3387 from,
3388 where_clause,
3389 ..
3390 } => {
3391 if tbl_name.as_str().eq_ignore_ascii_case(old_tbl) {
3392 *tbl_name = ast::Name::exact(new_tbl.to_owned());
3393 }
3394 for set in sets {
3395 rewrite_check_expr_table_refs(&mut set.expr, old_tbl, new_tbl);
3396 }
3397 if let Some(ref mut from) = from {
3398 rewrite_from_clause_table_refs(from, old_tbl, new_tbl);
3399 }
3400 if let Some(ref mut wc) = where_clause {
3401 rewrite_check_expr_table_refs(wc, old_tbl, new_tbl);
3402 }
3403 }
3404 ast::TriggerCmd::Insert {
3405 tbl_name,
3406 select,
3407 upsert,
3408 ..
3409 } => {
3410 if tbl_name.as_str().eq_ignore_ascii_case(old_tbl) {
3411 *tbl_name = ast::Name::exact(new_tbl.to_owned());
3412 }
3413 rewrite_select_table_refs(select, old_tbl, new_tbl);
3414 if let Some(ref mut upsert) = upsert {
3415 rewrite_upsert_table_refs(upsert, old_tbl, new_tbl);
3416 }
3417 }
3418 ast::TriggerCmd::Delete {
3419 tbl_name,
3420 where_clause,
3421 } => {
3422 if tbl_name.as_str().eq_ignore_ascii_case(old_tbl) {
3423 *tbl_name = ast::Name::exact(new_tbl.to_owned());
3424 }
3425 if let Some(ref mut wc) = where_clause {
3426 rewrite_check_expr_table_refs(wc, old_tbl, new_tbl);
3427 }
3428 }
3429 ast::TriggerCmd::Select(select) => {
3430 rewrite_select_table_refs(select, old_tbl, new_tbl);
3431 }
3432 }
3433}
3434
3435pub(crate) fn output_column_aliases(one_select: &ast::OneSelect) -> Vec<String> {
3440 let ast::OneSelect::Select { columns, .. } = one_select else {
3441 return Vec::new();
3442 };
3443 columns
3444 .iter()
3445 .filter_map(|col| match col {
3446 ast::ResultColumn::Expr(_, Some(alias)) => Some(normalize_ident(alias.name().as_str())),
3447 ast::ResultColumn::Expr(expr, None) => match expr.as_ref() {
3448 ast::Expr::Id(name) | ast::Expr::Name(name) => Some(normalize_ident(name.as_str())),
3449 ast::Expr::Qualified(_, col) | ast::Expr::DoublyQualified(_, _, col) => {
3450 Some(normalize_ident(col.as_str()))
3451 }
3452 _ => None,
3453 },
3454 _ => None,
3455 })
3456 .collect()
3457}
3458
3459pub(crate) fn is_order_by_alias_ref(
3468 body: &ast::SelectBody,
3469 expr: &ast::Expr,
3470 old_col: &str,
3471) -> bool {
3472 let name = match expr {
3473 ast::Expr::Id(n) | ast::Expr::Name(n) => n.as_str(),
3474 _ => return false,
3475 };
3476 if !name.eq_ignore_ascii_case(old_col) {
3477 return false;
3478 }
3479 one_select_has_explicit_alias(&body.select, name)
3480 || body
3481 .compounds
3482 .iter()
3483 .any(|c| one_select_has_explicit_alias(&c.select, name))
3484}
3485
3486fn one_select_has_explicit_alias(one_select: &ast::OneSelect, name: &str) -> bool {
3492 let ast::OneSelect::Select { columns, .. } = one_select else {
3493 return false;
3494 };
3495 columns.iter().any(|col| match col {
3496 ast::ResultColumn::Expr(_, Some(alias)) if alias.is_explicit() => {
3497 alias.name().as_str().eq_ignore_ascii_case(name)
3498 }
3499 _ => false,
3500 })
3501}
3502
3503fn rewrite_select_column_refs_scoped(
3505 select: &mut ast::Select,
3506 target_table: &str,
3507 trigger_table: &str,
3508 old_col: &str,
3509 new_col: &str,
3510 target_qualifiers: &mut Vec<String>,
3511) {
3512 if let Some(with_clause) = &mut select.with {
3513 for cte in &mut with_clause.ctes {
3514 rewrite_select_column_refs_scoped(
3515 &mut cte.select,
3516 target_table,
3517 trigger_table,
3518 old_col,
3519 new_col,
3520 target_qualifiers,
3521 );
3522 }
3523 }
3524
3525 rewrite_one_select_column_refs_scoped(
3526 &mut select.body.select,
3527 target_table,
3528 trigger_table,
3529 old_col,
3530 new_col,
3531 target_qualifiers,
3532 );
3533 for compound in &mut select.body.compounds {
3534 rewrite_one_select_column_refs_scoped(
3535 &mut compound.select,
3536 target_table,
3537 trigger_table,
3538 old_col,
3539 new_col,
3540 target_qualifiers,
3541 );
3542 }
3543 let added = match &select.body.select {
3545 ast::OneSelect::Select { from, .. } => {
3546 let local = from_clause_target_qualifiers(from, target_table);
3547 extend_qualifiers_scoped(target_qualifiers, &local)
3548 }
3549 _ => 0,
3550 };
3551 let rename_unqualified =
3552 !target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
3553 let body = &select.body;
3558 for col in &mut select.order_by {
3559 if is_order_by_alias_ref(body, &col.expr, old_col) {
3560 continue;
3561 }
3562 rename_identifiers_scoped_inner(
3563 &mut col.expr,
3564 target_table,
3565 trigger_table,
3566 old_col,
3567 new_col,
3568 rename_unqualified,
3569 Some(target_qualifiers),
3570 );
3571 }
3572 if let Some(limit) = &mut select.limit {
3573 rename_identifiers_scoped_inner(
3574 &mut limit.expr,
3575 target_table,
3576 trigger_table,
3577 old_col,
3578 new_col,
3579 rename_unqualified,
3580 Some(target_qualifiers),
3581 );
3582 if let Some(offset) = &mut limit.offset {
3583 rename_identifiers_scoped_inner(
3584 offset,
3585 target_table,
3586 trigger_table,
3587 old_col,
3588 new_col,
3589 rename_unqualified,
3590 Some(target_qualifiers),
3591 );
3592 }
3593 }
3594 target_qualifiers.truncate(target_qualifiers.len() - added);
3595}
3596
3597fn from_clause_target_qualifiers(
3598 from: &Option<ast::FromClause>,
3599 target_table: &str,
3600) -> Vec<String> {
3601 match from {
3602 Some(from_clause) => from_clause_target_qualifiers_inner(from_clause, target_table),
3603 None => Vec::new(),
3604 }
3605}
3606
3607fn from_clause_target_qualifiers_inner(
3608 from_clause: &ast::FromClause,
3609 target_table: &str,
3610) -> Vec<String> {
3611 let target_table = normalize_ident(target_table);
3612 let mut qualifiers = Vec::new();
3613 let mut seen = HashSet::default();
3614 collect_target_qualifiers(
3615 &from_clause.select,
3616 &target_table,
3617 &mut qualifiers,
3618 &mut seen,
3619 );
3620 for join in &from_clause.joins {
3621 collect_target_qualifiers(&join.table, &target_table, &mut qualifiers, &mut seen);
3622 }
3623 qualifiers
3624}
3625
3626fn collect_target_qualifiers(
3627 st: &ast::SelectTable,
3628 target_table: &str,
3629 qualifiers: &mut Vec<String>,
3630 seen: &mut HashSet<String>,
3631) {
3632 let ast::SelectTable::Table(name, alias, _) = st else {
3633 return;
3634 };
3635 if !name.name.as_str().eq_ignore_ascii_case(target_table) {
3636 return;
3637 }
3638
3639 if seen.insert(target_table.to_string()) {
3640 qualifiers.push(target_table.to_string());
3641 }
3642 if let Some(alias) = alias {
3643 let alias_norm = normalize_ident(alias.name().as_str());
3644 if seen.insert(alias_norm.clone()) {
3645 qualifiers.push(alias_norm);
3646 }
3647 }
3648}
3649
3650fn rewrite_one_select_column_refs_scoped(
3651 one: &mut ast::OneSelect,
3652 target_table: &str,
3653 trigger_table: &str,
3654 old_col: &str,
3655 new_col: &str,
3656 target_qualifiers: &mut Vec<String>,
3657) {
3658 match one {
3659 ast::OneSelect::Select {
3660 from,
3661 where_clause,
3662 columns,
3663 group_by,
3664 window_clause,
3665 ..
3666 } => {
3667 let local = from_clause_target_qualifiers(from, target_table);
3670 let added = extend_qualifiers_scoped(target_qualifiers, &local);
3671
3672 let rename_unqualified =
3673 !target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
3674
3675 if let Some(ref mut from) = from {
3676 rewrite_from_clause_column_refs_scoped(
3677 from,
3678 target_table,
3679 trigger_table,
3680 old_col,
3681 new_col,
3682 target_qualifiers,
3683 );
3684 }
3685 if let Some(ref mut wc) = where_clause {
3686 rename_identifiers_scoped_inner(
3687 wc,
3688 target_table,
3689 trigger_table,
3690 old_col,
3691 new_col,
3692 rename_unqualified,
3693 Some(target_qualifiers),
3694 );
3695 }
3696 for col in columns {
3697 if let ast::ResultColumn::Expr(ref mut expr, _) = col {
3698 rename_result_identifiers_scoped(
3699 expr,
3700 target_table,
3701 trigger_table,
3702 old_col,
3703 new_col,
3704 rename_unqualified,
3705 Some(target_qualifiers),
3706 );
3707 }
3708 }
3709 if let Some(ref mut gb) = group_by {
3710 for expr in &mut gb.exprs {
3711 rename_identifiers_scoped_inner(
3712 expr,
3713 target_table,
3714 trigger_table,
3715 old_col,
3716 new_col,
3717 rename_unqualified,
3718 Some(target_qualifiers),
3719 );
3720 }
3721 if let Some(ref mut having) = gb.having {
3722 rename_identifiers_scoped_inner(
3723 having,
3724 target_table,
3725 trigger_table,
3726 old_col,
3727 new_col,
3728 rename_unqualified,
3729 Some(target_qualifiers),
3730 );
3731 }
3732 }
3733 for window_def in window_clause {
3734 rewrite_window_column_refs_scoped(
3735 &mut window_def.window,
3736 target_table,
3737 trigger_table,
3738 old_col,
3739 new_col,
3740 target_qualifiers,
3741 );
3742 }
3743 target_qualifiers.truncate(target_qualifiers.len() - added);
3744 }
3745 ast::OneSelect::Values(rows) => {
3746 for row in rows {
3747 for expr in row {
3748 rename_identifiers_scoped(expr, target_table, trigger_table, old_col, new_col);
3749 }
3750 }
3751 }
3752 }
3753}
3754
3755fn rename_result_identifiers_scoped(
3756 expr: &mut ast::Expr,
3757 target_table: &str,
3758 trigger_table: &str,
3759 from: &str,
3760 to: &str,
3761 rename_unqualified: bool,
3762 target_qualifiers: Option<&[String]>,
3763) {
3764 let is_renaming_trigger_table = target_table.eq_ignore_ascii_case(trigger_table);
3765
3766 let _ = walk_expr_mut(
3767 expr,
3768 &mut |e: &mut ast::Expr| -> crate::Result<WalkControl> {
3769 match e {
3770 ast::Expr::Exists(_) => return Ok(WalkControl::SkipChildren),
3771 ast::Expr::Subquery(select) => {
3772 let mut quals = target_qualifiers.unwrap_or(&[]).to_vec();
3773 rewrite_select_column_refs_scoped(
3774 select,
3775 target_table,
3776 trigger_table,
3777 from,
3778 to,
3779 &mut quals,
3780 );
3781 }
3782 ast::Expr::InSelect { rhs, .. } => {
3783 let mut quals = target_qualifiers.unwrap_or(&[]).to_vec();
3784 rewrite_select_column_refs_scoped(
3785 rhs,
3786 target_table,
3787 trigger_table,
3788 from,
3789 to,
3790 &mut quals,
3791 );
3792 }
3793 ast::Expr::Id(ref name) | ast::Expr::Name(ref name)
3794 if rename_unqualified && name.as_str().eq_ignore_ascii_case(from) =>
3795 {
3796 *e = ast::Expr::Id(ast::Name::exact(to.to_owned()));
3797 }
3798 ast::Expr::Qualified(ref tbl, ref col_name)
3799 if col_name.as_str().eq_ignore_ascii_case(from) =>
3800 {
3801 let tbl_norm = normalize_ident(tbl.as_str());
3802 let should_rename = if tbl_norm == "new" || tbl_norm == "old" {
3803 is_renaming_trigger_table
3804 } else {
3805 target_qualifiers.is_some_and(|qualifiers| qualifiers.contains(&tbl_norm))
3806 || tbl_norm.eq_ignore_ascii_case(target_table)
3807 };
3808 if should_rename {
3809 let tbl = tbl.clone();
3810 *e = ast::Expr::Qualified(tbl, ast::Name::exact(to.to_owned()));
3811 }
3812 }
3813 _ => {}
3814 }
3815 Ok(WalkControl::Continue)
3816 },
3817 );
3818}
3819
3820fn rewrite_window_column_refs_scoped(
3821 window: &mut ast::Window,
3822 target_table: &str,
3823 trigger_table: &str,
3824 old_col: &str,
3825 new_col: &str,
3826 visible_target_qualifiers: &[String],
3827) {
3828 let rename_unqualified =
3829 !visible_target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
3830
3831 for expr in &mut window.partition_by {
3832 rename_identifiers_scoped_inner(
3833 expr,
3834 target_table,
3835 trigger_table,
3836 old_col,
3837 new_col,
3838 rename_unqualified,
3839 Some(visible_target_qualifiers),
3840 );
3841 }
3842 for sorted_col in &mut window.order_by {
3843 rename_identifiers_scoped_inner(
3844 &mut sorted_col.expr,
3845 target_table,
3846 trigger_table,
3847 old_col,
3848 new_col,
3849 rename_unqualified,
3850 Some(visible_target_qualifiers),
3851 );
3852 }
3853}
3854
3855fn rewrite_from_clause_column_refs_scoped(
3856 from: &mut ast::FromClause,
3857 target_table: &str,
3858 trigger_table: &str,
3859 old_col: &str,
3860 new_col: &str,
3861 target_qualifiers: &mut Vec<String>,
3862) {
3863 let local = from_clause_target_qualifiers_inner(from, target_table);
3864 let added = extend_qualifiers_scoped(target_qualifiers, &local);
3865
3866 let rename_unqualified =
3867 !target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
3868
3869 rewrite_select_table_entry_column_refs_scoped(
3870 &mut from.select,
3871 target_table,
3872 trigger_table,
3873 old_col,
3874 new_col,
3875 target_qualifiers,
3876 );
3877 for join in &mut from.joins {
3878 rewrite_select_table_entry_column_refs_scoped(
3879 &mut join.table,
3880 target_table,
3881 trigger_table,
3882 old_col,
3883 new_col,
3884 target_qualifiers,
3885 );
3886 if let Some(ast::JoinConstraint::On(ref mut expr)) = join.constraint {
3887 rename_identifiers_scoped_inner(
3888 expr,
3889 target_table,
3890 trigger_table,
3891 old_col,
3892 new_col,
3893 rename_unqualified,
3894 Some(target_qualifiers),
3895 );
3896 }
3897 }
3898 target_qualifiers.truncate(target_qualifiers.len() - added);
3899}
3900
3901fn rewrite_select_table_entry_column_refs_scoped(
3902 st: &mut ast::SelectTable,
3903 target_table: &str,
3904 trigger_table: &str,
3905 old_col: &str,
3906 new_col: &str,
3907 target_qualifiers: &mut Vec<String>,
3908) {
3909 match st {
3910 ast::SelectTable::TableCall(_, ref mut args, _) => {
3911 for arg in args {
3912 rename_identifiers_scoped_inner(
3913 arg,
3914 target_table,
3915 trigger_table,
3916 old_col,
3917 new_col,
3918 true,
3919 Some(target_qualifiers),
3920 );
3921 }
3922 }
3923 ast::SelectTable::Select(ref mut select, _) => {
3924 rewrite_select_column_refs_scoped(
3925 select,
3926 target_table,
3927 trigger_table,
3928 old_col,
3929 new_col,
3930 target_qualifiers,
3931 );
3932 }
3933 ast::SelectTable::Sub(ref mut from, _) => {
3934 rewrite_from_clause_column_refs_scoped(
3935 from,
3936 target_table,
3937 trigger_table,
3938 old_col,
3939 new_col,
3940 target_qualifiers,
3941 );
3942 }
3943 ast::SelectTable::Table(..) => {}
3944 }
3945}
3946
3947fn extend_qualifiers_scoped(qualifiers: &mut Vec<String>, local: &[String]) -> usize {
3950 let before = qualifiers.len();
3951 for qualifier in local {
3952 if !qualifiers.iter().any(|q| q == qualifier) {
3953 qualifiers.push(qualifier.clone());
3954 }
3955 }
3956 qualifiers.len() - before
3957}
3958
3959fn expr_still_references_renamed_column(
3960 expr: &ast::Expr,
3961 target_table: &str,
3962 trigger_table: &str,
3963 old_col: &str,
3964 rename_unqualified: bool,
3965 visible_target_qualifiers: &[String],
3966) -> bool {
3967 let mut found = false;
3968
3969 let _ = walk_expr(expr, &mut |e: &ast::Expr| -> crate::Result<WalkControl> {
3970 if found {
3971 return Ok(WalkControl::Continue);
3972 }
3973
3974 match e {
3975 ast::Expr::Subquery(select) | ast::Expr::Exists(select) => {
3976 let mut quals = visible_target_qualifiers.to_vec();
3977 found = select_still_references_renamed_column(
3978 select,
3979 target_table,
3980 trigger_table,
3981 old_col,
3982 &mut quals,
3983 );
3984 }
3985 ast::Expr::InSelect { rhs, .. } => {
3986 let mut quals = visible_target_qualifiers.to_vec();
3987 found = select_still_references_renamed_column(
3988 rhs,
3989 target_table,
3990 trigger_table,
3991 old_col,
3992 &mut quals,
3993 );
3994 }
3995 ast::Expr::Qualified(ns, col) | ast::Expr::DoublyQualified(_, ns, col) => {
3996 if col.as_str().eq_ignore_ascii_case(old_col) {
3997 let ns_norm = normalize_ident(ns.as_str());
3998 if ((ns_norm == "new" || ns_norm == "old")
3999 && target_table.eq_ignore_ascii_case(trigger_table))
4000 || visible_target_qualifiers.contains(&ns_norm)
4001 || (target_table.eq_ignore_ascii_case(trigger_table)
4002 && ns_norm.eq_ignore_ascii_case(trigger_table))
4003 {
4004 found = true;
4005 }
4006 }
4007 }
4008 ast::Expr::Id(name) | ast::Expr::Name(name) => {
4009 if rename_unqualified && name.as_str().eq_ignore_ascii_case(old_col) {
4010 found = true;
4011 }
4012 }
4013 _ => {}
4014 }
4015 Ok(WalkControl::Continue)
4016 });
4017
4018 found
4019}
4020
4021fn one_select_still_references_renamed_column(
4022 one: &ast::OneSelect,
4023 target_table: &str,
4024 trigger_table: &str,
4025 old_col: &str,
4026 target_qualifiers: &mut Vec<String>,
4027) -> bool {
4028 match one {
4029 ast::OneSelect::Select {
4030 from,
4031 where_clause,
4032 columns,
4033 group_by,
4034 ..
4035 } => {
4036 let local = from_clause_target_qualifiers(from, target_table);
4037 let added = extend_qualifiers_scoped(target_qualifiers, &local);
4038
4039 let rename_unqualified =
4040 !target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
4041
4042 let mut found = false;
4043 if let Some(from_clause) = from {
4044 if from_clause_still_references_renamed_column(
4045 from_clause,
4046 target_table,
4047 trigger_table,
4048 old_col,
4049 target_qualifiers,
4050 ) {
4051 found = true;
4052 }
4053 }
4054
4055 if !found {
4056 if let Some(where_expr) = where_clause {
4057 if expr_still_references_renamed_column(
4058 where_expr,
4059 target_table,
4060 trigger_table,
4061 old_col,
4062 rename_unqualified,
4063 target_qualifiers,
4064 ) {
4065 found = true;
4066 }
4067 }
4068 }
4069
4070 if !found {
4071 for col in columns {
4072 if let ast::ResultColumn::Expr(expr, _) = col {
4073 if expr_still_references_renamed_column(
4074 expr,
4075 target_table,
4076 trigger_table,
4077 old_col,
4078 rename_unqualified,
4079 target_qualifiers,
4080 ) {
4081 found = true;
4082 break;
4083 }
4084 }
4085 }
4086 }
4087
4088 if !found {
4089 if let Some(group_by) = group_by {
4090 for expr in &group_by.exprs {
4091 if expr_still_references_renamed_column(
4092 expr,
4093 target_table,
4094 trigger_table,
4095 old_col,
4096 rename_unqualified,
4097 target_qualifiers,
4098 ) {
4099 found = true;
4100 break;
4101 }
4102 }
4103 if !found {
4104 if let Some(having) = &group_by.having {
4105 if expr_still_references_renamed_column(
4106 having,
4107 target_table,
4108 trigger_table,
4109 old_col,
4110 rename_unqualified,
4111 target_qualifiers,
4112 ) {
4113 found = true;
4114 }
4115 }
4116 }
4117 }
4118 }
4119
4120 target_qualifiers.truncate(target_qualifiers.len() - added);
4121 found
4122 }
4123 ast::OneSelect::Values(rows) => {
4124 for row in rows {
4125 for expr in row {
4126 if expr_still_references_renamed_column(
4127 expr,
4128 target_table,
4129 trigger_table,
4130 old_col,
4131 true,
4132 target_qualifiers,
4133 ) {
4134 return true;
4135 }
4136 }
4137 }
4138 false
4139 }
4140 }
4141}
4142
4143fn select_still_references_renamed_column(
4144 select: &ast::Select,
4145 target_table: &str,
4146 trigger_table: &str,
4147 old_col: &str,
4148 target_qualifiers: &mut Vec<String>,
4149) -> bool {
4150 if let Some(with_clause) = &select.with {
4151 for cte in &with_clause.ctes {
4152 if select_still_references_renamed_column(
4153 &cte.select,
4154 target_table,
4155 trigger_table,
4156 old_col,
4157 target_qualifiers,
4158 ) {
4159 return true;
4160 }
4161 }
4162 }
4163
4164 if one_select_still_references_renamed_column(
4165 &select.body.select,
4166 target_table,
4167 trigger_table,
4168 old_col,
4169 target_qualifiers,
4170 ) {
4171 return true;
4172 }
4173
4174 for compound in &select.body.compounds {
4175 if one_select_still_references_renamed_column(
4176 &compound.select,
4177 target_table,
4178 trigger_table,
4179 old_col,
4180 target_qualifiers,
4181 ) {
4182 return true;
4183 }
4184 }
4185
4186 let added = match &select.body.select {
4188 ast::OneSelect::Select { from, .. } => {
4189 let local = from_clause_target_qualifiers(from, target_table);
4190 extend_qualifiers_scoped(target_qualifiers, &local)
4191 }
4192 _ => 0,
4193 };
4194 let rename_unqualified =
4195 !target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
4196
4197 let body = &select.body;
4201
4202 let mut found = false;
4203 for sorted_col in &select.order_by {
4204 if is_order_by_alias_ref(body, &sorted_col.expr, old_col) {
4205 continue;
4206 }
4207 if expr_still_references_renamed_column(
4208 &sorted_col.expr,
4209 target_table,
4210 trigger_table,
4211 old_col,
4212 rename_unqualified,
4213 target_qualifiers,
4214 ) {
4215 found = true;
4216 break;
4217 }
4218 }
4219
4220 if !found {
4221 if let Some(limit) = &select.limit {
4222 if expr_still_references_renamed_column(
4223 &limit.expr,
4224 target_table,
4225 trigger_table,
4226 old_col,
4227 rename_unqualified,
4228 target_qualifiers,
4229 ) {
4230 found = true;
4231 }
4232 if !found {
4233 if let Some(offset) = &limit.offset {
4234 if expr_still_references_renamed_column(
4235 offset,
4236 target_table,
4237 trigger_table,
4238 old_col,
4239 rename_unqualified,
4240 target_qualifiers,
4241 ) {
4242 found = true;
4243 }
4244 }
4245 }
4246 }
4247 }
4248
4249 target_qualifiers.truncate(target_qualifiers.len() - added);
4250 found
4251}
4252
4253fn select_table_still_references_renamed_column(
4254 st: &ast::SelectTable,
4255 target_table: &str,
4256 trigger_table: &str,
4257 old_col: &str,
4258 target_qualifiers: &mut Vec<String>,
4259) -> bool {
4260 match st {
4261 ast::SelectTable::TableCall(_, args, _) => args.iter().any(|arg| {
4262 expr_still_references_renamed_column(
4263 arg,
4264 target_table,
4265 trigger_table,
4266 old_col,
4267 true,
4268 target_qualifiers,
4269 )
4270 }),
4271 ast::SelectTable::Select(select, _) => select_still_references_renamed_column(
4272 select,
4273 target_table,
4274 trigger_table,
4275 old_col,
4276 target_qualifiers,
4277 ),
4278 ast::SelectTable::Sub(from, _) => from_clause_still_references_renamed_column(
4279 from,
4280 target_table,
4281 trigger_table,
4282 old_col,
4283 target_qualifiers,
4284 ),
4285 ast::SelectTable::Table(..) => false,
4286 }
4287}
4288
4289fn from_clause_still_references_renamed_column(
4290 from: &ast::FromClause,
4291 target_table: &str,
4292 trigger_table: &str,
4293 old_col: &str,
4294 target_qualifiers: &mut Vec<String>,
4295) -> bool {
4296 let local = from_clause_target_qualifiers_inner(from, target_table);
4297 let added = extend_qualifiers_scoped(target_qualifiers, &local);
4298
4299 let rename_unqualified =
4300 !target_qualifiers.is_empty() || target_table.eq_ignore_ascii_case(trigger_table);
4301
4302 let mut found = false;
4303 if select_table_still_references_renamed_column(
4304 &from.select,
4305 target_table,
4306 trigger_table,
4307 old_col,
4308 target_qualifiers,
4309 ) {
4310 found = true;
4311 }
4312
4313 if !found {
4314 for join in &from.joins {
4315 if select_table_still_references_renamed_column(
4316 &join.table,
4317 target_table,
4318 trigger_table,
4319 old_col,
4320 target_qualifiers,
4321 ) {
4322 found = true;
4323 break;
4324 }
4325 if let Some(ast::JoinConstraint::On(expr)) = &join.constraint {
4326 if expr_still_references_renamed_column(
4327 expr,
4328 target_table,
4329 trigger_table,
4330 old_col,
4331 rename_unqualified,
4332 target_qualifiers,
4333 ) {
4334 found = true;
4335 break;
4336 }
4337 }
4338 }
4339 }
4340
4341 target_qualifiers.truncate(target_qualifiers.len() - added);
4342 found
4343}
4344
4345pub fn trigger_still_references_renamed_column(
4346 trigger: &crate::schema::Trigger,
4347 target_table: &str,
4348 old_col: &str,
4349) -> bool {
4350 if trigger.table_name.eq_ignore_ascii_case(target_table) {
4351 if let ast::TriggerEvent::UpdateOf(cols) = &trigger.event {
4352 if cols
4353 .iter()
4354 .any(|col| col.as_str().eq_ignore_ascii_case(old_col))
4355 {
4356 return true;
4357 }
4358 }
4359 }
4360
4361 if let Some(when_clause) = &trigger.when_clause {
4362 if expr_still_references_renamed_column(
4363 when_clause,
4364 target_table,
4365 &trigger.table_name,
4366 old_col,
4367 false,
4368 &[],
4369 ) {
4370 return true;
4371 }
4372 }
4373
4374 for cmd in &trigger.commands {
4375 match cmd {
4376 ast::TriggerCmd::Update {
4377 tbl_name,
4378 sets,
4379 from,
4380 where_clause,
4381 ..
4382 } => {
4383 let targets_renamed_table = tbl_name.as_str().eq_ignore_ascii_case(target_table);
4384 let visible_target_qualifiers = from_clause_target_qualifiers(from, target_table);
4385
4386 if targets_renamed_table
4387 && sets.iter().any(|set| {
4388 set.col_names
4389 .iter()
4390 .any(|col_name| col_name.as_str().eq_ignore_ascii_case(old_col))
4391 })
4392 {
4393 return true;
4394 }
4395
4396 for set in sets {
4397 if expr_still_references_renamed_column(
4398 &set.expr,
4399 target_table,
4400 &trigger.table_name,
4401 old_col,
4402 targets_renamed_table,
4403 &visible_target_qualifiers,
4404 ) {
4405 return true;
4406 }
4407 }
4408
4409 if let Some(where_clause) = where_clause {
4410 if expr_still_references_renamed_column(
4411 where_clause,
4412 target_table,
4413 &trigger.table_name,
4414 old_col,
4415 targets_renamed_table,
4416 &visible_target_qualifiers,
4417 ) {
4418 return true;
4419 }
4420 }
4421
4422 if let Some(from_clause) = from {
4423 if from_clause_still_references_renamed_column(
4424 from_clause,
4425 target_table,
4426 &trigger.table_name,
4427 old_col,
4428 &mut Vec::new(),
4429 ) {
4430 return true;
4431 }
4432 }
4433 }
4434 ast::TriggerCmd::Insert {
4435 tbl_name,
4436 col_names,
4437 select,
4438 upsert,
4439 ..
4440 } => {
4441 if tbl_name.as_str().eq_ignore_ascii_case(target_table)
4442 && col_names
4443 .iter()
4444 .any(|col_name| col_name.as_str().eq_ignore_ascii_case(old_col))
4445 {
4446 return true;
4447 }
4448
4449 if select_still_references_renamed_column(
4450 select,
4451 target_table,
4452 &trigger.table_name,
4453 old_col,
4454 &mut Vec::new(),
4455 ) {
4456 return true;
4457 }
4458
4459 if let Some(upsert) = upsert {
4460 if let Some(index) = &upsert.index {
4461 for target in &index.targets {
4462 if expr_still_references_renamed_column(
4463 &target.expr,
4464 target_table,
4465 &trigger.table_name,
4466 old_col,
4467 tbl_name.as_str().eq_ignore_ascii_case(target_table),
4468 &[],
4469 ) {
4470 return true;
4471 }
4472 }
4473 if let Some(where_clause) = &index.where_clause {
4474 if expr_still_references_renamed_column(
4475 where_clause,
4476 target_table,
4477 &trigger.table_name,
4478 old_col,
4479 tbl_name.as_str().eq_ignore_ascii_case(target_table),
4480 &[],
4481 ) {
4482 return true;
4483 }
4484 }
4485 }
4486 }
4487 }
4488 ast::TriggerCmd::Delete {
4489 tbl_name,
4490 where_clause,
4491 } => {
4492 if let Some(where_clause) = where_clause {
4493 if expr_still_references_renamed_column(
4494 where_clause,
4495 target_table,
4496 &trigger.table_name,
4497 old_col,
4498 tbl_name.as_str().eq_ignore_ascii_case(target_table),
4499 &[],
4500 ) {
4501 return true;
4502 }
4503 }
4504 }
4505 ast::TriggerCmd::Select(select) => {
4506 if select_still_references_renamed_column(
4507 select,
4508 target_table,
4509 &trigger.table_name,
4510 old_col,
4511 &mut Vec::new(),
4512 ) {
4513 return true;
4514 }
4515 }
4516 }
4517 }
4518
4519 false
4520}
4521
4522fn rename_excluded_column_refs(expr: &mut ast::Expr, old_col: &str, new_col: &str) {
4523 let _ = walk_expr_mut(
4524 expr,
4525 &mut |e: &mut ast::Expr| -> crate::Result<WalkControl> {
4526 if let ast::Expr::Qualified(ns, col) | ast::Expr::DoublyQualified(_, ns, col) = e {
4527 if ns.as_str().eq_ignore_ascii_case("excluded")
4528 && col.as_str().eq_ignore_ascii_case(old_col)
4529 {
4530 *col = ast::Name::exact(new_col.to_owned());
4531 }
4532 }
4533 Ok(WalkControl::Continue)
4534 },
4535 );
4536}
4537
4538fn rewrite_upsert_column_refs_scoped(
4539 upsert: &mut ast::Upsert,
4540 table: &str,
4541 trigger_table: &str,
4542 insert_table: &str,
4543 old_col: &str,
4544 new_col: &str,
4545) {
4546 let insert_targets_renamed_table = insert_table.eq_ignore_ascii_case(table);
4547 let rewrite_expr = |expr: &mut ast::Expr| {
4548 if insert_targets_renamed_table {
4549 rename_identifiers_scoped(expr, table, trigger_table, old_col, new_col);
4550 rename_excluded_column_refs(expr, old_col, new_col);
4551 } else {
4552 rename_identifiers_scoped_when_clause(expr, table, trigger_table, old_col, new_col);
4553 }
4554 };
4555
4556 if let Some(ref mut index) = upsert.index {
4557 for target in &mut index.targets {
4558 rewrite_expr(&mut target.expr);
4559 }
4560 if let Some(ref mut wc) = index.where_clause {
4561 rewrite_expr(wc);
4562 }
4563 }
4564 if let ast::UpsertDo::Set {
4565 ref mut sets,
4566 ref mut where_clause,
4567 } = upsert.do_clause
4568 {
4569 for set in sets {
4570 if insert_targets_renamed_table {
4571 for col_name in &mut set.col_names {
4572 if col_name.as_str().eq_ignore_ascii_case(old_col) {
4573 *col_name = ast::Name::exact(new_col.to_owned());
4574 }
4575 }
4576 }
4577 rewrite_expr(&mut set.expr);
4578 }
4579 if let Some(ref mut wc) = where_clause {
4580 rewrite_expr(wc);
4581 }
4582 }
4583 if let Some(ref mut next) = upsert.next {
4584 rewrite_upsert_column_refs_scoped(
4585 next,
4586 table,
4587 trigger_table,
4588 insert_table,
4589 old_col,
4590 new_col,
4591 );
4592 }
4593}
4594
4595pub fn rewrite_trigger_cmd_column_refs(
4599 cmd: &mut ast::TriggerCmd,
4600 table: &str,
4601 trigger_table: &str,
4602 old_col: &str,
4603 new_col: &str,
4604) {
4605 match cmd {
4606 ast::TriggerCmd::Update {
4607 tbl_name,
4608 sets,
4609 from,
4610 where_clause,
4611 ..
4612 } => {
4613 let targets_renamed_table = tbl_name.as_str().eq_ignore_ascii_case(table);
4614 if targets_renamed_table {
4615 for set in sets {
4616 for col_name in &mut set.col_names {
4617 if col_name.as_str().eq_ignore_ascii_case(old_col) {
4618 *col_name = ast::Name::exact(new_col.to_owned());
4619 }
4620 }
4621 rename_identifiers_scoped(
4622 &mut set.expr,
4623 table,
4624 trigger_table,
4625 old_col,
4626 new_col,
4627 );
4628 }
4629 if let Some(ref mut wc) = where_clause {
4630 rename_identifiers_scoped(wc, table, trigger_table, old_col, new_col);
4631 }
4632 } else {
4633 for set in sets {
4634 rename_identifiers_scoped_when_clause(
4635 &mut set.expr,
4636 table,
4637 trigger_table,
4638 old_col,
4639 new_col,
4640 );
4641 }
4642 if let Some(ref mut wc) = where_clause {
4643 rename_identifiers_scoped_when_clause(
4644 wc,
4645 table,
4646 trigger_table,
4647 old_col,
4648 new_col,
4649 );
4650 }
4651 }
4652 if let Some(ref mut from) = from {
4653 rewrite_from_clause_column_refs_scoped(
4654 from,
4655 table,
4656 trigger_table,
4657 old_col,
4658 new_col,
4659 &mut Vec::new(),
4660 );
4661 }
4662 }
4663 ast::TriggerCmd::Insert {
4664 tbl_name,
4665 col_names,
4666 select,
4667 upsert,
4668 ..
4669 } => {
4670 let targets_renamed_table = tbl_name.as_str().eq_ignore_ascii_case(table);
4671 if targets_renamed_table {
4672 for col_name in col_names {
4673 if col_name.as_str().eq_ignore_ascii_case(old_col) {
4674 *col_name = ast::Name::exact(new_col.to_owned());
4675 }
4676 }
4677 }
4678 rewrite_select_column_refs_scoped(
4679 select,
4680 table,
4681 trigger_table,
4682 old_col,
4683 new_col,
4684 &mut Vec::new(),
4685 );
4686 if let Some(ref mut upsert) = upsert {
4687 rewrite_upsert_column_refs_scoped(
4688 upsert,
4689 table,
4690 trigger_table,
4691 tbl_name.as_str(),
4692 old_col,
4693 new_col,
4694 );
4695 }
4696 }
4697 ast::TriggerCmd::Delete {
4698 tbl_name,
4699 where_clause,
4700 } => {
4701 let targets_renamed_table = tbl_name.as_str().eq_ignore_ascii_case(table);
4702 if targets_renamed_table {
4703 if let Some(ref mut wc) = where_clause {
4704 rename_identifiers_scoped(wc, table, trigger_table, old_col, new_col);
4705 }
4706 } else if let Some(ref mut wc) = where_clause {
4707 rename_identifiers_scoped_when_clause(wc, table, trigger_table, old_col, new_col);
4708 }
4709 }
4710 ast::TriggerCmd::Select(select) => {
4711 rewrite_select_column_refs_scoped(
4712 select,
4713 table,
4714 trigger_table,
4715 old_col,
4716 new_col,
4717 &mut Vec::new(),
4718 );
4719 }
4720 }
4721}
4722
4723fn rewrite_select_table_refs(select: &mut ast::Select, old_tbl: &str, new_tbl: &str) {
4724 if let Some(with_clause) = &mut select.with {
4725 for cte in &mut with_clause.ctes {
4726 rewrite_select_table_refs(&mut cte.select, old_tbl, new_tbl);
4727 }
4728 }
4729 rewrite_one_select_table_refs(&mut select.body.select, old_tbl, new_tbl);
4730 for compound in &mut select.body.compounds {
4731 rewrite_one_select_table_refs(&mut compound.select, old_tbl, new_tbl);
4732 }
4733 for col in &mut select.order_by {
4734 rewrite_check_expr_table_refs(&mut col.expr, old_tbl, new_tbl);
4735 }
4736}
4737
4738fn rewrite_one_select_table_refs(one: &mut ast::OneSelect, old_tbl: &str, new_tbl: &str) {
4739 match one {
4740 ast::OneSelect::Select {
4741 from,
4742 where_clause,
4743 columns,
4744 group_by,
4745 ..
4746 } => {
4747 if let Some(ref mut from) = from {
4748 rewrite_from_clause_table_refs(from, old_tbl, new_tbl);
4749 }
4750 if let Some(ref mut wc) = where_clause {
4751 rewrite_check_expr_table_refs(wc, old_tbl, new_tbl);
4752 }
4753 for col in columns {
4754 match col {
4755 ast::ResultColumn::Expr(ref mut expr, _) => {
4756 rewrite_check_expr_table_refs(expr, old_tbl, new_tbl);
4757 }
4758 ast::ResultColumn::TableStar(ref mut name) => {
4759 if name.as_str().eq_ignore_ascii_case(old_tbl) {
4760 *name = ast::Name::exact(new_tbl.to_owned());
4761 }
4762 }
4763 ast::ResultColumn::Star => {}
4764 }
4765 }
4766 if let Some(ref mut gb) = group_by {
4767 for expr in &mut gb.exprs {
4768 rewrite_check_expr_table_refs(expr, old_tbl, new_tbl);
4769 }
4770 if let Some(ref mut having) = gb.having {
4771 rewrite_check_expr_table_refs(having, old_tbl, new_tbl);
4772 }
4773 }
4774 }
4775 ast::OneSelect::Values(rows) => {
4776 for row in rows {
4777 for expr in row {
4778 rewrite_check_expr_table_refs(expr, old_tbl, new_tbl);
4779 }
4780 }
4781 }
4782 }
4783}
4784
4785fn rewrite_from_clause_table_refs(from: &mut ast::FromClause, old_tbl: &str, new_tbl: &str) {
4786 rewrite_select_table_entry_table_refs(&mut from.select, old_tbl, new_tbl);
4787 for join in &mut from.joins {
4788 rewrite_select_table_entry_table_refs(&mut join.table, old_tbl, new_tbl);
4789 if let Some(ast::JoinConstraint::On(ref mut expr)) = join.constraint {
4790 rewrite_check_expr_table_refs(expr, old_tbl, new_tbl);
4791 }
4792 }
4793}
4794
4795fn rewrite_select_table_entry_table_refs(st: &mut ast::SelectTable, old_tbl: &str, new_tbl: &str) {
4796 match st {
4797 ast::SelectTable::Table(ref mut name, _, _) => {
4798 if name.name.as_str().eq_ignore_ascii_case(old_tbl) {
4799 name.name = ast::Name::exact(new_tbl.to_owned());
4800 }
4801 }
4802 ast::SelectTable::TableCall(ref mut name, ref mut args, _) => {
4803 if name.name.as_str().eq_ignore_ascii_case(old_tbl) {
4804 name.name = ast::Name::exact(new_tbl.to_owned());
4805 }
4806 for arg in args {
4807 rewrite_check_expr_table_refs(arg, old_tbl, new_tbl);
4808 }
4809 }
4810 ast::SelectTable::Select(ref mut select, _) => {
4811 rewrite_select_table_refs(select, old_tbl, new_tbl);
4812 }
4813 ast::SelectTable::Sub(ref mut from, _) => {
4814 rewrite_from_clause_table_refs(from, old_tbl, new_tbl);
4815 }
4816 }
4817}
4818
4819fn rewrite_upsert_table_refs(upsert: &mut ast::Upsert, old_tbl: &str, new_tbl: &str) {
4820 if let Some(ref mut index) = upsert.index {
4821 if let Some(ref mut wc) = index.where_clause {
4822 rewrite_check_expr_table_refs(wc, old_tbl, new_tbl);
4823 }
4824 }
4825 if let ast::UpsertDo::Set {
4826 ref mut sets,
4827 ref mut where_clause,
4828 } = upsert.do_clause
4829 {
4830 for set in sets {
4831 rewrite_check_expr_table_refs(&mut set.expr, old_tbl, new_tbl);
4832 }
4833 if let Some(ref mut wc) = where_clause {
4834 rewrite_check_expr_table_refs(wc, old_tbl, new_tbl);
4835 }
4836 }
4837 if let Some(ref mut next) = upsert.next {
4838 rewrite_upsert_table_refs(next, old_tbl, new_tbl);
4839 }
4840}
4841
4842#[cfg(clt_turso_tests)]
4843pub mod tests {
4844 use super::*;
4845 use crate::schema::{BTreeTable, Type as SchemaValueType};
4846 use turso_parser::ast::{self, Expr, FunctionTail, Literal, Name, Operator::*, Type, Variable};
4847 use turso_parser::parser::Parser;
4848
4849 #[test]
4850 fn test_normalize_ident() {
4851 assert_eq!(normalize_ident("foo"), "foo");
4852 assert_eq!(normalize_ident("FOO"), "foo");
4853 assert_eq!(normalize_ident("ὈΔΥΣΣΕΎΣ"), "ὈΔΥΣΣΕΎΣ");
4855 assert_eq!(normalize_ident("Foo_ΔΥΣ"), "foo_ΔΥΣ");
4856 }
4857
4858 fn schema_with_tables(create_table_sqls: &[&str]) -> Schema {
4859 let mut schema = Schema::new();
4860 for (index, create_table_sql) in create_table_sqls.iter().enumerate() {
4861 let root_page = i64::try_from(index).expect("test table index should fit in i64") + 2;
4862 let table = BTreeTable::from_sql(create_table_sql, root_page)
4863 .expect("test CREATE TABLE should parse");
4864 schema
4865 .add_btree_table(std::sync::Arc::new(table))
4866 .expect("test table should be added to schema");
4867 }
4868
4869 schema
4870 }
4871
4872 fn schema_with_table(create_table_sql: &str) -> Schema {
4873 schema_with_tables(&[create_table_sql])
4874 }
4875
4876 fn parse_select_from(sql: &str) -> Option<ast::FromClause> {
4877 let mut parser = Parser::new(sql.as_bytes());
4878 let cmd = parser
4879 .next_cmd()
4880 .expect("test SQL should parse")
4881 .expect("test SQL should contain a statement");
4882 let ast::Cmd::Stmt(ast::Stmt::Select(select)) = cmd else {
4883 panic!("expected SELECT statement");
4884 };
4885 match select.body.select {
4886 ast::OneSelect::Select { from, .. } => from,
4887 _ => panic!("expected simple SELECT"),
4888 }
4889 }
4890
4891 #[test]
4892 fn test_rewrite_view_sql_select_table_branch() {
4893 let schema = schema_with_table("CREATE TABLE t (a, b)");
4894 let view_sql = "CREATE VIEW v AS SELECT s.x FROM (SELECT b AS x FROM t) AS s";
4895
4896 let rewritten =
4897 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
4898 .unwrap()
4899 .expect("view should be rewritten");
4900
4901 assert!(rewritten.sql.contains("SELECT c AS x FROM t"));
4902 }
4903
4904 #[test]
4905 fn test_rewrite_view_sql_sub_branch() {
4906 let schema = schema_with_table("CREATE TABLE t (a, b)");
4907 let view_sql = "CREATE VIEW v AS SELECT s.b FROM (t) AS s";
4908
4909 let rewritten =
4910 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
4911 .unwrap()
4912 .expect("view should be rewritten");
4913
4914 assert!(!rewritten.sql.contains("s.b"), "{}", rewritten.sql);
4915 }
4916
4917 #[test]
4918 fn test_rewrite_view_sql_table_call_branch() {
4919 let schema = schema_with_table("CREATE TABLE t (a, b)");
4920 let view_sql =
4921 "CREATE VIEW v AS SELECT j.value FROM t JOIN json_each(json_array(t.b)) AS j";
4922
4923 let rewritten =
4924 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
4925 .unwrap()
4926 .expect("view should be rewritten");
4927
4928 assert!(!rewritten.sql.contains("t.b"), "{}", rewritten.sql);
4929 }
4930
4931 #[test]
4932 fn test_rewrite_view_sql_compound_branch() {
4933 let schema = schema_with_table("CREATE TABLE t (a, b)");
4934 let view_sql = "CREATE VIEW v AS SELECT b FROM t UNION ALL SELECT b FROM t ORDER BY b";
4935
4936 let rewritten =
4937 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
4938 .unwrap()
4939 .expect("view should be rewritten");
4940
4941 assert_eq!(rewritten.sql.matches("SELECT c FROM t").count(), 2);
4942 assert!(!rewritten.sql.contains("ORDER BY b"), "{}", rewritten.sql);
4943 assert!(rewritten.sql.contains("ORDER BY c"), "{}", rewritten.sql);
4944 }
4945
4946 #[test]
4947 fn test_rewrite_view_sql_cte_branch() {
4948 let schema = schema_with_table("CREATE TABLE t (a, b)");
4949 let view_sql = "CREATE VIEW v AS WITH cte AS (SELECT b FROM t) SELECT b FROM cte";
4950
4951 let rewritten =
4952 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
4953 .unwrap()
4954 .expect("view should be rewritten");
4955
4956 assert!(
4957 rewritten.sql.contains("WITH cte AS (SELECT c FROM t)"),
4958 "{}",
4959 rewritten.sql
4960 );
4961 assert!(
4962 rewritten.sql.contains("SELECT c FROM cte"),
4963 "{}",
4964 rewritten.sql
4965 );
4966 }
4967
4968 #[test]
4969 fn test_rewrite_view_sql_cte_branch_with_explicit_columns() {
4970 let schema = schema_with_table("CREATE TABLE t (a, b)");
4971 let view_sql = "CREATE VIEW v AS WITH cte(x) AS (SELECT b FROM t) SELECT x FROM cte";
4972
4973 let rewritten =
4974 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
4975 .unwrap()
4976 .expect("view should be rewritten");
4977
4978 assert!(
4979 rewritten.sql.contains("WITH cte(x)") || rewritten.sql.contains("WITH cte (x)"),
4980 "{}",
4981 rewritten.sql
4982 );
4983 assert!(
4984 rewritten.sql.contains("AS (SELECT c FROM t)"),
4985 "{}",
4986 rewritten.sql
4987 );
4988 assert!(
4989 rewritten.sql.contains("SELECT x FROM cte"),
4990 "{}",
4991 rewritten.sql
4992 );
4993 }
4994
4995 #[test]
4996 fn test_rewrite_trigger_cmd_table_refs_cte_branch() {
4997 let sql = "CREATE TEMP TRIGGER trg AFTER INSERT ON temp.old BEGIN WITH cte AS (SELECT * FROM temp.old) SELECT * FROM cte; END";
4998 let mut parser = Parser::new(sql.as_bytes());
4999 let cmd = parser
5000 .next_cmd()
5001 .expect("trigger SQL should parse")
5002 .expect("trigger SQL should produce a statement");
5003 let ast::Cmd::Stmt(ast::Stmt::CreateTrigger { commands, .. }) = cmd else {
5004 panic!("expected CREATE TRIGGER statement");
5005 };
5006 let mut commands = commands;
5007 let ast::TriggerCmd::Select(select) = &mut commands[0] else {
5008 panic!("expected SELECT trigger command");
5009 };
5010
5011 rewrite_select_table_refs(select, "old", "new");
5012
5013 let Some(with_clause) = &select.with else {
5014 panic!("expected WITH clause");
5015 };
5016 let ast::OneSelect::Select {
5017 from: Some(from), ..
5018 } = &with_clause.ctes[0].select.body.select
5019 else {
5020 panic!("expected CTE SELECT core");
5021 };
5022 let ast::SelectTable::Table(tbl_name, _, _) = from.select.as_ref() else {
5023 panic!("expected CTE SELECT FROM table");
5024 };
5025
5026 assert_eq!(
5027 tbl_name.db_name.as_ref().map(ast::Name::as_str),
5028 Some("temp")
5029 );
5030 assert_eq!(tbl_name.name.as_str(), "new");
5031 }
5032
5033 #[test]
5034 fn test_from_clause_target_qualifiers_dedups_case_insensitively() {
5035 let from = parse_select_from("SELECT 1 FROM Target AS tgt, target AS TARGET");
5036 assert_eq!(
5037 from_clause_target_qualifiers(&from, "target"),
5038 vec!["target".to_string(), "tgt".to_string()]
5039 );
5040 }
5041
5042 #[test]
5043 fn test_extend_qualifiers_scoped_preserves_first_seen_order() {
5044 let mut qualifiers = vec!["target".to_string(), "outer_alias".to_string()];
5045 let local = vec![
5046 "outer_alias".to_string(),
5047 "local_alias".to_string(),
5048 "target".to_string(),
5049 ];
5050
5051 let added = extend_qualifiers_scoped(&mut qualifiers, &local);
5052 assert_eq!(
5053 qualifiers,
5054 vec![
5055 "target".to_string(),
5056 "outer_alias".to_string(),
5057 "local_alias".to_string(),
5058 ]
5059 );
5060 assert_eq!(added, 1);
5061 qualifiers.truncate(qualifiers.len() - added);
5062 assert_eq!(
5063 qualifiers,
5064 vec!["target".to_string(), "outer_alias".to_string(),]
5065 );
5066 }
5067
5068 #[test]
5069 fn test_rewrite_trigger_cmd_table_refs_expr_subquery_branch() {
5070 let sql = "CREATE TEMP TRIGGER trg AFTER INSERT ON temp.old BEGIN SELECT EXISTS(SELECT 1 FROM temp.old); END";
5071 let mut parser = Parser::new(sql.as_bytes());
5072 let cmd = parser
5073 .next_cmd()
5074 .expect("trigger SQL should parse")
5075 .expect("trigger SQL should produce a statement");
5076 let ast::Cmd::Stmt(ast::Stmt::CreateTrigger { commands, .. }) = cmd else {
5077 panic!("expected CREATE TRIGGER statement");
5078 };
5079 let mut commands = commands;
5080 let ast::TriggerCmd::Select(select) = &mut commands[0] else {
5081 panic!("expected SELECT trigger command");
5082 };
5083
5084 rewrite_select_table_refs(select, "old", "new");
5085
5086 let ast::OneSelect::Select { columns, .. } = &select.body.select else {
5087 panic!("expected SELECT core");
5088 };
5089 let ast::ResultColumn::Expr(expr, _) = &columns[0] else {
5090 panic!("expected expression result column");
5091 };
5092 let ast::Expr::Exists(subquery) = expr.as_ref() else {
5093 panic!("expected EXISTS expression");
5094 };
5095 let ast::OneSelect::Select {
5096 from: Some(from), ..
5097 } = &subquery.body.select
5098 else {
5099 panic!("expected EXISTS subquery FROM clause");
5100 };
5101 let ast::SelectTable::Table(tbl_name, _, _) = from.select.as_ref() else {
5102 panic!("expected EXISTS subquery FROM table");
5103 };
5104
5105 assert_eq!(
5106 tbl_name.db_name.as_ref().map(ast::Name::as_str),
5107 Some("temp")
5108 );
5109 assert_eq!(tbl_name.name.as_str(), "new");
5110 }
5111
5112 #[test]
5113 fn test_rewrite_view_sql_join_on_branch() {
5114 let schema = schema_with_tables(&["CREATE TABLE t (a, b)", "CREATE TABLE u (b)"]);
5115 let view_sql = "CREATE VIEW v AS SELECT t.a FROM t JOIN u ON t.b = u.b";
5116
5117 let rewritten =
5118 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
5119 .unwrap()
5120 .expect("view should be rewritten");
5121
5122 assert!(!rewritten.sql.contains("t.b"), "{}", rewritten.sql);
5123 assert!(rewritten.sql.contains("t.c = u.b"), "{}", rewritten.sql);
5124 }
5125
5126 #[test]
5127 fn test_rewrite_view_sql_join_using_branch() {
5128 let schema = schema_with_tables(&["CREATE TABLE t (a, b)", "CREATE TABLE u (b)"]);
5129 let view_sql = "CREATE VIEW v AS SELECT t.a FROM t JOIN u USING (b)";
5130
5131 let rewritten =
5132 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
5133 .unwrap()
5134 .expect("view should be rewritten");
5135
5136 assert!(
5137 !rewritten.sql.contains("USING (b)") && !rewritten.sql.contains("USING(b)"),
5138 "{}",
5139 rewritten.sql
5140 );
5141 assert!(
5142 rewritten.sql.contains("USING (c)") || rewritten.sql.contains("USING(c)"),
5143 "{}",
5144 rewritten.sql
5145 );
5146 }
5147
5148 #[test]
5149 fn test_rewrite_view_sql_group_by_having_branch() {
5150 let schema = schema_with_table("CREATE TABLE t (a, b)");
5151 let view_sql = "CREATE VIEW v AS SELECT b FROM t GROUP BY b HAVING b > 0";
5152
5153 let rewritten =
5154 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
5155 .unwrap()
5156 .expect("view should be rewritten");
5157
5158 assert!(
5159 rewritten
5160 .sql
5161 .contains("SELECT c FROM t GROUP BY c HAVING c > 0"),
5162 "{}",
5163 rewritten.sql
5164 );
5165 }
5166
5167 #[test]
5168 fn test_rewrite_view_sql_window_clause_branch() {
5169 let schema = schema_with_table("CREATE TABLE t (a, b)");
5170 let view_sql = "CREATE VIEW v AS SELECT sum(a) OVER (PARTITION BY b ORDER BY b) FROM t";
5171
5172 let rewritten =
5173 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
5174 .unwrap()
5175 .expect("view should be rewritten");
5176
5177 assert!(
5178 !rewritten.sql.contains("PARTITION BY b"),
5179 "{}",
5180 rewritten.sql
5181 );
5182 assert!(!rewritten.sql.contains("ORDER BY b"), "{}", rewritten.sql);
5183 assert!(
5184 rewritten.sql.contains("PARTITION BY c"),
5185 "{}",
5186 rewritten.sql
5187 );
5188 assert!(rewritten.sql.contains("ORDER BY c"), "{}", rewritten.sql);
5189 }
5190
5191 #[test]
5192 fn test_rewrite_view_sql_limit_offset_branch() {
5193 let schema = schema_with_table("CREATE TABLE t (a, b)");
5194 let view_sql = "CREATE VIEW v AS SELECT a FROM t LIMIT b OFFSET b";
5195
5196 let rewritten =
5197 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
5198 .unwrap()
5199 .expect("view should be rewritten");
5200
5201 assert!(!rewritten.sql.contains("LIMIT b"), "{}", rewritten.sql);
5202 assert!(!rewritten.sql.contains("OFFSET b"), "{}", rewritten.sql);
5203 assert!(rewritten.sql.contains("LIMIT c"), "{}", rewritten.sql);
5204 assert!(rewritten.sql.contains("OFFSET c"), "{}", rewritten.sql);
5205 }
5206
5207 #[test]
5208 fn test_rewrite_view_sql_values_branch() {
5209 let schema = schema_with_table("CREATE TABLE t (a, b)");
5210 let view_sql = "CREATE VIEW v AS VALUES ((SELECT b FROM t LIMIT 1))";
5211
5212 let rewritten =
5213 rewrite_view_sql_for_column_rename(view_sql, &schema, "t", "main", "b", "c")
5214 .unwrap()
5215 .expect("view should be rewritten");
5216
5217 assert!(
5218 rewritten.sql.contains("VALUES ((SELECT c FROM t LIMIT 1))"),
5219 "{}",
5220 rewritten.sql
5221 );
5222 }
5223
5224 #[test]
5225 fn test_indexed_variable_comparison() {
5226 let expr1 = Expr::Variable(Variable::indexed(1u32.try_into().unwrap()));
5227 let expr2 = Expr::Variable(Variable::indexed(1u32.try_into().unwrap()));
5228 assert!(exprs_are_equivalent(&expr1, &expr2));
5229 }
5230
5231 #[test]
5232 fn test_named_variable_comparison() {
5233 let expr1 = Expr::Variable(Variable::named(":a".to_string(), 1u32.try_into().unwrap()));
5234 let expr2 = Expr::Variable(Variable::named(":a".to_string(), 1u32.try_into().unwrap()));
5235 assert!(exprs_are_equivalent(&expr1, &expr2));
5236
5237 let expr1 = Expr::Variable(Variable::named(":a".to_string(), 1u32.try_into().unwrap()));
5238 let expr2 = Expr::Variable(Variable::named(":b".to_string(), 2u32.try_into().unwrap()));
5239 assert!(!exprs_are_equivalent(&expr1, &expr2));
5240 }
5241
5242 #[test]
5243 fn test_basic_addition_exprs_are_equivalent() {
5244 let expr1 = Expr::Binary(
5245 Box::new(Expr::Literal(Literal::Numeric("826".to_string()))),
5246 Add,
5247 Box::new(Expr::Literal(Literal::Numeric("389".to_string()))),
5248 );
5249 let expr2 = Expr::Binary(
5250 Box::new(Expr::Literal(Literal::Numeric("389".to_string()))),
5251 Add,
5252 Box::new(Expr::Literal(Literal::Numeric("826".to_string()))),
5253 );
5254 assert!(exprs_are_equivalent(&expr1, &expr2));
5255 }
5256
5257 #[test]
5258 fn test_addition_expressions_equivalent_normalized() {
5259 let expr1 = Expr::Binary(
5261 Box::new(Expr::Literal(Literal::Numeric("123.0".to_string()))),
5262 Add,
5263 Box::new(Expr::Literal(Literal::Numeric("243.0".to_string()))),
5264 );
5265 let expr2 = Expr::Binary(
5266 Box::new(Expr::Literal(Literal::Numeric("243.0".to_string()))),
5267 Add,
5268 Box::new(Expr::Literal(Literal::Numeric("123.0".to_string()))),
5269 );
5270 assert!(exprs_are_equivalent(&expr1, &expr2));
5271
5272 let expr3 = Expr::Binary(
5274 Box::new(Expr::Literal(Literal::Numeric("123.0".to_string()))),
5275 Add,
5276 Box::new(Expr::Literal(Literal::Numeric("243".to_string()))),
5277 );
5278 let expr4 = Expr::Binary(
5279 Box::new(Expr::Literal(Literal::Numeric("243.0".to_string()))),
5280 Add,
5281 Box::new(Expr::Literal(Literal::Numeric("123".to_string()))),
5282 );
5283 assert!(!exprs_are_equivalent(&expr3, &expr4));
5284 }
5285
5286 #[test]
5287 fn test_subtraction_expressions_not_equivalent() {
5288 let expr3 = Expr::Binary(
5289 Box::new(Expr::Literal(Literal::Numeric("364".to_string()))),
5290 Subtract,
5291 Box::new(Expr::Literal(Literal::Numeric("22.0".to_string()))),
5292 );
5293 let expr4 = Expr::Binary(
5294 Box::new(Expr::Literal(Literal::Numeric("22.0".to_string()))),
5295 Subtract,
5296 Box::new(Expr::Literal(Literal::Numeric("364".to_string()))),
5297 );
5298 assert!(!exprs_are_equivalent(&expr3, &expr4));
5299 }
5300
5301 #[test]
5302 fn test_subtraction_expressions_normalized() {
5303 let expr3 = Expr::Binary(
5305 Box::new(Expr::Literal(Literal::Numeric("66.0".to_string()))),
5306 Subtract,
5307 Box::new(Expr::Literal(Literal::Numeric("22.0".to_string()))),
5308 );
5309 let expr4 = Expr::Binary(
5310 Box::new(Expr::Literal(Literal::Numeric("66.0".to_string()))),
5311 Subtract,
5312 Box::new(Expr::Literal(Literal::Numeric("22.0".to_string()))),
5313 );
5314 assert!(exprs_are_equivalent(&expr3, &expr4));
5315
5316 let expr5 = Expr::Binary(
5318 Box::new(Expr::Literal(Literal::Numeric("66.0".to_string()))),
5319 Subtract,
5320 Box::new(Expr::Literal(Literal::Numeric("22".to_string()))),
5321 );
5322 let expr6 = Expr::Binary(
5323 Box::new(Expr::Literal(Literal::Numeric("66".to_string()))),
5324 Subtract,
5325 Box::new(Expr::Literal(Literal::Numeric("22.0".to_string()))),
5326 );
5327 assert!(!exprs_are_equivalent(&expr5, &expr6));
5328 }
5329
5330 #[test]
5331 fn test_expressions_equivalent_case_insensitive_functioncalls() {
5332 let func1 = Expr::FunctionCall {
5333 name: Name::exact("SUM".to_string()),
5334 distinctness: None,
5335 args: vec![Expr::Id(Name::exact("x".to_string())).into()],
5336 order_by: vec![],
5337 within_group: vec![],
5338 filter_over: FunctionTail {
5339 filter_clause: None,
5340 over_clause: None,
5341 },
5342 };
5343 let func2 = Expr::FunctionCall {
5344 name: Name::exact("sum".to_string()),
5345 distinctness: None,
5346 args: vec![Expr::Id(Name::exact("x".to_string())).into()],
5347 order_by: vec![],
5348 within_group: vec![],
5349 filter_over: FunctionTail {
5350 filter_clause: None,
5351 over_clause: None,
5352 },
5353 };
5354 assert!(exprs_are_equivalent(&func1, &func2));
5355
5356 let func3 = Expr::FunctionCall {
5357 name: Name::exact("SUM".to_string()),
5358 distinctness: Some(ast::Distinctness::Distinct),
5359 args: vec![Expr::Id(Name::exact("x".to_string())).into()],
5360 order_by: vec![],
5361 within_group: vec![],
5362 filter_over: FunctionTail {
5363 filter_clause: None,
5364 over_clause: None,
5365 },
5366 };
5367 assert!(!exprs_are_equivalent(&func1, &func3));
5368 }
5369
5370 #[test]
5371 fn test_expressions_equivalent_identical_fn_with_distinct() {
5372 let sum = Expr::FunctionCall {
5373 name: Name::exact("SUM".to_string()),
5374 distinctness: None,
5375 args: vec![Expr::Id(Name::exact("x".to_string())).into()],
5376 order_by: vec![],
5377 within_group: vec![],
5378 filter_over: FunctionTail {
5379 filter_clause: None,
5380 over_clause: None,
5381 },
5382 };
5383 let sum_distinct = Expr::FunctionCall {
5384 name: Name::exact("SUM".to_string()),
5385 distinctness: Some(ast::Distinctness::Distinct),
5386 args: vec![Expr::Id(Name::exact("x".to_string())).into()],
5387 order_by: vec![],
5388 within_group: vec![],
5389 filter_over: FunctionTail {
5390 filter_clause: None,
5391 over_clause: None,
5392 },
5393 };
5394 assert!(!exprs_are_equivalent(&sum, &sum_distinct));
5395 }
5396
5397 #[test]
5398 fn test_expressions_equivalent_multiplication() {
5399 let expr1 = Expr::Binary(
5401 Box::new(Expr::Literal(Literal::Numeric("42.0".to_string()))),
5402 Multiply,
5403 Box::new(Expr::Literal(Literal::Numeric("38.0".to_string()))),
5404 );
5405 let expr2 = Expr::Binary(
5406 Box::new(Expr::Literal(Literal::Numeric("38.0".to_string()))),
5407 Multiply,
5408 Box::new(Expr::Literal(Literal::Numeric("42.0".to_string()))),
5409 );
5410 assert!(exprs_are_equivalent(&expr1, &expr2));
5411 }
5412
5413 #[test]
5414 fn test_expressions_both_parenthesized_equivalent() {
5415 let expr1 = Expr::Parenthesized(vec![Expr::Binary(
5417 Box::new(Expr::Literal(Literal::Numeric("683".to_string()))),
5418 Add,
5419 Box::new(Expr::Literal(Literal::Numeric("799".to_string()))),
5420 )
5421 .into()]);
5422 let expr2 = Expr::Binary(
5423 Box::new(Expr::Literal(Literal::Numeric("799".to_string()))),
5424 Add,
5425 Box::new(Expr::Literal(Literal::Numeric("683".to_string()))),
5426 );
5427 assert!(exprs_are_equivalent(&expr1, &expr2));
5428 }
5429 #[test]
5430 fn test_expressions_parenthesized_equivalent() {
5431 let expr7 = Expr::Parenthesized(vec![Expr::Binary(
5432 Box::new(Expr::Literal(Literal::Numeric("6".to_string()))),
5433 Add,
5434 Box::new(Expr::Literal(Literal::Numeric("7".to_string()))),
5435 )
5436 .into()]);
5437 let expr8 = Expr::Binary(
5438 Box::new(Expr::Literal(Literal::Numeric("6".to_string()))),
5439 Add,
5440 Box::new(Expr::Literal(Literal::Numeric("7".to_string()))),
5441 );
5442 assert!(exprs_are_equivalent(&expr7, &expr8));
5443 }
5444
5445 #[test]
5446 fn test_like_expressions_equivalent() {
5447 let expr1 = Expr::Like {
5448 lhs: Box::new(Expr::Id(Name::exact("name".to_string()))),
5449 not: false,
5450 op: ast::LikeOperator::Like,
5451 rhs: Box::new(Expr::Literal(Literal::String("%john%".to_string()))),
5452 escape: Some(Box::new(Expr::Literal(Literal::String("\\".to_string())))),
5453 };
5454 let expr2 = Expr::Like {
5455 lhs: Box::new(Expr::Id(Name::exact("name".to_string()))),
5456 not: false,
5457 op: ast::LikeOperator::Like,
5458 rhs: Box::new(Expr::Literal(Literal::String("%john%".to_string()))),
5459 escape: Some(Box::new(Expr::Literal(Literal::String("\\".to_string())))),
5460 };
5461 assert!(exprs_are_equivalent(&expr1, &expr2));
5462 }
5463
5464 #[test]
5465 fn test_expressions_equivalent_like_escaped() {
5466 let expr1 = Expr::Like {
5467 lhs: Box::new(Expr::Id(Name::exact("name".to_string()))),
5468 not: false,
5469 op: ast::LikeOperator::Like,
5470 rhs: Box::new(Expr::Literal(Literal::String("%john%".to_string()))),
5471 escape: Some(Box::new(Expr::Literal(Literal::String("\\".to_string())))),
5472 };
5473 let expr2 = Expr::Like {
5474 lhs: Box::new(Expr::Id(Name::exact("name".to_string()))),
5475 not: false,
5476 op: ast::LikeOperator::Like,
5477 rhs: Box::new(Expr::Literal(Literal::String("%john%".to_string()))),
5478 escape: Some(Box::new(Expr::Literal(Literal::String("#".to_string())))),
5479 };
5480 assert!(!exprs_are_equivalent(&expr1, &expr2));
5481 }
5482 #[test]
5483 fn test_expressions_equivalent_between() {
5484 let expr1 = Expr::Between {
5485 lhs: Box::new(Expr::Id(Name::exact("age".to_string()))),
5486 not: false,
5487 start: Box::new(Expr::Literal(Literal::Numeric("18".to_string()))),
5488 end: Box::new(Expr::Literal(Literal::Numeric("65".to_string()))),
5489 };
5490 let expr2 = Expr::Between {
5491 lhs: Box::new(Expr::Id(Name::exact("age".to_string()))),
5492 not: false,
5493 start: Box::new(Expr::Literal(Literal::Numeric("18".to_string()))),
5494 end: Box::new(Expr::Literal(Literal::Numeric("65".to_string()))),
5495 };
5496 assert!(exprs_are_equivalent(&expr1, &expr2));
5497
5498 let expr3 = Expr::Between {
5500 lhs: Box::new(Expr::Id(Name::exact("age".to_string()))),
5501 not: false,
5502 start: Box::new(Expr::Literal(Literal::Numeric("20".to_string()))),
5503 end: Box::new(Expr::Literal(Literal::Numeric("65".to_string()))),
5504 };
5505 assert!(!exprs_are_equivalent(&expr1, &expr3));
5506 }
5507 #[test]
5508 fn test_cast_exprs_equivalent() {
5509 let cast1 = Expr::Cast {
5510 expr: Box::new(Expr::Literal(Literal::Numeric("123".to_string()))),
5511 type_name: Some(Type {
5512 name: "INTEGER".to_string(),
5513 size: None,
5514 array_dimensions: 0,
5515 }),
5516 };
5517
5518 let cast2 = Expr::Cast {
5519 expr: Box::new(Expr::Literal(Literal::Numeric("123".to_string()))),
5520 type_name: Some(Type {
5521 name: "integer".to_string(),
5522 size: None,
5523 array_dimensions: 0,
5524 }),
5525 };
5526 assert!(exprs_are_equivalent(&cast1, &cast2));
5527 }
5528
5529 #[test]
5530 fn test_ident_equivalency() {
5531 assert!(check_ident_equivalency("\"foo\"", "foo"));
5532 assert!(check_ident_equivalency("[foo]", "foo"));
5533 assert!(check_ident_equivalency("`FOO`", "foo"));
5534 assert!(check_ident_equivalency("\"foo\"", "`FOO`"));
5535 assert!(!check_ident_equivalency("\"foo\"", "[bar]"));
5536 assert!(!check_ident_equivalency("foo", "\"bar\""));
5537 }
5538
5539 #[test]
5540 fn test_simple_uri() {
5541 let uri = "file:/home/user/db.sqlite";
5542 let opts = OpenOptions::parse(uri).unwrap();
5543 assert_eq!(opts.path, "/home/user/db.sqlite");
5544 assert_eq!(opts.authority, None);
5545 }
5546
5547 #[test]
5548 fn test_uri_with_authority() {
5549 let uri = "file://localhost/home/user/db.sqlite";
5550 let opts = OpenOptions::parse(uri).unwrap();
5551 assert_eq!(opts.path, "/home/user/db.sqlite");
5552 assert_eq!(opts.authority, Some("localhost"));
5553 }
5554
5555 #[test]
5556 fn test_uri_with_invalid_authority() {
5557 let uri = "file://example.com/home/user/db.sqlite";
5558 let result = OpenOptions::parse(uri);
5559 assert!(result.is_err());
5560 }
5561
5562 #[test]
5563 fn test_uri_with_query_params() {
5564 let uri = "file:/home/user/db.sqlite?vfs=unix&mode=ro&immutable=1";
5565 let opts = OpenOptions::parse(uri).unwrap();
5566 assert_eq!(opts.path, "/home/user/db.sqlite");
5567 assert_eq!(opts.vfs, Some("unix".to_string()));
5568 assert_eq!(opts.mode, OpenMode::ReadOnly);
5569 assert!(opts.immutable);
5570 }
5571
5572 #[test]
5573 fn test_uri_with_fragment() {
5574 let uri = "file:/home/user/db.sqlite#section1";
5575 let opts = OpenOptions::parse(uri).unwrap();
5576 assert_eq!(opts.path, "/home/user/db.sqlite");
5577 }
5578
5579 #[test]
5580 fn test_uri_with_percent_encoding() {
5581 let uri = "file:/home/user/db%20with%20spaces.sqlite?vfs=unix";
5582 let opts = OpenOptions::parse(uri).unwrap();
5583 assert_eq!(opts.path, "/home/user/db with spaces.sqlite");
5584 assert_eq!(opts.vfs, Some("unix".to_string()));
5585 }
5586
5587 #[test]
5588 fn test_uri_without_scheme() {
5589 let uri = "/home/user/db.sqlite";
5590 let result = OpenOptions::parse(uri);
5591 assert!(result.is_ok());
5592 assert_eq!(result.unwrap().path, "/home/user/db.sqlite");
5593 }
5594
5595 #[test]
5596 fn test_uri_with_empty_query() {
5597 let uri = "file:/home/user/db.sqlite?";
5598 let opts = OpenOptions::parse(uri).unwrap();
5599 assert_eq!(opts.path, "/home/user/db.sqlite");
5600 assert_eq!(opts.vfs, None);
5601 }
5602
5603 #[test]
5604 fn test_uri_with_partial_query() {
5605 let uri = "file:/home/user/db.sqlite?mode=rw";
5606 let opts = OpenOptions::parse(uri).unwrap();
5607 assert_eq!(opts.path, "/home/user/db.sqlite");
5608 assert_eq!(opts.mode, OpenMode::ReadWrite);
5609 assert_eq!(opts.vfs, None);
5610 }
5611
5612 #[test]
5613 fn test_uri_windows_style_path() {
5614 let uri = "file:///C:/Users/test/db.sqlite";
5615 let opts = OpenOptions::parse(uri).unwrap();
5616 assert_eq!(opts.path, "/C:/Users/test/db.sqlite");
5617 }
5618
5619 #[test]
5620 fn test_uri_with_only_query_params() {
5621 let uri = "file:?mode=memory&cache=shared";
5622 let opts = OpenOptions::parse(uri).unwrap();
5623 assert_eq!(opts.path, "");
5624 assert_eq!(opts.mode, OpenMode::Memory);
5625 assert_eq!(opts.cache, CacheMode::Shared);
5626 }
5627
5628 #[test]
5629 fn test_uri_with_only_fragment() {
5630 let uri = "file:#fragment";
5631 let opts = OpenOptions::parse(uri).unwrap();
5632 assert_eq!(opts.path, "");
5633 }
5634
5635 #[test]
5636 fn test_uri_with_invalid_scheme() {
5637 let uri = "http:/home/user/db.sqlite";
5638 let result = OpenOptions::parse(uri);
5639 assert!(result.is_ok());
5640 assert_eq!(result.unwrap().path, "http:/home/user/db.sqlite");
5641 }
5642
5643 #[test]
5644 fn test_uri_with_multiple_query_params() {
5645 let uri = "file:/home/user/db.sqlite?vfs=unix&mode=rw&cache=private&immutable=0";
5646 let opts = OpenOptions::parse(uri).unwrap();
5647 assert_eq!(opts.path, "/home/user/db.sqlite");
5648 assert_eq!(opts.vfs, Some("unix".to_string()));
5649 assert_eq!(opts.mode, OpenMode::ReadWrite);
5650 assert_eq!(opts.cache, CacheMode::Private);
5651 assert!(!opts.immutable);
5652 }
5653
5654 #[test]
5655 fn test_uri_with_unknown_query_param() {
5656 let uri = "file:/home/user/db.sqlite?unknown=param";
5657 let opts = OpenOptions::parse(uri).unwrap();
5658 assert_eq!(opts.path, "/home/user/db.sqlite");
5659 assert_eq!(opts.vfs, None);
5660 }
5661
5662 #[test]
5663 fn test_uri_with_multiple_equal_signs() {
5664 let uri = "file:/home/user/db.sqlite?vfs=unix=custom";
5665 let opts = OpenOptions::parse(uri).unwrap();
5666 assert_eq!(opts.path, "/home/user/db.sqlite");
5667 assert_eq!(opts.vfs, Some("unix=custom".to_string()));
5668 }
5669
5670 #[test]
5671 fn test_uri_with_trailing_slash() {
5672 let uri = "file:/home/user/db.sqlite/";
5673 let opts = OpenOptions::parse(uri).unwrap();
5674 assert_eq!(opts.path, "/home/user/db.sqlite/");
5675 }
5676
5677 #[test]
5678 fn test_uri_with_encoded_characters_in_query() {
5679 let uri = "file:/home/user/db.sqlite?vfs=unix%20mode";
5680 let opts = OpenOptions::parse(uri).unwrap();
5681 assert_eq!(opts.path, "/home/user/db.sqlite");
5682 assert_eq!(opts.vfs, Some("unix mode".to_string()));
5683 }
5684
5685 #[test]
5686 fn test_uri_windows_network_path() {
5687 let uri = "file://server/share/db.sqlite";
5688 let result = OpenOptions::parse(uri);
5689 assert!(result.is_err()); }
5691
5692 #[test]
5693 fn test_uri_windows_drive_letter_with_slash() {
5694 let uri = "file:///C:/database.sqlite";
5695 let opts = OpenOptions::parse(uri).unwrap();
5696 assert_eq!(opts.path, "/C:/database.sqlite");
5697 }
5698
5699 #[test]
5700 fn test_localhost_with_double_slash_and_no_path() {
5701 let uri = "file://localhost";
5702 let opts = OpenOptions::parse(uri).unwrap();
5703 assert_eq!(opts.path, "");
5704 assert_eq!(opts.authority, Some("localhost"));
5705 }
5706
5707 #[test]
5708 fn test_uri_windows_drive_letter_without_slash() {
5709 let uri = "file:///C:/database.sqlite";
5710 let opts = OpenOptions::parse(uri).unwrap();
5711 assert_eq!(opts.path, "/C:/database.sqlite");
5712 }
5713
5714 #[test]
5715 fn test_improper_mode() {
5716 let uri = "file:data.db?mode=readonly";
5719 let res = OpenOptions::parse(uri);
5720 assert!(res.is_err());
5721 let uri = "file:/home/user/db.sqlite?vfs=&mode=";
5723 let res = OpenOptions::parse(uri);
5724 assert!(res.is_err());
5725 }
5726
5727 #[test]
5729 fn test_simple_file_current_dir() {
5730 let uri = "file:data.db";
5731 let opts = OpenOptions::parse(uri).unwrap();
5732 assert_eq!(opts.path, "data.db");
5733 assert_eq!(opts.authority, None);
5734 assert_eq!(opts.vfs, None);
5735 assert_eq!(opts.mode, OpenMode::ReadWriteCreate);
5736 }
5737
5738 #[test]
5739 fn test_simple_file_three_slash() {
5740 let uri = "file:///home/data/data.db";
5741 let opts = OpenOptions::parse(uri).unwrap();
5742 assert_eq!(opts.path, "/home/data/data.db");
5743 assert_eq!(opts.authority, None);
5744 assert_eq!(opts.vfs, None);
5745 assert_eq!(opts.mode, OpenMode::ReadWriteCreate);
5746 }
5747
5748 #[test]
5749 fn test_simple_file_two_slash_localhost() {
5750 let uri = "file://localhost/home/fred/data.db";
5751 let opts = OpenOptions::parse(uri).unwrap();
5752 assert_eq!(opts.path, "/home/fred/data.db");
5753 assert_eq!(opts.authority, Some("localhost"));
5754 assert_eq!(opts.vfs, None);
5755 }
5756
5757 #[test]
5758 fn test_windows_double_invalid() {
5759 let uri = "file://C:/home/fred/data.db?mode=ro";
5760 let opts = OpenOptions::parse(uri);
5761 assert!(opts.is_err());
5762 }
5763
5764 #[test]
5765 fn test_simple_file_two_slash() {
5766 let uri = "file:///C:/Documents%20and%20Settings/fred/Desktop/data.db";
5767 let opts = OpenOptions::parse(uri).unwrap();
5768 assert_eq!(opts.path, "/C:/Documents and Settings/fred/Desktop/data.db");
5769 assert_eq!(opts.vfs, None);
5770 }
5771
5772 #[test]
5773 fn test_decode_percent_basic() {
5774 assert_eq!(decode_percent("hello%20world"), "hello world");
5775 assert_eq!(decode_percent("file%3Adata.db"), "file:data.db");
5776 assert_eq!(decode_percent("path%2Fto%2Ffile"), "path/to/file");
5777 }
5778
5779 #[test]
5780 fn test_decode_percent_edge_cases() {
5781 assert_eq!(decode_percent(""), "");
5782 assert_eq!(decode_percent("plain_text"), "plain_text");
5783 assert_eq!(
5784 decode_percent("%2Fhome%2Fuser%2Fdb.sqlite"),
5785 "/home/user/db.sqlite"
5786 );
5787 assert_eq!(decode_percent("%41%42%43"), "ABC");
5789 assert_eq!(decode_percent("%61%62%63"), "abc");
5790 }
5791
5792 #[test]
5793 fn test_decode_percent_invalid_sequences() {
5794 assert_eq!(decode_percent("hello%"), "hello%");
5796 assert_eq!(decode_percent("file%2"), "file%2");
5798 assert_eq!(decode_percent("file%2X.db"), "file%2X.db");
5800
5801 assert_eq!(decode_percent("path%2Fto%2"), "path/to%2");
5803 }
5804
5805 #[test]
5806 fn test_decode_percent_mixed_valid_invalid() {
5807 assert_eq!(decode_percent("hello%20world%"), "hello world%");
5808 assert_eq!(decode_percent("%2Fpath%2Xto%2Ffile"), "/path%2Xto/file");
5809 assert_eq!(decode_percent("file%3Adata.db%2"), "file:data.db%2");
5810 }
5811
5812 #[test]
5813 fn test_decode_percent_special_characters() {
5814 assert_eq!(
5815 decode_percent("%21%40%23%24%25%5E%26%2A%28%29"),
5816 "!@#$%^&*()"
5817 );
5818 assert_eq!(decode_percent("%5B%5D%7B%7D%7C%5C%3A"), "[]{}|\\:");
5819 }
5820
5821 #[test]
5822 fn test_decode_percent_unmodified_valid_text() {
5823 assert_eq!(
5825 decode_percent("C:/Users/Example/Database.sqlite"),
5826 "C:/Users/Example/Database.sqlite"
5827 );
5828 assert_eq!(
5829 decode_percent("/home/user/db.sqlite"),
5830 "/home/user/db.sqlite"
5831 );
5832 }
5833
5834 #[test]
5835 fn test_text_to_integer() {
5836 assert_eq!(
5837 checked_cast_text_to_numeric("1", false).unwrap(),
5838 Value::from_i64(1)
5839 );
5840 assert_eq!(
5841 checked_cast_text_to_numeric("-1", false).unwrap(),
5842 Value::from_i64(-1)
5843 );
5844 assert_eq!(
5845 checked_cast_text_to_numeric("1823400-00000", false).unwrap(),
5846 Value::from_i64(1823400)
5847 );
5848 assert_eq!(
5849 checked_cast_text_to_numeric("-10000000", false).unwrap(),
5850 Value::from_i64(-10000000)
5851 );
5852 assert_eq!(
5853 checked_cast_text_to_numeric("123xxx", false).unwrap(),
5854 Value::from_i64(123)
5855 );
5856 assert_eq!(
5857 checked_cast_text_to_numeric("9223372036854775807", false).unwrap(),
5858 Value::from_i64(i64::MAX)
5859 );
5860 assert_eq!(
5862 checked_cast_text_to_numeric("9223372036854775808", false).unwrap(),
5863 Value::from_f64(9.22337203685478e18)
5864 );
5865 assert_eq!(
5866 checked_cast_text_to_numeric("-9223372036854775808", false).unwrap(),
5867 Value::from_i64(i64::MIN)
5868 );
5869 assert_eq!(
5871 checked_cast_text_to_numeric("-9223372036854775809", false).unwrap(),
5872 Value::from_f64(-9.22337203685478e18)
5873 );
5874 assert!(checked_cast_text_to_numeric("-", false).is_err());
5875 }
5876
5877 #[test]
5878 fn test_text_to_real() {
5879 assert_eq!(
5880 checked_cast_text_to_numeric("1", false).unwrap(),
5881 Value::from_i64(1)
5882 );
5883 assert_eq!(
5884 checked_cast_text_to_numeric("-1", false).unwrap(),
5885 Value::from_i64(-1)
5886 );
5887 assert_eq!(
5888 checked_cast_text_to_numeric("1.0", false).unwrap(),
5889 Value::from_i64(1)
5890 );
5891 assert_eq!(
5892 checked_cast_text_to_numeric("-1.0", false).unwrap(),
5893 Value::from_i64(-1)
5894 );
5895 assert_eq!(
5896 checked_cast_text_to_numeric("1e10", false).unwrap(),
5897 Value::from_i64(10_000_000_000)
5898 );
5899 assert_eq!(
5900 checked_cast_text_to_numeric("-1e10", false).unwrap(),
5901 Value::from_i64(-10_000_000_000)
5902 );
5903 assert_eq!(
5904 checked_cast_text_to_numeric("1e-10", false).unwrap(),
5905 Value::from_f64(1e-10)
5906 );
5907 assert_eq!(
5908 checked_cast_text_to_numeric("-1e-10", false).unwrap(),
5909 Value::from_f64(-1e-10)
5910 );
5911 assert_eq!(
5912 checked_cast_text_to_numeric("1.123e10", false).unwrap(),
5913 Value::from_i64(11_230_000_000)
5914 );
5915 assert_eq!(
5916 checked_cast_text_to_numeric("-1.123e10", false).unwrap(),
5917 Value::from_i64(-11_230_000_000)
5918 );
5919 assert_eq!(
5920 checked_cast_text_to_numeric("1.123e-10", false).unwrap(),
5921 Value::from_f64(1.123e-10)
5922 );
5923 assert_eq!(
5924 checked_cast_text_to_numeric("-1.123-e-10", false).unwrap(),
5925 Value::from_f64(-1.123)
5926 );
5927 assert_eq!(
5928 checked_cast_text_to_numeric("1-282584294928", false).unwrap(),
5929 Value::from_i64(1)
5930 );
5931 assert_eq!(
5932 checked_cast_text_to_numeric("1.7976931348623157e309", false).unwrap(),
5933 Value::from_f64(f64::INFINITY),
5934 );
5935 assert_eq!(
5936 checked_cast_text_to_numeric("-1.7976931348623157e308", false).unwrap(),
5937 Value::from_f64(f64::MIN),
5938 );
5939 assert_eq!(
5940 checked_cast_text_to_numeric("-1.7976931348623157e309", false).unwrap(),
5941 Value::from_f64(f64::NEG_INFINITY),
5942 );
5943 assert_eq!(
5944 checked_cast_text_to_numeric("1E", false).unwrap(),
5945 Value::from_i64(1)
5946 );
5947 assert_eq!(
5948 checked_cast_text_to_numeric("1EE", false).unwrap(),
5949 Value::from_i64(1)
5950 );
5951 assert_eq!(
5952 checked_cast_text_to_numeric("-1E", false).unwrap(),
5953 Value::from_i64(-1)
5954 );
5955 assert_eq!(
5956 checked_cast_text_to_numeric("1.", false).unwrap(),
5957 Value::from_i64(1)
5958 );
5959 assert_eq!(
5960 checked_cast_text_to_numeric("-1.", false).unwrap(),
5961 Value::from_i64(-1)
5962 );
5963 assert_eq!(
5964 checked_cast_text_to_numeric("1.23E", false).unwrap(),
5965 Value::from_f64(1.23)
5966 );
5967 assert_eq!(
5968 checked_cast_text_to_numeric(".1.23E-", false).unwrap(),
5969 Value::from_f64(0.1)
5970 );
5971 assert_eq!(
5972 checked_cast_text_to_numeric("0", false).unwrap(),
5973 Value::from_i64(0)
5974 );
5975 assert_eq!(
5976 checked_cast_text_to_numeric("-0", false).unwrap(),
5977 Value::from_i64(0)
5978 );
5979 assert_eq!(
5980 checked_cast_text_to_numeric("-0", false).unwrap(),
5981 Value::from_i64(0)
5982 );
5983 assert_eq!(
5984 checked_cast_text_to_numeric("-0.0", false).unwrap(),
5985 Value::from_i64(0)
5986 );
5987 assert_eq!(
5988 checked_cast_text_to_numeric("0.0", false).unwrap(),
5989 Value::from_i64(0)
5990 );
5991 assert!(checked_cast_text_to_numeric("-", false).is_err());
5992 }
5993
5994 #[test]
5995 fn test_text_to_numeric() {
5996 assert_eq!(
5997 checked_cast_text_to_numeric("1", false).unwrap(),
5998 Value::from_i64(1)
5999 );
6000 assert_eq!(
6001 checked_cast_text_to_numeric("-1", false).unwrap(),
6002 Value::from_i64(-1)
6003 );
6004 assert_eq!(
6005 checked_cast_text_to_numeric("1823400-00000", false).unwrap(),
6006 Value::from_i64(1823400)
6007 );
6008 assert_eq!(
6009 checked_cast_text_to_numeric("-10000000", false).unwrap(),
6010 Value::from_i64(-10000000)
6011 );
6012 assert_eq!(
6013 checked_cast_text_to_numeric("123xxx", false).unwrap(),
6014 Value::from_i64(123)
6015 );
6016 assert_eq!(
6017 checked_cast_text_to_numeric("9223372036854775807", false).unwrap(),
6018 Value::from_i64(i64::MAX)
6019 );
6020 assert_eq!(
6021 checked_cast_text_to_numeric("9223372036854775808", false).unwrap(),
6022 Value::from_f64(9.22337203685478e18)
6023 ); assert_eq!(
6025 checked_cast_text_to_numeric("-9223372036854775808", false).unwrap(),
6026 Value::from_i64(i64::MIN)
6027 );
6028 assert_eq!(
6029 checked_cast_text_to_numeric("-9223372036854775809", false).unwrap(),
6030 Value::from_f64(-9.22337203685478e18)
6031 ); assert_eq!(
6034 checked_cast_text_to_numeric("1.0", false).unwrap(),
6035 Value::from_i64(1)
6036 );
6037 assert_eq!(
6038 checked_cast_text_to_numeric("-1.0", false).unwrap(),
6039 Value::from_i64(-1)
6040 );
6041 assert_eq!(
6042 checked_cast_text_to_numeric("1e10", false).unwrap(),
6043 Value::from_i64(10_000_000_000)
6044 );
6045 assert_eq!(
6046 checked_cast_text_to_numeric("-1e10", false).unwrap(),
6047 Value::from_i64(-10_000_000_000)
6048 );
6049 assert_eq!(
6050 checked_cast_text_to_numeric("1e-10", false).unwrap(),
6051 Value::from_f64(1e-10)
6052 );
6053 assert_eq!(
6054 checked_cast_text_to_numeric("-1e-10", false).unwrap(),
6055 Value::from_f64(-1e-10)
6056 );
6057 assert_eq!(
6058 checked_cast_text_to_numeric("1.123e10", false).unwrap(),
6059 Value::from_i64(11_230_000_000)
6060 );
6061 assert_eq!(
6062 checked_cast_text_to_numeric("-1.123e10", false).unwrap(),
6063 Value::from_i64(-11_230_000_000)
6064 );
6065 assert_eq!(
6066 checked_cast_text_to_numeric("1.123e-10", false).unwrap(),
6067 Value::from_f64(1.123e-10)
6068 );
6069 assert_eq!(
6070 checked_cast_text_to_numeric("-1.123-e-10", false).unwrap(),
6071 Value::from_f64(-1.123)
6072 );
6073 assert_eq!(
6074 checked_cast_text_to_numeric("1-282584294928", false).unwrap(),
6075 Value::from_i64(1)
6076 );
6077 assert!(checked_cast_text_to_numeric("xxx", false).is_err());
6078 assert_eq!(
6079 checked_cast_text_to_numeric("1.7976931348623157e309", false).unwrap(),
6080 Value::from_f64(f64::INFINITY)
6081 );
6082 assert_eq!(
6083 checked_cast_text_to_numeric("-1.7976931348623157e308", false).unwrap(),
6084 Value::from_f64(f64::MIN)
6085 );
6086 assert_eq!(
6087 checked_cast_text_to_numeric("-1.7976931348623157e309", false).unwrap(),
6088 Value::from_f64(f64::NEG_INFINITY)
6089 );
6090
6091 assert_eq!(
6092 checked_cast_text_to_numeric("1E", false).unwrap(),
6093 Value::from_i64(1)
6094 );
6095 assert_eq!(
6096 checked_cast_text_to_numeric("1EE", false).unwrap(),
6097 Value::from_i64(1)
6098 );
6099 assert_eq!(
6100 checked_cast_text_to_numeric("-1E", false).unwrap(),
6101 Value::from_i64(-1)
6102 );
6103 assert_eq!(
6104 checked_cast_text_to_numeric("1.", false).unwrap(),
6105 Value::from_i64(1)
6106 );
6107 assert_eq!(
6108 checked_cast_text_to_numeric("-1.", false).unwrap(),
6109 Value::from_i64(-1)
6110 );
6111 assert_eq!(
6112 checked_cast_text_to_numeric("1.23E", false).unwrap(),
6113 Value::from_f64(1.23)
6114 );
6115 assert_eq!(
6116 checked_cast_text_to_numeric("1.23E-", false).unwrap(),
6117 Value::from_f64(1.23)
6118 );
6119
6120 assert_eq!(
6121 checked_cast_text_to_numeric("0", false).unwrap(),
6122 Value::from_i64(0)
6123 );
6124 assert_eq!(
6125 checked_cast_text_to_numeric("-0", false).unwrap(),
6126 Value::from_i64(0)
6127 );
6128 assert_eq!(
6129 checked_cast_text_to_numeric("-0.0", false).unwrap(),
6130 Value::from_i64(0)
6131 );
6132 assert_eq!(
6133 checked_cast_text_to_numeric("0.0", false).unwrap(),
6134 Value::from_i64(0)
6135 );
6136 assert!(checked_cast_text_to_numeric("-", false).is_err());
6137 assert_eq!(
6138 checked_cast_text_to_numeric("-e", false).unwrap(),
6139 Value::from_f64(0.0)
6140 );
6141 assert_eq!(
6142 checked_cast_text_to_numeric("-E", false).unwrap(),
6143 Value::from_f64(0.0)
6144 );
6145 }
6146
6147 #[test]
6148 fn test_parse_numeric_str_valid_integer() {
6149 assert_eq!(parse_numeric_str("123"), Ok((ValueType::Integer, "123")));
6150 assert_eq!(parse_numeric_str("-456"), Ok((ValueType::Integer, "-456")));
6151 assert_eq!(parse_numeric_str("+789"), Ok((ValueType::Integer, "+789")));
6152 assert_eq!(
6153 parse_numeric_str("000789"),
6154 Ok((ValueType::Integer, "000789"))
6155 );
6156 }
6157
6158 #[test]
6159 fn test_parse_numeric_str_valid_float() {
6160 assert_eq!(
6161 parse_numeric_str("123.456"),
6162 Ok((ValueType::Float, "123.456"))
6163 );
6164 assert_eq!(
6165 parse_numeric_str("-0.789"),
6166 Ok((ValueType::Float, "-0.789"))
6167 );
6168 assert_eq!(
6169 parse_numeric_str("+0.789"),
6170 Ok((ValueType::Float, "+0.789"))
6171 );
6172 assert_eq!(parse_numeric_str("1e10"), Ok((ValueType::Float, "1e10")));
6173 assert_eq!(parse_numeric_str("+1e10"), Ok((ValueType::Float, "+1e10")));
6174 assert_eq!(
6175 parse_numeric_str("-1.23e-4"),
6176 Ok((ValueType::Float, "-1.23e-4"))
6177 );
6178 assert_eq!(
6179 parse_numeric_str("1.23E+4"),
6180 Ok((ValueType::Float, "1.23E+4"))
6181 );
6182 assert_eq!(parse_numeric_str("1.2.3"), Ok((ValueType::Float, "1.2")))
6183 }
6184
6185 #[test]
6186 fn test_parse_numeric_str_edge_cases() {
6187 assert_eq!(parse_numeric_str("1e"), Ok((ValueType::Float, "1")));
6188 assert_eq!(parse_numeric_str("1e-"), Ok((ValueType::Float, "1")));
6189 assert_eq!(parse_numeric_str("1e+"), Ok((ValueType::Float, "1")));
6190 assert_eq!(parse_numeric_str("-1e"), Ok((ValueType::Float, "-1")));
6191 assert_eq!(parse_numeric_str("-1e-"), Ok((ValueType::Float, "-1")));
6192 }
6193
6194 #[test]
6195 fn test_parse_numeric_str_invalid() {
6196 assert_eq!(parse_numeric_str(""), Err(()));
6197 assert_eq!(parse_numeric_str("abc"), Err(()));
6198 assert_eq!(parse_numeric_str("-"), Err(()));
6199 assert_eq!(parse_numeric_str("+"), Err(()));
6200 assert_eq!(parse_numeric_str("e10"), Err(()));
6201 assert_eq!(parse_numeric_str(".e10"), Err(()));
6202 }
6203
6204 #[test]
6205 fn test_parse_numeric_str_with_whitespace() {
6206 assert_eq!(parse_numeric_str(" 123"), Ok((ValueType::Integer, "123")));
6207 assert_eq!(
6208 parse_numeric_str(" -456.78 "),
6209 Ok((ValueType::Float, "-456.78"))
6210 );
6211 assert_eq!(
6212 parse_numeric_str(" 1.23e4 "),
6213 Ok((ValueType::Float, "1.23e4"))
6214 );
6215 }
6216
6217 #[test]
6218 fn test_parse_numeric_str_leading_zeros() {
6219 assert_eq!(
6220 parse_numeric_str("000123"),
6221 Ok((ValueType::Integer, "000123"))
6222 );
6223 assert_eq!(
6224 parse_numeric_str("000.456"),
6225 Ok((ValueType::Float, "000.456"))
6226 );
6227 assert_eq!(
6228 parse_numeric_str("0001e3"),
6229 Ok((ValueType::Float, "0001e3"))
6230 );
6231 }
6232
6233 #[test]
6234 fn test_parse_numeric_str_trailing_characters() {
6235 assert_eq!(parse_numeric_str("123abc"), Ok((ValueType::Integer, "123")));
6236 assert_eq!(
6237 parse_numeric_str("456.78xyz"),
6238 Ok((ValueType::Float, "456.78"))
6239 );
6240 assert_eq!(
6241 parse_numeric_str("1.23e4extra"),
6242 Ok((ValueType::Float, "1.23e4"))
6243 );
6244 }
6245
6246 #[test]
6247 fn test_sql_is_create_virtual_table() {
6248 assert!(sql_is_create_virtual_table(
6249 "CREATE VIRTUAL TABLE x USING y;"
6250 ));
6251 assert!(sql_is_create_virtual_table(
6252 "create virtual table x using y"
6253 ));
6254 assert!(sql_is_create_virtual_table(
6255 " \n\tCREATE VIRTUAL TABLE x USING y"
6256 ));
6257 assert!(sql_is_create_virtual_table(
6258 "-- comment\nCREATE /* c */ VIRTUAL TABLE x USING y"
6259 ));
6260 assert!(sql_is_create_virtual_table(
6261 "CREATE VIRTUAL TABLE IF NOT EXISTS x USING y(a, b)"
6262 ));
6263 assert!(sql_is_create_virtual_table(
6265 "CREATE VIRTUAL TABLE \"create table\" USING y"
6266 ));
6267 assert!(!sql_is_create_virtual_table(
6269 "CREATE TABLE t(x TEXT DEFAULT 'create virtual')"
6270 ));
6271 assert!(!sql_is_create_virtual_table(
6272 "CREATE TABLE \"create virtual\"(x)"
6273 ));
6274 assert!(!sql_is_create_virtual_table("CREATE TABLE t(x)"));
6275 assert!(!sql_is_create_virtual_table("CREATE VIRTUALX TABLE t(x)"));
6276 assert!(!sql_is_create_virtual_table("CREATE VIRTUAL"));
6277 assert!(!sql_is_create_virtual_table(""));
6278 }
6279
6280 #[test]
6281 fn test_module_name_basic() {
6282 let sql = "CREATE VIRTUAL TABLE x USING y;";
6283 assert_eq!(module_name_from_sql(sql).unwrap(), "y");
6284 }
6285
6286 #[test]
6287 fn test_module_name_with_args() {
6288 let sql = "CREATE VIRTUAL TABLE x USING modname('a', 'b');";
6289 assert_eq!(module_name_from_sql(sql).unwrap(), "modname");
6290 }
6291
6292 #[test]
6293 fn test_module_name_missing_using() {
6294 let sql = "CREATE VIRTUAL TABLE x (a, b);";
6295 assert!(module_name_from_sql(sql).is_err());
6296 }
6297
6298 #[test]
6299 fn test_module_name_no_semicolon() {
6300 let sql = "CREATE VIRTUAL TABLE x USING limbo(a, b)";
6301 assert_eq!(module_name_from_sql(sql).unwrap(), "limbo");
6302 }
6303
6304 #[test]
6305 fn test_module_name_no_semicolon_or_args() {
6306 let sql = "CREATE VIRTUAL TABLE x USING limbo";
6307 assert_eq!(module_name_from_sql(sql).unwrap(), "limbo");
6308 }
6309
6310 #[test]
6311 fn test_module_args_none() {
6312 let sql = "CREATE VIRTUAL TABLE x USING modname;";
6313 let args = module_args_from_sql(sql).unwrap();
6314 assert_eq!(args.len(), 0);
6315 }
6316
6317 #[test]
6318 fn test_module_args_basic() {
6319 let sql = "CREATE VIRTUAL TABLE x USING modname('arg1', 'arg2');";
6320 let args = module_args_from_sql(sql).unwrap();
6321 assert_eq!(args.len(), 2);
6322 assert_eq!("arg1", args[0].to_text().unwrap());
6323 assert_eq!("arg2", args[1].to_text().unwrap());
6324 for arg in args {
6325 unsafe { arg.__free_internal_type() }
6326 }
6327 }
6328
6329 #[test]
6330 fn test_module_args_with_escaped_quote() {
6331 let sql = "CREATE VIRTUAL TABLE x USING modname('a''b', 'c');";
6332 let args = module_args_from_sql(sql).unwrap();
6333 assert_eq!(args.len(), 2);
6334 assert_eq!(args[0].to_text().unwrap(), "a'b");
6335 assert_eq!(args[1].to_text().unwrap(), "c");
6336 for arg in args {
6337 unsafe { arg.__free_internal_type() }
6338 }
6339 }
6340
6341 #[test]
6342 fn test_module_args_unterminated_string() {
6343 let sql = "CREATE VIRTUAL TABLE x USING modname('arg1, 'arg2');";
6344 assert!(module_args_from_sql(sql).is_err());
6345 }
6346
6347 #[test]
6348 fn test_module_args_extra_garbage_after_quote() {
6349 let sql = "CREATE VIRTUAL TABLE x USING modname('arg1'x);";
6350 assert!(module_args_from_sql(sql).is_err());
6351 }
6352
6353 #[test]
6354 fn test_module_args_trailing_comma() {
6355 let sql = "CREATE VIRTUAL TABLE x USING modname('arg1',);";
6356 let args = module_args_from_sql(sql).unwrap();
6357 assert_eq!(args.len(), 1);
6358 assert_eq!("arg1", args[0].to_text().unwrap());
6359 for arg in args {
6360 unsafe { arg.__free_internal_type() }
6361 }
6362 }
6363
6364 #[test]
6365 fn test_parse_numeric_literal_hex() {
6366 assert_eq!(
6367 parse_numeric_literal("0x1234").unwrap(),
6368 Value::from_i64(4660)
6369 );
6370 assert_eq!(
6371 parse_numeric_literal("0xFFFFFFFF").unwrap(),
6372 Value::from_i64(4294967295)
6373 );
6374 assert_eq!(
6375 parse_numeric_literal("0x7FFFFFFF").unwrap(),
6376 Value::from_i64(2147483647)
6377 );
6378 assert_eq!(
6379 parse_numeric_literal("0x7FFFFFFFFFFFFFFF").unwrap(),
6380 Value::from_i64(9223372036854775807)
6381 );
6382 assert_eq!(
6383 parse_numeric_literal("0xFFFFFFFFFFFFFFFF").unwrap(),
6384 Value::from_i64(-1)
6385 );
6386 assert_eq!(
6387 parse_numeric_literal("0x8000000000000000").unwrap(),
6388 Value::from_i64(-9223372036854775808)
6389 );
6390
6391 assert_eq!(
6392 parse_numeric_literal("-0x1234").unwrap(),
6393 Value::from_i64(-4660)
6394 );
6395 assert!(parse_numeric_literal("-0x8000000000000000").is_err());
6397 }
6398
6399 #[test]
6400 fn test_parse_numeric_literal_integer() {
6401 assert_eq!(parse_numeric_literal("123").unwrap(), Value::from_i64(123));
6402 assert_eq!(
6403 parse_numeric_literal("9_223_372_036_854_775_807").unwrap(),
6404 Value::from_i64(9223372036854775807)
6405 );
6406 }
6407
6408 #[test]
6409 fn test_parse_numeric_literal_float() {
6410 assert_eq!(
6411 parse_numeric_literal("123.456").unwrap(),
6412 Value::from_f64(123.456)
6413 );
6414 assert_eq!(
6415 parse_numeric_literal(".123").unwrap(),
6416 Value::from_f64(0.123)
6417 );
6418 assert_eq!(
6419 parse_numeric_literal("1.23e10").unwrap(),
6420 Value::from_f64(1.23e10)
6421 );
6422 assert_eq!(
6423 parse_numeric_literal("1e-10").unwrap(),
6424 Value::from_f64(1e-10)
6425 );
6426 assert_eq!(
6427 parse_numeric_literal("1.23E+10").unwrap(),
6428 Value::from_f64(1.23e10)
6429 );
6430 assert_eq!(
6431 parse_numeric_literal("1.1_1").unwrap(),
6432 Value::from_f64(1.11)
6433 );
6434
6435 assert_eq!(
6437 parse_numeric_literal("9223372036854775808").unwrap(),
6438 Value::from_f64(9.223_372_036_854_776e18)
6439 );
6440 assert_eq!(
6442 parse_numeric_literal("-9223372036854775809").unwrap(),
6443 Value::from_f64(-9.223_372_036_854_776e18)
6444 );
6445 }
6446
6447 #[test]
6448 fn test_parse_pragma_bool() {
6449 assert!(parse_pragma_bool(&Expr::Literal(Literal::Numeric("1".into()))).unwrap(),);
6450 assert!(parse_pragma_bool(&Expr::Name(Name::exact("true".into()))).unwrap(),);
6451 assert!(parse_pragma_bool(&Expr::Name(Name::exact("on".into()))).unwrap(),);
6452 assert!(parse_pragma_bool(&Expr::Name(Name::exact("yes".into()))).unwrap(),);
6453
6454 assert!(!parse_pragma_bool(&Expr::Literal(Literal::Numeric("0".into()))).unwrap(),);
6455 assert!(!parse_pragma_bool(&Expr::Name(Name::exact("false".into()))).unwrap(),);
6456 assert!(!parse_pragma_bool(&Expr::Name(Name::exact("off".into()))).unwrap(),);
6457 assert!(!parse_pragma_bool(&Expr::Name(Name::exact("no".into()))).unwrap(),);
6458
6459 assert!(parse_pragma_bool(&Expr::Name(Name::exact("nono".into()))).is_err());
6460 assert!(parse_pragma_bool(&Expr::Name(Name::exact("10".into()))).is_err());
6461 assert!(parse_pragma_bool(&Expr::Name(Name::exact("-1".into()))).is_err());
6462 }
6463
6464 #[test]
6465 fn test_type_from_name() {
6466 let tc = vec![
6467 ("", (SchemaValueType::Blob, false)),
6468 ("INTEGER", (SchemaValueType::Integer, true)),
6469 ("INT", (SchemaValueType::Integer, false)),
6470 ("CHAR", (SchemaValueType::Text, false)),
6471 ("CLOB", (SchemaValueType::Text, false)),
6472 ("TEXT", (SchemaValueType::Text, false)),
6473 ("BLOB", (SchemaValueType::Blob, false)),
6474 ("REAL", (SchemaValueType::Real, false)),
6475 ("FLOAT", (SchemaValueType::Real, false)),
6476 ("DOUBLE", (SchemaValueType::Real, false)),
6477 ("U128", (SchemaValueType::Numeric, false)),
6478 ];
6479
6480 for (input, expected) in tc {
6481 let result = type_from_name(input);
6482 assert_eq!(result, expected, "Failed for input: {input}");
6483 }
6484 }
6485
6486 #[test]
6487 fn test_checked_cast_text_to_numeric_lossless_property() {
6488 assert_eq!(checked_cast_text_to_numeric("1.xx", true), Err(()));
6489 assert_eq!(checked_cast_text_to_numeric("abc", true), Err(()));
6490 assert_eq!(checked_cast_text_to_numeric("--5", true), Err(()));
6491 assert_eq!(checked_cast_text_to_numeric("12.34.56", true), Err(()));
6492 assert_eq!(checked_cast_text_to_numeric("", true), Err(()));
6493 assert_eq!(checked_cast_text_to_numeric(" ", true), Err(()));
6494 assert_eq!(
6495 checked_cast_text_to_numeric("0", true),
6496 Ok(Value::from_i64(0))
6497 );
6498 assert_eq!(
6499 checked_cast_text_to_numeric("42", true),
6500 Ok(Value::from_i64(42))
6501 );
6502 assert_eq!(
6503 checked_cast_text_to_numeric("-42", true),
6504 Ok(Value::from_i64(-42))
6505 );
6506 assert_eq!(
6507 checked_cast_text_to_numeric("999999999999", true),
6508 Ok(Value::from_i64(999_999_999_999))
6509 );
6510 assert_eq!(
6511 checked_cast_text_to_numeric("1.0", true),
6512 Ok(Value::from_i64(1))
6513 );
6514 assert_eq!(
6515 checked_cast_text_to_numeric("-3.22", true),
6516 Ok(Value::from_f64(-3.22))
6517 );
6518 assert_eq!(
6519 checked_cast_text_to_numeric("0.001", true),
6520 Ok(Value::from_f64(0.001))
6521 );
6522 assert_eq!(
6523 checked_cast_text_to_numeric("2e3", true),
6524 Ok(Value::from_i64(2000))
6525 );
6526 assert_eq!(
6527 checked_cast_text_to_numeric("-5.5e-2", true),
6528 Ok(Value::from_f64(-0.055))
6529 );
6530 assert_eq!(
6531 checked_cast_text_to_numeric(" 123 ", true),
6532 Ok(Value::from_i64(123))
6533 );
6534 assert_eq!(
6535 checked_cast_text_to_numeric("\t-3.22\n", true),
6536 Ok(Value::from_f64(-3.22))
6537 );
6538 }
6539
6540 #[test]
6541 fn test_trim_ascii_whitespace_helper() {
6542 assert_eq!(trim_ascii_whitespace(" hello "), "hello");
6543 assert_eq!(trim_ascii_whitespace("\t\nhello\r\n"), "hello");
6544 assert_eq!(trim_ascii_whitespace("hello"), "hello");
6545 assert_eq!(trim_ascii_whitespace(" "), "");
6546 assert_eq!(trim_ascii_whitespace(""), "");
6547
6548 assert_eq!(
6550 trim_ascii_whitespace("\u{00A0}hello\u{00A0}"),
6551 "\u{00A0}hello\u{00A0}"
6552 );
6553 assert_eq!(
6554 trim_ascii_whitespace(" \u{00A0}hello\u{00A0} "),
6555 "\u{00A0}hello\u{00A0}"
6556 );
6557 }
6558
6559 #[test]
6560 fn test_cast_real_to_integer_limits() {
6561 let max_exact = ((1i64 << 51) - 1) as f64;
6563 assert_eq!(cast_real_to_integer(max_exact), Ok((1i64 << 51) - 1));
6564 assert_eq!(cast_real_to_integer(-max_exact), Ok(-((1i64 << 51) - 1)));
6565
6566 assert_eq!(cast_real_to_integer((1i64 << 51) as f64), Ok(1i64 << 51));
6568 assert_eq!(cast_real_to_integer((1i64 << 52) as f64), Ok(1i64 << 52));
6569
6570 assert_eq!(cast_real_to_integer((1i64 << 62) as f64), Ok(1i64 << 62));
6572
6573 assert_eq!(
6575 cast_real_to_integer(426601719749026560.0),
6576 Ok(426601719749026560)
6577 );
6578
6579 assert_eq!(cast_real_to_integer(i64::MIN as f64), Err(()));
6582 assert_eq!(cast_real_to_integer(i64::MAX as f64), Err(()));
6583
6584 assert_eq!(cast_real_to_integer(9223372036854775808.0), Err(()));
6586
6587 assert_eq!(cast_real_to_integer(-9223372036854777856.0), Err(()));
6589
6590 assert_eq!(cast_real_to_integer(1.5), Err(()));
6592 assert_eq!(cast_real_to_integer(-1.5), Err(()));
6593
6594 assert_eq!(cast_real_to_integer(f64::INFINITY), Err(()));
6596 assert_eq!(cast_real_to_integer(f64::NEG_INFINITY), Err(()));
6597 assert_eq!(cast_real_to_integer(f64::NAN), Err(()));
6598 }
6599}