From db5c9c2145c7d133911202d505237c2eefeb9b71 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Fri, 3 Sep 2021 11:46:41 -0400 Subject: [PATCH 01/11] moving Statement so it holds structs --- src/ast/data_type.rs | 12 +- src/ast/mod.rs | 4 +- src/ast/statement/display.rs | 576 +++++++++++++++++++++++++++++++++++ src/ast/statement/mod.rs | 379 +++++++++++++++++++++++ 4 files changed, 966 insertions(+), 5 deletions(-) create mode 100644 src/ast/statement/display.rs create mode 100644 src/ast/statement/mod.rs diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 96071c228..904f70279 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -36,7 +36,7 @@ pub enum DataType { /// Variable-length binary type e.g. VARBINARY(10) Varbinary(u64), /// Large binary object e.g. BLOB(1000) - Blob(u64), + Blob(Option), /// Decimal type with optional precision and scale e.g. DECIMAL(10,2) Decimal(Option, Option), /// Floating point with optional precision e.g. FLOAT(8) @@ -88,7 +88,13 @@ impl fmt::Display for DataType { DataType::Clob(size) => write!(f, "CLOB({})", size), DataType::Binary(size) => write!(f, "BINARY({})", size), DataType::Varbinary(size) => write!(f, "VARBINARY({})", size), - DataType::Blob(size) => write!(f, "BLOB({})", size), + DataType::Blob(size) => { + if let Some(size) = size { + write!(f, "BLOB({})", size) + } else { + write!(f, "BLOB") + } + } DataType::Decimal(precision, scale) => { if let Some(scale) = scale { write!(f, "NUMERIC({},{})", precision.unwrap(), scale) @@ -101,7 +107,7 @@ impl fmt::Display for DataType { DataType::SmallInt(zerofill) => { format_type_with_optional_length(f, "SMALLINT", zerofill) } - DataType::Int(zerofill) => format_type_with_optional_length(f, "INT", zerofill), + DataType::Int(zerofill) => format_type_with_optional_length(f, "INTEGER", zerofill), DataType::BigInt(zerofill) => format_type_with_optional_length(f, "BIGINT", zerofill), DataType::Real => write!(f, "REAL"), DataType::Double => write!(f, "DOUBLE"), diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a3d52d777..c12ed3066 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -17,7 +17,7 @@ mod ddl; mod operator; mod query; mod value; - +mod statement; #[cfg(not(feature = "std"))] use alloc::{ boxed::Box, @@ -778,7 +778,7 @@ pub enum Statement { 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::Explain { diff --git a/src/ast/statement/display.rs b/src/ast/statement/display.rs new file mode 100644 index 000000000..6d199a41e --- /dev/null +++ b/src/ast/statement/display.rs @@ -0,0 +1,576 @@ +use super::*; + +impl fmt::Display for Statement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Statement::*; + match self { + Analyze(x) => write!(f, "{};", x), + Truncate(x) => write!(f, "{};", x), + Explain(x) => write!(f, "{};", x), + Query(x) => write!(f, "{};", x), + Msck(x) => write!(f, "{};", x), + Insert(x) => write!(f, "{};", x), + Directory(x) => write!(f, "{};", x), + Copy(x) => write!(f, "{};", x), + Update(x) => write!(f, "{};", x), + Delete(x) => write!(f, "{};", x), + CreateView(x) => write!(f, "{};", x), + CreateTable(x) => write!(f, "{};", x), + CreateVirtualTable(x) => write!(f, "{};", x), + CreateIndex(x) => write!(f, "{};", x), + AlterTable(x) => write!(f, "{};", x), + SetVariable(x) => write!(f, "{};", x), + ShowVariable(x) => write!(f, "{};", x), + ShowCreate(x) => write!(f, "{};", x), + ShowColumns(x) => write!(f, "{};", x), + StartTransaction(x) => write!(f, "{};", x), + SetTransaction(x) => write!(f, "{};", x), + Rollback(x) => write!(f, "{};", x), + Drop(x) => write!(f, "{};", x), + Commit(x) => write!(f, "{};", x), + CreateSchema(x) => write!(f, "{};", x), + CreateDatabase(x) => write!(f, "{};", x), + Assert(x) => write!(f, "{};", x), + Deallocate(x) => write!(f, "{};", x), + Execute(x) => write!(f, "{};", x), + Prepare(x) => write!(f, "{};", x), + } + } +} +impl fmt::Display for Explain { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "EXPLAIN ")?; + + if self.analyze { + write!(f, "ANALYZE ")?; + } + + if self.verbose { + write!(f, "VERBOSE ")?; + } + + write!(f, "{}", self.statement) + } +} + +impl fmt::Display for Directory { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "INSERT{overwrite}{local} DIRECTORY '{path}'", + overwrite = if self.overwrite { " OVERWRITE" } else { "" }, + local = if self.local { " LOCAL" } else { "" }, + path = self.path + )?; + if let Some(ref ff) = self.file_format { + write!(f, " STORED AS {}", ff)? + } + write!(f, " {}", self.source) + } +} + +impl fmt::Display for Msck { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "MSCK {repair}TABLE {table}", + repair = if self.repair { "REPAIR " } else { "" }, + table = self.table_name + )?; + if let Some(ref pa) = self.partition_action { + write!(f, " {}", pa)?; + } + Ok(()) + } +} + +impl fmt::Display for Truncate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "TRUNCATE TABLE {}", self.table_name)?; + if let Some(ref parts) = self.partitions { + if !parts.is_empty() { + write!(f, " PARTITION ({})", display_comma_separated(parts))?; + } + } + Ok(()) + } +} + +impl fmt::Display for Analyze { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ANALYZE TABLE {}", self.table_name)?; + if let Some(ref parts) = self.partitions { + if !parts.is_empty() { + write!(f, " PARTITION ({})", display_comma_separated(parts))?; + } + } + + if self.compute_statistics { + write!(f, " COMPUTE STATISTICS")?; + } + if self.noscan { + write!(f, " NOSCAN")?; + } + if self.cache_metadata { + write!(f, " CACHE METADATA")?; + } + if self.for_columns { + write!(f, " FOR COLUMNS")?; + if !self.columns.is_empty() { + write!(f, " {}", display_comma_separated(&self.columns))?; + } + } + Ok(()) + } +} + +impl fmt::Display for Insert { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(action) = &self.or { + write!(f, "INSERT OR {} INTO {} ", action, self.table_name)?; + } else { + write!( + f, + "INSERT {act}{tbl} {table_name} ", + table_name = self.table_name, + act = if self.overwrite { "OVERWRITE" } else { "INTO" }, + tbl = if self.table { " TABLE" } else { "" } + )?; + } + if !self.columns.is_empty() { + write!(f, "({}) ", display_comma_separated(&self.columns))?; + } + if let Some(ref parts) = self.partitioned { + if !parts.is_empty() { + write!(f, "PARTITION ({}) ", display_comma_separated(parts))?; + } + } + if !self.after_columns.is_empty() { + write!(f, "({}) ", display_comma_separated(&self.after_columns))?; + } + write!(f, "{}", self.source) + } +} + +impl fmt::Display for Copy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "COPY {}", self.table_name)?; + if !self.columns.is_empty() { + write!(f, " ({})", display_comma_separated(&self.columns))?; + } + write!(f, " FROM stdin; ")?; + if !self.values.is_empty() { + writeln!(f)?; + let mut delim = ""; + for v in &self.values { + write!(f, "{}", delim)?; + delim = "\t"; + if let Some(v) = v { + write!(f, "{}", v)?; + } else { + write!(f, "\\N")?; + } + } + } + write!(f, "\n\\.") + } +} + +impl fmt::Display for Update { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "UPDATE {}", self.table_name)?; + if !self.assignments.is_empty() { + write!(f, " SET {}", display_comma_separated(&self.assignments))?; + } + if let Some(ref selection) = self.selection { + write!(f, " WHERE {}", selection)?; + } + Ok(()) + } +} + +impl fmt::Display for Delete { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DELETE FROM {}", self.table_name)?; + if let Some(ref selection) = self.selection { + write!(f, " WHERE {}", selection)?; + } + Ok(()) + } +} + +impl fmt::Display for CreateDatabase { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "CREATE")?; + if self.if_not_exists { + write!(f, " IF NOT EXISTS")?; + } + write!(f, " {}", self.db_name)?; + if let Some(ref l) = self.location { + write!(f, " LOCATION '{}'", l)?; + } + if let Some(ref ml) = self.managed_location { + write!(f, " MANAGEDLOCATION '{}'", ml)?; + } + Ok(()) + } +} + +impl fmt::Display for CreateView { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "CREATE {or_replace}{materialized}VIEW {name}", + or_replace = if self.or_replace { "OR REPLACE " } else { "" }, + materialized = if self.materialized { + "MATERIALIZED " + } else { + "" + }, + name = self.name + )?; + if !self.with_options.is_empty() { + write!(f, " WITH ({})", display_comma_separated(&self.with_options))?; + } + if !self.columns.is_empty() { + write!(f, " ({})", display_comma_separated(&self.columns))?; + } + write!(f, " AS {}", self.query) + } +} + +impl fmt::Display for CreateTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ref name = self.name; + let ref columns = self.columns; + let ref constraints = self.constraints; + let ref table_properties = self.table_properties; + let ref with_options = self.with_options; + let ref or_replace = self.or_replace; + let ref if_not_exists = self.if_not_exists; + let ref hive_distribution = self.hive_distribution; + let ref hive_formats = self.hive_formats; + let ref external = self.external; + let ref temporary = self.temporary; + let ref file_format = self.file_format; + let ref location = self.location; + let ref query = self.query; + let ref without_rowid = self.without_rowid; + let ref like = self.like; + write!( + f, + "CREATE {or_replace}{external}{temporary}TABLE {if_not_exists}{name}", + or_replace = if *or_replace { "OR REPLACE " } else { "" }, + external = if *external { "EXTERNAL " } else { "" }, + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + temporary = if *temporary { "TEMPORARY " } 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() && like.is_none() { + // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens + write!(f, " ()")?; + } + // Only for SQLite + if *without_rowid { + write!(f, " WITHOUT ROWID")?; + } + + // Only for Hive + if let Some(l) = like { + write!(f, " LIKE {}", l)?; + } + match hive_distribution { + HiveDistributionStyle::PARTITIONED { columns } => { + write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?; + } + HiveDistributionStyle::CLUSTERED { + columns, + sorted_by, + num_buckets, + } => { + write!(f, " CLUSTERED BY ({})", display_comma_separated(columns))?; + if !sorted_by.is_empty() { + write!(f, " SORTED BY ({})", display_comma_separated(sorted_by))?; + } + if *num_buckets > 0 { + write!(f, " INTO {} BUCKETS", num_buckets)?; + } + } + HiveDistributionStyle::SKEWED { + columns, + on, + stored_as_directories, + } => { + write!( + f, + " SKEWED BY ({})) ON ({})", + display_comma_separated(columns), + display_comma_separated(on) + )?; + if *stored_as_directories { + write!(f, " STORED AS DIRECTORIES")?; + } + } + _ => (), + } + + if let Some(HiveFormat { + row_format, + storage, + location, + }) = hive_formats + { + match row_format { + Some(HiveRowFormat::SERDE { class }) => write!(f, " ROW FORMAT SERDE '{}'", class)?, + Some(HiveRowFormat::DELIMITED) => write!(f, " ROW FORMAT DELIMITED")?, + None => (), + } + match storage { + Some(HiveIOFormat::IOF { + input_format, + output_format, + }) => write!( + f, + " STORED AS INPUTFORMAT {} OUTPUTFORMAT {}", + input_format, output_format + )?, + Some(HiveIOFormat::FileFormat { format }) if !*external => { + write!(f, " STORED AS {}", format)? + } + _ => (), + } + if !*external { + if let Some(loc) = location { + write!(f, " LOCATION '{}'", loc)?; + } + } + } + if *external { + write!( + f, + " STORED AS {} LOCATION '{}'", + file_format.as_ref().unwrap(), + location.as_ref().unwrap() + )?; + } + if !table_properties.is_empty() { + write!( + f, + " TBLPROPERTIES ({})", + display_comma_separated(table_properties) + )?; + } + if !with_options.is_empty() { + write!(f, " WITH ({})", display_comma_separated(with_options))?; + } + if let Some(query) = query { + write!(f, " AS {}", query)?; + } + Ok(()) + } +} + +impl fmt::Display for CreateVirtualTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "CREATE VIRTUAL TABLE {if_not_exists}{name} USING {module_name}", + if_not_exists = if self.if_not_exists { + "IF NOT EXISTS " + } else { + "" + }, + name = self.name, + module_name = self.module_name + )?; + if !self.module_args.is_empty() { + write!(f, " ({})", display_comma_separated(&self.module_args))?; + } + Ok(()) + } +} + +impl fmt::Display for CreateIndex { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "CREATE {unique}INDEX {if_not_exists}{name} ON {table_name}({columns})", + unique = if self.unique { "UNIQUE " } else { "" }, + if_not_exists = if self.if_not_exists { + "IF NOT EXISTS " + } else { + "" + }, + name = self.name, + table_name = self.table_name, + columns = display_separated(&self.columns, ",") + ) + } +} + +impl fmt::Display for AlterTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ALTER TABLE {} {}", self.name, self.operation) + } +} + +impl fmt::Display for Drop { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DROP {}{} {}{}{}", + self.object_type, + if self.if_exists { " IF EXISTS" } else { "" }, + display_comma_separated(&self.names), + if self.cascade { " CASCADE" } else { "" }, + if self.purge { " PURGE" } else { "" } + ) + } +} + +impl fmt::Display for SetVariable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("SET ")?; + if self.local { + f.write_str("LOCAL ")?; + } + write!( + f, + "{hivevar}{name} = {value}", + hivevar = if self.hivevar { "HIVEVAR:" } else { "" }, + name = self.variable, + value = display_comma_separated(&self.value) + ) + } +} + +impl fmt::Display for ShowVariable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SHOW")?; + if !self.variable.is_empty() { + write!(f, " {}", display_separated(&self.variable, " "))?; + } + Ok(()) + } +} + +impl fmt::Display for ShowCreate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "SHOW CREATE {obj_type} {obj_name}", + obj_type = self.obj_type, + obj_name = self.obj_name, + )?; + Ok(()) + } +} + +impl fmt::Display for ShowColumns { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "SHOW {extended}{full}COLUMNS FROM {table_name}", + extended = if self.extended { "EXTENDED " } else { "" }, + full = if self.full { "FULL " } else { "" }, + table_name = self.table_name, + )?; + if let Some(filter) = &self.filter { + write!(f, " {}", filter)?; + } + Ok(()) + } +} + +impl fmt::Display for StartTransaction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "START TRANSACTION")?; + if !self.modes.is_empty() { + write!(f, " {}", display_comma_separated(&self.modes))?; + } + Ok(()) + } +} + +impl fmt::Display for SetTransaction { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "SET TRANSACTION")?; + if !self.modes.is_empty() { + write!(f, " {}", display_comma_separated(&self.modes))?; + } + Ok(()) + } +} + +impl fmt::Display for Commit { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "COMMIT{}", if self.chain { " AND CHAIN" } else { "" },) + } +} + +impl fmt::Display for Rollback { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ROLLBACK{}", if self.chain { " AND CHAIN" } else { "" },) + } +} + +impl fmt::Display for CreateSchema { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "CREATE SCHEMA {if_not_exists}{name}", + if_not_exists = if self.if_not_exists { + "IF NOT EXISTS " + } else { + "" + }, + name = self.schema_name + ) + } +} + +impl fmt::Display for Assert { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "ASSERT {}", self.condition)?; + if let Some(m) = &self.message { + write!(f, " AS {}", m)?; + } + Ok(()) + } +} +impl fmt::Display for Deallocate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "DEALLOCATE {prepare}{name}", + prepare = if self.prepare { "PREPARE " } else { "" }, + name = self.name, + ) + } +} + +impl fmt::Display for Execute { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "EXECUTE {}", self.name)?; + if !self.parameters.is_empty() { + write!(f, "({})", display_comma_separated(&self.parameters))?; + } + Ok(()) + } +} + +impl fmt::Display for Prepare { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PREPARE {} ", self.name)?; + if !self.data_types.is_empty() { + write!(f, "({}) ", display_comma_separated(&self.data_types))?; + } + write!(f, "AS {}", self.statement) + } +} diff --git a/src/ast/statement/mod.rs b/src/ast/statement/mod.rs new file mode 100644 index 000000000..8ea65cfd0 --- /dev/null +++ b/src/ast/statement/mod.rs @@ -0,0 +1,379 @@ +mod display; + +use super::*; +/// A top-level statement (SELECT, INSERT, CREATE, etc.) + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum Statement { + /// Analyze (Hive) + Analyze(Analyze), + /// Truncate (Hive) + Truncate(Truncate), + /// Msck (Hive) + Msck(Msck), + /// SELECT + Query(Box), + /// INSERT + Insert(Insert), + // TODO: Support ROW FORMAT + Directory(Directory), + Copy(Copy), + /// UPDATE + Update(Update), + /// DELETE + Delete(Delete), + /// CREATE VIEW + CreateView(CreateView), + /// CREATE TABLE + CreateTable(CreateTable), + /// SQLite's `CREATE VIRTUAL TABLE .. USING ()` + CreateVirtualTable(CreateVirtualTable), + /// CREATE INDEX + CreateIndex(CreateIndex), + /// ALTER TABLE + AlterTable(AlterTable), + /// DROP + Drop(Drop), + /// 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(SetVariable), + /// SHOW + /// + /// Note: this is a PostgreSQL-specific statement. + ShowVariable(ShowVariable), + /// SHOW CREATE TABLE + /// + /// Note: this is a MySQL-specific statement. + ShowCreate(ShowCreate), + /// SHOW COLUMNS + /// + /// Note: this is a MySQL-specific statement. + ShowColumns(ShowColumns), + /// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...` + StartTransaction(StartTransaction), + /// `SET TRANSACTION ...` + SetTransaction(SetTransaction), + /// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` + Commit(Commit), + /// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` + Rollback(Rollback), + /// CREATE SCHEMA + CreateSchema(CreateSchema), + /// CREATE DATABASE + CreateDatabase(CreateDatabase), + /// `ASSERT [AS ]` + Assert(Assert), + /// `DEALLOCATE [ PREPARE ] { name | ALL }` + /// + /// Note: this is a PostgreSQL-specific statement. + Deallocate(Deallocate), + /// `EXECUTE name [ ( parameter [, ...] ) ]` + /// + /// Note: this is a PostgreSQL-specific statement. + Execute(Execute), + /// `PREPARE name [ ( data_type [, ...] ) ] AS statement` + /// + /// Note: this is a PostgreSQL-specific statement. + Prepare(Prepare), + /// EXPLAIN + Explain(Explain), +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Analyze { + pub table_name: ObjectName, + pub partitions: Option>, + pub for_columns: bool, + pub columns: Vec, + pub cache_metadata: bool, + pub noscan: bool, + pub compute_statistics: bool, +} +/// Truncate (Hive) +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Truncate { + pub table_name: ObjectName, + pub partitions: Option>, +} +/// Msck (Hive) +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Msck { + pub table_name: ObjectName, + pub repair: bool, + pub partition_action: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Insert { + /// Only for Sqlite + pub or: Option, + /// TABLE + pub table_name: ObjectName, + /// COLUMNS + pub columns: Vec, + /// Overwrite (Hive) + pub overwrite: bool, + /// A SQL query that specifies what to insert + pub source: Box, + /// partitioned insert (Hive) + pub partitioned: Option>, + /// Columns defined after PARTITION + pub after_columns: Vec, + /// whether the insert has the table keyword (Hive) + pub table: bool, +} +// TODO: Support ROW FORMAT +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Directory { + pub overwrite: bool, + pub local: bool, + pub path: String, + pub file_format: Option, + pub source: Box, +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Copy { + /// TABLE + pub table_name: ObjectName, + /// COLUMNS + pub columns: Vec, + /// VALUES a vector of values to be copied + pub values: Vec>, +} +/// UPDATE +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Update { + /// TABLE + pub table_name: ObjectName, + /// Column assignments + pub assignments: Vec, + /// WHERE + pub selection: Option, +} +/// DELETE +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Delete { + /// FROM + pub table_name: ObjectName, + /// WHERE + pub selection: Option, +} +/// CREATE VIEW +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CreateView { + pub or_replace: bool, + pub materialized: bool, + /// View name + pub name: ObjectName, + pub columns: Vec, + pub query: Box, + pub with_options: Vec, +} +/// CREATE TABLE + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CreateTable { + pub or_replace: bool, + pub temporary: bool, + pub external: bool, + pub if_not_exists: bool, + /// Table name + pub name: ObjectName, + /// Optional schema + pub columns: Vec, + pub constraints: Vec, + pub hive_distribution: HiveDistributionStyle, + pub hive_formats: Option, + pub table_properties: Vec, + pub with_options: Vec, + pub file_format: Option, + pub location: Option, + pub query: Option>, + pub without_rowid: bool, + pub like: Option, +} +/// SQLite's `CREATE VIRTUAL TABLE .. USING ()` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CreateVirtualTable { + pub name: ObjectName, + pub if_not_exists: bool, + pub module_name: Ident, + pub module_args: Vec, +} +/// CREATE INDEX +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CreateIndex { + /// index name + pub name: ObjectName, + pub table_name: ObjectName, + pub columns: Vec, + pub unique: bool, + pub if_not_exists: bool, +} +/// ALTER TABLE +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct AlterTable { + /// Table name + pub name: ObjectName, + pub operation: AlterTableOperation, +} +/// DROP +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Drop { + /// The type of the object to drop: TABLE, VIEW, etc. + pub object_type: ObjectType, + /// An optional `IF EXISTS` clause. (Non-standard.) + pub if_exists: bool, + /// One or more objects to drop. (ANSI SQL requires exactly one.) + pub names: Vec, + /// Whether `CASCADE` was specified. This will be `false` when + /// `RESTRICT` or no drop behavior at all was specified. + pub cascade: bool, + /// Hive allows you specify whether the table's stored data will be + /// deleted along with the dropped table + pub purge: 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. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SetVariable { + pub local: bool, + pub hivevar: bool, + pub variable: Ident, + pub value: Vec, +} +/// SHOW +/// +/// Note: this is a PostgreSQL-specific statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ShowVariable { + pub variable: Vec, +} +/// SHOW CREATE TABLE +/// +/// Note: this is a MySQL-specific statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ShowCreate { + pub obj_type: ShowCreateObject, + pub obj_name: ObjectName, +} +/// SHOW COLUMNS +/// +/// Note: this is a MySQL-specific statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ShowColumns { + pub extended: bool, + pub full: bool, + pub table_name: ObjectName, + pub filter: Option, +} +/// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct StartTransaction { + pub modes: Vec, +} +/// `SET TRANSACTION ...` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct SetTransaction { + pub modes: Vec, +} +/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Commit { + pub chain: bool, +} +/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Rollback { + pub chain: bool, +} +/// CREATE SCHEMA +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CreateSchema { + pub schema_name: ObjectName, + pub if_not_exists: bool, +} +/// CREATE DATABASE +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct CreateDatabase { + pub db_name: ObjectName, + pub if_not_exists: bool, + pub location: Option, + pub managed_location: Option, +} +/// `ASSERT [AS ]` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Assert { + pub condition: Expr, + pub message: Option, +} +/// `DEALLOCATE [ PREPARE ] { name | ALL }` +/// +/// Note: this is a PostgreSQL-specific statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Deallocate { + pub name: Ident, + pub prepare: bool, +} +/// `EXECUTE name [ ( parameter [, ...] ) ]` +/// +/// Note: this is a PostgreSQL-specific statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Execute { + pub name: Ident, + pub parameters: Vec, +} +/// `PREPARE name [ ( data_type [, ...] ) ] AS statement` +/// +/// Note: this is a PostgreSQL-specific statement. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Prepare { + pub name: Ident, + pub data_types: Vec, + pub statement: Box, +} +/// EXPLAIN +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Explain { + /// Carry out the command and show actual run times and other statistics. + pub analyze: bool, + // Display additional information regarding the plan. + pub verbose: bool, + /// A SQL query that specifies what to explain + pub statement: Box, +} From 72d01f3a27d811d2118baa43f02a2f42adaf605b Mon Sep 17 00:00:00 2001 From: tvallotton Date: Fri, 3 Sep 2021 12:57:19 -0400 Subject: [PATCH 02/11] - --- src/ast/mod.rs | 772 +------------------------------------------------ src/parser.rs | 2 +- 2 files changed, 3 insertions(+), 771 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c12ed3066..084f0aaa6 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -18,6 +18,8 @@ mod operator; mod query; mod value; mod statement; +pub use statement::*; + #[cfg(not(feature = "std"))] use alloc::{ boxed::Box, @@ -545,776 +547,6 @@ impl fmt::Display for ShowCreateObject { } } -/// 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 { - /// Analyze (Hive) - Analyze { - table_name: ObjectName, - partitions: Option>, - for_columns: bool, - columns: Vec, - cache_metadata: bool, - noscan: bool, - compute_statistics: bool, - }, - /// Truncate (Hive) - Truncate { - table_name: ObjectName, - partitions: Option>, - }, - /// Msck (Hive) - Msck { - table_name: ObjectName, - repair: bool, - partition_action: Option, - }, - /// SELECT - Query(Box), - /// INSERT - Insert { - /// Only for Sqlite - or: Option, - /// TABLE - table_name: ObjectName, - /// COLUMNS - columns: Vec, - /// Overwrite (Hive) - overwrite: bool, - /// A SQL query that specifies what to insert - source: Box, - /// partitioned insert (Hive) - partitioned: Option>, - /// Columns defined after PARTITION - after_columns: Vec, - /// whether the insert has the table keyword (Hive) - table: bool, - }, - // TODO: Support ROW FORMAT - Directory { - overwrite: bool, - local: bool, - path: String, - file_format: Option, - 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 { - or_replace: bool, - materialized: bool, - /// View name - name: ObjectName, - columns: Vec, - query: Box, - with_options: Vec, - }, - /// CREATE TABLE - CreateTable { - or_replace: bool, - temporary: bool, - external: bool, - if_not_exists: bool, - /// Table name - name: ObjectName, - /// Optional schema - columns: Vec, - constraints: Vec, - hive_distribution: HiveDistributionStyle, - hive_formats: Option, - table_properties: Vec, - with_options: Vec, - file_format: Option, - location: Option, - query: Option>, - without_rowid: bool, - like: Option, - }, - /// 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, - /// Hive allows you specify whether the table's stored data will be - /// deleted along with the dropped table - purge: 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, - hivevar: bool, - variable: Ident, - value: Vec, - }, - /// SHOW - /// - /// Note: this is a PostgreSQL-specific statement. - ShowVariable { variable: Vec }, - /// SHOW CREATE TABLE - /// - /// Note: this is a MySQL-specific statement. - ShowCreate { - obj_type: ShowCreateObject, - obj_name: ObjectName, - }, - /// 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, - if_not_exists: bool, - }, - /// CREATE DATABASE - CreateDatabase { - db_name: ObjectName, - if_not_exists: bool, - location: Option, - managed_location: Option, - }, - /// `ASSERT [AS ]` - Assert { - condition: Expr, - message: Option, - }, - /// `DEALLOCATE [ PREPARE ] { name | ALL }` - /// - /// Note: this is a PostgreSQL-specific statement. - Deallocate { name: Ident, prepare: bool }, - /// `EXECUTE name [ ( parameter [, ...] ) ]` - /// - /// Note: this is a PostgreSQL-specific statement. - Execute { name: Ident, parameters: Vec }, - /// `PREPARE name [ ( data_type [, ...] ) ] AS statement` - /// - /// Note: this is a PostgreSQL-specific statement. - Prepare { - name: Ident, - data_types: Vec, - statement: Box, - }, - /// EXPLAIN - Explain { - /// Carry out the command and show actual run times and other statistics. - analyze: bool, - // Display additional information regarding the plan. - verbose: bool, - /// A SQL query that specifies what to explain - statement: Box, - }, -} - -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. - - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Statement::Explain { - verbose, - analyze, - statement, - } => { - write!(f, "EXPLAIN ")?; - - if *analyze { - write!(f, "ANALYZE ")?; - } - - if *verbose { - write!(f, "VERBOSE ")?; - } - - write!(f, "{}", statement) - } - Statement::Query(s) => write!(f, "{}", s), - Statement::Directory { - overwrite, - local, - path, - file_format, - source, - } => { - write!( - f, - "INSERT{overwrite}{local} DIRECTORY '{path}'", - overwrite = if *overwrite { " OVERWRITE" } else { "" }, - local = if *local { " LOCAL" } else { "" }, - path = path - )?; - if let Some(ref ff) = file_format { - write!(f, " STORED AS {}", ff)? - } - write!(f, " {}", source) - } - Statement::Msck { - table_name, - repair, - partition_action, - } => { - write!( - f, - "MSCK {repair}TABLE {table}", - repair = if *repair { "REPAIR " } else { "" }, - table = table_name - )?; - if let Some(pa) = partition_action { - write!(f, " {}", pa)?; - } - Ok(()) - } - Statement::Truncate { - table_name, - partitions, - } => { - write!(f, "TRUNCATE TABLE {}", table_name)?; - if let Some(ref parts) = partitions { - if !parts.is_empty() { - write!(f, " PARTITION ({})", display_comma_separated(parts))?; - } - } - Ok(()) - } - Statement::Analyze { - table_name, - partitions, - for_columns, - columns, - cache_metadata, - noscan, - compute_statistics, - } => { - write!(f, "ANALYZE TABLE {}", table_name)?; - if let Some(ref parts) = partitions { - if !parts.is_empty() { - write!(f, " PARTITION ({})", display_comma_separated(parts))?; - } - } - - if *compute_statistics { - write!(f, " COMPUTE STATISTICS")?; - } - if *noscan { - write!(f, " NOSCAN")?; - } - if *cache_metadata { - write!(f, " CACHE METADATA")?; - } - if *for_columns { - write!(f, " FOR COLUMNS")?; - if !columns.is_empty() { - write!(f, " {}", display_comma_separated(columns))?; - } - } - Ok(()) - } - Statement::Insert { - or, - table_name, - overwrite, - partitioned, - columns, - after_columns, - source, - table, - } => { - if let Some(action) = or { - write!(f, "INSERT OR {} INTO {} ", action, table_name)?; - } else { - write!( - f, - "INSERT {act}{tbl} {table_name} ", - table_name = table_name, - act = if *overwrite { "OVERWRITE" } else { "INTO" }, - tbl = if *table { " TABLE" } else { "" } - )?; - } - if !columns.is_empty() { - write!(f, "({}) ", display_comma_separated(columns))?; - } - if let Some(ref parts) = partitioned { - if !parts.is_empty() { - write!(f, "PARTITION ({}) ", display_comma_separated(parts))?; - } - } - if !after_columns.is_empty() { - write!(f, "({}) ", display_comma_separated(after_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 {}", 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::CreateDatabase { - db_name, - if_not_exists, - location, - managed_location, - } => { - write!(f, "CREATE")?; - if *if_not_exists { - write!(f, " IF NOT EXISTS")?; - } - write!(f, " {}", db_name)?; - if let Some(l) = location { - write!(f, " LOCATION '{}'", l)?; - } - if let Some(ml) = managed_location { - write!(f, " MANAGEDLOCATION '{}'", ml)?; - } - Ok(()) - } - Statement::CreateView { - name, - or_replace, - columns, - query, - materialized, - with_options, - } => { - write!( - f, - "CREATE {or_replace}{materialized}VIEW {name}", - or_replace = if *or_replace { "OR REPLACE " } else { "" }, - materialized = if *materialized { "MATERIALIZED " } else { "" }, - name = 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, - table_properties, - with_options, - or_replace, - if_not_exists, - hive_distribution, - hive_formats, - external, - temporary, - file_format, - location, - query, - without_rowid, - like, - } => { - // 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 {or_replace}{external}{temporary}TABLE {if_not_exists}{name}", - or_replace = if *or_replace { "OR REPLACE " } else { "" }, - external = if *external { "EXTERNAL " } else { "" }, - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - temporary = if *temporary { "TEMPORARY " } 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() && like.is_none() { - // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens - write!(f, " ()")?; - } - // Only for SQLite - if *without_rowid { - write!(f, " WITHOUT ROWID")?; - } - - // Only for Hive - if let Some(l) = like { - write!(f, " LIKE {}", l)?; - } - match hive_distribution { - HiveDistributionStyle::PARTITIONED { columns } => { - write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?; - } - HiveDistributionStyle::CLUSTERED { - columns, - sorted_by, - num_buckets, - } => { - write!(f, " CLUSTERED BY ({})", display_comma_separated(columns))?; - if !sorted_by.is_empty() { - write!(f, " SORTED BY ({})", display_comma_separated(sorted_by))?; - } - if *num_buckets > 0 { - write!(f, " INTO {} BUCKETS", num_buckets)?; - } - } - HiveDistributionStyle::SKEWED { - columns, - on, - stored_as_directories, - } => { - write!( - f, - " SKEWED BY ({})) ON ({})", - display_comma_separated(columns), - display_comma_separated(on) - )?; - if *stored_as_directories { - write!(f, " STORED AS DIRECTORIES")?; - } - } - _ => (), - } - - if let Some(HiveFormat { - row_format, - storage, - location, - }) = hive_formats - { - match row_format { - Some(HiveRowFormat::SERDE { class }) => { - write!(f, " ROW FORMAT SERDE '{}'", class)? - } - Some(HiveRowFormat::DELIMITED) => write!(f, " ROW FORMAT DELIMITED")?, - None => (), - } - match storage { - Some(HiveIOFormat::IOF { - input_format, - output_format, - }) => write!( - f, - " STORED AS INPUTFORMAT {} OUTPUTFORMAT {}", - input_format, output_format - )?, - Some(HiveIOFormat::FileFormat { format }) if !*external => { - write!(f, " STORED AS {}", format)? - } - _ => (), - } - if !*external { - if let Some(loc) = location { - write!(f, " LOCATION '{}'", loc)?; - } - } - } - if *external { - write!( - f, - " STORED AS {} LOCATION '{}'", - file_format.as_ref().unwrap(), - location.as_ref().unwrap() - )?; - } - if !table_properties.is_empty() { - write!( - f, - " TBLPROPERTIES ({})", - display_comma_separated(table_properties) - )?; - } - 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 {unique}INDEX {if_not_exists}{name} ON {table_name}({columns})", - unique = if *unique { "UNIQUE " } else { "" }, - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - name = name, - table_name = table_name, - columns = display_separated(columns, ",") - ), - Statement::AlterTable { name, operation } => { - write!(f, "ALTER TABLE {} {}", name, operation) - } - Statement::Drop { - object_type, - if_exists, - names, - cascade, - purge, - } => write!( - f, - "DROP {}{} {}{}{}", - object_type, - if *if_exists { " IF EXISTS" } else { "" }, - display_comma_separated(names), - if *cascade { " CASCADE" } else { "" }, - if *purge { " PURGE" } else { "" } - ), - Statement::SetVariable { - local, - variable, - hivevar, - value, - } => { - f.write_str("SET ")?; - if *local { - f.write_str("LOCAL ")?; - } - write!( - f, - "{hivevar}{name} = {value}", - hivevar = if *hivevar { "HIVEVAR:" } else { "" }, - name = variable, - value = display_comma_separated(value) - ) - } - Statement::ShowVariable { variable } => { - write!(f, "SHOW")?; - if !variable.is_empty() { - write!(f, " {}", display_separated(variable, " "))?; - } - Ok(()) - } - Statement::ShowCreate { obj_type, obj_name } => { - write!( - f, - "SHOW CREATE {obj_type} {obj_name}", - obj_type = obj_type, - obj_name = obj_name, - )?; - Ok(()) - } - Statement::ShowColumns { - extended, - full, - table_name, - filter, - } => { - write!( - f, - "SHOW {extended}{full}COLUMNS FROM {table_name}", - extended = if *extended { "EXTENDED " } else { "" }, - full = if *full { "FULL " } else { "" }, - table_name = 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, - if_not_exists, - } => write!( - f, - "CREATE SCHEMA {if_not_exists}{name}", - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - name = schema_name - ), - Statement::Assert { condition, message } => { - write!(f, "ASSERT {}", condition)?; - if let Some(m) = message { - write!(f, " AS {}", m)?; - } - Ok(()) - } - Statement::Deallocate { name, prepare } => write!( - f, - "DEALLOCATE {prepare}{name}", - prepare = if *prepare { "PREPARE " } else { "" }, - name = name, - ), - Statement::Execute { name, parameters } => { - write!(f, "EXECUTE {}", name)?; - if !parameters.is_empty() { - write!(f, "({})", display_comma_separated(parameters))?; - } - Ok(()) - } - Statement::Prepare { - name, - data_types, - statement, - } => { - write!(f, "PREPARE {} ", name)?; - if !data_types.is_empty() { - write!(f, "({}) ", display_comma_separated(data_types))?; - } - write!(f, "AS {}", statement) - } - } - } -} /// SQL assignment `foo = expr` as used in SQLUpdate #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/parser.rs b/src/parser.rs index 659f3d1ee..c020d7d6d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -272,7 +272,7 @@ impl<'a> Parser<'a> { } } - Ok(Statement::Analyze { + Ok(Analyze { table_name, for_columns, columns, From 29e00e0ceea3a2a2a734d0c448d78d2e6f22ae85 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Fri, 3 Sep 2021 17:23:52 -0400 Subject: [PATCH 03/11] - --- src/parser.rs | 87 ++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index c020d7d6d..1ff8fce5b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -139,6 +139,7 @@ impl<'a> Parser<'a> { /// Parse a single top-level statement (such as SELECT, INSERT, CREATE, etc.), /// stopping before the statement separator, if any. pub fn parse_statement(&mut self) -> Result { + use Statement::*; match self.next_token() { Token::Word(w) => match w.keyword { Keyword::EXPLAIN => Ok(self.parse_explain()?), @@ -150,7 +151,7 @@ impl<'a> Parser<'a> { Keyword::TRUNCATE => Ok(self.parse_truncate()?), Keyword::MSCK => Ok(self.parse_msck()?), Keyword::CREATE => Ok(self.parse_create()?), - Keyword::DROP => Ok(self.parse_drop()?), + Keyword::DROP => Ok(Drop(self.parse_drop()?)), Keyword::DELETE => Ok(self.parse_delete()?), Keyword::INSERT => Ok(self.parse_insert()?), Keyword::UPDATE => Ok(self.parse_update()?), @@ -1421,7 +1422,7 @@ impl<'a> Parser<'a> { }) } - pub fn parse_drop(&mut self) -> Result { + pub fn parse_drop(&mut self) -> Result { let object_type = if self.parse_keyword(Keyword::TABLE) { ObjectType::Table } else if self.parse_keyword(Keyword::VIEW) { @@ -1443,7 +1444,7 @@ impl<'a> Parser<'a> { if cascade && restrict { return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP"); } - Ok(Statement::Drop { + Ok(Drop { object_type, if_exists, names, @@ -1452,7 +1453,7 @@ impl<'a> Parser<'a> { }) } - pub fn parse_create_index(&mut self, unique: bool) -> Result { + pub fn parse_create_index(&mut self, unique: bool) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let index_name = self.parse_object_name()?; self.expect_keyword(Keyword::ON)?; @@ -1460,7 +1461,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let columns = self.parse_comma_separated(Parser::parse_order_by_expr)?; self.expect_token(&Token::RParen)?; - Ok(Statement::CreateIndex { + Ok(CreateIndex { name: index_name, table_name, columns, @@ -1798,7 +1799,7 @@ impl<'a> Parser<'a> { Ok(SqlOption { name, value }) } - pub fn parse_alter(&mut self) -> Result { + pub fn parse_alter(&mut self) -> Result { self.expect_keyword(Keyword::TABLE)?; let _ = self.parse_keyword(Keyword::ONLY); let table_name = self.parse_object_name()?; @@ -1883,20 +1884,20 @@ impl<'a> Parser<'a> { self.peek_token(), ); }; - Ok(Statement::AlterTable { + Ok(AlterTable { name: table_name, operation, }) } /// Parse a copy statement - pub fn parse_copy(&mut self) -> Result { + pub fn parse_copy(&mut self) -> Result { let table_name = self.parse_object_name()?; let columns = self.parse_parenthesized_column_list(Optional)?; self.expect_keywords(&[Keyword::FROM, Keyword::STDIN])?; self.expect_token(&Token::SemiColon)?; let values = self.parse_tsv(); - Ok(Statement::Copy { + Ok(Copy { table_name, columns, values, @@ -2203,7 +2204,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_delete(&mut self) -> Result { + pub fn parse_delete(&mut self) -> Result { self.expect_keyword(Keyword::FROM)?; let table_name = self.parse_object_name()?; let selection = if self.parse_keyword(Keyword::WHERE) { @@ -2212,19 +2213,19 @@ impl<'a> Parser<'a> { None }; - Ok(Statement::Delete { + Ok(Delete { table_name, selection, }) } - pub fn parse_explain(&mut self) -> Result { + pub fn parse_explain(&mut self) -> Result { let analyze = self.parse_keyword(Keyword::ANALYZE); let verbose = self.parse_keyword(Keyword::VERBOSE); let statement = Box::new(self.parse_statement()?); - Ok(Statement::Explain { + Ok(Explain { analyze, verbose, statement, @@ -2516,23 +2517,23 @@ impl<'a> Parser<'a> { if self.consume_token(&Token::Comma) { continue; } - return Ok(Statement::SetVariable { + return Ok(Statement::SetVariable(SetVariable { local: modifier == Some(Keyword::LOCAL), hivevar: Some(Keyword::HIVEVAR) == modifier, variable, value: values, - }); + })); } } else if variable.value == "TRANSACTION" && modifier.is_none() { - Ok(Statement::SetTransaction { + Ok(Statement::SetTransaction(SetTransaction { modes: self.parse_transaction_modes()?, - }) + })) } else { self.expected("equals sign or TO", self.peek_token()) } } - pub fn parse_show(&mut self) -> Result { + pub fn parse_show(&mut self) -> Result { if self .parse_one_of_keywords(&[ Keyword::EXTENDED, @@ -2547,13 +2548,13 @@ impl<'a> Parser<'a> { } else if self.parse_one_of_keywords(&[Keyword::CREATE]).is_some() { Ok(self.parse_show_create()?) } else { - Ok(Statement::ShowVariable { + Ok(ShowVariable { variable: self.parse_identifiers()?, }) } } - fn parse_show_create(&mut self) -> Result { + fn parse_show_create(&mut self) -> Result { let obj_type = match self.expect_one_of_keywords(&[ Keyword::TABLE, Keyword::TRIGGER, @@ -2574,10 +2575,10 @@ impl<'a> Parser<'a> { let obj_name = self.parse_object_name()?; - Ok(Statement::ShowCreate { obj_type, obj_name }) + Ok(ShowCreate { obj_type, obj_name }) } - fn parse_show_columns(&mut self) -> Result { + fn parse_show_columns(&mut self) -> Result { let extended = self.parse_keyword(Keyword::EXTENDED); let full = self.parse_keyword(Keyword::FULL); self.expect_one_of_keywords(&[Keyword::COLUMNS, Keyword::FIELDS])?; @@ -2587,7 +2588,7 @@ impl<'a> Parser<'a> { // allows both FROM FROM and FROM .
, // while we only support the latter for now. let filter = self.parse_show_statement_filter()?; - Ok(Statement::ShowColumns { + Ok(ShowColumns { extended, full, table_name, @@ -2872,13 +2873,13 @@ impl<'a> Parser<'a> { None }; let source = Box::new(self.parse_query()?); - Ok(Statement::Directory { + Ok(Statement::Directory(Directory { local, path, overwrite, file_format, source, - }) + })) } else { // Hive lets you put table here regardless let table = self.parse_keyword(Keyword::TABLE); @@ -2898,7 +2899,7 @@ impl<'a> Parser<'a> { let after_columns = self.parse_parenthesized_column_list(Optional)?; let source = Box::new(self.parse_query()?); - Ok(Statement::Insert { + Ok(Statement::Insert(Insert { or, table_name, overwrite, @@ -2907,11 +2908,11 @@ impl<'a> Parser<'a> { after_columns, source, table, - }) + })) } } - pub fn parse_update(&mut self) -> Result { + pub fn parse_update(&mut self) -> Result { let table_name = self.parse_object_name()?; self.expect_keyword(Keyword::SET)?; let assignments = self.parse_comma_separated(Parser::parse_assignment)?; @@ -2920,7 +2921,7 @@ impl<'a> Parser<'a> { } else { None }; - Ok(Statement::Update { + Ok(Update { table_name, assignments, selection, @@ -3084,16 +3085,16 @@ impl<'a> Parser<'a> { Ok(Values(values)) } - pub fn parse_start_transaction(&mut self) -> Result { + pub fn parse_start_transaction(&mut self) -> Result { self.expect_keyword(Keyword::TRANSACTION)?; - Ok(Statement::StartTransaction { + Ok(StartTransaction { modes: self.parse_transaction_modes()?, }) } - pub fn parse_begin(&mut self) -> Result { + pub fn parse_begin(&mut self) -> Result { let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]); - Ok(Statement::StartTransaction { + Ok(StartTransaction { modes: self.parse_transaction_modes()?, }) } @@ -3134,14 +3135,14 @@ impl<'a> Parser<'a> { Ok(modes) } - pub fn parse_commit(&mut self) -> Result { - Ok(Statement::Commit { + pub fn parse_commit(&mut self) -> Result { + Ok(Commit { chain: self.parse_commit_rollback_chain()?, }) } - pub fn parse_rollback(&mut self) -> Result { - Ok(Statement::Rollback { + pub fn parse_rollback(&mut self) -> Result { + Ok(Rollback { chain: self.parse_commit_rollback_chain()?, }) } @@ -3157,13 +3158,13 @@ impl<'a> Parser<'a> { } } - fn parse_deallocate(&mut self) -> Result { + fn parse_deallocate(&mut self) -> Result { let prepare = self.parse_keyword(Keyword::PREPARE); let name = self.parse_identifier()?; - Ok(Statement::Deallocate { name, prepare }) + Ok(Deallocate { name, prepare }) } - fn parse_execute(&mut self) -> Result { + fn parse_execute(&mut self) -> Result { let name = self.parse_identifier()?; let mut parameters = vec![]; @@ -3172,10 +3173,10 @@ impl<'a> Parser<'a> { self.expect_token(&Token::RParen)?; } - Ok(Statement::Execute { name, parameters }) + Ok(Execute { name, parameters }) } - fn parse_prepare(&mut self) -> Result { + fn parse_prepare(&mut self) -> Result { let name = self.parse_identifier()?; let mut data_types = vec![]; @@ -3186,7 +3187,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::AS)?; let statement = Box::new(self.parse_statement()?); - Ok(Statement::Prepare { + Ok(Prepare { name, data_types, statement, From 09428fd230f7805df724678a4955a8f66e02df32 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 00:38:02 -0400 Subject: [PATCH 04/11] updating parser and tests --- .vscode/settings.json | 4 ++ src/parser.rs | 104 ++++++++++++++------------- tests/sqlparser_common.rs | 134 +++++++++++++++++------------------ tests/sqlparser_mysql.rs | 34 ++++----- tests/sqlparser_postgres.rs | 84 +++++++++++----------- tests/sqlparser_snowflake.rs | 2 +- tests/sqlparser_sqlite.rs | 12 ++-- 7 files changed, 191 insertions(+), 183 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..cdfc449db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "RA_LOG=lsp_server": "debug", + "RA_LOG_FILE": "/Users/tomas/Documents/sqlparser-rs/output" +} \ No newline at end of file diff --git a/src/parser.rs b/src/parser.rs index 1ff8fce5b..65895f44d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -139,39 +139,39 @@ impl<'a> Parser<'a> { /// Parse a single top-level statement (such as SELECT, INSERT, CREATE, etc.), /// stopping before the statement separator, if any. pub fn parse_statement(&mut self) -> Result { - use Statement::*; + use Statement::*; match self.next_token() { Token::Word(w) => match w.keyword { - Keyword::EXPLAIN => Ok(self.parse_explain()?), - Keyword::ANALYZE => Ok(self.parse_analyze()?), + Keyword::EXPLAIN => Ok(Explain(self.parse_explain()?)), + Keyword::ANALYZE => Ok(Analyze(self.parse_analyze()?)), Keyword::SELECT | Keyword::WITH | Keyword::VALUES => { self.prev_token(); Ok(Statement::Query(Box::new(self.parse_query()?))) } - Keyword::TRUNCATE => Ok(self.parse_truncate()?), - Keyword::MSCK => Ok(self.parse_msck()?), + Keyword::TRUNCATE => Ok(Truncate(self.parse_truncate()?)), + Keyword::MSCK => Ok(Msck(self.parse_msck()?)), Keyword::CREATE => Ok(self.parse_create()?), Keyword::DROP => Ok(Drop(self.parse_drop()?)), - Keyword::DELETE => Ok(self.parse_delete()?), + Keyword::DELETE => Ok(Delete(self.parse_delete()?)), Keyword::INSERT => Ok(self.parse_insert()?), - Keyword::UPDATE => Ok(self.parse_update()?), - Keyword::ALTER => Ok(self.parse_alter()?), - Keyword::COPY => Ok(self.parse_copy()?), + Keyword::UPDATE => Ok(Update(self.parse_update()?)), + Keyword::ALTER => Ok(AlterTable(self.parse_alter()?)), + Keyword::COPY => Ok(Copy(self.parse_copy()?)), Keyword::SET => Ok(self.parse_set()?), Keyword::SHOW => Ok(self.parse_show()?), - Keyword::START => Ok(self.parse_start_transaction()?), + Keyword::START => Ok(StartTransaction(self.parse_start_transaction()?)), // `BEGIN` is a nonstandard but common alias for the // standard `START TRANSACTION` statement. It is supported // by at least PostgreSQL and MySQL. - Keyword::BEGIN => Ok(self.parse_begin()?), - Keyword::COMMIT => Ok(self.parse_commit()?), - Keyword::ROLLBACK => Ok(self.parse_rollback()?), - Keyword::ASSERT => Ok(self.parse_assert()?), + Keyword::BEGIN => Ok(StartTransaction(self.parse_begin()?)), + Keyword::COMMIT => Ok(Commit(self.parse_commit()?)), + Keyword::ROLLBACK => Ok(Rollback(self.parse_rollback()?)), + Keyword::ASSERT => Ok(Assert(self.parse_assert()?)), // `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific // syntaxes. They are used for Postgres prepared statement. - Keyword::DEALLOCATE => Ok(self.parse_deallocate()?), - Keyword::EXECUTE => Ok(self.parse_execute()?), - Keyword::PREPARE => Ok(self.parse_prepare()?), + Keyword::DEALLOCATE => Ok(Deallocate(self.parse_deallocate()?)), + Keyword::EXECUTE => Ok(Execute(self.parse_execute()?)), + Keyword::PREPARE => Ok(Prepare(self.parse_prepare()?)), Keyword::REPLACE if dialect_of!(self is SQLiteDialect ) => { self.prev_token(); Ok(self.parse_insert()?) @@ -186,7 +186,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_msck(&mut self) -> Result { + pub fn parse_msck(&mut self) -> Result { let repair = self.parse_keyword(Keyword::REPAIR); self.expect_keyword(Keyword::TABLE)?; let table_name = self.parse_object_name()?; @@ -206,14 +206,14 @@ impl<'a> Parser<'a> { Ok(pa) }) .unwrap_or_default(); - Ok(Statement::Msck { + Ok(Msck { repair, table_name, partition_action, }) } - pub fn parse_truncate(&mut self) -> Result { + pub fn parse_truncate(&mut self) -> Result { self.expect_keyword(Keyword::TABLE)?; let table_name = self.parse_object_name()?; let mut partitions = None; @@ -222,13 +222,13 @@ impl<'a> Parser<'a> { partitions = Some(self.parse_comma_separated(Parser::parse_expr)?); self.expect_token(&Token::RParen)?; } - Ok(Statement::Truncate { + Ok(Truncate { table_name, partitions, }) } - pub fn parse_analyze(&mut self) -> Result { + pub fn parse_analyze(&mut self) -> Result { self.expect_keyword(Keyword::TABLE)?; let table_name = self.parse_object_name()?; let mut for_columns = false; @@ -307,7 +307,7 @@ impl<'a> Parser<'a> { Ok(expr) } - pub fn parse_assert(&mut self) -> Result { + pub fn parse_assert(&mut self) -> Result { let condition = self.parse_expr()?; let message = if self.parse_keyword(Keyword::AS) { Some(self.parse_expr()?) @@ -315,7 +315,7 @@ impl<'a> Parser<'a> { None }; - Ok(Statement::Assert { condition, message }) + Ok(Assert { condition, message }) } /// Parse an expression prefix @@ -1263,37 +1263,42 @@ impl<'a> Parser<'a> { /// Parse a SQL CREATE statement pub fn parse_create(&mut self) -> Result { + use Statement::*; let or_replace = self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]); let temporary = self .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) .is_some(); if self.parse_keyword(Keyword::TABLE) { - self.parse_create_table(or_replace, temporary) + Ok(Statement::CreateTable( + self.parse_create_table(or_replace, temporary)?, + )) } else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { self.prev_token(); - self.parse_create_view(or_replace) + Ok(Statement::CreateView(self.parse_create_view(or_replace)?)) } else if self.parse_keyword(Keyword::EXTERNAL) { - self.parse_create_external_table(or_replace) + Ok(Statement::CreateTable( + self.parse_create_external_table(or_replace)?, + )) } else if or_replace { self.expected( "[EXTERNAL] TABLE or [MATERIALIZED] VIEW after CREATE OR REPLACE", self.peek_token(), ) } else if self.parse_keyword(Keyword::INDEX) { - self.parse_create_index(false) + Ok(CreateIndex(self.parse_create_index(false)?)) } else if self.parse_keywords(&[Keyword::UNIQUE, Keyword::INDEX]) { - self.parse_create_index(true) + Ok(CreateIndex(self.parse_create_index(true)?)) } else if self.parse_keyword(Keyword::VIRTUAL) { - self.parse_create_virtual_table() + Ok(CreateVirtualTable(self.parse_create_virtual_table()?)) } else if self.parse_keyword(Keyword::SCHEMA) { - self.parse_create_schema() + Ok(CreateSchema(self.parse_create_schema()?)) } else { self.expected("an object type after CREATE", self.peek_token()) } } /// SQLite-specific `CREATE VIRTUAL TABLE` - pub fn parse_create_virtual_table(&mut self) -> Result { + pub fn parse_create_virtual_table(&mut self) -> Result { self.expect_keyword(Keyword::TABLE)?; let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let table_name = self.parse_object_name()?; @@ -1304,7 +1309,7 @@ impl<'a> Parser<'a> { // definitions in a traditional CREATE TABLE statement", but // we don't implement that. let module_args = self.parse_parenthesized_column_list(Optional)?; - Ok(Statement::CreateVirtualTable { + Ok(CreateVirtualTable { name: table_name, if_not_exists, module_name, @@ -1312,16 +1317,16 @@ impl<'a> Parser<'a> { }) } - pub fn parse_create_schema(&mut self) -> Result { + pub fn parse_create_schema(&mut self) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let schema_name = self.parse_object_name()?; - Ok(Statement::CreateSchema { + Ok(CreateSchema { schema_name, if_not_exists, }) } - pub fn parse_create_database(&mut self) -> Result { + pub fn parse_create_database(&mut self) -> Result { let ine = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let db_name = self.parse_object_name()?; let mut location = None; @@ -1335,7 +1340,7 @@ impl<'a> Parser<'a> { _ => break, } } - Ok(Statement::CreateDatabase { + Ok(CreateDatabase { db_name, if_not_exists: ine, location, @@ -1346,7 +1351,7 @@ impl<'a> Parser<'a> { pub fn parse_create_external_table( &mut self, or_replace: bool, - ) -> Result { + ) -> Result { self.expect_keyword(Keyword::TABLE)?; let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let table_name = self.parse_object_name()?; @@ -1365,7 +1370,7 @@ impl<'a> Parser<'a> { }; let location = hive_formats.location.clone(); let table_properties = self.parse_options(Keyword::TBLPROPERTIES)?; - Ok(Statement::CreateTable { + Ok(CreateTable { name: table_name, columns, constraints, @@ -1401,7 +1406,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_create_view(&mut self, or_replace: bool) -> Result { + pub fn parse_create_view(&mut self, or_replace: bool) -> Result { let materialized = self.parse_keyword(Keyword::MATERIALIZED); self.expect_keyword(Keyword::VIEW)?; // Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet). @@ -1412,7 +1417,7 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::AS)?; let query = Box::new(self.parse_query()?); // Optional `WITH [ CASCADED | LOCAL ] CHECK OPTION` is widely supported here. - Ok(Statement::CreateView { + Ok(CreateView { name, columns, query, @@ -1530,7 +1535,7 @@ impl<'a> Parser<'a> { &mut self, or_replace: bool, temporary: bool, - ) -> Result { + ) -> Result { let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]); let table_name = self.parse_object_name()?; let like = if self.parse_keyword(Keyword::LIKE) || self.parse_keyword(Keyword::ILIKE) { @@ -1556,7 +1561,7 @@ impl<'a> Parser<'a> { None }; - Ok(Statement::CreateTable { + Ok(CreateTable { name: table_name, temporary, columns, @@ -2219,7 +2224,7 @@ impl<'a> Parser<'a> { }) } - pub fn parse_explain(&mut self) -> Result { + pub fn parse_explain(&mut self) -> Result { let analyze = self.parse_keyword(Keyword::ANALYZE); let verbose = self.parse_keyword(Keyword::VERBOSE); @@ -2533,7 +2538,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_show(&mut self) -> Result { + pub fn parse_show(&mut self) -> Result { if self .parse_one_of_keywords(&[ Keyword::EXTENDED, @@ -2544,13 +2549,13 @@ impl<'a> Parser<'a> { .is_some() { self.prev_token(); - Ok(self.parse_show_columns()?) + Ok(Statement::ShowColumns(self.parse_show_columns()?)) } else if self.parse_one_of_keywords(&[Keyword::CREATE]).is_some() { - Ok(self.parse_show_create()?) + Ok(Statement::ShowCreate(self.parse_show_create()?)) } else { - Ok(ShowVariable { + Ok(Statement::ShowVariable(ShowVariable { variable: self.parse_identifiers()?, - }) + })) } } @@ -3194,7 +3199,6 @@ impl<'a> Parser<'a> { }) } } - impl Word { pub fn to_ident(&self) -> Ident { Ident { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5545eaaff..e8ed54c73 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -64,12 +64,12 @@ fn parse_insert_values() { expected_rows: &[Vec], ) { match verified_stmt(sql) { - Statement::Insert { + Statement::Insert(Insert { table_name, columns, source, .. - } => { + }) => { assert_eq!(table_name.to_string(), expected_table_name); assert_eq!(columns.len(), expected_columns.len()); for (index, column) in columns.iter().enumerate() { @@ -108,7 +108,7 @@ fn parse_insert_sqlite() { .pop() .unwrap() { - Statement::Insert { or, .. } => assert_eq!(or, expected_action), + Statement::Insert(Insert { or, .. }) => assert_eq!(or, expected_action), _ => panic!("{}", sql), }; @@ -138,12 +138,12 @@ fn parse_insert_sqlite() { fn parse_update() { let sql = "UPDATE t SET a = 1, b = 2, c = 3 WHERE d"; match verified_stmt(sql) { - Statement::Update { + Statement::Update(Update { table_name, assignments, selection, .. - } => { + }) => { assert_eq!(table_name.to_string(), "t".to_string()); assert_eq!( assignments, @@ -201,7 +201,7 @@ fn parse_no_table_name() { fn parse_delete_statement() { let sql = "DELETE FROM \"table\""; match verified_stmt(sql) { - Statement::Delete { table_name, .. } => { + Statement::Delete(Delete { table_name, .. }) => { assert_eq!( ObjectName(vec![Ident::with_quote('"', "table")]), table_name @@ -217,11 +217,11 @@ fn parse_where_delete_statement() { let sql = "DELETE FROM foo WHERE name = 5"; match verified_stmt(sql) { - Statement::Delete { + Statement::Delete(Delete { table_name, selection, .. - } => { + }) => { assert_eq!(ObjectName(vec![Ident::new("foo")]), table_name); assert_eq!( @@ -1183,7 +1183,7 @@ fn parse_create_table() { FOREIGN KEY (lng) REFERENCES othertable4(longitude) ON UPDATE SET NULL)", ); match ast { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, columns, constraints, @@ -1193,7 +1193,7 @@ fn parse_create_table() { file_format: None, location: None, .. - } => { + }) => { assert_eq!("uk_cities", name.to_string()); assert_eq!( columns, @@ -1363,10 +1363,10 @@ fn parse_assert() { let sql = "ASSERT (SELECT COUNT(*) FROM my_table) > 0"; let ast = one_statement_parses_to(sql, "ASSERT (SELECT COUNT(*) FROM my_table) > 0"); match ast { - Statement::Assert { + Statement::Assert(Assert { condition: _condition, message, - } => { + }) => { assert_eq!(message, None); } _ => unreachable!(), @@ -1382,10 +1382,10 @@ fn parse_assert_message() { "ASSERT (SELECT COUNT(*) FROM my_table) > 0 AS 'No rows in my_table'", ); match ast { - Statement::Assert { + Statement::Assert(Assert { condition: _condition, message: Some(message), - } => { + }) => { match message { Expr::Value(Value::SingleQuotedString(s)) => assert_eq!(s, "No rows in my_table"), _ => unreachable!(), @@ -1400,7 +1400,7 @@ fn parse_create_schema() { let sql = "CREATE SCHEMA X"; match verified_stmt(sql) { - Statement::CreateSchema { schema_name, .. } => { + Statement::CreateSchema(CreateSchema { schema_name, .. }) => { assert_eq!(schema_name.to_string(), "X".to_owned()) } _ => unreachable!(), @@ -1412,7 +1412,7 @@ fn parse_drop_schema() { let sql = "DROP SCHEMA X"; match verified_stmt(sql) { - Statement::Drop { object_type, .. } => assert_eq!(object_type, ObjectType::Schema), + Statement::Drop(Drop { object_type, .. }) => assert_eq!(object_type, ObjectType::Schema), _ => unreachable!(), } } @@ -1422,7 +1422,7 @@ fn parse_create_table_as() { let sql = "CREATE TABLE t AS SELECT * FROM a"; match verified_stmt(sql) { - Statement::CreateTable { name, query, .. } => { + Statement::CreateTable(CreateTable { name, query, .. }) => { assert_eq!(name.to_string(), "t".to_string()); assert_eq!(query, Some(Box::new(verified_query("SELECT * FROM a")))); } @@ -1434,7 +1434,7 @@ fn parse_create_table_as() { // (without data types) in a CTAS, but we have yet to support that. let sql = "CREATE TABLE t (a INT, b INT) AS SELECT 1 AS b, 2 AS a"; match verified_stmt(sql) { - Statement::CreateTable { columns, query, .. } => { + Statement::CreateTable(CreateTable { columns, query, .. }) => { assert_eq!(columns.len(), 2); assert_eq!(columns[0].to_string(), "a INT".to_string()); assert_eq!(columns[1].to_string(), "b INT".to_string()); @@ -1452,9 +1452,9 @@ fn parse_create_or_replace_table() { let sql = "CREATE OR REPLACE TABLE t (a INT)"; match verified_stmt(sql) { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, or_replace, .. - } => { + }) => { assert_eq!(name.to_string(), "t".to_string()); assert!(or_replace); } @@ -1463,7 +1463,7 @@ fn parse_create_or_replace_table() { let sql = "CREATE TABLE t (a INT, b INT) AS SELECT 1 AS b, 2 AS a"; match verified_stmt(sql) { - Statement::CreateTable { columns, query, .. } => { + Statement::CreateTable(CreateTable { columns, query, .. }) => { assert_eq!(columns.len(), 2); assert_eq!(columns[0].to_string(), "a INT".to_string()); assert_eq!(columns[1].to_string(), "b INT".to_string()); @@ -1494,7 +1494,7 @@ fn parse_create_table_with_on_delete_on_update_2in_any_order() -> Result<(), Par fn parse_create_table_with_options() { let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)"; match verified_stmt(sql) { - Statement::CreateTable { with_options, .. } => { + Statement::CreateTable(CreateTable { with_options, .. }) => { assert_eq!( vec![ SqlOption { @@ -1535,7 +1535,7 @@ fn parse_create_external_table() { STORED AS TEXTFILE LOCATION '/tmp/example.csv'", ); match ast { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, columns, constraints, @@ -1545,7 +1545,7 @@ fn parse_create_external_table() { file_format, location, .. - } => { + }) => { assert_eq!("uk_cities", name.to_string()); assert_eq!( columns, @@ -1603,7 +1603,7 @@ fn parse_create_or_replace_external_table() { STORED AS TEXTFILE LOCATION '/tmp/example.csv'", ); match ast { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, columns, constraints, @@ -1614,7 +1614,7 @@ fn parse_create_or_replace_external_table() { location, or_replace, .. - } => { + }) => { assert_eq!("uk_cities", name.to_string()); assert_eq!( columns, @@ -1664,10 +1664,10 @@ fn parse_create_external_table_lowercase() { fn parse_alter_table() { let add_column = "ALTER TABLE tab ADD COLUMN foo TEXT;"; match one_statement_parses_to(add_column, "ALTER TABLE tab ADD COLUMN foo TEXT") { - Statement::AlterTable { + Statement::AlterTable(AlterTable { name, operation: AlterTableOperation::AddColumn { column_def }, - } => { + }) => { assert_eq!("tab", name.to_string()); assert_eq!("foo", column_def.name.to_string()); assert_eq!("TEXT", column_def.data_type.to_string()); @@ -1677,10 +1677,10 @@ fn parse_alter_table() { let rename_table = "ALTER TABLE tab RENAME TO new_tab"; match verified_stmt(rename_table) { - Statement::AlterTable { + Statement::AlterTable(AlterTable { name, operation: AlterTableOperation::RenameTable { table_name }, - } => { + }) => { assert_eq!("tab", name.to_string()); assert_eq!("new_tab", table_name.to_string()) } @@ -1689,14 +1689,14 @@ fn parse_alter_table() { let rename_column = "ALTER TABLE tab RENAME COLUMN foo TO new_foo"; match verified_stmt(rename_column) { - Statement::AlterTable { + Statement::AlterTable(AlterTable { name, operation: AlterTableOperation::RenameColumn { old_column_name, new_column_name, }, - } => { + }) => { assert_eq!("tab", name.to_string()); assert_eq!(old_column_name.to_string(), "foo"); assert_eq!(new_column_name.to_string(), "new_foo"); @@ -1722,10 +1722,10 @@ fn parse_alter_table_constraints() { fn check_one(constraint_text: &str) { match verified_stmt(&format!("ALTER TABLE tab ADD {}", constraint_text)) { - Statement::AlterTable { + Statement::AlterTable(AlterTable { name, operation: AlterTableOperation::AddConstraint(constraint), - } => { + }) => { assert_eq!("tab", name.to_string()); assert_eq!(constraint_text, constraint.to_string()); } @@ -1749,7 +1749,7 @@ fn parse_alter_table_drop_column() { fn check_one(constraint_text: &str) { match verified_stmt(&format!("ALTER TABLE tab {}", constraint_text)) { - Statement::AlterTable { + Statement::AlterTable(AlterTable { name, operation: AlterTableOperation::DropColumn { @@ -1757,7 +1757,7 @@ fn parse_alter_table_drop_column() { if_exists, cascade, }, - } => { + }) => { assert_eq!("tab", name.to_string()); assert_eq!("is_active", column_name.to_string()); assert!(if_exists); @@ -1802,11 +1802,11 @@ fn parse_scalar_function_in_projection() { fn run_explain_analyze(query: &str, expected_verbose: bool, expected_analyze: bool) { match verified_stmt(query) { - Statement::Explain { + Statement::Explain(Explain { analyze, verbose, statement, - } => { + }) => { assert_eq!(verbose, expected_verbose); assert_eq!(analyze, expected_analyze); assert_eq!("SELECT sqrt(id) FROM foo", statement.to_string()); @@ -2603,7 +2603,7 @@ fn parse_ctes() { // CTE in a view let sql = &format!("CREATE VIEW v AS {}", with); match verified_stmt(sql) { - Statement::CreateView { query, .. } => assert_ctes_in_select(&cte_sqls, &query), + Statement::CreateView(CreateView { query, .. }) => assert_ctes_in_select(&cte_sqls, &query), _ => panic!("Expected CREATE VIEW"), } // CTE in a CTE... @@ -2872,14 +2872,14 @@ fn parse_exists_subquery() { fn parse_create_view() { let sql = "CREATE VIEW myschema.myview AS SELECT foo FROM bar"; match verified_stmt(sql) { - Statement::CreateView { + Statement::CreateView(CreateView { name, columns, query, or_replace, materialized, with_options, - } => { + }) => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); @@ -2895,7 +2895,7 @@ fn parse_create_view() { fn parse_create_view_with_options() { let sql = "CREATE VIEW v WITH (foo = 'bar', a = 123) AS SELECT 1"; match verified_stmt(sql) { - Statement::CreateView { with_options, .. } => { + Statement::CreateView(CreateView { with_options, .. }) => { assert_eq!( vec![ SqlOption { @@ -2918,14 +2918,14 @@ fn parse_create_view_with_options() { fn parse_create_view_with_columns() { let sql = "CREATE VIEW v (has, cols) AS SELECT 1, 2"; match verified_stmt(sql) { - Statement::CreateView { + Statement::CreateView(CreateView { name, columns, or_replace, with_options, query, materialized, - } => { + }) => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![Ident::new("has"), Ident::new("cols")]); assert_eq!(with_options, vec![]); @@ -2940,14 +2940,14 @@ fn parse_create_view_with_columns() { fn parse_create_or_replace_view() { let sql = "CREATE OR REPLACE VIEW v AS SELECT 1"; match verified_stmt(sql) { - Statement::CreateView { + Statement::CreateView(CreateView { name, columns, or_replace, with_options, query, materialized, - } => { + }) => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); assert_eq!(with_options, vec![]); @@ -2967,14 +2967,14 @@ fn parse_create_or_replace_materialized_view() { // https://docs.snowflake.com/en/sql-reference/sql/create-materialized-view.html let sql = "CREATE OR REPLACE MATERIALIZED VIEW v AS SELECT 1"; match verified_stmt(sql) { - Statement::CreateView { + Statement::CreateView(CreateView { name, columns, or_replace, with_options, query, materialized, - } => { + }) => { assert_eq!("v", name.to_string()); assert_eq!(columns, vec![]); assert_eq!(with_options, vec![]); @@ -2990,14 +2990,14 @@ fn parse_create_or_replace_materialized_view() { fn parse_create_materialized_view() { let sql = "CREATE MATERIALIZED VIEW myschema.myview AS SELECT foo FROM bar"; match verified_stmt(sql) { - Statement::CreateView { + Statement::CreateView(CreateView { name, or_replace, columns, query, materialized, with_options, - } => { + }) => { assert_eq!("myschema.myview", name.to_string()); assert_eq!(Vec::::new(), columns); assert_eq!("SELECT foo FROM bar", query.to_string()); @@ -3013,13 +3013,13 @@ fn parse_create_materialized_view() { fn parse_drop_table() { let sql = "DROP TABLE foo"; match verified_stmt(sql) { - Statement::Drop { + Statement::Drop(Drop { object_type, if_exists, names, cascade, purge: _, - } => { + }) => { assert!(!if_exists); assert_eq!(ObjectType::Table, object_type); assert_eq!( @@ -3033,13 +3033,13 @@ fn parse_drop_table() { let sql = "DROP TABLE IF EXISTS foo, bar CASCADE"; match verified_stmt(sql) { - Statement::Drop { + Statement::Drop(Drop { object_type, if_exists, names, cascade, purge: _, - } => { + }) => { assert!(if_exists); assert_eq!(ObjectType::Table, object_type); assert_eq!( @@ -3068,9 +3068,9 @@ fn parse_drop_table() { fn parse_drop_view() { let sql = "DROP VIEW myschema.myview"; match verified_stmt(sql) { - Statement::Drop { + Statement::Drop(Drop { names, object_type, .. - } => { + }) => { assert_eq!( vec!["myschema.myview"], names.iter().map(ToString::to_string).collect::>() @@ -3318,7 +3318,7 @@ fn lateral_derived() { #[test] fn parse_start_transaction() { match verified_stmt("START TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE") { - Statement::StartTransaction { modes } => assert_eq!( + Statement::StartTransaction(StartTransaction { modes }) => assert_eq!( modes, vec![ TransactionMode::AccessMode(TransactionAccessMode::ReadOnly), @@ -3335,7 +3335,7 @@ fn parse_start_transaction() { "START TRANSACTION READ ONLY READ WRITE ISOLATION LEVEL SERIALIZABLE", "START TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE", ) { - Statement::StartTransaction { modes } => assert_eq!( + Statement::StartTransaction(StartTransaction { modes }) => assert_eq!( modes, vec![ TransactionMode::AccessMode(TransactionAccessMode::ReadOnly), @@ -3392,7 +3392,7 @@ fn parse_set_transaction() { // TRANSACTION, so no need to duplicate the tests here. We just do a quick // sanity check. match verified_stmt("SET TRANSACTION READ ONLY, READ WRITE, ISOLATION LEVEL SERIALIZABLE") { - Statement::SetTransaction { modes } => assert_eq!( + Statement::SetTransaction(SetTransaction { modes }) => assert_eq!( modes, vec![ TransactionMode::AccessMode(TransactionAccessMode::ReadOnly), @@ -3407,12 +3407,12 @@ fn parse_set_transaction() { #[test] fn parse_commit() { match verified_stmt("COMMIT") { - Statement::Commit { chain: false } => (), + Statement::Commit(Commit { chain: false }) => (), _ => unreachable!(), } match verified_stmt("COMMIT AND CHAIN") { - Statement::Commit { chain: true } => (), + Statement::Commit(Commit { chain: true }) => (), _ => unreachable!(), } @@ -3428,12 +3428,12 @@ fn parse_commit() { #[test] fn parse_rollback() { match verified_stmt("ROLLBACK") { - Statement::Rollback { chain: false } => (), + Statement::Rollback(Rollback { chain: false }) => (), _ => unreachable!(), } match verified_stmt("ROLLBACK AND CHAIN") { - Statement::Rollback { chain: true } => (), + Statement::Rollback(Rollback { chain: true }) => (), _ => unreachable!(), } @@ -3471,13 +3471,13 @@ fn parse_create_index() { }, ]; match verified_stmt(sql) { - Statement::CreateIndex { + Statement::CreateIndex(CreateIndex { name, table_name, columns, unique, if_not_exists, - } => { + }) => { assert_eq!("idx_name", name.to_string()); assert_eq!("test", table_name.to_string()); assert_eq!(indexed_columns, columns); @@ -3491,9 +3491,9 @@ fn parse_create_index() { fn parse_drop_index() { let sql = "DROP INDEX idx_a"; match verified_stmt(sql) { - Statement::Drop { + Statement::Drop(Drop { names, object_type, .. - } => { + }) => { assert_eq!( vec!["idx_a"], names.iter().map(ToString::to_string).collect::>() diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 3db902be1..7bef04c26 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -33,59 +33,59 @@ fn parse_show_columns() { let table_name = ObjectName(vec![Ident::new("mytable")]); assert_eq!( mysql_and_generic().verified_stmt("SHOW COLUMNS FROM mytable"), - Statement::ShowColumns { + Statement::ShowColumns(ShowColumns { extended: false, full: false, table_name: table_name.clone(), filter: None, - } + }) ); assert_eq!( mysql_and_generic().verified_stmt("SHOW COLUMNS FROM mydb.mytable"), - Statement::ShowColumns { + Statement::ShowColumns(ShowColumns { extended: false, full: false, table_name: ObjectName(vec![Ident::new("mydb"), Ident::new("mytable")]), filter: None, - } + }) ); assert_eq!( mysql_and_generic().verified_stmt("SHOW EXTENDED COLUMNS FROM mytable"), - Statement::ShowColumns { + Statement::ShowColumns(ShowColumns { extended: true, full: false, table_name: table_name.clone(), filter: None, - } + }) ); assert_eq!( mysql_and_generic().verified_stmt("SHOW FULL COLUMNS FROM mytable"), - Statement::ShowColumns { + Statement::ShowColumns(ShowColumns { extended: false, full: true, table_name: table_name.clone(), filter: None, - } + }) ); assert_eq!( mysql_and_generic().verified_stmt("SHOW COLUMNS FROM mytable LIKE 'pattern'"), - Statement::ShowColumns { + Statement::ShowColumns(ShowColumns { extended: false, full: false, table_name: table_name.clone(), filter: Some(ShowStatementFilter::Like("pattern".into())), - } + }) ); assert_eq!( mysql_and_generic().verified_stmt("SHOW COLUMNS FROM mytable WHERE 1 = 2"), - Statement::ShowColumns { + Statement::ShowColumns(ShowColumns { extended: false, full: false, table_name, filter: Some(ShowStatementFilter::Where( mysql_and_generic().verified_expr("1 = 2") )), - } + }) ); mysql_and_generic() .one_statement_parses_to("SHOW FIELDS FROM mytable", "SHOW COLUMNS FROM mytable"); @@ -114,10 +114,10 @@ fn parse_show_create() { ] { assert_eq!( mysql_and_generic().verified_stmt(format!("SHOW CREATE {} myident", obj_type).as_str()), - Statement::ShowCreate { + Statement::ShowCreate(ShowCreate { obj_type: obj_type.clone(), obj_name: obj_name.clone(), - } + }) ); } } @@ -126,7 +126,7 @@ fn parse_show_create() { fn parse_create_table_auto_increment() { let sql = "CREATE TABLE foo (bar INT PRIMARY KEY AUTO_INCREMENT)"; match mysql().verified_stmt(sql) { - Statement::CreateTable { name, columns, .. } => { + Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "foo"); assert_eq!( vec![ColumnDef { @@ -157,7 +157,7 @@ fn parse_create_table_auto_increment() { fn parse_quote_identifiers() { let sql = "CREATE TABLE `PRIMARY` (`BEGIN` INT PRIMARY KEY)"; match mysql().verified_stmt(sql) { - Statement::CreateTable { name, columns, .. } => { + Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "`PRIMARY`"); assert_eq!( vec![ColumnDef { @@ -180,7 +180,7 @@ fn parse_quote_identifiers() { fn parse_create_table_with_minimum_display_width() { let sql = "CREATE TABLE foo (bar_tinyint TINYINT(3), bar_smallint SMALLINT(5), bar_int INT(11), bar_bigint BIGINT(20))"; match mysql().verified_stmt(sql) { - Statement::CreateTable { name, columns, .. } => { + Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "foo"); assert_eq!( vec![ diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 2e66d313b..438a53d4e 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -37,7 +37,7 @@ fn parse_create_table_with_defaults() { active integer NOT NULL ) WITH (fillfactor = 20, user_catalog_table = true, autovacuum_vacuum_threshold = 100)"; match pg_and_generic().one_statement_parses_to(sql, "") { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, columns, constraints, @@ -47,7 +47,7 @@ fn parse_create_table_with_defaults() { file_format: None, location: None, .. - } => { + }) => { assert_eq!("public.customer", name.to_string()); assert_eq!( columns, @@ -243,12 +243,12 @@ fn parse_create_table_constraints_only() { let sql = "CREATE TABLE t (CONSTRAINT positive CHECK (2 > 1))"; let ast = pg_and_generic().verified_stmt(sql); match ast { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, columns, constraints, .. - } => { + }) => { assert_eq!("t", name.to_string()); assert!(columns.is_empty()); assert_eq!( @@ -265,11 +265,11 @@ fn parse_create_table_if_not_exists() { let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()"; let ast = pg_and_generic().verified_stmt(sql); match ast { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, if_not_exists: true, .. - } => { + }) => { assert_eq!("uk_cities", name.to_string()); } _ => unreachable!(), @@ -308,10 +308,10 @@ fn parse_create_schema_if_not_exists() { let sql = "CREATE SCHEMA IF NOT EXISTS schema_name"; let ast = pg_and_generic().verified_stmt(sql); match ast { - Statement::CreateSchema { + Statement::CreateSchema(CreateSchema { if_not_exists: true, schema_name, - } => assert_eq!("schema_name", schema_name.to_string()), + }) => assert_eq!("schema_name", schema_name.to_string()), _ => unreachable!(), } } @@ -321,11 +321,11 @@ fn parse_drop_schema_if_exists() { let sql = "DROP SCHEMA IF EXISTS schema_name"; let ast = pg().verified_stmt(sql); match ast { - Statement::Drop { + Statement::Drop(Drop { object_type, if_exists: true, .. - } => assert_eq!(object_type, ObjectType::Schema), + }) => assert_eq!(object_type, ObjectType::Schema), _ => unreachable!(), } } @@ -362,58 +362,58 @@ fn parse_set() { let stmt = pg_and_generic().verified_stmt("SET a = b"); assert_eq!( stmt, - Statement::SetVariable { + Statement::SetVariable(SetVariable { local: false, hivevar: false, variable: "a".into(), value: vec![SetVariableValue::Ident("b".into())], - } + }) ); let stmt = pg_and_generic().verified_stmt("SET a = 'b'"); assert_eq!( stmt, - Statement::SetVariable { + Statement::SetVariable(SetVariable { local: false, hivevar: false, variable: "a".into(), value: vec![SetVariableValue::Literal(Value::SingleQuotedString( "b".into() ))], - } + }) ); let stmt = pg_and_generic().verified_stmt("SET a = 0"); assert_eq!( stmt, - Statement::SetVariable { + Statement::SetVariable(SetVariable { local: false, hivevar: false, variable: "a".into(), value: vec![SetVariableValue::Literal(number("0"))], - } + }) ); let stmt = pg_and_generic().verified_stmt("SET a = DEFAULT"); assert_eq!( stmt, - Statement::SetVariable { + Statement::SetVariable(SetVariable { local: false, hivevar: false, variable: "a".into(), value: vec![SetVariableValue::Ident("DEFAULT".into())], - } + }) ); let stmt = pg_and_generic().verified_stmt("SET LOCAL a = b"); assert_eq!( stmt, - Statement::SetVariable { + Statement::SetVariable(SetVariable { local: true, hivevar: false, variable: "a".into(), value: vec![SetVariableValue::Ident("b".into())], - } + }) ); pg_and_generic().one_statement_parses_to("SET a TO b", "SET a = b"); @@ -446,17 +446,17 @@ fn parse_show() { let stmt = pg_and_generic().verified_stmt("SHOW a a"); assert_eq!( stmt, - Statement::ShowVariable { + Statement::ShowVariable(ShowVariable { variable: vec!["a".into(), "a".into()] - } + }) ); let stmt = pg_and_generic().verified_stmt("SHOW ALL ALL"); assert_eq!( stmt, - Statement::ShowVariable { + Statement::ShowVariable(ShowVariable { variable: vec!["ALL".into(), "ALL".into()] - } + }) ) } @@ -465,37 +465,37 @@ fn parse_deallocate() { let stmt = pg_and_generic().verified_stmt("DEALLOCATE a"); assert_eq!( stmt, - Statement::Deallocate { + Statement::Deallocate(Deallocate { name: "a".into(), prepare: false, - } + }) ); let stmt = pg_and_generic().verified_stmt("DEALLOCATE ALL"); assert_eq!( stmt, - Statement::Deallocate { + Statement::Deallocate(Deallocate { name: "ALL".into(), prepare: false, - } + }) ); let stmt = pg_and_generic().verified_stmt("DEALLOCATE PREPARE a"); assert_eq!( stmt, - Statement::Deallocate { + Statement::Deallocate(Deallocate { name: "a".into(), prepare: true, - } + }) ); let stmt = pg_and_generic().verified_stmt("DEALLOCATE PREPARE ALL"); assert_eq!( stmt, - Statement::Deallocate { + Statement::Deallocate(Deallocate { name: "ALL".into(), prepare: true, - } + }) ); } @@ -504,22 +504,22 @@ fn parse_execute() { let stmt = pg_and_generic().verified_stmt("EXECUTE a"); assert_eq!( stmt, - Statement::Execute { + Statement::Execute(Execute { name: "a".into(), parameters: vec![], - } + }) ); let stmt = pg_and_generic().verified_stmt("EXECUTE a(1, 't')"); assert_eq!( stmt, - Statement::Execute { + Statement::Execute(Execute { name: "a".into(), parameters: vec![ Expr::Value(number("1")), Expr::Value(Value::SingleQuotedString("t".to_string())) ], - } + }) ); } @@ -528,12 +528,12 @@ fn parse_prepare() { let stmt = pg_and_generic().verified_stmt("PREPARE a AS INSERT INTO customers VALUES (a1, a2, a3)"); let sub_stmt = match stmt { - Statement::Prepare { + Statement::Prepare(Prepare { name, data_types, statement, .. - } => { + }) => { assert_eq!(name, "a".into()); assert!(data_types.is_empty()); @@ -542,12 +542,12 @@ fn parse_prepare() { _ => unreachable!(), }; match sub_stmt.as_ref() { - Statement::Insert { + Statement::Insert(Insert { table_name, columns, source, .. - } => { + }) => { assert_eq!(table_name.to_string(), "customers"); assert!(columns.is_empty()); @@ -567,12 +567,12 @@ fn parse_prepare() { let stmt = pg_and_generic() .verified_stmt("PREPARE a (INT, TEXT) AS SELECT * FROM customers WHERE customers.id = a1"); let sub_stmt = match stmt { - Statement::Prepare { + Statement::Prepare(Prepare { name, data_types, statement, .. - } => { + }) => { assert_eq!(name, "a".into()); assert_eq!(data_types, vec![DataType::Int(None), DataType::Text]); diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index c08632a15..28c5c1664 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -27,7 +27,7 @@ use sqlparser::tokenizer::*; fn test_snowflake_create_table() { let sql = "CREATE TABLE _my_$table (am00unt number)"; match snowflake_and_generic().verified_stmt(sql) { - Statement::CreateTable { name, .. } => { + Statement::CreateTable(CreateTable { name, .. }) => { assert_eq!("_my_$table", name.to_string()); } _ => unreachable!(), diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 61436cb51..16bb5c21f 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -26,11 +26,11 @@ use sqlparser::tokenizer::Token; fn parse_create_table_without_rowid() { let sql = "CREATE TABLE t (a INT) WITHOUT ROWID"; match sqlite_and_generic().verified_stmt(sql) { - Statement::CreateTable { + Statement::CreateTable(CreateTable { name, without_rowid: true, .. - } => { + }) => { assert_eq!("t", name.to_string()); } _ => unreachable!(), @@ -41,12 +41,12 @@ fn parse_create_table_without_rowid() { fn parse_create_virtual_table() { let sql = "CREATE VIRTUAL TABLE IF NOT EXISTS t USING module_name (arg1, arg2)"; match sqlite_and_generic().verified_stmt(sql) { - Statement::CreateVirtualTable { + Statement::CreateVirtualTable(CreateVirtualTable { name, if_not_exists: true, module_name, module_args, - } => { + }) => { let args = vec![Ident::new("arg1"), Ident::new("arg2")]; assert_eq!("t", name.to_string()); assert_eq!("module_name", module_name.to_string()); @@ -63,7 +63,7 @@ fn parse_create_virtual_table() { fn parse_create_table_auto_increment() { let sql = "CREATE TABLE foo (bar INT PRIMARY KEY AUTOINCREMENT)"; match sqlite_and_generic().verified_stmt(sql) { - Statement::CreateTable { name, columns, .. } => { + Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "foo"); assert_eq!( vec![ColumnDef { @@ -94,7 +94,7 @@ fn parse_create_table_auto_increment() { fn parse_create_sqlite_quote() { let sql = "CREATE TABLE `PRIMARY` (\"KEY\" INT, [INDEX] INT)"; match sqlite().verified_stmt(sql) { - Statement::CreateTable { name, columns, .. } => { + Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "`PRIMARY`"); assert_eq!( vec![ From 8b5949effd8b3ba83ee0ee3b8b2acd3760cda0a3 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 01:08:29 -0400 Subject: [PATCH 05/11] fixing tests --- src/ast/data_type.rs | 8 ++++- src/ast/statement/display.rs | 60 ++++++++++++++++++------------------ tests/sqlparser_common.rs | 38 +++++++++++------------ tests/sqlparser_hive.rs | 4 +-- tests/sqlparser_mysql.rs | 4 +-- tests/sqlparser_postgres.rs | 6 ++-- tests/sqlparser_sqlite.rs | 6 ++-- 7 files changed, 66 insertions(+), 60 deletions(-) diff --git a/src/ast/data_type.rs b/src/ast/data_type.rs index 904f70279..8d4497519 100644 --- a/src/ast/data_type.rs +++ b/src/ast/data_type.rs @@ -107,7 +107,13 @@ impl fmt::Display for DataType { DataType::SmallInt(zerofill) => { format_type_with_optional_length(f, "SMALLINT", zerofill) } - DataType::Int(zerofill) => format_type_with_optional_length(f, "INTEGER", zerofill), + DataType::Int(zerofill) => { + if let Some(len) = zerofill { + write!(f, "INT({})", len) + } else { + write!(f, "INTEGER") + } + } DataType::BigInt(zerofill) => format_type_with_optional_length(f, "BIGINT", zerofill), DataType::Real => write!(f, "REAL"), DataType::Double => write!(f, "DOUBLE"), diff --git a/src/ast/statement/display.rs b/src/ast/statement/display.rs index 6d199a41e..bcf61d4f4 100644 --- a/src/ast/statement/display.rs +++ b/src/ast/statement/display.rs @@ -4,36 +4,36 @@ impl fmt::Display for Statement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Statement::*; match self { - Analyze(x) => write!(f, "{};", x), - Truncate(x) => write!(f, "{};", x), - Explain(x) => write!(f, "{};", x), - Query(x) => write!(f, "{};", x), - Msck(x) => write!(f, "{};", x), - Insert(x) => write!(f, "{};", x), - Directory(x) => write!(f, "{};", x), - Copy(x) => write!(f, "{};", x), - Update(x) => write!(f, "{};", x), - Delete(x) => write!(f, "{};", x), - CreateView(x) => write!(f, "{};", x), - CreateTable(x) => write!(f, "{};", x), - CreateVirtualTable(x) => write!(f, "{};", x), - CreateIndex(x) => write!(f, "{};", x), - AlterTable(x) => write!(f, "{};", x), - SetVariable(x) => write!(f, "{};", x), - ShowVariable(x) => write!(f, "{};", x), - ShowCreate(x) => write!(f, "{};", x), - ShowColumns(x) => write!(f, "{};", x), - StartTransaction(x) => write!(f, "{};", x), - SetTransaction(x) => write!(f, "{};", x), - Rollback(x) => write!(f, "{};", x), - Drop(x) => write!(f, "{};", x), - Commit(x) => write!(f, "{};", x), - CreateSchema(x) => write!(f, "{};", x), - CreateDatabase(x) => write!(f, "{};", x), - Assert(x) => write!(f, "{};", x), - Deallocate(x) => write!(f, "{};", x), - Execute(x) => write!(f, "{};", x), - Prepare(x) => write!(f, "{};", x), + Analyze(x) => write!(f, "{}", x), + Truncate(x) => write!(f, "{}", x), + Explain(x) => write!(f, "{}", x), + Query(x) => write!(f, "{}", x), + Msck(x) => write!(f, "{}", x), + Insert(x) => write!(f, "{}", x), + Directory(x) => write!(f, "{}", x), + Copy(x) => write!(f, "{}", x), + Update(x) => write!(f, "{}", x), + Delete(x) => write!(f, "{}", x), + CreateView(x) => write!(f, "{}", x), + CreateTable(x) => write!(f, "{}", x), + CreateVirtualTable(x) => write!(f, "{}", x), + CreateIndex(x) => write!(f, "{}", x), + AlterTable(x) => write!(f, "{}", x), + SetVariable(x) => write!(f, "{}", x), + ShowVariable(x) => write!(f, "{}", x), + ShowCreate(x) => write!(f, "{}", x), + ShowColumns(x) => write!(f, "{}", x), + StartTransaction(x) => write!(f, "{}", x), + SetTransaction(x) => write!(f, "{}", x), + Rollback(x) => write!(f, "{}", x), + Drop(x) => write!(f, "{}", x), + Commit(x) => write!(f, "{}", x), + CreateSchema(x) => write!(f, "{}", x), + CreateDatabase(x) => write!(f, "{}", x), + Assert(x) => write!(f, "{}", x), + Deallocate(x) => write!(f, "{}", x), + Execute(x) => write!(f, "{}", x), + Prepare(x) => write!(f, "{}", x), } } } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e8ed54c73..73c33dde4 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1160,9 +1160,9 @@ fn parse_create_table() { name VARCHAR(100) NOT NULL,\ lat DOUBLE NULL,\ lng DOUBLE, - constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), - ref INT REFERENCES othertable (a, b),\ - ref2 INT references othertable2 on delete cascade on update no action,\ + constrained INTEGER NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), + ref INTEGER REFERENCES othertable (a, b),\ + ref2 INTEGER references othertable2 on delete cascade on update no action,\ constraint fkey foreign key (lat) references othertable3 (lat) on delete restrict,\ constraint fkey2 foreign key (lat) references othertable4(lat) on delete no action on update restrict, \ foreign key (lat) references othertable4(lat) on update set default on delete cascade, \ @@ -1174,9 +1174,9 @@ fn parse_create_table() { name CHARACTER VARYING(100) NOT NULL, \ lat DOUBLE NULL, \ lng DOUBLE, \ - constrained INT NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), \ - ref INT REFERENCES othertable (a, b), \ - ref2 INT REFERENCES othertable2 ON DELETE CASCADE ON UPDATE NO ACTION, \ + constrained INTEGER NULL CONSTRAINT pkey PRIMARY KEY NOT NULL UNIQUE CHECK (constrained > 0), \ + ref INTEGER REFERENCES othertable (a, b), \ + ref2 INTEGER REFERENCES othertable2 ON DELETE CASCADE ON UPDATE NO ACTION, \ CONSTRAINT fkey FOREIGN KEY (lat) REFERENCES othertable3(lat) ON DELETE RESTRICT, \ CONSTRAINT fkey2 FOREIGN KEY (lat) REFERENCES othertable4(lat) ON DELETE NO ACTION ON UPDATE RESTRICT, \ FOREIGN KEY (lat) REFERENCES othertable4(lat) ON DELETE CASCADE ON UPDATE SET DEFAULT, \ @@ -1321,13 +1321,13 @@ fn parse_create_table() { _ => unreachable!(), } - let res = parse_sql_statements("CREATE TABLE t (a int NOT NULL GARBAGE)"); + let res = parse_sql_statements("CREATE TABLE t (a integer NOT NULL GARBAGE)"); assert!(res .unwrap_err() .to_string() .contains("Expected \',\' or \')\' after column definition, found: GARBAGE")); - let res = parse_sql_statements("CREATE TABLE t (a int NOT NULL CONSTRAINT foo)"); + let res = parse_sql_statements("CREATE TABLE t (a integer NOT NULL CONSTRAINT foo)"); assert!(res .unwrap_err() .to_string() @@ -1432,12 +1432,12 @@ fn parse_create_table_as() { // BigQuery allows specifying table schema in CTAS // ANSI SQL and PostgreSQL let you only specify the list of columns // (without data types) in a CTAS, but we have yet to support that. - let sql = "CREATE TABLE t (a INT, b INT) AS SELECT 1 AS b, 2 AS a"; + let sql = "CREATE TABLE t (a INTEGER, b INTEGER) AS SELECT 1 AS b, 2 AS a"; match verified_stmt(sql) { Statement::CreateTable(CreateTable { columns, query, .. }) => { assert_eq!(columns.len(), 2); - assert_eq!(columns[0].to_string(), "a INT".to_string()); - assert_eq!(columns[1].to_string(), "b INT".to_string()); + assert_eq!(columns[0].to_string(), "a INTEGER".to_string()); + assert_eq!(columns[1].to_string(), "b INTEGER".to_string()); assert_eq!( query, Some(Box::new(verified_query("SELECT 1 AS b, 2 AS a"))) @@ -1449,7 +1449,7 @@ fn parse_create_table_as() { #[test] fn parse_create_or_replace_table() { - let sql = "CREATE OR REPLACE TABLE t (a INT)"; + let sql = "CREATE OR REPLACE TABLE t (a INTEGER)"; match verified_stmt(sql) { Statement::CreateTable(CreateTable { @@ -1461,12 +1461,12 @@ fn parse_create_or_replace_table() { _ => unreachable!(), } - let sql = "CREATE TABLE t (a INT, b INT) AS SELECT 1 AS b, 2 AS a"; + let sql = "CREATE TABLE t (a INTEGER, b INTEGER) AS SELECT 1 AS b, 2 AS a"; match verified_stmt(sql) { Statement::CreateTable(CreateTable { columns, query, .. }) => { assert_eq!(columns.len(), 2); - assert_eq!(columns[0].to_string(), "a INT".to_string()); - assert_eq!(columns[1].to_string(), "b INT".to_string()); + assert_eq!(columns[0].to_string(), "a INTEGER".to_string()); + assert_eq!(columns[1].to_string(), "b INTEGER".to_string()); assert_eq!( query, Some(Box::new(verified_query("SELECT 1 AS b, 2 AS a"))) @@ -1492,7 +1492,7 @@ fn parse_create_table_with_on_delete_on_update_2in_any_order() -> Result<(), Par #[test] fn parse_create_table_with_options() { - let sql = "CREATE TABLE t (c INT) WITH (foo = 'bar', a = 123)"; + let sql = "CREATE TABLE t (c INTEGER) WITH (foo = 'bar', a = 123)"; match verified_stmt(sql) { Statement::CreateTable(CreateTable { with_options, .. }) => { assert_eq!( @@ -1516,7 +1516,7 @@ fn parse_create_table_with_options() { #[test] fn parse_create_table_trailing_comma() { let sql = "CREATE TABLE foo (bar int,)"; - all_dialects().one_statement_parses_to(sql, "CREATE TABLE foo (bar INT)"); + all_dialects().one_statement_parses_to(sql, "CREATE TABLE foo (bar INTEGER)"); } #[test] @@ -1731,7 +1731,7 @@ fn parse_alter_table_constraints() { } _ => unreachable!(), } - verified_stmt(&format!("CREATE TABLE foo (id INT, {})", constraint_text)); + verified_stmt(&format!("CREATE TABLE foo (id INTEGER, {})", constraint_text)); } } @@ -2767,7 +2767,7 @@ fn parse_multiple_statements() { ); test_with("DELETE FROM foo", "SELECT", " bar"); test_with("INSERT INTO foo VALUES (1)", "SELECT", " bar"); - test_with("CREATE TABLE foo (baz INT)", "SELECT", " bar"); + test_with("CREATE TABLE foo (baz INTEGER)", "SELECT", " bar"); // Make sure that empty statements do not cause an error: let res = parse_sql_statements(";;"); assert_eq!(0, res.unwrap().len()); diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 585be989b..a32524830 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -160,8 +160,8 @@ fn decimal_precision() { #[test] fn create_temp_table() { - let query = "CREATE TEMPORARY TABLE db.table (a INT NOT NULL)"; - let query2 = "CREATE TEMP TABLE db.table (a INT NOT NULL)"; + let query = "CREATE TEMPORARY TABLE db.table (a INTEGER NOT NULL)"; + let query2 = "CREATE TEMP TABLE db.table (a INTEGER NOT NULL)"; hive().verified_stmt(query); hive().one_statement_parses_to(query2, query); diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 7bef04c26..9fdea2389 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -124,7 +124,7 @@ fn parse_show_create() { #[test] fn parse_create_table_auto_increment() { - let sql = "CREATE TABLE foo (bar INT PRIMARY KEY AUTO_INCREMENT)"; + let sql = "CREATE TABLE foo (bar INTEGER PRIMARY KEY AUTO_INCREMENT)"; match mysql().verified_stmt(sql) { Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "foo"); @@ -155,7 +155,7 @@ fn parse_create_table_auto_increment() { #[test] fn parse_quote_identifiers() { - let sql = "CREATE TABLE `PRIMARY` (`BEGIN` INT PRIMARY KEY)"; + let sql = "CREATE TABLE `PRIMARY` (`BEGIN` INTEGER PRIMARY KEY)"; match mysql().verified_stmt(sql) { Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "`PRIMARY`"); diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 438a53d4e..c763f290d 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -203,7 +203,7 @@ fn parse_create_table_from_pg_dump() { active integer )"; pg().one_statement_parses_to(sql, "CREATE TABLE public.customer (\ - customer_id INT DEFAULT nextval(CAST('public.customer_customer_id_seq' AS REGCLASS)) NOT NULL, \ + customer_id INTEGER DEFAULT nextval(CAST('public.customer_customer_id_seq' AS REGCLASS)) NOT NULL, \ store_id SMALLINT NOT NULL, \ first_name CHARACTER VARYING(45) NOT NULL, \ last_name CHARACTER VARYING(45) NOT NULL, \ @@ -214,7 +214,7 @@ fn parse_create_table_from_pg_dump() { create_date1 DATE DEFAULT CAST(CAST('now' AS TEXT) AS DATE) NOT NULL, \ last_update TIMESTAMP DEFAULT now(), \ release_year public.year, \ - active INT\ + active INTEGER\ )"); } @@ -565,7 +565,7 @@ fn parse_prepare() { }; let stmt = pg_and_generic() - .verified_stmt("PREPARE a (INT, TEXT) AS SELECT * FROM customers WHERE customers.id = a1"); + .verified_stmt("PREPARE a (INTEGER, TEXT) AS SELECT * FROM customers WHERE customers.id = a1"); let sub_stmt = match stmt { Statement::Prepare(Prepare { name, diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 16bb5c21f..43c7b904f 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -24,7 +24,7 @@ use sqlparser::tokenizer::Token; #[test] fn parse_create_table_without_rowid() { - let sql = "CREATE TABLE t (a INT) WITHOUT ROWID"; + let sql = "CREATE TABLE t (a INTEGER) WITHOUT ROWID"; match sqlite_and_generic().verified_stmt(sql) { Statement::CreateTable(CreateTable { name, @@ -61,7 +61,7 @@ fn parse_create_virtual_table() { #[test] fn parse_create_table_auto_increment() { - let sql = "CREATE TABLE foo (bar INT PRIMARY KEY AUTOINCREMENT)"; + let sql = "CREATE TABLE foo (bar INTEGER PRIMARY KEY AUTOINCREMENT)"; match sqlite_and_generic().verified_stmt(sql) { Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "foo"); @@ -92,7 +92,7 @@ fn parse_create_table_auto_increment() { #[test] fn parse_create_sqlite_quote() { - let sql = "CREATE TABLE `PRIMARY` (\"KEY\" INT, [INDEX] INT)"; + let sql = "CREATE TABLE `PRIMARY` (\"KEY\" INTEGER, [INDEX] INTEGER)"; match sqlite().verified_stmt(sql) { Statement::CreateTable(CreateTable { name, columns, .. }) => { assert_eq!(name.to_string(), "`PRIMARY`"); From b461ff5ac68cc1b48e454d28202e36aace9d5046 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 10:09:04 -0400 Subject: [PATCH 06/11] boxing CreateTable to use less space --- src/ast/mod.rs | 5 +- src/ast/statement/mod.rs | 2 +- src/parser.rs | 4 +- tests/sqlparser_common.rs | 96 ++++++++++++++++++------------------ tests/sqlparser_mysql.rs | 12 +++-- tests/sqlparser_postgres.rs | 43 +++++++--------- tests/sqlparser_snowflake.rs | 4 +- tests/sqlparser_sqlite.rs | 17 ++++--- 8 files changed, 94 insertions(+), 89 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 084f0aaa6..15a27cbab 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -547,7 +547,10 @@ impl fmt::Display for ShowCreateObject { } } - +#[test] +fn func() { + dbg!(std::mem::size_of::()); +} /// SQL assignment `foo = expr` as used in SQLUpdate #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/ast/statement/mod.rs b/src/ast/statement/mod.rs index 8ea65cfd0..6870debb9 100644 --- a/src/ast/statement/mod.rs +++ b/src/ast/statement/mod.rs @@ -26,7 +26,7 @@ pub enum Statement { /// CREATE VIEW CreateView(CreateView), /// CREATE TABLE - CreateTable(CreateTable), + CreateTable(Box), /// SQLite's `CREATE VIRTUAL TABLE .. USING ()` CreateVirtualTable(CreateVirtualTable), /// CREATE INDEX diff --git a/src/parser.rs b/src/parser.rs index 65895f44d..b57e74a44 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1270,14 +1270,14 @@ impl<'a> Parser<'a> { .is_some(); if self.parse_keyword(Keyword::TABLE) { Ok(Statement::CreateTable( - self.parse_create_table(or_replace, temporary)?, + Box::new(self.parse_create_table(or_replace, temporary)?), )) } else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { self.prev_token(); Ok(Statement::CreateView(self.parse_create_view(or_replace)?)) } else if self.parse_keyword(Keyword::EXTERNAL) { Ok(Statement::CreateTable( - self.parse_create_external_table(or_replace)?, + Box::new(self.parse_create_external_table(or_replace)?), )) } else if or_replace { self.expected( diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 73c33dde4..95493337c 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1183,17 +1183,15 @@ fn parse_create_table() { FOREIGN KEY (lng) REFERENCES othertable4(longitude) ON UPDATE SET NULL)", ); match ast { - Statement::CreateTable(CreateTable { - name, - columns, - constraints, - with_options, - if_not_exists: false, - external: false, - file_format: None, - location: None, - .. - }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; + let constraints = table.constraints; + let with_options = table.with_options; + assert!(!table.external); + assert!(!table.if_not_exists); + assert!(table.file_format.is_none()); + assert!(table.location.is_none()); assert_eq!("uk_cities", name.to_string()); assert_eq!( columns, @@ -1422,9 +1420,12 @@ fn parse_create_table_as() { let sql = "CREATE TABLE t AS SELECT * FROM a"; match verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, query, .. }) => { - assert_eq!(name.to_string(), "t".to_string()); - assert_eq!(query, Some(Box::new(verified_query("SELECT * FROM a")))); + Statement::CreateTable(table) => { + assert_eq!(table.name.to_string(), "t".to_string()); + assert_eq!( + table.query, + Some(Box::new(verified_query("SELECT * FROM a"))) + ); } _ => unreachable!(), } @@ -1434,7 +1435,9 @@ fn parse_create_table_as() { // (without data types) in a CTAS, but we have yet to support that. let sql = "CREATE TABLE t (a INTEGER, b INTEGER) AS SELECT 1 AS b, 2 AS a"; match verified_stmt(sql) { - Statement::CreateTable(CreateTable { columns, query, .. }) => { + Statement::CreateTable(table) => { + let columns = table.columns; + let query = table.query; assert_eq!(columns.len(), 2); assert_eq!(columns[0].to_string(), "a INTEGER".to_string()); assert_eq!(columns[1].to_string(), "b INTEGER".to_string()); @@ -1452,23 +1455,22 @@ fn parse_create_or_replace_table() { let sql = "CREATE OR REPLACE TABLE t (a INTEGER)"; match verified_stmt(sql) { - Statement::CreateTable(CreateTable { - name, or_replace, .. - }) => { - assert_eq!(name.to_string(), "t".to_string()); - assert!(or_replace); + Statement::CreateTable(table) => { + assert_eq!(table.name.to_string(), "t".to_string()); + assert!(table.or_replace); } _ => unreachable!(), } let sql = "CREATE TABLE t (a INTEGER, b INTEGER) AS SELECT 1 AS b, 2 AS a"; match verified_stmt(sql) { - Statement::CreateTable(CreateTable { columns, query, .. }) => { + Statement::CreateTable(table) => { + let columns = table.columns; assert_eq!(columns.len(), 2); assert_eq!(columns[0].to_string(), "a INTEGER".to_string()); assert_eq!(columns[1].to_string(), "b INTEGER".to_string()); assert_eq!( - query, + table.query, Some(Box::new(verified_query("SELECT 1 AS b, 2 AS a"))) ); } @@ -1494,7 +1496,8 @@ fn parse_create_table_with_on_delete_on_update_2in_any_order() -> Result<(), Par fn parse_create_table_with_options() { let sql = "CREATE TABLE t (c INTEGER) WITH (foo = 'bar', a = 123)"; match verified_stmt(sql) { - Statement::CreateTable(CreateTable { with_options, .. }) => { + Statement::CreateTable(table) => { + let with_options = table.with_options; assert_eq!( vec![ SqlOption { @@ -1535,17 +1538,15 @@ fn parse_create_external_table() { STORED AS TEXTFILE LOCATION '/tmp/example.csv'", ); match ast { - Statement::CreateTable(CreateTable { - name, - columns, - constraints, - with_options, - if_not_exists, - external, - file_format, - location, - .. - }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; + let constraints = table.constraints; + let with_options = table.with_options; + let if_not_exists = table.if_not_exists; + let external = table.external; + let file_format = table.file_format; + let location = table.location; assert_eq!("uk_cities", name.to_string()); assert_eq!( columns, @@ -1603,18 +1604,16 @@ fn parse_create_or_replace_external_table() { STORED AS TEXTFILE LOCATION '/tmp/example.csv'", ); match ast { - Statement::CreateTable(CreateTable { - name, - columns, - constraints, - with_options, - if_not_exists, - external, - file_format, - location, - or_replace, - .. - }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; + let constraints = table.constraints; + let with_options = table.with_options; + let if_not_exists = table.if_not_exists; + let external = table.external; + let file_format = table.file_format; + let location = table.location; + let or_replace = table.or_replace; assert_eq!("uk_cities", name.to_string()); assert_eq!( columns, @@ -1731,7 +1730,10 @@ fn parse_alter_table_constraints() { } _ => unreachable!(), } - verified_stmt(&format!("CREATE TABLE foo (id INTEGER, {})", constraint_text)); + verified_stmt(&format!( + "CREATE TABLE foo (id INTEGER, {})", + constraint_text + )); } } diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 9fdea2389..2c1535d15 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -126,7 +126,9 @@ fn parse_show_create() { fn parse_create_table_auto_increment() { let sql = "CREATE TABLE foo (bar INTEGER PRIMARY KEY AUTO_INCREMENT)"; match mysql().verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, columns, .. }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; assert_eq!(name.to_string(), "foo"); assert_eq!( vec![ColumnDef { @@ -157,7 +159,9 @@ fn parse_create_table_auto_increment() { fn parse_quote_identifiers() { let sql = "CREATE TABLE `PRIMARY` (`BEGIN` INTEGER PRIMARY KEY)"; match mysql().verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, columns, .. }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; assert_eq!(name.to_string(), "`PRIMARY`"); assert_eq!( vec![ColumnDef { @@ -180,7 +184,9 @@ fn parse_quote_identifiers() { fn parse_create_table_with_minimum_display_width() { let sql = "CREATE TABLE foo (bar_tinyint TINYINT(3), bar_smallint SMALLINT(5), bar_int INT(11), bar_bigint BIGINT(20))"; match mysql().verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, columns, .. }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; assert_eq!(name.to_string(), "foo"); assert_eq!( vec![ diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index c763f290d..3d177ff0a 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -37,17 +37,14 @@ fn parse_create_table_with_defaults() { active integer NOT NULL ) WITH (fillfactor = 20, user_catalog_table = true, autovacuum_vacuum_threshold = 100)"; match pg_and_generic().one_statement_parses_to(sql, "") { - Statement::CreateTable(CreateTable { - name, - columns, - constraints, - with_options, - if_not_exists: false, - external: false, - file_format: None, - location: None, - .. - }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; + let constraints = table.constraints; + let with_options = table.with_options; + + assert!(!table.external); + assert!(!table.if_not_exists); assert_eq!("public.customer", name.to_string()); assert_eq!( columns, @@ -243,12 +240,10 @@ fn parse_create_table_constraints_only() { let sql = "CREATE TABLE t (CONSTRAINT positive CHECK (2 > 1))"; let ast = pg_and_generic().verified_stmt(sql); match ast { - Statement::CreateTable(CreateTable { - name, - columns, - constraints, - .. - }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; + let constraints = table.constraints; assert_eq!("t", name.to_string()); assert!(columns.is_empty()); assert_eq!( @@ -265,12 +260,9 @@ fn parse_create_table_if_not_exists() { let sql = "CREATE TABLE IF NOT EXISTS uk_cities ()"; let ast = pg_and_generic().verified_stmt(sql); match ast { - Statement::CreateTable(CreateTable { - name, - if_not_exists: true, - .. - }) => { - assert_eq!("uk_cities", name.to_string()); + Statement::CreateTable(table) => { + assert!(table.if_not_exists); + assert_eq!("uk_cities", table.name.to_string()); } _ => unreachable!(), } @@ -564,8 +556,9 @@ fn parse_prepare() { _ => unreachable!(), }; - let stmt = pg_and_generic() - .verified_stmt("PREPARE a (INTEGER, TEXT) AS SELECT * FROM customers WHERE customers.id = a1"); + let stmt = pg_and_generic().verified_stmt( + "PREPARE a (INTEGER, TEXT) AS SELECT * FROM customers WHERE customers.id = a1", + ); let sub_stmt = match stmt { Statement::Prepare(Prepare { name, diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 28c5c1664..967d5bd94 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -27,8 +27,8 @@ use sqlparser::tokenizer::*; fn test_snowflake_create_table() { let sql = "CREATE TABLE _my_$table (am00unt number)"; match snowflake_and_generic().verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, .. }) => { - assert_eq!("_my_$table", name.to_string()); + Statement::CreateTable(table) => { + assert_eq!("_my_$table", table.name.to_string()); } _ => unreachable!(), } diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs index 43c7b904f..a72531e13 100644 --- a/tests/sqlparser_sqlite.rs +++ b/tests/sqlparser_sqlite.rs @@ -26,12 +26,9 @@ use sqlparser::tokenizer::Token; fn parse_create_table_without_rowid() { let sql = "CREATE TABLE t (a INTEGER) WITHOUT ROWID"; match sqlite_and_generic().verified_stmt(sql) { - Statement::CreateTable(CreateTable { - name, - without_rowid: true, - .. - }) => { - assert_eq!("t", name.to_string()); + Statement::CreateTable(table) => { + assert!(table.without_rowid); + assert_eq!("t", table.name.to_string()); } _ => unreachable!(), } @@ -63,7 +60,9 @@ fn parse_create_virtual_table() { fn parse_create_table_auto_increment() { let sql = "CREATE TABLE foo (bar INTEGER PRIMARY KEY AUTOINCREMENT)"; match sqlite_and_generic().verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, columns, .. }) => { + Statement::CreateTable(table) => { + let name = table.name; + let columns = table.columns; assert_eq!(name.to_string(), "foo"); assert_eq!( vec![ColumnDef { @@ -94,7 +93,9 @@ fn parse_create_table_auto_increment() { fn parse_create_sqlite_quote() { let sql = "CREATE TABLE `PRIMARY` (\"KEY\" INTEGER, [INDEX] INTEGER)"; match sqlite().verified_stmt(sql) { - Statement::CreateTable(CreateTable { name, columns, .. }) => { + Statement::CreateTable(table) => { + let columns = table.columns; + let name = table.name; assert_eq!(name.to_string(), "`PRIMARY`"); assert_eq!( vec![ From bf66af37f3c78db34bb8dda87ee38f89637c6cf4 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 11:18:15 -0400 Subject: [PATCH 07/11] Moving Expr so it holds structs --- src/ast/expression/display.rs | 161 ++++++++++++++++++++++ src/ast/expression/mod.rs | 184 +++++++++++++++++++++++++ src/ast/mod.rs | 252 +--------------------------------- src/parser.rs | 90 ++++++------ tests/sqlparser_common.rs | 233 ++++++++++++++++--------------- tests/sqlparser_postgres.rs | 18 +-- 6 files changed, 521 insertions(+), 417 deletions(-) create mode 100644 src/ast/expression/display.rs create mode 100644 src/ast/expression/mod.rs diff --git a/src/ast/expression/display.rs b/src/ast/expression/display.rs new file mode 100644 index 000000000..d1f72608a --- /dev/null +++ b/src/ast/expression/display.rs @@ -0,0 +1,161 @@ +use super::*; + +impl fmt::Display for Expr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Expr::Identifier(s) => write!(f, "{}", s), + Expr::MapAccess(x) => write!(f, "{}", x), + 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(x) => write!(f, "{}", x), + Expr::InSubquery(x) => write!(f, "{}", x), + Expr::Between(x) => write!(f, "{}", x), + Expr::BinaryOp(x) => write!(f, "{}", x), + Expr::UnaryOp(x) => write!(f, "{}", x), + Expr::Cast(x) => write!(f, "{}", x), + Expr::TryCast(x) => write!(f, "{}", x), + Expr::Extract(x) => write!(f, "{}", x), + Expr::Collate(x) => write!(f, "{}", x), + Expr::Nested(ast) => write!(f, "({})", ast), + Expr::Value(v) => write!(f, "{}", v), + Expr::TypedString(x) => write!(f, "{}", x), + Expr::Function(fun) => write!(f, "{}", fun), + Expr::Case(x) => write!(f, "{}", x), + Expr::Exists(s) => write!(f, "EXISTS ({})", s), + Expr::Subquery(s) => write!(f, "({})", s), + Expr::ListAgg(listagg) => write!(f, "{}", listagg), + Expr::Substring(x) => write!(f, "{}", x), + Expr::Trim(x) => write!(f, "{}", x), + } + } +} + +impl fmt::Display for Trim { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TRIM(")?; + if let Some((ref ident, ref trim_char)) = self.trim_where { + write!(f, "{} {} FROM {}", ident, trim_char, self.expr)?; + } else { + write!(f, "{}", self.expr)?; + } + write!(f, ")") + } +} + +impl fmt::Display for Substring { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SUBSTRING({}", self.expr)?; + if let Some(ref from_part) = self.substring_from { + write!(f, " FROM {}", from_part)?; + } + if let Some(ref from_part) = self.substring_for { + write!(f, " FOR {}", from_part)?; + } + + write!(f, ")") + } +} + +impl fmt::Display for Case { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CASE")?; + if let Some(ref operand) = self.operand { + write!(f, " {}", operand)?; + } + for (c, r) in self.conditions.iter().zip(&self.results) { + write!(f, " WHEN {} THEN {}", c, r)?; + } + + if let Some(ref else_result) = self.else_result { + write!(f, " ELSE {}", else_result)?; + } + write!(f, " END") + } +} + +impl fmt::Display for TypedString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.data_type)?; + write!(f, " '{}'", &value::escape_single_quote_string(&self.value)) + } +} + +impl fmt::Display for Collate { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} COLLATE {}", self.expr, self.collation) + } +} +impl fmt::Display for UnaryOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.op == UnaryOperator::PGPostfixFactorial { + write!(f, "{}{}", self.expr, self.op) + } else { + write!(f, "{} {}", self.op, self.expr) + } + } +} +impl fmt::Display for InSubquery { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} {}IN ({})", + self.expr, + if self.negated { "NOT " } else { "" }, + self.subquery + ) + } +} +impl fmt::Display for Between { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} {}BETWEEN {} AND {}", + self.expr, + if self.negated { "NOT " } else { "" }, + self.low, + self.high + ) + } +} +impl fmt::Display for InList { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{} {}IN ({})", + self.expr, + if self.negated { "NOT " } else { "" }, + display_comma_separated(&self.list) + ) + } +} +impl fmt::Display for BinaryOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {} {}", self.left, self.op, self.right) + } +} +impl fmt::Display for MapAccess { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}[\"{}\"]", self.column, self.key) + } +} + +impl fmt::Display for Cast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CAST({} AS {})", self.expr, self.data_type) + } +} + +impl fmt::Display for TryCast { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TRY_CAST({} AS {})", self.expr, self.data_type) + } +} + +impl fmt::Display for Extract { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "EXTRACT({} FROM {})", self.field, self.expr) + } +} diff --git a/src/ast/expression/mod.rs b/src/ast/expression/mod.rs new file mode 100644 index 000000000..865998269 --- /dev/null +++ b/src/ast/expression/mod.rs @@ -0,0 +1,184 @@ +mod display; +use super::*; + +/// 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(InList), + /// `[ NOT ] IN (SELECT ...)` + InSubquery(InSubquery), + /// ` [ NOT ] BETWEEN AND ` + Between(Between), + /// Binary operation e.g. `1 + 1` or `foo > bar` + BinaryOp(BinaryOp), + /// Unary operation e.g. `NOT foo` + UnaryOp(UnaryOp), + /// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` + Cast(Cast), + /// TRY_CAST an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))` + // this differs from CAST in the choice of how to implement invalid conversions + TryCast(TryCast), + /// EXTRACT(DateTimeField FROM ) + Extract(Extract), + /// SUBSTRING( [FROM ] [FOR ]) + Substring(Substring), + /// TRIM([BOTH | LEADING | TRAILING] [FROM ])\ + /// Or\ + /// TRIM() + Trim(Trim), + /// `expr COLLATE collation` + Collate(Collate), + /// 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(TypedString), + MapAccess(MapAccess), + /// 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(Case), + /// 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), +} + +/// `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 +/// +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Case { + pub operand: Option>, + pub conditions: Vec, + pub results: Vec, + pub else_result: Option>, +} + +/// `[ NOT ] IN (val1, val2, ...)` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct InList { + pub expr: Box, + pub list: Vec, + pub negated: bool, +} +/// `[ NOT ] IN (SELECT ...)` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct InSubquery { + pub expr: Box, + pub subquery: Box, + pub negated: bool, +} +/// ` [ NOT ] BETWEEN AND ` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Between { + pub expr: Box, + pub negated: bool, + pub low: Box, + pub high: Box, +} +/// Binary operation e.g. `1 + 1` or `foo > bar` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct BinaryOp { + pub left: Box, + pub op: BinaryOperator, + pub right: Box, +} +/// Unary operation e.g. `NOT foo` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct UnaryOp { + pub op: UnaryOperator, + pub expr: Box, +} +/// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Cast { + pub expr: Box, + pub data_type: DataType, +} +/// TRY_CAST an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))` +// this differs from CAST in the choice of how to implement invalid conversions +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct TryCast { + pub expr: Box, + pub data_type: DataType, +} +/// EXTRACT(DateTimeField FROM ) +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Extract { + pub field: DateTimeField, + pub expr: Box, +} +/// SUBSTRING( [FROM ] [FOR ]) +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Substring { + pub expr: Box, + pub substring_from: Option>, + pub substring_for: Option>, +} +/// TRIM([BOTH | LEADING | TRAILING] [FROM ])\ +/// Or\ +/// TRIM() +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Trim { + pub expr: Box, + // ([BOTH | LEADING | TRAILING], ) + pub trim_where: Option<(TrimWhereField, Box)>, +} +/// `expr COLLATE collation` +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct Collate { + pub expr: Box, + pub collation: ObjectName, +} + +/// 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). +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct TypedString { + pub data_type: DataType, + pub value: String, +} +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct MapAccess { + pub column: Box, + pub key: String, +} diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 15a27cbab..e51e1d5a3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -14,11 +14,13 @@ mod data_type; mod ddl; +mod expression; mod operator; mod query; -mod value; mod statement; -pub use statement::*; +mod value; +pub use expression::*; +pub use statement::*; #[cfg(not(feature = "std"))] use alloc::{ @@ -149,250 +151,6 @@ impl fmt::Display for ObjectName { } } -/// 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, - }, - /// TRY_CAST an expression to a different data type e.g. `TRY_CAST(foo AS VARCHAR(123))` - // this differs from CAST in the choice of how to implement invalid conversions - TryCast { - expr: Box, - data_type: DataType, - }, - /// EXTRACT(DateTimeField FROM ) - Extract { - field: DateTimeField, - expr: Box, - }, - /// SUBSTRING( [FROM ] [FOR ]) - Substring { - expr: Box, - substring_from: Option>, - substring_for: Option>, - }, - /// TRIM([BOTH | LEADING | TRAILING] [FROM ])\ - /// Or\ - /// TRIM() - Trim { - expr: Box, - // ([BOTH | LEADING | TRAILING], ) - trim_where: Option<(TrimWhereField, 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, - }, - MapAccess { - column: Box, - key: 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::MapAccess { column, key } => write!(f, "{}[\"{}\"]", column, key), - 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 } => { - if op == &UnaryOperator::PGPostfixFactorial { - write!(f, "{}{}", expr, op) - } else { - write!(f, "{} {}", op, expr) - } - } - Expr::Cast { expr, data_type } => write!(f, "CAST({} AS {})", expr, data_type), - Expr::TryCast { expr, data_type } => write!(f, "TRY_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, - } => { - write!(f, "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)?; - } - write!(f, " END") - } - Expr::Exists(s) => write!(f, "EXISTS ({})", s), - Expr::Subquery(s) => write!(f, "({})", s), - Expr::ListAgg(listagg) => write!(f, "{}", listagg), - Expr::Substring { - expr, - substring_from, - substring_for, - } => { - write!(f, "SUBSTRING({}", expr)?; - if let Some(from_part) = substring_from { - write!(f, " FROM {}", from_part)?; - } - if let Some(from_part) = substring_for { - write!(f, " FOR {}", from_part)?; - } - - write!(f, ")") - } - Expr::Trim { expr, trim_where } => { - write!(f, "TRIM(")?; - if let Some((ident, trim_char)) = trim_where { - write!(f, "{} {} FROM {}", ident, trim_char, expr)?; - } else { - write!(f, "{}", expr)?; - } - - write!(f, ")") - } - } - } -} - /// A window specification (i.e. `OVER (PARTITION BY .. ORDER BY .. etc.)`) #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -549,7 +307,7 @@ impl fmt::Display for ShowCreateObject { #[test] fn func() { - dbg!(std::mem::size_of::()); + dbg!(std::mem::size_of::()); } /// SQL assignment `foo = expr` as used in SQLUpdate #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/parser.rs b/src/parser.rs index b57e74a44..b392cd868 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -347,10 +347,10 @@ impl<'a> Parser<'a> { // an unary negation `NOT ('a' LIKE 'b')`. To solve this, we don't accept the // `type 'string'` syntax for the custom data types at all. DataType::Custom(..) => parser_err!("dummy"), - data_type => Ok(Expr::TypedString { + data_type => Ok(Expr::TypedString(TypedString { data_type, value: parser.parse_literal_string()?, - }), + })), } })); @@ -369,10 +369,10 @@ impl<'a> Parser<'a> { Keyword::TRIM => self.parse_trim_expr(), Keyword::INTERVAL => self.parse_literal_interval(), Keyword::LISTAGG => self.parse_listagg_expr(), - Keyword::NOT => Ok(Expr::UnaryOp { + Keyword::NOT => Ok(Expr::UnaryOp(UnaryOp { op: UnaryOperator::Not, expr: Box::new(self.parse_subexpr(Self::UNARY_NOT_PREC)?), - }), + })), // Here `w` is a word, check if it's a part of a multi-part // identifier, a function call, or a simple identifier: _ => match self.peek_token() { @@ -411,10 +411,10 @@ impl<'a> Parser<'a> { } else { UnaryOperator::Minus }; - Ok(Expr::UnaryOp { + Ok(Expr::UnaryOp(UnaryOp { op, expr: Box::new(self.parse_subexpr(Self::PLUS_MINUS_PREC)?), - }) + })) } tok @ Token::DoubleExclamationMark | tok @ Token::PGSquareRoot @@ -431,10 +431,10 @@ impl<'a> Parser<'a> { Token::Tilde => UnaryOperator::PGBitwiseNot, _ => unreachable!(), }; - Ok(Expr::UnaryOp { + Ok(Expr::UnaryOp(UnaryOp { op, expr: Box::new(self.parse_subexpr(Self::PLUS_MINUS_PREC)?), - }) + })) } Token::Number(_, _) | Token::SingleQuotedString(_) @@ -459,10 +459,10 @@ impl<'a> Parser<'a> { }?; if self.parse_keyword(Keyword::COLLATE) { - Ok(Expr::Collate { + Ok(Expr::Collate(Collate { expr: Box::new(expr), collation: self.parse_object_name()?, - }) + })) } else { Ok(expr) } @@ -582,12 +582,12 @@ impl<'a> Parser<'a> { None }; self.expect_keyword(Keyword::END)?; - Ok(Expr::Case { + Ok(Expr::Case(Case { operand, conditions, results, else_result, - }) + })) } /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` @@ -597,10 +597,10 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::AS)?; let data_type = self.parse_data_type()?; self.expect_token(&Token::RParen)?; - Ok(Expr::Cast { + Ok(Expr::Cast(Cast { expr: Box::new(expr), data_type, - }) + })) } /// Parse a SQL TRY_CAST function e.g. `TRY_CAST(expr AS FLOAT)` @@ -610,10 +610,10 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::AS)?; let data_type = self.parse_data_type()?; self.expect_token(&Token::RParen)?; - Ok(Expr::TryCast { + Ok(Expr::TryCast(TryCast { expr: Box::new(expr), data_type, - }) + })) } /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. @@ -630,10 +630,10 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::FROM)?; let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; - Ok(Expr::Extract { + Ok(Expr::Extract(Extract { field, expr: Box::new(expr), - }) + })) } pub fn parse_substring_expr(&mut self) -> Result { @@ -650,11 +650,11 @@ impl<'a> Parser<'a> { } self.expect_token(&Token::RParen)?; - Ok(Expr::Substring { + Ok(Expr::Substring(Substring { expr: Box::new(expr), substring_from: from_expr.map(Box::new), substring_for: to_expr.map(Box::new), - }) + })) } /// TRIM (WHERE 'text' FROM 'text')\ @@ -676,10 +676,10 @@ impl<'a> Parser<'a> { let expr = self.parse_expr()?; self.expect_token(&Token::RParen)?; - Ok(Expr::Trim { + Ok(Expr::Trim(Trim { expr: Box::new(expr), trim_where: where_expr, - }) + })) } pub fn parse_trim_where(&mut self) -> Result { @@ -907,11 +907,11 @@ impl<'a> Parser<'a> { }; if let Some(op) = regular_binary_operator { - Ok(Expr::BinaryOp { + Ok(Expr::BinaryOp(BinaryOp { left: Box::new(expr), op, right: Box::new(self.parse_subexpr(precedence)?), - }) + })) } else if let Token::Word(w) = &tok { match w.keyword { Keyword::IS => { @@ -941,10 +941,10 @@ impl<'a> Parser<'a> { self.parse_pg_cast(expr) } else if Token::ExclamationMark == tok { // PostgreSQL factorial operation - Ok(Expr::UnaryOp { + Ok(Expr::UnaryOp(UnaryOp { op: UnaryOperator::PGPostfixFactorial, expr: Box::new(expr), - }) + })) } else if Token::LBracket == tok { self.parse_map_access(expr) } else { @@ -958,10 +958,12 @@ impl<'a> Parser<'a> { let tok = self.consume_token(&Token::RBracket); debug!("Tok: {}", tok); match expr { - e @ Expr::Identifier(_) | e @ Expr::CompoundIdentifier(_) => Ok(Expr::MapAccess { - column: Box::new(e), - key, - }), + e @ Expr::Identifier(_) | e @ Expr::CompoundIdentifier(_) => { + Ok(Expr::MapAccess(MapAccess { + column: Box::new(e), + key, + })) + } _ => Ok(expr), } } @@ -971,17 +973,17 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) { self.prev_token(); - Expr::InSubquery { + Expr::InSubquery(InSubquery { expr: Box::new(expr), subquery: Box::new(self.parse_query()?), negated, - } + }) } else { - Expr::InList { + Expr::InList(InList { expr: Box::new(expr), list: self.parse_comma_separated(Parser::parse_expr)?, negated, - } + }) }; self.expect_token(&Token::RParen)?; Ok(in_op) @@ -994,20 +996,20 @@ impl<'a> Parser<'a> { let low = self.parse_subexpr(Self::BETWEEN_PREC)?; self.expect_keyword(Keyword::AND)?; let high = self.parse_subexpr(Self::BETWEEN_PREC)?; - Ok(Expr::Between { + Ok(Expr::Between(Between { expr: Box::new(expr), negated, low: Box::new(low), high: Box::new(high), - }) + })) } /// Parse a postgresql casting style which is in the form of `expr::datatype` pub fn parse_pg_cast(&mut self, expr: Expr) -> Result { - Ok(Expr::Cast { + Ok(Expr::Cast(Cast { expr: Box::new(expr), data_type: self.parse_data_type()?, - }) + })) } const UNARY_NOT_PREC: u8 = 15; @@ -1269,16 +1271,16 @@ impl<'a> Parser<'a> { .parse_one_of_keywords(&[Keyword::TEMP, Keyword::TEMPORARY]) .is_some(); if self.parse_keyword(Keyword::TABLE) { - Ok(Statement::CreateTable( - Box::new(self.parse_create_table(or_replace, temporary)?), - )) + Ok(Statement::CreateTable(Box::new( + self.parse_create_table(or_replace, temporary)?, + ))) } else if self.parse_keyword(Keyword::MATERIALIZED) || self.parse_keyword(Keyword::VIEW) { self.prev_token(); Ok(Statement::CreateView(self.parse_create_view(or_replace)?)) } else if self.parse_keyword(Keyword::EXTERNAL) { - Ok(Statement::CreateTable( - Box::new(self.parse_create_external_table(or_replace)?), - )) + Ok(Statement::CreateTable(Box::new( + self.parse_create_external_table(or_replace)?, + ))) } else if or_replace { self.expected( "[EXTERNAL] TABLE or [MATERIALIZED] VIEW after CREATE OR REPLACE", diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 95493337c..8a7daac7b 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -225,11 +225,11 @@ fn parse_where_delete_statement() { assert_eq!(ObjectName(vec![Ident::new("foo")]), table_name); assert_eq!( - Expr::BinaryOp { + Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("name"))), op: Eq, right: Box::new(Expr::Value(number("5"))), - }, + }), selection.unwrap(), ); } @@ -326,9 +326,9 @@ fn parse_column_aliases() { let sql = "SELECT a.col + 1 AS newname FROM foo AS a"; let select = verified_only_select(sql); if let SelectItem::ExprWithAlias { - expr: Expr::BinaryOp { + expr: Expr::BinaryOp(BinaryOp { ref op, ref right, .. - }, + }), ref alias, } = only(&select.projection) { @@ -389,10 +389,10 @@ fn parse_select_count_distinct() { assert_eq!( &Expr::Function(Function { name: ObjectName(vec![Ident::new("COUNT")]), - args: vec![FunctionArg::Unnamed(Expr::UnaryOp { + args: vec![FunctionArg::Unnamed(Expr::UnaryOp(UnaryOp { op: UnaryOperator::Plus, expr: Box::new(Expr::Identifier(Ident::new("x"))), - })], + }))], over: None, distinct: true, }), @@ -482,13 +482,13 @@ fn parse_escaped_single_quote_string_predicate() { WHERE salary <> 'Jim''s salary'"; let ast = verified_only_select(sql); assert_eq!( - Some(Expr::BinaryOp { + Some(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("salary"))), op: NotEq, right: Box::new(Expr::Value(Value::SingleQuotedString( "Jim's salary".to_string() ))) - }), + })), ast.selection, ); } @@ -510,18 +510,18 @@ fn parse_number() { #[test] fn parse_compound_expr_1() { use self::BinaryOperator::*; - use self::Expr::*; + use self::Expr::Identifier; let sql = "a + b * c"; assert_eq!( - BinaryOp { + Expr::BinaryOp(BinaryOp { left: Box::new(Identifier(Ident::new("a"))), op: Plus, - right: Box::new(BinaryOp { + right: Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Identifier(Ident::new("b"))), op: Multiply, right: Box::new(Identifier(Ident::new("c"))) - }) - }, + })) + }), verified_expr(sql) ); } @@ -529,38 +529,37 @@ fn parse_compound_expr_1() { #[test] fn parse_compound_expr_2() { use self::BinaryOperator::*; - use self::Expr::*; + let sql = "a * b + c"; assert_eq!( - BinaryOp { - left: Box::new(BinaryOp { - left: Box::new(Identifier(Ident::new("a"))), + Expr::BinaryOp(BinaryOp { + left: Box::new(Expr::BinaryOp(BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("a"))), op: Multiply, - right: Box::new(Identifier(Ident::new("b"))) - }), + right: Box::new(Expr::Identifier(Ident::new("b"))) + })), op: Plus, - right: Box::new(Identifier(Ident::new("c"))) - }, + right: Box::new(Expr::Identifier(Ident::new("c"))) + }), verified_expr(sql) ); } #[test] fn parse_unary_math() { - use self::Expr::*; let sql = "- a + - b"; assert_eq!( - BinaryOp { - left: Box::new(UnaryOp { + Expr::BinaryOp(BinaryOp { + left: Box::new(Expr::UnaryOp(UnaryOp { op: UnaryOperator::Minus, - expr: Box::new(Identifier(Ident::new("a"))), - }), + expr: Box::new(Expr::Identifier(Ident::new("a"))), + })), op: BinaryOperator::Plus, - right: Box::new(UnaryOp { + right: Box::new(Expr::UnaryOp(UnaryOp { op: UnaryOperator::Minus, - expr: Box::new(Identifier(Ident::new("b"))), - }), - }, + expr: Box::new(Expr::Identifier(Ident::new("b"))), + })), + }), verified_expr(sql) ); } @@ -591,63 +590,63 @@ fn parse_not_precedence() { let sql = "NOT true OR true"; assert_matches!( verified_expr(sql), - Expr::BinaryOp { + Expr::BinaryOp(BinaryOp { op: BinaryOperator::Or, .. - } + }) ); // But NOT has lower precedence than comparison operators, so the following parses as NOT (a IS NULL) let sql = "NOT a IS NULL"; assert_matches!( verified_expr(sql), - Expr::UnaryOp { + Expr::UnaryOp(UnaryOp { op: UnaryOperator::Not, .. - } + }) ); // NOT has lower precedence than BETWEEN, so the following parses as NOT (1 NOT BETWEEN 1 AND 2) let sql = "NOT 1 NOT BETWEEN 1 AND 2"; assert_eq!( verified_expr(sql), - Expr::UnaryOp { + Expr::UnaryOp(UnaryOp { op: UnaryOperator::Not, - expr: Box::new(Expr::Between { + expr: Box::new(Expr::Between(Between { expr: Box::new(Expr::Value(number("1"))), low: Box::new(Expr::Value(number("1"))), high: Box::new(Expr::Value(number("2"))), negated: true, - }), - }, + })), + }), ); // NOT has lower precedence than LIKE, so the following parses as NOT ('a' NOT LIKE 'b') let sql = "NOT 'a' NOT LIKE 'b'"; assert_eq!( verified_expr(sql), - Expr::UnaryOp { + Expr::UnaryOp(UnaryOp { op: UnaryOperator::Not, - expr: Box::new(Expr::BinaryOp { + expr: Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Value(Value::SingleQuotedString("a".into()))), op: BinaryOperator::NotLike, right: Box::new(Expr::Value(Value::SingleQuotedString("b".into()))), - }), - }, + })), + }), ); // NOT has lower precedence than IN, so the following parses as NOT (a NOT IN 'a') let sql = "NOT a NOT IN ('a')"; assert_eq!( verified_expr(sql), - Expr::UnaryOp { + Expr::UnaryOp(UnaryOp { op: UnaryOperator::Not, - expr: Box::new(Expr::InList { + expr: Box::new(Expr::InList(InList { expr: Box::new(Expr::Identifier("a".into())), list: vec![Expr::Value(Value::SingleQuotedString("a".into()))], negated: true, - }), - }, + })), + }), ); } @@ -660,7 +659,7 @@ fn parse_like() { ); let select = verified_only_select(sql); assert_eq!( - Expr::BinaryOp { + Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("name"))), op: if negated { BinaryOperator::NotLike @@ -668,7 +667,7 @@ fn parse_like() { BinaryOperator::Like }, right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), - }, + }), select.selection.unwrap() ); @@ -680,7 +679,7 @@ fn parse_like() { ); let select = verified_only_select(sql); assert_eq!( - Expr::IsNull(Box::new(Expr::BinaryOp { + Expr::IsNull(Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("name"))), op: if negated { BinaryOperator::NotLike @@ -688,7 +687,7 @@ fn parse_like() { BinaryOperator::Like }, right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), - })), + }))), select.selection.unwrap() ); } @@ -705,7 +704,7 @@ fn parse_ilike() { ); let select = verified_only_select(sql); assert_eq!( - Expr::BinaryOp { + Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("name"))), op: if negated { BinaryOperator::NotILike @@ -713,7 +712,7 @@ fn parse_ilike() { BinaryOperator::ILike }, right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), - }, + }), select.selection.unwrap() ); @@ -725,7 +724,7 @@ fn parse_ilike() { ); let select = verified_only_select(sql); assert_eq!( - Expr::IsNull(Box::new(Expr::BinaryOp { + Expr::IsNull(Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("name"))), op: if negated { BinaryOperator::NotILike @@ -733,7 +732,7 @@ fn parse_ilike() { BinaryOperator::ILike }, right: Box::new(Expr::Value(Value::SingleQuotedString("%a".to_string()))), - })), + }))), select.selection.unwrap() ); } @@ -750,14 +749,14 @@ fn parse_in_list() { ); let select = verified_only_select(sql); assert_eq!( - Expr::InList { + Expr::InList(InList { expr: Box::new(Expr::Identifier(Ident::new("segment"))), list: vec![ Expr::Value(Value::SingleQuotedString("HIGH".to_string())), Expr::Value(Value::SingleQuotedString("MED".to_string())), ], negated, - }, + }), select.selection.unwrap() ); } @@ -770,11 +769,11 @@ fn parse_in_subquery() { let sql = "SELECT * FROM customers WHERE segment IN (SELECT segm FROM bar)"; let select = verified_only_select(sql); assert_eq!( - Expr::InSubquery { + Expr::InSubquery(InSubquery { expr: Box::new(Expr::Identifier(Ident::new("segment"))), subquery: Box::new(verified_query("SELECT segm FROM bar")), negated: false, - }, + }), select.selection.unwrap() ); } @@ -785,11 +784,11 @@ fn parse_string_agg() { let select = verified_only_select(sql); assert_eq!( - SelectItem::UnnamedExpr(Expr::BinaryOp { + SelectItem::UnnamedExpr(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("a"))), op: BinaryOperator::StringConcat, right: Box::new(Expr::Identifier(Ident::new("b"))), - }), + })), select.projection[0] ); } @@ -805,11 +804,11 @@ fn parse_bitwise_ops() { for (str_op, op) in bitwise_ops { let select = verified_only_select(&format!("SELECT a {} b", &str_op)); assert_eq!( - SelectItem::UnnamedExpr(Expr::BinaryOp { + SelectItem::UnnamedExpr(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("a"))), op: op.clone(), right: Box::new(Expr::Identifier(Ident::new("b"))), - }), + })), select.projection[0] ); } @@ -824,12 +823,12 @@ fn parse_between() { ); let select = verified_only_select(sql); assert_eq!( - Expr::Between { + Expr::Between(Between { expr: Box::new(Expr::Identifier(Ident::new("age"))), low: Box::new(Expr::Value(number("25"))), high: Box::new(Expr::Value(number("32"))), negated, - }, + }), select.selection.unwrap() ); } @@ -843,44 +842,44 @@ fn parse_between_with_expr() { let sql = "SELECT * FROM t WHERE 1 BETWEEN 1 + 2 AND 3 + 4 IS NULL"; let select = verified_only_select(sql); assert_eq!( - Expr::IsNull(Box::new(Expr::Between { + Expr::IsNull(Box::new(Expr::Between(Between { expr: Box::new(Expr::Value(number("1"))), - low: Box::new(Expr::BinaryOp { + low: Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Value(number("1"))), op: Plus, right: Box::new(Expr::Value(number("2"))), - }), - high: Box::new(Expr::BinaryOp { + })), + high: Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Value(number("3"))), op: Plus, right: Box::new(Expr::Value(number("4"))), - }), + })), negated: false, - })), + }))), select.selection.unwrap() ); let sql = "SELECT * FROM t WHERE 1 = 1 AND 1 + x BETWEEN 1 AND 2"; let select = verified_only_select(sql); assert_eq!( - Expr::BinaryOp { - left: Box::new(Expr::BinaryOp { + Expr::BinaryOp(BinaryOp { + left: Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Value(number("1"))), op: BinaryOperator::Eq, right: Box::new(Expr::Value(number("1"))), - }), + })), op: BinaryOperator::And, - right: Box::new(Expr::Between { - expr: Box::new(Expr::BinaryOp { + right: Box::new(Expr::Between(Between { + expr: Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Value(number("1"))), op: BinaryOperator::Plus, right: Box::new(Expr::Identifier(Ident::new("x"))), - }), + })), low: Box::new(Expr::Value(number("1"))), high: Box::new(Expr::Value(number("2"))), negated: false, - }), - }, + })), + }), select.selection.unwrap(), ) } @@ -980,7 +979,7 @@ fn parse_select_having() { let sql = "SELECT foo FROM bar GROUP BY foo HAVING COUNT(*) > 1"; let select = verified_only_select(sql); assert_eq!( - Some(Expr::BinaryOp { + Some(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Function(Function { name: ObjectName(vec![Ident::new("COUNT")]), args: vec![FunctionArg::Unnamed(Expr::Wildcard)], @@ -989,7 +988,7 @@ fn parse_select_having() { })), op: BinaryOperator::Gt, right: Box::new(Expr::Value(number("1"))) - }), + })), select.having ); @@ -1011,20 +1010,20 @@ fn parse_cast() { let sql = "SELECT CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(Cast { expr: Box::new(Expr::Identifier(Ident::new("id"))), data_type: DataType::BigInt(None) - }, + }), expr_from_projection(only(&select.projection)) ); let sql = "SELECT CAST(id AS TINYINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::Cast { + &Expr::Cast(Cast { expr: Box::new(Expr::Identifier(Ident::new("id"))), data_type: DataType::TinyInt(None) - }, + }), expr_from_projection(only(&select.projection)) ); @@ -1051,10 +1050,10 @@ fn parse_try_cast() { let sql = "SELECT TRY_CAST(id AS BIGINT) FROM customer"; let select = verified_only_select(sql); assert_eq!( - &Expr::TryCast { + &Expr::TryCast(TryCast { expr: Box::new(Expr::Identifier(Ident::new("id"))), data_type: DataType::BigInt(None) - }, + }), expr_from_projection(only(&select.projection)) ); one_statement_parses_to( @@ -1080,10 +1079,10 @@ fn parse_extract() { let sql = "SELECT EXTRACT(YEAR FROM d)"; let select = verified_only_select(sql); assert_eq!( - &Expr::Extract { + &Expr::Extract(Extract { field: DateTimeField::Year, expr: Box::new(Expr::Identifier(Ident::new("d"))), - }, + }), expr_from_projection(only(&select.projection)), ); @@ -1937,10 +1936,10 @@ fn parse_literal_date() { let sql = "SELECT DATE '1999-01-01'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString { + &Expr::TypedString(TypedString { data_type: DataType::Date, value: "1999-01-01".into() - }, + }), expr_from_projection(only(&select.projection)), ); } @@ -1950,10 +1949,10 @@ fn parse_literal_time() { let sql = "SELECT TIME '01:23:34'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString { + &Expr::TypedString(TypedString { data_type: DataType::Time, value: "01:23:34".into() - }, + }), expr_from_projection(only(&select.projection)), ); } @@ -1963,10 +1962,10 @@ fn parse_literal_timestamp() { let sql = "SELECT TIMESTAMP '1999-01-01 01:23:34'"; let select = verified_only_select(sql); assert_eq!( - &Expr::TypedString { + &Expr::TypedString(TypedString { data_type: DataType::Timestamp, value: "1999-01-01 01:23:34".into() - }, + }), expr_from_projection(only(&select.projection)), ); } @@ -2184,22 +2183,22 @@ fn parse_delimited_identifiers() { #[test] fn parse_parens() { use self::BinaryOperator::*; - use self::Expr::*; + use Expr::{Identifier, Nested}; let sql = "(a + b) - (c + d)"; assert_eq!( - BinaryOp { - left: Box::new(Nested(Box::new(BinaryOp { + Expr::BinaryOp(BinaryOp { + left: Box::new(Nested(Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Identifier(Ident::new("a"))), op: Plus, right: Box::new(Identifier(Ident::new("b"))) - }))), + })))), op: Minus, - right: Box::new(Nested(Box::new(BinaryOp { + right: Box::new(Nested(Box::new(Expr::BinaryOp(BinaryOp { left: Box::new(Identifier(Ident::new("c"))), op: Plus, right: Box::new(Identifier(Ident::new("d"))) - }))) - }, + })))) + }), verified_expr(sql) ); } @@ -2208,23 +2207,23 @@ fn parse_parens() { fn parse_searched_case_expr() { let sql = "SELECT CASE WHEN bar IS NULL THEN 'null' WHEN bar = 0 THEN '=0' WHEN bar >= 0 THEN '>=0' ELSE '<0' END FROM foo"; use self::BinaryOperator::*; - use self::Expr::{BinaryOp, Case, Identifier, IsNull}; + use self::Expr::{Identifier, IsNull}; let select = verified_only_select(sql); assert_eq!( - &Case { + &Expr::Case(Case { operand: None, conditions: vec![ IsNull(Box::new(Identifier(Ident::new("bar")))), - BinaryOp { + Expr::BinaryOp(BinaryOp { left: Box::new(Identifier(Ident::new("bar"))), op: Eq, right: Box::new(Expr::Value(number("0"))) - }, - BinaryOp { + }), + Expr::BinaryOp(BinaryOp { left: Box::new(Identifier(Ident::new("bar"))), op: GtEq, right: Box::new(Expr::Value(number("0"))) - } + }) ], results: vec![ Expr::Value(Value::SingleQuotedString("null".to_string())), @@ -2234,7 +2233,7 @@ fn parse_searched_case_expr() { else_result: Some(Box::new(Expr::Value(Value::SingleQuotedString( "<0".to_string() )))) - }, + }), expr_from_projection(only(&select.projection)), ); } @@ -2244,16 +2243,16 @@ fn parse_simple_case_expr() { // ANSI calls a CASE expression with an operand "" let sql = "SELECT CASE foo WHEN 1 THEN 'Y' ELSE 'N' END"; let select = verified_only_select(sql); - use self::Expr::{Case, Identifier}; + use self::Expr::Identifier; assert_eq!( - &Case { + &Expr::Case(Case { operand: Some(Box::new(Identifier(Ident::new("foo")))), conditions: vec![Expr::Value(number("1"))], results: vec![Expr::Value(Value::SingleQuotedString("Y".to_string())),], else_result: Some(Box::new(Expr::Value(Value::SingleQuotedString( "N".to_string() )))) - }, + }), expr_from_projection(only(&select.projection)), ); } @@ -2367,11 +2366,11 @@ fn parse_joins_on() { args: vec![], with_hints: vec![], }, - join_operator: f(JoinConstraint::On(Expr::BinaryOp { + join_operator: f(JoinConstraint::On(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier("c1".into())), op: BinaryOperator::Eq, right: Box::new(Expr::Identifier("c2".into())), - })), + }))), } } // Test parsing of aliases @@ -2780,11 +2779,11 @@ fn parse_scalar_subqueries() { let sql = "(SELECT 1) + (SELECT 2)"; assert_matches!( verified_expr(sql), - Expr::BinaryOp { + Expr::BinaryOp(BinaryOp { op: BinaryOperator::Plus, .. //left: box Subquery { .. }, //right: box Subquery { .. }, - } + }) ); } @@ -2843,10 +2842,10 @@ fn parse_exists_subquery() { let sql = "SELECT * FROM t WHERE NOT EXISTS (SELECT 1)"; let select = verified_only_select(sql); assert_eq!( - Expr::UnaryOp { + Expr::UnaryOp(UnaryOp { op: UnaryOperator::Not, expr: Box::new(Expr::Exists(Box::new(expected_inner))), - }, + }), select.selection.unwrap(), ); diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 3d177ff0a..77e0e6c86 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -42,7 +42,7 @@ fn parse_create_table_with_defaults() { let columns = table.columns; let constraints = table.constraints; let with_options = table.with_options; - + assert!(!table.external); assert!(!table.if_not_exists); assert_eq!("public.customer", name.to_string()); @@ -592,11 +592,11 @@ fn parse_pg_bitwise_binary_ops() { for (str_op, op) in bitwise_ops { let select = pg().verified_only_select(&format!("SELECT a {} b", &str_op)); assert_eq!( - SelectItem::UnnamedExpr(Expr::BinaryOp { + SelectItem::UnnamedExpr(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Identifier(Ident::new("a"))), op: op.clone(), right: Box::new(Expr::Identifier(Ident::new("b"))), - }), + })), select.projection[0] ); } @@ -615,10 +615,10 @@ fn parse_pg_unary_ops() { for (str_op, op) in pg_unary_ops { let select = pg().verified_only_select(&format!("SELECT {} a", &str_op)); assert_eq!( - SelectItem::UnnamedExpr(Expr::UnaryOp { + SelectItem::UnnamedExpr(Expr::UnaryOp(UnaryOp { op: op.clone(), expr: Box::new(Expr::Identifier(Ident::new("a"))), - }), + })), select.projection[0] ); } @@ -631,10 +631,10 @@ fn parse_pg_postfix_factorial() { for (str_op, op) in postfix_factorial { let select = pg().verified_only_select(&format!("SELECT a{}", &str_op)); assert_eq!( - SelectItem::UnnamedExpr(Expr::UnaryOp { + SelectItem::UnnamedExpr(Expr::UnaryOp(UnaryOp { op: op.clone(), expr: Box::new(Expr::Identifier(Ident::new("a"))), - }), + })), select.projection[0] ); } @@ -652,11 +652,11 @@ fn parse_pg_regex_match_ops() { for (str_op, op) in pg_regex_match_ops { let select = pg().verified_only_select(&format!("SELECT 'abc' {} '^a'", &str_op)); assert_eq!( - SelectItem::UnnamedExpr(Expr::BinaryOp { + SelectItem::UnnamedExpr(Expr::BinaryOp(BinaryOp { left: Box::new(Expr::Value(Value::SingleQuotedString("abc".into()))), op: op.clone(), right: Box::new(Expr::Value(Value::SingleQuotedString("^a".into()))), - }), + })), select.projection[0] ); } From b84e24b88380d10bf6c545a25183f95676f19a2c Mon Sep 17 00:00:00 2001 From: tvallotton <57121854+tvallotton@users.noreply.github.com> Date: Sat, 4 Sep 2021 11:21:48 -0400 Subject: [PATCH 08/11] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 205480b95..dfa40ecf2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +# Fork +This is a fork of [`sqlparser`](https://github.com/sqlparser-rs/sqlparser-rs). The major difference between `sqlparse` and this fork is that here `Statement` and `Expr` hold structs instead of variables. + # Extensible SQL Lexer and Parser for Rust [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) From 46a763663b914033958e98ed2de047c80f2a3f62 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 11:47:12 -0400 Subject: [PATCH 09/11] Adding Drop constraint to parser --- src/ast/ddl.rs | 20 ++++++++++++++++++-- src/parser.rs | 16 ++++++++++++++-- tests/sqlparser_common.rs | 10 ++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index a4a7b2f29..d5830f3a3 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -32,7 +32,11 @@ pub enum AlterTableOperation { /// `ADD [ COLUMN ] ` AddColumn { column_def: ColumnDef }, /// TODO: implement `DROP CONSTRAINT ` - DropConstraint { name: Ident }, + DropConstraint { + name: Ident, + cascade: bool, + restrict: bool, + }, /// `DROP [ COLUMN ] [ IF EXISTS ] [ CASCADE ]` DropColumn { column_name: Ident, @@ -87,7 +91,19 @@ impl fmt::Display for AlterTableOperation { display_comma_separated(partitions), ie = if *if_exists { " IF EXISTS" } else { "" } ), - AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name), + AlterTableOperation::DropConstraint { + name, + cascade, + restrict, + } => { + if *cascade { + write!(f, "DROP CONSTRAINT {} CASCADE", name) + } else if *restrict { + write!(f, "DROP CONSTRAINT {} RESTRICT", name) + } else { + write!(f, "DROP CONSTRAINT {}", name) + } + } AlterTableOperation::DropColumn { column_name, if_exists, diff --git a/src/parser.rs b/src/parser.rs index b392cd868..d2a6ce38c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1861,8 +1861,7 @@ impl<'a> Parser<'a> { partitions, if_exists: false, } - } else { - let _ = self.parse_keyword(Keyword::COLUMN); + } else if self.parse_keyword(Keyword::COLUMN) { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let column_name = self.parse_identifier()?; let cascade = self.parse_keyword(Keyword::CASCADE); @@ -1871,6 +1870,19 @@ impl<'a> Parser<'a> { if_exists, cascade, } + } else { + let _ = self.parse_keyword(Keyword::CONSTRAINT); + let name = self.parse_identifier()?; + let cascade = self.parse_keyword(Keyword::CASCADE); + let mut restrict = false; + if !cascade { + restrict = self.parse_keyword(Keyword::RESTRICT); + } + AlterTableOperation::DropConstraint { + name, + cascade, + restrict, + } } } else if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 8a7daac7b..cba8b661d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1769,6 +1769,16 @@ fn parse_alter_table_drop_column() { } } +#[test] +fn parse_alter_table_drop_constraint() { + let cascade = "ALTER TABLE table_name DROP CONSTRAINT constraint_name CASCADE"; + let restrict = "ALTER TABLE table_name DROP CONSTRAINT constraint_name RESTRICT"; + let plain = "ALTER TABLE table_name DROP CONSTRAINT constraint_name"; + assert_eq!(cascade, &format!("{}", verified_stmt(cascade))); + assert_eq!(restrict, &format!("{}", verified_stmt(restrict))); + assert_eq!(plain, &format!("{}", verified_stmt(plain))); +} + #[test] fn parse_bad_constraint() { let res = parse_sql_statements("ALTER TABLE tab ADD"); From 9b0f83e7da5388bba48cb0e98f8a9dc3e2eb0613 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 11:55:20 -0400 Subject: [PATCH 10/11] Fixing error --- src/parser.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index d2a6ce38c..6ede79921 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1861,17 +1861,7 @@ impl<'a> Parser<'a> { partitions, if_exists: false, } - } else if self.parse_keyword(Keyword::COLUMN) { - let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); - let column_name = self.parse_identifier()?; - let cascade = self.parse_keyword(Keyword::CASCADE); - AlterTableOperation::DropColumn { - column_name, - if_exists, - cascade, - } - } else { - let _ = self.parse_keyword(Keyword::CONSTRAINT); + } else if self.parse_keyword(Keyword::CONSTRAINT) { let name = self.parse_identifier()?; let cascade = self.parse_keyword(Keyword::CASCADE); let mut restrict = false; @@ -1883,6 +1873,16 @@ impl<'a> Parser<'a> { cascade, restrict, } + } else { + let _ = self.parse_keyword(Keyword::COLUMN); + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let column_name = self.parse_identifier()?; + let cascade = self.parse_keyword(Keyword::CASCADE); + AlterTableOperation::DropColumn { + column_name, + if_exists, + cascade, + } } } else if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; From 39e0dce4f2ca95b26013bdf2a2e1a50949ded929 Mon Sep 17 00:00:00 2001 From: tvallotton Date: Sat, 4 Sep 2021 12:26:16 -0400 Subject: [PATCH 11/11] Moving table constraint so it holds structs --- src/ast/ddl.rs | 60 ++++++++++++++++++++++++--------------- src/ast/mod.rs | 2 +- src/parser.rs | 11 +++---- tests/sqlparser_common.rs | 16 +++++------ 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index d5830f3a3..6c3e50479 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -145,54 +145,68 @@ impl fmt::Display for AlterTableOperation { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TableConstraint { /// `[ CONSTRAINT ] { PRIMARY KEY | UNIQUE } ()` - Unique { - name: Option, - columns: Vec, - /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint - is_primary: bool, - }, + Unique(Unique), /// A referential integrity constraint (`[ CONSTRAINT ] FOREIGN KEY () /// REFERENCES () /// { [ON DELETE ] [ON UPDATE ] | /// [ON UPDATE ] [ON DELETE ] /// }`). - ForeignKey { - name: Option, - columns: Vec, - foreign_table: ObjectName, - referred_columns: Vec, - on_delete: Option, - on_update: Option, - }, + ForeignKey(ForeignKey), /// `[ CONSTRAINT ] CHECK ()` - Check { - name: Option, - expr: Box, - }, + Check(Check), +} +/// `[ CONSTRAINT ] { PRIMARY KEY | UNIQUE } ()` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Unique { + pub name: Option, + pub columns: Vec, + /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint + pub is_primary: bool, +} +/// `[ CONSTRAINT ] CHECK ()` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Check { + pub name: Option, + pub expr: Box, +} + +/// A referential integrity constraint (`[ CONSTRAINT ] FOREIGN KEY () +/// REFERENCES () +/// { [ON DELETE ] [ON UPDATE ] | +/// [ON UPDATE ] [ON DELETE ] +/// }`). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ForeignKey { + pub name: Option, + pub columns: Vec, + pub foreign_table: ObjectName, + pub referred_columns: Vec, + pub on_delete: Option, + pub on_update: Option, } impl fmt::Display for TableConstraint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - TableConstraint::Unique { + TableConstraint::Unique(Unique { name, columns, is_primary, - } => write!( + }) => write!( f, "{}{} ({})", display_constraint_name(name), if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, display_comma_separated(columns) ), - TableConstraint::ForeignKey { + TableConstraint::ForeignKey(ForeignKey { name, columns, foreign_table, referred_columns, on_delete, on_update, - } => { + }) => { write!( f, "{}FOREIGN KEY ({}) REFERENCES {}({})", @@ -209,7 +223,7 @@ impl fmt::Display for TableConstraint { } Ok(()) } - TableConstraint::Check { name, expr } => { + TableConstraint::Check(Check { name, expr }) => { write!(f, "{}CHECK ({})", display_constraint_name(name), expr) } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e51e1d5a3..71fa3cc64 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -21,7 +21,7 @@ mod statement; mod value; pub use expression::*; pub use statement::*; - +pub use ddl::*; #[cfg(not(feature = "std"))] use alloc::{ boxed::Box, diff --git a/src/parser.rs b/src/parser.rs index 6ede79921..89c9438ff 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1730,6 +1730,7 @@ impl<'a> Parser<'a> { } else { None }; + match self.next_token() { Token::Word(w) if w.keyword == Keyword::PRIMARY || w.keyword == Keyword::UNIQUE => { let is_primary = w.keyword == Keyword::PRIMARY; @@ -1737,11 +1738,11 @@ impl<'a> Parser<'a> { self.expect_keyword(Keyword::KEY)?; } let columns = self.parse_parenthesized_column_list(Mandatory)?; - Ok(Some(TableConstraint::Unique { + Ok(Some(TableConstraint::Unique(Unique { name, columns, is_primary, - })) + }))) } Token::Word(w) if w.keyword == Keyword::FOREIGN => { self.expect_keyword(Keyword::KEY)?; @@ -1762,20 +1763,20 @@ impl<'a> Parser<'a> { break; } } - Ok(Some(TableConstraint::ForeignKey { + Ok(Some(TableConstraint::ForeignKey(ForeignKey { name, columns, foreign_table, referred_columns, on_delete, on_update, - })) + }))) } Token::Word(w) if w.keyword == Keyword::CHECK => { self.expect_token(&Token::LParen)?; let expr = Box::new(self.parse_expr()?); self.expect_token(&Token::RParen)?; - Ok(Some(TableConstraint::Check { name, expr })) + Ok(Some(TableConstraint::Check(Check { name, expr }))) } unexpected => { if name.is_some() { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index cba8b661d..8f4cead9c 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1279,38 +1279,38 @@ fn parse_create_table() { assert_eq!( constraints, vec![ - TableConstraint::ForeignKey { + TableConstraint::ForeignKey(ForeignKey { name: Some("fkey".into()), columns: vec!["lat".into()], foreign_table: ObjectName(vec!["othertable3".into()]), referred_columns: vec!["lat".into()], on_delete: Some(ReferentialAction::Restrict), on_update: None - }, - TableConstraint::ForeignKey { + }), + TableConstraint::ForeignKey(ForeignKey { name: Some("fkey2".into()), columns: vec!["lat".into()], foreign_table: ObjectName(vec!["othertable4".into()]), referred_columns: vec!["lat".into()], on_delete: Some(ReferentialAction::NoAction), on_update: Some(ReferentialAction::Restrict) - }, - TableConstraint::ForeignKey { + }), + TableConstraint::ForeignKey(ForeignKey { name: None, columns: vec!["lat".into()], foreign_table: ObjectName(vec!["othertable4".into()]), referred_columns: vec!["lat".into()], on_delete: Some(ReferentialAction::Cascade), on_update: Some(ReferentialAction::SetDefault) - }, - TableConstraint::ForeignKey { + }), + TableConstraint::ForeignKey(ForeignKey { name: None, columns: vec!["lng".into()], foreign_table: ObjectName(vec!["othertable4".into()]), referred_columns: vec!["longitude".into()], on_delete: None, on_update: Some(ReferentialAction::SetNull) - }, + }), ] ); assert_eq!(with_options, vec![]);