Merge remote-tracking branch 'origin/main'

This commit is contained in:
Dima 2025-06-28 11:33:15 +01:00
commit 09f85f5c45
30 changed files with 586 additions and 319 deletions

View file

@ -63,7 +63,7 @@ $ cargo run --example cli - [--dialectname]
};
let contents = if filename == "-" {
println!("Parsing from stdin using {:?}", dialect);
println!("Parsing from stdin using {dialect:?}");
let mut buf = Vec::new();
stdin()
.read_to_end(&mut buf)

View file

@ -45,25 +45,24 @@ fn basic_queries(c: &mut Criterion) {
let large_statement = {
let expressions = (0..1000)
.map(|n| format!("FN_{}(COL_{})", n, n))
.map(|n| format!("FN_{n}(COL_{n})"))
.collect::<Vec<_>>()
.join(", ");
let tables = (0..1000)
.map(|n| format!("TABLE_{}", n))
.map(|n| format!("TABLE_{n}"))
.collect::<Vec<_>>()
.join(" JOIN ");
let where_condition = (0..1000)
.map(|n| format!("COL_{} = {}", n, n))
.map(|n| format!("COL_{n} = {n}"))
.collect::<Vec<_>>()
.join(" OR ");
let order_condition = (0..1000)
.map(|n| format!("COL_{} DESC", n))
.map(|n| format!("COL_{n} DESC"))
.collect::<Vec<_>>()
.join(", ");
format!(
"SELECT {} FROM {} WHERE {} ORDER BY {}",
expressions, tables, where_condition, order_condition
"SELECT {expressions} FROM {tables} WHERE {where_condition} ORDER BY {order_condition}"
)
};

View file

@ -666,7 +666,7 @@ impl fmt::Display for DataType {
}
DataType::Enum(vals, bits) => {
match bits {
Some(bits) => write!(f, "ENUM{}", bits),
Some(bits) => write!(f, "ENUM{bits}"),
None => write!(f, "ENUM"),
}?;
write!(f, "(")?;
@ -714,16 +714,16 @@ impl fmt::Display for DataType {
}
// ClickHouse
DataType::Nullable(data_type) => {
write!(f, "Nullable({})", data_type)
write!(f, "Nullable({data_type})")
}
DataType::FixedString(character_length) => {
write!(f, "FixedString({})", character_length)
write!(f, "FixedString({character_length})")
}
DataType::LowCardinality(data_type) => {
write!(f, "LowCardinality({})", data_type)
write!(f, "LowCardinality({data_type})")
}
DataType::Map(key_data_type, value_data_type) => {
write!(f, "Map({}, {})", key_data_type, value_data_type)
write!(f, "Map({key_data_type}, {value_data_type})")
}
DataType::Tuple(fields) => {
write!(f, "Tuple({})", display_comma_separated(fields))
@ -745,7 +745,7 @@ impl fmt::Display for DataType {
DataType::NamedTable { name, columns } => {
write!(f, "{} TABLE ({})", name, display_comma_separated(columns))
}
DataType::GeometricType(kind) => write!(f, "{}", kind),
DataType::GeometricType(kind) => write!(f, "{kind}"),
DataType::TsVector => write!(f, "TSVECTOR"),
DataType::TsQuery => write!(f, "TSQUERY"),
}
@ -942,7 +942,7 @@ impl fmt::Display for CharacterLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CharacterLength::IntegerLength { length, unit } => {
write!(f, "{}", length)?;
write!(f, "{length}")?;
if let Some(unit) = unit {
write!(f, " {unit}")?;
}
@ -997,7 +997,7 @@ impl fmt::Display for BinaryLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BinaryLength::IntegerLength { length } => {
write!(f, "{}", length)?;
write!(f, "{length}")?;
}
BinaryLength::Max => {
write!(f, "MAX")?;

View file

@ -173,7 +173,7 @@ impl fmt::Display for AlterRoleOperation {
in_database,
} => {
if let Some(database_name) = in_database {
write!(f, "IN DATABASE {} ", database_name)?;
write!(f, "IN DATABASE {database_name} ")?;
}
match config_value {
@ -187,7 +187,7 @@ impl fmt::Display for AlterRoleOperation {
in_database,
} => {
if let Some(database_name) = in_database {
write!(f, "IN DATABASE {} ", database_name)?;
write!(f, "IN DATABASE {database_name} ")?;
}
match config_name {
@ -218,15 +218,15 @@ impl fmt::Display for Use {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("USE ")?;
match self {
Use::Catalog(name) => write!(f, "CATALOG {}", name),
Use::Schema(name) => write!(f, "SCHEMA {}", name),
Use::Database(name) => write!(f, "DATABASE {}", name),
Use::Warehouse(name) => write!(f, "WAREHOUSE {}", name),
Use::Role(name) => write!(f, "ROLE {}", name),
Use::Catalog(name) => write!(f, "CATALOG {name}"),
Use::Schema(name) => write!(f, "SCHEMA {name}"),
Use::Database(name) => write!(f, "DATABASE {name}"),
Use::Warehouse(name) => write!(f, "WAREHOUSE {name}"),
Use::Role(name) => write!(f, "ROLE {name}"),
Use::SecondaryRoles(secondary_roles) => {
write!(f, "SECONDARY ROLES {}", secondary_roles)
write!(f, "SECONDARY ROLES {secondary_roles}")
}
Use::Object(name) => write!(f, "{}", name),
Use::Object(name) => write!(f, "{name}"),
Use::Default => write!(f, "DEFAULT"),
}
}

View file

@ -57,7 +57,7 @@ impl fmt::Display for ReplicaIdentity {
ReplicaIdentity::None => f.write_str("NONE"),
ReplicaIdentity::Full => f.write_str("FULL"),
ReplicaIdentity::Default => f.write_str("DEFAULT"),
ReplicaIdentity::Index(idx) => write!(f, "USING INDEX {}", idx),
ReplicaIdentity::Index(idx) => write!(f, "USING INDEX {idx}"),
}
}
}
@ -450,7 +450,7 @@ pub enum Owner {
impl fmt::Display for Owner {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Owner::Ident(ident) => write!(f, "{}", ident),
Owner::Ident(ident) => write!(f, "{ident}"),
Owner::CurrentRole => write!(f, "CURRENT_ROLE"),
Owner::CurrentUser => write!(f, "CURRENT_USER"),
Owner::SessionUser => write!(f, "SESSION_USER"),
@ -525,7 +525,7 @@ impl fmt::Display for AlterTableOperation {
if *if_not_exists {
write!(f, " IF NOT EXISTS")?;
}
write!(f, " {} ({})", name, query)
write!(f, " {name} ({query})")
}
AlterTableOperation::Algorithm { equals, algorithm } => {
write!(
@ -540,7 +540,7 @@ impl fmt::Display for AlterTableOperation {
if *if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", name)
write!(f, " {name}")
}
AlterTableOperation::MaterializeProjection {
if_exists,
@ -551,9 +551,9 @@ impl fmt::Display for AlterTableOperation {
if *if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", name)?;
write!(f, " {name}")?;
if let Some(partition) = partition {
write!(f, " IN PARTITION {}", partition)?;
write!(f, " IN PARTITION {partition}")?;
}
Ok(())
}
@ -566,9 +566,9 @@ impl fmt::Display for AlterTableOperation {
if *if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", name)?;
write!(f, " {name}")?;
if let Some(partition) = partition {
write!(f, " IN PARTITION {}", partition)?;
write!(f, " IN PARTITION {partition}")?;
}
Ok(())
}
@ -1168,7 +1168,7 @@ impl fmt::Display for TableConstraint {
write!(f, " ON UPDATE {action}")?;
}
if let Some(characteristics) = characteristics {
write!(f, " {}", characteristics)?;
write!(f, " {characteristics}")?;
}
Ok(())
}
@ -1308,7 +1308,7 @@ impl fmt::Display for IndexType {
Self::SPGiST => write!(f, "SPGIST"),
Self::BRIN => write!(f, "BRIN"),
Self::Bloom => write!(f, "BLOOM"),
Self::Custom(name) => write!(f, "{}", name),
Self::Custom(name) => write!(f, "{name}"),
}
}
}
@ -1426,17 +1426,41 @@ impl fmt::Display for ColumnDef {
pub struct ViewColumnDef {
pub name: Ident,
pub data_type: Option<DataType>,
pub options: Option<Vec<ColumnOption>>,
pub options: Option<ColumnOptions>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ColumnOptions {
CommaSeparated(Vec<ColumnOption>),
SpaceSeparated(Vec<ColumnOption>),
}
impl ColumnOptions {
pub fn as_slice(&self) -> &[ColumnOption] {
match self {
ColumnOptions::CommaSeparated(options) => options.as_slice(),
ColumnOptions::SpaceSeparated(options) => options.as_slice(),
}
}
}
impl fmt::Display for ViewColumnDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(data_type) = self.data_type.as_ref() {
write!(f, " {}", data_type)?;
write!(f, " {data_type}")?;
}
if let Some(options) = self.options.as_ref() {
write!(f, " {}", display_comma_separated(options.as_slice()))?;
match options {
ColumnOptions::CommaSeparated(column_options) => {
write!(f, " {}", display_comma_separated(column_options.as_slice()))?;
}
ColumnOptions::SpaceSeparated(column_options) => {
write!(f, " {}", display_separated(column_options.as_slice(), " "))?
}
}
}
Ok(())
}
@ -1821,7 +1845,7 @@ impl fmt::Display for ColumnOption {
} => {
write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })?;
if let Some(characteristics) = characteristics {
write!(f, " {}", characteristics)?;
write!(f, " {characteristics}")?;
}
Ok(())
}
@ -1843,7 +1867,7 @@ impl fmt::Display for ColumnOption {
write!(f, " ON UPDATE {action}")?;
}
if let Some(characteristics) = characteristics {
write!(f, " {}", characteristics)?;
write!(f, " {characteristics}")?;
}
Ok(())
}
@ -1903,7 +1927,7 @@ impl fmt::Display for ColumnOption {
write!(f, "{parameters}")
}
OnConflict(keyword) => {
write!(f, "ON CONFLICT {:?}", keyword)?;
write!(f, "ON CONFLICT {keyword:?}")?;
Ok(())
}
Policy(parameters) => {

View file

@ -55,7 +55,7 @@ impl Display for IndexColumn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.column)?;
if let Some(operator_class) = &self.operator_class {
write!(f, " {}", operator_class)?;
write!(f, " {operator_class}")?;
}
Ok(())
}
@ -266,7 +266,7 @@ impl Display for CreateTable {
name = self.name,
)?;
if let Some(on_cluster) = &self.on_cluster {
write!(f, " ON CLUSTER {}", on_cluster)?;
write!(f, " ON CLUSTER {on_cluster}")?;
}
if !self.columns.is_empty() || !self.constraints.is_empty() {
f.write_str(" (")?;
@ -383,15 +383,15 @@ impl Display for CreateTable {
match &self.table_options {
options @ CreateTableOptions::With(_)
| options @ CreateTableOptions::Plain(_)
| options @ CreateTableOptions::TableProperties(_) => write!(f, " {}", options)?,
| options @ CreateTableOptions::TableProperties(_) => write!(f, " {options}")?,
_ => (),
}
if let Some(primary_key) = &self.primary_key {
write!(f, " PRIMARY KEY {}", primary_key)?;
write!(f, " PRIMARY KEY {primary_key}")?;
}
if let Some(order_by) = &self.order_by {
write!(f, " ORDER BY {}", order_by)?;
write!(f, " ORDER BY {order_by}")?;
}
if let Some(inherits) = &self.inherits {
write!(f, " INHERITS ({})", display_comma_separated(inherits))?;
@ -403,7 +403,7 @@ impl Display for CreateTable {
write!(f, " CLUSTER BY {cluster_by}")?;
}
if let options @ CreateTableOptions::Options(_) = &self.table_options {
write!(f, " {}", options)?;
write!(f, " {options}")?;
}
if let Some(external_volume) = self.external_volume.as_ref() {
write!(f, " EXTERNAL_VOLUME = '{external_volume}'")?;

View file

@ -67,7 +67,7 @@ impl fmt::Display for KeyValueOptions {
} else {
f.write_str(" ")?;
}
write!(f, "{}", option)?;
write!(f, "{option}")?;
}
}
Ok(())

View file

@ -28,6 +28,7 @@ use helpers::{
stmt_data_loading::{FileStagingCommand, StageLoadSelectItemKind},
};
use core::cmp::Ordering;
use core::ops::Deref;
use core::{
fmt::{self, Display},
@ -60,13 +61,14 @@ pub use self::ddl::{
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
AlterTableAlgorithm, AlterTableLock, AlterTableOperation, AlterType, AlterTypeAddValue,
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty,
ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction, Deduplicate,
DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint, TagsColumnOption,
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain, CreateFunction,
Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode,
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner,
Partition, ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint,
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
ViewColumnDef,
};
pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert};
pub use self::operator::{BinaryOperator, UnaryOperator};
@ -172,7 +174,7 @@ fn format_statement_list(f: &mut fmt::Formatter, statements: &[Statement]) -> fm
}
/// An identifier, decomposed into its value or character data and the quote style.
#[derive(Debug, Clone, PartialOrd, Ord)]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Ident {
@ -214,6 +216,35 @@ impl core::hash::Hash for Ident {
impl Eq for Ident {}
impl PartialOrd for Ident {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Ident {
fn cmp(&self, other: &Self) -> Ordering {
let Ident {
value,
quote_style,
// exhaustiveness check; we ignore spans in ordering
span: _,
} = self;
let Ident {
value: other_value,
quote_style: other_quote_style,
// exhaustiveness check; we ignore spans in ordering
span: _,
} = other;
// First compare by value, then by quote_style
value
.cmp(other_value)
.then_with(|| quote_style.cmp(other_quote_style))
}
}
impl Ident {
/// Create a new identifier with the given value and no quotes and an empty span.
pub fn new<S>(value: S) -> Self
@ -326,7 +357,7 @@ impl ObjectNamePart {
impl fmt::Display for ObjectNamePart {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ObjectNamePart::Identifier(ident) => write!(f, "{}", ident),
ObjectNamePart::Identifier(ident) => write!(f, "{ident}"),
}
}
}
@ -1179,8 +1210,8 @@ pub enum AccessExpr {
impl fmt::Display for AccessExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AccessExpr::Dot(expr) => write!(f, ".{}", expr),
AccessExpr::Subscript(subscript) => write!(f, "[{}]", subscript),
AccessExpr::Dot(expr) => write!(f, ".{expr}"),
AccessExpr::Subscript(subscript) => write!(f, "[{subscript}]"),
}
}
}
@ -1382,12 +1413,12 @@ impl fmt::Display for Expr {
match self {
Expr::Identifier(s) => write!(f, "{s}"),
Expr::Wildcard(_) => f.write_str("*"),
Expr::QualifiedWildcard(prefix, _) => write!(f, "{}.*", prefix),
Expr::QualifiedWildcard(prefix, _) => write!(f, "{prefix}.*"),
Expr::CompoundIdentifier(s) => write!(f, "{}", display_separated(s, ".")),
Expr::CompoundFieldAccess { root, access_chain } => {
write!(f, "{}", root)?;
write!(f, "{root}")?;
for field in access_chain {
write!(f, "{}", field)?;
write!(f, "{field}")?;
}
Ok(())
}
@ -1516,7 +1547,7 @@ impl fmt::Display for Expr {
} => {
let not_ = if *negated { "NOT " } else { "" };
if form.is_none() {
write!(f, "{} IS {}NORMALIZED", expr, not_)
write!(f, "{expr} IS {not_}NORMALIZED")
} else {
write!(
f,
@ -1838,7 +1869,7 @@ impl fmt::Display for Expr {
}
}
Expr::Named { expr, name } => {
write!(f, "{} AS {}", expr, name)
write!(f, "{expr} AS {name}")
}
Expr::Dictionary(fields) => {
write!(f, "{{{}}}", display_comma_separated(fields))
@ -2394,7 +2425,7 @@ impl fmt::Display for ConditionalStatements {
}
Ok(())
}
ConditionalStatements::BeginEnd(bes) => write!(f, "{}", bes),
ConditionalStatements::BeginEnd(bes) => write!(f, "{bes}"),
}
}
}
@ -2914,9 +2945,7 @@ impl Display for Set {
write!(
f,
"SET {modifier}ROLE {role_name}",
modifier = context_modifier
.map(|m| format!("{}", m))
.unwrap_or_default()
modifier = context_modifier.map(|m| format!("{m}")).unwrap_or_default()
)
}
Self::SetSessionParam(kind) => write!(f, "SET {kind}"),
@ -2949,7 +2978,7 @@ impl Display for Set {
charset_name,
collation_name,
} => {
write!(f, "SET NAMES {}", charset_name)?;
write!(f, "SET NAMES {charset_name}")?;
if let Some(collation) = collation_name {
f.write_str(" COLLATE ")?;
@ -2972,7 +3001,7 @@ impl Display for Set {
write!(
f,
"SET {}{}{} = {}",
scope.map(|s| format!("{}", s)).unwrap_or_default(),
scope.map(|s| format!("{s}")).unwrap_or_default(),
if *hivevar { "HIVEVAR:" } else { "" },
variable,
display_comma_separated(values)
@ -3914,6 +3943,7 @@ pub enum Statement {
or_alter: bool,
name: ObjectName,
params: Option<Vec<ProcedureParam>>,
language: Option<Ident>,
body: ConditionalStatements,
},
/// ```sql
@ -4214,7 +4244,7 @@ pub enum Statement {
/// ```sql
/// NOTIFY channel [ , payload ]
/// ```
/// send a notification event together with an optional “payload” string to channel
/// send a notification event together with an optional "payload" string to channel
///
/// See Postgres <https://www.postgresql.org/docs/current/sql-notify.html>
NOTIFY {
@ -4373,7 +4403,7 @@ impl fmt::Display for Statement {
write!(f, "{describe_alias} ")?;
if let Some(format) = hive_format {
write!(f, "{} ", format)?;
write!(f, "{format} ")?;
}
if *has_table_keyword {
write!(f, "TABLE ")?;
@ -4817,6 +4847,7 @@ impl fmt::Display for Statement {
name,
or_alter,
params,
language,
body,
} => {
write!(
@ -4832,6 +4863,10 @@ impl fmt::Display for Statement {
}
}
if let Some(language) = language {
write!(f, " LANGUAGE {language}")?;
}
write!(f, " AS {body}")
}
Statement::CreateMacro {
@ -5204,7 +5239,7 @@ impl fmt::Display for Statement {
if *only {
write!(f, "ONLY ")?;
}
write!(f, "{name} ", name = name)?;
write!(f, "{name} ")?;
if let Some(cluster) = on_cluster {
write!(f, "ON CLUSTER {cluster} ")?;
}
@ -5282,7 +5317,7 @@ impl fmt::Display for Statement {
)?;
if !session_params.options.is_empty() {
if *set {
write!(f, " {}", session_params)?;
write!(f, " {session_params}")?;
} else {
let options = session_params
.options
@ -5316,7 +5351,7 @@ impl fmt::Display for Statement {
if *purge { " PURGE" } else { "" },
)?;
if let Some(table_name) = table.as_ref() {
write!(f, " ON {}", table_name)?;
write!(f, " ON {table_name}")?;
};
Ok(())
}
@ -5571,7 +5606,7 @@ impl fmt::Display for Statement {
} => {
if *syntax_begin {
if let Some(modifier) = *modifier {
write!(f, "BEGIN {}", modifier)?;
write!(f, "BEGIN {modifier}")?;
} else {
write!(f, "BEGIN")?;
}
@ -5607,7 +5642,7 @@ impl fmt::Display for Statement {
if *end_syntax {
write!(f, "END")?;
if let Some(modifier) = *modifier {
write!(f, " {}", modifier)?;
write!(f, " {modifier}")?;
}
if *chain {
write!(f, " AND CHAIN")?;
@ -5706,7 +5741,7 @@ impl fmt::Display for Statement {
write!(f, " GRANTED BY {grantor}")?;
}
if let Some(cascade) = cascade {
write!(f, " {}", cascade)?;
write!(f, " {cascade}")?;
}
Ok(())
}
@ -5885,13 +5920,13 @@ impl fmt::Display for Statement {
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
)?;
if !directory_table_params.options.is_empty() {
write!(f, " DIRECTORY=({})", directory_table_params)?;
write!(f, " DIRECTORY=({directory_table_params})")?;
}
if !file_format.options.is_empty() {
write!(f, " FILE_FORMAT=({})", file_format)?;
write!(f, " FILE_FORMAT=({file_format})")?;
}
if !copy_options.options.is_empty() {
write!(f, " COPY_OPTIONS=({})", copy_options)?;
write!(f, " COPY_OPTIONS=({copy_options})")?;
}
if comment.is_some() {
write!(f, " COMMENT='{}'", comment.as_ref().unwrap())?;
@ -5914,7 +5949,7 @@ impl fmt::Display for Statement {
validation_mode,
partition,
} => {
write!(f, "COPY INTO {}", into)?;
write!(f, "COPY INTO {into}")?;
if let Some(into_columns) = into_columns {
write!(f, " ({})", display_comma_separated(into_columns))?;
}
@ -5930,12 +5965,12 @@ impl fmt::Display for Statement {
)?;
}
if let Some(from_obj_alias) = from_obj_alias {
write!(f, " AS {}", from_obj_alias)?;
write!(f, " AS {from_obj_alias}")?;
}
write!(f, ")")?;
} else if let Some(from_obj) = from_obj {
// Standard data load
write!(f, " FROM {}{}", from_obj, stage_params)?;
write!(f, " FROM {from_obj}{stage_params}")?;
if let Some(from_obj_alias) = from_obj_alias {
write!(f, " AS {from_obj_alias}")?;
}
@ -5948,24 +5983,24 @@ impl fmt::Display for Statement {
write!(f, " FILES = ('{}')", display_separated(files, "', '"))?;
}
if let Some(pattern) = pattern {
write!(f, " PATTERN = '{}'", pattern)?;
write!(f, " PATTERN = '{pattern}'")?;
}
if let Some(partition) = partition {
write!(f, " PARTITION BY {partition}")?;
}
if !file_format.options.is_empty() {
write!(f, " FILE_FORMAT=({})", file_format)?;
write!(f, " FILE_FORMAT=({file_format})")?;
}
if !copy_options.options.is_empty() {
match kind {
CopyIntoSnowflakeKind::Table => {
write!(f, " COPY_OPTIONS=({})", copy_options)?
write!(f, " COPY_OPTIONS=({copy_options})")?
}
CopyIntoSnowflakeKind::Location => write!(f, " {copy_options}")?,
}
}
if let Some(validation_mode) = validation_mode {
write!(f, " VALIDATION_MODE = {}", validation_mode)?;
write!(f, " VALIDATION_MODE = {validation_mode}")?;
}
Ok(())
}
@ -6011,10 +6046,10 @@ impl fmt::Display for Statement {
} => {
write!(f, "OPTIMIZE TABLE {name}")?;
if let Some(on_cluster) = on_cluster {
write!(f, " ON CLUSTER {on_cluster}", on_cluster = on_cluster)?;
write!(f, " ON CLUSTER {on_cluster}")?;
}
if let Some(partition) = partition {
write!(f, " {partition}", partition = partition)?;
write!(f, " {partition}")?;
}
if *include_final {
write!(f, " FINAL")?;
@ -6141,7 +6176,7 @@ impl fmt::Display for SetAssignment {
write!(
f,
"{}{} = {}",
self.scope.map(|s| format!("{}", s)).unwrap_or_default(),
self.scope.map(|s| format!("{s}")).unwrap_or_default(),
self.name,
self.value
)
@ -6870,7 +6905,7 @@ impl fmt::Display for GranteeName {
match self {
GranteeName::ObjectName(name) => name.fmt(f),
GranteeName::UserHost { user, host } => {
write!(f, "{}@{}", user, host)
write!(f, "{user}@{host}")
}
}
}
@ -6885,6 +6920,12 @@ pub enum GrantObjects {
AllSequencesInSchema { schemas: Vec<ObjectName> },
/// Grant privileges on `ALL TABLES IN SCHEMA <schema_name> [, ...]`
AllTablesInSchema { schemas: Vec<ObjectName> },
/// Grant privileges on `FUTURE SCHEMAS IN DATABASE <database_name> [, ...]`
FutureSchemasInDatabase { databases: Vec<ObjectName> },
/// Grant privileges on `FUTURE TABLES IN SCHEMA <schema_name> [, ...]`
FutureTablesInSchema { schemas: Vec<ObjectName> },
/// Grant privileges on `FUTURE VIEWS IN SCHEMA <schema_name> [, ...]`
FutureViewsInSchema { schemas: Vec<ObjectName> },
/// Grant privileges on specific databases
Databases(Vec<ObjectName>),
/// Grant privileges on specific schemas
@ -6953,6 +6994,27 @@ impl fmt::Display for GrantObjects {
display_comma_separated(schemas)
)
}
GrantObjects::FutureSchemasInDatabase { databases } => {
write!(
f,
"FUTURE SCHEMAS IN DATABASE {}",
display_comma_separated(databases)
)
}
GrantObjects::FutureTablesInSchema { schemas } => {
write!(
f,
"FUTURE TABLES IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::FutureViewsInSchema { schemas } => {
write!(
f,
"FUTURE VIEWS IN SCHEMA {}",
display_comma_separated(schemas)
)
}
GrantObjects::ResourceMonitors(objects) => {
write!(f, "RESOURCE MONITOR {}", display_comma_separated(objects))
}
@ -7040,7 +7102,7 @@ pub enum AssignmentTarget {
impl fmt::Display for AssignmentTarget {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AssignmentTarget::ColumnName(column) => write!(f, "{}", column),
AssignmentTarget::ColumnName(column) => write!(f, "{column}"),
AssignmentTarget::Tuple(columns) => write!(f, "({})", display_comma_separated(columns)),
}
}
@ -7285,8 +7347,8 @@ impl fmt::Display for FunctionArguments {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionArguments::None => Ok(()),
FunctionArguments::Subquery(query) => write!(f, "({})", query),
FunctionArguments::List(args) => write!(f, "({})", args),
FunctionArguments::Subquery(query) => write!(f, "({query})"),
FunctionArguments::List(args) => write!(f, "({args})"),
}
}
}
@ -7307,7 +7369,7 @@ pub struct FunctionArgumentList {
impl fmt::Display for FunctionArgumentList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(duplicate_treatment) = self.duplicate_treatment {
write!(f, "{} ", duplicate_treatment)?;
write!(f, "{duplicate_treatment} ")?;
}
write!(f, "{}", display_comma_separated(&self.args))?;
if !self.clauses.is_empty() {
@ -7367,7 +7429,7 @@ impl fmt::Display for FunctionArgumentClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FunctionArgumentClause::IgnoreOrRespectNulls(null_treatment) => {
write!(f, "{}", null_treatment)
write!(f, "{null_treatment}")
}
FunctionArgumentClause::OrderBy(order_by) => {
write!(f, "ORDER BY {}", display_comma_separated(order_by))
@ -7823,12 +7885,12 @@ pub enum SqlOption {
impl fmt::Display for SqlOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SqlOption::Clustered(c) => write!(f, "{}", c),
SqlOption::Clustered(c) => write!(f, "{c}"),
SqlOption::Ident(ident) => {
write!(f, "{}", ident)
write!(f, "{ident}")
}
SqlOption::KeyValue { key: name, value } => {
write!(f, "{} = {}", name, value)
write!(f, "{name} = {value}")
}
SqlOption::Partition {
column_name,
@ -7868,7 +7930,7 @@ impl fmt::Display for SqlOption {
SqlOption::NamedParenthesizedList(value) => {
write!(f, "{} = ", value.key)?;
if let Some(key) = &value.name {
write!(f, "{}", key)?;
write!(f, "{key}")?;
}
if !value.values.is_empty() {
write!(f, "({})", display_comma_separated(&value.values))?
@ -7925,7 +7987,7 @@ impl fmt::Display for AttachDuckDBDatabaseOption {
AttachDuckDBDatabaseOption::ReadOnly(Some(true)) => write!(f, "READ_ONLY true"),
AttachDuckDBDatabaseOption::ReadOnly(Some(false)) => write!(f, "READ_ONLY false"),
AttachDuckDBDatabaseOption::ReadOnly(None) => write!(f, "READ_ONLY"),
AttachDuckDBDatabaseOption::Type(t) => write!(f, "TYPE {}", t),
AttachDuckDBDatabaseOption::Type(t) => write!(f, "TYPE {t}"),
}
}
}
@ -9448,10 +9510,10 @@ impl fmt::Display for ShowStatementIn {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.clause)?;
if let Some(parent_type) = &self.parent_type {
write!(f, " {}", parent_type)?;
write!(f, " {parent_type}")?;
}
if let Some(parent_name) = &self.parent_name {
write!(f, " {}", parent_name)?;
write!(f, " {parent_name}")?;
}
Ok(())
}
@ -9532,7 +9594,7 @@ impl fmt::Display for TableObject {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TableName(table_name) => write!(f, "{table_name}"),
Self::TableFunction(func) => write!(f, "FUNCTION {}", func),
Self::TableFunction(func) => write!(f, "FUNCTION {func}"),
}
}
}
@ -9720,7 +9782,7 @@ pub struct ReturnStatement {
impl fmt::Display for ReturnStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.value {
Some(ReturnStatementValue::Expr(expr)) => write!(f, "RETURN {}", expr),
Some(ReturnStatementValue::Expr(expr)) => write!(f, "RETURN {expr}"),
None => write!(f, "RETURN"),
}
}
@ -9771,6 +9833,8 @@ impl fmt::Display for NullInclusion {
#[cfg(test)]
mod tests {
use crate::tokenizer::Location;
use super::*;
#[test]
@ -10066,4 +10130,16 @@ mod tests {
test_steps(OneOrManyWithParens::Many(vec![2]), vec![2], 3);
test_steps(OneOrManyWithParens::Many(vec![3, 4]), vec![3, 4], 4);
}
// Tests that the position in the code of an `Ident` does not affect its
// ordering.
#[test]
fn test_ident_ord() {
let mut a = Ident::with_span(Span::new(Location::new(1, 1), Location::new(1, 1)), "a");
let mut b = Ident::with_span(Span::new(Location::new(2, 2), Location::new(2, 2)), "b");
assert!(a < b);
std::mem::swap(&mut a.span, &mut b.span);
assert!(a < b);
}
}

View file

@ -1047,7 +1047,7 @@ impl fmt::Display for ConnectBy {
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Setting {
pub key: Ident,
pub value: Value,
pub value: Expr,
}
impl fmt::Display for Setting {
@ -1183,7 +1183,7 @@ impl fmt::Display for TableIndexHints {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} ", self.hint_type, self.index_type)?;
if let Some(for_clause) = &self.for_clause {
write!(f, "FOR {} ", for_clause)?;
write!(f, "FOR {for_clause} ")?;
}
write!(f, "({})", display_comma_separated(&self.index_names))
}
@ -1459,7 +1459,7 @@ impl fmt::Display for TableSampleQuantity {
}
write!(f, "{}", self.value)?;
if let Some(unit) = &self.unit {
write!(f, " {}", unit)?;
write!(f, " {unit}")?;
}
if self.parenthesized {
write!(f, ")")?;
@ -1552,7 +1552,7 @@ impl fmt::Display for TableSampleBucket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BUCKET {} OUT OF {}", self.bucket, self.total)?;
if let Some(on) = &self.on {
write!(f, " ON {}", on)?;
write!(f, " ON {on}")?;
}
Ok(())
}
@ -1561,19 +1561,19 @@ impl fmt::Display for TableSample {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.modifier)?;
if let Some(name) = &self.name {
write!(f, " {}", name)?;
write!(f, " {name}")?;
}
if let Some(quantity) = &self.quantity {
write!(f, " {}", quantity)?;
write!(f, " {quantity}")?;
}
if let Some(seed) = &self.seed {
write!(f, " {}", seed)?;
write!(f, " {seed}")?;
}
if let Some(bucket) = &self.bucket {
write!(f, " ({})", bucket)?;
write!(f, " ({bucket})")?;
}
if let Some(offset) = &self.offset {
write!(f, " OFFSET {}", offset)?;
write!(f, " OFFSET {offset}")?;
}
Ok(())
}
@ -1651,7 +1651,7 @@ impl fmt::Display for RowsPerMatch {
RowsPerMatch::AllRows(mode) => {
write!(f, "ALL ROWS PER MATCH")?;
if let Some(mode) = mode {
write!(f, " {}", mode)?;
write!(f, " {mode}")?;
}
Ok(())
}
@ -1777,7 +1777,7 @@ impl fmt::Display for MatchRecognizePattern {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use MatchRecognizePattern::*;
match self {
Symbol(symbol) => write!(f, "{}", symbol),
Symbol(symbol) => write!(f, "{symbol}"),
Exclude(symbol) => write!(f, "{{- {symbol} -}}"),
Permute(symbols) => write!(f, "PERMUTE({})", display_comma_separated(symbols)),
Concat(patterns) => write!(f, "{}", display_separated(patterns, " ")),
@ -2148,7 +2148,7 @@ impl fmt::Display for TableAliasColumnDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(ref data_type) = self.data_type {
write!(f, " {}", data_type)?;
write!(f, " {data_type}")?;
}
Ok(())
}
@ -2398,7 +2398,7 @@ impl fmt::Display for OrderBy {
write!(f, " {}", display_comma_separated(exprs))?;
}
OrderByKind::All(all) => {
write!(f, " ALL{}", all)?;
write!(f, " ALL{all}")?;
}
}
@ -2429,7 +2429,7 @@ impl fmt::Display for OrderByExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.expr, self.options)?;
if let Some(ref with_fill) = self.with_fill {
write!(f, " {}", with_fill)?
write!(f, " {with_fill}")?
}
Ok(())
}
@ -2452,13 +2452,13 @@ impl fmt::Display for WithFill {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WITH FILL")?;
if let Some(ref from) = self.from {
write!(f, " FROM {}", from)?;
write!(f, " FROM {from}")?;
}
if let Some(ref to) = self.to {
write!(f, " TO {}", to)?;
write!(f, " TO {to}")?;
}
if let Some(ref step) = self.step {
write!(f, " STEP {}", step)?;
write!(f, " STEP {step}")?;
}
Ok(())
}
@ -2487,7 +2487,7 @@ impl fmt::Display for InterpolateExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.column)?;
if let Some(ref expr) = self.expr {
write!(f, " AS {}", expr)?;
write!(f, " AS {expr}")?;
}
Ok(())
}
@ -2565,7 +2565,7 @@ impl fmt::Display for LimitClause {
Ok(())
}
LimitClause::OffsetCommaLimit { offset, limit } => {
write!(f, " LIMIT {}, {}", offset, limit)
write!(f, " LIMIT {offset}, {limit}")
}
}
}
@ -2702,12 +2702,12 @@ impl fmt::Display for PipeOperator {
write!(f, "DROP {}", display_comma_separated(columns.as_slice()))
}
PipeOperator::As { alias } => {
write!(f, "AS {}", alias)
write!(f, "AS {alias}")
}
PipeOperator::Limit { expr, offset } => {
write!(f, "LIMIT {}", expr)?;
write!(f, "LIMIT {expr}")?;
if let Some(offset) = offset {
write!(f, " OFFSET {}", offset)?;
write!(f, " OFFSET {offset}")?;
}
Ok(())
}
@ -2730,14 +2730,14 @@ impl fmt::Display for PipeOperator {
}
PipeOperator::Where { expr } => {
write!(f, "WHERE {}", expr)
write!(f, "WHERE {expr}")
}
PipeOperator::OrderBy { exprs } => {
write!(f, "ORDER BY {}", display_comma_separated(exprs.as_slice()))
}
PipeOperator::TableSample { sample } => {
write!(f, "{}", sample)
write!(f, "{sample}")
}
}
}
@ -3016,7 +3016,7 @@ pub enum FormatClause {
impl fmt::Display for FormatClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FormatClause::Identifier(ident) => write!(f, "FORMAT {}", ident),
FormatClause::Identifier(ident) => write!(f, "FORMAT {ident}"),
FormatClause::Null => write!(f, "FORMAT NULL"),
}
}
@ -3078,9 +3078,9 @@ impl fmt::Display for ForClause {
without_array_wrapper,
} => {
write!(f, "FOR JSON ")?;
write!(f, "{}", for_json)?;
write!(f, "{for_json}")?;
if let Some(root) = root {
write!(f, ", ROOT('{}')", root)?;
write!(f, ", ROOT('{root}')")?;
}
if *include_null_values {
write!(f, ", INCLUDE_NULL_VALUES")?;
@ -3098,7 +3098,7 @@ impl fmt::Display for ForClause {
r#type,
} => {
write!(f, "FOR XML ")?;
write!(f, "{}", for_xml)?;
write!(f, "{for_xml}")?;
if *binary_base64 {
write!(f, ", BINARY BASE64")?;
}
@ -3106,7 +3106,7 @@ impl fmt::Display for ForClause {
write!(f, ", TYPE")?;
}
if let Some(root) = root {
write!(f, ", ROOT('{}')", root)?;
write!(f, ", ROOT('{root}')")?;
}
if *elements {
write!(f, ", ELEMENTS")?;
@ -3133,7 +3133,7 @@ impl fmt::Display for ForXml {
ForXml::Raw(root) => {
write!(f, "RAW")?;
if let Some(root) = root {
write!(f, "('{}')", root)?;
write!(f, "('{root}')")?;
}
Ok(())
}
@ -3142,7 +3142,7 @@ impl fmt::Display for ForXml {
ForXml::Path(root) => {
write!(f, "PATH")?;
if let Some(root) = root {
write!(f, "('{}')", root)?;
write!(f, "('{root}')")?;
}
Ok(())
}
@ -3205,7 +3205,7 @@ impl fmt::Display for JsonTableColumn {
JsonTableColumn::Named(json_table_named_column) => {
write!(f, "{json_table_named_column}")
}
JsonTableColumn::ForOrdinality(ident) => write!(f, "{} FOR ORDINALITY", ident),
JsonTableColumn::ForOrdinality(ident) => write!(f, "{ident} FOR ORDINALITY"),
JsonTableColumn::Nested(json_table_nested_column) => {
write!(f, "{json_table_nested_column}")
}
@ -3271,10 +3271,10 @@ impl fmt::Display for JsonTableNamedColumn {
self.path
)?;
if let Some(on_empty) = &self.on_empty {
write!(f, " {} ON EMPTY", on_empty)?;
write!(f, " {on_empty} ON EMPTY")?;
}
if let Some(on_error) = &self.on_error {
write!(f, " {} ON ERROR", on_error)?;
write!(f, " {on_error} ON ERROR")?;
}
Ok(())
}
@ -3296,7 +3296,7 @@ impl fmt::Display for JsonTableColumnErrorHandling {
match self {
JsonTableColumnErrorHandling::Null => write!(f, "NULL"),
JsonTableColumnErrorHandling::Default(json_string) => {
write!(f, "DEFAULT {}", json_string)
write!(f, "DEFAULT {json_string}")
}
JsonTableColumnErrorHandling::Error => write!(f, "ERROR"),
}
@ -3429,12 +3429,12 @@ impl fmt::Display for XmlTableColumn {
default,
nullable,
} => {
write!(f, " {}", r#type)?;
write!(f, " {type}")?;
if let Some(p) = path {
write!(f, " PATH {}", p)?;
write!(f, " PATH {p}")?;
}
if let Some(d) = default {
write!(f, " DEFAULT {}", d)?;
write!(f, " DEFAULT {d}")?;
}
if !*nullable {
write!(f, " NOT NULL")?;
@ -3465,7 +3465,7 @@ impl fmt::Display for XmlPassingArgument {
}
write!(f, "{}", self.expr)?;
if let Some(alias) = &self.alias {
write!(f, " AS {}", alias)?;
write!(f, " AS {alias}")?;
}
Ok(())
}

View file

@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
use crate::ast::query::SelectItemQualifiedWildcardKind;
use crate::ast::{query::SelectItemQualifiedWildcardKind, ColumnOptions};
use core::iter;
use crate::tokenizer::Span;
@ -991,10 +991,13 @@ impl Spanned for ViewColumnDef {
options,
} = self;
union_spans(
core::iter::once(name.span)
.chain(options.iter().flat_map(|i| i.iter().map(|k| k.span()))),
)
name.span.union_opt(&options.as_ref().map(|o| o.span()))
}
}
impl Spanned for ColumnOptions {
fn span(&self) -> Span {
union_spans(self.as_slice().iter().map(|i| i.span()))
}
}
@ -1055,7 +1058,9 @@ impl Spanned for CreateTableOptions {
match self {
CreateTableOptions::None => Span::empty(),
CreateTableOptions::With(vec) => union_spans(vec.iter().map(|i| i.span())),
CreateTableOptions::Options(vec) => union_spans(vec.iter().map(|i| i.span())),
CreateTableOptions::Options(vec) => {
union_spans(vec.as_slice().iter().map(|i| i.span()))
}
CreateTableOptions::Plain(vec) => union_spans(vec.iter().map(|i| i.span())),
CreateTableOptions::TableProperties(vec) => union_spans(vec.iter().map(|i| i.span())),
}

View file

@ -116,7 +116,6 @@ impl From<ValueWithSpan> for Value {
derive(Visit, VisitMut),
visit(with = "visit_value")
)]
pub enum Value {
/// Numeric literal
#[cfg(not(feature = "bigdecimal"))]
@ -551,16 +550,16 @@ impl fmt::Display for EscapeUnicodeStringLiteral<'_> {
write!(f, r#"\\"#)?;
}
x if x.is_ascii() => {
write!(f, "{}", c)?;
write!(f, "{c}")?;
}
_ => {
let codepoint = c as u32;
// if the character fits in 32 bits, we can use the \XXXX format
// otherwise, we need to use the \+XXXXXX format
if codepoint <= 0xFFFF {
write!(f, "\\{:04X}", codepoint)?;
write!(f, "\\{codepoint:04X}")?;
} else {
write!(f, "\\+{:06X}", codepoint)?;
write!(f, "\\+{codepoint:06X}")?;
}
}
}

View file

@ -926,10 +926,10 @@ mod tests {
#[test]
fn overflow() {
let cond = (0..1000)
.map(|n| format!("X = {}", n))
.map(|n| format!("X = {n}"))
.collect::<Vec<_>>()
.join(" OR ");
let sql = format!("SELECT x where {0}", cond);
let sql = format!("SELECT x where {cond}");
let dialect = GenericDialect {};
let tokens = Tokenizer::new(&dialect, sql.as_str()).tokenize().unwrap();

View file

@ -615,7 +615,7 @@ pub trait Dialect: Debug + Any {
}
let token = parser.peek_token();
debug!("get_next_precedence_full() {:?}", token);
debug!("get_next_precedence_full() {token:?}");
match token.token {
Token::Word(w) if w.keyword == Keyword::OR => Ok(p!(Or)),
Token::Word(w) if w.keyword == Keyword::AND => Ok(p!(And)),
@ -1056,6 +1056,10 @@ pub trait Dialect: Debug + Any {
fn supports_set_names(&self) -> bool {
false
}
fn supports_space_separated_column_options(&self) -> bool {
false
}
}
/// This represents the operators for which precedence must be defined

View file

@ -104,7 +104,7 @@ impl Dialect for PostgreSqlDialect {
fn get_next_precedence(&self, parser: &Parser) -> Option<Result<u8, ParserError>> {
let token = parser.peek_token();
debug!("get_next_precedence() {:?}", token);
debug!("get_next_precedence() {token:?}");
// we only return some custom value here when the behaviour (not merely the numeric value) differs
// from the default implementation

View file

@ -360,6 +360,10 @@ impl Dialect for SnowflakeDialect {
fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
&RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR
}
fn supports_space_separated_column_options(&self) -> bool {
true
}
}
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {

View file

@ -395,6 +395,7 @@ define_keywords!(
FUNCTION,
FUNCTIONS,
FUSION,
FUTURE,
GENERAL,
GENERATE,
GENERATED,

View file

@ -436,7 +436,7 @@ impl<'a> Parser<'a> {
///
/// See example on [`Parser::new()`] for an example
pub fn try_with_sql(self, sql: &str) -> Result<Self, ParserError> {
debug!("Parsing sql '{}'...", sql);
debug!("Parsing sql '{sql}'...");
let tokens = Tokenizer::new(self.dialect, sql)
.with_unescape(self.options.unescape)
.tokenize_with_location()?;
@ -1226,10 +1226,10 @@ impl<'a> Parser<'a> {
expr = self.parse_compound_expr(expr, vec![])?;
debug!("prefix: {:?}", expr);
debug!("prefix: {expr:?}");
loop {
let next_precedence = self.get_next_precedence()?;
debug!("next precedence: {:?}", next_precedence);
debug!("next precedence: {next_precedence:?}");
if precedence >= next_precedence {
break;
@ -1631,8 +1631,7 @@ impl<'a> Parser<'a> {
Token::QuestionPipe => UnaryOperator::QuestionPipe,
_ => {
return Err(ParserError::ParserError(format!(
"Unexpected token in unary operator parsing: {:?}",
tok
"Unexpected token in unary operator parsing: {tok:?}"
)))
}
};
@ -2771,7 +2770,7 @@ impl<'a> Parser<'a> {
if self.dialect.supports_dictionary_syntax() {
self.prev_token(); // Put back the '{'
return self.parse_duckdb_struct_literal();
return self.parse_dictionary();
}
self.expected("an expression", token)
@ -3140,7 +3139,7 @@ impl<'a> Parser<'a> {
Ok(fields)
}
/// DuckDB specific: Parse a duckdb [dictionary]
/// DuckDB and ClickHouse specific: Parse a duckdb [dictionary] or a clickhouse [map] setting
///
/// Syntax:
///
@ -3149,18 +3148,18 @@ impl<'a> Parser<'a> {
/// ```
///
/// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
fn parse_duckdb_struct_literal(&mut self) -> Result<Expr, ParserError> {
/// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters
fn parse_dictionary(&mut self) -> Result<Expr, ParserError> {
self.expect_token(&Token::LBrace)?;
let fields =
self.parse_comma_separated0(Self::parse_duckdb_dictionary_field, Token::RBrace)?;
let fields = self.parse_comma_separated0(Self::parse_dictionary_field, Token::RBrace)?;
self.expect_token(&Token::RBrace)?;
Ok(Expr::Dictionary(fields))
}
/// Parse a field for a duckdb [dictionary]
/// Parse a field for a duckdb [dictionary] or a clickhouse [map] setting
///
/// Syntax
///
@ -3169,7 +3168,8 @@ impl<'a> Parser<'a> {
/// ```
///
/// [dictionary]: https://duckdb.org/docs/sql/data_types/struct#creating-structs
fn parse_duckdb_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> {
/// [map]: https://clickhouse.com/docs/operations/settings/settings#additional_table_filters
fn parse_dictionary_field(&mut self) -> Result<DictionaryField, ParserError> {
let key = self.parse_identifier()?;
self.expect_token(&Token::Colon)?;
@ -10579,17 +10579,7 @@ impl<'a> Parser<'a> {
/// Parses a column definition within a view.
fn parse_view_column(&mut self) -> Result<ViewColumnDef, ParserError> {
let name = self.parse_identifier()?;
let options = if (dialect_of!(self is BigQueryDialect | GenericDialect)
&& self.parse_keyword(Keyword::OPTIONS))
|| (dialect_of!(self is SnowflakeDialect | GenericDialect)
&& self.parse_keyword(Keyword::COMMENT))
{
self.prev_token();
self.parse_optional_column_option()?
.map(|option| vec![option])
} else {
None
};
let options = self.parse_view_column_options()?;
let data_type = if dialect_of!(self is ClickHouseDialect) {
Some(self.parse_data_type()?)
} else {
@ -10602,6 +10592,25 @@ impl<'a> Parser<'a> {
})
}
fn parse_view_column_options(&mut self) -> Result<Option<ColumnOptions>, ParserError> {
let mut options = Vec::new();
loop {
let option = self.parse_optional_column_option()?;
if let Some(option) = option {
options.push(option);
} else {
break;
}
}
if options.is_empty() {
Ok(None)
} else if self.dialect.supports_space_separated_column_options() {
Ok(Some(ColumnOptions::SpaceSeparated(options)))
} else {
Ok(Some(ColumnOptions::CommaSeparated(options)))
}
}
/// Parses a parenthesized comma-separated list of unqualified, possibly quoted identifiers.
/// For example: `(col1, "col 2", ...)`
pub fn parse_parenthesized_column_list(
@ -11208,7 +11217,7 @@ impl<'a> Parser<'a> {
let key_values = self.parse_comma_separated(|p| {
let key = p.parse_identifier()?;
p.expect_token(&Token::Eq)?;
let value = p.parse_value()?.value;
let value = p.parse_expr()?;
Ok(Setting { key, value })
})?;
Some(key_values)
@ -13650,7 +13659,7 @@ impl<'a> Parser<'a> {
let ident = self.parse_identifier()?;
if let GranteeName::ObjectName(namespace) = name {
name = GranteeName::ObjectName(ObjectName::from(vec![Ident::new(
format!("{}:{}", namespace, ident),
format!("{namespace}:{ident}"),
)]));
};
}
@ -13687,6 +13696,33 @@ impl<'a> Parser<'a> {
Some(GrantObjects::AllTablesInSchema {
schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?,
})
} else if self.parse_keywords(&[
Keyword::FUTURE,
Keyword::SCHEMAS,
Keyword::IN,
Keyword::DATABASE,
]) {
Some(GrantObjects::FutureSchemasInDatabase {
databases: self.parse_comma_separated(|p| p.parse_object_name(false))?,
})
} else if self.parse_keywords(&[
Keyword::FUTURE,
Keyword::TABLES,
Keyword::IN,
Keyword::SCHEMA,
]) {
Some(GrantObjects::FutureTablesInSchema {
schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?,
})
} else if self.parse_keywords(&[
Keyword::FUTURE,
Keyword::VIEWS,
Keyword::IN,
Keyword::SCHEMA,
]) {
Some(GrantObjects::FutureViewsInSchema {
schemas: self.parse_comma_separated(|p| p.parse_object_name(false))?,
})
} else if self.parse_keywords(&[
Keyword::ALL,
Keyword::SEQUENCES,
@ -14620,7 +14656,7 @@ impl<'a> Parser<'a> {
self.dialect
.get_reserved_keywords_for_select_item_operator(),
)
.map(|keyword| Ident::new(format!("{:?}", keyword)));
.map(|keyword| Ident::new(format!("{keyword:?}")));
match self.parse_wildcard_expr()? {
Expr::QualifiedWildcard(prefix, token) => Ok(SelectItem::QualifiedWildcard(
@ -15797,6 +15833,13 @@ impl<'a> Parser<'a> {
pub fn parse_create_procedure(&mut self, or_alter: bool) -> Result<Statement, ParserError> {
let name = self.parse_object_name(false)?;
let params = self.parse_optional_procedure_parameters()?;
let language = if self.parse_keyword(Keyword::LANGUAGE) {
Some(self.parse_identifier()?)
} else {
None
};
self.expect_keyword_is(Keyword::AS)?;
let body = self.parse_conditional_statements(&[Keyword::END])?;
@ -15805,6 +15848,7 @@ impl<'a> Parser<'a> {
name,
or_alter,
params,
language,
body,
})
}

View file

@ -270,7 +270,7 @@ impl TestedDialects {
tokenizer = tokenizer.with_unescape(options.unescape);
}
let tokens = tokenizer.tokenize().unwrap();
assert_eq!(expected, tokens, "Tokenized differently for {:?}", dialect);
assert_eq!(expected, tokens, "Tokenized differently for {dialect:?}");
});
}
}
@ -366,6 +366,11 @@ pub fn number(n: &str) -> Value {
Value::Number(n.parse().unwrap(), false)
}
/// Creates a [Value::SingleQuotedString]
pub fn single_quoted_string(s: impl Into<String>) -> Value {
Value::SingleQuotedString(s.into())
}
pub fn table_alias(name: impl Into<String>) -> Option<TableAlias> {
Some(TableAlias {
name: Ident::new(name),

View file

@ -1751,7 +1751,7 @@ impl<'a> Tokenizer<'a> {
(None, Some(tok)) => Ok(Some(tok)),
(None, None) => self.tokenizer_error(
chars.location(),
format!("Expected a valid binary operator after '{}'", prefix),
format!("Expected a valid binary operator after '{prefix}'"),
),
}
}
@ -1809,7 +1809,7 @@ impl<'a> Tokenizer<'a> {
chars.next();
let mut temp = String::new();
let end_delimiter = format!("${}$", value);
let end_delimiter = format!("${value}$");
loop {
match chars.next() {
@ -2402,13 +2402,13 @@ fn take_char_from_hex_digits(
location: chars.location(),
})?;
let digit = next_char.to_digit(16).ok_or_else(|| TokenizerError {
message: format!("Invalid hex digit in escaped unicode string: {}", next_char),
message: format!("Invalid hex digit in escaped unicode string: {next_char}"),
location: chars.location(),
})?;
result = result * 16 + digit;
}
char::from_u32(result).ok_or_else(|| TokenizerError {
message: format!("Invalid unicode character: {:x}", result),
message: format!("Invalid unicode character: {result:x}"),
location: chars.location(),
})
}
@ -3504,7 +3504,7 @@ mod tests {
}
fn check_unescape(s: &str, expected: Option<&str>) {
let s = format!("'{}'", s);
let s = format!("'{s}'");
let mut state = State {
peekable: s.chars().peekable(),
line: 0,

View file

@ -355,14 +355,16 @@ fn parse_create_view_with_options() {
ViewColumnDef {
name: Ident::new("age"),
data_type: None,
options: Some(vec![ColumnOption::Options(vec![SqlOption::KeyValue {
key: Ident::new("description"),
value: Expr::Value(
Value::DoubleQuotedString("field age".to_string()).with_span(
Span::new(Location::new(1, 42), Location::new(1, 52))
)
),
}])]),
options: Some(ColumnOptions::CommaSeparated(vec![ColumnOption::Options(
vec![SqlOption::KeyValue {
key: Ident::new("description"),
value: Expr::Value(
Value::DoubleQuotedString("field age".to_string()).with_span(
Span::new(Location::new(1, 42), Location::new(1, 52))
)
),
}]
)])),
},
],
columns

View file

@ -28,7 +28,7 @@ use test_utils::*;
use sqlparser::ast::Expr::{BinaryOp, Identifier};
use sqlparser::ast::SelectItem::UnnamedExpr;
use sqlparser::ast::TableFactor::Table;
use sqlparser::ast::Value::Number;
use sqlparser::ast::Value::Boolean;
use sqlparser::ast::*;
use sqlparser::dialect::ClickHouseDialect;
use sqlparser::dialect::GenericDialect;
@ -914,7 +914,7 @@ fn parse_create_view_with_fields_data_types() {
}]),
vec![]
)),
options: None
options: None,
},
ViewColumnDef {
name: "f".into(),
@ -926,7 +926,7 @@ fn parse_create_view_with_fields_data_types() {
}]),
vec![]
)),
options: None
options: None,
},
]
);
@ -965,38 +965,103 @@ fn parse_limit_by() {
#[test]
fn parse_settings_in_query() {
match clickhouse_and_generic()
.verified_stmt(r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#)
{
Statement::Query(query) => {
assert_eq!(
query.settings,
Some(vec![
Setting {
key: Ident::new("max_threads"),
value: Number("1".parse().unwrap(), false)
},
Setting {
key: Ident::new("max_block_size"),
value: Number("10000".parse().unwrap(), false)
},
])
);
fn check_settings(sql: &str, expected: Vec<Setting>) {
match clickhouse_and_generic().verified_stmt(sql) {
Statement::Query(q) => {
assert_eq!(q.settings, Some(expected));
}
_ => unreachable!(),
}
_ => unreachable!(),
}
for (sql, expected_settings) in [
(
r#"SELECT * FROM t SETTINGS max_threads = 1, max_block_size = 10000"#,
vec![
Setting {
key: Ident::new("max_threads"),
value: Expr::value(number("1")),
},
Setting {
key: Ident::new("max_block_size"),
value: Expr::value(number("10000")),
},
],
),
(
r#"SELECT * FROM t SETTINGS additional_table_filters = {'table_1': 'x != 2'}"#,
vec![Setting {
key: Ident::new("additional_table_filters"),
value: Expr::Dictionary(vec![DictionaryField {
key: Ident::with_quote('\'', "table_1"),
value: Expr::value(single_quoted_string("x != 2")).into(),
}]),
}],
),
(
r#"SELECT * FROM t SETTINGS additional_result_filter = 'x != 2', query_plan_optimize_lazy_materialization = false"#,
vec![
Setting {
key: Ident::new("additional_result_filter"),
value: Expr::value(single_quoted_string("x != 2")),
},
Setting {
key: Ident::new("query_plan_optimize_lazy_materialization"),
value: Expr::value(Boolean(false)),
},
],
),
] {
check_settings(sql, expected_settings);
}
let invalid_cases = vec![
"SELECT * FROM t SETTINGS a",
"SELECT * FROM t SETTINGS a=",
"SELECT * FROM t SETTINGS a=1, b",
"SELECT * FROM t SETTINGS a=1, b=",
"SELECT * FROM t SETTINGS a=1, b=c",
("SELECT * FROM t SETTINGS a", "Expected: =, found: EOF"),
(
"SELECT * FROM t SETTINGS a=",
"Expected: an expression, found: EOF",
),
("SELECT * FROM t SETTINGS a=1, b", "Expected: =, found: EOF"),
(
"SELECT * FROM t SETTINGS a=1, b=",
"Expected: an expression, found: EOF",
),
(
"SELECT * FROM t SETTINGS a = {",
"Expected: identifier, found: EOF",
),
(
"SELECT * FROM t SETTINGS a = {'b'",
"Expected: :, found: EOF",
),
(
"SELECT * FROM t SETTINGS a = {'b': ",
"Expected: an expression, found: EOF",
),
(
"SELECT * FROM t SETTINGS a = {'b': 'c',}",
"Expected: identifier, found: }",
),
(
"SELECT * FROM t SETTINGS a = {'b': 'c', 'd'}",
"Expected: :, found: }",
),
(
"SELECT * FROM t SETTINGS a = {'b': 'c', 'd': }",
"Expected: an expression, found: }",
),
(
"SELECT * FROM t SETTINGS a = {ANY(b)}",
"Expected: :, found: (",
),
];
for sql in invalid_cases {
clickhouse_and_generic()
.parse_sql_statements(sql)
.expect_err("Expected: SETTINGS key = value, found: ");
for (sql, error_msg) in invalid_cases {
assert_eq!(
clickhouse_and_generic()
.parse_sql_statements(sql)
.unwrap_err(),
ParserError(error_msg.to_string())
);
}
}
#[test]
@ -1345,7 +1410,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
clickhouse().verified_stmt(&format!("USE {}", object_name)),
clickhouse().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -1353,7 +1418,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
clickhouse().verified_stmt(&format!("USE {0}{1}{0}", quote, object_name)),
clickhouse().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),
@ -1367,7 +1432,7 @@ fn parse_use() {
fn test_query_with_format_clause() {
let format_options = vec!["TabSeparated", "JSONCompact", "NULL"];
for format in &format_options {
let sql = format!("SELECT * FROM t FORMAT {}", format);
let sql = format!("SELECT * FROM t FORMAT {format}");
match clickhouse_and_generic().verified_stmt(&sql) {
Statement::Query(query) => {
if *format == "NULL" {
@ -1550,11 +1615,11 @@ fn parse_select_table_function_settings() {
settings: Some(vec![
Setting {
key: "s0".into(),
value: Value::Number("3".parse().unwrap(), false),
value: Expr::value(number("3")),
},
Setting {
key: "s1".into(),
value: Value::SingleQuotedString("s".into()),
value: Expr::value(single_quoted_string("s")),
},
]),
},
@ -1575,11 +1640,11 @@ fn parse_select_table_function_settings() {
settings: Some(vec![
Setting {
key: "s0".into(),
value: Value::Number("3".parse().unwrap(), false),
value: Expr::value(number("3")),
},
Setting {
key: "s1".into(),
value: Value::SingleQuotedString("s".into()),
value: Expr::value(single_quoted_string("s")),
},
]),
},
@ -1589,7 +1654,6 @@ fn parse_select_table_function_settings() {
"SELECT * FROM t(SETTINGS a=)",
"SELECT * FROM t(SETTINGS a=1, b)",
"SELECT * FROM t(SETTINGS a=1, b=)",
"SELECT * FROM t(SETTINGS a=1, b=c)",
];
for sql in invalid_cases {
clickhouse_and_generic()

View file

@ -3563,7 +3563,7 @@ fn test_double_value() {
for (input, expected) in test_cases {
for (i, expr) in input.iter().enumerate() {
if let Statement::Query(query) =
dialects.one_statement_parses_to(&format!("SELECT {}", expr), "")
dialects.one_statement_parses_to(&format!("SELECT {expr}"), "")
{
if let SetExpr::Select(select) = *query.body {
assert_eq!(expected[i], select.projection[0]);
@ -4023,13 +4023,13 @@ fn parse_create_table_column_constraint_characteristics() {
syntax
};
let sql = format!("CREATE TABLE t (a int UNIQUE {})", syntax);
let sql = format!("CREATE TABLE t (a int UNIQUE {syntax})");
let expected_clause = if syntax.is_empty() {
String::new()
} else {
format!(" {syntax}")
};
let expected = format!("CREATE TABLE t (a INT UNIQUE{})", expected_clause);
let expected = format!("CREATE TABLE t (a INT UNIQUE{expected_clause})");
let ast = one_statement_parses_to(&sql, &expected);
let expected_value = if deferrable.is_some() || initially.is_some() || enforced.is_some() {
@ -7499,7 +7499,7 @@ fn parse_cte_in_data_modification_statements() {
assert_eq!(query.with.unwrap().to_string(), "WITH x AS (SELECT 1)");
assert!(matches!(*query.body, SetExpr::Update(_)));
}
other => panic!("Expected: UPDATE, got: {:?}", other),
other => panic!("Expected: UPDATE, got: {other:?}"),
}
match verified_stmt("WITH t (x) AS (SELECT 9) DELETE FROM q WHERE id IN (SELECT x FROM t)") {
@ -7507,7 +7507,7 @@ fn parse_cte_in_data_modification_statements() {
assert_eq!(query.with.unwrap().to_string(), "WITH t (x) AS (SELECT 9)");
assert!(matches!(*query.body, SetExpr::Delete(_)));
}
other => panic!("Expected: DELETE, got: {:?}", other),
other => panic!("Expected: DELETE, got: {other:?}"),
}
match verified_stmt("WITH x AS (SELECT 42) INSERT INTO t SELECT foo FROM x") {
@ -7515,7 +7515,7 @@ fn parse_cte_in_data_modification_statements() {
assert_eq!(query.with.unwrap().to_string(), "WITH x AS (SELECT 42)");
assert!(matches!(*query.body, SetExpr::Insert(_)));
}
other => panic!("Expected: INSERT, got: {:?}", other),
other => panic!("Expected: INSERT, got: {other:?}"),
}
}
@ -7990,7 +7990,7 @@ fn parse_create_view_with_columns() {
.map(|name| ViewColumnDef {
name,
data_type: None,
options: None
options: None,
})
.collect::<Vec<_>>()
);
@ -9386,9 +9386,11 @@ fn parse_grant() {
verified_stmt("GRANT SELECT ON VIEW view1 TO ROLE role1");
verified_stmt("GRANT EXEC ON my_sp TO runner");
verified_stmt("GRANT UPDATE ON my_table TO updater_role AS dbo");
all_dialects_where(|d| d.identifier_quote_style("none") == Some('['))
.verified_stmt("GRANT SELECT ON [my_table] TO [public]");
verified_stmt("GRANT SELECT ON FUTURE SCHEMAS IN DATABASE db1 TO ROLE role1");
verified_stmt("GRANT SELECT ON FUTURE TABLES IN SCHEMA db1.sc1 TO ROLE role1");
verified_stmt("GRANT SELECT ON FUTURE VIEWS IN SCHEMA db1.sc1 TO ROLE role1");
}
#[test]
@ -10043,7 +10045,7 @@ fn parse_offset_and_limit() {
#[test]
fn parse_time_functions() {
fn test_time_function(func_name: &'static str) {
let sql = format!("SELECT {}()", func_name);
let sql = format!("SELECT {func_name}()");
let select = verified_only_select(&sql);
let select_localtime_func_call_ast = Function {
name: ObjectName::from(vec![Ident::new(func_name)]),
@ -10065,7 +10067,7 @@ fn parse_time_functions() {
);
// Validating Parenthesis
let sql_without_parens = format!("SELECT {}", func_name);
let sql_without_parens = format!("SELECT {func_name}");
let mut ast_without_parens = select_localtime_func_call_ast;
ast_without_parens.args = FunctionArguments::None;
assert_eq!(
@ -14306,7 +14308,7 @@ fn overflow() {
let expr = std::iter::repeat_n("1", 1000)
.collect::<Vec<_>>()
.join(" + ");
let sql = format!("SELECT {}", expr);
let sql = format!("SELECT {expr}");
let mut statements = Parser::parse_sql(&GenericDialect {}, sql.as_str()).unwrap();
let statement = statements.pop().unwrap();
@ -14606,7 +14608,7 @@ fn test_conditional_statement_span() {
else_block.unwrap().span()
);
}
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
}
@ -15380,6 +15382,36 @@ fn join_precedence() {
);
}
#[test]
fn parse_create_procedure_with_language() {
let sql = r#"CREATE PROCEDURE test_proc LANGUAGE sql AS BEGIN SELECT 1; END"#;
match verified_stmt(sql) {
Statement::CreateProcedure {
or_alter,
name,
params,
language,
..
} => {
assert_eq!(or_alter, false);
assert_eq!(name.to_string(), "test_proc");
assert_eq!(params, Some(vec![]));
assert_eq!(
language,
Some(Ident {
value: "sql".into(),
quote_style: None,
span: Span {
start: Location::empty(),
end: Location::empty()
}
})
);
}
_ => unreachable!(),
}
}
#[test]
fn parse_create_procedure_with_parameter_modes() {
let sql = r#"CREATE PROCEDURE test_proc (IN a INTEGER, OUT b TEXT, INOUT c TIMESTAMP, d BOOL) AS BEGIN SELECT 1; END"#;

View file

@ -213,7 +213,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
databricks().verified_stmt(&format!("USE {}", object_name)),
databricks().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -221,7 +221,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
databricks().verified_stmt(&format!("USE {0}{1}{0}", quote, object_name)),
databricks().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),
@ -233,21 +233,21 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with keyword and different type of quotes
assert_eq!(
databricks().verified_stmt(&format!("USE CATALOG {0}my_catalog{0}", quote)),
databricks().verified_stmt(&format!("USE CATALOG {quote}my_catalog{quote}")),
Statement::Use(Use::Catalog(ObjectName::from(vec![Ident::with_quote(
quote,
"my_catalog".to_string(),
)])))
);
assert_eq!(
databricks().verified_stmt(&format!("USE DATABASE {0}my_database{0}", quote)),
databricks().verified_stmt(&format!("USE DATABASE {quote}my_database{quote}")),
Statement::Use(Use::Database(ObjectName::from(vec![Ident::with_quote(
quote,
"my_database".to_string(),
)])))
);
assert_eq!(
databricks().verified_stmt(&format!("USE SCHEMA {0}my_schema{0}", quote)),
databricks().verified_stmt(&format!("USE SCHEMA {quote}my_schema{quote}")),
Statement::Use(Use::Schema(ObjectName::from(vec![Ident::with_quote(
quote,
"my_schema".to_string(),
@ -357,6 +357,6 @@ fn data_type_timestamp_ntz() {
}]
);
}
s => panic!("Unexpected statement: {:?}", s),
s => panic!("Unexpected statement: {s:?}"),
}
}

View file

@ -368,7 +368,7 @@ fn test_duckdb_specific_int_types() {
("HUGEINT", DataType::HugeInt),
];
for (dtype_string, data_type) in duckdb_dtypes {
let sql = format!("SELECT 123::{}", dtype_string);
let sql = format!("SELECT 123::{dtype_string}");
let select = duckdb().verified_only_select(&sql);
assert_eq!(
&Expr::Cast {
@ -792,7 +792,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
duckdb().verified_stmt(&format!("USE {}", object_name)),
duckdb().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -800,7 +800,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
duckdb().verified_stmt(&format!("USE {0}{1}{0}", quote, object_name)),
duckdb().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),
@ -812,7 +812,9 @@ fn parse_use() {
for &quote in &quote_styles {
// Test double identifier with different type of quotes
assert_eq!(
duckdb().verified_stmt(&format!("USE {0}CATALOG{0}.{0}my_schema{0}", quote)),
duckdb().verified_stmt(&format!(
"USE {quote}CATALOG{quote}.{quote}my_schema{quote}"
)),
Statement::Use(Use::Object(ObjectName::from(vec![
Ident::with_quote(quote, "CATALOG"),
Ident::with_quote(quote, "my_schema")

View file

@ -524,7 +524,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
hive().verified_stmt(&format!("USE {}", object_name)),
hive().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -532,7 +532,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
hive().verified_stmt(&format!("USE {}{}{}", quote, object_name, quote)),
hive().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),

View file

@ -173,7 +173,8 @@ fn parse_create_procedure() {
value: "test".into(),
quote_style: None,
span: Span::empty(),
}])
}]),
language: None,
}
)
}
@ -1672,7 +1673,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
ms().verified_stmt(&format!("USE {}", object_name)),
ms().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -1680,7 +1681,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
ms().verified_stmt(&format!("USE {}{}{}", quote, object_name, quote)),
ms().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),
@ -2186,7 +2187,7 @@ fn parse_mssql_if_else() {
"IF 1 = 1 BEGIN SET @A = 1; END ELSE SET @A = 2;"
);
}
_ => panic!("Unexpected statements: {:?}", stmts),
_ => panic!("Unexpected statements: {stmts:?}"),
}
}
@ -2236,7 +2237,7 @@ fn test_mssql_if_statements_span() {
Span::new(Location::new(1, 21), Location::new(1, 36))
);
}
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Blocks
@ -2257,7 +2258,7 @@ fn test_mssql_if_statements_span() {
Span::new(Location::new(1, 32), Location::new(1, 57))
);
}
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
}

View file

@ -593,7 +593,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
mysql_and_generic().verified_stmt(&format!("USE {}", object_name)),
mysql_and_generic().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -601,8 +601,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
mysql_and_generic()
.verified_stmt(&format!("USE {}{}{}", quote, object_name, quote)),
mysql_and_generic().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),
@ -2263,11 +2262,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
Some(SelectItem::UnnamedExpr(Expr::CompoundIdentifier(parts))) => {
assert_eq!(&[Ident::new("t"), Ident::new("15to29")], &parts[..]);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Case 2: Qualified column name that starts with digits and on its own represents a number.
@ -2277,11 +2276,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
Some(SelectItem::UnnamedExpr(Expr::CompoundIdentifier(parts))) => {
assert_eq!(&[Ident::new("t"), Ident::new("15e29")], &parts[..]);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Case 3: Unqualified, the same token is parsed as a number.
@ -2295,11 +2294,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
Some(SelectItem::UnnamedExpr(Expr::Value(ValueWithSpan { value, .. }))) => {
assert_eq!(&number("15e29"), value);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Case 4: Quoted simple identifier.
@ -2309,11 +2308,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
Some(SelectItem::UnnamedExpr(Expr::Identifier(name))) => {
assert_eq!(&Ident::with_quote('`', "15e29"), name);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Case 5: Quoted compound identifier.
@ -2326,11 +2325,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
&parts[..]
);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Case 6: Multi-level compound identifiers.
@ -2347,11 +2346,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
&parts[..]
);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
// Case 7: Multi-level compound quoted identifiers.
@ -2368,11 +2367,11 @@ fn parse_qualified_identifiers_with_numeric_prefix() {
&parts[..]
);
}
proj => panic!("Unexpected projection: {:?}", proj),
proj => panic!("Unexpected projection: {proj:?}"),
},
body => panic!("Unexpected statement body: {:?}", body),
body => panic!("Unexpected statement body: {body:?}"),
},
stmt => panic!("Unexpected statement: {:?}", stmt),
stmt => panic!("Unexpected statement: {stmt:?}"),
}
}

View file

@ -2535,12 +2535,12 @@ fn parse_create_indices_with_operator_classes() {
for expected_operator_class in &operator_classes {
let single_column_sql_statement = format!(
"CREATE INDEX the_index_name ON users USING {expected_index_type} (concat_users_name(first_name, last_name){})",
expected_operator_class.as_ref().map(|oc| format!(" {}", oc))
expected_operator_class.as_ref().map(|oc| format!(" {oc}"))
.unwrap_or_default()
);
let multi_column_sql_statement = format!(
"CREATE INDEX the_index_name ON users USING {expected_index_type} (column_name,concat_users_name(first_name, last_name){})",
expected_operator_class.as_ref().map(|oc| format!(" {}", oc))
expected_operator_class.as_ref().map(|oc| format!(" {oc}"))
.unwrap_or_default()
);
@ -3273,7 +3273,7 @@ fn test_fn_arg_with_value_operator() {
assert!(matches!(
&args[..],
&[FunctionArg::ExprNamed { operator: FunctionArgOperator::Value, .. }]
), "Invalid function argument: {:?}", args);
), "Invalid function argument: {args:?}");
}
other => panic!("Expected: JSON_OBJECT('name' VALUE 'value') to be parsed as a function, but got {other:?}"),
}
@ -5679,7 +5679,7 @@ fn parse_drop_trigger() {
"DROP TRIGGER{} check_update ON table_name{}",
if if_exists { " IF EXISTS" } else { "" },
option
.map(|o| format!(" {}", o))
.map(|o| format!(" {o}"))
.unwrap_or_else(|| "".to_string())
);
assert_eq!(
@ -5773,8 +5773,7 @@ fn parse_trigger_related_functions() {
// Now we parse the statements and check if they are parsed correctly.
let mut statements = pg()
.parse_sql_statements(&format!(
"{}{}{}{}",
sql_table_creation, sql_create_function, sql_create_trigger, sql_drop_trigger
"{sql_table_creation}{sql_create_function}{sql_create_trigger}{sql_drop_trigger}"
))
.unwrap();

View file

@ -2510,10 +2510,7 @@ fn test_snowflake_stage_object_names_into_location() {
.zip(allowed_object_names.iter_mut())
{
let (formatted_name, object_name) = it;
let sql = format!(
"COPY INTO {} FROM 'gcs://mybucket/./../a.csv'",
formatted_name
);
let sql = format!("COPY INTO {formatted_name} FROM 'gcs://mybucket/./../a.csv'");
match snowflake().verified_stmt(&sql) {
Statement::CopyIntoSnowflake { into, .. } => {
assert_eq!(into.0, object_name.0)
@ -2536,10 +2533,7 @@ fn test_snowflake_stage_object_names_into_table() {
.zip(allowed_object_names.iter_mut())
{
let (formatted_name, object_name) = it;
let sql = format!(
"COPY INTO {} FROM 'gcs://mybucket/./../a.csv'",
formatted_name
);
let sql = format!("COPY INTO {formatted_name} FROM 'gcs://mybucket/./../a.csv'");
match snowflake().verified_stmt(&sql) {
Statement::CopyIntoSnowflake { into, .. } => {
assert_eq!(into.0, object_name.0)
@ -3020,7 +3014,7 @@ fn parse_use() {
for object_name in &valid_object_names {
// Test single identifier without quotes
assert_eq!(
snowflake().verified_stmt(&format!("USE {}", object_name)),
snowflake().verified_stmt(&format!("USE {object_name}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::new(
object_name.to_string()
)])))
@ -3028,7 +3022,7 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single identifier with different type of quotes
assert_eq!(
snowflake().verified_stmt(&format!("USE {}{}{}", quote, object_name, quote)),
snowflake().verified_stmt(&format!("USE {quote}{object_name}{quote}")),
Statement::Use(Use::Object(ObjectName::from(vec![Ident::with_quote(
quote,
object_name.to_string(),
@ -3040,7 +3034,9 @@ fn parse_use() {
for &quote in &quote_styles {
// Test double identifier with different type of quotes
assert_eq!(
snowflake().verified_stmt(&format!("USE {0}CATALOG{0}.{0}my_schema{0}", quote)),
snowflake().verified_stmt(&format!(
"USE {quote}CATALOG{quote}.{quote}my_schema{quote}"
)),
Statement::Use(Use::Object(ObjectName::from(vec![
Ident::with_quote(quote, "CATALOG"),
Ident::with_quote(quote, "my_schema")
@ -3059,35 +3055,37 @@ fn parse_use() {
for &quote in &quote_styles {
// Test single and double identifier with keyword and different type of quotes
assert_eq!(
snowflake().verified_stmt(&format!("USE DATABASE {0}my_database{0}", quote)),
snowflake().verified_stmt(&format!("USE DATABASE {quote}my_database{quote}")),
Statement::Use(Use::Database(ObjectName::from(vec![Ident::with_quote(
quote,
"my_database".to_string(),
)])))
);
assert_eq!(
snowflake().verified_stmt(&format!("USE SCHEMA {0}my_schema{0}", quote)),
snowflake().verified_stmt(&format!("USE SCHEMA {quote}my_schema{quote}")),
Statement::Use(Use::Schema(ObjectName::from(vec![Ident::with_quote(
quote,
"my_schema".to_string(),
)])))
);
assert_eq!(
snowflake().verified_stmt(&format!("USE SCHEMA {0}CATALOG{0}.{0}my_schema{0}", quote)),
snowflake().verified_stmt(&format!(
"USE SCHEMA {quote}CATALOG{quote}.{quote}my_schema{quote}"
)),
Statement::Use(Use::Schema(ObjectName::from(vec![
Ident::with_quote(quote, "CATALOG"),
Ident::with_quote(quote, "my_schema")
])))
);
assert_eq!(
snowflake().verified_stmt(&format!("USE ROLE {0}my_role{0}", quote)),
snowflake().verified_stmt(&format!("USE ROLE {quote}my_role{quote}")),
Statement::Use(Use::Role(ObjectName::from(vec![Ident::with_quote(
quote,
"my_role".to_string(),
)])))
);
assert_eq!(
snowflake().verified_stmt(&format!("USE WAREHOUSE {0}my_wh{0}", quote)),
snowflake().verified_stmt(&format!("USE WAREHOUSE {quote}my_wh{quote}")),
Statement::Use(Use::Warehouse(ObjectName::from(vec![Ident::with_quote(
quote,
"my_wh".to_string(),
@ -3124,7 +3122,7 @@ fn view_comment_option_should_be_after_column_list() {
"CREATE OR REPLACE VIEW v (a COMMENT 'a comment', b, c COMMENT 'c comment') COMMENT = 'Comment' AS SELECT a FROM t",
"CREATE OR REPLACE VIEW v (a COMMENT 'a comment', b, c COMMENT 'c comment') WITH (foo = bar) COMMENT = 'Comment' AS SELECT a FROM t",
] {
snowflake_and_generic()
snowflake()
.verified_stmt(sql);
}
}
@ -3133,7 +3131,7 @@ fn view_comment_option_should_be_after_column_list() {
fn parse_view_column_descriptions() {
let sql = "CREATE OR REPLACE VIEW v (a COMMENT 'Comment', b) AS SELECT a, b FROM table1";
match snowflake_and_generic().verified_stmt(sql) {
match snowflake().verified_stmt(sql) {
Statement::CreateView { name, columns, .. } => {
assert_eq!(name.to_string(), "v");
assert_eq!(
@ -3142,7 +3140,9 @@ fn parse_view_column_descriptions() {
ViewColumnDef {
name: Ident::new("a"),
data_type: None,
options: Some(vec![ColumnOption::Comment("Comment".to_string())]),
options: Some(ColumnOptions::SpaceSeparated(vec![ColumnOption::Comment(
"Comment".to_string()
)])),
},
ViewColumnDef {
name: Ident::new("b"),
@ -3627,7 +3627,7 @@ fn test_alter_session_followed_by_statement() {
.unwrap();
match stmts[..] {
[Statement::AlterSession { .. }, Statement::Query { .. }] => {}
_ => panic!("Unexpected statements: {:?}", stmts),
_ => panic!("Unexpected statements: {stmts:?}"),
}
}
@ -4165,3 +4165,10 @@ fn test_snowflake_fetch_clause_syntax() {
canonical,
);
}
#[test]
fn test_snowflake_create_view_with_multiple_column_options() {
let create_view_with_tag =
r#"CREATE VIEW X (COL WITH TAG (pii='email') COMMENT 'foobar') AS SELECT * FROM Y"#;
snowflake().verified_stmt(create_view_with_tag);
}

View file

@ -324,7 +324,7 @@ fn parse_create_table_on_conflict_col() {
Keyword::IGNORE,
Keyword::REPLACE,
] {
let sql = format!("CREATE TABLE t1 (a INT, b INT ON CONFLICT {:?})", keyword);
let sql = format!("CREATE TABLE t1 (a INT, b INT ON CONFLICT {keyword:?})");
match sqlite_and_generic().verified_stmt(&sql) {
Statement::CreateTable(CreateTable { columns, .. }) => {
assert_eq!(
@ -410,7 +410,7 @@ fn parse_window_function_with_filter() {
"count",
"user_defined_function",
] {
let sql = format!("SELECT {}(x) FILTER (WHERE y) OVER () FROM t", func_name);
let sql = format!("SELECT {func_name}(x) FILTER (WHERE y) OVER () FROM t");
let select = sqlite().verified_only_select(&sql);
assert_eq!(select.to_string(), sql);
assert_eq!(
@ -444,7 +444,7 @@ fn parse_window_function_with_filter() {
fn parse_attach_database() {
let sql = "ATTACH DATABASE 'test.db' AS test";
let verified_stmt = sqlite().verified_stmt(sql);
assert_eq!(sql, format!("{}", verified_stmt));
assert_eq!(sql, format!("{verified_stmt}"));
match verified_stmt {
Statement::AttachDatabase {
schema_name,