#[cfg(feature = "extra_checks")]
pub mod check;
pub mod fmt;
use std::num::ParseIntError;
use std::ops::Deref;
use std::str::{self, Bytes, FromStr as _};
use bumpalo::{collections::Vec, Bump};
#[cfg(feature = "serde")]
use serde::Serialize;
#[cfg(feature = "extra_checks")]
use check::ColumnCount;
use fmt::TokenStream;
use crate::custom_err;
use crate::dialect::TokenType::{self, *};
use crate::dialect::{from_token, is_identifier, Token};
use crate::parser::{parse::YYCODETYPE, ParserError};
#[derive(Default)]
pub struct ParameterInfo {
pub count: u16,
pub names: indexmap::IndexSet<String>,
}
impl TokenStream for ParameterInfo {
type Error = ParseIntError;
fn append(&mut self, ty: TokenType, value: Option<&str>) -> Result<(), Self::Error> {
if ty == TK_VARIABLE {
if let Some(variable) = value {
if variable == "?" {
self.count = self.count.saturating_add(1);
} else if variable.as_bytes()[0] == b'?' {
let n = u16::from_str(&variable[1..])?;
if n > self.count {
self.count = n;
}
} else if self.names.insert(variable.to_owned()) {
self.count = self.count.saturating_add(1);
}
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Cmd<'bump> {
Explain(Stmt<'bump>),
ExplainQueryPlan(Stmt<'bump>),
Stmt(Stmt<'bump>),
}
pub(crate) enum ExplainKind {
Explain,
QueryPlan,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Stmt<'bump> {
AlterTable(QualifiedName<'bump>, AlterTableBody<'bump>),
Analyze(Option<QualifiedName<'bump>>),
Attach {
expr: Expr<'bump>,
db_name: Expr<'bump>,
key: Option<Expr<'bump>>,
},
Begin(Option<TransactionType>, Option<Name<'bump>>),
Commit(Option<Name<'bump>>), CreateIndex {
unique: bool,
if_not_exists: bool,
idx_name: QualifiedName<'bump>,
tbl_name: Name<'bump>,
columns: &'bump [SortedColumn<'bump>],
where_clause: Option<Expr<'bump>>,
},
CreateTable {
temporary: bool, if_not_exists: bool,
tbl_name: QualifiedName<'bump>,
body: CreateTableBody<'bump>,
},
CreateTrigger {
temporary: bool,
if_not_exists: bool,
trigger_name: QualifiedName<'bump>,
time: Option<TriggerTime>,
event: TriggerEvent<'bump>,
tbl_name: QualifiedName<'bump>,
for_each_row: bool,
when_clause: Option<Expr<'bump>>,
commands: &'bump [TriggerCmd<'bump>],
},
CreateView {
temporary: bool,
if_not_exists: bool,
view_name: QualifiedName<'bump>,
columns: Option<&'bump [IndexedColumn<'bump>]>, select: &'bump Select<'bump>,
},
CreateVirtualTable {
if_not_exists: bool,
tbl_name: QualifiedName<'bump>,
module_name: Name<'bump>,
args: Option<&'bump [&'bump str]>,
},
Delete {
with: Option<With<'bump>>, tbl_name: QualifiedName<'bump>,
indexed: Option<Indexed<'bump>>,
where_clause: Option<Expr<'bump>>,
returning: Option<&'bump [ResultColumn<'bump>]>,
order_by: Option<&'bump [SortedColumn<'bump>]>,
limit: Option<&'bump Limit<'bump>>,
},
Detach(Expr<'bump>), DropIndex {
if_exists: bool,
idx_name: QualifiedName<'bump>,
},
DropTable {
if_exists: bool,
tbl_name: QualifiedName<'bump>,
},
DropTrigger {
if_exists: bool,
trigger_name: QualifiedName<'bump>,
},
DropView {
if_exists: bool,
view_name: QualifiedName<'bump>,
},
Insert {
with: Option<With<'bump>>, or_conflict: Option<ResolveType>, tbl_name: QualifiedName<'bump>,
columns: Option<DistinctNames<'bump>>,
body: InsertBody<'bump>,
returning: Option<&'bump [ResultColumn<'bump>]>,
},
Pragma(QualifiedName<'bump>, Option<PragmaBody<'bump>>),
Reindex {
obj_name: Option<QualifiedName<'bump>>,
},
Release(Name<'bump>), Rollback {
tx_name: Option<Name<'bump>>,
savepoint_name: Option<Name<'bump>>, },
Savepoint(Name<'bump>),
Select(&'bump Select<'bump>),
Update {
with: Option<With<'bump>>, or_conflict: Option<ResolveType>,
tbl_name: QualifiedName<'bump>,
indexed: Option<Indexed<'bump>>,
sets: &'bump [Set<'bump>], from: Option<FromClause<'bump>>,
where_clause: Option<Expr<'bump>>,
returning: Option<&'bump [ResultColumn<'bump>]>,
order_by: Option<&'bump [SortedColumn<'bump>]>,
limit: Option<&'bump Limit<'bump>>,
},
Vacuum(Option<Name<'bump>>, Option<Expr<'bump>>),
}
impl<'bump> Stmt<'bump> {
pub fn create_index(
unique: bool,
if_not_exists: bool,
idx_name: QualifiedName<'bump>,
tbl_name: Name<'bump>,
columns: Vec<'bump, SortedColumn<'bump>>,
where_clause: Option<Expr<'bump>>,
) -> Result<Self, ParserError> {
has_explicit_nulls(&columns)?;
Ok(Self::CreateIndex {
unique,
if_not_exists,
idx_name,
tbl_name,
columns: columns.into_bump_slice(),
where_clause,
})
}
#[allow(clippy::too_many_arguments)]
pub fn update(
with: Option<With<'bump>>,
or_conflict: Option<ResolveType>,
tbl_name: QualifiedName<'bump>,
indexed: Option<Indexed<'bump>>,
sets: Vec<'bump, Set<'bump>>, from: Option<FromClause<'bump>>,
where_clause: Option<Expr<'bump>>,
returning: Option<Vec<'bump, ResultColumn<'bump>>>,
order_by: Option<Vec<'bump, SortedColumn<'bump>>>,
limit: Option<&'bump Limit<'bump>>,
) -> Result<Self, ParserError> {
#[cfg(feature = "extra_checks")]
if let Some(FromClause {
select: Some(ref select),
ref joins,
..
}) = from
{
if matches!(select,
SelectTable::Table(qn, _, _) | SelectTable::TableCall(qn, _, _)
if *qn == tbl_name)
|| joins.as_ref().is_some_and(|js| js.iter().any(|j|
matches!(j.table, SelectTable::Table(ref qn, _, _) | SelectTable::TableCall(ref qn, _, _)
if *qn == tbl_name)))
{
return Err(custom_err!(
"target object/alias may not appear in FROM clause",
));
}
}
#[cfg(feature = "extra_checks")]
if order_by.is_some() && limit.is_none() {
return Err(custom_err!("ORDER BY without LIMIT on UPDATE"));
}
Ok(Self::Update {
with,
or_conflict,
tbl_name,
indexed,
sets: sets.into_bump_slice(),
from,
where_clause,
returning: returning.map(|r| r.into_bump_slice()),
order_by: order_by.map(|ob| ob.into_bump_slice()),
limit,
})
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Expr<'bump> {
Between {
lhs: &'bump Expr<'bump>,
not: bool,
start: &'bump Expr<'bump>,
end: &'bump Expr<'bump>,
},
Binary(&'bump Expr<'bump>, Operator, &'bump Expr<'bump>),
Case {
base: Option<&'bump Expr<'bump>>,
when_then_pairs: &'bump [(Expr<'bump>, Expr<'bump>)],
else_expr: Option<&'bump Expr<'bump>>,
},
Cast {
expr: &'bump Expr<'bump>,
type_name: Option<Type<'bump>>,
},
Collate(&'bump Expr<'bump>, &'bump str),
DoublyQualified(Name<'bump>, Name<'bump>, Name<'bump>),
Exists(&'bump Select<'bump>),
FunctionCall {
name: Id<'bump>,
distinctness: Option<Distinctness>,
args: Option<&'bump [Expr<'bump>]>,
order_by: Option<FunctionCallOrder<'bump>>,
filter_over: Option<FunctionTail<'bump>>,
},
FunctionCallStar {
name: Id<'bump>,
filter_over: Option<FunctionTail<'bump>>,
},
Id(Id<'bump>),
InList {
lhs: &'bump Expr<'bump>,
not: bool,
rhs: Option<&'bump [Expr<'bump>]>,
},
InSelect {
lhs: &'bump Expr<'bump>,
not: bool,
rhs: &'bump Select<'bump>,
},
InTable {
lhs: &'bump Expr<'bump>,
not: bool,
rhs: QualifiedName<'bump>,
args: Option<&'bump [Expr<'bump>]>,
},
IsNull(&'bump Expr<'bump>),
Like {
lhs: &'bump Expr<'bump>,
not: bool,
op: LikeOperator,
rhs: &'bump Expr<'bump>,
escape: Option<&'bump Expr<'bump>>,
},
Literal(Literal<'bump>),
Name(Name<'bump>),
NotNull(&'bump Expr<'bump>),
Parenthesized(Vec<'bump, Expr<'bump>>),
Qualified(Name<'bump>, Name<'bump>),
Raise(ResolveType, Option<&'bump Expr<'bump>>),
Subquery(&'bump Select<'bump>),
Unary(UnaryOperator, &'bump Expr<'bump>),
Variable(&'bump str),
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum FunctionCallOrder<'bump> {
SortList(&'bump [SortedColumn<'bump>]),
#[cfg(feature = "SQLITE_ENABLE_ORDERED_SET_AGGREGATES")]
WithinGroup(&'bump Expr<'bump>),
}
impl<'bump> FunctionCallOrder<'bump> {
#[cfg(feature = "SQLITE_ENABLE_ORDERED_SET_AGGREGATES")]
pub fn within_group(expr: &'bump Expr<'bump>) -> Self {
Self::WithinGroup(expr)
}
}
impl<'bump> Expr<'bump> {
pub fn parenthesized(x: Self, b: &'bump Bump) -> Self {
Self::Parenthesized(bumpalo::vec![in b; x])
}
pub fn id(xt: YYCODETYPE, x: Token, b: &'bump Bump) -> Self {
Self::Id(Id::from_token(xt, x, b))
}
pub fn collate(x: Self, ct: YYCODETYPE, c: Token, b: &'bump Bump) -> Self {
Self::Collate(b.alloc(x), from_token(ct, c, b))
}
pub fn cast(x: Self, type_name: Option<Type<'bump>>, b: &'bump Bump) -> Self {
Self::Cast {
expr: b.alloc(x),
type_name,
}
}
pub fn binary(left: Self, op: YYCODETYPE, right: Self, b: &'bump Bump) -> Self {
Self::Binary(b.alloc(left), Operator::from(op), b.alloc(right))
}
pub fn ptr(left: Self, op: Token, right: Self, b: &'bump Bump) -> Self {
let mut ptr = Operator::ArrowRight;
if op.1 == b"->>" {
ptr = Operator::ArrowRightShift;
}
Self::Binary(b.alloc(left), ptr, b.alloc(right))
}
pub fn like(
lhs: Self,
not: bool,
op: LikeOperator,
rhs: Self,
escape: Option<Self>,
b: &'bump Bump,
) -> Self {
Self::Like {
lhs: b.alloc(lhs),
not,
op,
rhs: b.alloc(rhs),
escape: escape.map(|e| b.alloc(e) as _),
}
}
pub fn not_null(x: Self, op: YYCODETYPE, b: &'bump Bump) -> Self {
if op == TK_ISNULL as YYCODETYPE {
Self::IsNull(b.alloc(x))
} else if op == TK_NOTNULL as YYCODETYPE {
Self::NotNull(b.alloc(x))
} else {
unreachable!()
}
}
pub fn unary(op: UnaryOperator, x: Self, b: &'bump Bump) -> Self {
Self::Unary(op, b.alloc(x))
}
pub fn between(lhs: Self, not: bool, start: Self, end: Self, b: &'bump Bump) -> Self {
Self::Between {
lhs: b.alloc(lhs),
not,
start: b.alloc(start),
end: b.alloc(end),
}
}
pub fn in_list(lhs: Self, not: bool, rhs: Option<Vec<'bump, Self>>, b: &'bump Bump) -> Self {
Self::InList {
lhs: b.alloc(lhs),
not,
rhs: rhs.map(|r| r.into_bump_slice()),
}
}
pub fn in_select(lhs: Self, not: bool, rhs: Select<'bump>, b: &'bump Bump) -> Self {
Self::InSelect {
lhs: b.alloc(lhs),
not,
rhs: b.alloc(rhs),
}
}
pub fn in_table(
lhs: Self,
not: bool,
rhs: QualifiedName<'bump>,
args: Option<Vec<'bump, Self>>,
b: &'bump Bump,
) -> Self {
Self::InTable {
lhs: b.alloc(lhs),
not,
rhs,
args: args.map(|a| a.into_bump_slice()),
}
}
pub fn sub_query(query: Select<'bump>, b: &'bump Bump) -> Self {
Self::Subquery(b.alloc(query))
}
pub fn function_call(
xt: YYCODETYPE,
x: Token,
distinctness: Option<Distinctness>,
args: Option<Vec<'bump, Self>>,
order_by: Option<FunctionCallOrder<'bump>>,
filter_over: Option<FunctionTail<'bump>>,
b: &'bump Bump,
) -> Result<Self, ParserError> {
#[cfg(feature = "extra_checks")]
if let Some(Distinctness::Distinct) = distinctness {
if args.as_ref().map_or(0, Vec::len) != 1 {
return Err(custom_err!(
"DISTINCT aggregates must have exactly one argument"
));
}
}
Ok(Self::FunctionCall {
name: Id::from_token(xt, x, b),
distinctness,
args: args.map(|a| a.into_bump_slice()),
order_by,
filter_over,
})
}
pub fn is_integer(&self) -> Option<i64> {
if let Self::Literal(Literal::Numeric(s)) = self {
i64::from_str(s).ok()
} else if let Self::Unary(UnaryOperator::Positive, e) = self {
e.is_integer()
} else if let Self::Unary(UnaryOperator::Negative, e) = self {
e.is_integer().map(i64::saturating_neg)
} else {
None
}
}
#[cfg(feature = "extra_checks")]
fn check_range(&self, term: &str, mx: u16) -> Result<(), ParserError> {
if let Some(i) = self.is_integer() {
if i < 1 || i > mx as i64 {
return Err(custom_err!(
"{} BY term out of range - should be between 1 and {}",
term,
mx
));
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Literal<'bump> {
Numeric(&'bump str),
String(&'bump str),
Blob(&'bump str),
Keyword(&'bump str),
Null,
CurrentDate,
CurrentTime,
CurrentTimestamp,
}
impl<'bump> Literal<'bump> {
pub fn from_ctime_kw(token: Token) -> Self {
if b"CURRENT_DATE".eq_ignore_ascii_case(token.1) {
Self::CurrentDate
} else if b"CURRENT_TIME".eq_ignore_ascii_case(token.1) {
Self::CurrentTime
} else if b"CURRENT_TIMESTAMP".eq_ignore_ascii_case(token.1) {
Self::CurrentTimestamp
} else {
unreachable!()
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum LikeOperator {
Glob,
Like,
Match,
Regexp,
}
impl LikeOperator {
pub fn from_token(token_type: YYCODETYPE, token: Token) -> Self {
if token_type == TK_MATCH as YYCODETYPE {
return Self::Match;
} else if token_type == TK_LIKE_KW as YYCODETYPE {
let token = token.1;
if b"LIKE".eq_ignore_ascii_case(token) {
return Self::Like;
} else if b"GLOB".eq_ignore_ascii_case(token) {
return Self::Glob;
} else if b"REGEXP".eq_ignore_ascii_case(token) {
return Self::Regexp;
}
}
unreachable!()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Operator {
Add,
And,
ArrowRight,
ArrowRightShift,
BitwiseAnd,
BitwiseOr,
Concat,
Equals,
Divide,
Greater,
GreaterEquals,
Is,
IsNot,
LeftShift,
Less,
LessEquals,
Modulus,
Multiply,
NotEquals,
Or,
RightShift,
Subtract,
}
impl From<YYCODETYPE> for Operator {
fn from(token_type: YYCODETYPE) -> Self {
match token_type {
x if x == TK_AND as YYCODETYPE => Self::And,
x if x == TK_OR as YYCODETYPE => Self::Or,
x if x == TK_LT as YYCODETYPE => Self::Less,
x if x == TK_GT as YYCODETYPE => Self::Greater,
x if x == TK_GE as YYCODETYPE => Self::GreaterEquals,
x if x == TK_LE as YYCODETYPE => Self::LessEquals,
x if x == TK_EQ as YYCODETYPE => Self::Equals,
x if x == TK_NE as YYCODETYPE => Self::NotEquals,
x if x == TK_BITAND as YYCODETYPE => Self::BitwiseAnd,
x if x == TK_BITOR as YYCODETYPE => Self::BitwiseOr,
x if x == TK_LSHIFT as YYCODETYPE => Self::LeftShift,
x if x == TK_RSHIFT as YYCODETYPE => Self::RightShift,
x if x == TK_PLUS as YYCODETYPE => Self::Add,
x if x == TK_MINUS as YYCODETYPE => Self::Subtract,
x if x == TK_STAR as YYCODETYPE => Self::Multiply,
x if x == TK_SLASH as YYCODETYPE => Self::Divide,
x if x == TK_REM as YYCODETYPE => Self::Modulus,
x if x == TK_CONCAT as YYCODETYPE => Self::Concat,
x if x == TK_IS as YYCODETYPE => Self::Is,
x if x == TK_NOT as YYCODETYPE => Self::IsNot,
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum UnaryOperator {
BitwiseNot,
Negative,
Not,
Positive,
}
impl From<YYCODETYPE> for UnaryOperator {
fn from(token_type: YYCODETYPE) -> Self {
match token_type {
x if x == TK_BITNOT as YYCODETYPE => Self::BitwiseNot,
x if x == TK_MINUS as YYCODETYPE => Self::Negative,
x if x == TK_NOT as YYCODETYPE => Self::Not,
x if x == TK_PLUS as YYCODETYPE => Self::Positive,
_ => unreachable!(),
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Select<'bump> {
pub with: Option<With<'bump>>, pub body: SelectBody<'bump>,
pub order_by: Option<&'bump [SortedColumn<'bump>]>, pub limit: Option<&'bump Limit<'bump>>,
}
impl<'bump> Select<'bump> {
pub fn new(
with: Option<With<'bump>>,
body: SelectBody<'bump>,
order_by: Option<Vec<'bump, SortedColumn<'bump>>>,
limit: Option<&'bump Limit<'bump>>,
) -> Result<Self, ParserError> {
let select = Self {
with,
body,
order_by: order_by.map(|ob| ob.into_bump_slice()),
limit,
};
#[cfg(feature = "extra_checks")]
if let Self {
order_by: Some(scs),
..
} = select
{
if let ColumnCount::Fixed(n) = select.column_count() {
for sc in scs {
sc.expr.check_range("ORDER", n)?;
}
}
}
Ok(select)
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct SelectBody<'bump> {
pub select: OneSelect<'bump>,
pub compounds: Option<Vec<'bump, CompoundSelect<'bump>>>,
}
impl<'bump> SelectBody<'bump> {
pub(crate) fn push(
&mut self,
cs: CompoundSelect<'bump>,
b: &'bump Bump,
) -> Result<(), ParserError> {
#[cfg(feature = "extra_checks")]
if let ColumnCount::Fixed(n) = self.select.column_count() {
if let ColumnCount::Fixed(m) = cs.select.column_count() {
if n != m {
return Err(custom_err!(
"SELECTs to the left and right of {} do not have the same number of result columns",
cs.operator
));
}
}
}
if let Some(ref mut v) = self.compounds {
v.push(cs);
} else {
self.compounds = Some(bumpalo::vec![in b; cs]);
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CompoundSelect<'bump> {
pub operator: CompoundOperator,
pub select: OneSelect<'bump>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum CompoundOperator {
Union,
UnionAll,
Except,
Intersect,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum OneSelect<'bump> {
Select {
distinctness: Option<Distinctness>,
columns: &'bump [ResultColumn<'bump>],
from: Option<FromClause<'bump>>,
where_clause: Option<&'bump Expr<'bump>>,
group_by: Option<&'bump [Expr<'bump>]>,
having: Option<&'bump Expr<'bump>>, window_clause: Option<&'bump [WindowDef<'bump>]>,
},
Values(Vec<'bump, Vec<'bump, Expr<'bump>>>),
}
impl<'bump> OneSelect<'bump> {
#[expect(clippy::too_many_arguments)]
pub fn new(
distinctness: Option<Distinctness>,
columns: Vec<'bump, ResultColumn<'bump>>,
from: Option<FromClause<'bump>>,
where_clause: Option<Expr<'bump>>,
group_by: Option<Vec<'bump, Expr<'bump>>>,
having: Option<Expr<'bump>>,
window_clause: Option<Vec<'bump, WindowDef<'bump>>>,
b: &'bump Bump,
) -> Result<Self, ParserError> {
#[cfg(feature = "extra_checks")]
if from.is_none()
&& columns
.iter()
.any(|rc| matches!(rc, ResultColumn::Star | ResultColumn::TableStar(_)))
{
return Err(custom_err!("no tables specified"));
}
let select = Self::Select {
distinctness,
columns: columns.into_bump_slice(),
from,
where_clause: where_clause.map(|wc| b.alloc(wc) as _),
group_by: group_by.map(|gb| gb.into_bump_slice()),
having: having.map(|h| b.alloc(h) as _),
window_clause: window_clause.map(|wc| wc.into_bump_slice()),
};
#[cfg(feature = "extra_checks")]
if let Self::Select {
group_by: Some(gb), ..
} = select
{
if let ColumnCount::Fixed(n) = select.column_count() {
for expr in gb {
expr.check_range("GROUP", n)?;
}
}
}
Ok(select)
}
pub fn push(
values: &mut Vec<'bump, Vec<'bump, Expr<'bump>>>,
v: Vec<'bump, Expr<'bump>>,
) -> Result<(), ParserError> {
#[cfg(feature = "extra_checks")]
if values[0].len() != v.len() {
return Err(custom_err!("all VALUES must have the same number of terms"));
}
values.push(v);
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FromClause<'bump> {
pub select: Option<&'bump SelectTable<'bump>>, pub joins: Option<Vec<'bump, JoinedSelectTable<'bump>>>,
op: Option<JoinOperator>, }
impl<'bump> FromClause<'bump> {
pub(crate) fn empty() -> Self {
Self {
select: None,
joins: None,
op: None,
}
}
pub(crate) fn push(
&mut self,
table: SelectTable<'bump>,
jc: Option<JoinConstraint<'bump>>,
b: &'bump Bump,
) -> Result<(), ParserError> {
let op = self.op.take();
if let Some(op) = op {
let jst = JoinedSelectTable {
operator: op,
table,
constraint: jc,
};
if jst.operator.is_natural() && jst.constraint.is_some() {
return Err(custom_err!(
"a NATURAL join may not have an ON or USING clause"
));
}
if let Some(ref mut joins) = self.joins {
joins.push(jst);
} else {
self.joins = Some(bumpalo::vec![in b; jst]);
}
} else {
if jc.is_some() {
return Err(custom_err!("a JOIN clause is required before ON"));
}
debug_assert!(self.select.is_none());
debug_assert!(self.joins.is_none());
self.select = Some(b.alloc(table));
}
Ok(())
}
pub(crate) fn push_op(&mut self, op: JoinOperator) {
self.op = Some(op);
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Distinctness {
Distinct,
All,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum ResultColumn<'bump> {
Expr(Expr<'bump>, Option<As<'bump>>),
Star,
TableStar(Name<'bump>),
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum As<'bump> {
As(Name<'bump>),
Elided(Name<'bump>), }
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct JoinedSelectTable<'bump> {
pub operator: JoinOperator,
pub table: SelectTable<'bump>,
pub constraint: Option<JoinConstraint<'bump>>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum SelectTable<'bump> {
Table(
QualifiedName<'bump>,
Option<As<'bump>>,
Option<Indexed<'bump>>,
),
TableCall(
QualifiedName<'bump>,
Option<&'bump [Expr<'bump>]>,
Option<As<'bump>>,
),
Select(&'bump Select<'bump>, Option<As<'bump>>),
Sub(FromClause<'bump>, Option<As<'bump>>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum JoinOperator {
Comma,
TypedJoin(Option<JoinType>),
}
impl JoinOperator {
pub(crate) fn from(
token: Token,
n1: Option<Name>,
n2: Option<Name>,
) -> Result<Self, ParserError> {
Ok({
let mut jt = JoinType::try_from(token.1)?;
for n in [&n1, &n2].into_iter().flatten() {
jt |= JoinType::try_from(n.0.as_bytes())?;
}
if (jt & (JoinType::INNER | JoinType::OUTER)) == (JoinType::INNER | JoinType::OUTER)
|| (jt & (JoinType::OUTER | JoinType::LEFT | JoinType::RIGHT)) == JoinType::OUTER
{
return Err(custom_err!(
"unknown join type: {} {} {}",
str::from_utf8(token.1).unwrap_or("invalid utf8"),
n1.as_ref().map_or("", |n| n.0),
n2.as_ref().map_or("", |n| n.0)
));
}
Self::TypedJoin(Some(jt))
})
}
fn is_natural(&self) -> bool {
match self {
Self::TypedJoin(Some(jt)) => jt.contains(JoinType::NATURAL),
_ => false,
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct JoinType: u8 {
const INNER = 0x01;
const CROSS = 0x02;
const NATURAL = 0x04;
const LEFT = 0x08;
const RIGHT = 0x10;
const OUTER = 0x20;
}
}
impl TryFrom<&[u8]> for JoinType {
type Error = ParserError;
fn try_from(s: &[u8]) -> Result<Self, ParserError> {
if b"CROSS".eq_ignore_ascii_case(s) {
Ok(Self::INNER | Self::CROSS)
} else if b"FULL".eq_ignore_ascii_case(s) {
Ok(Self::LEFT | Self::RIGHT | Self::OUTER)
} else if b"INNER".eq_ignore_ascii_case(s) {
Ok(Self::INNER)
} else if b"LEFT".eq_ignore_ascii_case(s) {
Ok(Self::LEFT | Self::OUTER)
} else if b"NATURAL".eq_ignore_ascii_case(s) {
Ok(Self::NATURAL)
} else if b"RIGHT".eq_ignore_ascii_case(s) {
Ok(Self::RIGHT | Self::OUTER)
} else if b"OUTER".eq_ignore_ascii_case(s) {
Ok(Self::OUTER)
} else {
Err(custom_err!(
"unknown join type: {}",
str::from_utf8(s).unwrap_or("invalid utf8")
))
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum JoinConstraint<'bump> {
On(Expr<'bump>),
Using(DistinctNames<'bump>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Id<'bump>(pub &'bump str);
impl<'bump> Id<'bump> {
pub fn from_token(ty: YYCODETYPE, token: Token, b: &'bump Bump) -> Self {
Self(from_token(ty, token, b))
}
}
#[derive(Clone, Debug, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Name<'bump>(pub &'bump str);
pub(crate) fn unquote(s: &str) -> (&str, u8) {
if s.is_empty() {
return (s, 0);
}
let bytes = s.as_bytes();
let mut quote = bytes[0];
if quote != b'"' && quote != b'`' && quote != b'\'' && quote != b'[' {
return (s, 0);
} else if quote == b'[' {
quote = b']';
}
debug_assert!(bytes.len() > 1);
debug_assert_eq!(quote, bytes[bytes.len() - 1]);
let sub = &s[1..bytes.len() - 1];
if quote == b']' || sub.len() < 2 {
(sub, 0)
} else {
(sub, quote)
}
}
impl<'bump> Name<'bump> {
pub fn from_token(ty: YYCODETYPE, token: Token, b: &'bump Bump) -> Self {
Self(from_token(ty, token, b))
}
fn as_bytes(&self) -> QuotedIterator<'_> {
let (sub, quote) = unquote(self.0);
QuotedIterator(sub.bytes(), quote)
}
#[cfg(feature = "extra_checks")]
fn is_reserved(&self) -> bool {
let bytes = self.as_bytes();
let reserved = QuotedIterator("sqlite_".bytes(), 0);
bytes.zip(reserved).fold(0u8, |acc, (b1, b2)| {
acc + if b1.eq_ignore_ascii_case(&b2) { 1 } else { 0 }
}) == 7u8
}
}
struct QuotedIterator<'s>(Bytes<'s>, u8);
impl Iterator for QuotedIterator<'_> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
match self.0.next() {
x @ Some(b) => {
if b == self.1 && self.0.next() != Some(self.1) {
panic!("Malformed string literal: {:?}", self.0);
}
x
}
x => x,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
if self.1 == 0 {
return self.0.size_hint();
}
(0, None)
}
}
fn eq_ignore_case_and_quote(mut it: QuotedIterator<'_>, mut other: QuotedIterator<'_>) -> bool {
loop {
match (it.next(), other.next()) {
(Some(b1), Some(b2)) => {
if !b1.eq_ignore_ascii_case(&b2) {
return false;
}
}
(None, None) => break,
_ => return false,
}
}
true
}
impl<'bump> std::hash::Hash for Name<'bump> {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
self.as_bytes()
.for_each(|b| hasher.write_u8(b.to_ascii_lowercase()));
}
}
impl<'bump> PartialEq for Name<'bump> {
fn eq(&self, other: &Self) -> bool {
eq_ignore_case_and_quote(self.as_bytes(), other.as_bytes())
}
}
impl<'bump> PartialEq<str> for Name<'bump> {
fn eq(&self, other: &str) -> bool {
eq_ignore_case_and_quote(self.as_bytes(), QuotedIterator(other.bytes(), 0u8))
}
}
impl<'bump> PartialEq<&str> for Name<'bump> {
fn eq(&self, other: &&str) -> bool {
eq_ignore_case_and_quote(self.as_bytes(), QuotedIterator(other.bytes(), 0u8))
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct QualifiedName<'bump> {
pub db_name: Option<Name<'bump>>,
pub name: Name<'bump>,
pub alias: Option<Name<'bump>>, }
impl<'bump> QualifiedName<'bump> {
pub fn single(name: Name<'bump>) -> Self {
Self {
db_name: None,
name,
alias: None,
}
}
pub fn fullname(db_name: Name<'bump>, name: Name<'bump>) -> Self {
Self {
db_name: Some(db_name),
name,
alias: None,
}
}
pub fn xfullname(db_name: Name<'bump>, name: Name<'bump>, alias: Name<'bump>) -> Self {
Self {
db_name: Some(db_name),
name,
alias: Some(alias),
}
}
pub fn alias(name: Name<'bump>, alias: Name<'bump>) -> Self {
Self {
db_name: None,
name,
alias: Some(alias),
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DistinctNames<'bump>(Vec<'bump, Name<'bump>>);
impl<'bump> DistinctNames<'bump> {
pub fn new(name: Name<'bump>, bump: &'bump Bump) -> Self {
let mut dn = Self(Vec::new_in(bump));
dn.0.push(name);
dn
}
pub fn single(name: Name<'bump>, bump: &'bump Bump) -> Self {
let mut dn = Self(Vec::with_capacity_in(1, bump));
dn.0.push(name);
dn
}
pub fn insert(&mut self, name: Name<'bump>) -> Result<(), ParserError> {
if self.0.contains(&name) {
return Err(custom_err!("column \"{}\" specified more than once", name));
}
self.0.push(name);
Ok(())
}
}
impl<'bump> Deref for DistinctNames<'bump> {
type Target = Vec<'bump, Name<'bump>>;
fn deref(&self) -> &Vec<'bump, Name<'bump>> {
&self.0
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum AlterTableBody<'bump> {
RenameTo(Name<'bump>),
AddColumn(ColumnDefinition<'bump>), DropColumnNotNull(Name<'bump>), SetColumnNotNull(Name<'bump>, Option<ResolveType>), RenameColumn {
old: Name<'bump>,
new: Name<'bump>,
},
DropColumn(Name<'bump>), AddConstraint(NamedTableConstraint<'bump>), DropConstraint(Name<'bump>),
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct TabFlags: u32 {
const HasHidden = 0x00000002;
const HasPrimaryKey = 0x00000004;
const Autoincrement = 0x00000008;
const HasVirtual = 0x00000020;
const HasStored = 0x00000040;
const HasGenerated = 0x00000060;
const WithoutRowid = 0x00000080;
const HasNotNull = 0x00000800;
const Strict = 0x00010000;
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum CreateTableBody<'bump> {
ColumnsAndConstraints {
columns: Vec<'bump, ColumnDefinition<'bump>>,
constraints: Option<&'bump [NamedTableConstraint<'bump>]>,
flags: TabFlags,
},
AsSelect(&'bump Select<'bump>),
}
impl<'bump> CreateTableBody<'bump> {
pub fn columns_and_constraints(
columns: Vec<'bump, ColumnDefinition<'bump>>,
constraints: Option<Vec<'bump, NamedTableConstraint<'bump>>>,
mut flags: TabFlags,
) -> Result<Self, ParserError> {
for col in &columns {
if col.flags.contains(ColFlags::PRIMKEY) {
flags |= TabFlags::HasPrimaryKey;
}
}
if let Some(ref constraints) = constraints {
for c in constraints {
if let NamedTableConstraint {
constraint: TableConstraint::PrimaryKey { .. },
..
} = c
{
if flags.contains(TabFlags::HasPrimaryKey) {
#[cfg(feature = "extra_checks")]
return Err(custom_err!("table has more than one primary key"));
} else {
flags |= TabFlags::HasPrimaryKey;
}
}
}
}
Ok(Self::ColumnsAndConstraints {
columns,
constraints: constraints.map(|cs| cs.into_bump_slice()),
flags,
})
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ColFlags: u16 {
const PRIMKEY = 0x0001;
const HASTYPE = 0x0004;
const UNIQUE = 0x0008;
const VIRTUAL = 0x0020;
const STORED = 0x0040;
const HASCOLL = 0x0200;
const GENERATED = Self::STORED.bits() | Self::VIRTUAL.bits();
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ColumnDefinition<'bump> {
pub col_name: Name<'bump>,
pub col_type: Option<Type<'bump>>,
pub constraints: &'bump [NamedColumnConstraint<'bump>],
pub flags: ColFlags,
}
impl<'bump> ColumnDefinition<'bump> {
pub fn new(
col_name: Name<'bump>,
mut col_type: Option<Type<'bump>>,
constraints: Vec<'bump, NamedColumnConstraint<'bump>>,
b: &'bump Bump,
) -> Result<Self, ParserError> {
let mut flags = ColFlags::empty();
#[allow(unused_variables)]
let mut default = false;
for constraint in &constraints {
match &constraint.constraint {
#[allow(unused_assignments)]
ColumnConstraint::Default(..) => {
default = true;
}
ColumnConstraint::Collate { .. } => {
flags |= ColFlags::HASCOLL;
}
ColumnConstraint::Generated { typ, .. } => {
flags |= ColFlags::VIRTUAL;
if let Some(id) = typ {
if id.0.eq_ignore_ascii_case("STORED") {
flags |= ColFlags::STORED;
}
}
}
#[cfg(feature = "extra_checks")]
ColumnConstraint::ForeignKey {
clause:
ForeignKeyClause {
tbl_name, columns, ..
},
..
} => {
if columns.as_ref().map_or(0, |cs| cs.len()) > 1 {
return Err(custom_err!(
"foreign key on {} should reference only one column of table {}",
col_name,
tbl_name
));
}
}
#[allow(unused_variables)]
ColumnConstraint::PrimaryKey { auto_increment, .. } => {
#[cfg(feature = "extra_checks")]
if *auto_increment
&& col_type
.as_ref()
.is_none_or(|t| !unquote(t.name).0.eq_ignore_ascii_case("INTEGER"))
{
return Err(custom_err!(
"AUTOINCREMENT is only allowed on an INTEGER PRIMARY KEY"
));
}
flags |= ColFlags::PRIMKEY | ColFlags::UNIQUE;
}
ColumnConstraint::Unique(..) => {
flags |= ColFlags::UNIQUE;
}
_ => {}
}
}
#[cfg(feature = "extra_checks")]
if flags.contains(ColFlags::PRIMKEY) && flags.intersects(ColFlags::GENERATED) {
return Err(custom_err!(
"generated columns cannot be part of the PRIMARY KEY"
));
} else if default && flags.intersects(ColFlags::GENERATED) {
return Err(custom_err!("cannot use DEFAULT on a generated column"));
}
if flags.intersects(ColFlags::GENERATED) {
if let Some(ref mut col_type) = col_type {
let mut split = col_type.name.split_ascii_whitespace();
if split
.next_back()
.is_some_and(|s| s.eq_ignore_ascii_case("ALWAYS"))
&& split
.next_back()
.is_some_and(|s| s.eq_ignore_ascii_case("GENERATED"))
{
let mut new_type = bumpalo::collections::String::new_in(b);
for (i, item) in split.enumerate() {
if i > 0 {
new_type.push(' ');
}
new_type.push_str(item);
}
col_type.name = new_type.into_bump_str();
}
}
if col_type.as_ref().is_some_and(|ct| ct.name.is_empty()) {
col_type = None;
}
}
if col_type.as_ref().is_some_and(|t| !t.name.is_empty()) {
flags |= ColFlags::HASTYPE;
}
Ok(Self {
col_name,
col_type,
constraints: constraints.into_bump_slice(),
flags,
})
}
pub fn add_column(columns: &mut Vec<'bump, Self>, cd: Self) -> Result<(), ParserError> {
if columns.iter().any(|c| c.col_name == cd.col_name) {
return Err(custom_err!("duplicate column name: {}", cd.col_name));
} else if cd.flags.contains(ColFlags::PRIMKEY)
&& columns.iter().any(|c| c.flags.contains(ColFlags::PRIMKEY))
{
#[cfg(feature = "extra_checks")]
return Err(custom_err!("table has more than one primary key")); }
columns.push(cd);
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NamedColumnConstraint<'bump> {
pub name: Option<Name<'bump>>,
pub constraint: ColumnConstraint<'bump>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum ColumnConstraint<'bump> {
PrimaryKey {
order: Option<SortOrder>,
conflict_clause: Option<ResolveType>,
auto_increment: bool,
},
NotNull {
nullable: bool,
conflict_clause: Option<ResolveType>,
},
Unique(Option<ResolveType>),
Check(Expr<'bump>),
Default(Expr<'bump>),
Defer(DeferSubclause), Collate {
collation_name: Name<'bump>, },
ForeignKey {
clause: ForeignKeyClause<'bump>,
defer_clause: Option<DeferSubclause>,
},
Generated {
expr: Expr<'bump>,
typ: Option<Id<'bump>>,
},
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NamedTableConstraint<'bump> {
pub name: Option<Name<'bump>>,
pub constraint: TableConstraint<'bump>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum TableConstraint<'bump> {
PrimaryKey {
columns: &'bump [SortedColumn<'bump>],
auto_increment: bool,
conflict_clause: Option<ResolveType>,
},
Unique {
columns: &'bump [SortedColumn<'bump>],
conflict_clause: Option<ResolveType>,
},
Check(Expr<'bump>, Option<ResolveType>),
ForeignKey {
columns: &'bump [IndexedColumn<'bump>],
clause: ForeignKeyClause<'bump>,
defer_clause: Option<DeferSubclause>,
},
}
impl<'bump> TableConstraint<'bump> {
pub fn primary_key(
columns: Vec<'bump, SortedColumn<'bump>>,
auto_increment: bool,
conflict_clause: Option<ResolveType>,
) -> Result<Self, ParserError> {
has_expression(&columns)?;
has_explicit_nulls(&columns)?;
Ok(Self::PrimaryKey {
columns: columns.into_bump_slice(),
auto_increment,
conflict_clause,
})
}
pub fn unique(
columns: Vec<'bump, SortedColumn<'bump>>,
conflict_clause: Option<ResolveType>,
) -> Result<Self, ParserError> {
has_expression(&columns)?;
has_explicit_nulls(&columns)?;
Ok(Self::Unique {
columns: columns.into_bump_slice(),
conflict_clause,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum SortOrder {
Asc,
Desc,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum NullsOrder {
First,
Last,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ForeignKeyClause<'bump> {
pub tbl_name: Name<'bump>,
pub columns: Option<&'bump [IndexedColumn<'bump>]>,
pub args: &'bump [RefArg<'bump>],
}
impl<'bump> ForeignKeyClause<'bump> {
pub fn new(
tbl_name: Name<'bump>,
columns: Option<Vec<'bump, IndexedColumn<'bump>>>,
args: Vec<'bump, RefArg<'bump>>,
) -> Self {
ForeignKeyClause {
tbl_name,
columns: columns.map(|cs| cs.into_bump_slice()),
args: args.into_bump_slice(),
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum RefArg<'bump> {
OnDelete(RefAct),
OnInsert(RefAct),
OnUpdate(RefAct),
Match(Name<'bump>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum RefAct {
SetNull,
SetDefault,
Cascade,
Restrict,
NoAction,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DeferSubclause {
pub deferrable: bool,
pub init_deferred: Option<InitDeferredPred>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum InitDeferredPred {
InitiallyDeferred,
InitiallyImmediate, }
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct IndexedColumn<'bump> {
pub col_name: Name<'bump>,
pub collation_name: Option<Name<'bump>>, pub order: Option<SortOrder>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Indexed<'bump> {
IndexedBy(Name<'bump>),
NotIndexed,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct SortedColumn<'bump> {
pub expr: Expr<'bump>,
pub order: Option<SortOrder>,
pub nulls: Option<NullsOrder>,
}
fn has_expression(columns: &Vec<SortedColumn>) -> Result<(), ParserError> {
for _column in columns {
if false {
return Err(custom_err!(
"expressions prohibited in PRIMARY KEY and UNIQUE constraints"
));
}
}
Ok(())
}
#[allow(unused_variables)]
fn has_explicit_nulls(columns: &[SortedColumn]) -> Result<(), ParserError> {
#[cfg(feature = "extra_checks")]
for column in columns {
if let Some(ref nulls) = column.nulls {
return Err(custom_err!(
"unsupported use of NULLS {}",
if *nulls == NullsOrder::First {
"FIRST"
} else {
"LAST"
}
));
}
}
Ok(())
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Limit<'bump> {
pub expr: Expr<'bump>,
pub offset: Option<Expr<'bump>>, }
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum InsertBody<'bump> {
Select(&'bump Select<'bump>, Option<&'bump Upsert<'bump>>),
DefaultValues,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Set<'bump> {
pub col_names: DistinctNames<'bump>,
pub expr: Expr<'bump>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum PragmaBody<'bump> {
Equals(PragmaValue<'bump>),
Call(PragmaValue<'bump>),
}
pub type PragmaValue<'bump> = Expr<'bump>;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum TriggerTime {
Before, After,
InsteadOf,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum TriggerEvent<'bump> {
Delete,
Insert,
Update,
UpdateOf(DistinctNames<'bump>),
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum TriggerCmd<'bump> {
Update {
or_conflict: Option<ResolveType>,
tbl_name: QualifiedName<'bump>,
sets: &'bump [Set<'bump>], from: Option<FromClause<'bump>>,
where_clause: Option<Expr<'bump>>,
},
Insert {
or_conflict: Option<ResolveType>,
tbl_name: QualifiedName<'bump>,
col_names: Option<DistinctNames<'bump>>,
select: &'bump Select<'bump>,
upsert: Option<&'bump Upsert<'bump>>,
},
Delete {
tbl_name: QualifiedName<'bump>,
where_clause: Option<Expr<'bump>>,
},
Select(&'bump Select<'bump>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum ResolveType {
Rollback,
Abort, Fail,
Ignore,
Replace,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct With<'bump> {
pub recursive: bool,
pub ctes: &'bump [CommonTableExpr<'bump>],
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Materialized {
Any,
Yes,
No,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CommonTableExpr<'bump> {
pub tbl_name: Name<'bump>,
pub columns: Option<&'bump [IndexedColumn<'bump>]>, pub materialized: Materialized,
pub select: &'bump Select<'bump>,
}
impl<'bump> CommonTableExpr<'bump> {
pub fn new(
tbl_name: Name<'bump>,
columns: Option<Vec<'bump, IndexedColumn<'bump>>>,
materialized: Materialized,
select: Select<'bump>,
b: &'bump Bump,
) -> Result<Self, ParserError> {
#[cfg(feature = "extra_checks")]
if let Some(ref columns) = columns {
if let check::ColumnCount::Fixed(cc) = select.column_count() {
if cc as usize != columns.len() {
return Err(custom_err!(
"table {} has {} values for {} columns",
tbl_name,
cc,
columns.len()
));
}
}
}
Ok(Self {
tbl_name,
columns: columns.map(|cs| cs.into_bump_slice()),
materialized,
select: b.alloc(select),
})
}
pub fn add_cte(ctes: &mut Vec<Self>, cte: Self) -> Result<(), ParserError> {
#[cfg(feature = "extra_checks")]
if ctes.iter().any(|c| c.tbl_name == cte.tbl_name) {
return Err(custom_err!("duplicate WITH table name: {}", cte.tbl_name));
}
ctes.push(cte);
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Type<'bump> {
pub name: &'bump str, pub size: Option<TypeSize<'bump>>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum TypeSize<'bump> {
MaxSize(&'bump Expr<'bump>),
TypeSize(&'bump Expr<'bump>, &'bump Expr<'bump>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum TransactionType {
Deferred, Immediate,
Exclusive,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Upsert<'bump> {
pub index: Option<UpsertIndex<'bump>>,
pub do_clause: UpsertDo<'bump>,
pub next: Option<&'bump Upsert<'bump>>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct UpsertIndex<'bump> {
pub targets: &'bump [SortedColumn<'bump>],
pub where_clause: Option<Expr<'bump>>,
}
impl<'bump> UpsertIndex<'bump> {
pub fn new(
targets: Vec<'bump, SortedColumn<'bump>>,
where_clause: Option<Expr<'bump>>,
) -> Result<Self, ParserError> {
has_explicit_nulls(&targets)?;
Ok(Self {
targets: targets.into_bump_slice(),
where_clause,
})
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum UpsertDo<'bump> {
Set {
sets: &'bump [Set<'bump>],
where_clause: Option<Expr<'bump>>,
},
Nothing,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FunctionTail<'bump> {
pub filter_clause: Option<&'bump Expr<'bump>>,
pub over_clause: Option<&'bump Over<'bump>>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Over<'bump> {
Window(&'bump Window<'bump>),
Name(Name<'bump>),
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct WindowDef<'bump> {
pub name: Name<'bump>,
pub window: Window<'bump>,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Window<'bump> {
pub base: Option<Name<'bump>>,
pub partition_by: Option<&'bump [Expr<'bump>]>,
pub order_by: Option<&'bump [SortedColumn<'bump>]>,
pub frame_clause: Option<FrameClause<'bump>>,
}
impl<'bump> Window<'bump> {
pub fn new(
base: Option<Name<'bump>>,
partition_by: Option<Vec<'bump, Expr<'bump>>>,
order_by: Option<Vec<'bump, SortedColumn<'bump>>>,
frame_clause: Option<FrameClause<'bump>>,
) -> Self {
Self {
base,
partition_by: partition_by.map(|pb| pb.into_bump_slice()),
order_by: order_by.map(|ob| ob.into_bump_slice()),
frame_clause,
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FrameClause<'bump> {
pub mode: FrameMode,
pub start: FrameBound<'bump>,
pub end: Option<FrameBound<'bump>>,
pub exclude: Option<FrameExclude>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum FrameMode {
Groups,
Range,
Rows,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum FrameBound<'bump> {
CurrentRow,
Following(Expr<'bump>),
Preceding(Expr<'bump>),
UnboundedFollowing,
UnboundedPreceding,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum FrameExclude {
NoOthers,
CurrentRow,
Group,
Ties,
}
#[cfg(test)]
mod test {
use super::Name;
#[test]
fn test_dequote() {
let b = bumpalo::Bump::new();
assert_eq!(name("x", &b), "x");
assert_eq!(name("`x`", &b), "x");
assert_eq!(name("`x``y`", &b), "x`y");
assert_eq!(name(r#""x""#, &b), "x");
assert_eq!(name(r#""x""y""#, &b), "x\"y");
assert_eq!(name("[x]", &b), "x");
}
fn name<'bump>(s: &'static str, b: &'bump bumpalo::Bump) -> Name<'bump> {
Name(b.alloc_str(s))
}
}