Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions datafusion/sql/src/unparser/dialect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub enum IntervalStyle {
pub enum DateFieldExtractStyle {
DatePart,
Extract,
Strftime,
}

pub struct DefaultDialect {}
Expand Down Expand Up @@ -213,6 +214,10 @@ impl Dialect for SqliteDialect {
Some('`')
}

fn date_field_extract_style(&self) -> DateFieldExtractStyle {
DateFieldExtractStyle::Strftime
}

fn date32_cast_dtype(&self) -> sqlparser::ast::DataType {
sqlparser::ast::DataType::Text
}
Expand Down
97 changes: 76 additions & 21 deletions datafusion/sql/src/unparser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,30 +524,78 @@ impl Unparser<'_> {
_func: &Arc<ScalarUDF>,
args: &[Expr],
) -> Option<ast::Expr> {
if func_name.to_lowercase() == "date_part"
&& self.dialect.date_field_extract_style() == DateFieldExtractStyle::Extract
&& args.len() == 2
{
let date_expr = self.expr_to_sql(&args[1]).ok()?;

if let Expr::Literal(ScalarValue::Utf8(Some(field))) = &args[0] {
let field = match field.to_lowercase().as_str() {
"year" => ast::DateTimeField::Year,
"month" => ast::DateTimeField::Month,
"day" => ast::DateTimeField::Day,
"hour" => ast::DateTimeField::Hour,
"minute" => ast::DateTimeField::Minute,
"second" => ast::DateTimeField::Second,
_ => return None,
};
if func_name.to_lowercase() == "date_part" {
match (self.dialect.date_field_extract_style(), args.len()) {
(DateFieldExtractStyle::Extract, 2) => {
let date_expr = self.expr_to_sql(&args[1]).ok()?;

if let Expr::Literal(ScalarValue::Utf8(Some(field))) = &args[0] {
let field = match field.to_lowercase().as_str() {
"year" => ast::DateTimeField::Year,
"month" => ast::DateTimeField::Month,
"day" => ast::DateTimeField::Day,
"hour" => ast::DateTimeField::Hour,
"minute" => ast::DateTimeField::Minute,
"second" => ast::DateTimeField::Second,
_ => return None,
};

return Some(ast::Expr::Extract {
field,
expr: Box::new(date_expr),
syntax: ast::ExtractSyntax::From,
});
}
}
(DateFieldExtractStyle::Strftime, 2) => {
let column = self.expr_to_sql(&args[1]).ok()?;

if let Expr::Literal(ScalarValue::Utf8(Some(field))) = &args[0] {
let field = match field.to_lowercase().as_str() {
"year" => "%Y",
"month" => "%m",
"day" => "%d",
"hour" => "%H",
"minute" => "%M",
"second" => "%S",
_ => return None,
};

return Some(ast::Expr::Extract {
field,
expr: Box::new(date_expr),
syntax: ast::ExtractSyntax::From,
});
return Some(ast::Expr::Function(ast::Function {
name: ast::ObjectName(vec![ast::Ident {
value: "strftime".to_string(),
quote_style: None,
}]),
args: ast::FunctionArguments::List(
ast::FunctionArgumentList {
duplicate_treatment: None,
args: vec![
ast::FunctionArg::Unnamed(
ast::FunctionArgExpr::Expr(ast::Expr::Value(
ast::Value::SingleQuotedString(
field.to_string(),
),
)),
),
ast::FunctionArg::Unnamed(
ast::FunctionArgExpr::Expr(column),
),
],
clauses: vec![],
},
),
filter: None,
null_treatment: None,
over: None,
within_group: vec![],
parameters: ast::FunctionArguments::None,
}));
}
}
_ => {} // no overrides for DateFieldExtractStyle::DatePart, because it's already a date_part
}
}

None
}

Expand Down Expand Up @@ -2178,6 +2226,7 @@ mod tests {
"YEAR",
"EXTRACT(YEAR FROM x)",
),
(DateFieldExtractStyle::Strftime, "YEAR", "strftime('%Y', x)"),
(
DateFieldExtractStyle::DatePart,
"MONTH",
Expand All @@ -2188,11 +2237,17 @@ mod tests {
"MONTH",
"EXTRACT(MONTH FROM x)",
),
(
DateFieldExtractStyle::Strftime,
"MONTH",
"strftime('%m', x)",
),
(
DateFieldExtractStyle::DatePart,
"DAY",
"date_part('DAY', x)",
),
(DateFieldExtractStyle::Strftime, "DAY", "strftime('%d', x)"),
(DateFieldExtractStyle::Extract, "DAY", "EXTRACT(DAY FROM x)"),
] {
let dialect = CustomDialectBuilder::new()
Expand Down