From 330f3756d77a94fbf0c1c3bbf8ed5fcbeb1c250d Mon Sep 17 00:00:00 2001 From: step-baby <3528004749@qq.com> Date: Thu, 8 Dec 2022 10:22:56 +0800 Subject: [PATCH 1/4] fix: stack overflow --- Cargo.toml | 1 + src/ast/mod.rs | 1946 ++++++++++++++++++++++++------------------------ src/parser.rs | 16 +- 3 files changed, 990 insertions(+), 973 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2355f4646..4b59765de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ serde = { version = "1.0", features = ["derive"], optional = true } # of dev-dependencies because of # https://github.com/rust-lang/cargo/issues/1596 serde_json = { version = "1.0", optional = true } +stacker = "0.1" [dev-dependencies] simple_logger = "4.0" diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 7f3d15438..a2d047e24 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1534,1084 +1534,1086 @@ impl fmt::Display for Statement { // 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::Kill { modifier, id } => { - write!(f, "KILL ")?; - - if let Some(m) = modifier { - write!(f, "{} ", m)?; - } - - write!(f, "{}", id) - } - Statement::ExplainTable { - describe_alias, - table_name, - } => { - if *describe_alias { - write!(f, "DESCRIBE ")?; - } else { - write!(f, "EXPLAIN ")?; - } + stacker::maybe_grow(2 * 1024 * 1024, 5 * 1024 * 1024, || { + match self { + Statement::Kill { modifier, id } => { + write!(f, "KILL ")?; - write!(f, "{}", table_name) - } - Statement::Explain { - describe_alias, - verbose, - analyze, - statement, - format, - } => { - if *describe_alias { - write!(f, "DESCRIBE ")?; - } else { - write!(f, "EXPLAIN ")?; - } - - if *analyze { - write!(f, "ANALYZE ")?; - } + if let Some(m) = modifier { + write!(f, "{} ", m)?; + } - if *verbose { - write!(f, "VERBOSE ")?; + write!(f, "{}", id) } + Statement::ExplainTable { + describe_alias, + table_name, + } => { + if *describe_alias { + write!(f, "DESCRIBE ")?; + } else { + write!(f, "EXPLAIN ")?; + } - if let Some(format) = format { - write!(f, "FORMAT {} ", format)?; - } + write!(f, "{}", table_name) + } + Statement::Explain { + describe_alias, + verbose, + analyze, + statement, + format, + } => { + if *describe_alias { + write!(f, "DESCRIBE ")?; + } else { + write!(f, "EXPLAIN ")?; + } - write!(f, "{}", statement) - } - Statement::Query(s) => write!(f, "{}", s), - Statement::Declare { - name, - binary, - sensitive, - scroll, - hold, - query, - } => { - write!(f, "DECLARE {} ", name)?; + if *analyze { + write!(f, "ANALYZE ")?; + } - if *binary { - write!(f, "BINARY ")?; - } + if *verbose { + write!(f, "VERBOSE ")?; + } - if let Some(sensitive) = sensitive { - if *sensitive { - write!(f, "INSENSITIVE ")?; - } else { - write!(f, "ASENSITIVE ")?; + if let Some(format) = format { + write!(f, "FORMAT {} ", format)?; } - } - if let Some(scroll) = scroll { - if *scroll { - write!(f, "SCROLL ")?; - } else { - write!(f, "NO SCROLL ")?; + write!(f, "{}", statement) + } + Statement::Query(s) => write!(f, "{}", s), + Statement::Declare { + name, + binary, + sensitive, + scroll, + hold, + query, + } => { + write!(f, "DECLARE {} ", name)?; + + if *binary { + write!(f, "BINARY ")?; } - } - write!(f, "CURSOR ")?; + if let Some(sensitive) = sensitive { + if *sensitive { + write!(f, "INSENSITIVE ")?; + } else { + write!(f, "ASENSITIVE ")?; + } + } - if let Some(hold) = hold { - if *hold { - write!(f, "WITH HOLD ")?; - } else { - write!(f, "WITHOUT HOLD ")?; + if let Some(scroll) = scroll { + if *scroll { + write!(f, "SCROLL ")?; + } else { + write!(f, "NO SCROLL ")?; + } } - } - write!(f, "FOR {}", query) - } - Statement::Fetch { - name, - direction, - into, - } => { - write!(f, "FETCH {} ", direction)?; + write!(f, "CURSOR ")?; - write!(f, "IN {}", name)?; + if let Some(hold) = hold { + if *hold { + write!(f, "WITH HOLD ")?; + } else { + write!(f, "WITHOUT HOLD ")?; + } + } - if let Some(into) = into { - write!(f, " INTO {}", into)?; + write!(f, "FOR {}", query) } + Statement::Fetch { + name, + direction, + into, + } => { + write!(f, "FETCH {} ", direction)?; - Ok(()) - } - 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))?; + write!(f, "IN {}", name)?; + + if let Some(into) = into { + write!(f, " INTO {}", into)?; } - } - if *compute_statistics { - write!(f, " COMPUTE STATISTICS")?; - } - if *noscan { - write!(f, " NOSCAN")?; - } - if *cache_metadata { - write!(f, " CACHE METADATA")?; + Ok(()) } - if *for_columns { - write!(f, " FOR COLUMNS")?; - if !columns.is_empty() { - write!(f, " {}", display_comma_separated(columns))?; + 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) } - Ok(()) - } - Statement::Insert { - or, - into, - table_name, - overwrite, - partitioned, - columns, - after_columns, - source, - table, - on, - returning, - } => { - if let Some(action) = or { - write!(f, "INSERT OR {} INTO {} ", action, table_name)?; - } else { + Statement::Msck { + table_name, + repair, + partition_action, + } => { write!( f, - "INSERT{over}{int}{tbl} {table_name} ", - table_name = table_name, - over = if *overwrite { " OVERWRITE" } else { "" }, - int = if *into { " INTO" } else { "" }, - tbl = if *table { " TABLE" } else { "" } + "MSCK {repair}TABLE {table}", + repair = if *repair { "REPAIR " } else { "" }, + table = table_name )?; - } - 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 let Some(pa) = partition_action { + write!(f, " {}", pa)?; } + Ok(()) } - if !after_columns.is_empty() { - write!(f, "({}) ", display_comma_separated(after_columns))?; + 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(()) } - write!(f, "{}", source)?; + 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 let Some(on) = on { - write!(f, "{}", on)?; + 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, + into, + table_name, + overwrite, + partitioned, + columns, + after_columns, + source, + table, + on, + returning, + } => { + if let Some(action) = or { + write!(f, "INSERT OR {} INTO {} ", action, table_name)?; + } else { + write!( + f, + "INSERT{over}{int}{tbl} {table_name} ", + table_name = table_name, + over = if *overwrite { " OVERWRITE" } else { "" }, + int = if *into { " INTO" } else { "" }, + 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)?; - if let Some(returning) = returning { - write!(f, " RETURNING {}", display_comma_separated(returning))?; - } + if let Some(on) = on { + write!(f, "{}", on)?; + } - Ok(()) - } + if let Some(returning) = returning { + write!(f, " RETURNING {}", display_comma_separated(returning))?; + } - Statement::Copy { - table_name, - columns, - to, - target, - options, - legacy_options, - values, - } => { - write!(f, "COPY {}", table_name)?; - if !columns.is_empty() { - write!(f, " ({})", display_comma_separated(columns))?; + Ok(()) } - write!(f, " {} {}", if *to { "TO" } else { "FROM" }, target)?; - if !options.is_empty() { - write!(f, " ({})", display_comma_separated(options))?; - } - if !legacy_options.is_empty() { - write!(f, " {}", display_separated(legacy_options, " "))?; - } - 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")?; + + Statement::Copy { + table_name, + columns, + to, + target, + options, + legacy_options, + values, + } => { + write!(f, "COPY {}", table_name)?; + if !columns.is_empty() { + write!(f, " ({})", display_comma_separated(columns))?; + } + write!(f, " {} {}", if *to { "TO" } else { "FROM" }, target)?; + if !options.is_empty() { + write!(f, " ({})", display_comma_separated(options))?; + } + if !legacy_options.is_empty() { + write!(f, " {}", display_separated(legacy_options, " "))?; + } + 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\\.")?; } - write!(f, "\n\\.")?; - } - Ok(()) - } - Statement::Update { - table, - assignments, - from, - selection, - returning, - } => { - write!(f, "UPDATE {}", table)?; - if !assignments.is_empty() { - write!(f, " SET {}", display_comma_separated(assignments))?; - } - if let Some(from) = from { - write!(f, " FROM {}", from)?; - } - if let Some(selection) = selection { - write!(f, " WHERE {}", selection)?; - } - if let Some(returning) = returning { - write!(f, " RETURNING {}", display_comma_separated(returning))?; - } - Ok(()) - } - Statement::Delete { - table_name, - using, - selection, - returning, - } => { - write!(f, "DELETE FROM {}", table_name)?; - if let Some(using) = using { - write!(f, " USING {}", using)?; + Ok(()) } - if let Some(selection) = selection { - write!(f, " WHERE {}", selection)?; + Statement::Update { + table, + assignments, + from, + selection, + returning, + } => { + write!(f, "UPDATE {}", table)?; + if !assignments.is_empty() { + write!(f, " SET {}", display_comma_separated(assignments))?; + } + if let Some(from) = from { + write!(f, " FROM {}", from)?; + } + if let Some(selection) = selection { + write!(f, " WHERE {}", selection)?; + } + if let Some(returning) = returning { + write!(f, " RETURNING {}", display_comma_separated(returning))?; + } + Ok(()) } - if let Some(returning) = returning { - write!(f, " RETURNING {}", display_comma_separated(returning))?; + Statement::Delete { + table_name, + using, + selection, + returning, + } => { + write!(f, "DELETE FROM {}", table_name)?; + if let Some(using) = using { + write!(f, " USING {}", using)?; + } + if let Some(selection) = selection { + write!(f, " WHERE {}", selection)?; + } + if let Some(returning) = returning { + write!(f, " RETURNING {}", display_comma_separated(returning))?; + } + Ok(()) } - Ok(()) - } - Statement::Close { cursor } => { - write!(f, "CLOSE {}", cursor)?; + Statement::Close { cursor } => { + write!(f, "CLOSE {}", cursor)?; - Ok(()) - } - Statement::CreateDatabase { - db_name, - if_not_exists, - location, - managed_location, - } => { - write!(f, "CREATE DATABASE")?; - 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::CreateFunction { - or_replace, - temporary, - name, - args, - return_type, - params, - } => { - write!( - f, - "CREATE {or_replace}{temp}FUNCTION {name}", - temp = if *temporary { "TEMPORARY " } else { "" }, - or_replace = if *or_replace { "OR REPLACE " } else { "" }, - )?; - if let Some(args) = args { - write!(f, "({})", display_comma_separated(args))?; - } - if let Some(return_type) = return_type { - write!(f, " RETURNS {}", return_type)?; - } - write!(f, "{params}")?; - Ok(()) - } - Statement::CreateView { - name, - or_replace, - columns, - query, - materialized, - with_options, - cluster_by, - } => { - 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))?; + Ok(()) } - if !cluster_by.is_empty() { - write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; + Statement::CreateDatabase { + db_name, + if_not_exists, + location, + managed_location, + } => { + write!(f, "CREATE DATABASE")?; + 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(()) } - write!(f, " AS {}", query) - } - Statement::CreateTable { - name, - columns, - constraints, - table_properties, - with_options, - or_replace, - if_not_exists, - hive_distribution, - hive_formats, - external, - global, - temporary, - file_format, - location, - query, - without_rowid, - like, - clone, - default_charset, - engine, - collation, - on_commit, - on_cluster, - } => { - // 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}{global}{temporary}TABLE {if_not_exists}{name}", - or_replace = if *or_replace { "OR REPLACE " } else { "" }, - external = if *external { "EXTERNAL " } else { "" }, - global = global - .map(|global| { - if global { - "GLOBAL " - } else { - "LOCAL " - } - }) - .unwrap_or(""), - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - temporary = if *temporary { "TEMPORARY " } else { "" }, - name = name, - )?; - if let Some(on_cluster) = on_cluster { + Statement::CreateFunction { + or_replace, + temporary, + name, + args, + return_type, + params, + } => { write!( f, - " ON CLUSTER {}", - on_cluster.replace('{', "'{").replace('}', "}'") + "CREATE {or_replace}{temp}FUNCTION {name}", + temp = if *temporary { "TEMPORARY " } else { "" }, + or_replace = if *or_replace { "OR REPLACE " } else { "" }, )?; - } - if !columns.is_empty() || !constraints.is_empty() { - write!(f, " ({}", display_comma_separated(columns))?; - if !columns.is_empty() && !constraints.is_empty() { - write!(f, ", ")?; + if let Some(args) = args { + write!(f, "({})", display_comma_separated(args))?; } - write!(f, "{})", display_comma_separated(constraints))?; - } else if query.is_none() && like.is_none() && clone.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)?; - } - - if let Some(c) = clone { - write!(f, " CLONE {}", c)?; + if let Some(return_type) = return_type { + write!(f, " RETURNS {}", return_type)?; + } + write!(f, "{params}")?; + Ok(()) } - - match hive_distribution { - HiveDistributionStyle::PARTITIONED { columns } => { - write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?; + Statement::CreateView { + name, + or_replace, + columns, + query, + materialized, + with_options, + cluster_by, + } => { + 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))?; } - 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)?; - } + if !columns.is_empty() { + write!(f, " ({})", display_comma_separated(columns))?; } - HiveDistributionStyle::SKEWED { - columns, - on, - stored_as_directories, - } => { + if !cluster_by.is_empty() { + write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; + } + write!(f, " AS {}", query) + } + Statement::CreateTable { + name, + columns, + constraints, + table_properties, + with_options, + or_replace, + if_not_exists, + hive_distribution, + hive_formats, + external, + global, + temporary, + file_format, + location, + query, + without_rowid, + like, + clone, + default_charset, + engine, + collation, + on_commit, + on_cluster, + } => { + // 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}{global}{temporary}TABLE {if_not_exists}{name}", + or_replace = if *or_replace { "OR REPLACE " } else { "" }, + external = if *external { "EXTERNAL " } else { "" }, + global = global + .map(|global| { + if global { + "GLOBAL " + } else { + "LOCAL " + } + }) + .unwrap_or(""), + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + temporary = if *temporary { "TEMPORARY " } else { "" }, + name = name, + )?; + if let Some(on_cluster) = on_cluster { write!( f, - " SKEWED BY ({})) ON ({})", - display_comma_separated(columns), - display_comma_separated(on) + " ON CLUSTER {}", + on_cluster.replace('{', "'{").replace('}', "}'") )?; - if *stored_as_directories { - write!(f, " STORED AS DIRECTORIES")?; + } + 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() && clone.is_none() { + // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens + write!(f, " ()")?; + } + // Only for SQLite + if *without_rowid { + write!(f, " WITHOUT ROWID")?; } - _ => (), - } - if let Some(HiveFormat { - row_format, - storage, - location, - }) = hive_formats - { - match row_format { - Some(HiveRowFormat::SERDE { class }) => { - write!(f, " ROW FORMAT SERDE '{}'", class)? + // Only for Hive + if let Some(l) = like { + write!(f, " LIKE {}", l)?; + } + + if let Some(c) = clone { + write!(f, " CLONE {}", c)?; + } + + match hive_distribution { + HiveDistributionStyle::PARTITIONED { columns } => { + write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?; } - 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)? + 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 !*external { - if let Some(loc) = location { - write!(f, " LOCATION '{}'", loc)?; + + 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)?; } + if let Some(engine) = engine { + write!(f, " ENGINE={}", engine)?; + } + if let Some(default_charset) = default_charset { + write!(f, " DEFAULT CHARSET={}", default_charset)?; + } + if let Some(collation) = collation { + write!(f, " COLLATE={}", collation)?; + } + + if on_commit.is_some() { + let on_commit = match on_commit { + Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS", + Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS", + Some(OnCommit::Drop) => "ON COMMIT DROP", + None => "", + }; + write!(f, " {}", on_commit)?; + } + + Ok(()) } - if *external { + Statement::CreateVirtualTable { + name, + if_not_exists, + module_name, + module_args, + } => { write!( f, - " STORED AS {} LOCATION '{}'", - file_format.as_ref().unwrap(), - location.as_ref().unwrap() + "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(()) } - if !table_properties.is_empty() { + Statement::CreateIndex { + name, + table_name, + using, + columns, + unique, + if_not_exists, + } => { write!( f, - " TBLPROPERTIES ({})", - display_comma_separated(table_properties) + "CREATE {unique}INDEX {if_not_exists}{name} ON {table_name}", + unique = if *unique { "UNIQUE " } else { "" }, + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + name = name, + table_name = table_name )?; + if let Some(value) = using { + write!(f, " USING {} ", value)?; + } + write!(f, "({})", display_separated(columns, ",")) + } + Statement::CreateRole { + names, + if_not_exists, + inherit, + login, + bypassrls, + password, + create_db, + create_role, + superuser, + replication, + connection_limit, + valid_until, + in_role, + in_group, + role, + user, + admin, + authorization_owner, + } => { + write!( + f, + "CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}", + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + names = display_separated(names, ", "), + superuser = match *superuser { + Some(true) => " SUPERUSER", + Some(false) => " NOSUPERUSER", + None => "" + }, + create_db = match *create_db { + Some(true) => " CREATEDB", + Some(false) => " NOCREATEDB", + None => "" + }, + create_role = match *create_role { + Some(true) => " CREATEROLE", + Some(false) => " NOCREATEROLE", + None => "" + }, + inherit = match *inherit { + Some(true) => " INHERIT", + Some(false) => " NOINHERIT", + None => "" + }, + login = match *login { + Some(true) => " LOGIN", + Some(false) => " NOLOGIN", + None => "" + }, + replication = match *replication { + Some(true) => " REPLICATION", + Some(false) => " NOREPLICATION", + None => "" + }, + bypassrls = match *bypassrls { + Some(true) => " BYPASSRLS", + Some(false) => " NOBYPASSRLS", + None => "" + } + )?; + if let Some(limit) = connection_limit { + write!(f, " CONNECTION LIMIT {}", limit)?; + } + match password { + Some(Password::Password(pass)) => write!(f, " PASSWORD {}", pass), + Some(Password::NullPassword) => write!(f, " PASSWORD NULL"), + None => Ok(()), + }?; + if let Some(until) = valid_until { + write!(f, " VALID UNTIL {}", until)?; + } + if !in_role.is_empty() { + write!(f, " IN ROLE {}", display_comma_separated(in_role))?; + } + if !in_group.is_empty() { + write!(f, " IN GROUP {}", display_comma_separated(in_group))?; + } + if !role.is_empty() { + write!(f, " ROLE {}", display_comma_separated(role))?; + } + if !user.is_empty() { + write!(f, " USER {}", display_comma_separated(user))?; + } + if !admin.is_empty() { + write!(f, " ADMIN {}", display_comma_separated(admin))?; + } + if let Some(owner) = authorization_owner { + write!(f, " AUTHORIZATION {}", owner)?; + } + Ok(()) } - if !with_options.is_empty() { - write!(f, " WITH ({})", display_comma_separated(with_options))?; - } - if let Some(query) = query { - write!(f, " AS {}", query)?; - } - if let Some(engine) = engine { - write!(f, " ENGINE={}", engine)?; - } - if let Some(default_charset) = default_charset { - write!(f, " DEFAULT CHARSET={}", default_charset)?; + Statement::AlterTable { name, operation } => { + write!(f, "ALTER TABLE {} {}", name, operation) + } + Statement::Drop { + object_type, + if_exists, + names, + cascade, + restrict, + purge, + } => write!( + f, + "DROP {}{} {}{}{}{}", + object_type, + if *if_exists { " IF EXISTS" } else { "" }, + display_comma_separated(names), + if *cascade { " CASCADE" } else { "" }, + if *restrict { " RESTRICT" } else { "" }, + if *purge { " PURGE" } else { "" } + ), + Statement::Discard { object_type } => { + write!(f, "DISCARD {object_type}", object_type = object_type)?; + Ok(()) } - if let Some(collation) = collation { - write!(f, " COLLATE={}", collation)?; + Self::SetRole { + context_modifier, + role_name, + } => { + let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE")); + write!(f, "SET{context_modifier} ROLE {role_name}") + } + 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) + ) } - - if on_commit.is_some() { - let on_commit = match on_commit { - Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS", - Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS", - Some(OnCommit::Drop) => "ON COMMIT DROP", - None => "", + Statement::SetTimeZone { local, value } => { + f.write_str("SET ")?; + if *local { + f.write_str("LOCAL ")?; + } + write!(f, "TIME ZONE {value}") + } + Statement::SetNames { + charset_name, + collation_name, + } => { + f.write_str("SET NAMES ")?; + f.write_str(charset_name)?; + + if let Some(collation) = collation_name { + f.write_str(" COLLATE ")?; + f.write_str(collation)?; }; - write!(f, " {}", on_commit)?; + + Ok(()) } + Statement::SetNamesDefault {} => { + f.write_str("SET NAMES DEFAULT")?; - 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(()) } - Ok(()) - } - Statement::CreateIndex { - name, - table_name, - using, - columns, - unique, - if_not_exists, - } => { - write!( - f, - "CREATE {unique}INDEX {if_not_exists}{name} ON {table_name}", - unique = if *unique { "UNIQUE " } else { "" }, - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - name = name, - table_name = table_name - )?; - if let Some(value) = using { - write!(f, " USING {} ", value)?; - } - write!(f, "({})", display_separated(columns, ",")) - } - Statement::CreateRole { - names, - if_not_exists, - inherit, - login, - bypassrls, - password, - create_db, - create_role, - superuser, - replication, - connection_limit, - valid_until, - in_role, - in_group, - role, - user, - admin, - authorization_owner, - } => { - write!( - f, - "CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}", - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - names = display_separated(names, ", "), - superuser = match *superuser { - Some(true) => " SUPERUSER", - Some(false) => " NOSUPERUSER", - None => "" - }, - create_db = match *create_db { - Some(true) => " CREATEDB", - Some(false) => " NOCREATEDB", - None => "" - }, - create_role = match *create_role { - Some(true) => " CREATEROLE", - Some(false) => " NOCREATEROLE", - None => "" - }, - inherit = match *inherit { - Some(true) => " INHERIT", - Some(false) => " NOINHERIT", - None => "" - }, - login = match *login { - Some(true) => " LOGIN", - Some(false) => " NOLOGIN", - None => "" - }, - replication = match *replication { - Some(true) => " REPLICATION", - Some(false) => " NOREPLICATION", - None => "" - }, - bypassrls = match *bypassrls { - Some(true) => " BYPASSRLS", - Some(false) => " NOBYPASSRLS", - None => "" + Statement::ShowVariable { variable } => { + write!(f, "SHOW")?; + if !variable.is_empty() { + write!(f, " {}", display_separated(variable, " "))?; } - )?; - if let Some(limit) = connection_limit { - write!(f, " CONNECTION LIMIT {}", limit)?; + Ok(()) } - match password { - Some(Password::Password(pass)) => write!(f, " PASSWORD {}", pass), - Some(Password::NullPassword) => write!(f, " PASSWORD NULL"), - None => Ok(()), - }?; - if let Some(until) = valid_until { - write!(f, " VALID UNTIL {}", until)?; + Statement::ShowVariables { filter } => { + write!(f, "SHOW VARIABLES")?; + if filter.is_some() { + write!(f, " {}", filter.as_ref().unwrap())?; + } + Ok(()) } - if !in_role.is_empty() { - write!(f, " IN ROLE {}", display_comma_separated(in_role))?; + Statement::ShowCreate { obj_type, obj_name } => { + write!( + f, + "SHOW CREATE {obj_type} {obj_name}", + obj_type = obj_type, + obj_name = obj_name, + )?; + Ok(()) } - if !in_group.is_empty() { - write!(f, " IN GROUP {}", display_comma_separated(in_group))?; + 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(()) } - if !role.is_empty() { - write!(f, " ROLE {}", display_comma_separated(role))?; + Statement::ShowTables { + extended, + full, + db_name, + filter, + } => { + write!( + f, + "SHOW {extended}{full}TABLES", + extended = if *extended { "EXTENDED " } else { "" }, + full = if *full { "FULL " } else { "" }, + )?; + if let Some(db_name) = db_name { + write!(f, " FROM {}", db_name)?; + } + if let Some(filter) = filter { + write!(f, " {}", filter)?; + } + Ok(()) } - if !user.is_empty() { - write!(f, " USER {}", display_comma_separated(user))?; + Statement::ShowFunctions { filter } => { + write!(f, "SHOW FUNCTIONS")?; + if let Some(filter) = filter { + write!(f, " {}", filter)?; + } + Ok(()) } - if !admin.is_empty() { - write!(f, " ADMIN {}", display_comma_separated(admin))?; + Statement::Use { db_name } => { + write!(f, "USE {}", db_name)?; + Ok(()) } - if let Some(owner) = authorization_owner { - write!(f, " AUTHORIZATION {}", owner)?; + Statement::ShowCollation { filter } => { + write!(f, "SHOW COLLATION")?; + if let Some(filter) = filter { + write!(f, " {}", filter)?; + } + Ok(()) } - Ok(()) - } - Statement::AlterTable { name, operation } => { - write!(f, "ALTER TABLE {} {}", name, operation) - } - Statement::Drop { - object_type, - if_exists, - names, - cascade, - restrict, - purge, - } => write!( - f, - "DROP {}{} {}{}{}{}", - object_type, - if *if_exists { " IF EXISTS" } else { "" }, - display_comma_separated(names), - if *cascade { " CASCADE" } else { "" }, - if *restrict { " RESTRICT" } else { "" }, - if *purge { " PURGE" } else { "" } - ), - Statement::Discard { object_type } => { - write!(f, "DISCARD {object_type}", object_type = object_type)?; - Ok(()) - } - Self::SetRole { - context_modifier, - role_name, - } => { - let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE")); - write!(f, "SET{context_modifier} ROLE {role_name}") - } - Statement::SetVariable { - local, - variable, - hivevar, - value, - } => { - f.write_str("SET ")?; - if *local { - f.write_str("LOCAL ")?; + Statement::StartTransaction { modes } => { + write!(f, "START TRANSACTION")?; + if !modes.is_empty() { + write!(f, " {}", display_comma_separated(modes))?; + } + Ok(()) } - write!( - f, - "{hivevar}{name} = {value}", - hivevar = if *hivevar { "HIVEVAR:" } else { "" }, - name = variable, - value = display_comma_separated(value) - ) - } - Statement::SetTimeZone { local, value } => { - f.write_str("SET ")?; - if *local { - f.write_str("LOCAL ")?; + Statement::SetTransaction { + modes, + snapshot, + session, + } => { + if *session { + write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?; + } else { + write!(f, "SET TRANSACTION")?; + } + if !modes.is_empty() { + write!(f, " {}", display_comma_separated(modes))?; + } + if let Some(snapshot_id) = snapshot { + write!(f, " SNAPSHOT {}", snapshot_id)?; + } + Ok(()) } - write!(f, "TIME ZONE {value}") - } - Statement::SetNames { - charset_name, - collation_name, - } => { - f.write_str("SET NAMES ")?; - f.write_str(charset_name)?; - - if let Some(collation) = collation_name { - f.write_str(" COLLATE ")?; - f.write_str(collation)?; - }; - - Ok(()) - } - Statement::SetNamesDefault {} => { - f.write_str("SET NAMES DEFAULT")?; - - Ok(()) - } - Statement::ShowVariable { variable } => { - write!(f, "SHOW")?; - if !variable.is_empty() { - write!(f, " {}", display_separated(variable, " "))?; + Statement::Commit { chain } => { + write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" },) } - Ok(()) - } - Statement::ShowVariables { filter } => { - write!(f, "SHOW VARIABLES")?; - if filter.is_some() { - write!(f, " {}", filter.as_ref().unwrap())?; + Statement::Rollback { chain } => { + write!(f, "ROLLBACK{}", if *chain { " AND CHAIN" } else { "" },) } - 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!( + Statement::CreateSchema { + schema_name, + if_not_exists, + } => 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::ShowTables { - extended, - full, - db_name, - filter, - } => { - write!( - f, - "SHOW {extended}{full}TABLES", - extended = if *extended { "EXTENDED " } else { "" }, - full = if *full { "FULL " } else { "" }, - )?; - if let Some(db_name) = db_name { - write!(f, " FROM {}", db_name)?; - } - if let Some(filter) = filter { - write!(f, " {}", filter)?; - } - Ok(()) - } - Statement::ShowFunctions { filter } => { - write!(f, "SHOW FUNCTIONS")?; - if let Some(filter) = filter { - write!(f, " {}", filter)?; - } - Ok(()) - } - Statement::Use { db_name } => { - write!(f, "USE {}", db_name)?; - Ok(()) - } - Statement::ShowCollation { filter } => { - write!(f, "SHOW COLLATION")?; - 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, - snapshot, - session, - } => { - if *session { - write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?; - } else { - write!(f, "SET TRANSACTION")?; - } - if !modes.is_empty() { - write!(f, " {}", display_comma_separated(modes))?; - } - if let Some(snapshot_id) = snapshot { - write!(f, " SNAPSHOT {}", snapshot_id)?; - } - 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::Grant { - privileges, - objects, - grantees, - with_grant_option, - granted_by, - } => { - write!(f, "GRANT {} ", privileges)?; - write!(f, "ON {} ", objects)?; - write!(f, "TO {}", display_comma_separated(grantees))?; - if *with_grant_option { - write!(f, " WITH GRANT OPTION")?; - } - if let Some(grantor) = granted_by { - write!(f, " GRANTED BY {}", grantor)?; + "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(()) } - Ok(()) - } - Statement::Revoke { - privileges, - objects, - grantees, - granted_by, - cascade, - } => { - write!(f, "REVOKE {} ", privileges)?; - write!(f, "ON {} ", objects)?; - write!(f, "FROM {}", display_comma_separated(grantees))?; - if let Some(grantor) = granted_by { - write!(f, " GRANTED BY {}", grantor)?; + Statement::Grant { + privileges, + objects, + grantees, + with_grant_option, + granted_by, + } => { + write!(f, "GRANT {} ", privileges)?; + write!(f, "ON {} ", objects)?; + write!(f, "TO {}", display_comma_separated(grantees))?; + if *with_grant_option { + write!(f, " WITH GRANT OPTION")?; + } + if let Some(grantor) = granted_by { + write!(f, " GRANTED BY {}", grantor)?; + } + Ok(()) } - write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?; - 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))?; + Statement::Revoke { + privileges, + objects, + grantees, + granted_by, + cascade, + } => { + write!(f, "REVOKE {} ", privileges)?; + write!(f, "ON {} ", objects)?; + write!(f, "FROM {}", display_comma_separated(grantees))?; + if let Some(grantor) = granted_by { + write!(f, " GRANTED BY {}", grantor)?; + } + write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?; + Ok(()) } - Ok(()) - } - Statement::Prepare { - name, - data_types, - statement, - } => { - write!(f, "PREPARE {} ", name)?; - if !data_types.is_empty() { - write!(f, "({}) ", display_comma_separated(data_types))?; + 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(()) } - write!(f, "AS {}", statement) - } - Statement::Comment { - object_type, - object_name, - comment, - } => { - write!(f, "COMMENT ON {} {} IS ", object_type, object_name)?; - if let Some(c) = comment { - write!(f, "'{}'", c) - } else { - write!(f, "NULL") + 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) + } + Statement::Comment { + object_type, + object_name, + comment, + } => { + write!(f, "COMMENT ON {} {} IS ", object_type, object_name)?; + if let Some(c) = comment { + write!(f, "'{}'", c) + } else { + write!(f, "NULL") + } } - } - Statement::Savepoint { name } => { - write!(f, "SAVEPOINT ")?; - write!(f, "{}", name) - } - Statement::Merge { - into, - table, - source, - on, - clauses, - } => { - write!( - f, - "MERGE{int} {table} USING {source} ", - int = if *into { " INTO" } else { "" } - )?; - write!(f, "ON {} ", on)?; - write!(f, "{}", display_separated(clauses, " ")) - } - Statement::Cache { - table_name, - table_flag, - has_as, - options, - query, - } => { - if table_flag.is_some() { + Statement::Savepoint { name } => { + write!(f, "SAVEPOINT ")?; + write!(f, "{}", name) + } + Statement::Merge { + into, + table, + source, + on, + clauses, + } => { write!( f, - "CACHE {table_flag} TABLE {table_name}", - table_flag = table_flag.clone().unwrap(), - table_name = table_name, + "MERGE{int} {table} USING {source} ", + int = if *into { " INTO" } else { "" } )?; - } else { - write!(f, "CACHE TABLE {table_name}", table_name = table_name,)?; - } + write!(f, "ON {} ", on)?; + write!(f, "{}", display_separated(clauses, " ")) + } + Statement::Cache { + table_name, + table_flag, + has_as, + options, + query, + } => { + if table_flag.is_some() { + write!( + f, + "CACHE {table_flag} TABLE {table_name}", + table_flag = table_flag.clone().unwrap(), + table_name = table_name, + )?; + } else { + write!(f, "CACHE TABLE {table_name}", table_name = table_name,)?; + } - if !options.is_empty() { - write!(f, " OPTIONS({})", display_comma_separated(options))?; - } + if !options.is_empty() { + write!(f, " OPTIONS({})", display_comma_separated(options))?; + } - let has_query = query.is_some(); - if *has_as && has_query { - write!(f, " AS {query}", query = query.clone().unwrap()) - } else if !has_as && has_query { - write!(f, " {query}", query = query.clone().unwrap()) - } else if *has_as && !has_query { - write!(f, " AS") - } else { - Ok(()) + let has_query = query.is_some(); + if *has_as && has_query { + write!(f, " AS {query}", query = query.clone().unwrap()) + } else if !has_as && has_query { + write!(f, " {query}", query = query.clone().unwrap()) + } else if *has_as && !has_query { + write!(f, " AS") + } else { + Ok(()) + } } - } - Statement::UNCache { - table_name, - if_exists, - } => { - if *if_exists { + Statement::UNCache { + table_name, + if_exists, + } => { + if *if_exists { + write!( + f, + "UNCACHE TABLE IF EXISTS {table_name}", + table_name = table_name + ) + } else { + write!(f, "UNCACHE TABLE {table_name}", table_name = table_name) + } + } + Statement::CreateSequence { + temporary, + if_not_exists, + name, + data_type, + sequence_options, + owned_by, + } => { + let as_type: String = if let Some(dt) = data_type.as_ref() { + //Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi + // " AS ".to_owned() + &dt.to_string() + [" AS ", &dt.to_string()].concat() + } else { + "".to_string() + }; write!( f, - "UNCACHE TABLE IF EXISTS {table_name}", - table_name = table_name - ) - } else { - write!(f, "UNCACHE TABLE {table_name}", table_name = table_name) - } - } - Statement::CreateSequence { - temporary, - if_not_exists, - name, - data_type, - sequence_options, - owned_by, - } => { - let as_type: String = if let Some(dt) = data_type.as_ref() { - //Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi - // " AS ".to_owned() + &dt.to_string() - [" AS ", &dt.to_string()].concat() - } else { - "".to_string() - }; - write!( - f, - "CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}", - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - temporary = if *temporary { "TEMPORARY " } else { "" }, - name = name, - as_type = as_type - )?; - for sequence_option in sequence_options { - write!(f, "{}", sequence_option)?; - } - if let Some(ob) = owned_by.as_ref() { - write!(f, " OWNED BY {}", ob)?; + "CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}", + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + temporary = if *temporary { "TEMPORARY " } else { "" }, + name = name, + as_type = as_type + )?; + for sequence_option in sequence_options { + write!(f, "{}", sequence_option)?; + } + if let Some(ob) = owned_by.as_ref() { + write!(f, " OWNED BY {}", ob)?; + } + write!(f, "") } - write!(f, "") } - } + }) } } diff --git a/src/parser.rs b/src/parser.rs index 87fb674aa..5e18bad44 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -161,7 +161,9 @@ impl<'a> Parser<'a> { return parser.expected("end of statement", parser.peek_token()); } - let statement = parser.parse_statement()?; + let statement = stacker::maybe_grow(2 * 1024 * 1024, 5 * 1024 * 1024, || { + parser.parse_statement() + })?; stmts.push(statement); expecting_statement_delimiter = true; } @@ -6821,4 +6823,16 @@ mod tests { )) ); } + + #[test] + fn test_stack_overflow() { + let pg_dialect = PostgreSqlDialect {}; + let sql = r#" + SELECT COUNT(*) FROM (SELECT id, user_id FROM test WHERE ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((user_id = $1 OR user_id = $2) OR user_id = $3) OR user_id = $4) OR user_id = $5) OR user_id = $6) OR user_id = $7) OR user_id = $8) OR user_id = $9) OR user_id = $10) OR user_id = $11) OR user_id = $12) OR user_id = $13) OR user_id = $14) OR user_id = $15) OR user_id = $16) OR user_id = $17) OR user_id = $18) OR user_id = $19) OR user_id = $20) OR user_id = $21) OR user_id = $22) OR user_id = $23) OR user_id = $24) OR user_id = $25) OR user_id = $26) OR user_id = $27) OR user_id = $28) OR user_id = $29) OR user_id = $30) OR user_id = $31) OR user_id = $32) OR user_id = $33) OR user_id = $34) OR user_id = $35) OR user_id = $36) OR user_id = $37) OR user_id = $38) OR user_id = $39) OR user_id = $40) OR user_id = $41) OR user_id = $42) OR user_id = $43) OR user_id = $44) OR user_id = $45) OR user_id = $46) OR user_id = $47) OR user_id = $48) OR user_id = $49) OR user_id = $50) OR user_id = $51) OR user_id = $52) OR user_id = $53) OR user_id = $54) OR user_id = $55) OR user_id = $56) OR user_id = $57) OR user_id = $58) OR user_id = $59) OR user_id = $60) OR user_id = $61) OR user_id = $62) OR user_id = $63) OR user_id = $64) OR user_id = $65) OR user_id = $66) OR user_id = $67) OR user_id = $68) OR user_id = $69) OR user_id = $70) OR user_id = $71) OR user_id = $72) OR user_id = $73) OR user_id = $74) OR user_id = $75) OR user_id = $76) OR user_id = $77) OR user_id = $78) OR user_id = $79) OR user_id = $80) OR user_id = $81) OR user_id = $82) OR user_id = $83) OR user_id = $84) OR user_id = $85) OR user_id = $86) OR user_id = $87) OR user_id = $88) OR user_id = $89) OR user_id = $90) OR user_id = $91) OR user_id = $92) OR user_id = $93) OR user_id = $94) OR user_id = $95) OR user_id = $96) OR user_id = $97) OR user_id = $98) OR user_id = $99) OR user_id = $100) OR user_id = $101) OR user_id = $102) OR user_id = $103) OR user_id = $104) OR user_id = $105) OR user_id = $106) OR user_id = $107) OR user_id = $108) OR user_id = $109) OR user_id = $110) OR user_id = $111) OR user_id = $112) OR user_id = $113) AND visible = $114) t"#; + let mut ast = Parser::parse_sql(&pg_dialect, sql).unwrap(); + assert_eq!( + ast[0].to_string(), + r#"SELECT COUNT(*) FROM (SELECT id, user_id FROM test WHERE ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((user_id = $1 OR user_id = $2) OR user_id = $3) OR user_id = $4) OR user_id = $5) OR user_id = $6) OR user_id = $7) OR user_id = $8) OR user_id = $9) OR user_id = $10) OR user_id = $11) OR user_id = $12) OR user_id = $13) OR user_id = $14) OR user_id = $15) OR user_id = $16) OR user_id = $17) OR user_id = $18) OR user_id = $19) OR user_id = $20) OR user_id = $21) OR user_id = $22) OR user_id = $23) OR user_id = $24) OR user_id = $25) OR user_id = $26) OR user_id = $27) OR user_id = $28) OR user_id = $29) OR user_id = $30) OR user_id = $31) OR user_id = $32) OR user_id = $33) OR user_id = $34) OR user_id = $35) OR user_id = $36) OR user_id = $37) OR user_id = $38) OR user_id = $39) OR user_id = $40) OR user_id = $41) OR user_id = $42) OR user_id = $43) OR user_id = $44) OR user_id = $45) OR user_id = $46) OR user_id = $47) OR user_id = $48) OR user_id = $49) OR user_id = $50) OR user_id = $51) OR user_id = $52) OR user_id = $53) OR user_id = $54) OR user_id = $55) OR user_id = $56) OR user_id = $57) OR user_id = $58) OR user_id = $59) OR user_id = $60) OR user_id = $61) OR user_id = $62) OR user_id = $63) OR user_id = $64) OR user_id = $65) OR user_id = $66) OR user_id = $67) OR user_id = $68) OR user_id = $69) OR user_id = $70) OR user_id = $71) OR user_id = $72) OR user_id = $73) OR user_id = $74) OR user_id = $75) OR user_id = $76) OR user_id = $77) OR user_id = $78) OR user_id = $79) OR user_id = $80) OR user_id = $81) OR user_id = $82) OR user_id = $83) OR user_id = $84) OR user_id = $85) OR user_id = $86) OR user_id = $87) OR user_id = $88) OR user_id = $89) OR user_id = $90) OR user_id = $91) OR user_id = $92) OR user_id = $93) OR user_id = $94) OR user_id = $95) OR user_id = $96) OR user_id = $97) OR user_id = $98) OR user_id = $99) OR user_id = $100) OR user_id = $101) OR user_id = $102) OR user_id = $103) OR user_id = $104) OR user_id = $105) OR user_id = $106) OR user_id = $107) OR user_id = $108) OR user_id = $109) OR user_id = $110) OR user_id = $111) OR user_id = $112) OR user_id = $113) AND visible = $114) AS t"#, + ); + } } From ee3e0f20392502d4e3e28249d12a015cbe73a5d9 Mon Sep 17 00:00:00 2001 From: step-baby <3528004749@qq.com> Date: Thu, 8 Dec 2022 10:42:56 +0800 Subject: [PATCH 2/4] fix: fmt --- src/ast/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a2d047e24..1c645673c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2038,10 +2038,10 @@ impl fmt::Display for Statement { } if let Some(HiveFormat { - row_format, - storage, - location, - }) = hive_formats + row_format, + storage, + location, + }) = hive_formats { match row_format { Some(HiveRowFormat::SERDE { class }) => { @@ -2052,9 +2052,9 @@ impl fmt::Display for Statement { } match storage { Some(HiveIOFormat::IOF { - input_format, - output_format, - }) => write!( + input_format, + output_format, + }) => write!( f, " STORED AS INPUTFORMAT {} OUTPUTFORMAT {}", input_format, output_format From 66180245da5e8f9e44a376df0a716d72bc1d3f2c Mon Sep 17 00:00:00 2001 From: step-baby <3528004749@qq.com> Date: Thu, 8 Dec 2022 13:53:33 +0800 Subject: [PATCH 3/4] fix: clippy --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 5e18bad44..5fd7af285 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6829,7 +6829,7 @@ mod tests { let pg_dialect = PostgreSqlDialect {}; let sql = r#" SELECT COUNT(*) FROM (SELECT id, user_id FROM test WHERE ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((user_id = $1 OR user_id = $2) OR user_id = $3) OR user_id = $4) OR user_id = $5) OR user_id = $6) OR user_id = $7) OR user_id = $8) OR user_id = $9) OR user_id = $10) OR user_id = $11) OR user_id = $12) OR user_id = $13) OR user_id = $14) OR user_id = $15) OR user_id = $16) OR user_id = $17) OR user_id = $18) OR user_id = $19) OR user_id = $20) OR user_id = $21) OR user_id = $22) OR user_id = $23) OR user_id = $24) OR user_id = $25) OR user_id = $26) OR user_id = $27) OR user_id = $28) OR user_id = $29) OR user_id = $30) OR user_id = $31) OR user_id = $32) OR user_id = $33) OR user_id = $34) OR user_id = $35) OR user_id = $36) OR user_id = $37) OR user_id = $38) OR user_id = $39) OR user_id = $40) OR user_id = $41) OR user_id = $42) OR user_id = $43) OR user_id = $44) OR user_id = $45) OR user_id = $46) OR user_id = $47) OR user_id = $48) OR user_id = $49) OR user_id = $50) OR user_id = $51) OR user_id = $52) OR user_id = $53) OR user_id = $54) OR user_id = $55) OR user_id = $56) OR user_id = $57) OR user_id = $58) OR user_id = $59) OR user_id = $60) OR user_id = $61) OR user_id = $62) OR user_id = $63) OR user_id = $64) OR user_id = $65) OR user_id = $66) OR user_id = $67) OR user_id = $68) OR user_id = $69) OR user_id = $70) OR user_id = $71) OR user_id = $72) OR user_id = $73) OR user_id = $74) OR user_id = $75) OR user_id = $76) OR user_id = $77) OR user_id = $78) OR user_id = $79) OR user_id = $80) OR user_id = $81) OR user_id = $82) OR user_id = $83) OR user_id = $84) OR user_id = $85) OR user_id = $86) OR user_id = $87) OR user_id = $88) OR user_id = $89) OR user_id = $90) OR user_id = $91) OR user_id = $92) OR user_id = $93) OR user_id = $94) OR user_id = $95) OR user_id = $96) OR user_id = $97) OR user_id = $98) OR user_id = $99) OR user_id = $100) OR user_id = $101) OR user_id = $102) OR user_id = $103) OR user_id = $104) OR user_id = $105) OR user_id = $106) OR user_id = $107) OR user_id = $108) OR user_id = $109) OR user_id = $110) OR user_id = $111) OR user_id = $112) OR user_id = $113) AND visible = $114) t"#; - let mut ast = Parser::parse_sql(&pg_dialect, sql).unwrap(); + let ast = Parser::parse_sql(&pg_dialect, sql).unwrap(); assert_eq!( ast[0].to_string(), r#"SELECT COUNT(*) FROM (SELECT id, user_id FROM test WHERE ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((user_id = $1 OR user_id = $2) OR user_id = $3) OR user_id = $4) OR user_id = $5) OR user_id = $6) OR user_id = $7) OR user_id = $8) OR user_id = $9) OR user_id = $10) OR user_id = $11) OR user_id = $12) OR user_id = $13) OR user_id = $14) OR user_id = $15) OR user_id = $16) OR user_id = $17) OR user_id = $18) OR user_id = $19) OR user_id = $20) OR user_id = $21) OR user_id = $22) OR user_id = $23) OR user_id = $24) OR user_id = $25) OR user_id = $26) OR user_id = $27) OR user_id = $28) OR user_id = $29) OR user_id = $30) OR user_id = $31) OR user_id = $32) OR user_id = $33) OR user_id = $34) OR user_id = $35) OR user_id = $36) OR user_id = $37) OR user_id = $38) OR user_id = $39) OR user_id = $40) OR user_id = $41) OR user_id = $42) OR user_id = $43) OR user_id = $44) OR user_id = $45) OR user_id = $46) OR user_id = $47) OR user_id = $48) OR user_id = $49) OR user_id = $50) OR user_id = $51) OR user_id = $52) OR user_id = $53) OR user_id = $54) OR user_id = $55) OR user_id = $56) OR user_id = $57) OR user_id = $58) OR user_id = $59) OR user_id = $60) OR user_id = $61) OR user_id = $62) OR user_id = $63) OR user_id = $64) OR user_id = $65) OR user_id = $66) OR user_id = $67) OR user_id = $68) OR user_id = $69) OR user_id = $70) OR user_id = $71) OR user_id = $72) OR user_id = $73) OR user_id = $74) OR user_id = $75) OR user_id = $76) OR user_id = $77) OR user_id = $78) OR user_id = $79) OR user_id = $80) OR user_id = $81) OR user_id = $82) OR user_id = $83) OR user_id = $84) OR user_id = $85) OR user_id = $86) OR user_id = $87) OR user_id = $88) OR user_id = $89) OR user_id = $90) OR user_id = $91) OR user_id = $92) OR user_id = $93) OR user_id = $94) OR user_id = $95) OR user_id = $96) OR user_id = $97) OR user_id = $98) OR user_id = $99) OR user_id = $100) OR user_id = $101) OR user_id = $102) OR user_id = $103) OR user_id = $104) OR user_id = $105) OR user_id = $106) OR user_id = $107) OR user_id = $108) OR user_id = $109) OR user_id = $110) OR user_id = $111) OR user_id = $112) OR user_id = $113) AND visible = $114) AS t"#, From 853a8909dbfb77598377b2f077073a9d90ba4621 Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Mon, 12 Dec 2022 17:31:38 -0500 Subject: [PATCH 4/4] Minimize potential conflicts --- src/ast/mod.rs | 1957 ++++++++++++++++++++++++------------------------ 1 file changed, 982 insertions(+), 975 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 1c645673c..1dac44d60 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1530,1090 +1530,1097 @@ pub enum Statement { } impl fmt::Display for Statement { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // ensure stack does not overflow for deeply nested queries + stacker::maybe_grow(2 * 1024 * 1024, 5 * 1024 * 1024, || self.fmt_inner(f)) + } +} + +impl Statement { + /// Implement unguarded stack display logic + /// // 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 { - stacker::maybe_grow(2 * 1024 * 1024, 5 * 1024 * 1024, || { - match self { - Statement::Kill { modifier, id } => { - write!(f, "KILL ")?; + fn fmt_inner(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Statement::Kill { modifier, id } => { + write!(f, "KILL ")?; - if let Some(m) = modifier { - write!(f, "{} ", m)?; - } + if let Some(m) = modifier { + write!(f, "{} ", m)?; + } - write!(f, "{}", id) + write!(f, "{}", id) + } + Statement::ExplainTable { + describe_alias, + table_name, + } => { + if *describe_alias { + write!(f, "DESCRIBE ")?; + } else { + write!(f, "EXPLAIN ")?; } - Statement::ExplainTable { - describe_alias, - table_name, - } => { - if *describe_alias { - write!(f, "DESCRIBE ")?; - } else { - write!(f, "EXPLAIN ")?; - } - write!(f, "{}", table_name) - } - Statement::Explain { - describe_alias, - verbose, - analyze, - statement, - format, - } => { - if *describe_alias { - write!(f, "DESCRIBE ")?; - } else { - write!(f, "EXPLAIN ")?; - } + write!(f, "{}", table_name) + } + Statement::Explain { + describe_alias, + verbose, + analyze, + statement, + format, + } => { + if *describe_alias { + write!(f, "DESCRIBE ")?; + } else { + write!(f, "EXPLAIN ")?; + } - if *analyze { - write!(f, "ANALYZE ")?; - } + if *analyze { + write!(f, "ANALYZE ")?; + } - if *verbose { - write!(f, "VERBOSE ")?; - } + if *verbose { + write!(f, "VERBOSE ")?; + } - if let Some(format) = format { - write!(f, "FORMAT {} ", format)?; - } + if let Some(format) = format { + write!(f, "FORMAT {} ", format)?; + } - write!(f, "{}", statement) - } - Statement::Query(s) => write!(f, "{}", s), - Statement::Declare { - name, - binary, - sensitive, - scroll, - hold, - query, - } => { - write!(f, "DECLARE {} ", name)?; - - if *binary { - write!(f, "BINARY ")?; - } + write!(f, "{}", statement) + } + Statement::Query(s) => write!(f, "{}", s), + Statement::Declare { + name, + binary, + sensitive, + scroll, + hold, + query, + } => { + write!(f, "DECLARE {} ", name)?; - if let Some(sensitive) = sensitive { - if *sensitive { - write!(f, "INSENSITIVE ")?; - } else { - write!(f, "ASENSITIVE ")?; - } + if *binary { + write!(f, "BINARY ")?; + } + + if let Some(sensitive) = sensitive { + if *sensitive { + write!(f, "INSENSITIVE ")?; + } else { + write!(f, "ASENSITIVE ")?; } + } - if let Some(scroll) = scroll { - if *scroll { - write!(f, "SCROLL ")?; - } else { - write!(f, "NO SCROLL ")?; - } + if let Some(scroll) = scroll { + if *scroll { + write!(f, "SCROLL ")?; + } else { + write!(f, "NO SCROLL ")?; } + } - write!(f, "CURSOR ")?; + write!(f, "CURSOR ")?; - if let Some(hold) = hold { - if *hold { - write!(f, "WITH HOLD ")?; - } else { - write!(f, "WITHOUT HOLD ")?; - } + if let Some(hold) = hold { + if *hold { + write!(f, "WITH HOLD ")?; + } else { + write!(f, "WITHOUT HOLD ")?; } - - write!(f, "FOR {}", query) } - Statement::Fetch { - name, - direction, - into, - } => { - write!(f, "FETCH {} ", direction)?; - write!(f, "IN {}", name)?; + write!(f, "FOR {}", query) + } + Statement::Fetch { + name, + direction, + into, + } => { + write!(f, "FETCH {} ", direction)?; + + write!(f, "IN {}", name)?; - if let Some(into) = into { - write!(f, " INTO {}", into)?; + if let Some(into) = into { + write!(f, " INTO {}", into)?; + } + + Ok(()) + } + 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))?; } + } - Ok(()) + if *compute_statistics { + write!(f, " COMPUTE STATISTICS")?; } - 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)? + 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))?; } - write!(f, " {}", source) } - Statement::Msck { - table_name, - repair, - partition_action, - } => { + Ok(()) + } + Statement::Insert { + or, + into, + table_name, + overwrite, + partitioned, + columns, + after_columns, + source, + table, + on, + returning, + } => { + if let Some(action) = or { + write!(f, "INSERT OR {} INTO {} ", action, table_name)?; + } else { write!( f, - "MSCK {repair}TABLE {table}", - repair = if *repair { "REPAIR " } else { "" }, - table = table_name + "INSERT{over}{int}{tbl} {table_name} ", + table_name = table_name, + over = if *overwrite { " OVERWRITE" } else { "" }, + int = if *into { " INTO" } else { "" }, + tbl = if *table { " TABLE" } else { "" } )?; - 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(()) + if !columns.is_empty() { + write!(f, "({}) ", display_comma_separated(columns))?; } - 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 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)?; - 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(()) + if let Some(on) = on { + write!(f, "{}", on)?; } - Statement::Insert { - or, - into, - table_name, - overwrite, - partitioned, - columns, - after_columns, - source, - table, - on, - returning, - } => { - if let Some(action) = or { - write!(f, "INSERT OR {} INTO {} ", action, table_name)?; - } else { - write!( - f, - "INSERT{over}{int}{tbl} {table_name} ", - table_name = table_name, - over = if *overwrite { " OVERWRITE" } else { "" }, - int = if *into { " INTO" } else { "" }, - 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)?; - if let Some(on) = on { - write!(f, "{}", on)?; - } + if let Some(returning) = returning { + write!(f, " RETURNING {}", display_comma_separated(returning))?; + } - if let Some(returning) = returning { - write!(f, " RETURNING {}", display_comma_separated(returning))?; - } + Ok(()) + } - Ok(()) + Statement::Copy { + table_name, + columns, + to, + target, + options, + legacy_options, + values, + } => { + write!(f, "COPY {}", table_name)?; + if !columns.is_empty() { + write!(f, " ({})", display_comma_separated(columns))?; } - - Statement::Copy { - table_name, - columns, - to, - target, - options, - legacy_options, - values, - } => { - write!(f, "COPY {}", table_name)?; - if !columns.is_empty() { - write!(f, " ({})", display_comma_separated(columns))?; - } - write!(f, " {} {}", if *to { "TO" } else { "FROM" }, target)?; - if !options.is_empty() { - write!(f, " ({})", display_comma_separated(options))?; - } - if !legacy_options.is_empty() { - write!(f, " {}", display_separated(legacy_options, " "))?; - } - 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, " {} {}", if *to { "TO" } else { "FROM" }, target)?; + if !options.is_empty() { + write!(f, " ({})", display_comma_separated(options))?; + } + if !legacy_options.is_empty() { + write!(f, " {}", display_separated(legacy_options, " "))?; + } + 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\\.")?; } - Ok(()) + write!(f, "\n\\.")?; } - Statement::Update { - table, - assignments, - from, - selection, - returning, - } => { - write!(f, "UPDATE {}", table)?; - if !assignments.is_empty() { - write!(f, " SET {}", display_comma_separated(assignments))?; - } - if let Some(from) = from { - write!(f, " FROM {}", from)?; - } - if let Some(selection) = selection { - write!(f, " WHERE {}", selection)?; - } - if let Some(returning) = returning { - write!(f, " RETURNING {}", display_comma_separated(returning))?; - } - Ok(()) + Ok(()) + } + Statement::Update { + table, + assignments, + from, + selection, + returning, + } => { + write!(f, "UPDATE {}", table)?; + if !assignments.is_empty() { + write!(f, " SET {}", display_comma_separated(assignments))?; } - Statement::Delete { - table_name, - using, - selection, - returning, - } => { - write!(f, "DELETE FROM {}", table_name)?; - if let Some(using) = using { - write!(f, " USING {}", using)?; - } - if let Some(selection) = selection { - write!(f, " WHERE {}", selection)?; - } - if let Some(returning) = returning { - write!(f, " RETURNING {}", display_comma_separated(returning))?; - } - Ok(()) + if let Some(from) = from { + write!(f, " FROM {}", from)?; + } + if let Some(selection) = selection { + write!(f, " WHERE {}", selection)?; } - Statement::Close { cursor } => { - write!(f, "CLOSE {}", cursor)?; + if let Some(returning) = returning { + write!(f, " RETURNING {}", display_comma_separated(returning))?; + } + Ok(()) + } + Statement::Delete { + table_name, + using, + selection, + returning, + } => { + write!(f, "DELETE FROM {}", table_name)?; + if let Some(using) = using { + write!(f, " USING {}", using)?; + } + if let Some(selection) = selection { + write!(f, " WHERE {}", selection)?; + } + if let Some(returning) = returning { + write!(f, " RETURNING {}", display_comma_separated(returning))?; + } + Ok(()) + } + Statement::Close { cursor } => { + write!(f, "CLOSE {}", cursor)?; - Ok(()) + Ok(()) + } + Statement::CreateDatabase { + db_name, + if_not_exists, + location, + managed_location, + } => { + write!(f, "CREATE DATABASE")?; + if *if_not_exists { + write!(f, " IF NOT EXISTS")?; } - Statement::CreateDatabase { - db_name, - if_not_exists, - location, - managed_location, - } => { - write!(f, "CREATE DATABASE")?; - 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(()) + write!(f, " {}", db_name)?; + if let Some(l) = location { + write!(f, " LOCATION '{}'", l)?; } - Statement::CreateFunction { - or_replace, - temporary, - name, - args, - return_type, - params, - } => { - write!( - f, - "CREATE {or_replace}{temp}FUNCTION {name}", - temp = if *temporary { "TEMPORARY " } else { "" }, - or_replace = if *or_replace { "OR REPLACE " } else { "" }, - )?; - if let Some(args) = args { - write!(f, "({})", display_comma_separated(args))?; - } - if let Some(return_type) = return_type { - write!(f, " RETURNS {}", return_type)?; - } - write!(f, "{params}")?; - Ok(()) + if let Some(ml) = managed_location { + write!(f, " MANAGEDLOCATION '{}'", ml)?; } - Statement::CreateView { - name, - or_replace, - columns, - query, - materialized, - with_options, - cluster_by, - } => { - 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))?; - } - if !cluster_by.is_empty() { - write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; - } - write!(f, " AS {}", query) - } - Statement::CreateTable { - name, - columns, - constraints, - table_properties, - with_options, - or_replace, - if_not_exists, - hive_distribution, - hive_formats, - external, - global, - temporary, - file_format, - location, - query, - without_rowid, - like, - clone, - default_charset, - engine, - collation, - on_commit, - on_cluster, - } => { - // 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` + Ok(()) + } + Statement::CreateFunction { + or_replace, + temporary, + name, + args, + return_type, + params, + } => { + write!( + f, + "CREATE {or_replace}{temp}FUNCTION {name}", + temp = if *temporary { "TEMPORARY " } else { "" }, + or_replace = if *or_replace { "OR REPLACE " } else { "" }, + )?; + if let Some(args) = args { + write!(f, "({})", display_comma_separated(args))?; + } + if let Some(return_type) = return_type { + write!(f, " RETURNS {}", return_type)?; + } + write!(f, "{params}")?; + Ok(()) + } + Statement::CreateView { + name, + or_replace, + columns, + query, + materialized, + with_options, + cluster_by, + } => { + 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))?; + } + if !cluster_by.is_empty() { + write!(f, " CLUSTER BY ({})", display_comma_separated(cluster_by))?; + } + write!(f, " AS {}", query) + } + Statement::CreateTable { + name, + columns, + constraints, + table_properties, + with_options, + or_replace, + if_not_exists, + hive_distribution, + hive_formats, + external, + global, + temporary, + file_format, + location, + query, + without_rowid, + like, + clone, + default_charset, + engine, + collation, + on_commit, + on_cluster, + } => { + // 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}{global}{temporary}TABLE {if_not_exists}{name}", + or_replace = if *or_replace { "OR REPLACE " } else { "" }, + external = if *external { "EXTERNAL " } else { "" }, + global = global + .map(|global| { + if global { + "GLOBAL " + } else { + "LOCAL " + } + }) + .unwrap_or(""), + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + temporary = if *temporary { "TEMPORARY " } else { "" }, + name = name, + )?; + if let Some(on_cluster) = on_cluster { write!( f, - "CREATE {or_replace}{external}{global}{temporary}TABLE {if_not_exists}{name}", - or_replace = if *or_replace { "OR REPLACE " } else { "" }, - external = if *external { "EXTERNAL " } else { "" }, - global = global - .map(|global| { - if global { - "GLOBAL " - } else { - "LOCAL " - } - }) - .unwrap_or(""), - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - temporary = if *temporary { "TEMPORARY " } else { "" }, - name = name, + " ON CLUSTER {}", + on_cluster.replace('{', "'{").replace('}', "}'") )?; - if let Some(on_cluster) = on_cluster { - write!( - f, - " ON CLUSTER {}", - on_cluster.replace('{', "'{").replace('}', "}'") - )?; - } - 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() && clone.is_none() { - // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens - write!(f, " ()")?; - } - // Only for SQLite - if *without_rowid { - write!(f, " WITHOUT ROWID")?; + } + 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() && clone.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)?; - } + // Only for Hive + if let Some(l) = like { + write!(f, " LIKE {}", l)?; + } - if let Some(c) = clone { - write!(f, " CLONE {}", c)?; - } + if let Some(c) = clone { + write!(f, " CLONE {}", c)?; + } - 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")?; - } - } - _ => (), + match hive_distribution { + HiveDistributionStyle::PARTITIONED { columns } => { + write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?; } - - 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 => (), + 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))?; } - 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 *num_buckets > 0 { + write!(f, " INTO {} BUCKETS", num_buckets)?; } } - if *external { + HiveDistributionStyle::SKEWED { + columns, + on, + stored_as_directories, + } => { write!( f, - " STORED AS {} LOCATION '{}'", - file_format.as_ref().unwrap(), - location.as_ref().unwrap() + " SKEWED BY ({})) ON ({})", + display_comma_separated(columns), + display_comma_separated(on) )?; + if *stored_as_directories { + write!(f, " STORED AS DIRECTORIES")?; + } } - if !table_properties.is_empty() { - write!( + _ => (), + } + + 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, - " 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)?; - } - if let Some(engine) = engine { - write!(f, " ENGINE={}", engine)?; - } - if let Some(default_charset) = default_charset { - write!(f, " DEFAULT CHARSET={}", default_charset)?; - } - if let Some(collation) = collation { - write!(f, " COLLATE={}", collation)?; + " STORED AS INPUTFORMAT {} OUTPUTFORMAT {}", + input_format, output_format + )?, + Some(HiveIOFormat::FileFormat { format }) if !*external => { + write!(f, " STORED AS {}", format)? + } + _ => (), } - - if on_commit.is_some() { - let on_commit = match on_commit { - Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS", - Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS", - Some(OnCommit::Drop) => "ON COMMIT DROP", - None => "", - }; - write!(f, " {}", on_commit)?; + if !*external { + if let Some(loc) = location { + write!(f, " LOCATION '{}'", loc)?; + } } - - Ok(()) } - Statement::CreateVirtualTable { - name, - if_not_exists, - module_name, - module_args, - } => { + if *external { 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 + " STORED AS {} LOCATION '{}'", + file_format.as_ref().unwrap(), + location.as_ref().unwrap() )?; - if !module_args.is_empty() { - write!(f, " ({})", display_comma_separated(module_args))?; - } - Ok(()) } - Statement::CreateIndex { - name, - table_name, - using, - columns, - unique, - if_not_exists, - } => { - write!( - f, - "CREATE {unique}INDEX {if_not_exists}{name} ON {table_name}", - unique = if *unique { "UNIQUE " } else { "" }, - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - name = name, - table_name = table_name - )?; - if let Some(value) = using { - write!(f, " USING {} ", value)?; - } - write!(f, "({})", display_separated(columns, ",")) - } - Statement::CreateRole { - names, - if_not_exists, - inherit, - login, - bypassrls, - password, - create_db, - create_role, - superuser, - replication, - connection_limit, - valid_until, - in_role, - in_group, - role, - user, - admin, - authorization_owner, - } => { + if !table_properties.is_empty() { write!( f, - "CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}", - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - names = display_separated(names, ", "), - superuser = match *superuser { - Some(true) => " SUPERUSER", - Some(false) => " NOSUPERUSER", - None => "" - }, - create_db = match *create_db { - Some(true) => " CREATEDB", - Some(false) => " NOCREATEDB", - None => "" - }, - create_role = match *create_role { - Some(true) => " CREATEROLE", - Some(false) => " NOCREATEROLE", - None => "" - }, - inherit = match *inherit { - Some(true) => " INHERIT", - Some(false) => " NOINHERIT", - None => "" - }, - login = match *login { - Some(true) => " LOGIN", - Some(false) => " NOLOGIN", - None => "" - }, - replication = match *replication { - Some(true) => " REPLICATION", - Some(false) => " NOREPLICATION", - None => "" - }, - bypassrls = match *bypassrls { - Some(true) => " BYPASSRLS", - Some(false) => " NOBYPASSRLS", - None => "" - } + " TBLPROPERTIES ({})", + display_comma_separated(table_properties) )?; - if let Some(limit) = connection_limit { - write!(f, " CONNECTION LIMIT {}", limit)?; - } - match password { - Some(Password::Password(pass)) => write!(f, " PASSWORD {}", pass), - Some(Password::NullPassword) => write!(f, " PASSWORD NULL"), - None => Ok(()), - }?; - if let Some(until) = valid_until { - write!(f, " VALID UNTIL {}", until)?; - } - if !in_role.is_empty() { - write!(f, " IN ROLE {}", display_comma_separated(in_role))?; - } - if !in_group.is_empty() { - write!(f, " IN GROUP {}", display_comma_separated(in_group))?; - } - if !role.is_empty() { - write!(f, " ROLE {}", display_comma_separated(role))?; - } - if !user.is_empty() { - write!(f, " USER {}", display_comma_separated(user))?; - } - if !admin.is_empty() { - write!(f, " ADMIN {}", display_comma_separated(admin))?; - } - if let Some(owner) = authorization_owner { - write!(f, " AUTHORIZATION {}", owner)?; - } - Ok(()) } - Statement::AlterTable { name, operation } => { - write!(f, "ALTER TABLE {} {}", name, operation) - } - Statement::Drop { - object_type, - if_exists, - names, - cascade, - restrict, - purge, - } => write!( - f, - "DROP {}{} {}{}{}{}", - object_type, - if *if_exists { " IF EXISTS" } else { "" }, - display_comma_separated(names), - if *cascade { " CASCADE" } else { "" }, - if *restrict { " RESTRICT" } else { "" }, - if *purge { " PURGE" } else { "" } - ), - Statement::Discard { object_type } => { - write!(f, "DISCARD {object_type}", object_type = object_type)?; - Ok(()) + if !with_options.is_empty() { + write!(f, " WITH ({})", display_comma_separated(with_options))?; } - Self::SetRole { - context_modifier, - role_name, - } => { - let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE")); - write!(f, "SET{context_modifier} ROLE {role_name}") - } - 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) - ) + if let Some(query) = query { + write!(f, " AS {}", query)?; + } + if let Some(engine) = engine { + write!(f, " ENGINE={}", engine)?; + } + if let Some(default_charset) = default_charset { + write!(f, " DEFAULT CHARSET={}", default_charset)?; + } + if let Some(collation) = collation { + write!(f, " COLLATE={}", collation)?; } - Statement::SetTimeZone { local, value } => { - f.write_str("SET ")?; - if *local { - f.write_str("LOCAL ")?; - } - write!(f, "TIME ZONE {value}") - } - Statement::SetNames { - charset_name, - collation_name, - } => { - f.write_str("SET NAMES ")?; - f.write_str(charset_name)?; - - if let Some(collation) = collation_name { - f.write_str(" COLLATE ")?; - f.write_str(collation)?; - }; - Ok(()) + if on_commit.is_some() { + let on_commit = match on_commit { + Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS", + Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS", + Some(OnCommit::Drop) => "ON COMMIT DROP", + None => "", + }; + write!(f, " {}", on_commit)?; } - Statement::SetNamesDefault {} => { - f.write_str("SET NAMES DEFAULT")?; - Ok(()) + 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))?; } - Statement::ShowVariable { variable } => { - write!(f, "SHOW")?; - if !variable.is_empty() { - write!(f, " {}", display_separated(variable, " "))?; + Ok(()) + } + Statement::CreateIndex { + name, + table_name, + using, + columns, + unique, + if_not_exists, + } => { + write!( + f, + "CREATE {unique}INDEX {if_not_exists}{name} ON {table_name}", + unique = if *unique { "UNIQUE " } else { "" }, + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + name = name, + table_name = table_name + )?; + if let Some(value) = using { + write!(f, " USING {} ", value)?; + } + write!(f, "({})", display_separated(columns, ",")) + } + Statement::CreateRole { + names, + if_not_exists, + inherit, + login, + bypassrls, + password, + create_db, + create_role, + superuser, + replication, + connection_limit, + valid_until, + in_role, + in_group, + role, + user, + admin, + authorization_owner, + } => { + write!( + f, + "CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}", + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + names = display_separated(names, ", "), + superuser = match *superuser { + Some(true) => " SUPERUSER", + Some(false) => " NOSUPERUSER", + None => "" + }, + create_db = match *create_db { + Some(true) => " CREATEDB", + Some(false) => " NOCREATEDB", + None => "" + }, + create_role = match *create_role { + Some(true) => " CREATEROLE", + Some(false) => " NOCREATEROLE", + None => "" + }, + inherit = match *inherit { + Some(true) => " INHERIT", + Some(false) => " NOINHERIT", + None => "" + }, + login = match *login { + Some(true) => " LOGIN", + Some(false) => " NOLOGIN", + None => "" + }, + replication = match *replication { + Some(true) => " REPLICATION", + Some(false) => " NOREPLICATION", + None => "" + }, + bypassrls = match *bypassrls { + Some(true) => " BYPASSRLS", + Some(false) => " NOBYPASSRLS", + None => "" } - Ok(()) + )?; + if let Some(limit) = connection_limit { + write!(f, " CONNECTION LIMIT {}", limit)?; } - Statement::ShowVariables { filter } => { - write!(f, "SHOW VARIABLES")?; - if filter.is_some() { - write!(f, " {}", filter.as_ref().unwrap())?; - } - Ok(()) + match password { + Some(Password::Password(pass)) => write!(f, " PASSWORD {}", pass), + Some(Password::NullPassword) => write!(f, " PASSWORD NULL"), + None => Ok(()), + }?; + if let Some(until) = valid_until { + write!(f, " VALID UNTIL {}", until)?; } - Statement::ShowCreate { obj_type, obj_name } => { - write!( - f, - "SHOW CREATE {obj_type} {obj_name}", - obj_type = obj_type, - obj_name = obj_name, - )?; - Ok(()) + if !in_role.is_empty() { + write!(f, " IN ROLE {}", display_comma_separated(in_role))?; } - 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(()) + if !in_group.is_empty() { + write!(f, " IN GROUP {}", display_comma_separated(in_group))?; } - Statement::ShowTables { - extended, - full, - db_name, - filter, - } => { - write!( - f, - "SHOW {extended}{full}TABLES", - extended = if *extended { "EXTENDED " } else { "" }, - full = if *full { "FULL " } else { "" }, - )?; - if let Some(db_name) = db_name { - write!(f, " FROM {}", db_name)?; - } - if let Some(filter) = filter { - write!(f, " {}", filter)?; - } - Ok(()) + if !role.is_empty() { + write!(f, " ROLE {}", display_comma_separated(role))?; } - Statement::ShowFunctions { filter } => { - write!(f, "SHOW FUNCTIONS")?; - if let Some(filter) = filter { - write!(f, " {}", filter)?; - } - Ok(()) + if !user.is_empty() { + write!(f, " USER {}", display_comma_separated(user))?; } - Statement::Use { db_name } => { - write!(f, "USE {}", db_name)?; - Ok(()) + if !admin.is_empty() { + write!(f, " ADMIN {}", display_comma_separated(admin))?; } - Statement::ShowCollation { filter } => { - write!(f, "SHOW COLLATION")?; - if let Some(filter) = filter { - write!(f, " {}", filter)?; - } - Ok(()) + if let Some(owner) = authorization_owner { + write!(f, " AUTHORIZATION {}", owner)?; } - Statement::StartTransaction { modes } => { - write!(f, "START TRANSACTION")?; - if !modes.is_empty() { - write!(f, " {}", display_comma_separated(modes))?; - } - Ok(()) + Ok(()) + } + Statement::AlterTable { name, operation } => { + write!(f, "ALTER TABLE {} {}", name, operation) + } + Statement::Drop { + object_type, + if_exists, + names, + cascade, + restrict, + purge, + } => write!( + f, + "DROP {}{} {}{}{}{}", + object_type, + if *if_exists { " IF EXISTS" } else { "" }, + display_comma_separated(names), + if *cascade { " CASCADE" } else { "" }, + if *restrict { " RESTRICT" } else { "" }, + if *purge { " PURGE" } else { "" } + ), + Statement::Discard { object_type } => { + write!(f, "DISCARD {object_type}", object_type = object_type)?; + Ok(()) + } + Self::SetRole { + context_modifier, + role_name, + } => { + let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE")); + write!(f, "SET{context_modifier} ROLE {role_name}") + } + Statement::SetVariable { + local, + variable, + hivevar, + value, + } => { + f.write_str("SET ")?; + if *local { + f.write_str("LOCAL ")?; } - Statement::SetTransaction { - modes, - snapshot, - session, - } => { - if *session { - write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?; - } else { - write!(f, "SET TRANSACTION")?; - } - if !modes.is_empty() { - write!(f, " {}", display_comma_separated(modes))?; - } - if let Some(snapshot_id) = snapshot { - write!(f, " SNAPSHOT {}", snapshot_id)?; - } - Ok(()) + write!( + f, + "{hivevar}{name} = {value}", + hivevar = if *hivevar { "HIVEVAR:" } else { "" }, + name = variable, + value = display_comma_separated(value) + ) + } + Statement::SetTimeZone { local, value } => { + f.write_str("SET ")?; + if *local { + f.write_str("LOCAL ")?; } - Statement::Commit { chain } => { - write!(f, "COMMIT{}", if *chain { " AND CHAIN" } else { "" },) + write!(f, "TIME ZONE {value}") + } + Statement::SetNames { + charset_name, + collation_name, + } => { + f.write_str("SET NAMES ")?; + f.write_str(charset_name)?; + + if let Some(collation) = collation_name { + f.write_str(" COLLATE ")?; + f.write_str(collation)?; + }; + + Ok(()) + } + Statement::SetNamesDefault {} => { + f.write_str("SET NAMES DEFAULT")?; + + Ok(()) + } + Statement::ShowVariable { variable } => { + write!(f, "SHOW")?; + if !variable.is_empty() { + write!(f, " {}", display_separated(variable, " "))?; } - Statement::Rollback { chain } => { - write!(f, "ROLLBACK{}", if *chain { " AND CHAIN" } else { "" },) + Ok(()) + } + Statement::ShowVariables { filter } => { + write!(f, "SHOW VARIABLES")?; + if filter.is_some() { + write!(f, " {}", filter.as_ref().unwrap())?; } - Statement::CreateSchema { - schema_name, - if_not_exists, - } => write!( + Ok(()) + } + Statement::ShowCreate { obj_type, obj_name } => { + 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(()) + "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)?; } - Statement::Grant { - privileges, - objects, - grantees, - with_grant_option, - granted_by, - } => { - write!(f, "GRANT {} ", privileges)?; - write!(f, "ON {} ", objects)?; - write!(f, "TO {}", display_comma_separated(grantees))?; - if *with_grant_option { - write!(f, " WITH GRANT OPTION")?; - } - if let Some(grantor) = granted_by { - write!(f, " GRANTED BY {}", grantor)?; - } - Ok(()) + Ok(()) + } + Statement::ShowTables { + extended, + full, + db_name, + filter, + } => { + write!( + f, + "SHOW {extended}{full}TABLES", + extended = if *extended { "EXTENDED " } else { "" }, + full = if *full { "FULL " } else { "" }, + )?; + if let Some(db_name) = db_name { + write!(f, " FROM {}", db_name)?; } - Statement::Revoke { - privileges, - objects, - grantees, - granted_by, - cascade, - } => { - write!(f, "REVOKE {} ", privileges)?; - write!(f, "ON {} ", objects)?; - write!(f, "FROM {}", display_comma_separated(grantees))?; - if let Some(grantor) = granted_by { - write!(f, " GRANTED BY {}", grantor)?; - } - write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?; - Ok(()) + if let Some(filter) = filter { + write!(f, " {}", filter)?; } - 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(()) + Ok(()) + } + Statement::ShowFunctions { filter } => { + write!(f, "SHOW FUNCTIONS")?; + if let Some(filter) = filter { + write!(f, " {}", filter)?; } - 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) - } - Statement::Comment { - object_type, - object_name, - comment, - } => { - write!(f, "COMMENT ON {} {} IS ", object_type, object_name)?; - if let Some(c) = comment { - write!(f, "'{}'", c) - } else { - write!(f, "NULL") - } + Ok(()) + } + Statement::Use { db_name } => { + write!(f, "USE {}", db_name)?; + Ok(()) + } + Statement::ShowCollation { filter } => { + write!(f, "SHOW COLLATION")?; + 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, + snapshot, + session, + } => { + if *session { + write!(f, "SET SESSION CHARACTERISTICS AS TRANSACTION")?; + } else { + write!(f, "SET TRANSACTION")?; + } + if !modes.is_empty() { + write!(f, " {}", display_comma_separated(modes))?; + } + if let Some(snapshot_id) = snapshot { + write!(f, " SNAPSHOT {}", snapshot_id)?; + } + 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::Grant { + privileges, + objects, + grantees, + with_grant_option, + granted_by, + } => { + write!(f, "GRANT {} ", privileges)?; + write!(f, "ON {} ", objects)?; + write!(f, "TO {}", display_comma_separated(grantees))?; + if *with_grant_option { + write!(f, " WITH GRANT OPTION")?; + } + if let Some(grantor) = granted_by { + write!(f, " GRANTED BY {}", grantor)?; + } + Ok(()) + } + Statement::Revoke { + privileges, + objects, + grantees, + granted_by, + cascade, + } => { + write!(f, "REVOKE {} ", privileges)?; + write!(f, "ON {} ", objects)?; + write!(f, "FROM {}", display_comma_separated(grantees))?; + if let Some(grantor) = granted_by { + write!(f, " GRANTED BY {}", grantor)?; + } + write!(f, " {}", if *cascade { "CASCADE" } else { "RESTRICT" })?; + 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) + } + Statement::Comment { + object_type, + object_name, + comment, + } => { + write!(f, "COMMENT ON {} {} IS ", object_type, object_name)?; + if let Some(c) = comment { + write!(f, "'{}'", c) + } else { + write!(f, "NULL") } - Statement::Savepoint { name } => { - write!(f, "SAVEPOINT ")?; - write!(f, "{}", name) - } - Statement::Merge { - into, - table, - source, - on, - clauses, - } => { + } + Statement::Savepoint { name } => { + write!(f, "SAVEPOINT ")?; + write!(f, "{}", name) + } + Statement::Merge { + into, + table, + source, + on, + clauses, + } => { + write!( + f, + "MERGE{int} {table} USING {source} ", + int = if *into { " INTO" } else { "" } + )?; + write!(f, "ON {} ", on)?; + write!(f, "{}", display_separated(clauses, " ")) + } + Statement::Cache { + table_name, + table_flag, + has_as, + options, + query, + } => { + if table_flag.is_some() { write!( f, - "MERGE{int} {table} USING {source} ", - int = if *into { " INTO" } else { "" } + "CACHE {table_flag} TABLE {table_name}", + table_flag = table_flag.clone().unwrap(), + table_name = table_name, )?; - write!(f, "ON {} ", on)?; - write!(f, "{}", display_separated(clauses, " ")) - } - Statement::Cache { - table_name, - table_flag, - has_as, - options, - query, - } => { - if table_flag.is_some() { - write!( - f, - "CACHE {table_flag} TABLE {table_name}", - table_flag = table_flag.clone().unwrap(), - table_name = table_name, - )?; - } else { - write!(f, "CACHE TABLE {table_name}", table_name = table_name,)?; - } - - if !options.is_empty() { - write!(f, " OPTIONS({})", display_comma_separated(options))?; - } + } else { + write!(f, "CACHE TABLE {table_name}", table_name = table_name,)?; + } - let has_query = query.is_some(); - if *has_as && has_query { - write!(f, " AS {query}", query = query.clone().unwrap()) - } else if !has_as && has_query { - write!(f, " {query}", query = query.clone().unwrap()) - } else if *has_as && !has_query { - write!(f, " AS") - } else { - Ok(()) - } + if !options.is_empty() { + write!(f, " OPTIONS({})", display_comma_separated(options))?; } - Statement::UNCache { - table_name, - if_exists, - } => { - if *if_exists { - write!( - f, - "UNCACHE TABLE IF EXISTS {table_name}", - table_name = table_name - ) - } else { - write!(f, "UNCACHE TABLE {table_name}", table_name = table_name) - } + + let has_query = query.is_some(); + if *has_as && has_query { + write!(f, " AS {query}", query = query.clone().unwrap()) + } else if !has_as && has_query { + write!(f, " {query}", query = query.clone().unwrap()) + } else if *has_as && !has_query { + write!(f, " AS") + } else { + Ok(()) } - Statement::CreateSequence { - temporary, - if_not_exists, - name, - data_type, - sequence_options, - owned_by, - } => { - let as_type: String = if let Some(dt) = data_type.as_ref() { - //Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi - // " AS ".to_owned() + &dt.to_string() - [" AS ", &dt.to_string()].concat() - } else { - "".to_string() - }; + } + Statement::UNCache { + table_name, + if_exists, + } => { + if *if_exists { write!( f, - "CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}", - if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, - temporary = if *temporary { "TEMPORARY " } else { "" }, - name = name, - as_type = as_type - )?; - for sequence_option in sequence_options { - write!(f, "{}", sequence_option)?; - } - if let Some(ob) = owned_by.as_ref() { - write!(f, " OWNED BY {}", ob)?; - } - write!(f, "") + "UNCACHE TABLE IF EXISTS {table_name}", + table_name = table_name + ) + } else { + write!(f, "UNCACHE TABLE {table_name}", table_name = table_name) } } - }) + Statement::CreateSequence { + temporary, + if_not_exists, + name, + data_type, + sequence_options, + owned_by, + } => { + let as_type: String = if let Some(dt) = data_type.as_ref() { + //Cannot use format!(" AS {}", dt), due to format! is not available in --target thumbv6m-none-eabi + // " AS ".to_owned() + &dt.to_string() + [" AS ", &dt.to_string()].concat() + } else { + "".to_string() + }; + write!( + f, + "CREATE {temporary}SEQUENCE {if_not_exists}{name}{as_type}", + if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" }, + temporary = if *temporary { "TEMPORARY " } else { "" }, + name = name, + as_type = as_type + )?; + for sequence_option in sequence_options { + write!(f, "{}", sequence_option)?; + } + if let Some(ob) = owned_by.as_ref() { + write!(f, " OWNED BY {}", ob)?; + } + write!(f, "") + } + } } }