use tok::{self, Tok};
use ast::*;
#[LALR]
#[recursive_ascent]
grammar<'input>(text: &'input str);
CommaList<T>: Vec<T> = {
T => vec![<>],
<v:CommaList<T>> "," <e:T> => {
let mut v = v;
v.push(e);
v
},
};
Qualified<T>: QualifiedName =
<db_name:(<DatabaseName> ".")?> <name:T> => QualifiedName { db_name: db_name, name: name };
pub CmdList: Vec<Option<Cmd>> = {
<v:(<ExplainCmd?> ";")*> <e:ExplainCmd?> => match e {
None => v,
e => {
let mut v = v;
v.push(e);
v
}
}
};
ExplainCmd: Cmd =
<explain:("explain" <("query" "plan")?>)?> <cmd:Stmt> => {
if explain.is_some() {
if explain.unwrap().is_some() {
Cmd::ExplainQueryPlan(cmd)
} else {
Cmd::Explain(cmd)
}
} else {
Cmd::Stmt(cmd)
}
};
Stmt: Stmt = {
AlterTable,
Analyze,
Attach,
Begin,
Commit,
CreateIndex,
CreateTable,
CreateTrigger,
CreateView,
CreateVirtualTable,
Delete,
Detach,
DropIndex,
DropTable,
DropTrigger,
DropView,
Insert,
Pragma,
Reindex,
Release,
Rollback,
Savepoint,
Select => Stmt::Select(<>),
Update,
Vacuum,
};
///////////////////// Begin and end transactions. ////////////////////////////
TransactionName = Name;
Transaction =
"transaction" <TransactionName?>;
Begin: Stmt =
"begin" <tt:TransactionType?> <name:Transaction?> => Stmt::Begin(tt, name.unwrap_or(None));
TransactionType: TransactionType = {
"deferred" => TransactionType::Deferred,
"immediate" => TransactionType::Immediate,
"exclusive" => TransactionType::Exclusive,
};
Commit: Stmt =
CommitHead <Transaction?> => Stmt::Commit(<>.unwrap_or(None));
CommitHead: () = {
"commit" => (),
"end" => (),
};
Rollback: Stmt =
"rollback" <tx:Transaction?> <sp:("to" "savepoint"? <SavepointName>)?> => Stmt::Rollback {
tx_name: tx.unwrap_or(None), savepoint_name: sp
};
SavepointName = Name;
Savepoint: Stmt =
"savepoint" <SavepointName> => Stmt::Savepoint(<>);
Release: Stmt =
"release" "savepoint"? <SavepointName> => Stmt::Release(<>);
///////////////////// The CREATE TABLE statement ////////////////////////////
DatabaseName = Name;
#[inline]
TableName = Name;
CreateTable: Stmt =
"create" <temporary:"temp"?> "table" <if_not_exists:IfNotExists?> <tbl_name:QualifiedTableName> <body:CreateTableBody> => Stmt::CreateTable {
temporary: temporary.is_some(), if_not_exists: if_not_exists.is_some(), tbl_name: tbl_name, body: body
};
CreateTableBody: CreateTableBody = {
"(" <columns:CommaList<ColumnDef>> <constraints:("," <CommaList<NamedTableConstraint>>)?> ")" <without:("without" <Name>)?> => CreateTableBody::ColumnsAndConstraints {
columns: columns, constraints: constraints, without: without
}, // TODO check Name == "rowid"*
"as" <Select> => CreateTableBody::AsSelect(<>),
};
IfNotExists: () =
"if" "not" "exists" => ();
ColumnName = Name;
Id: Name = {
"Id" => <>.to_owned(),
"indexed" => "INDEXED".to_owned(),
// TODO fallback
"temp" => "TEMP".to_owned(),
// "key" => "KEY".to_owned(),
// "match" => "KEY".to_owned(),
"row" => "ROW".to_owned(),
};
IdString: Name = {
Id,
// TODO "StringLiteral",
};
// nm
Name: Name = {
Id,
"StringLiteral" => <>.to_owned(),
"cross" => "CROSS".to_owned(),
"inner" => "INNER".to_owned(),
"left" => "LEFT".to_owned(),
"natural" => "NATURAL".to_owned(),
"outer" => "OUTER".to_owned(),
"right" => "RIGHT".to_owned(),
};
ConstraintName = Name;
ColumnDef: ColumnDefinition =
<col_name:ColumnName> <col_type:TypeName?> <constraints:NamedColumnConstraint*> => ColumnDefinition {
col_name: col_name, col_type: col_type, constraints: constraints
};
// fullname
QualifiedTableName = Qualified<TableName>;
// typetoken
TypeName: Type = {
IdString+ => Type { name: <>.concat(), size: None },
<name:IdString+> "(" <size:SignedNumber> ")" => Type {
name: name.concat(), size: Some(TypeSize::MaxSize(size))
},
<name:IdString+> "(" <n1:SignedNumber> "," <n2:SignedNumber> ")" => Type {
name: name.concat(), size: Some(TypeSize::TypeSize(n1, n2))
},
};
// signed
SignedNumber: String =
<sign:(Sign)?> <num:NumericLiteral> => {
match sign {
Some(sign) => format!("{}{}", sign, num),
None => num.to_owned(),
}
};
Sign: &'static str = {
"+" => "+",
"-" => "-",
};
// number
NumericLiteral = {
"Integer",
"Float",
};
CollationName = IdString;
NamedColumnConstraint: NamedColumnConstraint =
<name:("constraint" <ConstraintName>)?> <constraint:ColumnConstraint> => NamedColumnConstraint {
name: name, constraint: constraint
};
ColumnConstraint: ColumnConstraint = {
"primary" "key" <order:SortOrder?> <conflict_clause:ConflictClause?> <auto_increment:"autoincrement"?> => ColumnConstraint::PrimaryKey {
order: order, conflict_clause: conflict_clause, auto_increment: auto_increment.is_some()
},
<not:"not"?> "null" <conflict_clause:ConflictClause?> => ColumnConstraint::NotNull {
nullable: not.is_none(), conflict_clause: conflict_clause
},
"unique" <ConflictClause?> => ColumnConstraint::Unique(<>),
"check" "(" <Expr> ")" => ColumnConstraint::Check(<>),
"default" <DefaultValue> => ColumnConstraint::Default(<>),
"collate" <collation_name:CollationName> => ColumnConstraint::Collate { collation_name: collation_name },
"references" <tbl_name:TableName> <columns:("(" <CommaList<IndexedColumn>> ")")?> <args:RefArg*> <deref_clause:FixmeDeferSubclause?> => {
let clause = ForeignKeyClause {
tbl_name: tbl_name, columns: columns, args: args
};
ColumnConstraint::ForeignKey { clause: clause, deref_clause: deref_clause }
},
};
// FIXME Ambiguity with "not" "null", so the optional "not" is temporary removed...
FixmeDeferSubclause: DeferSubclause =
"deferrable" <init_deferred:InitDeferredPred?> => DeferSubclause {
deferrable: true, init_deferred: init_deferred
};
ConflictClause =
"on" "conflict" <ResolveType>;
ResolveType: ResolveType = {
"rollback" => ResolveType::Rollback,
"abort" => ResolveType::Abort,
"fail" => ResolveType::Fail,
"ignore" => ResolveType::Ignore,
"replace" => ResolveType::Replace,
};
DefaultValue: DefaultValue = {
LiteralValue => DefaultValue::Expr(Expr::Literal(<>.to_owned())),
"(" <Expr> ")" => DefaultValue::Expr(<>),
"+" <NumericLiteral> => DefaultValue::Expr(Expr::NumericLiteral(format!("+{}", <>))), // Ambiguous grammar with SignedNumber and LiteralValue
"-" <NumericLiteral> => DefaultValue::Expr(Expr::NumericLiteral(format!("-{}", <>))),
Id => DefaultValue::Expr(Expr::Id(<>)),
};
RefArg: RefArg = {
"on" "delete" <RefAct> => RefArg::OnDelete(<>),
"on" "update" <RefAct> => RefArg::OnUpdate(<>),
"match" <Name> => RefArg::Match(<>),
};
RefAct: RefAct = {
"set" "null" => RefAct::SetNull,
"set" "default" => RefAct::SetDefault,
"cascade" => RefAct::Cascade,
"restrict" => RefAct::Restrict,
"no" "action" => RefAct::NoAction,
};
// term
LiteralValue: &'input str = {
NumericLiteral,
"StringLiteral",
"Blob",
"null" => "NULL",
"current_date" => "CURRENT_DATE",
"current_time" => "CURRENT_TIME",
"current_timestamp" => "CURRENT_TIMESTAMP",
};
SortOrder: SortOrder = {
"asc" => SortOrder::Asc,
"desc" => SortOrder::Desc,
};
NamedTableConstraint: NamedTableConstraint =
<name:("constraint" <ConstraintName>)?> <constraint:TableConstraint> => NamedTableConstraint {
name: name, constraint: constraint
};
TableConstraint: TableConstraint = {
"primary" "key" "(" <columns:CommaList<SortedColumn>> <auto_increment:"autoincrement"?> ")" <conflict_clause:ConflictClause?> => TableConstraint::PrimaryKey {
columns: columns, auto_increment: auto_increment.is_some(), conflict_clause: conflict_clause
},
"unique" "(" <columns:CommaList<SortedColumn>> ")" <conflict_clause:ConflictClause?> => TableConstraint::Unique {
columns: columns, conflict_clause: conflict_clause
},
"check" "(" <Expr> ")" => TableConstraint::Check(<>),
"foreign" "key" "(" <columns:CommaList<IndexedColumn>> ")" "references" <tbl_name:TableName> <ref_columns:("(" <CommaList<IndexedColumn>> ")")?> <args:RefArg*> <deref_clause:DeferSubclause?> => {
let clause = ForeignKeyClause {
tbl_name: tbl_name, columns: ref_columns, args: args
};
TableConstraint::ForeignKey {
columns: columns, clause: clause, deref_clause: deref_clause
}
},
};
// sortlist
SortedColumn: SortedColumn =
<expr:Expr> <order:SortOrder?> => SortedColumn { expr: expr, order: order };
// eidlist
IndexedColumn: IndexedColumn =
<col_name:ColumnName> <collation_name:("collate" <CollationName>)?> <order:SortOrder?> => IndexedColumn {
col_name: col_name, collation_name: collation_name, order: order
};
DeferSubclause: DeferSubclause =
<deferrable:"not"?> "deferrable" <init_deferred:InitDeferredPred?> => DeferSubclause {
deferrable: deferrable.is_none(), init_deferred: init_deferred
};
InitDeferredPred: InitDeferredPred = {
"initially" "deferred" => InitDeferredPred::InitiallyDeferred,
"initially" "immediate" => InitDeferredPred::InitiallyImmediate,
};
////////////////////////// The DROP TABLE /////////////////////////////////////
DropTable: Stmt =
"drop" "table" <if_exists:IfExists?> <tbl_name:QualifiedTableName> => Stmt::DropTable {
if_exists: if_exists.is_some(), tbl_name: tbl_name
};
IfExists: () =
"if" "exists" => ();
///////////////////// The CREATE VIEW statement ///////////////////////////////
CreateView: Stmt =
"create" <temporary:"temp"?> "view" <if_not_exists:IfNotExists?> <view_name:QualifiedTableName> <columns:("(" <CommaList<IndexedColumn>> ")")?> "as" <select:Select> => Stmt::CreateView {
temporary: temporary.is_some(), if_not_exists: if_not_exists.is_some(), view_name: view_name, columns: columns, select: select
};
DropView: Stmt =
"drop" "view" <if_exists:IfExists?> <view_name:QualifiedTableName> => Stmt::DropView {
if_exists: if_exists.is_some(), view_name: view_name
};
//////////////////////// The SELECT statement /////////////////////////////////
Select: Select =
<with:With?> <body:SelectNoWith> <order_by:OrderBy?> <limit:Limit?> => Select {
with: with, body: body, order_by: order_by, limit: limit
};
SelectNoWith: SelectBody = {
OneSelect => SelectBody { select: <>, compounds: None },
<body:SelectNoWith> <operator:CompoundOperator> <select:OneSelect> => {
let compound = CompoundSelect { operator: operator, select: select };
let mut body = body;
match body.compounds {
Some(ref mut compounds) => compounds.push(compound),
None => body.compounds = Some(vec![compound]),
};
body
},
};
CompoundOperator: CompoundOperator = {
"union" => CompoundOperator::Union,
"union" "all" => CompoundOperator::UnionAll,
"except" => CompoundOperator::Except,
"intersect" => CompoundOperator::Intersect,
};
OneSelect: OneSelect = {
"select" <distinctness:Distinct?> <columns:CommaList<ResultColumn>> <from:From?> <where_clause:Where?> <group_by:GroupBy?> => OneSelect::Select {
distinctness: distinctness, columns: columns, from: from, where_clause: where_clause, group_by: group_by
},
Values => OneSelect::Values(<>),
};
Distinct: Distinctness = {
"distinct" => Distinctness::Distinct,
"all" => Distinctness::All,
};
ResultColumn: ResultColumn = {
Expr As? => ResultColumn::Expr(<>),
"*" => ResultColumn::Star,
<TableName> "." "*" => ResultColumn::TableStar(<>),
};
Values: Vec<Vec<Expr>> = {
"values" "(" <CommaList<Expr>> ")" => vec![<>],
<values:Values> "," "(" <others:CommaList<Expr>> ")" => {
let mut values = values;
values.push(others);
values
},
};
As: As = {
"as" <Name> => As::As(<>),
IdString => As::Elided(<>),
};
From: FromClause =
"from" <SelectTableList>;
SelectTableList: FromClause = {
SelectTable => FromClause { select: Box::new(<>), joins: None },
<lst:SelectTableList> <operator:JoinOperator> <table:SelectTable> <constraint:JoinConstraint?> => {
let join = JoinedSelectTable { operator: operator, table: table, constraint: constraint };
let mut lst = lst;
match lst.joins {
Some(ref mut joins) => joins.push(join),
None => lst.joins = Some(vec![join]),
};
lst
},
};
SelectTable: SelectTable = {
QualifiedTableName As? Indexed? => SelectTable::Table(<>),
<QualifiedTableName> "(" <CommaList<Expr>?> ")" <As?> => SelectTable::TableCall(<>),
"(" <Select> ")" <As?> => SelectTable::Select(<>),
"(" <SelectTableList> ")" <As?> => SelectTable::Sub(<>),
};
JoinConstraint: JoinConstraint = {
"on" <Expr> => JoinConstraint::On(<>),
"using" "(" <CommaList<ColumnName>> ")" => JoinConstraint::Using(<>),
};
JoinOperator: JoinOperator = {
"," => JoinOperator::Comma,
<natural:"natural"?> <join_type:JoinType?> "join" => JoinOperator::TypedJoin {
natural: natural.is_some(), join_type: join_type
},
};
JoinType: JoinType = {
"left" <"outer"?> => {
if <>.is_some() {
JoinType::LeftOuter
} else {
JoinType::Left
}
},
"inner" => JoinType::Inner,
"cross" => JoinType::Cross,
};
IndexName = Name;
Indexed: Indexed = {
"indexed" "by" <IndexName> => Indexed::IndexedBy(<>),
"not" "indexed" => Indexed::NotIndexed,
};
Where: Expr =
"where" <Expr>;
GroupBy: GroupBy =
"group" "by" <exprs:CommaList<Expr>> <having:("having" <Expr>)?> => GroupBy {
exprs: exprs, having: having
};
OrderBy: Vec<SortedColumn> =
"order" "by" <CommaList<SortedColumn>>;
Limit: Limit = {
"limit" <Expr> => Limit { expr: <>, offset: None },
"limit" <expr:Expr> "offset" <offset:Expr> => Limit { expr: expr, offset: Some(offset) },
"limit" <expr:Expr> "," <offset:Expr> => Limit { expr: expr, offset: Some(offset) },
};
/////////////////////////// The DELETE statement /////////////////////////////
Delete: Stmt =
<with:With?> "delete" "from" <tbl_name:QualifiedTableName> <indexed:Indexed?> <where_clause:Where?> <order_by:OrderBy?> <limit:Limit?> => Stmt::Delete {
with: with, tbl_name: tbl_name, indexed: indexed, where_clause: where_clause, order_by: order_by, limit: limit
};
////////////////////////// The UPDATE command ////////////////////////////////
Update: Stmt =
<with:With?> "update" <or_conflict:OrConflict?> <tbl_name:QualifiedTableName> <indexed:Indexed?> "set" <sets:CommaList<Set>> <where_clause:Where?> <order_by:OrderBy?> <limit:Limit?> => Stmt::Update {
with: with, or_conflict: or_conflict, tbl_name: tbl_name, indexed: indexed, sets: sets, where_clause: where_clause, order_by: order_by, limit: limit
};
OrConflict: ResolveType =
"or" <ResolveType>;
Set: Set =
<col_name:ColumnName> "=" <expr:Expr> => Set {
col_name: col_name, expr: expr
};
////////////////////////// The INSERT command /////////////////////////////////
Insert: Stmt =
<with:With?> <or_conflict:InsertCmd> "into" <tbl_name:QualifiedTableName> <columns:("(" <CommaList<ColumnName>> ")")?> <body:InsertBody> => Stmt::Insert {
with: with, or_conflict: or_conflict, tbl_name: tbl_name, columns: columns, body: body
};
InsertCmd: Option<ResolveType> = {
"insert" <OrConflict?>,
"replace" => Some(ResolveType::Replace),
};
InsertBody: InsertBody = {
Select => InsertBody::Select(<>),
"default" "values" => InsertBody::DefaultValues,
};
/////////////////////////// Expression Processing /////////////////////////////
Expr: Expr = {
OrExpr,
};
// %left OR.
OrExpr: Expr = {
<lhs:OrExpr> "or" <rhs:AndExpr> => Expr::Binary(Box::new(lhs), Operator::Or, Box::new(rhs)),
AndExpr,
};
// %left AND.
AndExpr: Expr = {
<lhs:AndExpr> "and" <rhs:NotExpr> => Expr::Binary(Box::new(lhs), Operator::And, Box::new(rhs)),
NotExpr,
};
// %right NOT.
NotExpr: Expr = {
"not" <NotExpr> => Expr::Unary(UnaryOperator::Not, Box::new(<>)),
EqExpr,
};
// %left IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ.
EqExpr: Expr = {
<lhs:EqExpr> "=" <rhs:CompExpr> => Expr::Binary(Box::new(lhs), Operator::Equals, Box::new(rhs)),
<lhs:EqExpr> "<>" <rhs:CompExpr> => Expr::Binary(Box::new(lhs), Operator::NotEquals, Box::new(rhs)),
<lhs:EqExpr> <not:"not"?> <op:LikeOperator> <rhs:CompExpr> <escape:("escape" <BitExpr>)?> => Expr::Like {
lhs: Box::new(lhs),
not: not.is_some(),
op: op,
rhs: Box::new(rhs),
escape: escape.map(|e| Box::new(e)),
},
<EqExpr> "isnull" => Expr::Isnull(Box::new(<>)),
<EqExpr> "notnull" => Expr::NotNull(Box::new(<>)),
<EqExpr> "not" "null" => Expr::NotNull(Box::new(<>)),
<lhs:EqExpr> "is" <not:"not"?> <rhs:CompExpr> => {
if not.is_some() {
Expr::Binary(Box::new(lhs), Operator::IsNot, Box::new(rhs))
} else {
Expr::Binary(Box::new(lhs), Operator::Is, Box::new(rhs))
}
},
<lhs:EqExpr> <not:"not"?> "between" <start:NotExpr> "and" <end:CompExpr> => Expr::Between {
lhs: Box::new(lhs),
not: not.is_some(),
start: Box::new(start),
end: Box::new(end),
},
<lhs:EqExpr> <not:"not"?> "in" "(" <rhs:CommaList<Expr>?> ")" => Expr::InList {
lhs: Box::new(lhs),
not: not.is_some(),
rhs: rhs.map(|v| v.into_iter().map(|e| Box::new(e)).collect()),
},
<lhs:EqExpr> <not:"not"?> "in" "(" <rhs:Select> ")" => Expr::InSelect {
lhs: Box::new(lhs),
not: not.is_some(),
rhs: Box::new(rhs),
},
<lhs:EqExpr> <not:"not"?> "in" <rhs:QualifiedTableName> => Expr::InTable {
lhs: Box::new(lhs),
not: not.is_some(),
rhs: rhs,
},
CompExpr,
};
// %left GT LE LT GE.
CompExpr: Expr = {
<lhs:CompExpr> ">" <rhs:BitExpr> => Expr::Binary(Box::new(lhs), Operator::Greater, Box::new(rhs)),
<lhs:CompExpr> "=>" <rhs:BitExpr> => Expr::Binary(Box::new(lhs), Operator::GreaterEquals, Box::new(rhs)),
<lhs:CompExpr> "<=" <rhs:BitExpr> => Expr::Binary(Box::new(lhs), Operator::LessEquals, Box::new(rhs)),
<lhs:CompExpr> "<" <rhs:BitExpr> => Expr::Binary(Box::new(lhs), Operator::Less, Box::new(rhs)),
BitExpr,
};
// %right ESCAPE.
// %left BITAND BITOR LSHIFT RSHIFT.
BitExpr: Expr = {
<lhs:BitExpr> "&" <rhs:SumExpr> => Expr::Binary(Box::new(lhs), Operator::BitwiseAnd, Box::new(rhs)),
<lhs:BitExpr> "|" <rhs:SumExpr> => Expr::Binary(Box::new(lhs), Operator::BitwiseOr, Box::new(rhs)),
<lhs:BitExpr> "<<" <rhs:SumExpr> => Expr::Binary(Box::new(lhs), Operator::LeftShift, Box::new(rhs)),
<lhs:BitExpr> ">>" <rhs:SumExpr> => Expr::Binary(Box::new(lhs), Operator::RightShift, Box::new(rhs)),
SumExpr,
};
// %left PLUS MINUS.
SumExpr: Expr = {
<lhs:SumExpr> "+" <rhs:MulExpr> => Expr::Binary(Box::new(lhs), Operator::Add, Box::new(rhs)),
<lhs:SumExpr> "-" <rhs:MulExpr> => Expr::Binary(Box::new(lhs), Operator::Substract, Box::new(rhs)),
MulExpr,
};
// %left STAR SLASH REM.
MulExpr: Expr = {
<lhs:MulExpr> "*" <rhs:ConcatExpr> => Expr::Binary(Box::new(lhs), Operator::Multiply, Box::new(rhs)),
<lhs:MulExpr> "/" <rhs:ConcatExpr> => Expr::Binary(Box::new(lhs), Operator::Divide, Box::new(rhs)),
<lhs:MulExpr> "%" <rhs:ConcatExpr> => Expr::Binary(Box::new(lhs), Operator::Modulus, Box::new(rhs)),
ConcatExpr,
};
// %left CONCAT.
ConcatExpr: Expr = {
<lhs:ConcatExpr> "||" <rhs:CollateExpr> => Expr::Binary(Box::new(lhs), Operator::Concat, Box::new(rhs)),
CollateExpr,
};
// %left COLLATE.
CollateExpr: Expr = {
<expr:CollateExpr> "collate" <name:CollationName> => Expr::Collate(Box::new(expr), name),
BitNotExpr,
};
// %right BITNOT.
BitNotExpr: Expr = {
"~" <BitNotExpr> => Expr::Unary(UnaryOperator::BitwiseNot, Box::new(<>)),
"-" <BitNotExpr> => Expr::Unary(UnaryOperator::Negative, Box::new(<>)),
"+" <BitNotExpr> => Expr::Unary(UnaryOperator::Positive, Box::new(<>)),
OtherExpr,
};
OtherExpr: Expr = {
LiteralValue => Expr::Literal(<>.to_owned()),
"(" <Expr> ")" => Expr::Parenthesized(Box::new(<>)),
Id => Expr::Id(<>),
// TODO JOIN_KW
<Name> "." <Name> => Expr::Qualified(<>),
<Name> "." <Name> "." <Name> => Expr::DoublyQualified(<>),
"Variable" => Expr::Variable(<>.to_owned()),
"cast" "(" <expr:Expr> "as" <type_name:TypeName> ")" => Expr::Cast {
expr: Box::new(expr),
type_name: type_name,
},
<name:Id> "(" <distinctness:Distinct?> <args:CommaList<Expr>?> ")" => Expr::FunctionCall {
name: name,
distinctness: distinctness,
args: args.map(|v| v.into_iter().map(|e| Box::new(e)).collect()),
},
<Id> "(" "*" ")" => Expr::FunctionCallStar(<>),
"(" <Select> ")" => Expr::Subquery(Box::new(<>)),
"exists" "(" <Select> ")" => Expr::Exists(Box::new(<>)),
"case" <base:Expr?> <pairs:("when" <Expr> "then" <Expr>)+> <else_expr:("else" <Expr>)?> "end" => Expr::Case {
base: base.map(|e| Box::new(e)),
when_then_pairs: pairs.into_iter().map(|(w,t)| (Box::new(w), Box::new(t))).collect(),
else_expr: else_expr.map(|e| Box::new(e)),
},
"raise" "(" "ignore" ")" => Expr::Raise(ResolveType::Ignore, None),
"raise" "(" <rt:RaiseType> "," <err:"StringLiteral"> ")" => Expr::Raise(rt, Some(err.to_owned())), // TODO Name versus StringLiteral
};
LikeOperator: LikeOperator = {
"glob" => LikeOperator::Glob,
"like" => LikeOperator::Like,
"match" => LikeOperator::Match,
"regexp" => LikeOperator::Regexp,
};
RaiseType: ResolveType = {
"rollback" => ResolveType::Rollback,
"abort" => ResolveType::Abort,
"fail" => ResolveType::Fail,
};
///////////////////////////// The CREATE INDEX command ///////////////////////
CreateIndex: Stmt =
"create" <unique:"unique"?> "index" <if_not_exists:IfNotExists?> <idx_name:Qualified<IndexName>> "on" <tbl_name:TableName> "(" <columns:CommaList<SortedColumn>> ")" <where_clause:Where?> => Stmt::CreateIndex {
unique: unique.is_some(), if_not_exists: if_not_exists.is_some(), idx_name: idx_name, tbl_name: tbl_name, columns: columns, where_clause: where_clause
};
///////////////////////////// The DROP INDEX command /////////////////////////
DropIndex: Stmt =
"drop" "index" <if_exists:IfExists?> <idx_name:Qualified<IndexName>> => Stmt::DropIndex {
if_exists: if_exists.is_some(), idx_name: idx_name
};
///////////////////////////// The VACUUM command /////////////////////////////
Vacuum: Stmt =
"vacuum" <DatabaseName?> => Stmt::Vacuum(<>);
///////////////////////////// The PRAGMA command /////////////////////////////
PragmaName = Name;
Pragma: Stmt =
"pragma" <Qualified<PragmaName>> <PragmaBody?> => Stmt::Pragma(<>);
PragmaBody: PragmaBody = {
"=" <PragmaValue> => PragmaBody::Equals(<>),
"(" <PragmaValue> ")" => PragmaBody::Call(<>),
};
PragmaValue: PragmaValue = {
SignedNumber,
Name,
"on" => "on".to_owned(),
"delete" => "delete".to_owned(),
"default" => "default".to_owned(),
"exclusive" => "exclusive".to_owned(),
"full" => "full".to_owned(),
"no" => "no".to_owned(),
};
//////////////////////////// The CREATE TRIGGER command /////////////////////
TriggerName = Name;
CreateTrigger: Stmt =
"create" <trigger:TriggerDecl> "begin" <cmds:(<TriggerCmd> ";")+> "end" => {
let mut trigger = trigger;
if let Stmt::CreateTrigger { ref mut commands, .. } = trigger {
*commands = cmds;
} else {
unreachable!();
}
trigger
};
TriggerDecl: Stmt =
<temporary:"temp"?> "trigger" <if_not_exists:IfNotExists?> <trigger_name:Qualified<TriggerName>> <time:TriggerTime?> <event:TriggerEvent> "on" <tbl_name:QualifiedTableName> <for_each_row:ForEachRow?> <when_clause:WhenClause?> => Stmt::CreateTrigger {
temporary: temporary.is_some(), if_not_exists: if_not_exists.is_some(), trigger_name: trigger_name, time: time, event: event, tbl_name: tbl_name, for_each_row: for_each_row.is_some(), when_clause: when_clause, commands: Vec::with_capacity(0)
};
TriggerTime: TriggerTime = {
"before" => TriggerTime::Before,
"after" => TriggerTime::After,
"instead" "of" => TriggerTime::InsteadOf,
};
TriggerEvent: TriggerEvent = {
"delete" => TriggerEvent::Delete,
"insert" => TriggerEvent::Insert,
"update" => TriggerEvent::Update,
"update" "of" <CommaList<ColumnName>> => TriggerEvent::UpdateOf(<>),
};
ForEachRow: () =
"for" "each" "row" => ();
WhenClause: Expr =
"when" <Expr>;
// Disallow qualified table names on INSERT, UPDATE, and DELETE statements
// within a trigger. The table to INSERT, UPDATE, or DELETE is always in
// the same database as the table that the trigger fires on.
TriggerCmd: TriggerCmd = {
"update" <or_conflict:OrConflict?> <tbl_name:TableName> "set" <sets:CommaList<Set>> <where_clause:Where?> => TriggerCmd::Update {
or_conflict: or_conflict, tbl_name: tbl_name, sets: sets, where_clause: where_clause
},
<or_conflict:InsertCmd> "into" <tbl_name:TableName> <col_names:("(" <CommaList<ColumnName>> ")")?> <select:Select> => TriggerCmd::Insert {
or_conflict: or_conflict, tbl_name: tbl_name, col_names: col_names, select: select
},
"delete" "from" <tbl_name:TableName> <where_clause:Where?> => TriggerCmd::Delete {
tbl_name: tbl_name, where_clause: where_clause
},
Select => TriggerCmd::Select(<>),
};
//////////////////////// DROP TRIGGER statement //////////////////////////////
DropTrigger: Stmt =
"drop" "trigger" <if_exists:IfExists?> <trigger_name:Qualified<TriggerName>> => Stmt::DropTrigger {
if_exists: if_exists.is_some(), trigger_name: trigger_name
};
//////////////////////// ATTACH DATABASE file AS name /////////////////////////
Attach: Stmt =
"attach" "database"? <expr:Expr> "as" <db_name:Expr> <key:("key" <Expr>)?> => Stmt::Attach {
expr: expr, db_name: db_name, key: key
};
Detach: Stmt =
"detach" "database"? <Expr> => Stmt::Detach(<>);
////////////////////////// REINDEX collation //////////////////////////////////
Reindex: Stmt =
"reindex" <QualifiedTableName?> => Stmt::Reindex { obj_name: <> };
/////////////////////////////////// ANALYZE ///////////////////////////////////
Analyze: Stmt =
"analyze" <QualifiedTableName?> => Stmt::Analyze(<>);
//////////////////////// ALTER TABLE table ... ////////////////////////////////
AlterTable: Stmt =
"alter" "table" <QualifiedTableName> <AlterTableBody> => Stmt::AlterTable(<>);
AlterTableBody: AlterTableBody = {
"rename" "to" <TableName> => AlterTableBody::RenameTo(<>),
"add" "column"? <ColumnDef> => AlterTableBody::AddColumn(<>),
};
//////////////////////// CREATE VIRTUAL TABLE ... /////////////////////////////
ModuleName = Name;
CreateVirtualTable: Stmt =
"create" "virtual" "table" <if_not_exists:IfNotExists?> <tbl_name:QualifiedTableName> "using" <module_name:ModuleName> <args:("(" <CommaList<VirtualTableArg>?> ")")?> => Stmt::CreateVirtualTable {
if_not_exists: if_not_exists.is_some(), tbl_name: tbl_name, module_name: module_name, args: args.unwrap_or(None)
};
VirtualTableArg: Expr = {
"StringLiteral" => Expr::Literal(<>.to_owned()),
"Id" => Expr::Id(<>.to_owned()),
NumericLiteral => Expr::NumericLiteral(<>.to_owned()),
// TODO ANY
};
//////////////////////// COMMON TABLE EXPRESSIONS ////////////////////////////
With: With =
"with" <recursive:"recursive"?> <ctes:CommaList<CommonTableExpr>> => With {
recursive: recursive.is_some(), ctes: ctes
};
CommonTableExpr: CommonTableExpr =
<tbl_name:TableName> <columns:("(" <CommaList<IndexedColumn>> ")")?> "as" "(" <select:Select> ")" => CommonTableExpr {
tbl_name: tbl_name, columns: columns, select: select
};
extern {
type Location = usize;
type Error = tok::Error;
enum Tok<'input> {
"abort" => Tok::Abort,
"action" => Tok::Action,
"add" => Tok::Add,
"after" => Tok::After,
"all" => Tok::All,
"alter" => Tok::Alter,
"analyze" => Tok::Analyze,
"and" => Tok::And,
"as" => Tok::As,
"asc" => Tok::Asc,
"attach" => Tok::Attach,
"autoincrement" => Tok::Autoincr,
"before" => Tok::Before,
"begin" => Tok::Begin,
"between" => Tok::Between,
"by" => Tok::By,
"cascade" => Tok::Cascade,
"case" => Tok::Case,
"cast" => Tok::Cast,
"check" => Tok::Check,
"collate" => Tok::Collate,
"column" => Tok::ColumnKw,
"commit" => Tok::Commit,
"conflict" => Tok::Conflict,
"constraint" => Tok::Constraint,
"create" => Tok::Create,
"cross" => Tok::Cross,
"current_date" => Tok::CurrentDate,
"current_time" => Tok::CurrentTime,
"current_timestamp" => Tok::CurrentTimestamp,
"database" => Tok::Database,
"default" => Tok::Default,
"deferrable" => Tok::Deferrable,
"deferred" => Tok::Deferred,
"delete" => Tok::Delete,
"desc" => Tok::Desc,
"detach" => Tok::Detach,
"distinct" => Tok::Distinct,
"drop" => Tok::Drop,
"each" => Tok::Each,
"else" => Tok::Else,
"end" => Tok::End,
"escape" => Tok::Escape,
"except" => Tok::Except,
"exclusive" => Tok::Exclusive,
"exists" => Tok::Exists,
"explain" => Tok::Explain,
"fail" => Tok::Fail,
"for" => Tok::For,
"foreign" => Tok::Foreign,
"from" => Tok::From,
"full" => Tok::Full,
"glob" => Tok::Glob,
"group" => Tok::Group,
"having" => Tok::Having,
"if" => Tok::If,
"ignore" => Tok::Ignore,
"immediate" => Tok::Immediate,
"in" => Tok::In,
"index" => Tok::Index,
"indexed" => Tok::Indexed,
"initially" => Tok::Initially,
"inner" => Tok::Inner,
"insert" => Tok::Insert,
"instead" => Tok::Instead,
"intersect" => Tok::Intersect,
"into" => Tok::Into,
"is" => Tok::Is,
"isnull" => Tok::IsNull,
"join" => Tok::Join,
"key" => Tok::Key,
"left" => Tok::Left,
"like" => Tok::Like,
"limit" => Tok::Limit,
"match" => Tok::Match,
"natural" => Tok::Natural,
"no" => Tok::No,
"not" => Tok::Not,
"notnull" => Tok::NotNull,
"null" => Tok::Null,
"of" => Tok::Of,
"offset" => Tok::Offset,
"on" => Tok::On,
"or" => Tok::Or,
"order" => Tok::Order,
"outer" => Tok::Outer,
"plan" => Tok::Plan,
"pragma" => Tok::Pragma,
"primary" => Tok::Primary,
"query" => Tok::Query,
"raise" => Tok::Raise,
"recursive" => Tok::Recursive,
"references" => Tok::References,
"regexp" => Tok::Regexp,
"reindex" => Tok::Reindex,
"release" => Tok::Release,
"rename" => Tok::Rename,
"replace" => Tok::Replace,
"restrict" => Tok::Restrict,
"right" => Tok::Right,
"rollback" => Tok::Rollback,
"row" => Tok::Row,
"savepoint" => Tok::Savepoint,
"select" => Tok::Select,
"set" => Tok::Set,
"table" => Tok::Table,
"temp" => Tok::Temp,
"then" => Tok::Then,
"to" => Tok::To,
"transaction" => Tok::Transaction,
"trigger" => Tok::Trigger,
"union" => Tok::Union,
"unique" => Tok::Unique,
"update" => Tok::Update,
"using" => Tok::Using,
"vacuum" => Tok::Vacuum,
"values" => Tok::Values,
"view" => Tok::View,
"virtual" => Tok::Virtual,
"when" => Tok::When,
"where" => Tok::Where,
"with" => Tok::With,
"without" => Tok::Without,
"StringLiteral" => Tok::StringLiteral(<&'input str>),
"Id" => Tok::Id(<&'input str>),
"Variable" => Tok::Variable(<&'input str>),
"Blob" => Tok::Blob(<&'input str>),
"Integer" => Tok::Integer(<&'input str>),
"Float" => Tok::Float(<&'input str>),
"&" => Tok::BitAnd,
"~" => Tok::BitNot,
"|" => Tok::BitOr,
"," => Tok::Comma,
"||" => Tok::Concat,
"." => Tok::Dot,
"=" => Tok::Equals,
">" => Tok::GreaterThan,
"=>" => Tok::GreaterEquals,
"(" => Tok::LeftParen,
"<<" => Tok::LeftShift,
"<=" => Tok::LessEquals,
"<" => Tok::LessThan,
"-" => Tok::Minus,
"<>" => Tok::NotEquals,
"+" => Tok::Plus,
"%" => Tok::Reminder,
")" => Tok::RightParen,
">>" => Tok::RightShift,
";" => Tok::Semi,
"/" => Tok::Slash,
"*" => Tok::Star,
}
}