pub mod check;
pub mod fmt;
use std::num::ParseIntError;
use std::ops::Deref;
use std::str::{Bytes, FromStr};
use fmt::{ToTokens, TokenStream};
use indexmap::{IndexMap, IndexSet};
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: u32,
pub names: 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 = u32::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(Clone, Debug, PartialEq, Eq)]
pub enum Cmd {
Explain(Stmt),
ExplainQueryPlan(Stmt),
Stmt(Stmt),
}
pub(crate) enum ExplainKind {
Explain,
QueryPlan,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Stmt {
AlterTable(QualifiedName, AlterTableBody),
Analyze(Option<QualifiedName>),
Attach {
expr: Expr,
db_name: Expr,
key: Option<Expr>,
},
Begin(Option<TransactionType>, Option<Name>),
Commit(Option<Name>), CreateIndex {
unique: bool,
if_not_exists: bool,
idx_name: QualifiedName,
tbl_name: Name,
columns: Vec<SortedColumn>,
where_clause: Option<Expr>,
},
CreateTable {
temporary: bool, if_not_exists: bool,
tbl_name: QualifiedName,
body: CreateTableBody,
},
CreateTrigger {
temporary: bool,
if_not_exists: bool,
trigger_name: QualifiedName,
time: Option<TriggerTime>,
event: TriggerEvent,
tbl_name: QualifiedName,
for_each_row: bool,
when_clause: Option<Expr>,
commands: Vec<TriggerCmd>,
},
CreateView {
temporary: bool,
if_not_exists: bool,
view_name: QualifiedName,
columns: Option<Vec<IndexedColumn>>,
select: Select,
},
CreateVirtualTable {
if_not_exists: bool,
tbl_name: QualifiedName,
module_name: Name,
args: Option<Vec<String>>, },
Delete {
with: Option<With>,
tbl_name: QualifiedName,
indexed: Option<Indexed>,
where_clause: Option<Expr>,
returning: Option<Vec<ResultColumn>>,
order_by: Option<Vec<SortedColumn>>,
limit: Option<Limit>,
},
Detach(Expr), DropIndex {
if_exists: bool,
idx_name: QualifiedName,
},
DropTable {
if_exists: bool,
tbl_name: QualifiedName,
},
DropTrigger {
if_exists: bool,
trigger_name: QualifiedName,
},
DropView {
if_exists: bool,
view_name: QualifiedName,
},
Insert {
with: Option<With>,
or_conflict: Option<ResolveType>, tbl_name: QualifiedName,
columns: Option<DistinctNames>,
body: InsertBody,
returning: Option<Vec<ResultColumn>>,
},
Pragma(QualifiedName, Option<PragmaBody>),
Reindex {
obj_name: Option<QualifiedName>,
},
Release(Name), Rollback {
tx_name: Option<Name>,
savepoint_name: Option<Name>, },
Savepoint(Name),
Select(Select),
Update {
with: Option<With>,
or_conflict: Option<ResolveType>,
tbl_name: QualifiedName,
indexed: Option<Indexed>,
sets: Vec<Set>,
from: Option<FromClause>,
where_clause: Option<Expr>,
returning: Option<Vec<ResultColumn>>,
order_by: Option<Vec<SortedColumn>>,
limit: Option<Limit>,
},
Vacuum(Option<Name>, Option<Expr>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Expr {
Between {
lhs: Box<Expr>,
not: bool,
start: Box<Expr>,
end: Box<Expr>,
},
Binary(Box<Expr>, Operator, Box<Expr>),
Case {
base: Option<Box<Expr>>,
when_then_pairs: Vec<(Expr, Expr)>,
else_expr: Option<Box<Expr>>,
},
Cast {
expr: Box<Expr>,
type_name: Option<Type>,
},
Collate(Box<Expr>, String),
DoublyQualified(Name, Name, Name),
Exists(Box<Select>),
FunctionCall {
name: Id,
distinctness: Option<Distinctness>,
args: Option<Vec<Expr>>,
order_by: Option<Vec<SortedColumn>>,
filter_over: Option<FunctionTail>,
},
FunctionCallStar {
name: Id,
filter_over: Option<FunctionTail>,
},
Id(Id),
InList {
lhs: Box<Expr>,
not: bool,
rhs: Option<Vec<Expr>>,
},
InSelect {
lhs: Box<Expr>,
not: bool,
rhs: Box<Select>,
},
InTable {
lhs: Box<Expr>,
not: bool,
rhs: QualifiedName,
args: Option<Vec<Expr>>,
},
IsNull(Box<Expr>),
Like {
lhs: Box<Expr>,
not: bool,
op: LikeOperator,
rhs: Box<Expr>,
escape: Option<Box<Expr>>,
},
Literal(Literal),
Name(Name),
NotNull(Box<Expr>),
Parenthesized(Vec<Expr>),
Qualified(Name, Name),
Raise(ResolveType, Option<Box<Expr>>),
Subquery(Box<Select>),
Unary(UnaryOperator, Box<Expr>),
Variable(String),
}
impl Expr {
pub fn parenthesized(x: Expr) -> Expr {
Expr::Parenthesized(vec![x])
}
pub fn id(xt: YYCODETYPE, x: Token) -> Expr {
Expr::Id(Id::from_token(xt, x))
}
pub fn collate(x: Expr, ct: YYCODETYPE, c: Token) -> Expr {
Expr::Collate(Box::new(x), from_token(ct, c))
}
pub fn cast(x: Expr, type_name: Option<Type>) -> Expr {
Expr::Cast {
expr: Box::new(x),
type_name,
}
}
pub fn binary(left: Expr, op: YYCODETYPE, right: Expr) -> Expr {
Expr::Binary(Box::new(left), Operator::from(op), Box::new(right))
}
pub fn ptr(left: Expr, op: Token, right: Expr) -> Expr {
let mut ptr = Operator::ArrowRight;
if let Some(ref op) = op.1 {
if op == "->>" {
ptr = Operator::ArrowRightShift;
}
}
Expr::Binary(Box::new(left), ptr, Box::new(right))
}
pub fn like(lhs: Expr, not: bool, op: LikeOperator, rhs: Expr, escape: Option<Expr>) -> Expr {
Expr::Like {
lhs: Box::new(lhs),
not,
op,
rhs: Box::new(rhs),
escape: escape.map(Box::new),
}
}
pub fn not_null(x: Expr, op: YYCODETYPE) -> Expr {
if op == TK_ISNULL as YYCODETYPE {
Expr::IsNull(Box::new(x))
} else if op == TK_NOTNULL as YYCODETYPE {
Expr::NotNull(Box::new(x))
} else {
unreachable!()
}
}
pub fn unary(op: UnaryOperator, x: Expr) -> Expr {
Expr::Unary(op, Box::new(x))
}
pub fn between(lhs: Expr, not: bool, start: Expr, end: Expr) -> Expr {
Expr::Between {
lhs: Box::new(lhs),
not,
start: Box::new(start),
end: Box::new(end),
}
}
pub fn in_list(lhs: Expr, not: bool, rhs: Option<Vec<Expr>>) -> Expr {
Expr::InList {
lhs: Box::new(lhs),
not,
rhs,
}
}
pub fn in_select(lhs: Expr, not: bool, rhs: Select) -> Expr {
Expr::InSelect {
lhs: Box::new(lhs),
not,
rhs: Box::new(rhs),
}
}
pub fn in_table(lhs: Expr, not: bool, rhs: QualifiedName, args: Option<Vec<Expr>>) -> Expr {
Expr::InTable {
lhs: Box::new(lhs),
not,
rhs,
args,
}
}
pub fn sub_query(query: Select) -> Expr {
Expr::Subquery(Box::new(query))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Literal {
Numeric(String),
String(String),
Blob(String),
Keyword(String),
Null,
CurrentDate,
CurrentTime,
CurrentTimestamp,
}
impl Literal {
pub fn from_ctime_kw(token: Token) -> Literal {
if let Some(ref token) = token.1 {
if "CURRENT_DATE".eq_ignore_ascii_case(token) {
Literal::CurrentDate
} else if "CURRENT_TIME".eq_ignore_ascii_case(token) {
Literal::CurrentTime
} else if "CURRENT_TIMESTAMP".eq_ignore_ascii_case(token) {
Literal::CurrentTimestamp
} else {
unreachable!()
}
} else {
unreachable!()
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum LikeOperator {
Glob,
Like,
Match,
Regexp,
}
impl LikeOperator {
pub fn from_token(token_type: YYCODETYPE, token: Token) -> LikeOperator {
if token_type == TK_MATCH as YYCODETYPE {
return LikeOperator::Match;
} else if token_type == TK_LIKE_KW as YYCODETYPE {
if let Some(ref token) = token.1 {
if "LIKE".eq_ignore_ascii_case(token) {
return LikeOperator::Like;
} else if "GLOB".eq_ignore_ascii_case(token) {
return LikeOperator::Glob;
} else if "REGEXP".eq_ignore_ascii_case(token) {
return LikeOperator::Regexp;
}
}
}
unreachable!()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Operator {
Add,
And,
ArrowRight,
ArrowRightShift,
BitwiseAnd,
BitwiseOr,
Concat,
Equals,
Divide,
Greater,
GreaterEquals,
Is,
IsNot,
LeftShift,
Less,
LessEquals,
Modulus,
Multiply,
NotEquals,
Or,
RightShift,
Substract,
}
impl From<YYCODETYPE> for Operator {
fn from(token_type: YYCODETYPE) -> Operator {
match token_type {
x if x == TK_AND as YYCODETYPE => Operator::And,
x if x == TK_OR as YYCODETYPE => Operator::Or,
x if x == TK_LT as YYCODETYPE => Operator::Less,
x if x == TK_GT as YYCODETYPE => Operator::Greater,
x if x == TK_GE as YYCODETYPE => Operator::GreaterEquals,
x if x == TK_LE as YYCODETYPE => Operator::LessEquals,
x if x == TK_EQ as YYCODETYPE => Operator::Equals,
x if x == TK_NE as YYCODETYPE => Operator::NotEquals,
x if x == TK_BITAND as YYCODETYPE => Operator::BitwiseAnd,
x if x == TK_BITOR as YYCODETYPE => Operator::BitwiseOr,
x if x == TK_LSHIFT as YYCODETYPE => Operator::LeftShift,
x if x == TK_RSHIFT as YYCODETYPE => Operator::RightShift,
x if x == TK_PLUS as YYCODETYPE => Operator::Add,
x if x == TK_MINUS as YYCODETYPE => Operator::Substract,
x if x == TK_STAR as YYCODETYPE => Operator::Multiply,
x if x == TK_SLASH as YYCODETYPE => Operator::Divide,
x if x == TK_REM as YYCODETYPE => Operator::Modulus,
x if x == TK_CONCAT as YYCODETYPE => Operator::Concat,
x if x == TK_IS as YYCODETYPE => Operator::Is,
x if x == TK_NOT as YYCODETYPE => Operator::IsNot,
_ => unreachable!(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum UnaryOperator {
BitwiseNot,
Negative,
Not,
Positive,
}
impl From<YYCODETYPE> for UnaryOperator {
fn from(token_type: YYCODETYPE) -> UnaryOperator {
match token_type {
x if x == TK_BITNOT as YYCODETYPE => UnaryOperator::BitwiseNot,
x if x == TK_MINUS as YYCODETYPE => UnaryOperator::Negative,
x if x == TK_NOT as YYCODETYPE => UnaryOperator::Not,
x if x == TK_PLUS as YYCODETYPE => UnaryOperator::Positive,
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Select {
pub with: Option<With>,
pub body: SelectBody,
pub order_by: Option<Vec<SortedColumn>>, pub limit: Option<Limit>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SelectBody {
pub select: OneSelect,
pub compounds: Option<Vec<CompoundSelect>>,
}
impl SelectBody {
pub(crate) fn push(&mut self, cs: CompoundSelect) -> Result<(), ParserError> {
use crate::ast::check::ColumnCount;
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(vec![cs]);
}
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompoundSelect {
pub operator: CompoundOperator,
pub select: OneSelect,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CompoundOperator {
Union,
UnionAll,
Except,
Intersect,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OneSelect {
Select {
distinctness: Option<Distinctness>,
columns: Vec<ResultColumn>,
from: Option<FromClause>,
where_clause: Option<Expr>,
group_by: Option<GroupBy>,
window_clause: Option<Vec<WindowDef>>,
},
Values(Vec<Vec<Expr>>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FromClause {
pub select: Option<Box<SelectTable>>, pub joins: Option<Vec<JoinedSelectTable>>,
op: Option<JoinOperator>, }
impl FromClause {
pub(crate) fn empty() -> FromClause {
FromClause {
select: None,
joins: None,
op: None,
}
}
pub(crate) fn push(
&mut self,
table: SelectTable,
jc: Option<JoinConstraint>,
) -> 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(vec![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(Box::new(table));
}
Ok(())
}
pub(crate) fn push_op(&mut self, op: JoinOperator) {
self.op = Some(op);
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Distinctness {
Distinct,
All,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ResultColumn {
Expr(Expr, Option<As>),
Star,
TableStar(Name),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum As {
As(Name),
Elided(Name), }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct JoinedSelectTable {
pub operator: JoinOperator,
pub table: SelectTable,
pub constraint: Option<JoinConstraint>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SelectTable {
Table(QualifiedName, Option<As>, Option<Indexed>),
TableCall(QualifiedName, Option<Vec<Expr>>, Option<As>),
Select(Select, Option<As>),
Sub(FromClause, Option<As>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum JoinOperator {
Comma,
TypedJoin(Option<JoinType>),
}
impl JoinOperator {
pub(crate) fn from(
token: Token,
n1: Option<Name>,
n2: Option<Name>,
) -> Result<JoinOperator, ParserError> {
Ok(if let Some(ref t) = token.1 {
let mut jt = JoinType::try_from(t.as_ref())?;
for n in [&n1, &n2].into_iter().flatten() {
jt |= JoinType::try_from(n.0.as_ref())?;
}
if (jt & (JoinType::INNER | JoinType::OUTER)) == (JoinType::INNER | JoinType::OUTER)
|| (jt & (JoinType::OUTER | JoinType::LEFT | JoinType::RIGHT)) == JoinType::OUTER
{
return Err(custom_err!(
"unsupported JOIN type: {} {:?} {:?}",
t,
n1,
n2
));
}
JoinOperator::TypedJoin(Some(jt))
} else {
unreachable!()
})
}
fn is_natural(&self) -> bool {
match self {
JoinOperator::TypedJoin(Some(jt)) => jt.contains(JoinType::NATURAL),
_ => false,
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct JoinType: u8 {
const INNER = 0x01;
const CROSS = 0x02;
const NATURAL = 0x04;
const LEFT = 0x08;
const RIGHT = 0x10;
const OUTER = 0x20;
}
}
impl TryFrom<&str> for JoinType {
type Error = ParserError;
fn try_from(s: &str) -> Result<JoinType, ParserError> {
if "CROSS".eq_ignore_ascii_case(s) {
Ok(JoinType::INNER | JoinType::CROSS)
} else if "FULL".eq_ignore_ascii_case(s) {
Ok(JoinType::LEFT | JoinType::RIGHT | JoinType::OUTER)
} else if "INNER".eq_ignore_ascii_case(s) {
Ok(JoinType::INNER)
} else if "LEFT".eq_ignore_ascii_case(s) {
Ok(JoinType::LEFT | JoinType::OUTER)
} else if "NATURAL".eq_ignore_ascii_case(s) {
Ok(JoinType::NATURAL)
} else if "RIGHT".eq_ignore_ascii_case(s) {
Ok(JoinType::RIGHT | JoinType::OUTER)
} else if "OUTER".eq_ignore_ascii_case(s) {
Ok(JoinType::OUTER)
} else {
Err(custom_err!("unsupported JOIN type: {}", s))
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum JoinConstraint {
On(Expr),
Using(DistinctNames),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GroupBy {
pub exprs: Vec<Expr>,
pub having: Option<Expr>, }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Id(pub String);
impl Id {
pub fn from_token(ty: YYCODETYPE, token: Token) -> Id {
Id(from_token(ty, token))
}
}
#[derive(Clone, Debug, Eq)]
pub struct Name(pub String);
impl Name {
pub fn from_token(ty: YYCODETYPE, token: Token) -> Name {
Name(from_token(ty, token))
}
fn as_bytes(&self) -> QuotedIterator<'_> {
if self.0.is_empty() {
return QuotedIterator(self.0.bytes(), 0);
}
let bytes = self.0.as_bytes();
let mut quote = bytes[0];
if quote != b'"' && quote != b'`' && quote != b'\'' && quote != b'[' {
return QuotedIterator(self.0.bytes(), 0);
} else if quote == b'[' {
quote = b']';
}
debug_assert!(bytes.len() > 1);
debug_assert_eq!(quote, bytes[bytes.len() - 1]);
let sub = &self.0.as_str()[1..bytes.len() - 1];
if quote == b']' {
return QuotedIterator(sub.bytes(), 0); }
QuotedIterator(sub.bytes(), quote)
}
}
struct QuotedIterator<'s>(Bytes<'s>, u8);
impl<'s> Iterator for QuotedIterator<'s> {
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 std::hash::Hash for Name {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
self.as_bytes()
.for_each(|b| hasher.write_u8(b.to_ascii_lowercase()));
}
}
impl PartialEq for Name {
#[inline(always)]
fn eq(&self, other: &Name) -> bool {
eq_ignore_case_and_quote(self.as_bytes(), other.as_bytes())
}
}
impl PartialEq<str> for Name {
#[inline(always)]
fn eq(&self, other: &str) -> bool {
eq_ignore_case_and_quote(self.as_bytes(), QuotedIterator(other.bytes(), 0u8))
}
}
impl PartialEq<&str> for Name {
#[inline(always)]
fn eq(&self, other: &&str) -> bool {
eq_ignore_case_and_quote(self.as_bytes(), QuotedIterator(other.bytes(), 0u8))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QualifiedName {
pub db_name: Option<Name>,
pub name: Name,
pub alias: Option<Name>, }
impl QualifiedName {
pub fn single(name: Name) -> Self {
QualifiedName {
db_name: None,
name,
alias: None,
}
}
pub fn fullname(db_name: Name, name: Name) -> Self {
QualifiedName {
db_name: Some(db_name),
name,
alias: None,
}
}
pub fn xfullname(db_name: Name, name: Name, alias: Name) -> Self {
QualifiedName {
db_name: Some(db_name),
name,
alias: Some(alias),
}
}
pub fn alias(name: Name, alias: Name) -> Self {
QualifiedName {
db_name: None,
name,
alias: Some(alias),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DistinctNames(IndexSet<Name>);
impl DistinctNames {
pub fn new(name: Name) -> DistinctNames {
let mut dn = DistinctNames(IndexSet::new());
dn.0.insert(name);
dn
}
pub fn single(name: Name) -> DistinctNames {
let mut dn = DistinctNames(IndexSet::with_capacity(1));
dn.0.insert(name);
dn
}
pub fn insert(&mut self, name: Name) -> Result<(), ParserError> {
if self.0.contains(&name) {
return Err(custom_err!("column \"{}\" specified more than once", name));
}
self.0.insert(name);
Ok(())
}
}
impl Deref for DistinctNames {
type Target = IndexSet<Name>;
fn deref(&self) -> &IndexSet<Name> {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AlterTableBody {
RenameTo(Name),
AddColumn(ColumnDefinition), RenameColumn {
old: Name,
new: Name,
},
DropColumn(Name), }
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CreateTableBody {
ColumnsAndConstraints {
columns: IndexMap<Name, ColumnDefinition>,
constraints: Option<Vec<NamedTableConstraint>>,
options: TableOptions,
},
AsSelect(Select),
}
impl CreateTableBody {
pub fn columns_and_constraints(
columns: IndexMap<Name, ColumnDefinition>,
constraints: Option<Vec<NamedTableConstraint>>,
options: TableOptions,
) -> Result<CreateTableBody, ParserError> {
Ok(CreateTableBody::ColumnsAndConstraints {
columns,
constraints,
options,
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ColumnDefinition {
pub col_name: Name,
pub col_type: Option<Type>,
pub constraints: Vec<NamedColumnConstraint>,
}
impl ColumnDefinition {
pub fn add_column(
columns: &mut IndexMap<Name, ColumnDefinition>,
mut cd: ColumnDefinition,
) -> Result<(), ParserError> {
let col_name = &cd.col_name;
if columns.contains_key(col_name) {
return Err(custom_err!("duplicate column name: {}", col_name));
}
if let Some(ref mut col_type) = cd.col_type {
let mut split = col_type.name.split_ascii_whitespace();
let truncate = if split
.next_back()
.map_or(false, |s| s.eq_ignore_ascii_case("ALWAYS"))
&& split
.next_back()
.map_or(false, |s| s.eq_ignore_ascii_case("GENERATED"))
{
let mut generated = false;
for constraint in &cd.constraints {
if let ColumnConstraint::Generated { .. } = constraint.constraint {
generated = true;
break;
}
}
generated
} else {
false
};
if truncate {
let new_type: Vec<&str> = split.collect();
col_type.name = new_type.join(" ");
}
}
for constraint in &cd.constraints {
if let ColumnConstraint::ForeignKey {
clause:
ForeignKeyClause {
tbl_name, columns, ..
},
..
} = &constraint.constraint
{
if columns.as_ref().map_or(0, |v| v.len()) > 1 {
return Err(custom_err!(
"foreign key on {} should reference only one column of table {}",
col_name,
tbl_name
));
}
}
}
columns.insert(col_name.clone(), cd);
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NamedColumnConstraint {
pub name: Option<Name>,
pub constraint: ColumnConstraint,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ColumnConstraint {
PrimaryKey {
order: Option<SortOrder>,
conflict_clause: Option<ResolveType>,
auto_increment: bool,
},
NotNull {
nullable: bool,
conflict_clause: Option<ResolveType>,
},
Unique(Option<ResolveType>),
Check(Expr),
Default(Expr),
Defer(DeferSubclause), Collate {
collation_name: Name, },
ForeignKey {
clause: ForeignKeyClause,
deref_clause: Option<DeferSubclause>,
},
Generated {
expr: Expr,
typ: Option<Id>,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NamedTableConstraint {
pub name: Option<Name>,
pub constraint: TableConstraint,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TableConstraint {
PrimaryKey {
columns: Vec<SortedColumn>,
auto_increment: bool,
conflict_clause: Option<ResolveType>,
},
Unique {
columns: Vec<SortedColumn>,
conflict_clause: Option<ResolveType>,
},
Check(Expr),
ForeignKey {
columns: Vec<IndexedColumn>,
clause: ForeignKeyClause,
deref_clause: Option<DeferSubclause>,
},
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TableOptions: u8 {
const NONE = 0;
const WITHOUT_ROWID = 1;
const STRICT = 2;
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SortOrder {
Asc,
Desc,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NullsOrder {
First,
Last,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ForeignKeyClause {
pub tbl_name: Name,
pub columns: Option<Vec<IndexedColumn>>,
pub args: Vec<RefArg>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RefArg {
OnDelete(RefAct),
OnInsert(RefAct),
OnUpdate(RefAct),
Match(Name),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RefAct {
SetNull,
SetDefault,
Cascade,
Restrict,
NoAction,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DeferSubclause {
pub deferrable: bool,
pub init_deferred: Option<InitDeferredPred>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InitDeferredPred {
InitiallyDeferred,
InitiallyImmediate, }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IndexedColumn {
pub col_name: Name,
pub collation_name: Option<Name>, pub order: Option<SortOrder>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Indexed {
IndexedBy(Name),
NotIndexed,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SortedColumn {
pub expr: Expr,
pub order: Option<SortOrder>,
pub nulls: Option<NullsOrder>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Limit {
pub expr: Expr,
pub offset: Option<Expr>, }
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum InsertBody {
Select(Select, Option<Upsert>),
DefaultValues,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Set {
pub col_names: DistinctNames,
pub expr: Expr,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum PragmaBody {
Equals(PragmaValue),
Call(PragmaValue),
}
pub type PragmaValue = Expr;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TriggerTime {
Before, After,
InsteadOf,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TriggerEvent {
Delete,
Insert,
Update,
UpdateOf(DistinctNames),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TriggerCmd {
Update {
or_conflict: Option<ResolveType>,
tbl_name: Name,
sets: Vec<Set>,
from: Option<FromClause>,
where_clause: Option<Expr>,
},
Insert {
or_conflict: Option<ResolveType>,
tbl_name: Name,
col_names: Option<DistinctNames>,
select: Select,
upsert: Option<Upsert>,
returning: Option<Vec<ResultColumn>>,
},
Delete {
tbl_name: Name,
where_clause: Option<Expr>,
},
Select(Select),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ResolveType {
Rollback,
Abort, Fail,
Ignore,
Replace,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct With {
pub recursive: bool,
pub ctes: Vec<CommonTableExpr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Materialized {
Any,
Yes,
No,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommonTableExpr {
pub tbl_name: Name,
pub columns: Option<Vec<IndexedColumn>>, pub materialized: Materialized,
pub select: Select,
}
impl CommonTableExpr {
pub fn add_cte(
ctes: &mut Vec<CommonTableExpr>,
cte: CommonTableExpr,
) -> Result<(), ParserError> {
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(Clone, Debug, PartialEq, Eq)]
pub struct Type {
pub name: String, pub size: Option<TypeSize>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeSize {
MaxSize(Box<Expr>),
TypeSize(Box<Expr>, Box<Expr>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TransactionType {
Deferred, Immediate,
Exclusive,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Upsert {
pub index: Option<UpsertIndex>,
pub do_clause: UpsertDo,
pub next: Option<Box<Upsert>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpsertIndex {
pub targets: Vec<SortedColumn>,
pub where_clause: Option<Expr>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UpsertDo {
Set {
sets: Vec<Set>,
where_clause: Option<Expr>,
},
Nothing,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FunctionTail {
pub filter_clause: Option<Box<Expr>>,
pub over_clause: Option<Box<Over>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Over {
Window(Window),
Name(Name),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WindowDef {
pub name: Name,
pub window: Window,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Window {
pub base: Option<Name>,
pub partition_by: Option<Vec<Expr>>,
pub order_by: Option<Vec<SortedColumn>>,
pub frame_clause: Option<FrameClause>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FrameClause {
pub mode: FrameMode,
pub start: FrameBound,
pub end: Option<FrameBound>,
pub exclude: Option<FrameExclude>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FrameMode {
Groups,
Range,
Rows,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FrameBound {
CurrentRow,
Following(Expr),
Preceding(Expr),
UnboundedFollowing,
UnboundedPreceding,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FrameExclude {
NoOthers,
CurrentRow,
Group,
Ties,
}
#[cfg(test)]
mod test {
use super::Name;
#[test]
fn test_dequote() {
assert_eq!(name("x"), "x");
assert_eq!(name("`x`"), "x");
assert_eq!(name("`x``y`"), "x`y");
assert_eq!(name(r#""x""#), "x");
assert_eq!(name(r#""x""y""#), "x\"y");
assert_eq!(name("[x]"), "x");
}
fn name(s: &'static str) -> Name {
Name(s.to_owned())
}
}