diff --git a/core/ext/mod.rs b/core/ext/mod.rs index b6fb67fbb..8ac0bdcc2 100644 --- a/core/ext/mod.rs +++ b/core/ext/mod.rs @@ -127,7 +127,7 @@ impl Database { let Stmt::CreateTable { body, .. } = stmt else { return ResultCode::Error; }; - let Ok(columns) = columns_from_create_table_body(body) else { + let Ok(columns) = columns_from_create_table_body(*body) else { return ResultCode::Error; }; let vtab_module = self.vtab_modules.get(name).unwrap().clone(); diff --git a/core/schema.rs b/core/schema.rs index f4a6aee2b..aa38cd191 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -150,7 +150,7 @@ impl BTreeTable { let cmd = parser.next()?; match cmd { Some(Cmd::Stmt(Stmt::CreateTable { tbl_name, body, .. })) => { - create_table(tbl_name, body, root_page) + create_table(tbl_name, *body, root_page) } _ => todo!("Expected CREATE TABLE statement"), } diff --git a/core/translate/mod.rs b/core/translate/mod.rs index 79791866b..7ff780e2b 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -34,6 +34,7 @@ use crate::{bail_parse_error, Connection, LimboError, Result, SymbolTable}; use insert::translate_insert; use select::translate_select; use sqlite3_parser::ast::{self, fmt::ToTokens}; +use sqlite3_parser::ast::{Delete, Insert}; use std::cell::RefCell; use std::fmt::Display; use std::rc::{Rc, Weak}; @@ -51,7 +52,7 @@ pub fn translate( let mut change_cnt_on = false; let program = match stmt { - ast::Stmt::AlterTable(_, _) => bail_parse_error!("ALTER TABLE not supported yet"), + ast::Stmt::AlterTable(_) => bail_parse_error!("ALTER TABLE not supported yet"), ast::Stmt::Analyze(_) => bail_parse_error!("ANALYZE not supported yet"), ast::Stmt::Attach { .. } => bail_parse_error!("ATTACH not supported yet"), ast::Stmt::Begin(_, _) => bail_parse_error!("BEGIN not supported yet"), @@ -67,19 +68,20 @@ pub fn translate( bail_parse_error!("TEMPORARY table not supported yet"); } - translate_create_table(query_mode, tbl_name, body, if_not_exists, schema)? + translate_create_table(query_mode, tbl_name, *body, if_not_exists, schema)? } ast::Stmt::CreateTrigger { .. } => bail_parse_error!("CREATE TRIGGER not supported yet"), ast::Stmt::CreateView { .. } => bail_parse_error!("CREATE VIEW not supported yet"), ast::Stmt::CreateVirtualTable { .. } => { bail_parse_error!("CREATE VIRTUAL TABLE not supported yet") } - ast::Stmt::Delete { - tbl_name, - where_clause, - limit, - .. - } => { + ast::Stmt::Delete(delete) => { + let Delete { + tbl_name, + where_clause, + limit, + .. + } = *delete; change_cnt_on = true; translate_delete(query_mode, schema, &tbl_name, where_clause, limit, syms)? } @@ -92,7 +94,7 @@ pub fn translate( query_mode, &schema, &name, - body, + body.map(|b| *b), database_header.clone(), pager, )?, @@ -103,14 +105,15 @@ pub fn translate( ast::Stmt::Select(select) => translate_select(query_mode, schema, *select, syms)?, ast::Stmt::Update { .. } => bail_parse_error!("UPDATE not supported yet"), ast::Stmt::Vacuum(_, _) => bail_parse_error!("VACUUM not supported yet"), - ast::Stmt::Insert { - with, - or_conflict, - tbl_name, - columns, - body, - returning, - } => { + ast::Stmt::Insert(insert) => { + let Insert { + with, + or_conflict, + tbl_name, + columns, + body, + returning, + } = *insert; change_cnt_on = true; translate_insert( query_mode, diff --git a/core/translate/select.rs b/core/translate/select.rs index b5091af64..22168ab7a 100644 --- a/core/translate/select.rs +++ b/core/translate/select.rs @@ -11,8 +11,8 @@ use crate::util::normalize_ident; use crate::vdbe::builder::{ProgramBuilderOpts, QueryMode}; use crate::SymbolTable; use crate::{schema::Schema, vdbe::builder::ProgramBuilder, Result}; -use sqlite3_parser::ast::ResultColumn; use sqlite3_parser::ast::{self}; +use sqlite3_parser::ast::{ResultColumn, SelectInner}; pub fn translate_select( query_mode: QueryMode, @@ -42,13 +42,14 @@ pub fn prepare_select_plan( syms: &SymbolTable, ) -> Result { match *select.body.select { - ast::OneSelect::Select { - mut columns, - from, - where_clause, - group_by, - .. - } => { + ast::OneSelect::Select(select_inner) => { + let SelectInner { + mut columns, + from, + where_clause, + group_by, + .. + } = *select_inner; let col_count = columns.len(); if col_count == 0 { crate::bail_parse_error!("SELECT without columns is not allowed"); diff --git a/vendored/sqlite3-parser/src/lexer/sql/test.rs b/vendored/sqlite3-parser/src/lexer/sql/test.rs index 2ba066bdd..c0923403b 100644 --- a/vendored/sqlite3-parser/src/lexer/sql/test.rs +++ b/vendored/sqlite3-parser/src/lexer/sql/test.rs @@ -3,7 +3,7 @@ use fallible_iterator::FallibleIterator; use super::{Error, Parser}; use crate::parser::ast::fmt::ToTokens; use crate::parser::{ - ast::{Cmd, Name, ParameterInfo, QualifiedName, Stmt}, + ast::{Cmd, ParameterInfo, Stmt}, ParserError, }; @@ -73,20 +73,12 @@ fn vtab_args() -> Result<(), Error> { body TEXT CHECK(length(body)<10240) );"; let r = parse_cmd(sql); - let Cmd::Stmt(Stmt::CreateVirtualTable { - tbl_name: QualifiedName { - name: Name(tbl_name), - .. - }, - module_name: Name(module_name), - args: Some(args), - .. - }) = r - else { + let Cmd::Stmt(Stmt::CreateVirtualTable(create_virtual_table)) = r else { panic!("unexpected AST") }; - assert_eq!(tbl_name, "mail"); - assert_eq!(module_name, "fts3"); + assert_eq!(create_virtual_table.tbl_name.name, "mail"); + assert_eq!(create_virtual_table.module_name.0, "fts3"); + let args = create_virtual_table.args.as_ref().unwrap(); assert_eq!(args.len(), 2); assert_eq!(args[0], "subject VARCHAR(256) NOT NULL"); assert_eq!(args[1], "body TEXT CHECK(length(body)<10240)"); diff --git a/vendored/sqlite3-parser/src/parser/ast/check.rs b/vendored/sqlite3-parser/src/parser/ast/check.rs index ca1e8cb55..3df2c1c97 100644 --- a/vendored/sqlite3-parser/src/parser/ast/check.rs +++ b/vendored/sqlite3-parser/src/parser/ast/check.rs @@ -56,20 +56,29 @@ impl Stmt { /// Like `sqlite3_column_count` but more limited pub fn column_count(&self) -> ColumnCount { match self { - Self::Delete { - returning: Some(returning), - .. - } => column_count(returning), - Self::Insert { - returning: Some(returning), - .. - } => column_count(returning), + Self::Delete(delete) => { + let Delete { returning, .. } = &**delete; + match returning { + Some(returning) => column_count(returning), + None => ColumnCount::None, + } + } + Self::Insert(insert) => { + let Insert { returning, .. } = &**insert; + match returning { + Some(returning) => column_count(returning), + None => ColumnCount::None, + } + } Self::Pragma(..) => ColumnCount::Dynamic, Self::Select(s) => s.column_count(), - Self::Update { - returning: Some(returning), - .. - } => column_count(returning), + Self::Update(update) => { + let Update { returning, .. } = &**update; + match returning { + Some(returning) => column_count(returning), + None => ColumnCount::None, + } + } _ => ColumnCount::None, } } @@ -94,22 +103,28 @@ impl Stmt { /// check for extra rules pub fn check(&self) -> Result<(), ParserError> { match self { - Self::AlterTable(old_name, AlterTableBody::RenameTo(new_name)) => { - if *new_name == old_name.name { - return Err(custom_err!( - "there is already another table or index with this name: {}", - new_name - )); - } - Ok(()) - } - Self::AlterTable(.., AlterTableBody::AddColumn(cd)) => { - for c in cd { - if let ColumnConstraint::PrimaryKey { .. } = c { - return Err(custom_err!("Cannot add a PRIMARY KEY column")); - } else if let ColumnConstraint::Unique(..) = c { - return Err(custom_err!("Cannot add a UNIQUE column")); + Self::AlterTable(alter_table) => { + let (old_name, body) = &**alter_table; + match body { + AlterTableBody::RenameTo(new_name) => { + if *new_name == old_name.name { + return Err(custom_err!( + "there is already another table or index with this name: {}", + new_name + )); + } } + AlterTableBody::AddColumn(cd) => { + for c in cd { + if let ColumnConstraint::PrimaryKey { .. } = c { + return Err(custom_err!("Cannot add a PRIMARY KEY column")); + } + if let ColumnConstraint::Unique(..) = c { + return Err(custom_err!("Cannot add a UNIQUE column")); + } + } + } + _ => {} } Ok(()) } @@ -153,31 +168,47 @@ impl Stmt { _ => Ok(()), } } - Self::Delete { - order_by: Some(_), - limit: None, - .. - } => Err(custom_err!("ORDER BY without LIMIT on DELETE")), - Self::Insert { - columns: Some(columns), - body, - .. - } => match &**body { - InsertBody::Select(select, ..) => match select.body.select.column_count() { - ColumnCount::Fixed(n) if n != columns.len() => { - Err(custom_err!("{} values for {} columns", n, columns.len())) + Self::Delete(delete) => { + let Delete { + order_by, limit, .. + } = &**delete; + if let Some(_) = order_by { + if limit.is_none() { + return Err(custom_err!("ORDER BY without LIMIT on DELETE")); } - _ => Ok(()), - }, - InsertBody::DefaultValues => { - Err(custom_err!("0 values for {} columns", columns.len())) } - }, - Self::Update { - order_by: Some(_), - limit: None, - .. - } => Err(custom_err!("ORDER BY without LIMIT on UPDATE")), + Ok(()) + } + Self::Insert(insert) => { + let Insert { columns, body, .. } = &**insert; + if columns.is_none() { + return Ok(()); + } + let columns = columns.as_ref().unwrap(); + match &*body { + InsertBody::Select(select, ..) => match select.body.select.column_count() { + ColumnCount::Fixed(n) if n != columns.len() => { + Err(custom_err!("{} values for {} columns", n, columns.len())) + } + _ => Ok(()), + }, + InsertBody::DefaultValues => { + Err(custom_err!("0 values for {} columns", columns.len())) + } + } + } + Self::Update(update) => { + let Update { + order_by, limit, .. + } = &**update; + if let Some(_) = order_by { + if limit.is_none() { + return Err(custom_err!("ORDER BY without LIMIT on UPDATE")); + } + } + + Ok(()) + } _ => Ok(()), } } @@ -295,7 +326,10 @@ impl OneSelect { /// Like `sqlite3_column_count` but more limited pub fn column_count(&self) -> ColumnCount { match self { - Self::Select { columns, .. } => column_count(columns), + Self::Select(select) => { + let SelectInner { columns, .. } = &**select; + column_count(columns) + } Self::Values(values) => { assert!(!values.is_empty()); // TODO Validate ColumnCount::Fixed(values[0].len()) diff --git a/vendored/sqlite3-parser/src/parser/ast/fmt.rs b/vendored/sqlite3-parser/src/parser/ast/fmt.rs index 34a7fa3f0..3c264d607 100644 --- a/vendored/sqlite3-parser/src/parser/ast/fmt.rs +++ b/vendored/sqlite3-parser/src/parser/ast/fmt.rs @@ -119,7 +119,8 @@ impl Display for Cmd { impl ToTokens for Stmt { fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { match self { - Self::AlterTable(tbl_name, body) => { + 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)?; @@ -211,17 +212,18 @@ impl ToTokens for Stmt { tbl_name.to_tokens(s)?; body.to_tokens(s) } - Self::CreateTrigger { - temporary, - if_not_exists, - trigger_name, - time, - event, - tbl_name, - for_each_row, - when_clause, - commands, - } => { + Self::CreateTrigger(trigger) => { + let CreateTrigger { + temporary, + if_not_exists, + trigger_name, + time, + event, + tbl_name, + for_each_row, + when_clause, + commands, + } = &**trigger; s.append(TK_CREATE, None)?; if *temporary { s.append(TK_TEMP, None)?; @@ -281,12 +283,13 @@ impl ToTokens for Stmt { s.append(TK_AS, None)?; select.to_tokens(s) } - Self::CreateVirtualTable { - if_not_exists, - tbl_name, - module_name, - args, - } => { + Self::CreateVirtualTable(create_virtual_table) => { + let CreateVirtualTable { + if_not_exists, + tbl_name, + module_name, + args, + } = &**create_virtual_table; s.append(TK_CREATE, None)?; s.append(TK_VIRTUAL, None)?; s.append(TK_TABLE, None)?; @@ -304,15 +307,16 @@ impl ToTokens for Stmt { } s.append(TK_RP, None) } - Self::Delete { - with, - tbl_name, - indexed, - where_clause, - returning, - order_by, - limit, - } => { + Self::Delete(delete) => { + let Delete { + with, + tbl_name, + indexed, + where_clause, + returning, + order_by, + limit, + } = &**delete; if let Some(with) = with { with.to_tokens(s)?; } @@ -392,14 +396,15 @@ impl ToTokens for Stmt { } view_name.to_tokens(s) } - Self::Insert { - with, - or_conflict, - tbl_name, - columns, - body, - returning, - } => { + Self::Insert(insert) => { + let Insert { + with, + or_conflict, + tbl_name, + columns, + body, + returning, + } = &**insert; if let Some(with) = with { with.to_tokens(s)?; } @@ -465,18 +470,19 @@ impl ToTokens for Stmt { name.to_tokens(s) } Self::Select(select) => select.to_tokens(s), - Self::Update { - with, - or_conflict, - tbl_name, - indexed, - sets, - from, - where_clause, - returning, - order_by, - limit, - } => { + Self::Update(update) => { + let Update { + with, + or_conflict, + tbl_name, + indexed, + sets, + from, + where_clause, + returning, + order_by, + limit, + } = &**update; if let Some(with) = with { with.to_tokens(s)?; } @@ -891,14 +897,15 @@ impl Display for CompoundOperator { impl ToTokens for OneSelect { fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { match self { - Self::Select { - distinctness, - columns, - from, - where_clause, - group_by, - window_clause, - } => { + Self::Select(select) => { + let SelectInner { + distinctness, + columns, + from, + where_clause, + group_by, + window_clause, + } = &**select; s.append(TK_SELECT, None)?; if let Some(ref distinctness) = distinctness { distinctness.to_tokens(s)?; @@ -1649,13 +1656,14 @@ impl ToTokens for TriggerEvent { impl ToTokens for TriggerCmd { fn to_tokens(&self, s: &mut S) -> Result<(), S::Error> { match self { - Self::Update { - or_conflict, - tbl_name, - sets, - from, - where_clause, - } => { + Self::Update(update) => { + let TriggerCmdUpdate { + or_conflict, + tbl_name, + sets, + from, + where_clause, + } = &**update; s.append(TK_UPDATE, None)?; if let Some(or_conflict) = or_conflict { s.append(TK_OR, None)?; @@ -1674,14 +1682,15 @@ impl ToTokens for TriggerCmd { } Ok(()) } - Self::Insert { - or_conflict, - tbl_name, - col_names, - select, - upsert, - returning, - } => { + Self::Insert(insert) => { + let TriggerCmdInsert { + or_conflict, + tbl_name, + col_names, + select, + upsert, + returning, + } = &**insert; if let Some(ResolveType::Replace) = or_conflict { s.append(TK_REPLACE, None)?; } else { @@ -1708,14 +1717,11 @@ impl ToTokens for TriggerCmd { } Ok(()) } - Self::Delete { - tbl_name, - where_clause, - } => { + Self::Delete(delete) => { s.append(TK_DELETE, None)?; s.append(TK_FROM, None)?; - tbl_name.to_tokens(s)?; - if let Some(where_clause) = where_clause { + delete.tbl_name.to_tokens(s)?; + if let Some(where_clause) = &delete.where_clause { s.append(TK_WHERE, None)?; where_clause.to_tokens(s)?; } diff --git a/vendored/sqlite3-parser/src/parser/ast/mod.rs b/vendored/sqlite3-parser/src/parser/ast/mod.rs index 8cf4f805d..3ea9d5992 100644 --- a/vendored/sqlite3-parser/src/parser/ast/mod.rs +++ b/vendored/sqlite3-parser/src/parser/ast/mod.rs @@ -71,7 +71,7 @@ pub(crate) enum ExplainKind { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Stmt { /// `ALTER TABLE`: table name, body - AlterTable(QualifiedName, AlterTableBody), + AlterTable(Box<(QualifiedName, AlterTableBody)>), /// `ANALYSE`: object name Analyze(Option), /// `ATTACH DATABASE` @@ -95,13 +95,13 @@ pub enum Stmt { /// `IF NOT EXISTS` if_not_exists: bool, /// index name - idx_name: QualifiedName, + idx_name: Box, /// table name tbl_name: Name, /// indexed columns or expressions columns: Vec, /// partial index - where_clause: Option, + where_clause: Option>, }, /// `CREATE TABLE` CreateTable { @@ -112,29 +112,10 @@ pub enum Stmt { /// table name tbl_name: QualifiedName, /// table body - body: CreateTableBody, + body: Box, }, /// `CREATE TRIGGER` - CreateTrigger { - /// `TEMPORARY` - temporary: bool, - /// `IF NOT EXISTS` - if_not_exists: bool, - /// trigger name - trigger_name: QualifiedName, - /// `BEFORE`/`AFTER`/`INSTEAD OF` - time: Option, - /// `DELETE`/`INSERT`/`UPDATE` - event: Box, - /// table name - tbl_name: QualifiedName, - /// `FOR EACH ROW` - for_each_row: bool, - /// `WHEN` - when_clause: Option>, - /// statements - commands: Vec, - }, + CreateTrigger(Box), /// `CREATE VIEW` CreateView { /// `TEMPORARY` @@ -149,35 +130,11 @@ pub enum Stmt { select: Box), /// `UPDATE` - Update { - /// CTE - with: Option, - /// `OR` - or_conflict: Option, - /// table name - tbl_name: QualifiedName, - /// `INDEXED` - indexed: Option, - /// `SET` assignments - sets: Vec, - /// `FROM` - from: Option, - /// `WHERE` clause - where_clause: Option>, - /// `RETURNING` - returning: Option>, - /// `ORDER BY` - order_by: Option>, - /// `LIMIT` - limit: Option>, - }, + Update(Box), /// `VACUUM`: database name, into expr - Vacuum(Option, Option), + Vacuum(Option, Option>), +} + +/// `CREATE VIRTUAL TABLE` +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateVirtualTable { + /// `IF NOT EXISTS` + pub if_not_exists: bool, + /// table name + pub tbl_name: QualifiedName, + /// module name + pub module_name: Name, + /// args + pub args: Option>, // TODO smol str +} + +/// `CREATE TRIGGER +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CreateTrigger { + /// `TEMPORARY` + pub temporary: bool, + /// `IF NOT EXISTS` + pub if_not_exists: bool, + /// trigger name + pub trigger_name: QualifiedName, + /// `BEFORE`/`AFTER`/`INSTEAD OF` + pub time: Option, + /// `DELETE`/`INSERT`/`UPDATE` + pub event: TriggerEvent, + /// table name + pub tbl_name: QualifiedName, + /// `FOR EACH ROW` + pub for_each_row: bool, + /// `WHEN` + pub when_clause: Option, + /// statements + pub commands: Vec, +} + +/// `INSERT` +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Insert { + /// CTE + pub with: Option, + /// `OR` + pub or_conflict: Option, // TODO distinction between REPLACE and INSERT OR REPLACE + /// table name + pub tbl_name: QualifiedName, + /// `COLUMNS` + pub columns: Option, + /// `VALUES` or `SELECT` + pub body: InsertBody, + /// `RETURNING` + pub returning: Option>, +} + +/// `UPDATE` clause +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Update { + /// CTE + pub with: Option, + /// `OR` + pub or_conflict: Option, + /// table name + pub tbl_name: QualifiedName, + /// `INDEXED` + pub indexed: Option, + /// `SET` assignments + pub sets: Vec, + /// `FROM` + pub from: Option, + /// `WHERE` clause + pub where_clause: Option>, + /// `RETURNING` + pub returning: Option>, + /// `ORDER BY` + pub order_by: Option>, + /// `LIMIT` + pub limit: Option>, +} + +/// `DELETE` +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Delete { + /// CTE + pub with: Option, + /// `FROM` table name + pub tbl_name: QualifiedName, + /// `INDEXED` + pub indexed: Option, + /// `WHERE` clause + pub where_clause: Option>, + /// `RETURNING` + pub returning: Option>, + /// `ORDER BY` + pub order_by: Option>, + /// `LIMIT` + pub limit: Option>, } /// SQL expression @@ -771,24 +791,28 @@ pub enum CompoundOperator { #[derive(Clone, Debug, PartialEq, Eq)] pub enum OneSelect { /// `SELECT` - Select { - /// `DISTINCT` - distinctness: Option, - /// columns - columns: Vec, - /// `FROM` clause - from: Option, - /// `WHERE` clause - where_clause: Option, - /// `GROUP BY` - group_by: Option, - /// `WINDOW` definition - window_clause: Option>, - }, + Select(Box), /// `VALUES` Values(Vec>), } +#[derive(Clone, Debug, PartialEq, Eq)] +/// `SELECT` core +pub struct SelectInner { + /// `DISTINCT` + pub distinctness: Option, + /// columns + pub columns: Vec, + /// `FROM` clause + pub from: Option, + /// `WHERE` clause + pub where_clause: Option, + /// `GROUP BY` + pub group_by: Option, + /// `WINDOW` definition + pub window_clause: Option>, +} + /// `SELECT` ... `FROM` clause // https://sqlite.org/syntax/join-clause.html #[derive(Clone, Debug, PartialEq, Eq)] @@ -1632,44 +1656,56 @@ pub enum TriggerEvent { #[derive(Clone, Debug, PartialEq, Eq)] pub enum TriggerCmd { /// `UPDATE` - Update { - /// `OR` - or_conflict: Option, - /// table name - tbl_name: Name, - /// `SET` assignments - sets: Vec, - /// `FROM` - from: Option, - /// `WHERE` clause - where_clause: Option, - }, + Update(Box), /// `INSERT` - Insert { - /// `OR` - or_conflict: Option, - /// table name - tbl_name: Name, - /// `COLUMNS` - col_names: Option, - /// `SELECT` or `VALUES` - select: Box), } +/// `UPDATE` trigger command +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TriggerCmdUpdate { + /// `OR` + pub or_conflict: Option, + /// table name + pub tbl_name: Name, + /// `SET` assignments + pub sets: Vec, + /// `FROM` + pub from: Option, + /// `WHERE` clause + pub where_clause: Option, +} + +/// `INSERT` trigger command +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct TriggerCmdInsert { + /// `OR` + pub or_conflict: Option, + /// table name + pub tbl_name: Name, + /// `COLUMNS` + pub col_names: Option, + /// `SELECT` or `VALUES` + pub select: Box