Skip to content

Commit 618ad71

Browse files
committed
fixup! Add support for DuckDB's CREATE MACRO statements
1 parent 6749806 commit 618ad71

File tree

3 files changed

+77
-87
lines changed

3 files changed

+77
-87
lines changed

src/ast/mod.rs

+28-40
Original file line numberDiff line numberDiff line change
@@ -1589,21 +1589,8 @@ pub enum Statement {
15891589
or_replace: bool,
15901590
temporary: bool,
15911591
name: ObjectName,
1592-
args: Option<Vec<OperateMacroArg>>,
1593-
expr: Expr,
1594-
},
1595-
/// ```sql
1596-
/// CREATE MACRO
1597-
/// ```
1598-
///
1599-
/// Supported variants:
1600-
/// 1. [DuckDB](https://duckdb.org/docs/sql/statements/create_macro)
1601-
CreateTableMacro {
1602-
or_replace: bool,
1603-
temporary: bool,
1604-
name: ObjectName,
1605-
args: Option<Vec<OperateMacroArg>>,
1606-
query: Query,
1592+
args: Option<Vec<MacroArg>>,
1593+
definition: MacroDefinition,
16071594
},
16081595
/// ```sql
16091596
/// CREATE STAGE
@@ -2129,7 +2116,7 @@ impl fmt::Display for Statement {
21292116
temporary,
21302117
name,
21312118
args,
2132-
expr,
2119+
definition,
21332120
} => {
21342121
write!(
21352122
f,
@@ -2140,26 +2127,10 @@ impl fmt::Display for Statement {
21402127
if let Some(args) = args {
21412128
write!(f, "({})", display_comma_separated(args))?;
21422129
}
2143-
write!(f, " AS {expr}")?;
2144-
Ok(())
2145-
}
2146-
Statement::CreateTableMacro {
2147-
or_replace,
2148-
temporary,
2149-
name,
2150-
args,
2151-
query,
2152-
} => {
2153-
write!(
2154-
f,
2155-
"CREATE {or_replace}{temp}MACRO {name} ",
2156-
temp = if *temporary { "TEMPORARY " } else { "" },
2157-
or_replace = if *or_replace { "OR REPLACE " } else { "" },
2158-
)?;
2159-
if let Some(args) = args {
2160-
write!(f, "({})", display_comma_separated(args))?;
2130+
match definition {
2131+
MacroDefinition::Expr(expr) => write!(f, " AS {expr}")?,
2132+
MacroDefinition::Table(query) => write!(f, " AS TABLE {query}")?,
21612133
}
2162-
write!(f, " AS TABLE {query}")?;
21632134
Ok(())
21642135
}
21652136
Statement::CreateView {
@@ -4368,19 +4339,19 @@ impl fmt::Display for CreateFunctionUsing {
43684339
}
43694340
}
43704341

4371-
/// DuckDB specific feature.
4342+
/// `NAME = <EXPR>` arguments for DuckDB macros
43724343
///
43734344
/// See [Create Macro - DuckDB](https://duckdb.org/docs/sql/statements/create_macro)
43744345
/// for more details
43754346
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
43764347
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43774348
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4378-
pub struct OperateMacroArg {
4349+
pub struct MacroArg {
43794350
pub name: Ident,
43804351
pub default_expr: Option<Expr>,
43814352
}
43824353

4383-
impl OperateMacroArg {
4354+
impl MacroArg {
43844355
/// Returns an argument with name.
43854356
pub fn new(name: &str) -> Self {
43864357
Self {
@@ -4390,8 +4361,7 @@ impl OperateMacroArg {
43904361
}
43914362
}
43924363

4393-
4394-
impl fmt::Display for OperateMacroArg {
4364+
impl fmt::Display for MacroArg {
43954365
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43964366
write!(f, "{}", self.name)?;
43974367
if let Some(default_expr) = &self.default_expr {
@@ -4401,6 +4371,24 @@ impl fmt::Display for OperateMacroArg {
44014371
}
44024372
}
44034373

4374+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4375+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4376+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4377+
pub enum MacroDefinition {
4378+
Expr(Expr),
4379+
Table(Query),
4380+
}
4381+
4382+
impl fmt::Display for MacroDefinition {
4383+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4384+
match self {
4385+
MacroDefinition::Expr(expr) => write!(f, "{expr}")?,
4386+
MacroDefinition::Table(query) => write!(f, "{query}")?,
4387+
}
4388+
Ok(())
4389+
}
4390+
}
4391+
44044392
/// Schema possible naming variants ([1]).
44054393
///
44064394
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#schema-definition

src/parser.rs

+22-31
Original file line numberDiff line numberDiff line change
@@ -2626,7 +2626,7 @@ impl<'a> Parser<'a> {
26262626
return_type,
26272627
params,
26282628
})
2629-
} else if dialect_of!(self is DuckDbDialect) {
2629+
} else if dialect_of!(self is DuckDbDialect | GenericDialect) {
26302630
self.parse_create_macro(or_replace, temporary)
26312631
} else {
26322632
self.prev_token();
@@ -2708,55 +2708,46 @@ impl<'a> Parser<'a> {
27082708
or_replace: bool,
27092709
temporary: bool,
27102710
) -> Result<Statement, ParserError> {
2711-
if dialect_of!(self is DuckDbDialect) {
2711+
if dialect_of!(self is DuckDbDialect | GenericDialect) {
27122712
let name = self.parse_object_name()?;
27132713
self.expect_token(&Token::LParen)?;
27142714
let args = if self.consume_token(&Token::RParen) {
27152715
self.prev_token();
27162716
None
27172717
} else {
2718-
Some(self.parse_comma_separated(Parser::parse_macro_arg)?)
2718+
Some(self.parse_comma_separated(Parser::parse_macro_arg)?)
27192719
};
27202720

27212721
self.expect_token(&Token::RParen)?;
27222722
self.expect_keyword(Keyword::AS)?;
27232723

2724-
if self.parse_keyword(Keyword::TABLE) {
2725-
Ok(Statement::CreateTableMacro {
2726-
or_replace,
2727-
temporary,
2728-
name,
2729-
args,
2730-
query: self.parse_query()?,
2731-
})
2732-
} else {
2733-
Ok(Statement::CreateMacro {
2734-
or_replace,
2735-
temporary,
2736-
name,
2737-
args,
2738-
expr: self.parse_expr()?,
2739-
})
2740-
}
2724+
Ok(Statement::CreateMacro {
2725+
or_replace,
2726+
temporary,
2727+
name,
2728+
args,
2729+
definition: if self.parse_keyword(Keyword::TABLE) {
2730+
MacroDefinition::Table(self.parse_query()?)
2731+
} else {
2732+
MacroDefinition::Expr(self.parse_expr()?)
2733+
},
2734+
})
27412735
} else {
27422736
self.prev_token();
27432737
self.expected("an object type after CREATE", self.peek_token())
27442738
}
27452739
}
27462740

2747-
fn parse_macro_arg(&mut self) -> Result<OperateMacroArg, ParserError> {
2741+
fn parse_macro_arg(&mut self) -> Result<MacroArg, ParserError> {
27482742
let name = self.parse_identifier()?;
27492743

2750-
let default_expr = if self.consume_token(&Token::DuckAssignment) || self.consume_token(&Token::RArrow)
2751-
{
2752-
Some(self.parse_expr()?)
2753-
} else {
2754-
None
2755-
};
2756-
Ok(OperateMacroArg {
2757-
name,
2758-
default_expr,
2759-
})
2744+
let default_expr =
2745+
if self.consume_token(&Token::DuckAssignment) || self.consume_token(&Token::RArrow) {
2746+
Some(self.parse_expr()?)
2747+
} else {
2748+
None
2749+
};
2750+
Ok(MacroArg { name, default_expr })
27602751
}
27612752

27622753
pub fn parse_create_external_table(

tests/sqlparser_duckdb.rs

+27-16
Original file line numberDiff line numberDiff line change
@@ -71,50 +71,61 @@ fn parse_div_infix() {
7171

7272
#[test]
7373
fn test_create_macro() {
74-
let _macro = duckdb().verified_stmt("CREATE MACRO schema.add(a, b) AS a + b");
74+
let macro_ = duckdb().verified_stmt("CREATE MACRO schema.add(a, b) AS a + b");
7575
let expected = Statement::CreateMacro {
7676
or_replace: false,
7777
temporary: false,
7878
name: ObjectName(vec![Ident::new("schema"), Ident::new("add")]),
79-
args: Some(vec![OperateMacroArg::new("a"), OperateMacroArg::new("b")]),
80-
expr: Expr::BinaryOp {
79+
args: Some(vec![MacroArg::new("a"), MacroArg::new("b")]),
80+
definition: MacroDefinition::Expr(Expr::BinaryOp {
8181
left: Box::new(Expr::Identifier(Ident::new("a"))),
8282
op: BinaryOperator::Plus,
8383
right: Box::new(Expr::Identifier(Ident::new("b"))),
84-
},
84+
}),
8585
};
86-
assert_eq!(expected, _macro);
86+
assert_eq!(expected, macro_);
8787
}
8888

8989
#[test]
9090
fn test_create_macro_default_args() {
91-
let _macro = duckdb().verified_stmt("CREATE MACRO add_default(a, b := 5) AS a + b");
91+
let macro_ = duckdb().verified_stmt("CREATE MACRO add_default(a, b := 5) AS a + b");
9292
let expected = Statement::CreateMacro {
9393
or_replace: false,
9494
temporary: false,
9595
name: ObjectName(vec![Ident::new("add_default")]),
96-
args: Some(vec![OperateMacroArg::new("a"), OperateMacroArg{ name: Ident::new("b"), default_expr: Some(Expr::Value(Value::Number("5".into(), false)))}]),
97-
expr: Expr::BinaryOp {
96+
args: Some(vec![
97+
MacroArg::new("a"),
98+
MacroArg {
99+
name: Ident::new("b"),
100+
default_expr: Some(Expr::Value(Value::Number("5".into(), false))),
101+
},
102+
]),
103+
definition: MacroDefinition::Expr(Expr::BinaryOp {
98104
left: Box::new(Expr::Identifier(Ident::new("a"))),
99105
op: BinaryOperator::Plus,
100106
right: Box::new(Expr::Identifier(Ident::new("b"))),
101-
},
107+
}),
102108
};
103-
assert_eq!(expected, _macro);
109+
assert_eq!(expected, macro_);
104110
}
105111

106112
#[test]
107113
fn test_create_table_macro() {
108114
let query = "SELECT col1_value AS column1, col2_value AS column2 UNION ALL SELECT 'Hello' AS col1_value, 456 AS col2_value";
109-
let _macro = duckdb().verified_stmt(
110-
&("CREATE OR REPLACE TEMPORARY MACRO dynamic_table (col1_value, col2_value) AS TABLE ".to_string() + query),
115+
let macro_ = duckdb().verified_stmt(
116+
&("CREATE OR REPLACE TEMPORARY MACRO dynamic_table(col1_value, col2_value) AS TABLE "
117+
.to_string()
118+
+ query),
111119
);
112-
let expected = Statement::CreateTableMacro {
120+
let expected = Statement::CreateMacro {
113121
or_replace: true,
114122
temporary: true,
115123
name: ObjectName(vec![Ident::new("dynamic_table")]),
116-
args: Some(vec![OperateMacroArg::new("col1_value"), OperateMacroArg::new("col2_value")]),
117-
query: duckdb().verified_query(query),
124+
args: Some(vec![
125+
MacroArg::new("col1_value"),
126+
MacroArg::new("col2_value"),
127+
]),
128+
definition: MacroDefinition::Table(duckdb().verified_query(query)),
118129
};
119-
assert_eq!(expected, _macro);
130+
assert_eq!(expected, macro_);
120131
}

0 commit comments

Comments
 (0)