// Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! SQL Abstract Syntax Tree (AST) types mod data_type; mod ddl; mod operator; mod query; mod value; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::fmt; pub use self::data_type::DataType; pub use self::ddl::{ AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, ReferentialAction, TableConstraint, }; pub use self::operator::{BinaryOperator, UnaryOperator}; pub use self::query::{ Cte, Fetch, Join, JoinConstraint, JoinOperator, Offset, OffsetRows, OrderByExpr, Query, Select, SelectItem, SetExpr, SetOperator, TableAlias, TableFactor, TableWithJoins, Top, Values, }; pub use self::value::{DateTimeField, Value}; struct DisplaySeparated<'a, T> where T: fmt::Display, { slice: &'a [T], sep: &'static str, } impl<'a, T> fmt::Display for DisplaySeparated<'a, T> where T: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut delim = ""; for t in self.slice { write!(f, "{}", delim)?; delim = self.sep; write!(f, "{}", t)?; } Ok(()) } } fn display_separated<'a, T>(slice: &'a [T], sep: &'static str) -> DisplaySeparated<'a, T> where T: fmt::Display, { DisplaySeparated { slice, sep } } fn display_comma_separated(slice: &[T]) -> DisplaySeparated<'_, T> where T: fmt::Display, { DisplaySeparated { slice, sep: ", " } } /// An identifier, decomposed into its value or character data and the quote style. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ident { /// The value of the identifier without quotes. pub value: String, /// The starting quote if any. Valid quote characters are the single quote, /// double quote, backtick, and opening square bracket. pub quote_style: Option, } impl Ident { /// Create a new identifier with the given value and no quotes. pub fn new(value: S) -> Self where S: Into, { Ident { value: value.into(), quote_style: None, } } /// Create a new quoted identifier with the given quote and value. This function /// panics if the given quote is not a valid quote character. pub fn with_quote(quote: char, value: S) -> Self where S: Into, { assert!(quote == '\'' || quote == '"' || quote == '`' || quote == '['); Ident { value: value.into(), quote_style: Some(quote), } } } impl From<&str> for Ident { fn from(value: &str) -> Self { Ident { value: value.to_string(), quote_style: None, } } } impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.quote_style { Some(q) if q == '"' || q == '\'' || q == '`' => write!(f, "{}{}{}", q, self.value, q), Some(q) if q == '[' => write!(f, "[{}]", self.value), None => f.write_str(&self.value), _ => panic!("unexpected quote style"), } } } /// A name of a table, view, custom type, etc., possibly multi-part, i.e. db.schema.obj #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ObjectName(pub Vec); impl fmt::Display for ObjectName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", display_separated(&self.0, ".")) } } /// An SQL expression of any type. /// /// The parser does not distinguish between expressions of different types /// (e.g. boolean vs string), so the caller must handle expressions of /// inappropriate type, like `WHERE 1` or `SELECT 1=1`, as necessary. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Expr { /// Identifier e.g. table name or column name Identifier(Ident), /// Unqualified wildcard (`*`). SQL allows this in limited contexts, such as: /// - right after `SELECT` (which is represented as a [SelectItem::Wildcard] instead) /// - or as part of an aggregate function, e.g. `COUNT(*)`, /// /// ...but we currently also accept it in contexts where it doesn't make /// sense, such as `* + *` Wildcard, /// Qualified wildcard, e.g. `alias.*` or `schema.table.*`. /// (Same caveats apply to `QualifiedWildcard` as to `Wildcard`.) QualifiedWildcard(Vec), /// Multi-part identifier, e.g. `table_alias.column` or `schema.table.col` CompoundIdentifier(Vec), /// `IS NULL` expression IsNull(Box), /// `IS NOT NULL` expression IsNotNull(Box), /// `[ NOT ] IN (val1, val2, ...)` InList { expr: Box, list: Vec, negated: bool, }, /// `[ NOT ] IN (SELECT ...)` InSubquery { expr: Box, subquery: Box, negated: bool, }, /// ` [ NOT ] BETWEEN AND ` Between { expr: Box, negated: bool, low: Box, high: Box, }, /// Binary operation e.g. `1 + 1` or `foo > bar` BinaryOp { left: Box, op: BinaryOperator, right: Box, }, /// Unary operation e.g. `NOT foo` UnaryOp { op: UnaryOperator, expr: Box }, /// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` Cast { expr: Box, data_type: DataType, }, Extract { field: DateTimeField, expr: Box, }, /// `expr COLLATE collation` Collate { expr: Box, collation: ObjectName, }, /// Nested expression e.g. `(foo > bar)` or `(1)` Nested(Box), /// A literal value, such as string, number, date or NULL Value(Value), /// A constant of form ` 'value'`. /// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`), /// as well as constants of other types (a non-standard PostgreSQL extension). TypedString { data_type: DataType, value: String }, /// Scalar function call e.g. `LEFT(foo, 5)` Function(Function), /// `CASE [] WHEN THEN ... [ELSE ] END` /// /// Note we only recognize a complete single expression as ``, /// not `< 0` nor `1, 2, 3` as allowed in a `` per /// Case { operand: Option>, conditions: Vec, results: Vec, else_result: Option>, }, /// An exists expression `EXISTS(SELECT ...)`, used in expressions like /// `WHERE EXISTS (SELECT ...)`. Exists(Box), /// A parenthesized subquery `(SELECT ...)`, used in expression like /// `SELECT (subquery) AS x` or `WHERE (subquery) = x` Subquery(Box), /// The `LISTAGG` function `SELECT LISTAGG(...) WITHIN GROUP (ORDER BY ...)` ListAgg(ListAgg), } impl fmt::Display for Expr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Expr::Identifier(s) => write!(f, "{}", s), Expr::Wildcard => f.write_str("*"), Expr::QualifiedWildcard(q) => write!(f, "{}.*", display_separated(q, ".")), Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")), Expr::IsNull(ast) => write!(f, "{} IS NULL", ast), Expr::IsNotNull(ast) => write!(f, "{} IS NOT NULL", ast), Expr::InList { expr, list, negated, } => write!( f, "{} {}IN ({})", expr, if *negated { "NOT " } else { "" }, display_comma_separated(list) ), Expr::InSubquery { expr, subquery, negated, } => write!( f, "{} {}IN ({})", expr, if *negated { "NOT " } else { "" }, subquery ), Expr::Between { expr, negated, low, high, } => write!( f, "{} {}BETWEEN {} AND {}", expr, if *negated { "NOT " } else { "" }, low, high ), Expr::BinaryOp { left, op, right } => write!(f, "{} {} {}", left, op, right), Expr::UnaryOp { op, expr } => write!(f, "{} {}", op, expr), Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type), Expr::Extract { field, expr } => write!(f, "EXTRACT({} FROM {})", field, expr), Expr::Collate { expr, collation } => write!(f, "{} COLLATE {}", expr, collation), Expr::Nested(ast) => write!(f, "({})", ast), Expr::Value(v) => write!(f, "{}", v), Expr::TypedString { data_type, value } => { write!(f, "{}", data_type)?; write!(f, " '{}'", &value::escape_single_quote_string(value)) } Expr::Function(fun) => write!(f, "{}", fun), Expr::Case { operand, conditions, results, else_result, } => { f.write_str("CASE")?; if let Some(operand) = operand { write!(f, " {}", operand)?; } for (c, r) in conditions.iter().zip(results) { write!(f, " WHEN {} THEN {}", c, r)?; } if let Some(else_result) = else_result { write!(f, " ELSE {}", else_result)?; } f.write_str(" END") } Expr::Exists(s) => write!(f, "EXISTS ({})", s), Expr::Subquery(s) => write!(f, "({})", s), Expr::ListAgg(listagg) => write!(f, "{}", listagg), } } } /// A window specification (i.e. `OVER (PARTITION BY .. ORDER BY .. etc.)`) #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct WindowSpec { pub partition_by: Vec, pub order_by: Vec, pub window_frame: Option, } impl fmt::Display for WindowSpec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut delim = ""; if !self.partition_by.is_empty() { delim = " "; write!( f, "PARTITION BY {}", display_comma_separated(&self.partition_by) )?; } if !self.order_by.is_empty() { f.write_str(delim)?; delim = " "; write!(f, "ORDER BY {}", display_comma_separated(&self.order_by))?; } if let Some(window_frame) = &self.window_frame { if let Some(end_bound) = &window_frame.end_bound { f.write_str(delim)?; write!( f, "{} BETWEEN {} AND {}", window_frame.units, window_frame.start_bound, end_bound )?; } else { f.write_str(delim)?; write!(f, "{} {}", window_frame.units, window_frame.start_bound)?; } } Ok(()) } } /// Specifies the data processed by a window function, e.g. /// `RANGE UNBOUNDED PRECEDING` or `ROWS BETWEEN 5 PRECEDING AND CURRENT ROW`. /// /// Note: The parser does not validate the specified bounds; the caller should /// reject invalid bounds like `ROWS UNBOUNDED FOLLOWING` before execution. #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct WindowFrame { pub units: WindowFrameUnits, pub start_bound: WindowFrameBound, /// The right bound of the `BETWEEN .. AND` clause. The end bound of `None` /// indicates the shorthand form (e.g. `ROWS 1 PRECEDING`), which must /// behave the same as `end_bound = WindowFrameBound::CurrentRow`. pub end_bound: Option, // TBD: EXCLUDE } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum WindowFrameUnits { Rows, Range, Groups, } impl fmt::Display for WindowFrameUnits { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match self { WindowFrameUnits::Rows => "ROWS", WindowFrameUnits::Range => "RANGE", WindowFrameUnits::Groups => "GROUPS", }) } } /// Specifies [WindowFrame]'s `start_bound` and `end_bound` #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum WindowFrameBound { /// `CURRENT ROW` CurrentRow, /// ` PRECEDING` or `UNBOUNDED PRECEDING` Preceding(Option), /// ` FOLLOWING` or `UNBOUNDED FOLLOWING`. Following(Option), } impl fmt::Display for WindowFrameBound { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { WindowFrameBound::CurrentRow => f.write_str("CURRENT ROW"), WindowFrameBound::Preceding(None) => f.write_str("UNBOUNDED PRECEDING"), WindowFrameBound::Following(None) => f.write_str("UNBOUNDED FOLLOWING"), WindowFrameBound::Preceding(Some(n)) => write!(f, "{} PRECEDING", n), WindowFrameBound::Following(Some(n)) => write!(f, "{} FOLLOWING", n), } } } /// A top-level statement (SELECT, INSERT, CREATE, etc.) #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Statement { /// SELECT Query(Box), /// INSERT Insert { /// TABLE table_name: ObjectName, /// COLUMNS columns: Vec, /// A SQL query that specifies what to insert source: Box, }, Copy { /// TABLE table_name: ObjectName, /// COLUMNS columns: Vec, /// VALUES a vector of values to be copied values: Vec>, }, /// UPDATE Update { /// TABLE table_name: ObjectName, /// Column assignments assignments: Vec, /// WHERE selection: Option, }, /// DELETE Delete { /// FROM table_name: ObjectName, /// WHERE selection: Option, }, /// CREATE VIEW CreateView { /// View name name: ObjectName, columns: Vec, query: Box, materialized: bool, with_options: Vec, }, /// CREATE TABLE CreateTable { /// Table name name: ObjectName, /// Optional schema columns: Vec, constraints: Vec, with_options: Vec, if_not_exists: bool, external: bool, file_format: Option, location: Option, query: Option>, without_rowid: bool, }, /// SQLite's `CREATE VIRTUAL TABLE .. USING ()` CreateVirtualTable { name: ObjectName, if_not_exists: bool, module_name: Ident, module_args: Vec, }, /// CREATE INDEX CreateIndex { /// index name name: ObjectName, table_name: ObjectName, columns: Vec, unique: bool, if_not_exists: bool, }, /// ALTER TABLE AlterTable { /// Table name name: ObjectName, operation: AlterTableOperation, }, /// DROP Drop { /// The type of the object to drop: TABLE, VIEW, etc. object_type: ObjectType, /// An optional `IF EXISTS` clause. (Non-standard.) if_exists: bool, /// One or more objects to drop. (ANSI SQL requires exactly one.) names: Vec, /// Whether `CASCADE` was specified. This will be `false` when /// `RESTRICT` or no drop behavior at all was specified. cascade: bool, }, /// SET /// /// Note: this is not a standard SQL statement, but it is supported by at /// least MySQL and PostgreSQL. Not all MySQL-specific syntatic forms are /// supported yet. SetVariable { local: bool, variable: Ident, value: SetVariableValue, }, /// SHOW /// /// Note: this is a PostgreSQL-specific statement. ShowVariable { variable: Ident }, /// SHOW COLUMNS /// /// Note: this is a MySQL-specific statement. ShowColumns { extended: bool, full: bool, table_name: ObjectName, filter: Option, }, /// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...` StartTransaction { modes: Vec }, /// `SET TRANSACTION ...` SetTransaction { modes: Vec }, /// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` Commit { chain: bool }, /// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` Rollback { chain: bool }, /// CREATE SCHEMA CreateSchema { schema_name: ObjectName }, /// ASSERT [AS ] Assert { condition: Expr, // AS or , separator: String, message: Option, }, } impl fmt::Display for Statement { // Clippy thinks this function is too complicated, but it is painful to // split up without extracting structs for each `Statement` variant. #[allow(clippy::cognitive_complexity)] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Statement::Query(s) => write!(f, "{}", s), Statement::Insert { table_name, columns, source, } => { write!(f, "INSERT INTO {} ", table_name)?; if !columns.is_empty() { write!(f, "({}) ", display_comma_separated(columns))?; } write!(f, "{}", source) } Statement::Copy { table_name, columns, values, } => { write!(f, "COPY {}", table_name)?; if !columns.is_empty() { write!(f, " ({})", display_comma_separated(columns))?; } write!(f, " FROM stdin; ")?; if !values.is_empty() { writeln!(f)?; let mut delim = ""; for v in values { write!(f, "{}", delim)?; delim = "\t"; if let Some(v) = v { write!(f, "{}", v)?; } else { write!(f, "\\N")?; } } } write!(f, "\n\\.") } Statement::Update { table_name, assignments, selection, } => { write!(f, "UPDATE {}", table_name)?; if !assignments.is_empty() { write!(f, " SET ")?; write!(f, "{}", display_comma_separated(assignments))?; } if let Some(selection) = selection { write!(f, " WHERE {}", selection)?; } Ok(()) } Statement::Delete { table_name, selection, } => { write!(f, "DELETE FROM {}", table_name)?; if let Some(selection) = selection { write!(f, " WHERE {}", selection)?; } Ok(()) } Statement::CreateView { name, columns, query, materialized, with_options, } => { write!(f, "CREATE")?; if *materialized { write!(f, " MATERIALIZED")?; } write!(f, " VIEW {}", name)?; if !with_options.is_empty() { write!(f, " WITH ({})", display_comma_separated(with_options))?; } if !columns.is_empty() { write!(f, " ({})", display_comma_separated(columns))?; } write!(f, " AS {}", query) } Statement::CreateTable { name, columns, constraints, with_options, if_not_exists, external, file_format, location, query, without_rowid, } => { // We want to allow the following options // Empty column list, allowed by PostgreSQL: // `CREATE TABLE t ()` // No columns provided for CREATE TABLE AS: // `CREATE TABLE t AS SELECT a from t2` // Columns provided for CREATE TABLE AS: // `CREATE TABLE t (a INT) AS SELECT a from t2` write!( f, "CREATE {external}TABLE {if_not_exists}{name}", external = if *external { "EXTERNAL " } else { "" }, if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, name = name, )?; if !columns.is_empty() || !constraints.is_empty() { write!(f, " ({}", display_comma_separated(columns))?; if !columns.is_empty() && !constraints.is_empty() { write!(f, ", ")?; } write!(f, "{})", display_comma_separated(constraints))?; } else if query.is_none() { // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens write!(f, " ()")?; } // Only for SQLite if *without_rowid { write!(f, " WITHOUT ROWID")?; } if *external { write!( f, " STORED AS {} LOCATION '{}'", file_format.as_ref().unwrap(), location.as_ref().unwrap() )?; } if !with_options.is_empty() { write!(f, " WITH ({})", display_comma_separated(with_options))?; } if let Some(query) = query { write!(f, " AS {}", query)?; } Ok(()) } Statement::CreateVirtualTable { name, if_not_exists, module_name, module_args, } => { write!( f, "CREATE VIRTUAL TABLE {if_not_exists}{name} USING {module_name}", if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, name = name, module_name = module_name )?; if !module_args.is_empty() { write!(f, " ({})", display_comma_separated(module_args))?; } Ok(()) } Statement::CreateIndex { name, table_name, columns, unique, if_not_exists, } => { write!( f, "CREATE{}INDEX{}{} ON {}({}", if *unique { " UNIQUE " } else { " " }, if *if_not_exists { " IF NOT EXISTS " } else { " " }, name, table_name, display_separated(columns, ",") )?; write!(f, ");") } Statement::AlterTable { name, operation } => { write!(f, "ALTER TABLE {} {}", name, operation) } Statement::Drop { object_type, if_exists, names, cascade, } => write!( f, "DROP {}{} {}{}", object_type, if *if_exists { " IF EXISTS" } else { "" }, display_comma_separated(names), if *cascade { " CASCADE" } else { "" }, ), Statement::SetVariable { local, variable, value, } => { f.write_str("SET ")?; if *local { f.write_str("LOCAL ")?; } write!(f, "{} = {}", variable, value) } Statement::ShowVariable { variable } => write!(f, "SHOW {}", variable), Statement::ShowColumns { extended, full, table_name, filter, } => { f.write_str("SHOW ")?; if *extended { f.write_str("EXTENDED ")?; } if *full { f.write_str("FULL ")?; } write!(f, "COLUMNS FROM {}", table_name)?; if let Some(filter) = filter { write!(f, " {}", filter)?; } Ok(()) } Statement::StartTransaction { modes } => { write!(f, "START TRANSACTION")?; if !modes.is_empty() { write!(f, " {}", display_comma_separated(modes))?; } Ok(()) } Statement::SetTransaction { modes } => { write!(f, "SET TRANSACTION")?; if !modes.is_empty() { write!(f, " {}", display_comma_separated(modes))?; } Ok(()) } Statement::Commit { chain } => { write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" },) } Statement::Rollback { chain } => { write!(f, "ROLLBACK{}", if *chain { " AND CHAIN" } else { "" },) } Statement::CreateSchema { schema_name } => write!(f, "CREATE SCHEMA {}", schema_name), Statement::Assert { condition, separator, message, } => { write!(f, "ASSERT {}", condition)?; if let Some(m) = message { write!(f, " {} {}", separator, m)?; } Ok(()) } } } } /// SQL assignment `foo = expr` as used in SQLUpdate #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Assignment { pub id: Ident, pub value: Expr, } impl fmt::Display for Assignment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} = {}", self.id, self.value) } } /// A function call #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Function { pub name: ObjectName, pub args: Vec, pub over: Option, // aggregate functions may specify eg `COUNT(DISTINCT x)` pub distinct: bool, } impl fmt::Display for Function { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{}({}{})", self.name, if self.distinct { "DISTINCT " } else { "" }, display_comma_separated(&self.args), )?; if let Some(o) = &self.over { write!(f, " OVER ({})", o)?; } Ok(()) } } /// External table's available file format #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FileFormat { TEXTFILE, SEQUENCEFILE, ORC, PARQUET, AVRO, RCFILE, JSONFILE, } impl fmt::Display for FileFormat { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::FileFormat::*; f.write_str(match self { TEXTFILE => "TEXTFILE", SEQUENCEFILE => "SEQUENCEFILE", ORC => "ORC", PARQUET => "PARQUET", AVRO => "AVRO", RCFILE => "RCFILE", JSONFILE => "TEXTFILE", }) } } /// A `LISTAGG` invocation `LISTAGG( [ DISTINCT ] [, ] [ON OVERFLOW ] ) ) /// [ WITHIN GROUP (ORDER BY [, ...] ) ]` #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ListAgg { pub distinct: bool, pub expr: Box, pub separator: Option>, pub on_overflow: Option, pub within_group: Vec, } impl fmt::Display for ListAgg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "LISTAGG({}{}", if self.distinct { "DISTINCT " } else { "" }, self.expr )?; if let Some(separator) = &self.separator { write!(f, ", {}", separator)?; } if let Some(on_overflow) = &self.on_overflow { write!(f, "{}", on_overflow)?; } write!(f, ")")?; if !self.within_group.is_empty() { write!( f, " WITHIN GROUP (ORDER BY {})", display_comma_separated(&self.within_group) )?; } Ok(()) } } /// The `ON OVERFLOW` clause of a LISTAGG invocation #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ListAggOnOverflow { /// `ON OVERFLOW ERROR` Error, /// `ON OVERFLOW TRUNCATE [ ] WITH[OUT] COUNT` Truncate { filler: Option>, with_count: bool, }, } impl fmt::Display for ListAggOnOverflow { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, " ON OVERFLOW")?; match self { ListAggOnOverflow::Error => write!(f, " ERROR"), ListAggOnOverflow::Truncate { filler, with_count } => { write!(f, " TRUNCATE")?; if let Some(filler) = filler { write!(f, " {}", filler)?; } if *with_count { write!(f, " WITH")?; } else { write!(f, " WITHOUT")?; } write!(f, " COUNT") } } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ObjectType { Table, View, Index, Schema, } impl fmt::Display for ObjectType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match self { ObjectType::Table => "TABLE", ObjectType::View => "VIEW", ObjectType::Index => "INDEX", ObjectType::Schema => "SCHEMA", }) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct SqlOption { pub name: Ident, pub value: Value, } impl fmt::Display for SqlOption { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} = {}", self.name, self.value) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TransactionMode { AccessMode(TransactionAccessMode), IsolationLevel(TransactionIsolationLevel), } impl fmt::Display for TransactionMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use TransactionMode::*; match self { AccessMode(access_mode) => write!(f, "{}", access_mode), IsolationLevel(iso_level) => write!(f, "ISOLATION LEVEL {}", iso_level), } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TransactionAccessMode { ReadOnly, ReadWrite, } impl fmt::Display for TransactionAccessMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use TransactionAccessMode::*; f.write_str(match self { ReadOnly => "READ ONLY", ReadWrite => "READ WRITE", }) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TransactionIsolationLevel { ReadUncommitted, ReadCommitted, RepeatableRead, Serializable, } impl fmt::Display for TransactionIsolationLevel { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use TransactionIsolationLevel::*; f.write_str(match self { ReadUncommitted => "READ UNCOMMITTED", ReadCommitted => "READ COMMITTED", RepeatableRead => "REPEATABLE READ", Serializable => "SERIALIZABLE", }) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ShowStatementFilter { Like(String), Where(Expr), } impl fmt::Display for ShowStatementFilter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use ShowStatementFilter::*; match self { Like(pattern) => write!(f, "LIKE '{}'", value::escape_single_quote_string(pattern)), Where(expr) => write!(f, "WHERE {}", expr), } } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum SetVariableValue { Ident(Ident), Literal(Value), } impl fmt::Display for SetVariableValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use SetVariableValue::*; match self { Ident(ident) => write!(f, "{}", ident), Literal(literal) => write!(f, "{}", literal), } } }