1212import net .sf .jsqlparser .expression .Expression ;
1313import net .sf .jsqlparser .statement .Statement ;
1414import net .sf .jsqlparser .statement .StatementVisitor ;
15+ import net .sf .jsqlparser .statement .create .table .ColDataType ;
1516import net .sf .jsqlparser .statement .select .Select ;
1617
1718import java .io .Serializable ;
19+ import java .util .ArrayList ;
1820import java .util .List ;
1921import java .util .function .Consumer ;
2022
2123/**
22- * DuckDB's {@code CREATE [OR REPLACE] [TEMPORARY] MACRO name (parameters) AS [TABLE] body}, a named
23- * expression or query that is inlined at call sites.
24+ * DuckDB's
25+ * {@code CREATE [OR REPLACE] [TEMPORARY] {MACRO | FUNCTION} [IF NOT EXISTS] name (parameters) AS [TABLE] body [, (parameters) AS [TABLE] body ...]},
26+ * a named expression or query that is inlined at call sites, optionally overloaded on its
27+ * parameters.
28+ *
29+ * <p>
30+ * {@link #getParameters()}, {@link #getExpression()} and {@link #getSelect()} and their setters
31+ * address the first overload.
32+ *
33+ * <p>
34+ * As in DuckDB, a {@code ,} after a {@code TABLE} body that ends with a list (FROM, GROUP BY, ORDER
35+ * BY ...) continues that list rather than starting a new overload.
2436 *
2537 * @see <a href="https://duckdb.org/docs/stable/sql/statements/create_macro">CREATE MACRO</a>
2638 */
2739public class CreateMacro implements Statement {
2840 private boolean orReplace ;
2941 private boolean temporary ;
42+ private boolean functionKeyword ;
43+ private boolean ifNotExists ;
3044 private String name ;
31- private List <Parameter > parameters ;
32- private Expression expression ;
33- private Select select ;
45+ private List <Overload > overloads = new ArrayList <>();
3446
3547 public boolean isOrReplace () {
3648 return orReplace ;
@@ -58,6 +70,33 @@ public CreateMacro withTemporary(boolean temporary) {
5870 return this ;
5971 }
6072
73+ /** Whether it was written {@code CREATE FUNCTION}, DuckDB's alias of {@code CREATE MACRO}. */
74+ public boolean isFunctionKeyword () {
75+ return functionKeyword ;
76+ }
77+
78+ public void setFunctionKeyword (boolean functionKeyword ) {
79+ this .functionKeyword = functionKeyword ;
80+ }
81+
82+ public CreateMacro withFunctionKeyword (boolean functionKeyword ) {
83+ setFunctionKeyword (functionKeyword );
84+ return this ;
85+ }
86+
87+ public boolean isIfNotExists () {
88+ return ifNotExists ;
89+ }
90+
91+ public void setIfNotExists (boolean ifNotExists ) {
92+ this .ifNotExists = ifNotExists ;
93+ }
94+
95+ public CreateMacro withIfNotExists (boolean ifNotExists ) {
96+ setIfNotExists (ifNotExists );
97+ return this ;
98+ }
99+
61100 public String getName () {
62101 return name ;
63102 }
@@ -71,12 +110,33 @@ public CreateMacro withName(String name) {
71110 return this ;
72111 }
73112
113+ public List <Overload > getOverloads () {
114+ return overloads ;
115+ }
116+
117+ /** Copies {@code overloads}, so that the first-overload setters can add to an empty list. */
118+ public void setOverloads (List <Overload > overloads ) {
119+ this .overloads = overloads == null ? new ArrayList <>() : new ArrayList <>(overloads );
120+ }
121+
122+ public CreateMacro withOverloads (List <Overload > overloads ) {
123+ setOverloads (overloads );
124+ return this ;
125+ }
126+
127+ private Overload firstOverload () {
128+ if (overloads .isEmpty ()) {
129+ overloads .add (new Overload ());
130+ }
131+ return overloads .get (0 );
132+ }
133+
74134 public List <Parameter > getParameters () {
75- return parameters ;
135+ return overloads . isEmpty () ? null : overloads . get ( 0 ). getParameters () ;
76136 }
77137
78138 public void setParameters (List <Parameter > parameters ) {
79- this . parameters = parameters ;
139+ firstOverload (). setParameters ( parameters ) ;
80140 }
81141
82142 public CreateMacro withParameters (List <Parameter > parameters ) {
@@ -85,11 +145,11 @@ public CreateMacro withParameters(List<Parameter> parameters) {
85145 }
86146
87147 public Expression getExpression () {
88- return expression ;
148+ return overloads . isEmpty () ? null : overloads . get ( 0 ). getExpression () ;
89149 }
90150
91151 public void setExpression (Expression expression ) {
92- this . expression = expression ;
152+ firstOverload (). setExpression ( expression ) ;
93153 }
94154
95155 public CreateMacro withExpression (Expression expression ) {
@@ -98,11 +158,11 @@ public CreateMacro withExpression(Expression expression) {
98158 }
99159
100160 public Select getSelect () {
101- return select ;
161+ return overloads . isEmpty () ? null : overloads . get ( 0 ). getSelect () ;
102162 }
103163
104164 public void setSelect (Select select ) {
105- this . select = select ;
165+ firstOverload (). setSelect ( select ) ;
106166 }
107167
108168 public CreateMacro withSelect (Select select ) {
@@ -112,7 +172,7 @@ public CreateMacro withSelect(Select select) {
112172
113173 /** A table macro returns a relation, a scalar macro an expression. */
114174 public boolean isTable () {
115- return select != null ;
175+ return getSelect () != null ;
116176 }
117177
118178 public StringBuilder appendTo (StringBuilder builder ) {
@@ -129,19 +189,16 @@ public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expres
129189 if (temporary ) {
130190 builder .append ("TEMPORARY " );
131191 }
132- builder .append ("MACRO " ).append (name ).append (" (" );
133- for (int i = 0 ; i < parameters .size (); i ++) {
192+ builder .append (functionKeyword ? "FUNCTION " : "MACRO " );
193+ if (ifNotExists ) {
194+ builder .append ("IF NOT EXISTS " );
195+ }
196+ builder .append (name ).append (" " );
197+ for (int i = 0 ; i < overloads .size (); i ++) {
134198 if (i > 0 ) {
135199 builder .append (", " );
136200 }
137- parameters .get (i ).appendTo (builder , expressionRenderer );
138- }
139- builder .append (") AS " );
140- if (select != null ) {
141- builder .append ("TABLE " );
142- selectRenderer .accept (select );
143- } else {
144- expressionRenderer .accept (expression );
201+ overloads .get (i ).appendTo (builder , expressionRenderer , selectRenderer );
145202 }
146203 return builder ;
147204 }
@@ -156,24 +213,113 @@ public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
156213 return statementVisitor .visit (this , context );
157214 }
158215
159- /** A macro parameter, optionally carrying a default value ({@code b := 5}). */
216+ /** One {@code (parameters) AS [TABLE] body} of a macro. */
217+ public static class Overload implements Serializable {
218+ private List <Parameter > parameters = new ArrayList <>();
219+ private Expression expression ;
220+ private Select select ;
221+
222+ public List <Parameter > getParameters () {
223+ return parameters ;
224+ }
225+
226+ public void setParameters (List <Parameter > parameters ) {
227+ this .parameters = parameters ;
228+ }
229+
230+ public Overload withParameters (List <Parameter > parameters ) {
231+ setParameters (parameters );
232+ return this ;
233+ }
234+
235+ public Expression getExpression () {
236+ return expression ;
237+ }
238+
239+ public void setExpression (Expression expression ) {
240+ this .expression = expression ;
241+ }
242+
243+ public Overload withExpression (Expression expression ) {
244+ setExpression (expression );
245+ return this ;
246+ }
247+
248+ public Select getSelect () {
249+ return select ;
250+ }
251+
252+ public void setSelect (Select select ) {
253+ this .select = select ;
254+ }
255+
256+ public Overload withSelect (Select select ) {
257+ setSelect (select );
258+ return this ;
259+ }
260+
261+ /** A table overload returns a relation, a scalar one an expression. */
262+ public boolean isTable () {
263+ return select != null ;
264+ }
265+
266+ @ Override
267+ public String toString () {
268+ StringBuilder builder = new StringBuilder ();
269+ return appendTo (builder , builder ::append , builder ::append ).toString ();
270+ }
271+
272+ private StringBuilder appendTo (StringBuilder builder ,
273+ Consumer <Expression > expressionRenderer , Consumer <Select > selectRenderer ) {
274+ builder .append ("(" );
275+ for (int i = 0 ; i < parameters .size (); i ++) {
276+ if (i > 0 ) {
277+ builder .append (", " );
278+ }
279+ parameters .get (i ).appendTo (builder , expressionRenderer );
280+ }
281+ builder .append (") AS " );
282+ if (select != null ) {
283+ builder .append ("TABLE " );
284+ selectRenderer .accept (select );
285+ } else {
286+ expressionRenderer .accept (expression );
287+ }
288+ return builder ;
289+ }
290+ }
291+
292+ /**
293+ * A macro parameter, optionally typed ({@code a INTEGER}) and carrying a default value
294+ * ({@code b := 5}).
295+ */
160296 public static class Parameter implements Serializable {
161297 private final String name ;
298+ private final ColDataType type ;
162299 private final Expression defaultValue ;
163300
164301 public Parameter (String name ) {
165- this (name , null );
302+ this (name , null , null );
166303 }
167304
168305 public Parameter (String name , Expression defaultValue ) {
306+ this (name , null , defaultValue );
307+ }
308+
309+ public Parameter (String name , ColDataType type , Expression defaultValue ) {
169310 this .name = name ;
311+ this .type = type ;
170312 this .defaultValue = defaultValue ;
171313 }
172314
173315 public String getName () {
174316 return name ;
175317 }
176318
319+ public ColDataType getType () {
320+ return type ;
321+ }
322+
177323 public Expression getDefaultValue () {
178324 return defaultValue ;
179325 }
@@ -187,6 +333,9 @@ public String toString() {
187333 private StringBuilder appendTo (StringBuilder builder ,
188334 Consumer <Expression > expressionRenderer ) {
189335 builder .append (name );
336+ if (type != null ) {
337+ builder .append (" " ).append (type );
338+ }
190339 if (defaultValue != null ) {
191340 builder .append (" := " );
192341 expressionRenderer .accept (defaultValue );
0 commit comments