Files
turso/parser/src/ast/fmt.rs
Pekka Enberg 433b60555f Add BEGIN CONCURRENT support for MVCC mode
Currently, when MVCC is enabled, every transaction mode supports
concurrent reads and writes, which makes it hard to adopt for existing
applications that use `BEGIN DEFERRED` or `BEGIN IMMEDIATE`.

Therefore, add support for `BEGIN CONCURRENT` transactions when MVCC is
enabled. The transaction mode allows multiple concurrent read/write
transactions that don't block each other, with conflicts resolved at
commit time. Furthermore, implement the correct semantics for `BEGIN
DEFERRED` and `BEGIN IMMEDIATE` by taking advantage of the pager level
write lock when transaction upgrades to write. This means that now
concurrent MVCC transactions are serialized against the legacy ones when
needed.

The implementation includes:

- Parser support for CONCURRENT keyword in BEGIN statements

- New Concurrent variant in TransactionMode to distinguish from regular
  read/write transactions

- MVCC store tracking of exclusive transactions to support IMMEDIATE and
  EXCLUSIVE modes alongside CONCURRENT

- Proper transaction state management for all transaction types in MVCC

This enables better concurrency for applications that can handle
optimistic concurrency control, while still supporting traditional
SQLite transaction semantics via IMMEDIATE and EXCLUSIVE modes.
2025-09-11 16:05:52 +03:00

2471 lines
76 KiB
Rust

//! AST node format
use std::fmt::{self, Display, Formatter, Write};
use crate::ast::*;
use crate::token::TokenType;
use crate::token::TokenType::*;
use crate::Result;
use crate::ast::TableInternalId;
/// Context to be used in ToSqlString
pub trait ToSqlContext {
/// Given an id, get the table name
/// First Option indicates whether the table exists
///
/// Currently not considering aliases
fn get_table_name(&self, _id: TableInternalId) -> Option<&str> {
None
}
/// Given a table id and a column index, get the column name
/// First Option indicates whether the column exists
/// Second Option indicates whether the column has a name
fn get_column_name(&self, _table_id: TableInternalId, _col_idx: usize) -> Option<Option<&str>> {
None
}
// help function to handle missing table/column names
fn get_table_and_column_names(
&self,
table_id: TableInternalId,
col_idx: usize,
) -> (String, String) {
let table_name = self
.get_table_name(table_id)
.map(|s| s.to_owned())
.unwrap_or_else(|| format!("t{}", table_id.0));
let column_name = self
.get_column_name(table_id, col_idx)
.map(|opt| {
opt.map(|s| s.to_owned())
.unwrap_or_else(|| format!("c{col_idx}"))
})
.unwrap_or_else(|| format!("c{col_idx}"));
(table_name, column_name)
}
}
pub struct WriteTokenStream<'a, T: Write> {
write: &'a mut T,
spaced: bool,
}
impl<'a, T: Write> WriteTokenStream<'a, T> {
/// Create a new token stream writing to the specified writer
pub fn new(write: &'a mut T) -> Self {
Self {
write,
spaced: true,
}
}
}
impl<T: Write> TokenStream for WriteTokenStream<'_, T> {
type Error = fmt::Error;
fn append(&mut self, ty: TokenType, value: Option<&str>) -> fmt::Result {
if !self.spaced {
match ty {
TK_COMMA | TK_SEMI | TK_RP | TK_DOT => {}
_ => {
self.write.write_char(' ')?;
self.spaced = true;
}
};
}
match (ty, ty.as_str(), value) {
(TK_BLOB, None, value) => {
self.write.write_char('X')?;
self.write.write_char('\'')?;
if let Some(str) = value {
self.write.write_str(str)?;
}
self.write.write_char('\'')?;
Ok(())
}
(_, ty_str, value) => {
if let Some(str) = ty_str {
self.write.write_str(str)?;
self.spaced = ty == TK_LP || ty == TK_DOT; // str should not be whitespace
}
if let Some(str) = value {
// trick for pretty-print
self.spaced = str.bytes().all(|b| b.is_ascii_whitespace());
self.write.write_str(str)?;
}
Ok(())
}
}
}
}
pub struct BlankContext;
impl ToSqlContext for BlankContext {}
/// Stream of token
pub trait TokenStream {
/// Potential error raised
type Error;
/// Push token to this stream
fn append(&mut self, ty: TokenType, value: Option<&str>) -> Result<(), Self::Error>;
/// Interspace iterator with commas
fn comma<I, C: ToSqlContext>(&mut self, items: I, context: &C) -> Result<(), Self::Error>
where
I: IntoIterator,
I::Item: ToTokens,
{
let iter = items.into_iter();
for (i, item) in iter.enumerate() {
if i != 0 {
self.append(TK_COMMA, None)?;
}
item.to_tokens(self, context)?;
}
Ok(())
}
}
pub struct SqlDisplayer<'a, 'b, C: ToSqlContext, T: ToTokens> {
ctx: &'a C,
start_node: &'b T,
}
impl<'a, 'b, C: ToSqlContext, T: ToTokens> SqlDisplayer<'a, 'b, C, T> {
/// Create a new displayer with the specified context and AST node
pub fn new(ctx: &'a C, start_node: &'b T) -> Self {
Self { ctx, start_node }
}
}
impl<'a, 'b, C: ToSqlContext, T: ToTokens> Display for SqlDisplayer<'a, 'b, C, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut stream = WriteTokenStream::new(f);
self.start_node.to_tokens(&mut stream, self.ctx)
}
}
/// Generate token(s) from AST node
/// Also implements Display to make sure devs won't forget Display
pub trait ToTokens: Display {
/// Send token(s) to the specified stream with context
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error>;
// Return displayer representation with context
fn displayer<'a, 'b, C: ToSqlContext>(&'b self, ctx: &'a C) -> SqlDisplayer<'a, 'b, C, Self>
where
Self: Sized,
{
SqlDisplayer::new(ctx, self)
}
}
macro_rules! impl_display_for_to_tokens {
($struct:ty) => {
impl Display for $struct {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.displayer(&BlankContext).fmt(f)
}
}
};
}
impl<T: ?Sized + ToTokens> ToTokens for &T {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
ToTokens::to_tokens(&**self, s, context)
}
}
impl<T: ?Sized + ToTokens> ToTokens for Box<T> {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
ToTokens::to_tokens(self.as_ref(), s, context)
}
}
impl ToTokens for String {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(TK_ANY, Some(self.as_ref()))
}
}
impl_display_for_to_tokens!(Cmd);
impl ToTokens for Cmd {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Explain(stmt) => {
s.append(TK_EXPLAIN, None)?;
stmt.to_tokens(s, context)?;
}
Self::ExplainQueryPlan(stmt) => {
s.append(TK_EXPLAIN, None)?;
s.append(TK_QUERY, None)?;
s.append(TK_PLAN, None)?;
stmt.to_tokens(s, context)?;
}
Self::Stmt(stmt) => {
stmt.to_tokens(s, context)?;
}
}
s.append(TK_SEMI, None)
}
}
impl_display_for_to_tokens!(Stmt);
impl ToTokens for Stmt {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::AlterTable(AlterTable { name, body }) => {
s.append(TK_ALTER, None)?;
s.append(TK_TABLE, None)?;
name.to_tokens(s, context)?;
body.to_tokens(s, context)
}
Self::Analyze { name } => {
s.append(TK_ANALYZE, None)?;
if let Some(name) = name {
name.to_tokens(s, context)?;
}
Ok(())
}
Self::Attach { expr, db_name, key } => {
s.append(TK_ATTACH, None)?;
expr.to_tokens(s, context)?;
s.append(TK_AS, None)?;
db_name.to_tokens(s, context)?;
if let Some(key) = key {
s.append(TK_KEY, None)?;
key.to_tokens(s, context)?;
}
Ok(())
}
Self::Begin { typ, name } => {
s.append(TK_BEGIN, None)?;
if let Some(typ) = typ {
typ.to_tokens(s, context)?;
}
if let Some(name) = name {
s.append(TK_TRANSACTION, None)?;
name.to_tokens(s, context)?;
}
Ok(())
}
Self::Commit { name } => {
s.append(TK_COMMIT, None)?;
if let Some(name) = name {
s.append(TK_TRANSACTION, None)?;
name.to_tokens(s, context)?;
}
Ok(())
}
Self::CreateIndex {
unique,
if_not_exists,
idx_name,
tbl_name,
columns,
where_clause,
} => {
s.append(TK_CREATE, None)?;
if *unique {
s.append(TK_UNIQUE, None)?;
}
s.append(TK_INDEX, None)?;
if *if_not_exists {
s.append(TK_IF, None)?;
s.append(TK_NOT, None)?;
s.append(TK_EXISTS, None)?;
}
idx_name.to_tokens(s, context)?;
s.append(TK_ON, None)?;
tbl_name.to_tokens(s, context)?;
s.append(TK_LP, None)?;
comma(columns, s, context)?;
s.append(TK_RP, None)?;
if let Some(where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::CreateTable {
temporary,
if_not_exists,
tbl_name,
body,
} => {
s.append(TK_CREATE, None)?;
if *temporary {
s.append(TK_TEMP, None)?;
}
s.append(TK_TABLE, None)?;
if *if_not_exists {
s.append(TK_IF, None)?;
s.append(TK_NOT, None)?;
s.append(TK_EXISTS, None)?;
}
tbl_name.to_tokens(s, context)?;
body.to_tokens(s, context)
}
Self::CreateTrigger {
temporary,
if_not_exists,
trigger_name,
time,
event,
tbl_name,
for_each_row,
when_clause,
commands,
} => {
s.append(TK_CREATE, None)?;
if *temporary {
s.append(TK_TEMP, None)?;
}
s.append(TK_TRIGGER, None)?;
if *if_not_exists {
s.append(TK_IF, None)?;
s.append(TK_NOT, None)?;
s.append(TK_EXISTS, None)?;
}
trigger_name.to_tokens(s, context)?;
if let Some(time) = time {
time.to_tokens(s, context)?;
}
event.to_tokens(s, context)?;
s.append(TK_ON, None)?;
tbl_name.to_tokens(s, context)?;
if *for_each_row {
s.append(TK_FOR, None)?;
s.append(TK_EACH, None)?;
s.append(TK_ROW, None)?;
}
if let Some(when_clause) = when_clause {
s.append(TK_WHEN, None)?;
when_clause.to_tokens(s, context)?;
}
s.append(TK_BEGIN, Some("\n"))?;
for command in commands {
command.to_tokens(s, context)?;
s.append(TK_SEMI, Some("\n"))?;
}
s.append(TK_END, None)
}
Self::CreateView {
temporary,
if_not_exists,
view_name,
columns,
select,
} => {
s.append(TK_CREATE, None)?;
if *temporary {
s.append(TK_TEMP, None)?;
}
s.append(TK_VIEW, None)?;
if *if_not_exists {
s.append(TK_IF, None)?;
s.append(TK_NOT, None)?;
s.append(TK_EXISTS, None)?;
}
view_name.to_tokens(s, context)?;
if !columns.is_empty() {
s.append(TK_LP, None)?;
comma(columns, s, context)?;
s.append(TK_RP, None)?;
}
s.append(TK_AS, None)?;
select.to_tokens(s, context)
}
Self::CreateMaterializedView {
if_not_exists,
view_name,
columns,
select,
} => {
s.append(TK_CREATE, None)?;
s.append(TK_MATERIALIZED, None)?;
s.append(TK_VIEW, None)?;
if *if_not_exists {
s.append(TK_IF, None)?;
s.append(TK_NOT, None)?;
s.append(TK_EXISTS, None)?;
}
view_name.to_tokens(s, context)?;
if !columns.is_empty() {
s.append(TK_LP, None)?;
comma(columns, s, context)?;
s.append(TK_RP, None)?;
}
s.append(TK_AS, None)?;
select.to_tokens(s, context)
}
Self::CreateVirtualTable(CreateVirtualTable {
if_not_exists,
tbl_name,
module_name,
args,
}) => {
s.append(TK_CREATE, None)?;
s.append(TK_VIRTUAL, None)?;
s.append(TK_TABLE, None)?;
if *if_not_exists {
s.append(TK_IF, None)?;
s.append(TK_NOT, None)?;
s.append(TK_EXISTS, None)?;
}
tbl_name.to_tokens(s, context)?;
s.append(TK_USING, None)?;
module_name.to_tokens(s, context)?;
s.append(TK_LP, None)?;
if !args.is_empty() {
comma(args, s, context)?;
}
s.append(TK_RP, None)
}
Self::Delete {
with,
tbl_name,
indexed,
where_clause,
returning,
order_by,
limit,
} => {
if let Some(with) = with {
with.to_tokens(s, context)?;
}
s.append(TK_DELETE, None)?;
s.append(TK_FROM, None)?;
tbl_name.to_tokens(s, context)?;
if let Some(indexed) = indexed {
indexed.to_tokens(s, context)?;
}
if let Some(where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
if !returning.is_empty() {
s.append(TK_RETURNING, None)?;
comma(returning, s, context)?;
}
if !order_by.is_empty() {
s.append(TK_ORDER, None)?;
s.append(TK_BY, None)?;
comma(order_by, s, context)?;
}
if let Some(limit) = limit {
limit.to_tokens(s, context)?;
}
Ok(())
}
Self::Detach { name } => {
s.append(TK_DETACH, None)?;
name.to_tokens(s, context)
}
Self::DropIndex {
if_exists,
idx_name,
} => {
s.append(TK_DROP, None)?;
s.append(TK_INDEX, None)?;
if *if_exists {
s.append(TK_IF, None)?;
s.append(TK_EXISTS, None)?;
}
idx_name.to_tokens(s, context)
}
Self::DropTable {
if_exists,
tbl_name,
} => {
s.append(TK_DROP, None)?;
s.append(TK_TABLE, None)?;
if *if_exists {
s.append(TK_IF, None)?;
s.append(TK_EXISTS, None)?;
}
tbl_name.to_tokens(s, context)
}
Self::DropTrigger {
if_exists,
trigger_name,
} => {
s.append(TK_DROP, None)?;
s.append(TK_TRIGGER, None)?;
if *if_exists {
s.append(TK_IF, None)?;
s.append(TK_EXISTS, None)?;
}
trigger_name.to_tokens(s, context)
}
Self::DropView {
if_exists,
view_name,
} => {
s.append(TK_DROP, None)?;
s.append(TK_VIEW, None)?;
if *if_exists {
s.append(TK_IF, None)?;
s.append(TK_EXISTS, None)?;
}
view_name.to_tokens(s, context)
}
Self::Insert {
with,
or_conflict,
tbl_name,
columns,
body,
returning,
} => {
if let Some(with) = with {
with.to_tokens(s, context)?;
}
if let Some(ResolveType::Replace) = or_conflict {
s.append(TK_REPLACE, None)?;
} else {
s.append(TK_INSERT, None)?;
if let Some(or_conflict) = or_conflict {
s.append(TK_OR, None)?;
or_conflict.to_tokens(s, context)?;
}
}
s.append(TK_INTO, None)?;
tbl_name.to_tokens(s, context)?;
if !columns.is_empty() {
s.append(TK_LP, None)?;
comma(columns, s, context)?;
s.append(TK_RP, None)?;
}
body.to_tokens(s, context)?;
if !returning.is_empty() {
s.append(TK_RETURNING, None)?;
comma(returning, s, context)?;
}
Ok(())
}
Self::Pragma { name, body } => {
s.append(TK_PRAGMA, None)?;
name.to_tokens(s, context)?;
if let Some(body) = body {
body.to_tokens(s, context)?;
}
Ok(())
}
Self::Reindex { name } => {
s.append(TK_REINDEX, None)?;
if let Some(name) = name {
name.to_tokens(s, context)?;
}
Ok(())
}
Self::Release { name } => {
s.append(TK_RELEASE, None)?;
name.to_tokens(s, context)
}
Self::Rollback {
tx_name,
savepoint_name,
} => {
s.append(TK_ROLLBACK, None)?;
if let Some(tx_name) = tx_name {
s.append(TK_TRANSACTION, None)?;
tx_name.to_tokens(s, context)?;
}
if let Some(savepoint_name) = savepoint_name {
s.append(TK_TO, None)?;
savepoint_name.to_tokens(s, context)?;
}
Ok(())
}
Self::Savepoint { name } => {
s.append(TK_SAVEPOINT, None)?;
name.to_tokens(s, context)
}
Self::Select(select) => select.to_tokens(s, context),
Self::Update(Update {
with,
or_conflict,
tbl_name,
indexed,
sets,
from,
where_clause,
returning,
order_by,
limit,
}) => {
if let Some(with) = with {
with.to_tokens(s, context)?;
}
s.append(TK_UPDATE, None)?;
if let Some(or_conflict) = or_conflict {
s.append(TK_OR, None)?;
or_conflict.to_tokens(s, context)?;
}
tbl_name.to_tokens(s, context)?;
if let Some(indexed) = indexed {
indexed.to_tokens(s, context)?;
}
s.append(TK_SET, None)?;
comma(sets, s, context)?;
if let Some(from) = from {
s.append(TK_FROM, None)?;
from.to_tokens(s, context)?;
}
if let Some(where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
if !returning.is_empty() {
s.append(TK_RETURNING, None)?;
comma(returning, s, context)?;
}
if !order_by.is_empty() {
s.append(TK_ORDER, None)?;
s.append(TK_BY, None)?;
comma(order_by, s, context)?;
}
if let Some(limit) = limit {
limit.to_tokens(s, context)?;
}
Ok(())
}
Self::Vacuum { name, into } => {
s.append(TK_VACUUM, None)?;
if let Some(ref name) = name {
name.to_tokens(s, context)?;
}
if let Some(ref into) = into {
s.append(TK_INTO, None)?;
into.to_tokens(s, context)?;
}
Ok(())
}
}
}
}
impl_display_for_to_tokens!(Expr);
impl ToTokens for Expr {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Between {
lhs,
not,
start,
end,
} => {
lhs.to_tokens(s, context)?;
if *not {
s.append(TK_NOT, None)?;
}
s.append(TK_BETWEEN, None)?;
start.to_tokens(s, context)?;
s.append(TK_AND, None)?;
end.to_tokens(s, context)
}
Self::Binary(lhs, op, rhs) => {
lhs.to_tokens(s, context)?;
op.to_tokens(s, context)?;
rhs.to_tokens(s, context)
}
Self::Register(reg) => {
// This is for internal use only, not part of SQL syntax
// Use a special notation that won't conflict with SQL
s.append(TK_VARIABLE, Some(&format!("$r{reg}")))
}
Self::Case {
base,
when_then_pairs,
else_expr,
} => {
s.append(TK_CASE, None)?;
if let Some(ref base) = base {
base.to_tokens(s, context)?;
}
for (when, then) in when_then_pairs {
s.append(TK_WHEN, None)?;
when.to_tokens(s, context)?;
s.append(TK_THEN, None)?;
then.to_tokens(s, context)?;
}
if let Some(ref else_expr) = else_expr {
s.append(TK_ELSE, None)?;
else_expr.to_tokens(s, context)?;
}
s.append(TK_END, None)
}
Self::Cast { expr, type_name } => {
s.append(TK_CAST, None)?;
s.append(TK_LP, None)?;
expr.to_tokens(s, context)?;
s.append(TK_AS, None)?;
if let Some(ref type_name) = type_name {
type_name.to_tokens(s, context)?;
}
s.append(TK_RP, None)
}
Self::Collate(expr, collation) => {
expr.to_tokens(s, context)?;
s.append(TK_COLLATE, None)?;
double_quote(collation.as_str(), s)
}
Self::DoublyQualified(db_name, tbl_name, col_name) => {
db_name.to_tokens(s, context)?;
s.append(TK_DOT, None)?;
tbl_name.to_tokens(s, context)?;
s.append(TK_DOT, None)?;
col_name.to_tokens(s, context)
}
Self::Exists(subquery) => {
s.append(TK_EXISTS, None)?;
s.append(TK_LP, None)?;
subquery.to_tokens(s, context)?;
s.append(TK_RP, None)
}
Self::FunctionCall {
name,
distinctness,
args,
order_by,
filter_over,
} => {
name.to_tokens(s, context)?;
s.append(TK_LP, None)?;
if let Some(distinctness) = distinctness {
distinctness.to_tokens(s, context)?;
}
if !args.is_empty() {
comma(args, s, context)?;
}
if !order_by.is_empty() {
s.append(TK_ORDER, None)?;
s.append(TK_BY, None)?;
comma(order_by, s, context)?;
}
s.append(TK_RP, None)?;
filter_over.to_tokens(s, context)?;
Ok(())
}
Self::FunctionCallStar { name, filter_over } => {
name.to_tokens(s, context)?;
s.append(TK_LP, None)?;
s.append(TK_STAR, None)?;
s.append(TK_RP, None)?;
filter_over.to_tokens(s, context)?;
Ok(())
}
Self::Id(id) => id.to_tokens(s, context),
Self::Column { table, column, .. } => {
let (tbl_name, col_name) = context.get_table_and_column_names(*table, *column);
s.append(TK_ID, Some(tbl_name.as_ref()))?;
s.append(TK_DOT, None)?;
s.append(TK_ID, Some(col_name.as_ref()))
}
Self::InList { lhs, not, rhs } => {
lhs.to_tokens(s, context)?;
if *not {
s.append(TK_NOT, None)?;
}
s.append(TK_IN, None)?;
s.append(TK_LP, None)?;
if !rhs.is_empty() {
comma(rhs, s, context)?;
}
s.append(TK_RP, None)
}
Self::InSelect { lhs, not, rhs } => {
lhs.to_tokens(s, context)?;
if *not {
s.append(TK_NOT, None)?;
}
s.append(TK_IN, None)?;
s.append(TK_LP, None)?;
rhs.to_tokens(s, context)?;
s.append(TK_RP, None)
}
Self::InTable {
lhs,
not,
rhs,
args,
} => {
lhs.to_tokens(s, context)?;
if *not {
s.append(TK_NOT, None)?;
}
s.append(TK_IN, None)?;
rhs.to_tokens(s, context)?;
if !args.is_empty() {
s.append(TK_LP, None)?;
comma(args, s, context)?;
s.append(TK_RP, None)?;
}
Ok(())
}
Self::IsNull(sub_expr) => {
sub_expr.to_tokens(s, context)?;
s.append(TK_ISNULL, None)
}
Self::Like {
lhs,
not,
op,
rhs,
escape,
} => {
lhs.to_tokens(s, context)?;
if *not {
s.append(TK_NOT, None)?;
}
op.to_tokens(s, context)?;
rhs.to_tokens(s, context)?;
if let Some(escape) = escape {
s.append(TK_ESCAPE, None)?;
escape.to_tokens(s, context)?;
}
Ok(())
}
Self::Literal(lit) => lit.to_tokens(s, context),
Self::Name(name) => name.to_tokens(s, context),
Self::NotNull(sub_expr) => {
sub_expr.to_tokens(s, context)?;
s.append(TK_NOTNULL, None)
}
Self::Parenthesized(exprs) => {
s.append(TK_LP, None)?;
comma(exprs, s, context)?;
s.append(TK_RP, None)
}
Self::Qualified(qualifier, qualified) => {
qualifier.to_tokens(s, context)?;
s.append(TK_DOT, None)?;
qualified.to_tokens(s, context)
}
Self::Raise(rt, err) => {
s.append(TK_RAISE, None)?;
s.append(TK_LP, None)?;
rt.to_tokens(s, context)?;
if let Some(err) = err {
s.append(TK_COMMA, None)?;
err.to_tokens(s, context)?;
}
s.append(TK_RP, None)
}
Self::RowId { .. } => Ok(()),
Self::Subquery(query) => {
s.append(TK_LP, None)?;
query.to_tokens(s, context)?;
s.append(TK_RP, None)
}
Self::Unary(op, sub_expr) => {
op.to_tokens(s, context)?;
sub_expr.to_tokens(s, context)
}
Self::Variable(var) => match var.chars().next() {
Some(c) if c == '$' || c == '@' || c == '#' || c == ':' => {
s.append(TK_VARIABLE, Some(var))
}
Some(_) => s.append(TK_VARIABLE, Some(&("?".to_owned() + var))),
None => s.append(TK_VARIABLE, Some("?")),
},
}
}
}
impl_display_for_to_tokens!(Literal);
impl ToTokens for Literal {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
match self {
Self::Numeric(ref num) => s.append(TK_FLOAT, Some(num)), // TODO Validate TK_FLOAT
Self::String(ref str) => s.append(TK_STRING, Some(str)),
Self::Blob(ref blob) => s.append(TK_BLOB, Some(blob)),
Self::Keyword(ref str) => s.append(TK_ID, Some(str)), // TODO Validate TK_ID
Self::Null => s.append(TK_NULL, None),
Self::CurrentDate => s.append(TK_CTIME_KW, Some("CURRENT_DATE")),
Self::CurrentTime => s.append(TK_CTIME_KW, Some("CURRENT_TIME")),
Self::CurrentTimestamp => s.append(TK_CTIME_KW, Some("CURRENT_TIMESTAMP")),
}
}
}
impl_display_for_to_tokens!(LikeOperator);
impl ToTokens for LikeOperator {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
TK_LIKE_KW,
Some(match self {
Self::Glob => "GLOB",
Self::Like => "LIKE",
Self::Match => "MATCH",
Self::Regexp => "REGEXP",
}),
)
}
}
impl_display_for_to_tokens!(Operator);
impl ToTokens for Operator {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
match self {
Self::Add => s.append(TK_PLUS, None),
Self::And => s.append(TK_AND, None),
Self::ArrowRight => s.append(TK_PTR, Some("->")),
Self::ArrowRightShift => s.append(TK_PTR, Some("->>")),
Self::BitwiseAnd => s.append(TK_BITAND, None),
Self::BitwiseOr => s.append(TK_BITOR, None),
Self::BitwiseNot => s.append(TK_BITNOT, None),
Self::Concat => s.append(TK_CONCAT, None),
Self::Equals => s.append(TK_EQ, None),
Self::Divide => s.append(TK_SLASH, None),
Self::Greater => s.append(TK_GT, None),
Self::GreaterEquals => s.append(TK_GE, None),
Self::Is => s.append(TK_IS, None),
Self::IsNot => {
s.append(TK_IS, None)?;
s.append(TK_NOT, None)
}
Self::LeftShift => s.append(TK_LSHIFT, None),
Self::Less => s.append(TK_LT, None),
Self::LessEquals => s.append(TK_LE, None),
Self::Modulus => s.append(TK_REM, None),
Self::Multiply => s.append(TK_STAR, None),
Self::NotEquals => s.append(TK_NE, None),
Self::Or => s.append(TK_OR, None),
Self::RightShift => s.append(TK_RSHIFT, None),
Self::Subtract => s.append(TK_MINUS, None),
}
}
}
impl_display_for_to_tokens!(UnaryOperator);
impl ToTokens for UnaryOperator {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
match self {
Self::BitwiseNot => TK_BITNOT,
Self::Negative => TK_MINUS,
Self::Not => TK_NOT,
Self::Positive => TK_PLUS,
},
None,
)
}
}
impl_display_for_to_tokens!(Select);
impl ToTokens for Select {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if let Some(ref with) = self.with {
with.to_tokens(s, context)?;
}
self.body.to_tokens(s, context)?;
if !self.order_by.is_empty() {
s.append(TK_ORDER, None)?;
s.append(TK_BY, None)?;
comma(&self.order_by, s, context)?;
}
if let Some(ref limit) = self.limit {
limit.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(SelectBody);
impl ToTokens for SelectBody {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.select.to_tokens(s, context)?;
for compound in &self.compounds {
compound.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(CompoundSelect);
impl ToTokens for CompoundSelect {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.operator.to_tokens(s, context)?;
self.select.to_tokens(s, context)
}
}
impl_display_for_to_tokens!(CompoundOperator);
impl ToTokens for CompoundOperator {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
match self {
Self::Union => s.append(TK_UNION, None),
Self::UnionAll => {
s.append(TK_UNION, None)?;
s.append(TK_ALL, None)
}
Self::Except => s.append(TK_EXCEPT, None),
Self::Intersect => s.append(TK_INTERSECT, None),
}
}
}
impl_display_for_to_tokens!(OneSelect);
impl ToTokens for OneSelect {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Select {
distinctness,
columns,
from,
where_clause,
group_by,
window_clause,
} => {
s.append(TK_SELECT, None)?;
if let Some(ref distinctness) = distinctness {
distinctness.to_tokens(s, context)?;
}
comma(columns, s, context)?;
if let Some(ref from) = from {
s.append(TK_FROM, None)?;
from.to_tokens(s, context)?;
}
if let Some(ref where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
if let Some(ref group_by) = group_by {
group_by.to_tokens(s, context)?;
}
if !window_clause.is_empty() {
s.append(TK_WINDOW, None)?;
comma(window_clause, s, context)?;
}
Ok(())
}
Self::Values(values) => {
for (i, vals) in values.iter().enumerate() {
if i == 0 {
s.append(TK_VALUES, None)?;
} else {
s.append(TK_COMMA, None)?;
}
s.append(TK_LP, None)?;
comma(vals, s, context)?;
s.append(TK_RP, None)?;
}
Ok(())
}
}
}
}
impl_display_for_to_tokens!(FromClause);
impl ToTokens for FromClause {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.select.as_ref().to_tokens(s, context)?;
for join in &self.joins {
join.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(Distinctness);
impl ToTokens for Distinctness {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
match self {
Self::Distinct => TK_DISTINCT,
Self::All => TK_ALL,
},
None,
)
}
}
impl_display_for_to_tokens!(ResultColumn);
impl ToTokens for ResultColumn {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Expr(expr, alias) => {
expr.to_tokens(s, context)?;
if let Some(alias) = alias {
alias.to_tokens(s, context)?;
}
Ok(())
}
Self::Star => s.append(TK_STAR, None),
Self::TableStar(tbl_name) => {
tbl_name.to_tokens(s, context)?;
s.append(TK_DOT, None)?;
s.append(TK_STAR, None)
}
}
}
}
impl_display_for_to_tokens!(As);
impl ToTokens for As {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::As(ref name) => {
s.append(TK_AS, None)?;
name.to_tokens(s, context)
}
Self::Elided(ref name) => name.to_tokens(s, context),
}
}
}
impl_display_for_to_tokens!(JoinedSelectTable);
impl ToTokens for JoinedSelectTable {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.operator.to_tokens(s, context)?;
self.table.to_tokens(s, context)?;
if let Some(ref constraint) = self.constraint {
constraint.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(SelectTable);
impl ToTokens for SelectTable {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Table(name, alias, indexed) => {
name.to_tokens(s, context)?;
if let Some(alias) = alias {
alias.to_tokens(s, context)?;
}
if let Some(indexed) = indexed {
indexed.to_tokens(s, context)?;
}
Ok(())
}
Self::TableCall(name, exprs, alias) => {
name.to_tokens(s, context)?;
s.append(TK_LP, None)?;
comma(exprs, s, context)?;
s.append(TK_RP, None)?;
if let Some(alias) = alias {
alias.to_tokens(s, context)?;
}
Ok(())
}
Self::Select(select, alias) => {
s.append(TK_LP, None)?;
select.to_tokens(s, context)?;
s.append(TK_RP, None)?;
if let Some(alias) = alias {
alias.to_tokens(s, context)?;
}
Ok(())
}
Self::Sub(from, alias) => {
s.append(TK_LP, None)?;
from.to_tokens(s, context)?;
s.append(TK_RP, None)?;
if let Some(alias) = alias {
alias.to_tokens(s, context)?;
}
Ok(())
}
}
}
}
impl_display_for_to_tokens!(JoinOperator);
impl ToTokens for JoinOperator {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Comma => s.append(TK_COMMA, None),
Self::TypedJoin(join_type) => {
if let Some(ref join_type) = join_type {
join_type.to_tokens(s, context)?;
}
s.append(TK_JOIN, None)
}
}
}
}
impl_display_for_to_tokens!(JoinType);
impl ToTokens for JoinType {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
if self.contains(Self::NATURAL) {
s.append(TK_JOIN_KW, Some("NATURAL"))?;
}
if self.contains(Self::INNER) {
if self.contains(Self::CROSS) {
s.append(TK_JOIN_KW, Some("CROSS"))?;
}
s.append(TK_JOIN_KW, Some("INNER"))?;
} else {
if self.contains(Self::LEFT) {
if self.contains(Self::RIGHT) {
s.append(TK_JOIN_KW, Some("FULL"))?;
} else {
s.append(TK_JOIN_KW, Some("LEFT"))?;
}
} else if self.contains(Self::RIGHT) {
s.append(TK_JOIN_KW, Some("RIGHT"))?;
}
if self.contains(Self::OUTER) {
s.append(TK_JOIN_KW, Some("OUTER"))?;
}
}
Ok(())
}
}
impl_display_for_to_tokens!(JoinConstraint);
impl ToTokens for JoinConstraint {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::On(expr) => {
s.append(TK_ON, None)?;
expr.to_tokens(s, context)
}
Self::Using(col_names) => {
s.append(TK_USING, None)?;
s.append(TK_LP, None)?;
comma(col_names, s, context)?;
s.append(TK_RP, None)
}
}
}
}
impl_display_for_to_tokens!(GroupBy);
impl ToTokens for GroupBy {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
s.append(TK_GROUP, None)?;
s.append(TK_BY, None)?;
comma(&self.exprs, s, context)?;
if let Some(ref having) = self.having {
s.append(TK_HAVING, None)?;
having.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(Name);
impl ToTokens for Name {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
double_quote(self.as_str(), s)
}
}
impl_display_for_to_tokens!(QualifiedName);
impl ToTokens for QualifiedName {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if let Some(ref db_name) = self.db_name {
db_name.to_tokens(s, context)?;
s.append(TK_DOT, None)?;
}
self.name.to_tokens(s, context)?;
if let Some(ref alias) = self.alias {
s.append(TK_AS, None)?;
alias.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(AlterTableBody);
impl ToTokens for AlterTableBody {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::RenameTo(name) => {
s.append(TK_RENAME, None)?;
s.append(TK_TO, None)?;
name.to_tokens(s, context)
}
Self::AddColumn(def) => {
s.append(TK_ADD, None)?;
s.append(TK_COLUMNKW, None)?;
def.to_tokens(s, context)
}
Self::AlterColumn { old, new } => {
s.append(TK_ALTER, None)?;
s.append(TK_COLUMNKW, None)?;
old.to_tokens(s, context)?;
s.append(TK_TO, None)?;
new.to_tokens(s, context)
}
Self::RenameColumn { old, new } => {
s.append(TK_RENAME, None)?;
s.append(TK_COLUMNKW, None)?;
old.to_tokens(s, context)?;
s.append(TK_TO, None)?;
new.to_tokens(s, context)
}
Self::DropColumn(name) => {
s.append(TK_DROP, None)?;
s.append(TK_COLUMNKW, None)?;
name.to_tokens(s, context)
}
}
}
}
impl_display_for_to_tokens!(CreateTableBody);
impl ToTokens for CreateTableBody {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::ColumnsAndConstraints {
columns,
constraints,
options,
} => {
s.append(TK_LP, None)?;
comma(columns, s, context)?;
if !constraints.is_empty() {
s.append(TK_COMMA, None)?;
comma(constraints, s, context)?;
}
s.append(TK_RP, None)?;
if options.contains(TableOptions::WITHOUT_ROWID) {
s.append(TK_WITHOUT, None)?;
s.append(TK_ID, Some("ROWID"))?;
}
if options.contains(TableOptions::STRICT) {
s.append(TK_ID, Some("STRICT"))?;
}
Ok(())
}
Self::AsSelect(select) => {
s.append(TK_AS, None)?;
select.to_tokens(s, context)
}
}
}
}
impl_display_for_to_tokens!(ColumnDefinition);
impl ToTokens for ColumnDefinition {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.col_name.to_tokens(s, context)?;
if let Some(ref col_type) = self.col_type {
col_type.to_tokens(s, context)?;
}
for constraint in &self.constraints {
constraint.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(NamedColumnConstraint);
impl ToTokens for NamedColumnConstraint {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if let Some(ref name) = self.name {
s.append(TK_CONSTRAINT, None)?;
name.to_tokens(s, context)?;
}
self.constraint.to_tokens(s, context)
}
}
impl_display_for_to_tokens!(ColumnConstraint);
impl ToTokens for ColumnConstraint {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::PrimaryKey {
order,
conflict_clause,
auto_increment,
} => {
s.append(TK_PRIMARY, None)?;
s.append(TK_KEY, None)?;
if let Some(order) = order {
order.to_tokens(s, context)?;
}
if let Some(conflict_clause) = conflict_clause {
s.append(TK_ON, None)?;
s.append(TK_CONFLICT, None)?;
conflict_clause.to_tokens(s, context)?;
}
if *auto_increment {
s.append(TK_AUTOINCR, None)?;
}
Ok(())
}
Self::NotNull {
nullable,
conflict_clause,
} => {
if !nullable {
s.append(TK_NOT, None)?;
}
s.append(TK_NULL, None)?;
if let Some(conflict_clause) = conflict_clause {
s.append(TK_ON, None)?;
s.append(TK_CONFLICT, None)?;
conflict_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Unique(conflict_clause) => {
s.append(TK_UNIQUE, None)?;
if let Some(conflict_clause) = conflict_clause {
s.append(TK_ON, None)?;
s.append(TK_CONFLICT, None)?;
conflict_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Check(expr) => {
s.append(TK_CHECK, None)?;
s.append(TK_LP, None)?;
expr.to_tokens(s, context)?;
s.append(TK_RP, None)
}
Self::Default(expr) => {
s.append(TK_DEFAULT, None)?;
expr.to_tokens(s, context)
}
Self::Collate { collation_name } => {
s.append(TK_COLLATE, None)?;
collation_name.to_tokens(s, context)
}
Self::ForeignKey {
clause,
deref_clause,
} => {
s.append(TK_REFERENCES, None)?;
clause.to_tokens(s, context)?;
if let Some(deref_clause) = deref_clause {
deref_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Generated { expr, typ } => {
s.append(TK_AS, None)?;
s.append(TK_LP, None)?;
expr.to_tokens(s, context)?;
s.append(TK_RP, None)?;
if let Some(typ) = typ {
typ.to_tokens(s, context)?;
}
Ok(())
}
}
}
}
impl_display_for_to_tokens!(NamedTableConstraint);
impl ToTokens for NamedTableConstraint {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if let Some(ref name) = self.name {
s.append(TK_CONSTRAINT, None)?;
name.to_tokens(s, context)?;
}
self.constraint.to_tokens(s, context)
}
}
impl_display_for_to_tokens!(TableConstraint);
impl ToTokens for TableConstraint {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::PrimaryKey {
columns,
auto_increment,
conflict_clause,
} => {
s.append(TK_PRIMARY, None)?;
s.append(TK_KEY, None)?;
s.append(TK_LP, None)?;
comma(columns, s, context)?;
if *auto_increment {
s.append(TK_AUTOINCR, None)?;
}
s.append(TK_RP, None)?;
if let Some(conflict_clause) = conflict_clause {
s.append(TK_ON, None)?;
s.append(TK_CONFLICT, None)?;
conflict_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Unique {
columns,
conflict_clause,
} => {
s.append(TK_UNIQUE, None)?;
s.append(TK_LP, None)?;
comma(columns, s, context)?;
s.append(TK_RP, None)?;
if let Some(conflict_clause) = conflict_clause {
s.append(TK_ON, None)?;
s.append(TK_CONFLICT, None)?;
conflict_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Check(expr) => {
s.append(TK_CHECK, None)?;
s.append(TK_LP, None)?;
expr.to_tokens(s, context)?;
s.append(TK_RP, None)
}
Self::ForeignKey {
columns,
clause,
deref_clause,
} => {
s.append(TK_FOREIGN, None)?;
s.append(TK_KEY, None)?;
s.append(TK_LP, None)?;
comma(columns, s, context)?;
s.append(TK_RP, None)?;
s.append(TK_REFERENCES, None)?;
clause.to_tokens(s, context)?;
if let Some(deref_clause) = deref_clause {
deref_clause.to_tokens(s, context)?;
}
Ok(())
}
}
}
}
impl_display_for_to_tokens!(SortOrder);
impl ToTokens for SortOrder {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
match self {
Self::Asc => TK_ASC,
Self::Desc => TK_DESC,
},
None,
)
}
}
impl_display_for_to_tokens!(NullsOrder);
impl ToTokens for NullsOrder {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(TK_NULLS, None)?;
s.append(
match self {
Self::First => TK_FIRST,
Self::Last => TK_LAST,
},
None,
)
}
}
impl_display_for_to_tokens!(ForeignKeyClause);
impl ToTokens for ForeignKeyClause {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.tbl_name.to_tokens(s, context)?;
if !self.columns.is_empty() {
s.append(TK_LP, None)?;
comma(&self.columns, s, context)?;
s.append(TK_RP, None)?;
}
for arg in &self.args {
arg.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(RefArg);
impl ToTokens for RefArg {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::OnDelete(ref action) => {
s.append(TK_ON, None)?;
s.append(TK_DELETE, None)?;
action.to_tokens(s, context)
}
Self::OnInsert(ref action) => {
s.append(TK_ON, None)?;
s.append(TK_INSERT, None)?;
action.to_tokens(s, context)
}
Self::OnUpdate(ref action) => {
s.append(TK_ON, None)?;
s.append(TK_UPDATE, None)?;
action.to_tokens(s, context)
}
Self::Match(ref name) => {
s.append(TK_MATCH, None)?;
name.to_tokens(s, context)
}
}
}
}
impl_display_for_to_tokens!(RefAct);
impl ToTokens for RefAct {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
match self {
Self::SetNull => {
s.append(TK_SET, None)?;
s.append(TK_NULL, None)
}
Self::SetDefault => {
s.append(TK_SET, None)?;
s.append(TK_DEFAULT, None)
}
Self::Cascade => s.append(TK_CASCADE, None),
Self::Restrict => s.append(TK_RESTRICT, None),
Self::NoAction => {
s.append(TK_NO, None)?;
s.append(TK_ACTION, None)
}
}
}
}
impl_display_for_to_tokens!(DeferSubclause);
impl ToTokens for DeferSubclause {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if !self.deferrable {
s.append(TK_NOT, None)?;
}
s.append(TK_DEFERRABLE, None)?;
if let Some(init_deferred) = self.init_deferred {
init_deferred.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(InitDeferredPred);
impl ToTokens for InitDeferredPred {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(TK_INITIALLY, None)?;
s.append(
match self {
Self::InitiallyDeferred => TK_DEFERRED,
Self::InitiallyImmediate => TK_IMMEDIATE,
},
None,
)
}
}
impl_display_for_to_tokens!(IndexedColumn);
impl ToTokens for IndexedColumn {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.col_name.to_tokens(s, context)?;
if let Some(ref collation_name) = self.collation_name {
s.append(TK_COLLATE, None)?;
collation_name.to_tokens(s, context)?;
}
if let Some(order) = self.order {
order.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(Indexed);
impl ToTokens for Indexed {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::IndexedBy(ref name) => {
s.append(TK_INDEXED, None)?;
s.append(TK_BY, None)?;
name.to_tokens(s, context)
}
Self::NotIndexed => {
s.append(TK_NOT, None)?;
s.append(TK_INDEXED, None)
}
}
}
}
impl_display_for_to_tokens!(SortedColumn);
impl ToTokens for SortedColumn {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.expr.to_tokens(s, context)?;
if let Some(ref order) = self.order {
order.to_tokens(s, context)?;
}
if let Some(ref nulls) = self.nulls {
nulls.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(Limit);
impl ToTokens for Limit {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
s.append(TK_LIMIT, None)?;
self.expr.to_tokens(s, context)?;
if let Some(ref offset) = self.offset {
s.append(TK_OFFSET, None)?;
offset.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(InsertBody);
impl ToTokens for InsertBody {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Select(select, upsert) => {
select.to_tokens(s, context)?;
if let Some(upsert) = upsert {
upsert.to_tokens(s, context)?;
}
Ok(())
}
Self::DefaultValues => {
s.append(TK_DEFAULT, None)?;
s.append(TK_VALUES, None)
}
}
}
}
impl_display_for_to_tokens!(Set);
impl ToTokens for Set {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if self.col_names.len() == 1 {
comma(&self.col_names, s, context)?;
} else {
s.append(TK_LP, None)?;
comma(&self.col_names, s, context)?;
s.append(TK_RP, None)?;
}
s.append(TK_EQ, None)?;
self.expr.to_tokens(s, context)
}
}
impl_display_for_to_tokens!(PragmaBody);
impl ToTokens for PragmaBody {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Equals(value) => {
s.append(TK_EQ, None)?;
value.to_tokens(s, context)
}
Self::Call(value) => {
s.append(TK_LP, None)?;
value.to_tokens(s, context)?;
s.append(TK_RP, None)
}
}
}
}
impl_display_for_to_tokens!(TriggerTime);
impl ToTokens for TriggerTime {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
match self {
Self::Before => s.append(TK_BEFORE, None),
Self::After => s.append(TK_AFTER, None),
Self::InsteadOf => {
s.append(TK_INSTEAD, None)?;
s.append(TK_OF, None)
}
}
}
}
impl_display_for_to_tokens!(TriggerEvent);
impl ToTokens for TriggerEvent {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Delete => s.append(TK_DELETE, None),
Self::Insert => s.append(TK_INSERT, None),
Self::Update => s.append(TK_UPDATE, None),
Self::UpdateOf(ref col_names) => {
s.append(TK_UPDATE, None)?;
s.append(TK_OF, None)?;
comma(col_names, s, context)
}
}
}
}
impl_display_for_to_tokens!(TriggerCmd);
impl ToTokens for TriggerCmd {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Update {
or_conflict,
tbl_name,
sets,
from,
where_clause,
} => {
s.append(TK_UPDATE, None)?;
if let Some(or_conflict) = or_conflict {
s.append(TK_OR, None)?;
or_conflict.to_tokens(s, context)?;
}
tbl_name.to_tokens(s, context)?;
s.append(TK_SET, None)?;
comma(sets, s, context)?;
if let Some(from) = from {
s.append(TK_FROM, None)?;
from.to_tokens(s, context)?;
}
if let Some(where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Insert {
or_conflict,
tbl_name,
col_names,
select,
upsert,
returning,
} => {
if let Some(ResolveType::Replace) = or_conflict {
s.append(TK_REPLACE, None)?;
} else {
s.append(TK_INSERT, None)?;
if let Some(or_conflict) = or_conflict {
s.append(TK_OR, None)?;
or_conflict.to_tokens(s, context)?;
}
}
s.append(TK_INTO, None)?;
tbl_name.to_tokens(s, context)?;
if !col_names.is_empty() {
s.append(TK_LP, None)?;
comma(col_names, s, context)?;
s.append(TK_RP, None)?;
}
select.to_tokens(s, context)?;
if let Some(upsert) = upsert {
upsert.to_tokens(s, context)?;
}
if !returning.is_empty() {
s.append(TK_RETURNING, None)?;
comma(returning, s, context)?;
}
Ok(())
}
Self::Delete {
tbl_name,
where_clause,
} => {
s.append(TK_DELETE, None)?;
s.append(TK_FROM, None)?;
tbl_name.to_tokens(s, context)?;
if let Some(where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Select(select) => select.to_tokens(s, context),
}
}
}
impl_display_for_to_tokens!(ResolveType);
impl ToTokens for ResolveType {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
match self {
Self::Rollback => TK_ROLLBACK,
Self::Abort => TK_ABORT,
Self::Fail => TK_FAIL,
Self::Ignore => TK_IGNORE,
Self::Replace => TK_REPLACE,
},
None,
)
}
}
impl_display_for_to_tokens!(With);
impl ToTokens for With {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
s.append(TK_WITH, None)?;
if self.recursive {
s.append(TK_RECURSIVE, None)?;
}
comma(&self.ctes, s, context)
}
}
impl_display_for_to_tokens!(CommonTableExpr);
impl ToTokens for CommonTableExpr {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.tbl_name.to_tokens(s, context)?;
if !self.columns.is_empty() {
s.append(TK_LP, None)?;
comma(&self.columns, s, context)?;
s.append(TK_RP, None)?;
}
s.append(TK_AS, None)?;
match self.materialized {
Materialized::Any => {}
Materialized::Yes => {
s.append(TK_MATERIALIZED, None)?;
}
Materialized::No => {
s.append(TK_NOT, None)?;
s.append(TK_MATERIALIZED, None)?;
}
};
s.append(TK_LP, None)?;
self.select.to_tokens(s, context)?;
s.append(TK_RP, None)
}
}
impl_display_for_to_tokens!(Type);
impl ToTokens for Type {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self.size {
None => s.append(TK_ID, Some(&self.name)),
Some(ref size) => {
s.append(TK_ID, Some(&self.name))?; // TODO check there is no forbidden chars
s.append(TK_LP, None)?;
size.to_tokens(s, context)?;
s.append(TK_RP, None)
}
}
}
}
impl_display_for_to_tokens!(TypeSize);
impl ToTokens for TypeSize {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::MaxSize(size) => size.to_tokens(s, context),
Self::TypeSize(size1, size2) => {
size1.to_tokens(s, context)?;
s.append(TK_COMMA, None)?;
size2.to_tokens(s, context)
}
}
}
}
impl_display_for_to_tokens!(TransactionType);
impl ToTokens for TransactionType {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
match self {
Self::Deferred => TK_DEFERRED,
Self::Immediate => TK_IMMEDIATE,
Self::Exclusive => TK_EXCLUSIVE,
Self::Concurrent => TK_CONCURRENT,
},
None,
)
}
}
impl_display_for_to_tokens!(Upsert);
impl ToTokens for Upsert {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
s.append(TK_ON, None)?;
s.append(TK_CONFLICT, None)?;
if let Some(ref index) = self.index {
index.to_tokens(s, context)?;
}
self.do_clause.to_tokens(s, context)?;
if let Some(ref next) = self.next {
next.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(UpsertIndex);
impl ToTokens for UpsertIndex {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
s.append(TK_LP, None)?;
comma(&self.targets, s, context)?;
s.append(TK_RP, None)?;
if let Some(ref where_clause) = self.where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(UpsertDo);
impl ToTokens for UpsertDo {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Set { sets, where_clause } => {
s.append(TK_DO, None)?;
s.append(TK_UPDATE, None)?;
s.append(TK_SET, None)?;
comma(sets, s, context)?;
if let Some(where_clause) = where_clause {
s.append(TK_WHERE, None)?;
where_clause.to_tokens(s, context)?;
}
Ok(())
}
Self::Nothing => {
s.append(TK_DO, None)?;
s.append(TK_NOTHING, None)
}
}
}
}
impl_display_for_to_tokens!(FunctionTail);
impl ToTokens for FunctionTail {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
if let Some(ref filter_clause) = self.filter_clause {
s.append(TK_FILTER, None)?;
s.append(TK_LP, None)?;
s.append(TK_WHERE, None)?;
filter_clause.to_tokens(s, context)?;
s.append(TK_RP, None)?;
}
if let Some(ref over_clause) = self.over_clause {
s.append(TK_OVER, None)?;
over_clause.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(Over);
impl ToTokens for Over {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::Window(ref window) => window.to_tokens(s, context),
Self::Name(ref name) => name.to_tokens(s, context),
}
}
}
impl_display_for_to_tokens!(WindowDef);
impl ToTokens for WindowDef {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.name.to_tokens(s, context)?;
s.append(TK_AS, None)?;
self.window.to_tokens(s, context)
}
}
impl_display_for_to_tokens!(Window);
impl ToTokens for Window {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
s.append(TK_LP, None)?;
if let Some(ref base) = self.base {
base.to_tokens(s, context)?;
}
if !self.partition_by.is_empty() {
s.append(TK_PARTITION, None)?;
s.append(TK_BY, None)?;
comma(&self.partition_by, s, context)?;
}
if !self.order_by.is_empty() {
s.append(TK_ORDER, None)?;
s.append(TK_BY, None)?;
comma(&self.order_by, s, context)?;
}
if let Some(ref frame_clause) = self.frame_clause {
frame_clause.to_tokens(s, context)?;
}
s.append(TK_RP, None)
}
}
impl_display_for_to_tokens!(FrameClause);
impl ToTokens for FrameClause {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
self.mode.to_tokens(s, context)?;
if let Some(ref end) = self.end {
s.append(TK_BETWEEN, None)?;
self.start.to_tokens(s, context)?;
s.append(TK_AND, None)?;
end.to_tokens(s, context)?;
} else {
self.start.to_tokens(s, context)?;
}
if let Some(ref exclude) = self.exclude {
s.append(TK_EXCLUDE, None)?;
exclude.to_tokens(s, context)?;
}
Ok(())
}
}
impl_display_for_to_tokens!(FrameMode);
impl ToTokens for FrameMode {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
s.append(
match self {
Self::Groups => TK_GROUPS,
Self::Range => TK_RANGE,
Self::Rows => TK_ROWS,
},
None,
)
}
}
impl_display_for_to_tokens!(FrameBound);
impl ToTokens for FrameBound {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
context: &C,
) -> Result<(), S::Error> {
match self {
Self::CurrentRow => {
s.append(TK_CURRENT, None)?;
s.append(TK_ROW, None)
}
Self::Following(value) => {
value.to_tokens(s, context)?;
s.append(TK_FOLLOWING, None)
}
Self::Preceding(value) => {
value.to_tokens(s, context)?;
s.append(TK_PRECEDING, None)
}
Self::UnboundedFollowing => {
s.append(TK_UNBOUNDED, None)?;
s.append(TK_FOLLOWING, None)
}
Self::UnboundedPreceding => {
s.append(TK_UNBOUNDED, None)?;
s.append(TK_PRECEDING, None)
}
}
}
}
impl_display_for_to_tokens!(FrameExclude);
impl ToTokens for FrameExclude {
fn to_tokens<S: TokenStream + ?Sized, C: ToSqlContext>(
&self,
s: &mut S,
_: &C,
) -> Result<(), S::Error> {
match self {
Self::NoOthers => {
s.append(TK_NO, None)?;
s.append(TK_OTHERS, None)
}
Self::CurrentRow => {
s.append(TK_CURRENT, None)?;
s.append(TK_ROW, None)
}
Self::Group => s.append(TK_GROUP, None),
Self::Ties => s.append(TK_TIES, None),
}
}
}
fn comma<I, S: TokenStream + ?Sized, C: ToSqlContext>(
items: I,
s: &mut S,
context: &C,
) -> Result<(), S::Error>
where
I: IntoIterator,
I::Item: ToTokens,
{
s.comma(items, context)
}
// TK_ID: [...] / `...` / "..." / some keywords / non keywords
fn double_quote<S: TokenStream + ?Sized>(name: &str, s: &mut S) -> Result<(), S::Error> {
if name.is_empty() {
return s.append(TK_ID, Some("\"\""));
}
s.append(TK_ID, Some(name))
}