Merge 'perf/prepare: box even more stuff to shrink the YYMINORTYPE enum further' from Jussi Saurio

the boxings will continue until morale improves
<img width="621" alt="Screenshot 2025-02-09 at 14 16 21"
src="https://github.com/user-
attachments/assets/887d218d-3db7-4a64-ad81-b7431adb23bb" />

Closes #947
This commit is contained in:
Pekka Enberg 2025-02-09 17:36:03 +02:00
commit 9e65d7ddea
9 changed files with 415 additions and 343 deletions

View file

@ -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();

View file

@ -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"),
}

View file

@ -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,

View file

@ -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<Plan> {
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");

View file

@ -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)");

View file

@ -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())

View file

@ -119,7 +119,8 @@ impl Display for Cmd {
impl ToTokens for Stmt {
fn to_tokens<S: TokenStream>(&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<S: TokenStream>(&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<S: TokenStream>(&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)?;
}

View file

@ -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<QualifiedName>),
/// `ATTACH DATABASE`
@ -95,13 +95,13 @@ pub enum Stmt {
/// `IF NOT EXISTS`
if_not_exists: bool,
/// index name
idx_name: QualifiedName,
idx_name: Box<QualifiedName>,
/// table name
tbl_name: Name,
/// indexed columns or expressions
columns: Vec<SortedColumn>,
/// partial index
where_clause: Option<Expr>,
where_clause: Option<Box<Expr>>,
},
/// `CREATE TABLE`
CreateTable {
@ -112,29 +112,10 @@ pub enum Stmt {
/// table name
tbl_name: QualifiedName,
/// table body
body: CreateTableBody,
body: Box<CreateTableBody>,
},
/// `CREATE TRIGGER`
CreateTrigger {
/// `TEMPORARY`
temporary: bool,
/// `IF NOT EXISTS`
if_not_exists: bool,
/// trigger name
trigger_name: QualifiedName,
/// `BEFORE`/`AFTER`/`INSTEAD OF`
time: Option<TriggerTime>,
/// `DELETE`/`INSERT`/`UPDATE`
event: Box<TriggerEvent>,
/// table name
tbl_name: QualifiedName,
/// `FOR EACH ROW`
for_each_row: bool,
/// `WHEN`
when_clause: Option<Box<Expr>>,
/// statements
commands: Vec<TriggerCmd>,
},
CreateTrigger(Box<CreateTrigger>),
/// `CREATE VIEW`
CreateView {
/// `TEMPORARY`
@ -149,35 +130,11 @@ pub enum Stmt {
select: Box<Select>,
},
/// `CREATE VIRTUAL TABLE`
CreateVirtualTable {
/// `IF NOT EXISTS`
if_not_exists: bool,
/// table name
tbl_name: QualifiedName,
/// module
module_name: Name,
/// args
args: Option<Vec<String>>, // TODO smol str
},
CreateVirtualTable(Box<CreateVirtualTable>),
/// `DELETE`
Delete {
/// CTE
with: Option<With>,
/// `FROM` table name
tbl_name: QualifiedName,
/// `INDEXED`
indexed: Option<Indexed>,
/// `WHERE` clause
where_clause: Option<Box<Expr>>,
/// `RETURNING`
returning: Option<Vec<ResultColumn>>,
/// `ORDER BY`
order_by: Option<Vec<SortedColumn>>,
/// `LIMIT`
limit: Option<Box<Limit>>,
},
Delete(Box<Delete>),
/// `DETACH DATABASE`: db name
Detach(Expr), // TODO distinction between DETACH and DETACH DATABASE
Detach(Box<Expr>), // TODO distinction between DETACH and DETACH DATABASE
/// `DROP INDEX`
DropIndex {
/// `IF EXISTS`
@ -207,22 +164,9 @@ pub enum Stmt {
view_name: QualifiedName,
},
/// `INSERT`
Insert {
/// CTE
with: Option<With>,
/// `OR`
or_conflict: Option<ResolveType>, // TODO distinction between REPLACE and INSERT OR REPLACE
/// table name
tbl_name: QualifiedName,
/// `COLUMNS`
columns: Option<DistinctNames>,
/// `VALUES` or `SELECT`
body: Box<InsertBody>,
/// `RETURNING`
returning: Option<Vec<ResultColumn>>,
},
Insert(Box<Insert>),
/// `PRAGMA`: pragma name, body
Pragma(QualifiedName, Option<PragmaBody>),
Pragma(Box<QualifiedName>, Option<Box<PragmaBody>>),
/// `REINDEX`
Reindex {
/// collation or index or table name
@ -242,30 +186,106 @@ pub enum Stmt {
/// `SELECT`
Select(Box<Select>),
/// `UPDATE`
Update {
/// CTE
with: Option<With>,
/// `OR`
or_conflict: Option<ResolveType>,
/// table name
tbl_name: QualifiedName,
/// `INDEXED`
indexed: Option<Indexed>,
/// `SET` assignments
sets: Vec<Set>,
/// `FROM`
from: Option<FromClause>,
/// `WHERE` clause
where_clause: Option<Box<Expr>>,
/// `RETURNING`
returning: Option<Vec<ResultColumn>>,
/// `ORDER BY`
order_by: Option<Vec<SortedColumn>>,
/// `LIMIT`
limit: Option<Box<Limit>>,
},
Update(Box<Update>),
/// `VACUUM`: database name, into expr
Vacuum(Option<Name>, Option<Expr>),
Vacuum(Option<Name>, Option<Box<Expr>>),
}
/// `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<Vec<String>>, // 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<TriggerTime>,
/// `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<Expr>,
/// statements
pub commands: Vec<TriggerCmd>,
}
/// `INSERT`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Insert {
/// CTE
pub with: Option<With>,
/// `OR`
pub or_conflict: Option<ResolveType>, // TODO distinction between REPLACE and INSERT OR REPLACE
/// table name
pub tbl_name: QualifiedName,
/// `COLUMNS`
pub columns: Option<DistinctNames>,
/// `VALUES` or `SELECT`
pub body: InsertBody,
/// `RETURNING`
pub returning: Option<Vec<ResultColumn>>,
}
/// `UPDATE` clause
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Update {
/// CTE
pub with: Option<With>,
/// `OR`
pub or_conflict: Option<ResolveType>,
/// table name
pub tbl_name: QualifiedName,
/// `INDEXED`
pub indexed: Option<Indexed>,
/// `SET` assignments
pub sets: Vec<Set>,
/// `FROM`
pub from: Option<FromClause>,
/// `WHERE` clause
pub where_clause: Option<Box<Expr>>,
/// `RETURNING`
pub returning: Option<Vec<ResultColumn>>,
/// `ORDER BY`
pub order_by: Option<Vec<SortedColumn>>,
/// `LIMIT`
pub limit: Option<Box<Limit>>,
}
/// `DELETE`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Delete {
/// CTE
pub with: Option<With>,
/// `FROM` table name
pub tbl_name: QualifiedName,
/// `INDEXED`
pub indexed: Option<Indexed>,
/// `WHERE` clause
pub where_clause: Option<Box<Expr>>,
/// `RETURNING`
pub returning: Option<Vec<ResultColumn>>,
/// `ORDER BY`
pub order_by: Option<Vec<SortedColumn>>,
/// `LIMIT`
pub limit: Option<Box<Limit>>,
}
/// SQL expression
@ -771,24 +791,28 @@ pub enum CompoundOperator {
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OneSelect {
/// `SELECT`
Select {
/// `DISTINCT`
distinctness: Option<Distinctness>,
/// columns
columns: Vec<ResultColumn>,
/// `FROM` clause
from: Option<FromClause>,
/// `WHERE` clause
where_clause: Option<Expr>,
/// `GROUP BY`
group_by: Option<GroupBy>,
/// `WINDOW` definition
window_clause: Option<Vec<WindowDef>>,
},
Select(Box<SelectInner>),
/// `VALUES`
Values(Vec<Vec<Expr>>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
/// `SELECT` core
pub struct SelectInner {
/// `DISTINCT`
pub distinctness: Option<Distinctness>,
/// columns
pub columns: Vec<ResultColumn>,
/// `FROM` clause
pub from: Option<FromClause>,
/// `WHERE` clause
pub where_clause: Option<Expr>,
/// `GROUP BY`
pub group_by: Option<GroupBy>,
/// `WINDOW` definition
pub window_clause: Option<Vec<WindowDef>>,
}
/// `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<ResolveType>,
/// table name
tbl_name: Name,
/// `SET` assignments
sets: Vec<Set>,
/// `FROM`
from: Option<FromClause>,
/// `WHERE` clause
where_clause: Option<Expr>,
},
Update(Box<TriggerCmdUpdate>),
/// `INSERT`
Insert {
/// `OR`
or_conflict: Option<ResolveType>,
/// table name
tbl_name: Name,
/// `COLUMNS`
col_names: Option<DistinctNames>,
/// `SELECT` or `VALUES`
select: Box<Select>,
/// `ON CONLICT` clause
upsert: Option<Upsert>,
/// `RETURNING`
returning: Option<Vec<ResultColumn>>,
},
Insert(Box<TriggerCmdInsert>),
/// `DELETE`
Delete {
/// table name
tbl_name: Name,
/// `WHERE` clause
where_clause: Option<Expr>,
},
Delete(Box<TriggerCmdDelete>),
/// `SELECT`
Select(Box<Select>),
}
/// `UPDATE` trigger command
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TriggerCmdUpdate {
/// `OR`
pub or_conflict: Option<ResolveType>,
/// table name
pub tbl_name: Name,
/// `SET` assignments
pub sets: Vec<Set>,
/// `FROM`
pub from: Option<FromClause>,
/// `WHERE` clause
pub where_clause: Option<Expr>,
}
/// `INSERT` trigger command
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TriggerCmdInsert {
/// `OR`
pub or_conflict: Option<ResolveType>,
/// table name
pub tbl_name: Name,
/// `COLUMNS`
pub col_names: Option<DistinctNames>,
/// `SELECT` or `VALUES`
pub select: Box<Select>,
/// `ON CONLICT` clause
pub upsert: Option<Upsert>,
/// `RETURNING`
pub returning: Option<Vec<ResultColumn>>,
}
/// `DELETE` trigger command
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TriggerCmdDelete {
/// table name
pub tbl_name: Name,
/// `WHERE` clause
pub where_clause: Option<Expr>,
}
/// Conflict resolution types
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ResolveType {

View file

@ -109,7 +109,7 @@ cmd ::= ROLLBACK trans_opt(Y) TO savepoint_opt nm(X). {
///////////////////// The CREATE TABLE statement ////////////////////////////
//
cmd ::= createkw temp(T) TABLE ifnotexists(E) fullname(Y) create_table_args(X). {
self.ctx.stmt = Some(Stmt::CreateTable{ temporary: T, if_not_exists: E, tbl_name: Y, body: X });
self.ctx.stmt = Some(Stmt::CreateTable{ temporary: T, if_not_exists: E, tbl_name: Y, body: Box::new(X) });
}
createkw(A) ::= CREATE(A).
@ -525,14 +525,14 @@ multiselect_op(A) ::= INTERSECT. {A = CompoundOperator::Intersect;}
oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
groupby_opt(P). {
A = OneSelect::Select{ distinctness: D, columns: W, from: X, where_clause: Y,
group_by: P, window_clause: None };
A = OneSelect::Select(Box::new(SelectInner{ distinctness: D, columns: W, from: X, where_clause: Y,
group_by: P, window_clause: None }));
}
%ifndef SQLITE_OMIT_WINDOWFUNC
oneselect(A) ::= SELECT distinct(D) selcollist(W) from(X) where_opt(Y)
groupby_opt(P) window_clause(R). {
A = OneSelect::Select{ distinctness: D, columns: W, from: X, where_clause: Y,
group_by: P, window_clause: Some(R) };
A = OneSelect::Select(Box::new(SelectInner{ distinctness: D, columns: W, from: X, where_clause: Y,
group_by: P, window_clause: Some(R) }));
}
%endif
@ -761,14 +761,14 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y).
cmd ::= with(C) DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W)
orderby_opt(O) limit_opt(L). {
let (where_clause, returning) = W;
self.ctx.stmt = Some(Stmt::Delete{ with: C, tbl_name: X, indexed: I, where_clause: where_clause.map(Box::new), returning,
order_by: O, limit: L });
self.ctx.stmt = Some(Stmt::Delete(Box::new(Delete{ with: C, tbl_name: X, indexed: I, where_clause: where_clause.map(Box::new), returning,
order_by: O, limit: L })));
}
%else
cmd ::= with(C) DELETE FROM xfullname(X) indexed_opt(I) where_opt_ret(W). {
let (where_clause, returning) = W;
self.ctx.stmt = Some(Stmt::Delete{ with: C, tbl_name: X, indexed: I, where_clause: where_clause.map(Box::new), returning,
order_by: None, limit: None });
self.ctx.stmt = Some(Stmt::Delete(Box::new(Delete{ with: C, tbl_name: X, indexed: I, where_clause: where_clause.map(Box::new), returning,
order_by: None, limit: None })));
}
%endif
@ -790,15 +790,15 @@ where_opt_ret(A) ::= WHERE expr(X) RETURNING selcollist(Y).
cmd ::= with(C) UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
where_opt_ret(W) orderby_opt(O) limit_opt(L). {
let (where_clause, returning) = W;
self.ctx.stmt = Some(Stmt::Update { with: C, or_conflict: R, tbl_name: X, indexed: I, sets: Y, from: F,
where_clause: where_clause.map(Box::new), returning, order_by: O, limit: L });
self.ctx.stmt = Some(Stmt::Update(Box::new(Update{ with: C, or_conflict: R, tbl_name: X, indexed: I, sets: Y, from: F,
where_clause: where_clause.map(Box::new), returning, order_by: O, limit: L })));
}
%else
cmd ::= with(C) UPDATE orconf(R) xfullname(X) indexed_opt(I) SET setlist(Y) from(F)
where_opt_ret(W). {
let (where_clause, returning) = W;
self.ctx.stmt = Some(Stmt::Update { with: C, or_conflict: R, tbl_name: X, indexed: I, sets: Y, from: F,
where_clause: where_clause.map(Box::new), returning, order_by: None, limit: None });
self.ctx.stmt = Some(Stmt::Update(Box::new(Update{ with: C, or_conflict: R, tbl_name: X, indexed: I, sets: Y, from: F,
where_clause: where_clause.map(Box::new), returning, order_by: None, limit: None })));
}
%endif
@ -827,14 +827,14 @@ cmd ::= with(W) insert_cmd(R) INTO xfullname(X) idlist_opt(F) select(S)
upsert(U). {
let (upsert, returning) = U;
let body = InsertBody::Select(Box::new(S), upsert);
self.ctx.stmt = Some(Stmt::Insert{ with: W, or_conflict: R, tbl_name: X, columns: F,
body: Box::new(body), returning });
self.ctx.stmt = Some(Stmt::Insert(Box::new(Insert{ with: W, or_conflict: R, tbl_name: X, columns: F,
body, returning })));
}
cmd ::= with(W) insert_cmd(R) INTO xfullname(X) idlist_opt(F) DEFAULT VALUES returning(Y).
{
let body = InsertBody::DefaultValues;
self.ctx.stmt = Some(Stmt::Insert{ with: W, or_conflict: R, tbl_name: X, columns: F,
body: Box::new(body), returning: Y });
self.ctx.stmt = Some(Stmt::Insert(Box::new(Insert{ with: W, or_conflict: R, tbl_name: X, columns: F,
body, returning: Y })));
}
%type upsert {(Option<Upsert>, Option<Vec<ResultColumn>>)}
@ -1077,8 +1077,8 @@ paren_exprlist(A) ::= LP exprlist(X) RP. {A = X;}
//
cmd ::= createkw uniqueflag(U) INDEX ifnotexists(NE) fullname(X)
ON nm(Y) LP sortlist(Z) RP where_opt(W). {
self.ctx.stmt = Some(Stmt::CreateIndex { unique: U, if_not_exists: NE, idx_name: X,
tbl_name: Y, columns: Z, where_clause: W });
self.ctx.stmt = Some(Stmt::CreateIndex { unique: U, if_not_exists: NE, idx_name: Box::new(X),
tbl_name: Y, columns: Z, where_clause: W.map(Box::new) });
}
%type uniqueflag {bool}
@ -1130,8 +1130,8 @@ cmd ::= DROP INDEX ifexists(E) fullname(X). {self.ctx.stmt = Some(Stmt::DropIn
//
%if !SQLITE_OMIT_VACUUM && !SQLITE_OMIT_ATTACH
%type vinto {Option<Expr>}
cmd ::= VACUUM vinto(Y). {self.ctx.stmt = Some(Stmt::Vacuum(None, Y));}
cmd ::= VACUUM nm(X) vinto(Y). {self.ctx.stmt = Some(Stmt::Vacuum(Some(X), Y));}
cmd ::= VACUUM vinto(Y). {self.ctx.stmt = Some(Stmt::Vacuum(None, Y.map(Box::new)));}
cmd ::= VACUUM nm(X) vinto(Y). {self.ctx.stmt = Some(Stmt::Vacuum(Some(X), Y.map(Box::new)));}
vinto(A) ::= INTO expr(X). {A = Some(X);}
vinto(A) ::= . {A = None;}
%endif
@ -1139,13 +1139,13 @@ vinto(A) ::= . {A = None;}
///////////////////////////// The PRAGMA command /////////////////////////////
//
%ifndef SQLITE_OMIT_PRAGMA
cmd ::= PRAGMA fullname(X). {self.ctx.stmt = Some(Stmt::Pragma(X, None));}
cmd ::= PRAGMA fullname(X) EQ nmnum(Y). {self.ctx.stmt = Some(Stmt::Pragma(X, Some(PragmaBody::Equals(Y))));}
cmd ::= PRAGMA fullname(X) LP nmnum(Y) RP. {self.ctx.stmt = Some(Stmt::Pragma(X, Some(PragmaBody::Call(Y))));}
cmd ::= PRAGMA fullname(X). {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), None));}
cmd ::= PRAGMA fullname(X) EQ nmnum(Y). {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Equals(Y)))));}
cmd ::= PRAGMA fullname(X) LP nmnum(Y) RP. {self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Call(Y)))));}
cmd ::= PRAGMA fullname(X) EQ minus_num(Y).
{self.ctx.stmt = Some(Stmt::Pragma(X, Some(PragmaBody::Equals(Y))));}
{self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Equals(Y)))));}
cmd ::= PRAGMA fullname(X) LP minus_num(Y) RP.
{self.ctx.stmt = Some(Stmt::Pragma(X, Some(PragmaBody::Call(Y))));}
{self.ctx.stmt = Some(Stmt::Pragma(Box::new(X), Some(Box::new(PragmaBody::Call(Y)))));}
%type nmnum {Expr}
nmnum(A) ::= plus_num(A).
@ -1166,10 +1166,10 @@ minus_num(A) ::= MINUS number(X). {A = Expr::unary(UnaryOperator::Negative,
cmd ::= createkw temp(T) TRIGGER ifnotexists(NOERR) fullname(B) trigger_time(C) trigger_event(D)
ON fullname(E) foreach_clause(X) when_clause(G) BEGIN trigger_cmd_list(S) END. {
self.ctx.stmt = Some(Stmt::CreateTrigger{
temporary: T, if_not_exists: NOERR, trigger_name: B, time: C, event: Box::new(D), tbl_name: E,
for_each_row: X, when_clause: G.map(Box::new), commands: S
});
self.ctx.stmt = Some(Stmt::CreateTrigger(Box::new(CreateTrigger{
temporary: T, if_not_exists: NOERR, trigger_name: B, time: C, event: D, tbl_name: E,
for_each_row: X, when_clause: G, commands: S
})));
}
%type trigger_time {Option<TriggerTime>}
@ -1235,17 +1235,17 @@ tridxby ::= NOT INDEXED. {
// UPDATE
trigger_cmd(A) ::=
UPDATE orconf(R) trnm(X) tridxby SET setlist(Y) from(F) where_opt(Z).
{A = TriggerCmd::Update{ or_conflict: R, tbl_name: X, sets: Y, from: F, where_clause: Z };}
{A = TriggerCmd::Update(Box::new(TriggerCmdUpdate{ or_conflict: R, tbl_name: X, sets: Y, from: F, where_clause: Z }));}
// INSERT
trigger_cmd(A) ::= insert_cmd(R) INTO
trnm(X) idlist_opt(F) select(S) upsert(U). {
let (upsert, returning) = U;
A = TriggerCmd::Insert{ or_conflict: R, tbl_name: X, col_names: F, select: Box::new(S), upsert, returning };/*A-overwrites-R*/
A = TriggerCmd::Insert(Box::new(TriggerCmdInsert{ or_conflict: R, tbl_name: X, col_names: F, select: Box::new(S), upsert, returning }));/*A-overwrites-R*/
}
// DELETE
trigger_cmd(A) ::= DELETE FROM trnm(X) tridxby where_opt(Y).
{A = TriggerCmd::Delete{ tbl_name: X, where_clause: Y };}
{A = TriggerCmd::Delete(Box::new(TriggerCmdDelete{ tbl_name: X, where_clause: Y }));}
// SELECT
trigger_cmd(A) ::= select(X).
@ -1279,7 +1279,7 @@ cmd ::= ATTACH database_kw_opt expr(F) AS expr(D) key_opt(K). {
self.ctx.stmt = Some(Stmt::Attach{ expr: Box::new(F), db_name: Box::new(D), key: K.map(Box::new) });
}
cmd ::= DETACH database_kw_opt expr(D). {
self.ctx.stmt = Some(Stmt::Detach(D));
self.ctx.stmt = Some(Stmt::Detach(Box::new(D)));
}
%type key_opt {Option<Expr>}
@ -1305,19 +1305,19 @@ cmd ::= ANALYZE fullname(X). {self.ctx.stmt = Some(Stmt::Analyze(Some(X)));}
//////////////////////// ALTER TABLE table ... ////////////////////////////////
%ifndef SQLITE_OMIT_ALTERTABLE
cmd ::= ALTER TABLE fullname(X) RENAME TO nm(Z). {
self.ctx.stmt = Some(Stmt::AlterTable(X, AlterTableBody::RenameTo(Z)));
self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::RenameTo(Z)))));
}
cmd ::= ALTER TABLE fullname(X)
ADD kwcolumn_opt columnname(Y) carglist(C). {
let (col_name, col_type) = Y;
let cd = ColumnDefinition{ col_name, col_type, constraints: C };
self.ctx.stmt = Some(Stmt::AlterTable(X, AlterTableBody::AddColumn(cd)));
self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::AddColumn(cd)))));
}
cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). {
self.ctx.stmt = Some(Stmt::AlterTable(X, AlterTableBody::RenameColumn{ old: Y, new: Z }));
self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::RenameColumn{ old: Y, new: Z }))));
}
cmd ::= ALTER TABLE fullname(X) DROP kwcolumn_opt nm(Y). {
self.ctx.stmt = Some(Stmt::AlterTable(X, AlterTableBody::DropColumn(Y)));
self.ctx.stmt = Some(Stmt::AlterTable(Box::new((X, AlterTableBody::DropColumn(Y)))));
}
kwcolumn_opt ::= .
@ -1329,15 +1329,15 @@ kwcolumn_opt ::= COLUMNKW.
cmd ::= create_vtab(X). {self.ctx.stmt = Some(X);}
cmd ::= create_vtab(X) LP vtabarglist RP. {
let mut stmt = X;
if let Stmt::CreateVirtualTable{ ref mut args, .. } = stmt {
*args = self.ctx.module_args();
if let Stmt::CreateVirtualTable(ref mut create_virtual_table) = stmt {
create_virtual_table.args = self.ctx.module_args();
}
self.ctx.stmt = Some(stmt);
}
%type create_vtab {Stmt}
create_vtab(A) ::= createkw VIRTUAL TABLE ifnotexists(E)
fullname(X) USING nm(Z). {
A = Stmt::CreateVirtualTable{ if_not_exists: E, tbl_name: X, module_name: Z, args: None };
A = Stmt::CreateVirtualTable(Box::new(CreateVirtualTable{ if_not_exists: E, tbl_name: X, module_name: Z, args: None }));
}
vtabarglist ::= vtabarg.
vtabarglist ::= vtabarglist COMMA vtabarg.