diff --git a/core/translate/display.rs b/core/translate/display.rs index 8647b7f9d..78f72e1ea 100644 --- a/core/translate/display.rs +++ b/core/translate/display.rs @@ -2,14 +2,20 @@ use core::fmt; use std::fmt::{Display, Formatter}; use turso_sqlite3_parser::{ - ast::{SortOrder, TableInternalId}, - to_sql_string::{ToSqlContext, ToSqlString}, + ast::{ + self, + fmt::{ToTokens, TokenStream}, + SortOrder, TableInternalId, + }, + dialect::TokenType, + to_sql_string::ToSqlContext, }; use crate::{schema::Table, translate::plan::TableReferences}; use super::plan::{ - Aggregate, DeletePlan, JoinedTable, Operation, Plan, Search, SelectPlan, UpdatePlan, + Aggregate, DeletePlan, JoinedTable, Operation, Plan, ResultSetColumn, Search, SelectPlan, + UpdatePlan, }; impl Display for Aggregate { @@ -262,11 +268,16 @@ impl ToSqlContext for PlanContext<'_> { } } -impl ToSqlString for Plan { - fn to_sql_string(&self, context: &C) -> String { - // Make the Plans pass their own context +impl ToTokens for Plan { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { - Self::Select(select) => select.to_sql_string(&PlanContext(&[&select.table_references])), + Self::Select(select) => { + select.to_tokens_with_context(s, &PlanContext(&[&select.table_references]))?; + } Self::CompoundSelect { left, right_most, @@ -281,186 +292,188 @@ impl ToSqlString for Plan { .collect::>(); let context = &PlanContext(all_refs.as_slice()); - let mut ret = Vec::new(); for (plan, operator) in left { - ret.push(format!("{} {}", plan.to_sql_string(context), operator)); + plan.to_tokens_with_context(s, context)?; + operator.to_tokens_with_context(s, context)?; } - ret.push(right_most.to_sql_string(context)); - if let Some(order_by) = &order_by { - ret.push(format!( - "ORDER BY {}", - order_by - .iter() - .map(|(expr, order)| format!( - "{} {}", - expr.to_sql_string(context), - order - )) - .collect::>() - .join(", ") - )); + + right_most.to_tokens_with_context(s, context)?; + + if let Some(order_by) = order_by { + s.append(TokenType::TK_ORDER, None)?; + s.append(TokenType::TK_BY, None)?; + + s.comma( + order_by.iter().map(|(expr, order)| ast::SortedColumn { + expr: expr.clone(), + order: Some(*order), + nulls: None, + }), + context, + )?; } + if let Some(limit) = &limit { - ret.push(format!("LIMIT {limit}")); + s.append(TokenType::TK_LIMIT, None)?; + s.append(TokenType::TK_FLOAT, Some(&limit.to_string()))?; } + if let Some(offset) = &offset { - ret.push(format!("OFFSET {offset}")); + s.append(TokenType::TK_OFFSET, None)?; + s.append(TokenType::TK_FLOAT, Some(&offset.to_string()))?; } - ret.join(" ") } - Self::Delete(delete) => delete.to_sql_string(context), - Self::Update(update) => update.to_sql_string(context), + Self::Delete(delete) => delete.to_tokens_with_context(s, context)?, + Self::Update(update) => update.to_tokens_with_context(s, context)?, } + + Ok(()) } } -impl ToSqlString for JoinedTable { - fn to_sql_string( +impl ToTokens for JoinedTable { + fn to_tokens_with_context( &self, + s: &mut S, _context: &C, - ) -> String { - let table_or_subquery = - match &self.table { - Table::BTree(..) | Table::Virtual(..) => self.table.get_name().to_string(), - Table::FromClauseSubquery(from_clause_subquery) => { - // Could possibly merge the contexts together here - format!( - "({})", - from_clause_subquery.plan.to_sql_string(&PlanContext(&[ - &from_clause_subquery.plan.table_references - ])) - ) + ) -> Result<(), S::Error> { + match &self.table { + Table::BTree(..) | Table::Virtual(..) => { + let name = self.table.get_name(); + s.append(TokenType::TK_ID, Some(name))?; + if self.identifier != name { + s.append(TokenType::TK_AS, None)?; + s.append(TokenType::TK_ID, Some(&self.identifier))?; } - }; - // JOIN is done at a higher level - format!( - "{}{}", - table_or_subquery, - if self.identifier != table_or_subquery { - format!(" AS {}", self.identifier) - } else { - "".to_string() } - ) + Table::FromClauseSubquery(from_clause_subquery) => { + s.append(TokenType::TK_LP, None)?; + // Could possibly merge the contexts together here + from_clause_subquery.plan.to_tokens_with_context( + s, + &PlanContext(&[&from_clause_subquery.plan.table_references]), + )?; + s.append(TokenType::TK_RP, None)?; + + s.append(TokenType::TK_AS, None)?; + s.append(TokenType::TK_ID, Some(&self.identifier))?; + } + }; + + Ok(()) } } // TODO: currently cannot print the original CTE as it is optimized into a subquery -impl ToSqlString for SelectPlan { - fn to_sql_string( +impl ToTokens for SelectPlan { + fn to_tokens_with_context( &self, + s: &mut S, context: &C, - ) -> String { - let mut ret = Vec::new(); - // VALUES SELECT statement + ) -> Result<(), S::Error> { if !self.values.is_empty() { - ret.push(format!( - "VALUES {}", - self.values - .iter() - .map(|value| { - let joined_value = value - .iter() - .map(|e| e.to_sql_string(context)) - .collect::>() - .join(", "); - format!("({joined_value})") - }) - .collect::>() - .join(", ") - )); + ast::OneSelect::Values(self.values.clone()).to_tokens_with_context(s, context)?; } else { - // standard SELECT statement - ret.push("SELECT".to_string()); + s.append(TokenType::TK_SELECT, None)?; if self.distinctness.is_distinct() { - ret.push("DISTINCT".to_string()); + s.append(TokenType::TK_DISTINCT, None)?; } - ret.push( - self.result_columns - .iter() - .map(|cols| { - format!( - "{}{}", - cols.expr.to_sql_string(context), - cols.alias - .as_ref() - .map_or("".to_string(), |alias| format!(" AS {alias}")) - ) - }) - .collect::>() - .join(", "), - ); - ret.push("FROM".to_string()); - ret.extend(self.join_order.iter().enumerate().map(|(idx, order)| { - let table_ref = self.joined_tables().get(order.original_idx).unwrap(); - if idx == 0 { - table_ref.to_sql_string(context) - } else { - format!( - "{}JOIN {}", - if order.is_outer { "OUTER " } else { "" }, - table_ref.to_sql_string(context) - ) + for (i, ResultSetColumn { expr, alias, .. }) in self.result_columns.iter().enumerate() { + if i != 0 { + s.append(TokenType::TK_COMMA, None)?; + } + + expr.to_tokens_with_context(s, context)?; + if let Some(alias) = alias { + s.append(TokenType::TK_AS, None)?; + s.append(TokenType::TK_ID, Some(alias))?; } - })); - if !self.where_clause.is_empty() { - ret.push("WHERE".to_string()); - ret.push( - self.where_clause - .iter() - .map(|where_clause| where_clause.expr.to_sql_string(context)) - .collect::>() - .join(" AND "), - ); } + s.append(TokenType::TK_FROM, None)?; + + for (i, order) in self.join_order.iter().enumerate() { + if i != 0 { + if order.is_outer { + s.append(TokenType::TK_ORDER, None)?; + } + s.append(TokenType::TK_JOIN, None)?; + } + + let table_ref = self.joined_tables().get(order.original_idx).unwrap(); + table_ref.to_tokens_with_context(s, context)?; + } + + if !self.where_clause.is_empty() { + s.append(TokenType::TK_WHERE, None)?; + + for (i, expr) in self + .where_clause + .iter() + .map(|where_clause| where_clause.expr.clone()) + .enumerate() + { + if i != 0 { + s.append(TokenType::TK_AND, None)?; + } + expr.to_tokens_with_context(s, context)?; + } + } + if let Some(group_by) = &self.group_by { - // TODO: see later if group_by needs more context to parse the expressions - // We will see this when this panics - ret.push("GROUP BY".to_string()); - ret.push( - group_by - .exprs - .iter() - .map(|expr| expr.to_sql_string(context)) - .collect::>() - .join(", "), - ); + s.append(TokenType::TK_GROUP, None)?; + s.append(TokenType::TK_BY, None)?; + + s.comma(group_by.exprs.iter(), context)?; + // TODO: not sure where I need to place the group_by.sort_order if let Some(having) = &group_by.having { - ret.push("HAVING".to_string()); - ret.push( - having - .iter() - .map(|expr| expr.to_sql_string(context)) - .collect::>() - .join(" AND "), - ); + s.append(TokenType::TK_HAVING, None)?; + + for (i, expr) in having.iter().enumerate() { + if i != 0 { + s.append(TokenType::TK_AND, None)?; + } + expr.to_tokens_with_context(s, context)?; + } } } } + if let Some(order_by) = &self.order_by { - ret.push(format!( - "ORDER BY {}", - order_by - .iter() - .map(|(expr, order)| format!("{} {}", expr.to_sql_string(context), order)) - .collect::>() - .join(", ") - )); + s.append(TokenType::TK_ORDER, None)?; + s.append(TokenType::TK_BY, None)?; + + s.comma( + order_by.iter().map(|(expr, order)| ast::SortedColumn { + expr: expr.clone(), + order: Some(*order), + nulls: None, + }), + context, + )?; } + if let Some(limit) = &self.limit { - ret.push(format!("LIMIT {limit}")); + s.append(TokenType::TK_LIMIT, None)?; + s.append(TokenType::TK_FLOAT, Some(&limit.to_string()))?; } + if let Some(offset) = &self.offset { - ret.push(format!("OFFSET {offset}")); + s.append(TokenType::TK_OFFSET, None)?; + s.append(TokenType::TK_FLOAT, Some(&offset.to_string()))?; } - ret.join(" ") + + Ok(()) } } -impl ToSqlString for DeletePlan { - fn to_sql_string(&self, _context: &C) -> String { +impl ToTokens for DeletePlan { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { let table = self .table_references .joined_tables() @@ -468,42 +481,61 @@ impl ToSqlString for DeletePlan { .expect("Delete Plan should have only one table reference"); let context = &[&self.table_references]; let context = &PlanContext(context); - let mut ret = Vec::new(); - ret.push(format!("DELETE FROM {}", table.table.get_name())); + s.append(TokenType::TK_DELETE, None)?; + s.append(TokenType::TK_FROM, None)?; + s.append(TokenType::TK_ID, Some(table.table.get_name()))?; if !self.where_clause.is_empty() { - ret.push("WHERE".to_string()); - ret.push( - self.where_clause - .iter() - .map(|where_clause| where_clause.expr.to_sql_string(context)) - .collect::>() - .join(" AND "), - ); + s.append(TokenType::TK_WHERE, None)?; + + for (i, expr) in self + .where_clause + .iter() + .map(|where_clause| where_clause.expr.clone()) + .enumerate() + { + if i != 0 { + s.append(TokenType::TK_AND, None)?; + } + expr.to_tokens_with_context(s, context)?; + } } + if let Some(order_by) = &self.order_by { - ret.push(format!( - "ORDER BY {}", - order_by - .iter() - .map(|(expr, order)| format!("{} {}", expr.to_sql_string(context), order)) - .collect::>() - .join(", ") - )); + s.append(TokenType::TK_ORDER, None)?; + s.append(TokenType::TK_BY, None)?; + + s.comma( + order_by.iter().map(|(expr, order)| ast::SortedColumn { + expr: expr.clone(), + order: Some(*order), + nulls: None, + }), + context, + )?; } + if let Some(limit) = &self.limit { - ret.push(format!("LIMIT {limit}")); + s.append(TokenType::TK_LIMIT, None)?; + s.append(TokenType::TK_FLOAT, Some(&limit.to_string()))?; } + if let Some(offset) = &self.offset { - ret.push(format!("OFFSET {offset}")); + s.append(TokenType::TK_OFFSET, None)?; + s.append(TokenType::TK_FLOAT, Some(&offset.to_string()))?; } - ret.join(" ") + + Ok(()) } } -impl ToSqlString for UpdatePlan { - fn to_sql_string(&self, _context: &C) -> String { +impl ToTokens for UpdatePlan { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { let table = self .table_references .joined_tables() @@ -511,60 +543,69 @@ impl ToSqlString for UpdatePlan { .expect("UPDATE Plan should have only one table reference"); let context = [&self.table_references]; let context = &PlanContext(&context); - let mut ret = Vec::new(); - // TODO: we don't work with conflict clauses yet + s.append(TokenType::TK_UPDATE, None)?; + s.append(TokenType::TK_ID, Some(table.table.get_name()))?; + s.append(TokenType::TK_SET, None)?; - ret.push(format!("UPDATE {} SET", table.table.get_name())); + s.comma( + self.set_clauses.iter().map(|(col_idx, set_expr)| { + let col_name = table + .table + .get_column_at(*col_idx) + .as_ref() + .unwrap() + .name + .as_ref() + .unwrap(); - // TODO: does not support column_name_list yet - ret.push( - self.set_clauses - .iter() - .map(|(col_idx, set_expr)| { - format!( - "{} = {}", - table - .table - .get_column_at(*col_idx) - .as_ref() - .unwrap() - .name - .as_ref() - .unwrap(), - set_expr.to_sql_string(context) - ) - }) - .collect::>() - .join(", "), - ); + ast::Set { + col_names: ast::DistinctNames::single(ast::Name(col_name.clone())), + expr: set_expr.clone(), + } + }), + context, + )?; if !self.where_clause.is_empty() { - ret.push("WHERE".to_string()); - ret.push( - self.where_clause - .iter() - .map(|where_clause| where_clause.expr.to_sql_string(context)) - .collect::>() - .join(" AND "), - ); + s.append(TokenType::TK_WHERE, None)?; + + let mut iter = self + .where_clause + .iter() + .map(|where_clause| where_clause.expr.clone()); + iter.next() + .expect("should not be empty") + .to_tokens_with_context(s, context)?; + for expr in iter { + s.append(TokenType::TK_AND, None)?; + expr.to_tokens_with_context(s, context)?; + } } + if let Some(order_by) = &self.order_by { - ret.push(format!( - "ORDER BY {}", - order_by - .iter() - .map(|(expr, order)| format!("{} {}", expr.to_sql_string(context), order)) - .collect::>() - .join(", ") - )); + s.append(TokenType::TK_ORDER, None)?; + s.append(TokenType::TK_BY, None)?; + + s.comma( + order_by.iter().map(|(expr, order)| ast::SortedColumn { + expr: expr.clone(), + order: Some(*order), + nulls: None, + }), + context, + )?; } + if let Some(limit) = &self.limit { - ret.push(format!("LIMIT {limit}")); + s.append(TokenType::TK_LIMIT, None)?; + s.append(TokenType::TK_FLOAT, Some(&limit.to_string()))?; } if let Some(offset) = &self.offset { - ret.push(format!("OFFSET {offset}")); + s.append(TokenType::TK_OFFSET, None)?; + s.append(TokenType::TK_FLOAT, Some(&offset.to_string()))?; } - ret.join(" ") + + Ok(()) } } diff --git a/core/translate/optimizer/mod.rs b/core/translate/optimizer/mod.rs index e77c0224d..0032469a5 100644 --- a/core/translate/optimizer/mod.rs +++ b/core/translate/optimizer/mod.rs @@ -7,10 +7,7 @@ use cost::Cost; use join::{compute_best_join_order, BestJoinOrderResult}; use lift_common_subexpressions::lift_common_subexpressions_from_binary_or_terms; use order::{compute_order_target, plan_satisfies_order_target, EliminatesSortBy}; -use turso_sqlite3_parser::{ - ast::{self, Expr, SortOrder}, - to_sql_string::ToSqlString as _, -}; +use turso_sqlite3_parser::ast::{self, fmt::ToTokens as _, Expr, SortOrder}; use crate::{ parameters::PARAM_PREFIX, @@ -51,7 +48,11 @@ pub fn optimize_plan(plan: &mut Plan, schema: &Schema) -> Result<()> { } } // When debug tracing is enabled, print the optimized plan as a SQL string for debugging - tracing::debug!(plan_sql = plan.to_sql_string(&crate::translate::display::PlanContext(&[]))); + tracing::debug!( + plan_sql = plan + .format_with_context(&crate::translate::display::PlanContext(&[])) + .unwrap() + ); Ok(()) } diff --git a/simulator/model/query/predicate.rs b/simulator/model/query/predicate.rs index dad89f430..45c344786 100644 --- a/simulator/model/query/predicate.rs +++ b/simulator/model/query/predicate.rs @@ -1,12 +1,9 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; -use turso_sqlite3_parser::{ast, to_sql_string::ToSqlString}; +use turso_sqlite3_parser::ast::{self, fmt::ToTokens}; -use crate::model::{ - query::EmptyContext, - table::{SimValue, Table}, -}; +use crate::model::table::{SimValue, Table}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Predicate(pub ast::Expr); @@ -138,6 +135,6 @@ pub fn expr_to_value(expr: &ast::Expr, row: &[SimValue], table: &Table) -> Optio impl Display for Predicate { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.0.to_sql_string(&EmptyContext)) + self.0.to_fmt(f) } } diff --git a/simulator/model/query/select.rs b/simulator/model/query/select.rs index 17d0a6844..9cc9fc4b8 100644 --- a/simulator/model/query/select.rs +++ b/simulator/model/query/select.rs @@ -4,7 +4,7 @@ use anyhow::Context; pub use ast::Distinctness; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use turso_sqlite3_parser::{ast, to_sql_string::ToSqlString}; +use turso_sqlite3_parser::ast::{self, fmt::ToTokens}; use crate::{ generation::Shadow, @@ -383,7 +383,7 @@ impl Shadow for SelectInner { } else { return Err(anyhow::anyhow!( "Failed to evaluate expression in free select ({})", - expr.0.to_sql_string(&EmptyContext {}) + expr.0.format_with_context(&EmptyContext {}).unwrap() )); } } @@ -508,7 +508,7 @@ impl Select { } impl Display for Select { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.to_sql_ast().to_sql_string(&EmptyContext {})) + self.to_sql_ast().to_fmt_with_context(f, &EmptyContext {}) } } diff --git a/vendored/sqlite3-parser/src/dialect/mod.rs b/vendored/sqlite3-parser/src/dialect/mod.rs index c1325f4b2..f67d95655 100644 --- a/vendored/sqlite3-parser/src/dialect/mod.rs +++ b/vendored/sqlite3-parser/src/dialect/mod.rs @@ -222,7 +222,7 @@ impl TokenType { TK_LE => Some("<="), TK_LT => Some("<"), TK_MINUS => Some("-"), - TK_NE => Some("<>"), // or != + TK_NE => Some("!="), // or <> TK_PLUS => Some("+"), TK_REM => Some("%"), TK_RP => Some(")"), diff --git a/vendored/sqlite3-parser/src/parser/ast/fmt.rs b/vendored/sqlite3-parser/src/parser/ast/fmt.rs index 6598e71d7..c95f062bb 100644 --- a/vendored/sqlite3-parser/src/parser/ast/fmt.rs +++ b/vendored/sqlite3-parser/src/parser/ast/fmt.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Display, Formatter, Write}; use crate::ast::*; use crate::dialect::TokenType::*; +use crate::to_sql_string::ToSqlContext; struct FmtTokenStream<'a, 'b> { f: &'a mut Formatter<'b>, @@ -85,65 +86,128 @@ impl TokenStream for WriteTokenStream<'_, T> { } } +struct BlankContext; + +impl ToSqlContext for BlankContext { + fn get_column_name(&self, _table_id: crate::ast::TableInternalId, _col_idx: usize) -> &str { + "" + } + + fn get_table_name(&self, _id: crate::ast::TableInternalId) -> &str { + "" + } +} + /// Stream of token pub trait TokenStream { /// Potential error raised type Error; /// Push token to this stream fn append(&mut self, ty: TokenType, value: Option<&str>) -> Result<(), Self::Error>; + /// Interspace iterator with commas + fn comma(&mut self, items: I, context: &C) -> Result<(), Self::Error> + where + I: IntoIterator, + I::Item: ToTokens, + { + let iter = items.into_iter(); + for (i, item) in iter.enumerate() { + if i != 0 { + self.append(TK_COMMA, None)?; + } + item.to_tokens_with_context(self, context)?; + } + Ok(()) + } } /// Generate token(s) from AST node pub trait ToTokens { + /// Send token(s) to the specified stream with context + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error>; + /// Send token(s) to the specified stream - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error>; + fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + self.to_tokens_with_context(s, &BlankContext) + } + /// Format AST node fn to_fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let mut s = FmtTokenStream { f, spaced: true }; - self.to_tokens(&mut s) + self.to_fmt_with_context(f, &BlankContext) } + + /// Format AST node with context + fn to_fmt_with_context( + &self, + f: &mut Formatter<'_>, + context: &C, + ) -> fmt::Result { + let mut s = FmtTokenStream { f, spaced: true }; + self.to_tokens_with_context(&mut s, context) + } + /// Format AST node to string fn format(&self) -> Result { - let mut s = String::new(); + self.format_with_context(&BlankContext) + } + /// Format AST node to string with context + fn format_with_context(&self, context: &C) -> Result { + let mut s = String::new(); let mut w = WriteTokenStream { write: &mut s, spaced: true, }; - self.to_tokens(&mut w)?; + self.to_tokens_with_context(&mut w, context)?; Ok(s) } } impl ToTokens for &T { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - ToTokens::to_tokens(&**self, s) + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + ToTokens::to_tokens_with_context(&**self, s, context) } } impl ToTokens for String { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append(TK_ANY, Some(self.as_ref())) } } impl ToTokens for Cmd { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Explain(stmt) => { s.append(TK_EXPLAIN, None)?; - stmt.to_tokens(s)?; + stmt.to_tokens_with_context(s, context)?; } Self::ExplainQueryPlan(stmt) => { s.append(TK_EXPLAIN, None)?; s.append(TK_QUERY, None)?; s.append(TK_PLAN, None)?; - stmt.to_tokens(s)?; + stmt.to_tokens_with_context(s, context)?; } Self::Stmt(stmt) => { - stmt.to_tokens(s)?; + stmt.to_tokens_with_context(s, context)?; } } s.append(TK_SEMI, None) @@ -157,41 +221,45 @@ impl Display for Cmd { } impl ToTokens for Stmt { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::AlterTable(alter_table) => { let (tbl_name, body) = &**alter_table; s.append(TK_ALTER, None)?; s.append(TK_TABLE, None)?; - tbl_name.to_tokens(s)?; - body.to_tokens(s) + tbl_name.to_tokens_with_context(s, context)?; + body.to_tokens_with_context(s, context) } Self::Analyze(obj_name) => { s.append(TK_ANALYZE, None)?; if let Some(obj_name) = obj_name { - obj_name.to_tokens(s)?; + obj_name.to_tokens_with_context(s, context)?; } Ok(()) } Self::Attach { expr, db_name, key } => { s.append(TK_ATTACH, None)?; - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; s.append(TK_AS, None)?; - db_name.to_tokens(s)?; + db_name.to_tokens_with_context(s, context)?; if let Some(key) = key { s.append(TK_KEY, None)?; - key.to_tokens(s)?; + key.to_tokens_with_context(s, context)?; } Ok(()) } Self::Begin(tx_type, tx_name) => { s.append(TK_BEGIN, None)?; if let Some(tx_type) = tx_type { - tx_type.to_tokens(s)?; + tx_type.to_tokens_with_context(s, context)?; } if let Some(tx_name) = tx_name { s.append(TK_TRANSACTION, None)?; - tx_name.to_tokens(s)?; + tx_name.to_tokens_with_context(s, context)?; } Ok(()) } @@ -199,7 +267,7 @@ impl ToTokens for Stmt { s.append(TK_COMMIT, None)?; if let Some(tx_name) = tx_name { s.append(TK_TRANSACTION, None)?; - tx_name.to_tokens(s)?; + tx_name.to_tokens_with_context(s, context)?; } Ok(()) } @@ -221,15 +289,15 @@ impl ToTokens for Stmt { s.append(TK_NOT, None)?; s.append(TK_EXISTS, None)?; } - idx_name.to_tokens(s)?; + idx_name.to_tokens_with_context(s, context)?; s.append(TK_ON, None)?; - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; s.append(TK_RP, None)?; if let Some(where_clause) = where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } Ok(()) } @@ -249,8 +317,8 @@ impl ToTokens for Stmt { s.append(TK_NOT, None)?; s.append(TK_EXISTS, None)?; } - tbl_name.to_tokens(s)?; - body.to_tokens(s) + tbl_name.to_tokens_with_context(s, context)?; + body.to_tokens_with_context(s, context) } Self::CreateTrigger(trigger) => { let CreateTrigger { @@ -274,13 +342,13 @@ impl ToTokens for Stmt { s.append(TK_NOT, None)?; s.append(TK_EXISTS, None)?; } - trigger_name.to_tokens(s)?; + trigger_name.to_tokens_with_context(s, context)?; if let Some(time) = time { - time.to_tokens(s)?; + time.to_tokens_with_context(s, context)?; } - event.to_tokens(s)?; + event.to_tokens_with_context(s, context)?; s.append(TK_ON, None)?; - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; if *for_each_row { s.append(TK_FOR, None)?; s.append(TK_EACH, None)?; @@ -288,11 +356,11 @@ impl ToTokens for Stmt { } if let Some(when_clause) = when_clause { s.append(TK_WHEN, None)?; - when_clause.to_tokens(s)?; + when_clause.to_tokens_with_context(s, context)?; } s.append(TK_BEGIN, Some("\n"))?; for command in commands { - command.to_tokens(s)?; + command.to_tokens_with_context(s, context)?; s.append(TK_SEMI, Some("\n"))?; } s.append(TK_END, None) @@ -314,14 +382,14 @@ impl ToTokens for Stmt { s.append(TK_NOT, None)?; s.append(TK_EXISTS, None)?; } - view_name.to_tokens(s)?; + view_name.to_tokens_with_context(s, context)?; if let Some(columns) = columns { s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; s.append(TK_RP, None)?; } s.append(TK_AS, None)?; - select.to_tokens(s) + select.to_tokens_with_context(s, context) } Self::CreateVirtualTable(create_virtual_table) => { let CreateVirtualTable { @@ -338,12 +406,12 @@ impl ToTokens for Stmt { s.append(TK_NOT, None)?; s.append(TK_EXISTS, None)?; } - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; s.append(TK_USING, None)?; - module_name.to_tokens(s)?; + module_name.to_tokens_with_context(s, context)?; s.append(TK_LP, None)?; if let Some(args) = args { - comma(args, s)?; + comma(args, s, context)?; } s.append(TK_RP, None) } @@ -358,35 +426,35 @@ impl ToTokens for Stmt { limit, } = &**delete; if let Some(with) = with { - with.to_tokens(s)?; + with.to_tokens_with_context(s, context)?; } s.append(TK_DELETE, None)?; s.append(TK_FROM, None)?; - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; if let Some(indexed) = indexed { - indexed.to_tokens(s)?; + indexed.to_tokens_with_context(s, context)?; } if let Some(where_clause) = where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } if let Some(returning) = returning { s.append(TK_RETURNING, None)?; - comma(returning, s)?; + comma(returning, s, context)?; } if let Some(order_by) = order_by { s.append(TK_ORDER, None)?; s.append(TK_BY, None)?; - comma(order_by, s)?; + comma(order_by, s, context)?; } if let Some(limit) = limit { - limit.to_tokens(s)?; + limit.to_tokens_with_context(s, context)?; } Ok(()) } Self::Detach(expr) => { s.append(TK_DETACH, None)?; - expr.to_tokens(s) + expr.to_tokens_with_context(s, context) } Self::DropIndex { if_exists, @@ -398,7 +466,7 @@ impl ToTokens for Stmt { s.append(TK_IF, None)?; s.append(TK_EXISTS, None)?; } - idx_name.to_tokens(s) + idx_name.to_tokens_with_context(s, context) } Self::DropTable { if_exists, @@ -410,7 +478,7 @@ impl ToTokens for Stmt { s.append(TK_IF, None)?; s.append(TK_EXISTS, None)?; } - tbl_name.to_tokens(s) + tbl_name.to_tokens_with_context(s, context) } Self::DropTrigger { if_exists, @@ -422,7 +490,7 @@ impl ToTokens for Stmt { s.append(TK_IF, None)?; s.append(TK_EXISTS, None)?; } - trigger_name.to_tokens(s) + trigger_name.to_tokens_with_context(s, context) } Self::DropView { if_exists, @@ -434,7 +502,7 @@ impl ToTokens for Stmt { s.append(TK_IF, None)?; s.append(TK_EXISTS, None)?; } - view_name.to_tokens(s) + view_name.to_tokens_with_context(s, context) } Self::Insert(insert) => { let Insert { @@ -446,7 +514,7 @@ impl ToTokens for Stmt { returning, } = &**insert; if let Some(with) = with { - with.to_tokens(s)?; + with.to_tokens_with_context(s, context)?; } if let Some(ResolveType::Replace) = or_conflict { s.append(TK_REPLACE, None)?; @@ -454,41 +522,41 @@ impl ToTokens for Stmt { s.append(TK_INSERT, None)?; if let Some(or_conflict) = or_conflict { s.append(TK_OR, None)?; - or_conflict.to_tokens(s)?; + or_conflict.to_tokens_with_context(s, context)?; } } s.append(TK_INTO, None)?; - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; if let Some(columns) = columns { s.append(TK_LP, None)?; - comma(columns.deref(), s)?; + comma(columns.deref(), s, context)?; s.append(TK_RP, None)?; } - body.to_tokens(s)?; + body.to_tokens_with_context(s, context)?; if let Some(returning) = returning { s.append(TK_RETURNING, None)?; - comma(returning, s)?; + comma(returning, s, context)?; } Ok(()) } Self::Pragma(name, value) => { s.append(TK_PRAGMA, None)?; - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; if let Some(value) = value { - value.to_tokens(s)?; + value.to_tokens_with_context(s, context)?; } Ok(()) } Self::Reindex { obj_name } => { s.append(TK_REINDEX, None)?; if let Some(obj_name) = obj_name { - obj_name.to_tokens(s)?; + obj_name.to_tokens_with_context(s, context)?; } Ok(()) } Self::Release(name) => { s.append(TK_RELEASE, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } Self::Rollback { tx_name, @@ -497,19 +565,19 @@ impl ToTokens for Stmt { s.append(TK_ROLLBACK, None)?; if let Some(tx_name) = tx_name { s.append(TK_TRANSACTION, None)?; - tx_name.to_tokens(s)?; + tx_name.to_tokens_with_context(s, context)?; } if let Some(savepoint_name) = savepoint_name { s.append(TK_TO, None)?; - savepoint_name.to_tokens(s)?; + savepoint_name.to_tokens_with_context(s, context)?; } Ok(()) } Self::Savepoint(name) => { s.append(TK_SAVEPOINT, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } - Self::Select(select) => select.to_tokens(s), + Self::Select(select) => select.to_tokens_with_context(s, context), Self::Update(update) => { let Update { with, @@ -524,49 +592,49 @@ impl ToTokens for Stmt { limit, } = &**update; if let Some(with) = with { - with.to_tokens(s)?; + with.to_tokens_with_context(s, context)?; } s.append(TK_UPDATE, None)?; if let Some(or_conflict) = or_conflict { s.append(TK_OR, None)?; - or_conflict.to_tokens(s)?; + or_conflict.to_tokens_with_context(s, context)?; } - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; if let Some(indexed) = indexed { - indexed.to_tokens(s)?; + indexed.to_tokens_with_context(s, context)?; } s.append(TK_SET, None)?; - comma(sets, s)?; + comma(sets, s, context)?; if let Some(from) = from { s.append(TK_FROM, None)?; - from.to_tokens(s)?; + from.to_tokens_with_context(s, context)?; } if let Some(where_clause) = where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } if let Some(returning) = returning { s.append(TK_RETURNING, None)?; - comma(returning, s)?; + comma(returning, s, context)?; } if let Some(order_by) = order_by { s.append(TK_ORDER, None)?; s.append(TK_BY, None)?; - comma(order_by, s)?; + comma(order_by, s, context)?; } if let Some(limit) = limit { - limit.to_tokens(s)?; + limit.to_tokens_with_context(s, context)?; } Ok(()) } Self::Vacuum(name, expr) => { s.append(TK_VACUUM, None)?; if let Some(ref name) = name { - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; } if let Some(ref expr) = expr { s.append(TK_INTO, None)?; - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; } Ok(()) } @@ -575,7 +643,11 @@ impl ToTokens for Stmt { } impl ToTokens for Expr { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Between { lhs, @@ -583,19 +655,19 @@ impl ToTokens for Expr { start, end, } => { - lhs.to_tokens(s)?; + lhs.to_tokens_with_context(s, context)?; if *not { s.append(TK_NOT, None)?; } s.append(TK_BETWEEN, None)?; - start.to_tokens(s)?; + start.to_tokens_with_context(s, context)?; s.append(TK_AND, None)?; - end.to_tokens(s) + end.to_tokens_with_context(s, context) } Self::Binary(lhs, op, rhs) => { - lhs.to_tokens(s)?; - op.to_tokens(s)?; - rhs.to_tokens(s) + lhs.to_tokens_with_context(s, context)?; + op.to_tokens_with_context(s, context)?; + rhs.to_tokens_with_context(s, context) } Self::Case { base, @@ -604,46 +676,46 @@ impl ToTokens for Expr { } => { s.append(TK_CASE, None)?; if let Some(ref base) = base { - base.to_tokens(s)?; + base.to_tokens_with_context(s, context)?; } for (when, then) in when_then_pairs { s.append(TK_WHEN, None)?; - when.to_tokens(s)?; + when.to_tokens_with_context(s, context)?; s.append(TK_THEN, None)?; - then.to_tokens(s)?; + then.to_tokens_with_context(s, context)?; } if let Some(ref else_expr) = else_expr { s.append(TK_ELSE, None)?; - else_expr.to_tokens(s)?; + else_expr.to_tokens_with_context(s, context)?; } s.append(TK_END, None) } Self::Cast { expr, type_name } => { s.append(TK_CAST, None)?; s.append(TK_LP, None)?; - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; s.append(TK_AS, None)?; if let Some(ref type_name) = type_name { - type_name.to_tokens(s)?; + type_name.to_tokens_with_context(s, context)?; } s.append(TK_RP, None) } Self::Collate(expr, collation) => { - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; s.append(TK_COLLATE, None)?; double_quote(collation, s) } Self::DoublyQualified(db_name, tbl_name, col_name) => { - db_name.to_tokens(s)?; + db_name.to_tokens_with_context(s, context)?; s.append(TK_DOT, None)?; - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; s.append(TK_DOT, None)?; - col_name.to_tokens(s) + col_name.to_tokens_with_context(s, context) } Self::Exists(subquery) => { s.append(TK_EXISTS, None)?; s.append(TK_LP, None)?; - subquery.to_tokens(s)?; + subquery.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } Self::FunctionCall { @@ -653,57 +725,61 @@ impl ToTokens for Expr { order_by, filter_over, } => { - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; s.append(TK_LP, None)?; if let Some(distinctness) = distinctness { - distinctness.to_tokens(s)?; + distinctness.to_tokens_with_context(s, context)?; } if let Some(args) = args { - comma(args, s)?; + comma(args, s, context)?; } if let Some(order_by) = order_by { s.append(TK_ORDER, None)?; s.append(TK_BY, None)?; - comma(order_by, s)?; + comma(order_by, s, context)?; } s.append(TK_RP, None)?; if let Some(filter_over) = filter_over { - filter_over.to_tokens(s)?; + filter_over.to_tokens_with_context(s, context)?; } Ok(()) } Self::FunctionCallStar { name, filter_over } => { - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; s.append(TK_LP, None)?; s.append(TK_STAR, None)?; s.append(TK_RP, None)?; if let Some(filter_over) = filter_over { - filter_over.to_tokens(s)?; + filter_over.to_tokens_with_context(s, context)?; } Ok(()) } - Self::Id(id) => id.to_tokens(s), - Self::Column { .. } => Ok(()), + Self::Id(id) => id.to_tokens_with_context(s, context), + Self::Column { table, column, .. } => { + s.append(TK_ID, Some(context.get_table_name(*table)))?; + s.append(TK_DOT, None)?; + s.append(TK_ID, Some(context.get_column_name(*table, *column))) + } Self::InList { lhs, not, rhs } => { - lhs.to_tokens(s)?; + lhs.to_tokens_with_context(s, context)?; if *not { s.append(TK_NOT, None)?; } s.append(TK_IN, None)?; s.append(TK_LP, None)?; if let Some(rhs) = rhs { - comma(rhs, s)?; + comma(rhs, s, context)?; } s.append(TK_RP, None) } Self::InSelect { lhs, not, rhs } => { - lhs.to_tokens(s)?; + lhs.to_tokens_with_context(s, context)?; if *not { s.append(TK_NOT, None)?; } s.append(TK_IN, None)?; s.append(TK_LP, None)?; - rhs.to_tokens(s)?; + rhs.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } Self::InTable { @@ -712,21 +788,21 @@ impl ToTokens for Expr { rhs, args, } => { - lhs.to_tokens(s)?; + lhs.to_tokens_with_context(s, context)?; if *not { s.append(TK_NOT, None)?; } s.append(TK_IN, None)?; - rhs.to_tokens(s)?; + rhs.to_tokens_with_context(s, context)?; if let Some(args) = args { s.append(TK_LP, None)?; - comma(args, s)?; + comma(args, s, context)?; s.append(TK_RP, None)?; } Ok(()) } Self::IsNull(sub_expr) => { - sub_expr.to_tokens(s)?; + sub_expr.to_tokens_with_context(s, context)?; s.append(TK_ISNULL, None) } Self::Like { @@ -736,53 +812,53 @@ impl ToTokens for Expr { rhs, escape, } => { - lhs.to_tokens(s)?; + lhs.to_tokens_with_context(s, context)?; if *not { s.append(TK_NOT, None)?; } - op.to_tokens(s)?; - rhs.to_tokens(s)?; + op.to_tokens_with_context(s, context)?; + rhs.to_tokens_with_context(s, context)?; if let Some(escape) = escape { s.append(TK_ESCAPE, None)?; - escape.to_tokens(s)?; + escape.to_tokens_with_context(s, context)?; } Ok(()) } - Self::Literal(lit) => lit.to_tokens(s), - Self::Name(name) => name.to_tokens(s), + Self::Literal(lit) => lit.to_tokens_with_context(s, context), + Self::Name(name) => name.to_tokens_with_context(s, context), Self::NotNull(sub_expr) => { - sub_expr.to_tokens(s)?; + sub_expr.to_tokens_with_context(s, context)?; s.append(TK_NOTNULL, None) } Self::Parenthesized(exprs) => { s.append(TK_LP, None)?; - comma(exprs, s)?; + comma(exprs, s, context)?; s.append(TK_RP, None) } Self::Qualified(qualifier, qualified) => { - qualifier.to_tokens(s)?; + qualifier.to_tokens_with_context(s, context)?; s.append(TK_DOT, None)?; - qualified.to_tokens(s) + qualified.to_tokens_with_context(s, context) } Self::Raise(rt, err) => { s.append(TK_RAISE, None)?; s.append(TK_LP, None)?; - rt.to_tokens(s)?; + rt.to_tokens_with_context(s, context)?; if let Some(err) = err { s.append(TK_COMMA, None)?; - err.to_tokens(s)?; + err.to_tokens_with_context(s, context)?; } s.append(TK_RP, None) } Self::RowId { .. } => Ok(()), Self::Subquery(query) => { s.append(TK_LP, None)?; - query.to_tokens(s)?; + query.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } Self::Unary(op, sub_expr) => { - op.to_tokens(s)?; - sub_expr.to_tokens(s) + op.to_tokens_with_context(s, context)?; + sub_expr.to_tokens_with_context(s, context) } Self::Variable(var) => match var.chars().next() { Some(c) if c == '$' || c == '@' || c == '#' || c == ':' => { @@ -802,7 +878,11 @@ impl Display for Expr { } impl ToTokens for Literal { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { match self { Self::Numeric(ref num) => s.append(TK_FLOAT, Some(num)), // TODO Validate TK_FLOAT Self::String(ref str) => s.append(TK_STRING, Some(str)), @@ -817,7 +897,11 @@ impl ToTokens for Literal { } impl ToTokens for LikeOperator { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( TK_LIKE_KW, Some(match self { @@ -831,7 +915,11 @@ impl ToTokens for LikeOperator { } impl ToTokens for Operator { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { match self { Self::Add => s.append(TK_PLUS, None), Self::And => s.append(TK_AND, None), @@ -864,7 +952,11 @@ impl ToTokens for Operator { } impl ToTokens for UnaryOperator { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( match self { Self::BitwiseNot => TK_BITNOT, @@ -878,29 +970,37 @@ impl ToTokens for UnaryOperator { } impl ToTokens for Select { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if let Some(ref with) = self.with { - with.to_tokens(s)?; + with.to_tokens_with_context(s, context)?; } - self.body.to_tokens(s)?; + self.body.to_tokens_with_context(s, context)?; if let Some(ref order_by) = self.order_by { s.append(TK_ORDER, None)?; s.append(TK_BY, None)?; - comma(order_by, s)?; + comma(order_by, s, context)?; } if let Some(ref limit) = self.limit { - limit.to_tokens(s)?; + limit.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for SelectBody { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.select.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.select.to_tokens_with_context(s, context)?; if let Some(ref compounds) = self.compounds { for compound in compounds { - compound.to_tokens(s)?; + compound.to_tokens_with_context(s, context)?; } } Ok(()) @@ -908,14 +1008,22 @@ impl ToTokens for SelectBody { } impl ToTokens for CompoundSelect { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.operator.to_tokens(s)?; - self.select.to_tokens(s) + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.operator.to_tokens_with_context(s, context)?; + self.select.to_tokens_with_context(s, context) } } impl ToTokens for CompoundOperator { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { match self { Self::Union => s.append(TK_UNION, None), Self::UnionAll => { @@ -935,7 +1043,11 @@ impl Display for CompoundOperator { } impl ToTokens for OneSelect { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Select(select) => { let SelectInner { @@ -948,23 +1060,23 @@ impl ToTokens for OneSelect { } = &**select; s.append(TK_SELECT, None)?; if let Some(ref distinctness) = distinctness { - distinctness.to_tokens(s)?; + distinctness.to_tokens_with_context(s, context)?; } - comma(columns, s)?; + comma(columns, s, context)?; if let Some(ref from) = from { s.append(TK_FROM, None)?; - from.to_tokens(s)?; + from.to_tokens_with_context(s, context)?; } if let Some(ref where_clause) = where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } if let Some(ref group_by) = group_by { - group_by.to_tokens(s)?; + group_by.to_tokens_with_context(s, context)?; } if let Some(ref window_clause) = window_clause { s.append(TK_WINDOW, None)?; - comma(window_clause, s)?; + comma(window_clause, s, context)?; } Ok(()) } @@ -976,7 +1088,7 @@ impl ToTokens for OneSelect { s.append(TK_COMMA, None)?; } s.append(TK_LP, None)?; - comma(vals, s)?; + comma(vals, s, context)?; s.append(TK_RP, None)?; } Ok(()) @@ -986,11 +1098,18 @@ impl ToTokens for OneSelect { } impl ToTokens for FromClause { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.select.as_ref().unwrap().to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.select + .as_ref() + .unwrap() + .to_tokens_with_context(s, context)?; if let Some(ref joins) = self.joins { for join in joins { - join.to_tokens(s)?; + join.to_tokens_with_context(s, context)?; } } Ok(()) @@ -998,7 +1117,11 @@ impl ToTokens for FromClause { } impl ToTokens for Distinctness { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( match self { Self::Distinct => TK_DISTINCT, @@ -1010,18 +1133,22 @@ impl ToTokens for Distinctness { } impl ToTokens for ResultColumn { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Expr(expr, alias) => { - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; if let Some(alias) = alias { - alias.to_tokens(s)?; + alias.to_tokens_with_context(s, context)?; } Ok(()) } Self::Star => s.append(TK_STAR, None), Self::TableStar(tbl_name) => { - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; s.append(TK_DOT, None)?; s.append(TK_STAR, None) } @@ -1030,68 +1157,80 @@ impl ToTokens for ResultColumn { } impl ToTokens for As { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::As(ref name) => { s.append(TK_AS, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } - Self::Elided(ref name) => name.to_tokens(s), + Self::Elided(ref name) => name.to_tokens_with_context(s, context), } } } impl ToTokens for JoinedSelectTable { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.operator.to_tokens(s)?; - self.table.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.operator.to_tokens_with_context(s, context)?; + self.table.to_tokens_with_context(s, context)?; if let Some(ref constraint) = self.constraint { - constraint.to_tokens(s)?; + constraint.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for SelectTable { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Table(name, alias, indexed) => { - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; if let Some(alias) = alias { - alias.to_tokens(s)?; + alias.to_tokens_with_context(s, context)?; } if let Some(indexed) = indexed { - indexed.to_tokens(s)?; + indexed.to_tokens_with_context(s, context)?; } Ok(()) } Self::TableCall(name, exprs, alias) => { - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; s.append(TK_LP, None)?; if let Some(exprs) = exprs { - comma(exprs, s)?; + comma(exprs, s, context)?; } s.append(TK_RP, None)?; if let Some(alias) = alias { - alias.to_tokens(s)?; + alias.to_tokens_with_context(s, context)?; } Ok(()) } Self::Select(select, alias) => { s.append(TK_LP, None)?; - select.to_tokens(s)?; + select.to_tokens_with_context(s, context)?; s.append(TK_RP, None)?; if let Some(alias) = alias { - alias.to_tokens(s)?; + alias.to_tokens_with_context(s, context)?; } Ok(()) } Self::Sub(from, alias) => { s.append(TK_LP, None)?; - from.to_tokens(s)?; + from.to_tokens_with_context(s, context)?; s.append(TK_RP, None)?; if let Some(alias) = alias { - alias.to_tokens(s)?; + alias.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1100,12 +1239,16 @@ impl ToTokens for SelectTable { } impl ToTokens for JoinOperator { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Comma => s.append(TK_COMMA, None), Self::TypedJoin(join_type) => { if let Some(ref join_type) = join_type { - join_type.to_tokens(s)?; + join_type.to_tokens_with_context(s, context)?; } s.append(TK_JOIN, None) } @@ -1114,7 +1257,11 @@ impl ToTokens for JoinOperator { } impl ToTokens for JoinType { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { if self.contains(Self::NATURAL) { s.append(TK_JOIN_KW, Some("NATURAL"))?; } @@ -1142,16 +1289,20 @@ impl ToTokens for JoinType { } impl ToTokens for JoinConstraint { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::On(expr) => { s.append(TK_ON, None)?; - expr.to_tokens(s) + expr.to_tokens_with_context(s, context) } Self::Using(col_names) => { s.append(TK_USING, None)?; s.append(TK_LP, None)?; - comma(col_names.deref(), s)?; + comma(col_names.deref(), s, context)?; s.append(TK_RP, None) } } @@ -1159,26 +1310,38 @@ impl ToTokens for JoinConstraint { } impl ToTokens for GroupBy { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { s.append(TK_GROUP, None)?; s.append(TK_BY, None)?; - comma(&self.exprs, s)?; + comma(&self.exprs, s, context)?; if let Some(ref having) = self.having { s.append(TK_HAVING, None)?; - having.to_tokens(s)?; + having.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for Id { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { double_quote(&self.0, s) } } impl ToTokens for Name { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { double_quote(self.0.as_str(), s) } } @@ -1190,50 +1353,63 @@ impl Display for Name { } impl ToTokens for QualifiedName { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if let Some(ref db_name) = self.db_name { - db_name.to_tokens(s)?; + db_name.to_tokens_with_context(s, context)?; s.append(TK_DOT, None)?; } - self.name.to_tokens(s)?; + self.name.to_tokens_with_context(s, context)?; if let Some(ref alias) = self.alias { s.append(TK_AS, None)?; - alias.to_tokens(s)?; + alias.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for AlterTableBody { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::RenameTo(name) => { s.append(TK_RENAME, None)?; s.append(TK_TO, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } Self::AddColumn(def) => { s.append(TK_ADD, None)?; s.append(TK_COLUMNKW, None)?; - def.to_tokens(s) + def.to_tokens_with_context(s, context) } Self::RenameColumn { old, new } => { s.append(TK_RENAME, None)?; - old.to_tokens(s)?; + s.append(TK_COLUMNKW, None)?; + old.to_tokens_with_context(s, context)?; s.append(TK_TO, None)?; - new.to_tokens(s) + new.to_tokens_with_context(s, context) } Self::DropColumn(name) => { s.append(TK_DROP, None)?; s.append(TK_COLUMNKW, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } } } } impl ToTokens for CreateTableBody { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::ColumnsAndConstraints { columns, @@ -1241,10 +1417,10 @@ impl ToTokens for CreateTableBody { options, } => { s.append(TK_LP, None)?; - comma(columns.values(), s)?; + comma(columns.values(), s, context)?; if let Some(constraints) = constraints { s.append(TK_COMMA, None)?; - comma(constraints, s)?; + comma(constraints, s, context)?; } s.append(TK_RP, None)?; if options.contains(TableOptions::WITHOUT_ROWID) { @@ -1258,37 +1434,49 @@ impl ToTokens for CreateTableBody { } Self::AsSelect(select) => { s.append(TK_AS, None)?; - select.to_tokens(s) + select.to_tokens_with_context(s, context) } } } } impl ToTokens for ColumnDefinition { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.col_name.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.col_name.to_tokens_with_context(s, context)?; if let Some(ref col_type) = self.col_type { - col_type.to_tokens(s)?; + col_type.to_tokens_with_context(s, context)?; } for constraint in &self.constraints { - constraint.to_tokens(s)?; + constraint.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for NamedColumnConstraint { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if let Some(ref name) = self.name { s.append(TK_CONSTRAINT, None)?; - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; } - self.constraint.to_tokens(s) + self.constraint.to_tokens_with_context(s, context) } } impl ToTokens for ColumnConstraint { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::PrimaryKey { order, @@ -1298,12 +1486,12 @@ impl ToTokens for ColumnConstraint { s.append(TK_PRIMARY, None)?; s.append(TK_KEY, None)?; if let Some(order) = order { - order.to_tokens(s)?; + order.to_tokens_with_context(s, context)?; } if let Some(conflict_clause) = conflict_clause { s.append(TK_ON, None)?; s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens(s)?; + conflict_clause.to_tokens_with_context(s, context)?; } if *auto_increment { s.append(TK_AUTOINCR, None)?; @@ -1321,7 +1509,7 @@ impl ToTokens for ColumnConstraint { if let Some(conflict_clause) = conflict_clause { s.append(TK_ON, None)?; s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens(s)?; + conflict_clause.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1330,43 +1518,43 @@ impl ToTokens for ColumnConstraint { if let Some(conflict_clause) = conflict_clause { s.append(TK_ON, None)?; s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens(s)?; + conflict_clause.to_tokens_with_context(s, context)?; } Ok(()) } Self::Check(expr) => { s.append(TK_CHECK, None)?; s.append(TK_LP, None)?; - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } Self::Default(expr) => { s.append(TK_DEFAULT, None)?; - expr.to_tokens(s) + expr.to_tokens_with_context(s, context) } - Self::Defer(deref_clause) => deref_clause.to_tokens(s), + Self::Defer(deref_clause) => deref_clause.to_tokens_with_context(s, context), Self::Collate { collation_name } => { s.append(TK_COLLATE, None)?; - collation_name.to_tokens(s) + collation_name.to_tokens_with_context(s, context) } Self::ForeignKey { clause, deref_clause, } => { s.append(TK_REFERENCES, None)?; - clause.to_tokens(s)?; + clause.to_tokens_with_context(s, context)?; if let Some(deref_clause) = deref_clause { - deref_clause.to_tokens(s)?; + deref_clause.to_tokens_with_context(s, context)?; } Ok(()) } Self::Generated { expr, typ } => { s.append(TK_AS, None)?; s.append(TK_LP, None)?; - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; s.append(TK_RP, None)?; if let Some(typ) = typ { - typ.to_tokens(s)?; + typ.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1375,17 +1563,25 @@ impl ToTokens for ColumnConstraint { } impl ToTokens for NamedTableConstraint { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if let Some(ref name) = self.name { s.append(TK_CONSTRAINT, None)?; - name.to_tokens(s)?; + name.to_tokens_with_context(s, context)?; } - self.constraint.to_tokens(s) + self.constraint.to_tokens_with_context(s, context) } } impl ToTokens for TableConstraint { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::PrimaryKey { columns, @@ -1395,7 +1591,7 @@ impl ToTokens for TableConstraint { s.append(TK_PRIMARY, None)?; s.append(TK_KEY, None)?; s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; if *auto_increment { s.append(TK_AUTOINCR, None)?; } @@ -1403,7 +1599,7 @@ impl ToTokens for TableConstraint { if let Some(conflict_clause) = conflict_clause { s.append(TK_ON, None)?; s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens(s)?; + conflict_clause.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1413,19 +1609,19 @@ impl ToTokens for TableConstraint { } => { s.append(TK_UNIQUE, None)?; s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; s.append(TK_RP, None)?; if let Some(conflict_clause) = conflict_clause { s.append(TK_ON, None)?; s.append(TK_CONFLICT, None)?; - conflict_clause.to_tokens(s)?; + conflict_clause.to_tokens_with_context(s, context)?; } Ok(()) } Self::Check(expr) => { s.append(TK_CHECK, None)?; s.append(TK_LP, None)?; - expr.to_tokens(s)?; + expr.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } Self::ForeignKey { @@ -1436,12 +1632,12 @@ impl ToTokens for TableConstraint { s.append(TK_FOREIGN, None)?; s.append(TK_KEY, None)?; s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; s.append(TK_RP, None)?; s.append(TK_REFERENCES, None)?; - clause.to_tokens(s)?; + clause.to_tokens_with_context(s, context)?; if let Some(deref_clause) = deref_clause { - deref_clause.to_tokens(s)?; + deref_clause.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1450,7 +1646,11 @@ impl ToTokens for TableConstraint { } impl ToTokens for SortOrder { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( match self { Self::Asc => TK_ASC, @@ -1462,7 +1662,11 @@ impl ToTokens for SortOrder { } impl ToTokens for NullsOrder { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append(TK_NULLS, None)?; s.append( match self { @@ -1475,48 +1679,60 @@ impl ToTokens for NullsOrder { } impl ToTokens for ForeignKeyClause { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.tbl_name.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.tbl_name.to_tokens_with_context(s, context)?; if let Some(ref columns) = self.columns { s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; s.append(TK_RP, None)?; } for arg in &self.args { - arg.to_tokens(s)?; + arg.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for RefArg { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::OnDelete(ref action) => { s.append(TK_ON, None)?; s.append(TK_DELETE, None)?; - action.to_tokens(s) + action.to_tokens_with_context(s, context) } Self::OnInsert(ref action) => { s.append(TK_ON, None)?; s.append(TK_INSERT, None)?; - action.to_tokens(s) + action.to_tokens_with_context(s, context) } Self::OnUpdate(ref action) => { s.append(TK_ON, None)?; s.append(TK_UPDATE, None)?; - action.to_tokens(s) + action.to_tokens_with_context(s, context) } Self::Match(ref name) => { s.append(TK_MATCH, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } } } } impl ToTokens for RefAct { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { match self { Self::SetNull => { s.append(TK_SET, None)?; @@ -1537,20 +1753,28 @@ impl ToTokens for RefAct { } impl ToTokens for DeferSubclause { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if !self.deferrable { s.append(TK_NOT, None)?; } s.append(TK_DEFERRABLE, None)?; if let Some(init_deferred) = self.init_deferred { - init_deferred.to_tokens(s)?; + init_deferred.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for InitDeferredPred { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append(TK_INITIALLY, None)?; s.append( match self { @@ -1563,26 +1787,34 @@ impl ToTokens for InitDeferredPred { } impl ToTokens for IndexedColumn { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.col_name.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.col_name.to_tokens_with_context(s, context)?; if let Some(ref collation_name) = self.collation_name { s.append(TK_COLLATE, None)?; - collation_name.to_tokens(s)?; + collation_name.to_tokens_with_context(s, context)?; } if let Some(order) = self.order { - order.to_tokens(s)?; + order.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for Indexed { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::IndexedBy(ref name) => { s.append(TK_INDEXED, None)?; s.append(TK_BY, None)?; - name.to_tokens(s) + name.to_tokens_with_context(s, context) } Self::NotIndexed => { s.append(TK_NOT, None)?; @@ -1593,37 +1825,49 @@ impl ToTokens for Indexed { } impl ToTokens for SortedColumn { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.expr.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.expr.to_tokens_with_context(s, context)?; if let Some(ref order) = self.order { - order.to_tokens(s)?; + order.to_tokens_with_context(s, context)?; } if let Some(ref nulls) = self.nulls { - nulls.to_tokens(s)?; + nulls.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for Limit { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { s.append(TK_LIMIT, None)?; - self.expr.to_tokens(s)?; + self.expr.to_tokens_with_context(s, context)?; if let Some(ref offset) = self.offset { s.append(TK_OFFSET, None)?; - offset.to_tokens(s)?; + offset.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for InsertBody { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Select(select, upsert) => { - select.to_tokens(s)?; + select.to_tokens_with_context(s, context)?; if let Some(upsert) = upsert { - upsert.to_tokens(s)?; + upsert.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1636,29 +1880,37 @@ impl ToTokens for InsertBody { } impl ToTokens for Set { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if self.col_names.len() == 1 { - comma(self.col_names.deref(), s)?; + comma(self.col_names.deref(), s, context)?; } else { s.append(TK_LP, None)?; - comma(self.col_names.deref(), s)?; + comma(self.col_names.deref(), s, context)?; s.append(TK_RP, None)?; } s.append(TK_EQ, None)?; - self.expr.to_tokens(s) + self.expr.to_tokens_with_context(s, context) } } impl ToTokens for PragmaBody { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Equals(value) => { s.append(TK_EQ, None)?; - value.to_tokens(s) + value.to_tokens_with_context(s, context) } Self::Call(value) => { s.append(TK_LP, None)?; - value.to_tokens(s)?; + value.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } } @@ -1666,7 +1918,11 @@ impl ToTokens for PragmaBody { } impl ToTokens for TriggerTime { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { match self { Self::Before => s.append(TK_BEFORE, None), Self::After => s.append(TK_AFTER, None), @@ -1679,7 +1935,11 @@ impl ToTokens for TriggerTime { } impl ToTokens for TriggerEvent { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Delete => s.append(TK_DELETE, None), Self::Insert => s.append(TK_INSERT, None), @@ -1687,14 +1947,18 @@ impl ToTokens for TriggerEvent { Self::UpdateOf(ref col_names) => { s.append(TK_UPDATE, None)?; s.append(TK_OF, None)?; - comma(col_names.deref(), s) + comma(col_names.deref(), s, context) } } } } impl ToTokens for TriggerCmd { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Update(update) => { let TriggerCmdUpdate { @@ -1707,18 +1971,18 @@ impl ToTokens for TriggerCmd { s.append(TK_UPDATE, None)?; if let Some(or_conflict) = or_conflict { s.append(TK_OR, None)?; - or_conflict.to_tokens(s)?; + or_conflict.to_tokens_with_context(s, context)?; } - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; s.append(TK_SET, None)?; - comma(sets, s)?; + comma(sets, s, context)?; if let Some(from) = from { s.append(TK_FROM, None)?; - from.to_tokens(s)?; + from.to_tokens_with_context(s, context)?; } if let Some(where_clause) = where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1737,43 +2001,47 @@ impl ToTokens for TriggerCmd { s.append(TK_INSERT, None)?; if let Some(or_conflict) = or_conflict { s.append(TK_OR, None)?; - or_conflict.to_tokens(s)?; + or_conflict.to_tokens_with_context(s, context)?; } } s.append(TK_INTO, None)?; - tbl_name.to_tokens(s)?; + tbl_name.to_tokens_with_context(s, context)?; if let Some(col_names) = col_names { s.append(TK_LP, None)?; - comma(col_names.deref(), s)?; + comma(col_names.deref(), s, context)?; s.append(TK_RP, None)?; } - select.to_tokens(s)?; + select.to_tokens_with_context(s, context)?; if let Some(upsert) = upsert { - upsert.to_tokens(s)?; + upsert.to_tokens_with_context(s, context)?; } if let Some(returning) = returning { s.append(TK_RETURNING, None)?; - comma(returning, s)?; + comma(returning, s, context)?; } Ok(()) } Self::Delete(delete) => { s.append(TK_DELETE, None)?; s.append(TK_FROM, None)?; - delete.tbl_name.to_tokens(s)?; + delete.tbl_name.to_tokens_with_context(s, context)?; if let Some(where_clause) = &delete.where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } Ok(()) } - Self::Select(select) => select.to_tokens(s), + Self::Select(select) => select.to_tokens_with_context(s, context), } } } impl ToTokens for ResolveType { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( match self { Self::Rollback => TK_ROLLBACK, @@ -1788,21 +2056,29 @@ impl ToTokens for ResolveType { } impl ToTokens for With { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { s.append(TK_WITH, None)?; if self.recursive { s.append(TK_RECURSIVE, None)?; } - comma(&self.ctes, s) + comma(&self.ctes, s, context) } } impl ToTokens for CommonTableExpr { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.tbl_name.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.tbl_name.to_tokens_with_context(s, context)?; if let Some(ref columns) = self.columns { s.append(TK_LP, None)?; - comma(columns, s)?; + comma(columns, s, context)?; s.append(TK_RP, None)?; } s.append(TK_AS, None)?; @@ -1817,19 +2093,23 @@ impl ToTokens for CommonTableExpr { } }; s.append(TK_LP, None)?; - self.select.to_tokens(s)?; + self.select.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } } impl ToTokens for Type { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self.size { None => s.append(TK_ID, Some(&self.name)), Some(ref size) => { s.append(TK_ID, Some(&self.name))?; // TODO check there is no forbidden chars s.append(TK_LP, None)?; - size.to_tokens(s)?; + size.to_tokens_with_context(s, context)?; s.append(TK_RP, None) } } @@ -1837,20 +2117,28 @@ impl ToTokens for Type { } impl ToTokens for TypeSize { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { - Self::MaxSize(size) => size.to_tokens(s), + Self::MaxSize(size) => size.to_tokens_with_context(s, context), Self::TypeSize(size1, size2) => { - size1.to_tokens(s)?; + size1.to_tokens_with_context(s, context)?; s.append(TK_COMMA, None)?; - size2.to_tokens(s) + size2.to_tokens_with_context(s, context) } } } } impl ToTokens for TransactionType { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( match self { Self::Deferred => TK_DEFERRED, @@ -1863,44 +2151,56 @@ impl ToTokens for TransactionType { } impl ToTokens for Upsert { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { s.append(TK_ON, None)?; s.append(TK_CONFLICT, None)?; if let Some(ref index) = self.index { - index.to_tokens(s)?; + index.to_tokens_with_context(s, context)?; } - self.do_clause.to_tokens(s)?; + self.do_clause.to_tokens_with_context(s, context)?; if let Some(ref next) = self.next { - next.to_tokens(s)?; + next.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for UpsertIndex { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { s.append(TK_LP, None)?; - comma(&self.targets, s)?; + comma(&self.targets, s, context)?; s.append(TK_RP, None)?; if let Some(ref where_clause) = self.where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for UpsertDo { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::Set { sets, where_clause } => { s.append(TK_DO, None)?; s.append(TK_UPDATE, None)?; s.append(TK_SET, None)?; - comma(sets, s)?; + comma(sets, s, context)?; if let Some(where_clause) = where_clause { s.append(TK_WHERE, None)?; - where_clause.to_tokens(s)?; + where_clause.to_tokens_with_context(s, context)?; } Ok(()) } @@ -1913,83 +2213,107 @@ impl ToTokens for UpsertDo { } impl ToTokens for FunctionTail { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { if let Some(ref filter_clause) = self.filter_clause { s.append(TK_FILTER, None)?; s.append(TK_LP, None)?; s.append(TK_WHERE, None)?; - filter_clause.to_tokens(s)?; + filter_clause.to_tokens_with_context(s, context)?; s.append(TK_RP, None)?; } if let Some(ref over_clause) = self.over_clause { s.append(TK_OVER, None)?; - over_clause.to_tokens(s)?; + over_clause.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for Over { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { - Self::Window(ref window) => window.to_tokens(s), - Self::Name(ref name) => name.to_tokens(s), + Self::Window(ref window) => window.to_tokens_with_context(s, context), + Self::Name(ref name) => name.to_tokens_with_context(s, context), } } } impl ToTokens for WindowDef { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.name.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.name.to_tokens_with_context(s, context)?; s.append(TK_AS, None)?; - self.window.to_tokens(s) + self.window.to_tokens_with_context(s, context) } } impl ToTokens for Window { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { s.append(TK_LP, None)?; if let Some(ref base) = self.base { - base.to_tokens(s)?; + base.to_tokens_with_context(s, context)?; } if let Some(ref partition_by) = self.partition_by { s.append(TK_PARTITION, None)?; s.append(TK_BY, None)?; - comma(partition_by, s)?; + comma(partition_by, s, context)?; } if let Some(ref order_by) = self.order_by { s.append(TK_ORDER, None)?; s.append(TK_BY, None)?; - comma(order_by, s)?; + comma(order_by, s, context)?; } if let Some(ref frame_clause) = self.frame_clause { - frame_clause.to_tokens(s)?; + frame_clause.to_tokens_with_context(s, context)?; } s.append(TK_RP, None) } } impl ToTokens for FrameClause { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { - self.mode.to_tokens(s)?; + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { + self.mode.to_tokens_with_context(s, context)?; if let Some(ref end) = self.end { s.append(TK_BETWEEN, None)?; - self.start.to_tokens(s)?; + self.start.to_tokens_with_context(s, context)?; s.append(TK_AND, None)?; - end.to_tokens(s)?; + end.to_tokens_with_context(s, context)?; } else { - self.start.to_tokens(s)?; + self.start.to_tokens_with_context(s, context)?; } if let Some(ref exclude) = self.exclude { s.append(TK_EXCLUDE, None)?; - exclude.to_tokens(s)?; + exclude.to_tokens_with_context(s, context)?; } Ok(()) } } impl ToTokens for FrameMode { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { s.append( match self { Self::Groups => TK_GROUPS, @@ -2002,18 +2326,22 @@ impl ToTokens for FrameMode { } impl ToTokens for FrameBound { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + context: &C, + ) -> Result<(), S::Error> { match self { Self::CurrentRow => { s.append(TK_CURRENT, None)?; s.append(TK_ROW, None) } Self::Following(value) => { - value.to_tokens(s)?; + value.to_tokens_with_context(s, context)?; s.append(TK_FOLLOWING, None) } Self::Preceding(value) => { - value.to_tokens(s)?; + value.to_tokens_with_context(s, context)?; s.append(TK_PRECEDING, None) } Self::UnboundedFollowing => { @@ -2029,7 +2357,11 @@ impl ToTokens for FrameBound { } impl ToTokens for FrameExclude { - fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { + fn to_tokens_with_context( + &self, + s: &mut S, + _: &C, + ) -> Result<(), S::Error> { match self { Self::NoOthers => { s.append(TK_NO, None)?; @@ -2045,42 +2377,22 @@ impl ToTokens for FrameExclude { } } -fn comma(items: I, s: &mut S) -> Result<(), S::Error> +fn comma( + items: I, + s: &mut S, + context: &C, +) -> Result<(), S::Error> where I: IntoIterator, I::Item: ToTokens, { - let iter = items.into_iter(); - for (i, item) in iter.enumerate() { - if i != 0 { - s.append(TK_COMMA, None)?; - } - item.to_tokens(s)?; - } - Ok(()) + s.comma(items, context) } // TK_ID: [...] / `...` / "..." / some keywords / non keywords -fn double_quote(name: &str, s: &mut S) -> Result<(), S::Error> { +fn double_quote(name: &str, s: &mut S) -> Result<(), S::Error> { if name.is_empty() { return s.append(TK_ID, Some("\"\"")); } - if is_identifier(name) { - // identifier must be quoted when they match a keyword... - /*if is_keyword(name) { - f.write_char('`')?; - f.write_str(name)?; - return f.write_char('`'); - }*/ - return s.append(TK_ID, Some(name)); - } - /*f.write_char('"')?; - for c in name.chars() { - if c == '"' { - f.write_char(c)?; - } - f.write_char(c)?; - } - f.write_char('"')*/ s.append(TK_ID, Some(name)) } diff --git a/vendored/sqlite3-parser/src/parser/ast/mod.rs b/vendored/sqlite3-parser/src/parser/ast/mod.rs index 630c1063c..9c661e5bc 100644 --- a/vendored/sqlite3-parser/src/parser/ast/mod.rs +++ b/vendored/sqlite3-parser/src/parser/ast/mod.rs @@ -14,7 +14,7 @@ use indexmap::{IndexMap, IndexSet}; use crate::custom_err; use crate::dialect::TokenType::{self, *}; -use crate::dialect::{from_token, is_identifier, Token}; +use crate::dialect::{from_token, Token}; use crate::parser::{parse::YYCODETYPE, ParserError}; /// `?` or `$` Prepared statement arg placeholder(s) diff --git a/vendored/sqlite3-parser/src/to_sql_string/expr.rs b/vendored/sqlite3-parser/src/to_sql_string/expr.rs index ca1180448..5c6016f6a 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/expr.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/expr.rs @@ -1,428 +1,2 @@ -use std::fmt::Display; - -use crate::ast::{self, fmt::ToTokens, Expr}; - -use super::ToSqlString; - -impl ToSqlString for Expr { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = String::new(); - match self { - Expr::Between { - lhs, - not, - start, - end, - } => { - ret.push_str(&lhs.to_sql_string(context)); - ret.push(' '); - - if *not { - ret.push_str("NOT "); - } - - ret.push_str("BETWEEN "); - - ret.push_str(&start.to_sql_string(context)); - - ret.push_str(" AND "); - - ret.push_str(&end.to_sql_string(context)); - } - Expr::Binary(lhs, op, rhs) => { - ret.push_str(&lhs.to_sql_string(context)); - ret.push(' '); - ret.push_str(&op.to_string()); - ret.push(' '); - ret.push_str(&rhs.to_sql_string(context)); - } - Expr::Case { - base, - when_then_pairs, - else_expr, - } => { - ret.push_str("CASE "); - if let Some(base) = base { - ret.push_str(&base.to_sql_string(context)); - ret.push(' '); - } - for (when, then) in when_then_pairs { - ret.push_str("WHEN "); - ret.push_str(&when.to_sql_string(context)); - ret.push_str(" THEN "); - ret.push_str(&then.to_sql_string(context)); - } - if let Some(else_expr) = else_expr { - ret.push_str(" ELSE "); - ret.push_str(&else_expr.to_sql_string(context)); - } - ret.push_str(" END"); - } - Expr::Cast { expr, type_name } => { - ret.push_str("CAST"); - ret.push('('); - ret.push_str(&expr.to_sql_string(context)); - if let Some(type_name) = type_name { - ret.push_str(" AS "); - ret.push_str(&type_name.to_sql_string(context)); - } - ret.push(')'); - } - Expr::Collate(expr, name) => { - ret.push_str(&expr.to_sql_string(context)); - ret.push_str(" COLLATE "); - ret.push_str(name); - } - Expr::DoublyQualified(name, name1, name2) => { - ret.push_str(&name.0); - ret.push('.'); - ret.push_str(&name1.0); - ret.push('.'); - ret.push_str(&name2.0); - } - Expr::Exists(select) => { - ret.push_str("EXISTS ("); - ret.push_str(&select.to_sql_string(context)); - ret.push(')'); - } - Expr::FunctionCall { - name, - distinctness: _, - args, - order_by: _, - filter_over, - } => { - ret.push_str(&name.0); - // TODO: pretty sure there should be no ORDER_BY nor DISTINCT - ret.push('('); - if let Some(args) = args { - let joined_args = args - .iter() - .map(|arg| arg.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push_str(&joined_args); - } - ret.push(')'); - if let Some(filter_over) = filter_over { - if let Some(filter) = &filter_over.filter_clause { - ret.push_str(&format!( - " FILTER (WHERE {})", - filter.to_sql_string(context) - )); - } - if let Some(over) = &filter_over.over_clause { - ret.push(' '); - ret.push_str(&over.to_sql_string(context)); - } - } - } - Expr::FunctionCallStar { name, filter_over } => { - ret.push_str(&name.0); - ret.push_str("(*)"); - if let Some(filter_over) = filter_over { - if let Some(filter) = &filter_over.filter_clause { - ret.push_str(&format!( - " FILTER (WHERE {})", - filter.to_sql_string(context) - )); - } - if let Some(over) = &filter_over.over_clause { - ret.push(' '); - ret.push_str(&over.to_sql_string(context)); - } - } - } - Expr::Id(id) => { - ret.push_str(&id.0); - } - Expr::Column { - database: _, // TODO: Ignore database for now - table, - column, - is_rowid_alias: _, - } => { - ret.push_str(context.get_table_name(*table)); - ret.push('.'); - ret.push_str(context.get_column_name(*table, *column)); - } - Expr::RowId { database: _, table } => { - ret.push_str(&format!("{}.rowid", context.get_table_name(*table))) - } - Expr::InList { lhs, not, rhs } => { - ret.push_str(&format!( - "{} {}IN ({})", - lhs.to_sql_string(context), - if *not { "NOT " } else { "" }, - if let Some(rhs) = rhs { - rhs.iter() - .map(|expr| expr.to_sql_string(context)) - .collect::>() - .join(", ") - } else { - "".to_string() - } - )); - } - Expr::InSelect { lhs, not, rhs } => { - ret.push_str(&format!( - "{} {}IN ({})", - lhs.to_sql_string(context), - if *not { "NOT " } else { "" }, - rhs.to_sql_string(context) - )); - } - Expr::InTable { - lhs, - not, - rhs, - args, - } => { - ret.push_str(&lhs.to_sql_string(context)); - ret.push(' '); - if *not { - ret.push_str("NOT "); - } - ret.push_str(&rhs.to_sql_string(context)); - - if let Some(args) = args { - ret.push('('); - let joined_args = args - .iter() - .map(|expr| expr.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push_str(&joined_args); - ret.push(')'); - } - } - Expr::IsNull(expr) => { - ret.push_str(&expr.to_sql_string(context)); - ret.push_str(" ISNULL"); - } - Expr::Like { - lhs, - not, - op, - rhs, - escape, - } => { - ret.push_str(&lhs.to_sql_string(context)); - ret.push(' '); - if *not { - ret.push_str("NOT "); - } - ret.push_str(&op.to_string()); - ret.push(' '); - ret.push_str(&rhs.to_sql_string(context)); - if let Some(escape) = escape { - ret.push_str(" ESCAPE "); - ret.push_str(&escape.to_sql_string(context)); - } - } - Expr::Literal(literal) => { - ret.push_str(&literal.to_string()); - } - Expr::Name(name) => { - ret.push_str(&name.0); - } - Expr::NotNull(expr) => { - ret.push_str(&expr.to_sql_string(context)); - ret.push_str(" NOT NULL"); - } - Expr::Parenthesized(exprs) => { - ret.push('('); - let joined_args = exprs - .iter() - .map(|expr| expr.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push_str(&joined_args); - ret.push(')'); - } - Expr::Qualified(name, name1) => { - ret.push_str(&name.0); - ret.push('.'); - ret.push_str(&name1.0); - } - Expr::Raise(resolve_type, expr) => { - ret.push_str("RAISE("); - ret.push_str(&resolve_type.to_string()); - if let Some(expr) = expr { - ret.push_str(", "); - ret.push_str(&expr.to_sql_string(context)); - } - ret.push(')'); - } - Expr::Subquery(select) => { - ret.push('('); - ret.push_str(&select.to_sql_string(context)); - ret.push(')'); - } - Expr::Unary(unary_operator, expr) => { - ret.push_str(&unary_operator.to_string()); - ret.push(' '); - ret.push_str(&expr.to_sql_string(context)); - } - Expr::Variable(variable) => { - ret.push_str(variable); - } - }; - ret - } -} - -impl Display for ast::Operator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = match self { - Self::Add => "+", - Self::And => "AND", - Self::ArrowRight => "->", - Self::ArrowRightShift => "->>", - Self::BitwiseAnd => "&", - Self::BitwiseNot => "~", - Self::BitwiseOr => "|", - Self::Concat => "||", - Self::Divide => "/", - Self::Equals => "=", - Self::Greater => ">", - Self::GreaterEquals => ">=", - Self::Is => "IS", - Self::IsNot => "IS NOT", - Self::LeftShift => "<<", - Self::Less => "<", - Self::LessEquals => "<=", - Self::Modulus => "%", - Self::Multiply => "*", - Self::NotEquals => "!=", - Self::Or => "OR", - Self::RightShift => ">>", - Self::Subtract => "-", - }; - write!(f, "{value}") - } -} - -impl ToSqlString for ast::Type { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = self.name.clone(); - if let Some(size) = &self.size { - ret.push(' '); - ret.push('('); - ret.push_str(&size.to_sql_string(context)); - ret.push(')'); - } - ret - } -} - -impl ToSqlString for ast::TypeSize { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = String::new(); - match self { - Self::MaxSize(e) => { - ret.push_str(&e.to_sql_string(context)); - } - Self::TypeSize(lhs, rhs) => { - ret.push_str(&lhs.to_sql_string(context)); - ret.push_str(", "); - ret.push_str(&rhs.to_sql_string(context)); - } - }; - ret - } -} - -impl Display for ast::Distinctness { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::All => "ALL", - Self::Distinct => "DISTINCT", - } - ) - } -} - -// Can't impl Display here as it is already implemented for it -impl ToSqlString for ast::QualifiedName { - fn to_sql_string(&self, _context: &C) -> String { - let mut ret = String::new(); - if let Some(db_name) = &self.db_name { - ret.push_str(&db_name.0); - ret.push('.'); - } - if let Some(alias) = &self.alias { - ret.push_str(&alias.0); - ret.push('.'); - } - ret.push_str(&self.name.0); - ret - } -} - -impl Display for ast::LikeOperator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl Display for ast::Literal { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Blob(b) => format!("x'{b}'"), - Self::CurrentDate => "CURRENT_DATE".to_string(), - Self::CurrentTime => "CURRENT_TIME".to_string(), - Self::CurrentTimestamp => "CURRENT_TIMESTAMP".to_string(), - Self::Keyword(keyword) => keyword.clone(), - Self::Null => "NULL".to_string(), - Self::Numeric(num) => num.clone(), - Self::String(s) => s.clone(), - } - ) - } -} - -impl Display for ast::ResolveType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl Display for ast::UnaryOperator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::BitwiseNot => "~", - Self::Negative => "-", - Self::Not => "NOT", - Self::Positive => "+", - } - ) - } -} - -impl ToSqlString for ast::Over { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = vec!["OVER".to_string()]; - match self { - Self::Name(name) => { - ret.push(name.0.clone()); - } - Self::Window(window) => { - ret.push(window.to_sql_string(context)); - } - } - ret.join(" ") - } -} - #[cfg(test)] mod tests {} diff --git a/vendored/sqlite3-parser/src/to_sql_string/mod.rs b/vendored/sqlite3-parser/src/to_sql_string/mod.rs index af0bab5ef..47004a96a 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/mod.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/mod.rs @@ -15,18 +15,6 @@ pub trait ToSqlContext { fn get_column_name(&self, table_id: TableInternalId, col_idx: usize) -> &str; } -/// Trait to convert an ast to a string -pub trait ToSqlString { - /// Convert the given value to String - fn to_sql_string(&self, context: &C) -> String; -} - -impl ToSqlString for Box { - fn to_sql_string(&self, context: &C) -> String { - T::to_sql_string(self, context) - } -} - #[cfg(test)] mod tests { use super::ToSqlContext; diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs index 701afbf94..7dabd3913 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/alter_table.rs @@ -1,277 +1,74 @@ -use std::fmt::Display; - -use crate::{ast, to_sql_string::ToSqlString}; - -impl ToSqlString for ast::AlterTableBody { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::AddColumn(col_def) => format!("ADD COLUMN {}", col_def.to_sql_string(context)), - Self::DropColumn(name) => format!("DROP COLUMN {}", name.0), - Self::RenameColumn { old, new } => format!("RENAME COLUMN {} TO {}", old.0, new.0), - Self::RenameTo(name) => format!("RENAME TO {}", name.0), - } - } -} - -impl ToSqlString for ast::ColumnDefinition { - fn to_sql_string(&self, context: &C) -> String { - format!( - "{}{}{}", - self.col_name.0, - if let Some(col_type) = &self.col_type { - format!(" {}", col_type.to_sql_string(context)) - } else { - "".to_string() - }, - if !self.constraints.is_empty() { - format!( - " {}", - self.constraints - .iter() - .map(|constraint| constraint.to_sql_string(context)) - .collect::>() - .join(" ") - ) - } else { - "".to_string() - } - ) - } -} - -impl ToSqlString for ast::NamedColumnConstraint { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = Vec::new(); - if let Some(name) = &self.name { - ret.push(format!("CONSTRAINT {}", name.0)); - } - ret.push(self.constraint.to_sql_string(context)); - ret.join(" ") - } -} - -impl ToSqlString for ast::ColumnConstraint { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::Check(expr) => format!("CHECK ({})", expr.to_sql_string(context)), - Self::Collate { collation_name } => format!("COLLATE {}", collation_name.0), - Self::Default(expr) => { - if matches!(expr, ast::Expr::Literal(..)) { - format!("DEFAULT {}", expr.to_sql_string(context)) - } else { - format!("DEFAULT ({})", expr.to_sql_string(context)) - } - } - Self::Defer(expr) => expr.to_string(), - Self::ForeignKey { - clause, - deref_clause, - } => format!( - "{}{}", - clause, - if let Some(deref) = deref_clause { - deref.to_string() - } else { - "".to_string() - } - ), - Self::Generated { expr, typ } => { - // Don't need to add the generated part - format!( - "AS ({}){}", - expr.to_sql_string(context), - if let Some(typ) = typ { - format!(" {}", &typ.0) - } else { - "".to_string() - } - ) - } - Self::NotNull { - nullable: _, - conflict_clause, - } => { - // nullable should always be true here - format!( - "NOT NULL{}", - conflict_clause.map_or("".to_string(), |conflict| format!(" {conflict}")) - ) - } - Self::PrimaryKey { - order, - conflict_clause, - auto_increment, - } => { - format!( - "PRIMARY KEY{}{}{}", - order.map_or("".to_string(), |order| format!(" {order}")), - conflict_clause.map_or("".to_string(), |conflict| format!(" {conflict}")), - auto_increment.then_some(" AUTOINCREMENT").unwrap_or("") - ) - } - Self::Unique(conflict_clause) => { - format!( - "UNIQUE{}", - conflict_clause.map_or("".to_string(), |conflict| format!(" {conflict}")) - ) - } - } - } -} - -impl Display for ast::ForeignKeyClause { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = format!( - "REFERENCES {}{}{}", - self.tbl_name.0, - if let Some(columns) = &self.columns { - format!( - "({})", - columns - .iter() - .map(|cols| cols.to_string()) - .collect::>() - .join(", ") - ) - } else { - "".to_string() - }, - if !self.args.is_empty() { - format!( - " {}", - self.args - .iter() - .map(|arg| arg.to_string()) - .collect::>() - .join(" ") - ) - } else { - "".to_string() - } - ); - write!(f, "{value}") - } -} - -impl Display for ast::RefArg { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = match self { - Self::Match(name) => format!("MATCH {}", name.0), - Self::OnDelete(act) => format!("ON DELETE {act}"), - Self::OnUpdate(act) => format!("ON UPDATE {act}"), - Self::OnInsert(..) => unimplemented!( - "On Insert does not exist in SQLite: https://www.sqlite.org/lang_altertable.html" - ), - }; - write!(f, "{value}") - } -} - -impl Display for ast::RefAct { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = match self { - Self::Cascade => "CASCADE", - Self::NoAction => "NO ACTION", - Self::Restrict => "RESTRICT", - Self::SetDefault => "SET DEFAULT", - Self::SetNull => "SET NULL", - }; - write!(f, "{value}") - } -} - -impl Display for ast::DeferSubclause { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = format!( - "{}{}", - if self.deferrable { - "NOT DEFERRABLE" - } else { - "DEFERRABLE" - }, - if let Some(init_deffered) = &self.init_deferred { - match init_deffered { - ast::InitDeferredPred::InitiallyDeferred => " INITIALLY DEFERRED", - ast::InitDeferredPred::InitiallyImmediate => " INITIALLY IMMEDIATE", - } - } else { - "" - } - ); - write!(f, "{value}") - } -} #[cfg(test)] mod tests { use crate::to_sql_string_test; to_sql_string_test!( test_alter_table_rename, - "ALTER TABLE t RENAME TO new_table_name;" + "ALTER TABLE t RENAME TO new_table_name" ); to_sql_string_test!( test_alter_table_add_column, - "ALTER TABLE t ADD COLUMN c INTEGER;" + "ALTER TABLE t ADD COLUMN c INTEGER" ); to_sql_string_test!( test_alter_table_add_column_with_default, - "ALTER TABLE t ADD COLUMN c TEXT DEFAULT 'value';" + "ALTER TABLE t ADD COLUMN c TEXT DEFAULT 'value'" ); to_sql_string_test!( test_alter_table_add_column_not_null_default, - "ALTER TABLE t ADD COLUMN c REAL NOT NULL DEFAULT 0.0;" + "ALTER TABLE t ADD COLUMN c REAL NOT NULL DEFAULT 0.0" ); to_sql_string_test!( test_alter_table_add_column_unique, "ALTER TABLE t ADD COLUMN c TEXT UNIQUE", - ignore = "ParserError = Cannot add a UNIQUE column;" + ignore = "ParserError = Cannot add a UNIQUE column" ); to_sql_string_test!( test_alter_table_rename_column, - "ALTER TABLE t RENAME COLUMN old_name TO new_name;" + "ALTER TABLE t RENAME COLUMN old_name TO new_name" ); - to_sql_string_test!(test_alter_table_drop_column, "ALTER TABLE t DROP COLUMN c;"); + to_sql_string_test!(test_alter_table_drop_column, "ALTER TABLE t DROP COLUMN c"); to_sql_string_test!( test_alter_table_add_column_check, - "ALTER TABLE t ADD COLUMN c INTEGER CHECK (c > 0);" + "ALTER TABLE t ADD COLUMN c INTEGER CHECK (c > 0)" ); to_sql_string_test!( test_alter_table_add_column_foreign_key, - "ALTER TABLE t ADD COLUMN c INTEGER REFERENCES t2(id) ON DELETE CASCADE;" + "ALTER TABLE t ADD COLUMN c INTEGER REFERENCES t2 (id) ON DELETE CASCADE" ); to_sql_string_test!( test_alter_table_add_column_collate, - "ALTER TABLE t ADD COLUMN c TEXT COLLATE NOCASE;" + "ALTER TABLE t ADD COLUMN c TEXT COLLATE NOCASE" ); to_sql_string_test!( test_alter_table_add_column_primary_key, - "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY;", + "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY", ignore = "ParserError = Cannot add a PRIMARY KEY column" ); to_sql_string_test!( test_alter_table_add_column_primary_key_autoincrement, - "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY AUTOINCREMENT;", + "ALTER TABLE t ADD COLUMN c INTEGER PRIMARY KEY AUTOINCREMENT", ignore = "ParserError = Cannot add a PRIMARY KEY column" ); to_sql_string_test!( test_alter_table_add_generated_column, - "ALTER TABLE t ADD COLUMN c_generated AS (a + b) STORED;" + "ALTER TABLE t ADD COLUMN c_generated AS (a + b) STORED" ); to_sql_string_test!( test_alter_table_add_column_schema, - "ALTER TABLE schema_name.t ADD COLUMN c INTEGER;" + "ALTER TABLE schema_name.t ADD COLUMN c INTEGER" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs index 3dccb625f..3167413e8 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_table.rs @@ -1,240 +1,121 @@ -use std::fmt::Display; - -use crate::{ast, to_sql_string::ToSqlString}; - -impl ToSqlString for ast::CreateTableBody { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::AsSelect(select) => format!("AS {}", select.to_sql_string(context)), - Self::ColumnsAndConstraints { - columns, - constraints, - options, - } => { - format!( - "({}{}){}", - columns - .iter() - .map(|(_, col)| col.to_sql_string(context)) - .collect::>() - .join(", "), - constraints - .as_ref() - .map_or("".to_string(), |constraints| format!( - ", {}", - constraints - .iter() - .map(|constraint| constraint.to_sql_string(context)) - .collect::>() - .join(", ") - )), - options - ) - } - } - } -} - -impl ToSqlString for ast::NamedTableConstraint { - fn to_sql_string(&self, context: &C) -> String { - if let Some(name) = &self.name { - format!( - "CONSTRAINT {} {}", - name.0, - self.constraint.to_sql_string(context) - ) - } else { - self.constraint.to_sql_string(context).to_string() - } - } -} - -impl ToSqlString for ast::TableConstraint { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::Check(expr) => format!("CHECK ({})", expr.to_sql_string(context)), - Self::ForeignKey { - columns, - clause, - deref_clause, - } => format!( - "FOREIGN KEY ({}) {}{}", - columns - .iter() - .map(|col| col.to_string()) - .collect::>() - .join(", "), - clause, - if let Some(deref) = deref_clause { - deref.to_string() - } else { - "".to_string() - } - ), - Self::PrimaryKey { - columns, - auto_increment, - conflict_clause, - } => format!( - "PRIMARY KEY ({}){}{}", - columns - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", "), - conflict_clause.map_or("".to_string(), |conflict| format!(" {conflict}")), - auto_increment.then_some(" AUTOINCREMENT").unwrap_or("") - ), - Self::Unique { - columns, - conflict_clause, - } => format!( - "UNIQUE ({}){}", - columns - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", "), - conflict_clause.map_or("".to_string(), |conflict| format!(" {conflict}")) - ), - } - } -} - -impl Display for ast::TableOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - if *self == Self::NONE { - "" - } else if *self == Self::STRICT { - " STRICT" - } else { - " WITHOUT ROWID" - } - ) - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; to_sql_string_test!( test_create_table_simple, - "CREATE TABLE t (a INTEGER, b TEXT);" + "CREATE TABLE t (a INTEGER, b TEXT)" ); to_sql_string_test!( test_create_table_primary_key, - "CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);" + "CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)" ); to_sql_string_test!( test_create_table_multi_primary_key, - "CREATE TABLE t (a INTEGER, b TEXT, PRIMARY KEY (a, b));" + "CREATE TABLE t (a INTEGER, b TEXT, PRIMARY KEY (a, b))" ); to_sql_string_test!( test_create_table_data_types, - "CREATE TABLE t (a INTEGER, b TEXT, c REAL, d BLOB, e NUMERIC);" + "CREATE TABLE t (a INTEGER, b TEXT, c REAL, d BLOB, e NUMERIC)" ); to_sql_string_test!( test_create_table_foreign_key, - "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t(id));" + "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t (id))" ); to_sql_string_test!( test_create_table_foreign_key_cascade, - "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t(id) ON DELETE CASCADE);" + "CREATE TABLE t2 (id INTEGER PRIMARY KEY, t_id INTEGER, FOREIGN KEY (t_id) REFERENCES t (id) ON DELETE CASCADE)" ); to_sql_string_test!( test_create_table_unique, - "CREATE TABLE t (a INTEGER UNIQUE, b TEXT);" + "CREATE TABLE t (a INTEGER UNIQUE, b TEXT)" ); to_sql_string_test!( test_create_table_not_null, - "CREATE TABLE t (a INTEGER NOT NULL, b TEXT);" + "CREATE TABLE t (a INTEGER NOT NULL, b TEXT)" ); to_sql_string_test!( test_create_table_check, - "CREATE TABLE t (a INTEGER CHECK (a > 0), b TEXT);" + "CREATE TABLE t (a INTEGER CHECK (a > 0), b TEXT)" ); to_sql_string_test!( test_create_table_default, - "CREATE TABLE t (a INTEGER DEFAULT 0, b TEXT);" + "CREATE TABLE t (a INTEGER DEFAULT 0, b TEXT)" ); to_sql_string_test!( test_create_table_multiple_constraints, - "CREATE TABLE t (a INTEGER NOT NULL UNIQUE, b TEXT DEFAULT 'default');" + "CREATE TABLE t (a INTEGER NOT NULL UNIQUE, b TEXT DEFAULT 'default')" ); to_sql_string_test!( test_create_table_generated_column, - "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b));" + "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b))" ); to_sql_string_test!( test_create_table_generated_stored, - "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) STORED);" + "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) STORED)" ); to_sql_string_test!( test_create_table_generated_virtual, - "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) VIRTUAL);" + "CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER AS (a + b) VIRTUAL)" ); to_sql_string_test!( test_create_table_quoted_columns, - "CREATE TABLE t (\"select\" INTEGER, \"from\" TEXT);" + "CREATE TABLE t (\"select\" INTEGER, \"from\" TEXT)" ); to_sql_string_test!( test_create_table_quoted_table, - "CREATE TABLE \"my table\" (a INTEGER);" + "CREATE TABLE \"my table\" (a INTEGER)" ); to_sql_string_test!( test_create_table_if_not_exists, - "CREATE TABLE IF NOT EXISTS t (a INTEGER);" + "CREATE TABLE IF NOT EXISTS t (a INTEGER)" ); - to_sql_string_test!(test_create_temp_table, "CREATE TEMP TABLE t (a INTEGER);"); + to_sql_string_test!(test_create_temp_table, "CREATE TEMP TABLE t (a INTEGER)"); to_sql_string_test!( test_create_table_without_rowid, - "CREATE TABLE t (a INTEGER PRIMARY KEY, b TEXT) WITHOUT ROWID;" + "CREATE TABLE t (a INTEGER PRIMARY KEY, b TEXT) WITHOUT ROWID" ); to_sql_string_test!( test_create_table_named_primary_key, - "CREATE TABLE t (a INTEGER CONSTRAINT pk_a PRIMARY KEY);" + "CREATE TABLE t (a INTEGER CONSTRAINT pk_a PRIMARY KEY)" ); to_sql_string_test!( test_create_table_named_unique, - "CREATE TABLE t (a INTEGER, CONSTRAINT unique_a UNIQUE (a));" + "CREATE TABLE t (a INTEGER, CONSTRAINT unique_a UNIQUE (a))" ); to_sql_string_test!( test_create_table_named_foreign_key, - "CREATE TABLE t2 (id INTEGER, t_id INTEGER, CONSTRAINT fk_t FOREIGN KEY (t_id) REFERENCES t(id));" + "CREATE TABLE t2 (id INTEGER, t_id INTEGER, CONSTRAINT fk_t FOREIGN KEY (t_id) REFERENCES t (id))" ); to_sql_string_test!( test_create_table_complex, - "CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER NOT NULL, b TEXT DEFAULT 'default', c INTEGER AS (a * 2), CONSTRAINT unique_a UNIQUE (a));" + "CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER NOT NULL, b TEXT DEFAULT 'default', c INTEGER AS (a * 2), CONSTRAINT unique_a UNIQUE (a))" ); to_sql_string_test!( test_create_table_multiple_foreign_keys, - "CREATE TABLE t3 (id INTEGER PRIMARY KEY, t1_id INTEGER, t2_id INTEGER, FOREIGN KEY (t1_id) REFERENCES t1(id), FOREIGN KEY (t2_id) REFERENCES t2(id));" + "CREATE TABLE t3 (id INTEGER PRIMARY KEY, t1_id INTEGER, t2_id INTEGER, FOREIGN KEY (t1_id) REFERENCES t1 (id), FOREIGN KEY (t2_id) REFERENCES t2 (id))" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs index 0fb2d700d..5f6ba39c0 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_trigger.rs @@ -1,248 +1,3 @@ -use std::fmt::Display; - -use crate::{ - ast::{self, fmt::ToTokens}, - to_sql_string::ToSqlString, -}; - -impl ToSqlString for ast::CreateTrigger { - fn to_sql_string(&self, context: &C) -> String { - format!( - "CREATE{} TRIGGER {}{}{} {} ON {}{}{} BEGIN {} END;", - if self.temporary { " TEMP" } else { "" }, - if self.if_not_exists { - "IF NOT EXISTS " - } else { - "" - }, - self.trigger_name.to_sql_string(context), - self.time.map_or("".to_string(), |time| format!(" {time}")), - self.event, - self.tbl_name.to_sql_string(context), - if self.for_each_row { - " FOR EACH ROW" - } else { - "" - }, - self.when_clause - .as_ref() - .map_or("".to_string(), |expr| format!( - " WHEN {}", - expr.to_sql_string(context) - )), - self.commands - .iter() - .map(|command| format!("{};", command.to_sql_string(context))) - .collect::>() - .join(" ") - ) - } -} - -impl Display for ast::TriggerTime { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl Display for ast::TriggerEvent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Delete => "DELETE".to_string(), - Self::Insert => "INSERT".to_string(), - Self::Update => "UPDATE".to_string(), - Self::UpdateOf(col_names) => format!( - "UPDATE OF {}", - col_names - .iter() - .map(|name| name.0.clone()) - .collect::>() - .join(", ") - ), - } - ) - } -} - -impl ToSqlString for ast::TriggerCmd { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::Delete(delete) => delete.to_sql_string(context), - Self::Insert(insert) => insert.to_sql_string(context), - Self::Select(select) => select.to_sql_string(context), - Self::Update(update) => update.to_sql_string(context), - } - } -} - -impl ToSqlString for ast::TriggerCmdDelete { - fn to_sql_string(&self, context: &C) -> String { - // https://sqlite.org/lang_createtrigger.html - // TODO: no CTEs and returning clause present in ast for delete - // Also for tbl_name it should be a qualified table name with indexed by clauses - format!( - "DELETE FROM {}{}", - self.tbl_name.0, - self.where_clause - .as_ref() - .map_or("".to_string(), |expr| format!( - " WHERE {}", - expr.to_sql_string(context) - )) - ) - } -} - -impl ToSqlString for ast::TriggerCmdInsert { - fn to_sql_string(&self, context: &C) -> String { - // https://sqlite.org/lang_createtrigger.html - // FOR TRIGGER SHOULD JUST USE REGULAR INSERT AST - // TODO: no ALIAS after table name - // TODO: no DEFAULT VALUES - format!( - "INSERT {}INTO {} {}{}{}{}", - self.or_conflict - .map_or("".to_string(), |conflict| format!("OR {conflict} ")), - self.tbl_name.0, - self.col_names - .as_ref() - .map_or("".to_string(), |col_names| format!( - "({}) ", - col_names - .iter() - .map(|name| name.0.clone()) - .collect::>() - .join(", ") - )), - self.select.to_sql_string(context), - self.upsert - .as_ref() - .map_or("".to_string(), |upsert| format!( - " {}", - upsert.to_sql_string(context) - )), - self.returning - .as_ref() - .map_or("".to_string(), |returning| format!( - " RETURNING {}", - returning - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", ") - )) - ) - } -} - -impl ToSqlString for ast::Upsert { - fn to_sql_string(&self, context: &C) -> String { - format!( - "ON CONFLICT{}{}{}", - self.index.as_ref().map_or("".to_string(), |index| format!( - "{} ", - index.to_sql_string(context) - )), - self.do_clause.to_sql_string(context), - self.next.as_ref().map_or("".to_string(), |next| format!( - " {}", - next.to_sql_string(context) - )) - ) - } -} - -impl ToSqlString for ast::UpsertIndex { - fn to_sql_string(&self, context: &C) -> String { - format!( - "({}){}", - self.targets - .iter() - .map(|target| target.to_sql_string(context)) - .collect::>() - .join(", "), - self.where_clause - .as_ref() - .map_or("".to_string(), |expr| format!( - " WHERE {}", - expr.to_sql_string(context) - )) - ) - } -} - -impl ToSqlString for ast::UpsertDo { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::Nothing => "DO NOTHING".to_string(), - Self::Set { sets, where_clause } => { - format!( - "DO UPDATE SET {}{}", - sets.iter() - .map(|set| set.to_sql_string(context)) - .collect::>() - .join(", "), - where_clause.as_ref().map_or("".to_string(), |expr| format!( - " WHERE {}", - expr.to_sql_string(context) - )) - ) - } - } - } -} - -impl ToSqlString for ast::Set { - fn to_sql_string(&self, context: &C) -> String { - if self.col_names.len() == 1 { - format!( - "{} = {}", - &self.col_names[0], - self.expr.to_sql_string(context) - ) - } else { - format!( - "({}) = {}", - self.col_names - .iter() - .map(|name| name.0.clone()) - .collect::>() - .join(", "), - self.expr.to_sql_string(context) - ) - } - } -} - -impl ToSqlString for ast::TriggerCmdUpdate { - fn to_sql_string(&self, context: &C) -> String { - format!( - "UPDATE {}{} SET {}{}{}", - self.or_conflict - .map_or("".to_string(), |conflict| format!("OR {conflict}")), - self.tbl_name.0, // TODO: should be a qualified table name, - self.sets - .iter() - .map(|set| set.to_sql_string(context)) - .collect::>() - .join(", "), - self.from.as_ref().map_or("".to_string(), |from| format!( - " {}", - from.to_sql_string(context) - )), - self.where_clause - .as_ref() - .map_or("".to_string(), |expr| format!( - " WHERE {}", - expr.to_sql_string(context) - )) - ) - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; @@ -255,7 +10,7 @@ mod tests { BEGIN INSERT INTO employee_log (action, employee_id, timestamp) VALUES ('INSERT', NEW.id, CURRENT_TIMESTAMP); - END;" + END" ); to_sql_string_test!( @@ -266,7 +21,7 @@ mod tests { BEGIN INSERT INTO employee_log (action, employee_id, old_value, new_value, timestamp) VALUES ('UPDATE', OLD.id, OLD.salary, NEW.salary, CURRENT_TIMESTAMP); - END;" + END" ); to_sql_string_test!( @@ -277,7 +32,7 @@ mod tests { BEGIN INSERT INTO employee_log (action, employee_id, timestamp) VALUES ('DELETE', OLD.id, CURRENT_TIMESTAMP); - END;" + END" ); to_sql_string_test!( @@ -287,8 +42,8 @@ mod tests { FOR EACH ROW WHEN NEW.salary < 0 BEGIN - SELECT RAISE(FAIL, 'Salary cannot be negative'); - END;" + SELECT RAISE (FAIL, 'Salary cannot be negative'); + END" ); to_sql_string_test!( @@ -299,6 +54,6 @@ mod tests { BEGIN INSERT INTO departments (name) SELECT NEW.department WHERE NOT EXISTS (SELECT 1 FROM departments WHERE name = NEW.department); INSERT INTO employees (name, department_id) VALUES (NEW.name, (SELECT id FROM departments WHERE name = NEW.department)); - END;" + END" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs index 5617867c3..8887bf932 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/create_virtual_table.rs @@ -1,84 +1,64 @@ -use crate::{ast, to_sql_string::ToSqlString}; - -impl ToSqlString for ast::CreateVirtualTable { - fn to_sql_string(&self, context: &C) -> String { - format!( - "CREATE VIRTUAL TABLE {}{} USING {}{};", - if self.if_not_exists { - "IF NOT EXISTS " - } else { - "" - }, - self.tbl_name.to_sql_string(context), - self.module_name.0, - self.args - .as_ref() - .map_or("".to_string(), |args| format!("({})", args.join(", "))) - ) - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; to_sql_string_test!( test_create_virtual_table_fts5_basic, - "CREATE VIRTUAL TABLE docs USING fts5(title, content);" + "CREATE VIRTUAL TABLE docs USING fts5 (title, content)" ); to_sql_string_test!( test_create_virtual_table_fts5_tokenizer, - "CREATE VIRTUAL TABLE docs USING fts5(title, content, tokenize = 'porter');" + "CREATE VIRTUAL TABLE docs USING fts5 (title, content, tokenize = 'porter')" ); to_sql_string_test!( test_create_virtual_table_fts5_unindexed, - "CREATE VIRTUAL TABLE docs USING fts5(title, content, metadata UNINDEXED);" + "CREATE VIRTUAL TABLE docs USING fts5 (title, content, metadata UNINDEXED)" ); to_sql_string_test!( test_create_virtual_table_fts5_prefix, - "CREATE VIRTUAL TABLE docs USING fts5(title, content, tokenize = 'unicode61', prefix = '2 4');" + "CREATE VIRTUAL TABLE docs USING fts5 (title, content, tokenize = 'unicode61', prefix = '2 4')" ); to_sql_string_test!( test_create_virtual_table_fts5_contentless, - "CREATE VIRTUAL TABLE docs USING fts5(title, content, content = '');" + "CREATE VIRTUAL TABLE docs USING fts5 (title, content, content = '')" ); to_sql_string_test!( test_create_virtual_table_fts5_external_content, - "CREATE VIRTUAL TABLE docs_fts USING fts5(title, content, content = 'documents');" + "CREATE VIRTUAL TABLE docs_fts USING fts5 (title, content, content = 'documents')" ); to_sql_string_test!( test_create_virtual_table_rtree, - "CREATE VIRTUAL TABLE geo USING rtree(id, min_x, max_x, min_y, max_y);" + "CREATE VIRTUAL TABLE geo USING rtree (id, min_x, max_x, min_y, max_y)" ); to_sql_string_test!( test_create_virtual_table_rtree_aux, - "CREATE VIRTUAL TABLE geo USING rtree(id, min_x, max_x, min_y, max_y, +name TEXT, +category INTEGER);" + "CREATE VIRTUAL TABLE geo USING rtree (id, min_x, max_x, min_y, max_y, +name TEXT, +category INTEGER)" ); to_sql_string_test!( test_create_virtual_table_if_not_exists, - "CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5(title, content);" + "CREATE VIRTUAL TABLE IF NOT EXISTS docs USING fts5 (title, content)" ); to_sql_string_test!( test_create_virtual_table_fts4, - "CREATE VIRTUAL TABLE docs USING fts4(title, content, matchinfo = 'fts3');" + "CREATE VIRTUAL TABLE docs USING fts4 (title, content, matchinfo = 'fts3')" ); to_sql_string_test!( test_create_virtual_table_fts5_detail, - "CREATE VIRTUAL TABLE docs USING fts5(title, body TEXT, detail = 'none');" + "CREATE VIRTUAL TABLE docs USING fts5 (title, body TEXT, detail = 'none')" ); to_sql_string_test!( test_create_virtual_table_schema, - "CREATE VIRTUAL TABLE main.docs USING fts5(title, content);" + "CREATE VIRTUAL TABLE main.docs USING fts5 (title, content)" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs index 857a72210..18dd8b783 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/delete.rs @@ -1,133 +1,82 @@ -use crate::{ast, to_sql_string::ToSqlString}; - -impl ToSqlString for ast::Delete { - fn to_sql_string(&self, context: &C) -> String { - format!( - "{}DELETE FROM {}{}{}{}{}{};", - self.with.as_ref().map_or("".to_string(), |with| format!( - "{} ", - with.to_sql_string(context) - )), - self.tbl_name.to_sql_string(context), - self.indexed - .as_ref() - .map_or("".to_string(), |indexed| format!(" {indexed}")), - self.where_clause - .as_ref() - .map_or("".to_string(), |expr| format!( - " WHERE {}", - expr.to_sql_string(context) - )), - self.returning - .as_ref() - .map_or("".to_string(), |returning| format!( - " RETURNING {}", - returning - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", ") - )), - self.order_by - .as_ref() - .map_or("".to_string(), |order_by| format!( - " ORDER BY {}", - order_by - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", ") - )), - self.limit.as_ref().map_or("".to_string(), |limit| format!( - " {}", - limit.to_sql_string(context) - )) - ) - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; // Basic DELETE from a single table - to_sql_string_test!(test_delete_all, "DELETE FROM employees;"); + to_sql_string_test!(test_delete_all, "DELETE FROM employees"); // DELETE with a simple WHERE clause - to_sql_string_test!( - test_delete_with_where, - "DELETE FROM employees WHERE id = 1;" - ); + to_sql_string_test!(test_delete_with_where, "DELETE FROM employees WHERE id = 1"); // DELETE with multiple WHERE conditions to_sql_string_test!( test_delete_with_multi_where, - "DELETE FROM employees WHERE salary < 50000 AND department_id = 3;" + "DELETE FROM employees WHERE salary < 50000 AND department_id = 3" ); // DELETE with IN clause to_sql_string_test!( test_delete_with_in, - "DELETE FROM employees WHERE id IN (1, 2, 3);" + "DELETE FROM employees WHERE id IN (1, 2, 3)" ); // DELETE with subquery in WHERE to_sql_string_test!( test_delete_with_subquery, - "DELETE FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales');" + "DELETE FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales')" ); // DELETE with EXISTS clause to_sql_string_test!( test_delete_with_exists, - "DELETE FROM employees WHERE EXISTS (SELECT 1 FROM orders WHERE orders.employee_id = employees.id AND orders.status = 'pending');" + "DELETE FROM employees WHERE EXISTS (SELECT 1 FROM orders WHERE orders.employee_id = employees.id AND orders.status = 'pending')" ); // DELETE with RETURNING clause to_sql_string_test!( test_delete_with_returning, - "DELETE FROM employees WHERE salary < 30000 RETURNING id, name;" + "DELETE FROM employees WHERE salary < 30000 RETURNING id, name" ); // DELETE with LIMIT clause to_sql_string_test!( test_delete_with_limit, - "DELETE FROM employees WHERE salary < 40000 LIMIT 5;" + "DELETE FROM employees WHERE salary < 40000 LIMIT 5" ); // DELETE with ORDER BY and LIMIT to_sql_string_test!( test_delete_with_order_by_limit, - "DELETE FROM employees WHERE salary < 40000 ORDER BY id DESC LIMIT 5;" + "DELETE FROM employees WHERE salary < 40000 ORDER BY id DESC LIMIT 5" ); // DELETE from schema-qualified table to_sql_string_test!( test_delete_schema_qualified, - "DELETE FROM main.employees WHERE id = 1;" + "DELETE FROM main.employees WHERE id = 1" ); // DELETE with BETWEEN clause to_sql_string_test!( test_delete_with_between, - "DELETE FROM employees WHERE salary BETWEEN 30000 AND 50000;" + "DELETE FROM employees WHERE salary BETWEEN 30000 AND 50000" ); // DELETE with NULL check to_sql_string_test!( test_delete_with_null, - "DELETE FROM employees WHERE department_id IS NULL;" + "DELETE FROM employees WHERE department_id IS NULL" ); // DELETE with LIKE clause to_sql_string_test!( test_delete_with_like, - "DELETE FROM employees WHERE name LIKE 'J%';" + "DELETE FROM employees WHERE name LIKE 'J%'" ); // DELETE with complex expression in WHERE to_sql_string_test!( test_delete_with_complex_expression, - "DELETE FROM employees WHERE (salary * 1.1) > 60000 AND department_id != 1;" + "DELETE FROM employees WHERE (salary * 1.1) > 60000 AND department_id != 1" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs index 1bf67ed1d..89daf151c 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/insert.rs @@ -1,57 +1,3 @@ -use crate::{ast, to_sql_string::ToSqlString}; - -impl ToSqlString for ast::Insert { - fn to_sql_string(&self, context: &C) -> String { - format!( - "{}INSERT {}INTO {} {}{}{}", - self.with.as_ref().map_or("".to_string(), |with| format!( - "{} ", - with.to_sql_string(context) - )), - self.or_conflict - .map_or("".to_string(), |conflict| format!("OR {conflict} ")), - self.tbl_name.to_sql_string(context), - self.columns - .as_ref() - .map_or("".to_string(), |col_names| format!( - "({}) ", - col_names - .iter() - .map(|name| name.0.clone()) - .collect::>() - .join(", ") - )), - self.body.to_sql_string(context), - self.returning - .as_ref() - .map_or("".to_string(), |returning| format!( - " RETURNING {}", - returning - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", ") - )) - ) - } -} - -impl ToSqlString for ast::InsertBody { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::DefaultValues => "DEFAULT VALUES".to_string(), - Self::Select(select, upsert) => format!( - "{}{}", - select.to_sql_string(context), - upsert.as_ref().map_or("".to_string(), |upsert| format!( - " {}", - upsert.to_sql_string(context) - )), - ), - } - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; @@ -59,90 +5,90 @@ mod tests { // Basic INSERT with all columns to_sql_string_test!( test_insert_basic, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000);" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000)" ); // INSERT with multiple rows to_sql_string_test!( test_insert_multiple_rows, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000), (2, 'Jane Smith', 60000);" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000), (2, 'Jane Smith', 60000)" ); // INSERT with specific columns to_sql_string_test!( test_insert_specific_columns, - "INSERT INTO employees (name, salary) VALUES ('Alice Brown', 55000);" + "INSERT INTO employees (name, salary) VALUES ('Alice Brown', 55000)" ); // INSERT with DEFAULT VALUES to_sql_string_test!( test_insert_default_values, - "INSERT INTO employees DEFAULT VALUES;" + "INSERT INTO employees DEFAULT VALUES" ); // INSERT with SELECT subquery to_sql_string_test!( test_insert_select_subquery, - "INSERT INTO employees (id, name, salary) SELECT id, name, salary FROM temp_employees WHERE salary > 40000;" + "INSERT INTO employees (id, name, salary) SELECT id, name, salary FROM temp_employees WHERE salary > 40000" ); // INSERT with ON CONFLICT IGNORE to_sql_string_test!( test_insert_on_conflict_ignore, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO NOTHING;" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO NOTHING" ); // INSERT with ON CONFLICT REPLACE to_sql_string_test!( test_insert_on_conflict_replace, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET name = excluded.name, salary = excluded.salary;" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO UPDATE SET name = excluded.name, salary = excluded.salary" ); // INSERT with RETURNING clause to_sql_string_test!( test_insert_with_returning, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) RETURNING id, name;" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) RETURNING id, name" ); // INSERT with NULL values to_sql_string_test!( test_insert_with_null, - "INSERT INTO employees (id, name, salary, department_id) VALUES (1, 'John Doe', NULL, NULL);" + "INSERT INTO employees (id, name, salary, department_id) VALUES (1, 'John Doe', NULL, NULL)" ); // INSERT with expression in VALUES to_sql_string_test!( test_insert_with_expression, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000 * 1.1);" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000 * 1.1)" ); // INSERT into schema-qualified table to_sql_string_test!( test_insert_schema_qualified, - "INSERT INTO main.employees (id, name, salary) VALUES (1, 'John Doe', 50000);" + "INSERT INTO main.employees (id, name, salary) VALUES (1, 'John Doe', 50000)" ); // INSERT with subquery and JOIN to_sql_string_test!( test_insert_subquery_join, - "INSERT INTO employees (id, name, department_id) SELECT e.id, e.name, d.id FROM temp_employees e JOIN departments d ON e.dept_name = d.name;" + "INSERT INTO employees (id, name, department_id) SELECT e.id, e.name, d.id FROM temp_employees e JOIN departments d ON e.dept_name = d.name" ); // INSERT with all columns from SELECT to_sql_string_test!( test_insert_all_columns_select, - "INSERT INTO employees SELECT * FROM temp_employees;" + "INSERT INTO employees SELECT * FROM temp_employees" ); // INSERT with ON CONFLICT and WHERE clause to_sql_string_test!( test_insert_on_conflict_where, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET salary = excluded.salary WHERE excluded.salary > employees.salary;" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO UPDATE SET salary = excluded.salary WHERE excluded.salary > employees.salary" ); // INSERT with quoted column names (reserved words) to_sql_string_test!( test_insert_quoted_columns, - "INSERT INTO employees (\"select\", \"from\") VALUES (1, 'data');" + "INSERT INTO employees (\"select\", \"from\") VALUES (1, 'data')" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs index 3477a757a..10d6af66f 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/mod.rs @@ -1,7 +1,3 @@ -use crate::ast; - -use super::ToSqlString; - mod alter_table; mod create_table; mod create_trigger; @@ -11,196 +7,6 @@ mod insert; mod select; mod update; -impl ToSqlString for ast::Stmt { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::AlterTable(alter_table) => { - let (name, body) = alter_table.as_ref(); - format!( - "ALTER TABLE {} {};", - name.to_sql_string(context), - body.to_sql_string(context) - ) - } - Self::Analyze(name) => { - if let Some(name) = name { - format!("ANALYZE {};", name.to_sql_string(context)) - } else { - "ANALYZE;".to_string() - } - } - Self::Attach { - expr, - db_name, - key: _, - } => { - // TODO: what is `key` in the attach syntax? - format!( - "ATTACH {} AS {};", - expr.to_sql_string(context), - db_name.to_sql_string(context) - ) - } - // TODO: not sure where name is applied here - // https://www.sqlite.org/lang_transaction.html - Self::Begin(transaction_type, _name) => { - let t_type = transaction_type.map_or("", |t_type| match t_type { - ast::TransactionType::Deferred => " DEFERRED", - ast::TransactionType::Exclusive => " EXCLUSIVE", - ast::TransactionType::Immediate => " IMMEDIATE", - }); - format!("BEGIN{t_type};") - } - // END or COMMIT are equivalent here, so just defaulting to COMMIT - // TODO: again there are no names in the docs - Self::Commit(_name) => "COMMIT;".to_string(), - Self::CreateIndex { - unique, - if_not_exists, - idx_name, - tbl_name, - columns, - where_clause, - } => format!( - "CREATE {}INDEX {}{} ON {} ({}){};", - unique.then_some("UNIQUE ").unwrap_or(""), - if_not_exists.then_some("IF NOT EXISTS ").unwrap_or(""), - idx_name.to_sql_string(context), - tbl_name.0, - columns - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", "), - where_clause - .as_ref() - .map_or("".to_string(), |where_clause| format!( - " WHERE {}", - where_clause.to_sql_string(context) - )) - ), - Self::CreateTable { - temporary, - if_not_exists, - tbl_name, - body, - } => format!( - "CREATE{} TABLE {}{} {};", - temporary.then_some(" TEMP").unwrap_or(""), - if_not_exists.then_some("IF NOT EXISTS ").unwrap_or(""), - tbl_name.to_sql_string(context), - body.to_sql_string(context) - ), - Self::CreateTrigger(trigger) => trigger.to_sql_string(context), - Self::CreateView { - temporary, - if_not_exists, - view_name, - columns, - select, - } => { - format!( - "CREATE{} VIEW {}{}{} AS {};", - temporary.then_some(" TEMP").unwrap_or(""), - if_not_exists.then_some("IF NOT EXISTS ").unwrap_or(""), - view_name.to_sql_string(context), - columns.as_ref().map_or("".to_string(), |columns| format!( - " ({})", - columns - .iter() - .map(|col| col.to_string()) - .collect::>() - .join(", ") - )), - select.to_sql_string(context) - ) - } - Self::CreateVirtualTable(create_virtual_table) => { - create_virtual_table.to_sql_string(context) - } - Self::Delete(delete) => delete.to_sql_string(context), - Self::Detach(name) => format!("DETACH {};", name.to_sql_string(context)), - Self::DropIndex { - if_exists, - idx_name, - } => format!( - "DROP INDEX{} {};", - if_exists.then_some("IF EXISTS ").unwrap_or(""), - idx_name.to_sql_string(context) - ), - Self::DropTable { - if_exists, - tbl_name, - } => format!( - "DROP TABLE{} {};", - if_exists.then_some("IF EXISTS ").unwrap_or(""), - tbl_name.to_sql_string(context) - ), - Self::DropTrigger { - if_exists, - trigger_name, - } => format!( - "DROP TRIGGER{} {};", - if_exists.then_some("IF EXISTS ").unwrap_or(""), - trigger_name.to_sql_string(context) - ), - Self::DropView { - if_exists, - view_name, - } => format!( - "DROP VIEW{} {};", - if_exists.then_some("IF EXISTS ").unwrap_or(""), - view_name.to_sql_string(context) - ), - Self::Insert(insert) => format!("{};", insert.to_sql_string(context)), - Self::Pragma(name, body) => format!( - "PRAGMA {}{};", - name.to_sql_string(context), - body.as_ref() - .map_or("".to_string(), |body| match body.as_ref() { - ast::PragmaBody::Equals(expr) => - format!(" = {}", expr.to_sql_string(context)), - ast::PragmaBody::Call(expr) => format!("({})", expr.to_sql_string(context)), - }) - ), - // TODO: missing collation name - Self::Reindex { obj_name } => format!( - "REINDEX{};", - obj_name.as_ref().map_or("".to_string(), |name| format!( - " {}", - name.to_sql_string(context) - )) - ), - Self::Release(name) => format!("RELEASE {};", name.0), - Self::Rollback { - // TODO: there is no transaction name in SQLITE - // https://www.sqlite.org/lang_transaction.html - tx_name: _, - savepoint_name, - } => format!( - "ROLLBACK{};", - savepoint_name - .as_ref() - .map_or("".to_string(), |name| format!(" TO {}", name.0)) - ), - Self::Savepoint(name) => format!("SAVEPOINT {};", name.0), - Self::Select(select) => format!("{};", select.to_sql_string(context)), - Self::Update(update) => format!("{};", update.to_sql_string(context)), - Self::Vacuum(name, expr) => { - format!( - "VACUUM{}{};", - name.as_ref() - .map_or("".to_string(), |name| format!(" {}", name.0)), - expr.as_ref().map_or("".to_string(), |expr| format!( - " INTO {}", - expr.to_sql_string(context) - )) - ) - } - } - } -} - #[cfg(test)] mod tests { use crate::to_sql_string::ToSqlContext; @@ -211,6 +17,7 @@ mod tests { ($test_name:ident, $input:expr) => { #[test] fn $test_name() { + use $crate::parser::ast::fmt::ToTokens; let context = $crate::to_sql_string::stmt::tests::TestContext; let input = $input.split_whitespace().collect::>().join(" "); let mut parser = $crate::lexer::sql::Parser::new(input.as_bytes()); @@ -219,7 +26,7 @@ mod tests { .unwrap(); assert_eq!( input, - $crate::to_sql_string::ToSqlString::to_sql_string(cmd.stmt(), &context) + cmd.stmt().format_with_context(&context).unwrap().replace('\n', " "), ); } }; @@ -227,6 +34,7 @@ mod tests { #[test] $(#[$attribute])* fn $test_name() { + use $crate::parser::ast::fmt::ToTokens; let context = $crate::to_sql_string::stmt::tests::TestContext; let input = $input.split_whitespace().collect::>().join(" "); let mut parser = $crate::lexer::sql::Parser::new(input.as_bytes()); @@ -235,7 +43,7 @@ mod tests { .unwrap(); assert_eq!( input, - $crate::to_sql_string::ToSqlString::to_sql_string(cmd.stmt(), &context) + cmd.stmt().format_with_context(&context).unwrap().replace('\n', " "), ); } } @@ -255,163 +63,163 @@ mod tests { } } - to_sql_string_test!(test_analyze, "ANALYZE;"); + to_sql_string_test!(test_analyze, "ANALYZE"); to_sql_string_test!( test_analyze_table, - "ANALYZE table;", + "ANALYZE table", ignore = "parser can't parse table name" ); to_sql_string_test!( test_analyze_schema_table, - "ANALYZE schema.table;", + "ANALYZE schema.table", ignore = "parser can't parse schema.table name" ); - to_sql_string_test!(test_attach, "ATTACH './test.db' AS test_db;"); + to_sql_string_test!(test_attach, "ATTACH './test.db' AS test_db"); - to_sql_string_test!(test_transaction, "BEGIN;"); + to_sql_string_test!(test_transaction, "BEGIN"); - to_sql_string_test!(test_transaction_deferred, "BEGIN DEFERRED;"); + to_sql_string_test!(test_transaction_deferred, "BEGIN DEFERRED"); - to_sql_string_test!(test_transaction_immediate, "BEGIN IMMEDIATE;"); + to_sql_string_test!(test_transaction_immediate, "BEGIN IMMEDIATE"); - to_sql_string_test!(test_transaction_exclusive, "BEGIN EXCLUSIVE;"); + to_sql_string_test!(test_transaction_exclusive, "BEGIN EXCLUSIVE"); - to_sql_string_test!(test_commit, "COMMIT;"); + to_sql_string_test!(test_commit, "COMMIT"); // Test a simple index on a single column to_sql_string_test!( test_create_index_simple, - "CREATE INDEX idx_name ON employees (last_name);" + "CREATE INDEX idx_name ON employees (last_name)" ); // Test a unique index to enforce uniqueness on a column to_sql_string_test!( test_create_unique_index, - "CREATE UNIQUE INDEX idx_unique_email ON users (email);" + "CREATE UNIQUE INDEX idx_unique_email ON users (email)" ); // Test a multi-column index to_sql_string_test!( test_create_index_multi_column, - "CREATE INDEX idx_name_salary ON employees (last_name, salary);" + "CREATE INDEX idx_name_salary ON employees (last_name, salary)" ); // Test a partial index with a WHERE clause to_sql_string_test!( test_create_partial_index, - "CREATE INDEX idx_active_users ON users (username) WHERE active = true;" + "CREATE INDEX idx_active_users ON users (username) WHERE active = true" ); // Test an index on an expression to_sql_string_test!( test_create_index_on_expression, - "CREATE INDEX idx_upper_name ON employees (UPPER(last_name));" + "CREATE INDEX idx_upper_name ON employees (UPPER (last_name))" ); // Test an index with descending order to_sql_string_test!( test_create_index_descending, - "CREATE INDEX idx_salary_desc ON employees (salary DESC);" + "CREATE INDEX idx_salary_desc ON employees (salary DESC)" ); // Test an index with mixed ascending and descending orders on multiple columns to_sql_string_test!( test_create_index_mixed_order, - "CREATE INDEX idx_name_asc_salary_desc ON employees (last_name ASC, salary DESC);" + "CREATE INDEX idx_name_asc_salary_desc ON employees (last_name ASC, salary DESC)" ); // Test 1: View with DISTINCT keyword to_sql_string_test!( test_create_view_distinct, - "CREATE VIEW view_distinct AS SELECT DISTINCT name FROM employees;" + "CREATE VIEW view_distinct AS SELECT DISTINCT name FROM employees" ); // Test 2: View with LIMIT clause to_sql_string_test!( test_create_view_limit, - "CREATE VIEW view_limit AS SELECT id, name FROM employees LIMIT 10;" + "CREATE VIEW view_limit AS SELECT id, name FROM employees LIMIT 10" ); // Test 3: View with CASE expression to_sql_string_test!( test_create_view_case, - "CREATE VIEW view_case AS SELECT name, CASE WHEN salary > 70000 THEN 'High' ELSE 'Low' END AS salary_level FROM employees;" + "CREATE VIEW view_case AS SELECT name, CASE WHEN salary > 70000 THEN 'High' ELSE 'Low' END AS salary_level FROM employees" ); // Test 4: View with LEFT JOIN to_sql_string_test!( test_create_view_left_join, - "CREATE VIEW view_left_join AS SELECT e.name, d.name AS department FROM employees e LEFT JOIN departments d ON e.department_id = d.id;" + "CREATE VIEW view_left_join AS SELECT e.name, d.name AS department FROM employees e LEFT OUTER JOIN departments d ON e.department_id = d.id" ); // Test 5: View with HAVING clause to_sql_string_test!( test_create_view_having, - "CREATE VIEW view_having AS SELECT department_id, AVG(salary) AS avg_salary FROM employees GROUP BY department_id HAVING AVG(salary) > 55000;" + "CREATE VIEW view_having AS SELECT department_id, AVG (salary) AS avg_salary FROM employees GROUP BY department_id HAVING AVG (salary) > 55000" ); // Test 6: View with CTE (Common Table Expression) to_sql_string_test!( test_create_view_cte, - "CREATE VIEW view_cte AS WITH high_earners AS (SELECT * FROM employees WHERE salary > 80000) SELECT id, name FROM high_earners;" + "CREATE VIEW view_cte AS WITH high_earners AS (SELECT * FROM employees WHERE salary > 80000) SELECT id, name FROM high_earners" ); // Test 7: View with multiple conditions in WHERE to_sql_string_test!( test_create_view_multi_where, - "CREATE VIEW view_multi_where AS SELECT id, name FROM employees WHERE salary > 50000 AND department_id = 3;" + "CREATE VIEW view_multi_where AS SELECT id, name FROM employees WHERE salary > 50000 AND department_id = 3" ); // Test 8: View with NULL handling to_sql_string_test!( test_create_view_null, - "CREATE VIEW view_null AS SELECT name, COALESCE(salary, 0) AS salary FROM employees;" + "CREATE VIEW view_null AS SELECT name, COALESCE (salary, 0) AS salary FROM employees" ); // Test 9: View with subquery in WHERE clause to_sql_string_test!( test_create_view_subquery_where, - "CREATE VIEW view_subquery_where AS SELECT name FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales');" + "CREATE VIEW view_subquery_where AS SELECT name FROM employees WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales')" ); // Test 10: View with arithmetic expression to_sql_string_test!( test_create_view_arithmetic, - "CREATE VIEW view_arithmetic AS SELECT name, salary * 1.1 AS adjusted_salary FROM employees;" + "CREATE VIEW view_arithmetic AS SELECT name, salary * 1.1 AS adjusted_salary FROM employees" ); - to_sql_string_test!(test_detach, "DETACH 'x.db';"); + to_sql_string_test!(test_detach, "DETACH 'x.db'"); - to_sql_string_test!(test_drop_index, "DROP INDEX schema_name.test_index;"); + to_sql_string_test!(test_drop_index, "DROP INDEX schema_name.test_index"); - to_sql_string_test!(test_drop_table, "DROP TABLE schema_name.test_table;"); + to_sql_string_test!(test_drop_table, "DROP TABLE schema_name.test_table"); - to_sql_string_test!(test_drop_trigger, "DROP TRIGGER schema_name.test_trigger;"); + to_sql_string_test!(test_drop_trigger, "DROP TRIGGER schema_name.test_trigger"); - to_sql_string_test!(test_drop_view, "DROP VIEW schema_name.test_view;"); + to_sql_string_test!(test_drop_view, "DROP VIEW schema_name.test_view"); - to_sql_string_test!(test_pragma_equals, "PRAGMA schema_name.Pragma_name = 1;"); + to_sql_string_test!(test_pragma_equals, "PRAGMA schema_name.Pragma_name = 1"); - to_sql_string_test!(test_pragma_call, "PRAGMA schema_name.Pragma_name_2(1);"); + to_sql_string_test!(test_pragma_call, "PRAGMA schema_name.Pragma_name_2 (1)"); - to_sql_string_test!(test_reindex, "REINDEX schema_name.test_table;"); + to_sql_string_test!(test_reindex, "REINDEX schema_name.test_table"); - to_sql_string_test!(test_reindex_2, "REINDEX;"); + to_sql_string_test!(test_reindex_2, "REINDEX"); - to_sql_string_test!(test_release, "RELEASE savepoint_name;"); + to_sql_string_test!(test_release, "RELEASE savepoint_name"); - to_sql_string_test!(test_rollback, "ROLLBACK;"); + to_sql_string_test!(test_rollback, "ROLLBACK"); - to_sql_string_test!(test_rollback_2, "ROLLBACK TO savepoint_name;"); + to_sql_string_test!(test_rollback_2, "ROLLBACK TO savepoint_name"); - to_sql_string_test!(test_savepoint, "SAVEPOINT savepoint_name;"); + to_sql_string_test!(test_savepoint, "SAVEPOINT savepoint_name"); - to_sql_string_test!(test_vacuum, "VACUUM;"); + to_sql_string_test!(test_vacuum, "VACUUM"); - to_sql_string_test!(test_vacuum_2, "VACUUM schema_name;"); + to_sql_string_test!(test_vacuum_2, "VACUUM schema_name"); - to_sql_string_test!(test_vacuum_3, "VACUUM schema_name INTO test.db;"); + to_sql_string_test!(test_vacuum_3, "VACUUM schema_name INTO test.db"); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs index 8989ca401..68e4cc4a9 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/select.rs @@ -1,658 +1,122 @@ -use std::fmt::Display; - -use crate::{ - ast::{self, fmt::ToTokens}, - to_sql_string::{ToSqlContext, ToSqlString}, -}; - -impl ToSqlString for ast::Select { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = Vec::new(); - if let Some(with) = &self.with { - ret.push(with.to_sql_string(context)); - } - - ret.push(self.body.to_sql_string(context)); - - if let Some(order_by) = &self.order_by { - // TODO: SortedColumn missing collation in ast - let joined_cols = order_by - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push(format!("ORDER BY {joined_cols}")); - } - if let Some(limit) = &self.limit { - ret.push(limit.to_sql_string(context)); - } - ret.join(" ") - } -} - -impl ToSqlString for ast::SelectBody { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = self.select.to_sql_string(context); - - if let Some(compounds) = &self.compounds { - ret.push(' '); - let compound_selects = compounds - .iter() - .map(|compound_select| { - let mut curr = compound_select.operator.to_string(); - curr.push(' '); - curr.push_str(&compound_select.select.to_sql_string(context)); - curr - }) - .collect::>() - .join(" "); - ret.push_str(&compound_selects); - } - ret - } -} - -impl ToSqlString for ast::OneSelect { - fn to_sql_string(&self, context: &C) -> String { - match self { - ast::OneSelect::Select(select) => select.to_sql_string(context), - ast::OneSelect::Values(values) => { - let joined_values = values - .iter() - .map(|value| { - let joined_value = value - .iter() - .map(|e| e.to_sql_string(context)) - .collect::>() - .join(", "); - format!("({joined_value})") - }) - .collect::>() - .join(", "); - format!("VALUES {joined_values}") - } - } - } -} - -impl ToSqlString for ast::SelectInner { - fn to_sql_string(&self, context: &C) -> String { - // dbg!(&self); - let mut ret = Vec::with_capacity(2 + self.columns.len()); - ret.push("SELECT".to_string()); - if let Some(distinct) = self.distinctness { - ret.push(distinct.to_string()); - } - let joined_cols = self - .columns - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push(joined_cols); - - if let Some(from) = &self.from { - ret.push(from.to_sql_string(context)); - } - if let Some(where_expr) = &self.where_clause { - ret.push("WHERE".to_string()); - ret.push(where_expr.to_sql_string(context)); - } - if let Some(group_by) = &self.group_by { - ret.push(group_by.to_sql_string(context)); - } - if let Some(window_clause) = &self.window_clause { - ret.push("WINDOW".to_string()); - let joined_window = window_clause - .iter() - .map(|window_def| window_def.to_sql_string(context)) - .collect::>() - .join(","); - ret.push(joined_window); - } - - ret.join(" ") - } -} - -impl ToSqlString for ast::FromClause { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = String::from("FROM"); - if let Some(select_table) = &self.select { - ret.push(' '); - ret.push_str(&select_table.to_sql_string(context)); - } - if let Some(joins) = &self.joins { - ret.push(' '); - let joined_joins = joins - .iter() - .map(|join| { - let mut curr = join.operator.to_string(); - curr.push(' '); - curr.push_str(&join.table.to_sql_string(context)); - if let Some(join_constraint) = &join.constraint { - curr.push(' '); - curr.push_str(&join_constraint.to_sql_string(context)); - } - curr - }) - .collect::>() - .join(" "); - ret.push_str(&joined_joins); - } - ret - } -} - -impl ToSqlString for ast::SelectTable { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = String::new(); - match self { - Self::Table(name, alias, indexed) => { - ret.push_str(&name.to_sql_string(context)); - if let Some(alias) = alias { - ret.push(' '); - ret.push_str(&alias.to_string()); - } - if let Some(indexed) = indexed { - ret.push(' '); - ret.push_str(&indexed.to_string()); - } - } - Self::TableCall(table_func, args, alias) => { - ret.push_str(&table_func.to_sql_string(context)); - if let Some(args) = args { - ret.push(' '); - let joined_args = args - .iter() - .map(|arg| arg.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push_str(&joined_args); - } - if let Some(alias) = alias { - ret.push(' '); - ret.push_str(&alias.to_string()); - } - } - Self::Select(select, alias) => { - ret.push('('); - ret.push_str(&select.to_sql_string(context)); - ret.push(')'); - if let Some(alias) = alias { - ret.push(' '); - ret.push_str(&alias.to_string()); - } - } - Self::Sub(from_clause, alias) => { - ret.push('('); - ret.push_str(&from_clause.to_sql_string(context)); - ret.push(')'); - if let Some(alias) = alias { - ret.push(' '); - ret.push_str(&alias.to_string()); - } - } - } - ret - } -} - -impl ToSqlString for ast::With { - fn to_sql_string(&self, context: &C) -> String { - format!( - "WITH{} {}", - if self.recursive { " RECURSIVE " } else { "" }, - self.ctes - .iter() - .map(|cte| cte.to_sql_string(context)) - .collect::>() - .join(", ") - ) - } -} - -impl ToSqlString for ast::Limit { - fn to_sql_string(&self, context: &C) -> String { - format!( - "LIMIT {}{}", - self.expr.to_sql_string(context), - self.offset - .as_ref() - .map_or("".to_string(), |offset| format!( - " OFFSET {}", - offset.to_sql_string(context) - )) - ) - // TODO: missing , + expr in ast - } -} - -impl ToSqlString for ast::CommonTableExpr { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = Vec::with_capacity(self.columns.as_ref().map_or(2, |cols| cols.len())); - ret.push(self.tbl_name.0.clone()); - if let Some(cols) = &self.columns { - let joined_cols = cols - .iter() - .map(|col| col.to_string()) - .collect::>() - .join(", "); - - ret.push(format!("({joined_cols})")); - } - ret.push(format!( - "AS {}({})", - { - let mut materialized = self.materialized.to_string(); - if !materialized.is_empty() { - materialized.push(' '); - } - materialized - }, - self.select.to_sql_string(context) - )); - ret.join(" ") - } -} - -impl Display for ast::IndexedColumn { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.col_name.0) - } -} - -impl ToSqlString for ast::SortedColumn { - fn to_sql_string(&self, context: &C) -> String { - let mut curr = self.expr.to_sql_string(context); - if let Some(sort_order) = self.order { - curr.push(' '); - curr.push_str(&sort_order.to_string()); - } - if let Some(nulls_order) = self.nulls { - curr.push(' '); - curr.push_str(&nulls_order.to_string()); - } - curr - } -} - -impl Display for ast::SortOrder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl Display for ast::NullsOrder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl Display for ast::Materialized { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = match self { - Self::Any => "", - Self::No => "NOT MATERIALIZED", - Self::Yes => "MATERIALIZED", - }; - write!(f, "{value}") - } -} - -impl ToSqlString for ast::ResultColumn { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = String::new(); - match self { - Self::Expr(expr, alias) => { - ret.push_str(&expr.to_sql_string(context)); - if let Some(alias) = alias { - ret.push(' '); - ret.push_str(&alias.to_string()); - } - } - Self::Star => { - ret.push('*'); - } - Self::TableStar(name) => { - ret.push_str(&format!("{}.*", name.0)); - } - } - ret - } -} - -impl Display for ast::As { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::As(alias) => { - format!("AS {}", alias.0) - } - Self::Elided(alias) => alias.0.clone(), - } - ) - } -} - -impl Display for ast::Indexed { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::NotIndexed => "NOT INDEXED".to_string(), - Self::IndexedBy(name) => format!("INDEXED BY {}", name.0), - } - ) - } -} - -impl Display for ast::JoinOperator { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::Comma => ",".to_string(), - Self::TypedJoin(join) => { - let join_keyword = "JOIN"; - if let Some(join) = join { - format!("{join} {join_keyword}") - } else { - join_keyword.to_string() - } - } - } - ) - } -} - -impl Display for ast::JoinType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value = { - let mut modifiers = Vec::new(); - if self.contains(Self::NATURAL) { - modifiers.push("NATURAL"); - } - if self.contains(Self::LEFT) || self.contains(Self::RIGHT) { - // TODO: I think the parser incorrectly asigns outer to every LEFT and RIGHT query - if self.contains(Self::LEFT | Self::RIGHT) { - modifiers.push("FULL"); - } else if self.contains(Self::LEFT) { - modifiers.push("LEFT"); - } else if self.contains(Self::RIGHT) { - modifiers.push("RIGHT"); - } - // FIXME: ignore outer joins as I think they are parsed incorrectly in the bitflags - // if self.contains(Self::OUTER) { - // modifiers.push("OUTER"); - // } - } - - if self.contains(Self::INNER) { - modifiers.push("INNER"); - } - if self.contains(Self::CROSS) { - modifiers.push("CROSS"); - } - modifiers.join(" ") - }; - write!(f, "{value}") - } -} - -impl ToSqlString for ast::JoinConstraint { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::On(expr) => { - format!("ON {}", expr.to_sql_string(context)) - } - Self::Using(col_names) => { - let joined_names = col_names - .iter() - .map(|col| col.0.clone()) - .collect::>() - .join(","); - format!("USING ({joined_names})") - } - } - } -} - -impl ToSqlString for ast::GroupBy { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = String::from("GROUP BY "); - let curr = self - .exprs - .iter() - .map(|expr| expr.to_sql_string(context)) - .collect::>() - .join(","); - ret.push_str(&curr); - if let Some(having) = &self.having { - ret.push_str(&format!(" HAVING {}", having.to_sql_string(context))); - } - ret - } -} - -impl ToSqlString for ast::WindowDef { - fn to_sql_string(&self, context: &C) -> String { - format!("{} AS {}", self.name.0, self.window.to_sql_string(context)) - } -} - -impl ToSqlString for ast::Window { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = Vec::new(); - if let Some(name) = &self.base { - ret.push(name.0.clone()); - } - if let Some(partition) = &self.partition_by { - let joined_exprs = partition - .iter() - .map(|e| e.to_sql_string(context)) - .collect::>() - .join(","); - ret.push(format!("PARTITION BY {joined_exprs}")); - } - if let Some(order_by) = &self.order_by { - let joined_cols = order_by - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", "); - ret.push(format!("ORDER BY {joined_cols}")); - } - if let Some(frame_claue) = &self.frame_clause { - ret.push(frame_claue.to_sql_string(context)); - } - format!("({})", ret.join(" ")) - } -} - -impl ToSqlString for ast::FrameClause { - fn to_sql_string(&self, context: &C) -> String { - let mut ret = Vec::new(); - ret.push(self.mode.to_string()); - let start_sql = self.start.to_sql_string(context); - if let Some(end) = &self.end { - ret.push(format!( - "BETWEEN {} AND {}", - start_sql, - end.to_sql_string(context) - )); - } else { - ret.push(start_sql); - } - if let Some(exclude) = &self.exclude { - ret.push(exclude.to_string()); - } - - ret.join(" ") - } -} - -impl Display for ast::FrameMode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.to_fmt(f) - } -} - -impl ToSqlString for ast::FrameBound { - fn to_sql_string(&self, context: &C) -> String { - match self { - Self::CurrentRow => "CURRENT ROW".to_string(), - Self::Following(expr) => format!("{} FOLLOWING", expr.to_sql_string(context)), - Self::Preceding(expr) => format!("{} PRECEDING", expr.to_sql_string(context)), - Self::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(), - Self::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(), - } - } -} - -impl Display for ast::FrameExclude { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", { - let clause = match self { - Self::CurrentRow => "CURRENT ROW", - Self::Group => "GROUP", - Self::NoOthers => "NO OTHERS", - Self::Ties => "TIES", - }; - format!("EXCLUDE {clause}") - }) - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; - to_sql_string_test!(test_select_basic, "SELECT 1;"); + to_sql_string_test!(test_select_basic, "SELECT 1"); - to_sql_string_test!(test_select_table, "SELECT * FROM t;"); + to_sql_string_test!(test_select_table, "SELECT * FROM t"); - to_sql_string_test!(test_select_table_2, "SELECT a FROM t;"); + to_sql_string_test!(test_select_table_2, "SELECT a FROM t"); - to_sql_string_test!(test_select_multiple_columns, "SELECT a, b, c FROM t;"); + to_sql_string_test!(test_select_multiple_columns, "SELECT a, b, c FROM t"); - to_sql_string_test!(test_select_with_alias, "SELECT a AS col1 FROM t;"); + to_sql_string_test!(test_select_with_alias, "SELECT a AS col1 FROM t"); - to_sql_string_test!(test_select_with_table_alias, "SELECT t1.a FROM t AS t1;"); + to_sql_string_test!(test_select_with_table_alias, "SELECT t1.a FROM t AS t1"); - to_sql_string_test!(test_select_with_where, "SELECT a FROM t WHERE b = 1;"); + to_sql_string_test!(test_select_with_where, "SELECT a FROM t WHERE b = 1"); to_sql_string_test!( test_select_with_multiple_conditions, - "SELECT a FROM t WHERE b = 1 AND c > 2;" + "SELECT a FROM t WHERE b = 1 AND c > 2" ); - to_sql_string_test!( - test_select_with_order_by, - "SELECT a FROM t ORDER BY a DESC;" - ); + to_sql_string_test!(test_select_with_order_by, "SELECT a FROM t ORDER BY a DESC"); - to_sql_string_test!(test_select_with_limit, "SELECT a FROM t LIMIT 10;"); + to_sql_string_test!(test_select_with_limit, "SELECT a FROM t LIMIT 10"); - to_sql_string_test!( - test_select_with_offset, - "SELECT a FROM t LIMIT 10 OFFSET 5;" - ); + to_sql_string_test!(test_select_with_offset, "SELECT a FROM t LIMIT 10 OFFSET 5"); to_sql_string_test!( test_select_with_join, - "SELECT a FROM t JOIN t2 ON t.b = t2.b;" + "SELECT a FROM t JOIN t2 ON t.b = t2.b" ); to_sql_string_test!( test_select_with_group_by, - "SELECT a, COUNT(*) FROM t GROUP BY a;" + "SELECT a, COUNT (*) FROM t GROUP BY a" ); to_sql_string_test!( test_select_with_having, - "SELECT a, COUNT(*) FROM t GROUP BY a HAVING COUNT(*) > 1;" + "SELECT a, COUNT (*) FROM t GROUP BY a HAVING COUNT (*) > 1" ); - to_sql_string_test!(test_select_with_distinct, "SELECT DISTINCT a FROM t;"); + to_sql_string_test!(test_select_with_distinct, "SELECT DISTINCT a FROM t"); - to_sql_string_test!(test_select_with_function, "SELECT COUNT(a) FROM t;"); + to_sql_string_test!(test_select_with_function, "SELECT COUNT (a) FROM t"); to_sql_string_test!( test_select_with_subquery, - "SELECT a FROM (SELECT b FROM t) AS sub;" + "SELECT a FROM (SELECT b FROM t) AS sub" ); to_sql_string_test!( test_select_nested_subquery, - "SELECT a FROM (SELECT b FROM (SELECT c FROM t WHERE c > 10) AS sub1 WHERE b < 20) AS sub2;" + "SELECT a FROM (SELECT b FROM (SELECT c FROM t WHERE c > 10) AS sub1 WHERE b < 20) AS sub2" ); to_sql_string_test!( test_select_multiple_joins, - "SELECT t1.a, t2.b, t3.c FROM t1 JOIN t2 ON t1.id = t2.id LEFT JOIN t3 ON t2.id = t3.id;" + "SELECT t1.a, t2.b, t3.c FROM t1 JOIN t2 ON t1.id = t2.id LEFT OUTER JOIN t3 ON t2.id = t3.id" ); to_sql_string_test!( test_select_with_cte, - "WITH cte AS (SELECT a FROM t WHERE b = 1) SELECT a FROM cte WHERE a > 10;" + "WITH cte AS (SELECT a FROM t WHERE b = 1) SELECT a FROM cte WHERE a > 10" ); to_sql_string_test!( test_select_with_window_function, - "SELECT a, ROW_NUMBER() OVER (PARTITION BY b ORDER BY c DESC) AS rn FROM t;" + "SELECT a, ROW_NUMBER () OVER (PARTITION BY b ORDER BY c DESC) AS rn FROM t" ); to_sql_string_test!( test_select_with_complex_where, - "SELECT a FROM t WHERE b IN (1, 2, 3) AND c BETWEEN 10 AND 20 OR d IS NULL;" + "SELECT a FROM t WHERE b IN (1, 2, 3) AND c BETWEEN 10 AND 20 OR d IS NULL" ); to_sql_string_test!( test_select_with_case, - "SELECT CASE WHEN a > 0 THEN 'positive' ELSE 'non-positive' END AS result FROM t;" + "SELECT CASE WHEN a > 0 THEN 'positive' ELSE 'non-positive' END AS result FROM t" ); - to_sql_string_test!(test_select_with_aggregate_and_join, "SELECT t1.a, COUNT(t2.b) FROM t1 LEFT JOIN t2 ON t1.id = t2.id GROUP BY t1.a HAVING COUNT(t2.b) > 5;"); + to_sql_string_test!(test_select_with_aggregate_and_join, "SELECT t1.a, COUNT (t2.b) FROM t1 LEFT OUTER JOIN t2 ON t1.id = t2.id GROUP BY t1.a HAVING COUNT (t2.b) > 5"); - to_sql_string_test!(test_select_with_multiple_ctes, "WITH cte1 AS (SELECT a FROM t WHERE b = 1), cte2 AS (SELECT c FROM t2 WHERE d = 2) SELECT cte1.a, cte2.c FROM cte1 JOIN cte2 ON cte1.a = cte2.c;"); + to_sql_string_test!(test_select_with_multiple_ctes, "WITH cte1 AS (SELECT a FROM t WHERE b = 1), cte2 AS (SELECT c FROM t2 WHERE d = 2) SELECT cte1.a, cte2.c FROM cte1 JOIN cte2 ON cte1.a = cte2.c"); to_sql_string_test!( test_select_with_union, - "SELECT a FROM t1 UNION SELECT b FROM t2;" + "SELECT a FROM t1 UNION SELECT b FROM t2" ); to_sql_string_test!( test_select_with_union_all, - "SELECT a FROM t1 UNION ALL SELECT b FROM t2;" + "SELECT a FROM t1 UNION ALL SELECT b FROM t2" ); to_sql_string_test!( test_select_with_exists, - "SELECT a FROM t WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t.a);" + "SELECT a FROM t WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t.a)" ); to_sql_string_test!( test_select_with_correlated_subquery, - "SELECT a, (SELECT COUNT(*) FROM t2 WHERE t2.b = t.a) AS count_b FROM t;" + "SELECT a, (SELECT COUNT (*) FROM t2 WHERE t2.b = t.a) AS count_b FROM t" ); to_sql_string_test!( test_select_with_complex_order_by, - "SELECT a, b FROM t ORDER BY CASE WHEN a IS NULL THEN 1 ELSE 0 END, b ASC, c DESC;" + "SELECT a, b FROM t ORDER BY CASE WHEN a IS NULL THEN 1 ELSE 0 END, b ASC, c DESC" ); to_sql_string_test!( test_select_with_full_outer_join, - "SELECT t1.a, t2.b FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id;", + "SELECT t1.a, t2.b FROM t1 FULL OUTER JOIN t2 ON t1.id = t2.id", ignore = "OUTER JOIN is incorrectly parsed in parser" ); - to_sql_string_test!(test_select_with_aggregate_window, "SELECT a, SUM(b) OVER (PARTITION BY c ORDER BY d ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS running_sum FROM t;"); + to_sql_string_test!(test_select_with_aggregate_window, "SELECT a, SUM (b) OVER (PARTITION BY c ORDER BY d ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS running_sum FROM t"); to_sql_string_test!( test_select_with_exclude, @@ -660,7 +124,7 @@ mod tests { c.name, o.order_id, o.order_amount, - SUM(o.order_amount) OVER (PARTITION BY c.id + SUM (o.order_amount) OVER (PARTITION BY c.id ORDER BY o.order_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) AS running_total_excluding_current @@ -669,6 +133,6 @@ JOIN orders o ON c.id = o.customer_id WHERE EXISTS (SELECT 1 FROM orders o2 WHERE o2.customer_id = c.id - AND o2.order_amount > 1000);" + AND o2.order_amount > 1000)" ); } diff --git a/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs b/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs index ea400f8a7..90e818d7e 100644 --- a/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs +++ b/vendored/sqlite3-parser/src/to_sql_string/stmt/update.rs @@ -1,48 +1,3 @@ -use crate::{ast, to_sql_string::ToSqlString}; - -impl ToSqlString for ast::Update { - fn to_sql_string(&self, context: &C) -> String { - format!( - "{}UPDATE {}{}{} SET {}{}{}{}", - self.with.as_ref().map_or("".to_string(), |with| format!( - "{} ", - with.to_sql_string(context) - )), - self.or_conflict - .map_or("".to_string(), |conflict| format!("OR {conflict} ")), - self.tbl_name.to_sql_string(context), - self.indexed - .as_ref() - .map_or("".to_string(), |indexed| format!(" {indexed}")), - self.sets - .iter() - .map(|set| set.to_sql_string(context)) - .collect::>() - .join(", "), - self.from.as_ref().map_or("".to_string(), |from| format!( - " {}", - from.to_sql_string(context) - )), - self.where_clause - .as_ref() - .map_or("".to_string(), |expr| format!( - " WHERE {}", - expr.to_sql_string(context) - )), - self.returning - .as_ref() - .map_or("".to_string(), |returning| format!( - " RETURNING {}", - returning - .iter() - .map(|col| col.to_sql_string(context)) - .collect::>() - .join(", ") - )) - ) - } -} - #[cfg(test)] mod tests { use crate::to_sql_string_test; @@ -50,90 +5,90 @@ mod tests { // Basic UPDATE with a single column to_sql_string_test!( test_update_single_column, - "UPDATE employees SET salary = 55000;" + "UPDATE employees SET salary = 55000" ); // UPDATE with multiple columns to_sql_string_test!( test_update_multiple_columns, - "UPDATE employees SET salary = 60000, name = 'John Smith';" + "UPDATE employees SET salary = 60000, name = 'John Smith'" ); // UPDATE with a WHERE clause to_sql_string_test!( test_update_with_where, - "UPDATE employees SET salary = 60000 WHERE id = 1;" + "UPDATE employees SET salary = 60000 WHERE id = 1" ); // UPDATE with multiple WHERE conditions to_sql_string_test!( test_update_with_multi_where, - "UPDATE employees SET salary = 65000 WHERE department_id = 3 AND salary < 50000;" + "UPDATE employees SET salary = 65000 WHERE department_id = 3 AND salary < 50000" ); // UPDATE with a subquery in SET to_sql_string_test!( test_update_with_subquery_set, - "UPDATE employees SET department_id = (SELECT id FROM departments WHERE name = 'Sales') WHERE id = 1;" + "UPDATE employees SET department_id = (SELECT id FROM departments WHERE name = 'Sales') WHERE id = 1" ); // UPDATE with a subquery in WHERE to_sql_string_test!( test_update_with_subquery_where, - "UPDATE employees SET salary = 70000 WHERE department_id IN (SELECT id FROM departments WHERE name = 'Marketing');" + "UPDATE employees SET salary = 70000 WHERE department_id IN (SELECT id FROM departments WHERE name = 'Marketing')" ); // UPDATE with EXISTS clause to_sql_string_test!( test_update_with_exists, - "UPDATE employees SET salary = 75000 WHERE EXISTS (SELECT 1 FROM orders WHERE orders.employee_id = employees.id AND orders.status = 'pending');" + "UPDATE employees SET salary = 75000 WHERE EXISTS (SELECT 1 FROM orders WHERE orders.employee_id = employees.id AND orders.status = 'pending')" ); // UPDATE with FROM clause (join-like behavior) to_sql_string_test!( test_update_with_from, - "UPDATE employees SET salary = 80000 FROM departments WHERE employees.department_id = departments.id AND departments.name = 'Engineering';" + "UPDATE employees SET salary = 80000 FROM departments WHERE employees.department_id = departments.id AND departments.name = 'Engineering'" ); // UPDATE with RETURNING clause to_sql_string_test!( test_update_with_returning, - "UPDATE employees SET salary = 60000 WHERE id = 1 RETURNING id, name, salary;" + "UPDATE employees SET salary = 60000 WHERE id = 1 RETURNING id, name, salary" ); // UPDATE with expression in SET to_sql_string_test!( test_update_with_expression, - "UPDATE employees SET salary = salary * 1.1 WHERE department_id = 2;" + "UPDATE employees SET salary = salary * 1.1 WHERE department_id = 2" ); // UPDATE with NULL value to_sql_string_test!( test_update_with_null, - "UPDATE employees SET department_id = NULL WHERE id = 1;" + "UPDATE employees SET department_id = NULL WHERE id = 1" ); // UPDATE with schema-qualified table to_sql_string_test!( test_update_schema_qualified, - "UPDATE main.employees SET salary = 65000 WHERE id = 1;" + "UPDATE main.employees SET salary = 65000 WHERE id = 1" ); // UPDATE with CASE expression to_sql_string_test!( test_update_with_case, - "UPDATE employees SET salary = CASE WHEN salary < 50000 THEN 55000 ELSE salary * 1.05 END WHERE department_id = 3;" + "UPDATE employees SET salary = CASE WHEN salary < 50000 THEN 55000 ELSE salary * 1.05 END WHERE department_id = 3" ); // UPDATE with LIKE clause in WHERE to_sql_string_test!( test_update_with_like, - "UPDATE employees SET name = 'Updated' WHERE name LIKE 'J%';" + "UPDATE employees SET name = 'Updated' WHERE name LIKE 'J%'" ); // UPDATE with ON CONFLICT (upsert-like behavior) to_sql_string_test!( test_update_with_on_conflict, - "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT(id) DO UPDATE SET name = excluded.name, salary = excluded.salary;" + "INSERT INTO employees (id, name, salary) VALUES (1, 'John Doe', 50000) ON CONFLICT (id) DO UPDATE SET name = excluded.name, salary = excluded.salary" ); }