Skip to content

Commit

Permalink
fix: Use separate types for FormatClause and InputFormatClause
Browse files Browse the repository at this point in the history
  • Loading branch information
bombsimon committed Jan 10, 2025
1 parent e75aafe commit 93ca1fb
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 48 deletions.
6 changes: 3 additions & 3 deletions src/ast/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ use sqlparser_derive::{Visit, VisitMut};
pub use super::ddl::{ColumnDef, TableConstraint};

use super::{
display_comma_separated, display_separated, Assignment, ClusteredBy, CommentDef, Expr,
FileFormat, FormatClause, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
display_comma_separated, display_separated, query::InputFormatClause, Assignment, ClusteredBy,
CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
SqliteOnConflict, TableEngine, TableWithJoins, Tag, WrappedCollection,
Expand Down Expand Up @@ -510,7 +510,7 @@ pub struct Insert {
/// ClickHouse syntax: `INSERT INTO tbl FORMAT JSONEachRow {"foo": 1, "bar": 2}, {"foo": 3}`
///
/// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
pub format_clause: Option<FormatClause>,
pub format_clause: Option<InputFormatClause>,
}

impl Display for Insert {
Expand Down
8 changes: 4 additions & 4 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ pub use self::operator::{BinaryOperator, UnaryOperator};
pub use self::query::{
AfterMatchSkip, ConnectBy, Cte, CteAsMaterialized, Distinct, EmptyMatchesMode,
ExceptSelectItem, ExcludeSelectItem, ExprWithAlias, Fetch, ForClause, ForJson, ForXml,
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem, Interpolate,
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonTableColumn,
JsonTableColumnErrorHandling, JsonTableNamedColumn, JsonTableNestedColumn, LateralView,
LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol, Measure,
FormatClause, GroupByExpr, GroupByWithModifier, IdentWithAlias, IlikeSelectItem,
InputFormatClause, Interpolate, InterpolateExpr, Join, JoinConstraint, JoinOperator,
JsonTableColumn, JsonTableColumnErrorHandling, JsonTableNamedColumn, JsonTableNestedColumn,
LateralView, LockClause, LockType, MatchRecognizePattern, MatchRecognizeSymbol, Measure,
NamedWindowDefinition, NamedWindowExpr, NonBlock, Offset, OffsetRows, OpenJsonTableColumn,
OrderBy, OrderByExpr, PivotValueSource, ProjectionSelect, Query, RenameSelectItem,
RepetitionQuantifier, ReplaceSelectElement, ReplaceSelectItem, RowsPerMatch, Select,
Expand Down
36 changes: 24 additions & 12 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2465,27 +2465,39 @@ impl fmt::Display for GroupByExpr {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum FormatClause {
Identifier {
ident: Ident,
expr: Option<Vec<Expr>>,
},
Identifier(Ident),
Null,
}

impl fmt::Display for FormatClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FormatClause::Identifier { ident, expr } => {
write!(f, "FORMAT {}", ident)?;
FormatClause::Identifier(ident) => write!(f, "FORMAT {}", ident),
FormatClause::Null => write!(f, "FORMAT NULL"),
}
}
}

if let Some(exprs) = expr {
write!(f, " {}", display_comma_separated(exprs))?;
}
/// FORMAT identifier in input context, specific to ClickHouse.
///
/// [ClickHouse]: <https://clickhouse.com/docs/en/interfaces/formats>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct InputFormatClause {
pub ident: Ident,
pub values: Vec<Expr>,
}

Ok(())
}
FormatClause::Null => write!(f, "FORMAT NULL"),
impl fmt::Display for InputFormatClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FORMAT {}", self.ident)?;

if !self.values.is_empty() {
write!(f, " {}", display_comma_separated(self.values.as_slice()))?;
}

Ok(())
}
}

Expand Down
40 changes: 15 additions & 25 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9589,7 +9589,12 @@ impl<'a> Parser<'a> {
let format_clause = if dialect_of!(self is ClickHouseDialect | GenericDialect)
&& self.parse_keyword(Keyword::FORMAT)
{
Some(self.parse_format_clause(false)?)
if self.parse_keyword(Keyword::NULL) {
Some(FormatClause::Null)
} else {
let ident = self.parse_identifier()?;
Some(FormatClause::Identifier(ident))
}
} else {
None
};
Expand Down Expand Up @@ -11934,7 +11939,7 @@ impl<'a> Parser<'a> {
let settings = self.parse_settings()?;

let format = if self.parse_keyword(Keyword::FORMAT) {
Some(self.parse_format_clause(true)?)
Some(self.parse_input_format_clause()?)
} else {
None
};
Expand Down Expand Up @@ -12035,32 +12040,17 @@ impl<'a> Parser<'a> {
}

// Parses format clause used for [ClickHouse]. Formats are different when using `SELECT` and
// `INSERT` and also when using the CLI for pipes. It may or may not take an additional
// expression after the format so we try to parse the expression but allow failure.
//
// Since we know we never take an additional expression in `SELECT` context we never only try
// to parse if `can_have_expression` is true.
// `INSERT` and also when using the CLI for pipes. For `INSERT` it can take an optional values
// list which we try to parse here.
//
// <https://clickhouse.com/docs/en/interfaces/formats>
pub fn parse_format_clause(
&mut self,
can_have_expression: bool,
) -> Result<FormatClause, ParserError> {
if self.parse_keyword(Keyword::NULL) {
Ok(FormatClause::Null)
} else {
let ident = self.parse_identifier()?;
let expr = if can_have_expression {
match self.try_parse(|p| p.parse_comma_separated(|p| p.parse_expr())) {
Ok(expr) => Some(expr),
_ => None,
}
} else {
None
};
pub fn parse_input_format_clause(&mut self) -> Result<InputFormatClause, ParserError> {
let ident = self.parse_identifier()?;
let values = self
.try_parse(|p| p.parse_comma_separated(|p| p.parse_expr()))
.unwrap_or_default();

Ok(FormatClause::Identifier { ident, expr })
}
Ok(InputFormatClause { ident, values })
}

/// Returns true if the immediate tokens look like the
Expand Down
5 changes: 1 addition & 4 deletions tests/sqlparser_clickhouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1378,10 +1378,7 @@ fn test_query_with_format_clause() {
} else {
assert_eq!(
query.format_clause,
Some(FormatClause::Identifier {
ident: Ident::new(*format),
expr: None
})
Some(FormatClause::Identifier(Ident::new(*format))),
);
}
}
Expand Down

0 comments on commit 93ca1fb

Please sign in to comment.