diff --git a/core/translate/display.rs b/core/translate/display.rs index 6cbbbed3c..78f72e1ea 100644 --- a/core/translate/display.rs +++ b/core/translate/display.rs @@ -8,7 +8,7 @@ use turso_sqlite3_parser::{ SortOrder, TableInternalId, }, dialect::TokenType, - to_sql_string::{ToSqlContext, ToSqlString}, + to_sql_string::ToSqlContext, }; use crate::{schema::Table, translate::plan::TableReferences}; @@ -331,58 +331,6 @@ impl ToTokens for Plan { } } -impl ToSqlString for Plan { - fn to_sql_string(&self, context: &C) -> String { - // Make the Plans pass their own context - match self { - Self::Select(select) => select.to_sql_string(&PlanContext(&[&select.table_references])), - Self::CompoundSelect { - left, - right_most, - limit, - offset, - order_by, - } => { - let all_refs = left - .iter() - .flat_map(|(plan, _)| std::iter::once(&plan.table_references)) - .chain(std::iter::once(&right_most.table_references)) - .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)); - } - 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(", ") - )); - } - if let Some(limit) = &limit { - ret.push(format!("LIMIT {limit}")); - } - if let Some(offset) = &offset { - ret.push(format!("OFFSET {offset}")); - } - ret.join(" ") - } - Self::Delete(delete) => delete.to_sql_string(context), - Self::Update(update) => update.to_sql_string(context), - } - } -} - impl ToTokens for JoinedTable { fn to_tokens_with_context( &self, @@ -416,37 +364,7 @@ impl ToTokens for JoinedTable { } } -impl ToSqlString for JoinedTable { - fn to_sql_string( - &self, - _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 - ])) - ) - } - }; - // JOIN is done at a higher level - format!( - "{}{}", - table_or_subquery, - if self.identifier != table_or_subquery { - format!(" AS {}", self.identifier) - } else { - "".to_string() - } - ) - } -} - +// TODO: currently cannot print the original CTE as it is optimized into a subquery impl ToTokens for SelectPlan { fn to_tokens_with_context( &self, @@ -469,7 +387,7 @@ impl ToTokens for SelectPlan { 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))?; + s.append(TokenType::TK_ID, Some(alias))?; } } s.append(TokenType::TK_FROM, None)?; @@ -550,120 +468,6 @@ impl ToTokens for SelectPlan { } } -// TODO: currently cannot print the original CTE as it is optimized into a subquery -impl ToSqlString for SelectPlan { - fn to_sql_string( - &self, - context: &C, - ) -> String { - let mut ret = Vec::new(); - // VALUES SELECT statement - 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(", ") - )); - } else { - // standard SELECT statement - ret.push("SELECT".to_string()); - if self.distinctness.is_distinct() { - ret.push("DISTINCT".to_string()); - } - 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) - ) - } - })); - 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 "), - ); - } - 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(", "), - ); - // 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 "), - ); - } - } - } - 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(", ") - )); - } - if let Some(limit) = &self.limit { - ret.push(format!("LIMIT {limit}")); - } - if let Some(offset) = &self.offset { - ret.push(format!("OFFSET {offset}")); - } - ret.join(" ") - } -} - impl ToTokens for DeletePlan { fn to_tokens_with_context( &self, @@ -726,49 +530,6 @@ impl ToTokens for DeletePlan { } } -impl ToSqlString for DeletePlan { - fn to_sql_string(&self, _context: &C) -> String { - let table = self - .table_references - .joined_tables() - .first() - .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())); - - 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 "), - ); - } - 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(", ") - )); - } - if let Some(limit) = &self.limit { - ret.push(format!("LIMIT {limit}")); - } - if let Some(offset) = &self.offset { - ret.push(format!("OFFSET {offset}")); - } - ret.join(" ") - } -} - impl ToTokens for UpdatePlan { fn to_tokens_with_context( &self, @@ -848,70 +609,3 @@ impl ToTokens for UpdatePlan { Ok(()) } } - -impl ToSqlString for UpdatePlan { - fn to_sql_string(&self, _context: &C) -> String { - let table = self - .table_references - .joined_tables() - .first() - .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 - - ret.push(format!("UPDATE {} SET", table.table.get_name())); - - // 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(", "), - ); - - 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 "), - ); - } - 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(", ") - )); - } - if let Some(limit) = &self.limit { - ret.push(format!("LIMIT {limit}")); - } - if let Some(offset) = &self.offset { - ret.push(format!("OFFSET {offset}")); - } - ret.join(" ") - } -} 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 5e88173fa..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,206 +1,3 @@ -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; 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 508f3c313..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,122 +1,3 @@ -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; 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 cf7f030ae..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; 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 627ebced8..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,23 +1,3 @@ -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; 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 d1f7085b6..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,51 +1,3 @@ -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; @@ -54,10 +6,7 @@ mod tests { 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!( 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 4b459689c..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; 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 ad65900af..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,7 +17,7 @@ mod tests { ($test_name:ident, $input:expr) => { #[test] fn $test_name() { - use crate::parser::ast::fmt::ToTokens; + 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()); @@ -228,7 +34,7 @@ mod tests { #[test] $(#[$attribute])* fn $test_name() { - use crate::parser::ast::fmt::ToTokens; + 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()); 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 82846dd86..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,533 +1,3 @@ -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; @@ -551,17 +21,11 @@ mod tests { "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_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, 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 1a0e9b5a7..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;