Skip to content

Commit 0c0180c

Browse files
hayssamsclaude
andcommitted
Support DuckDB CREATE MACRO
Adds CREATE [OR REPLACE] [TEMPORARY] MACRO name (parameters) AS body for both scalar macros and table macros (AS TABLE SELECT ...). Parameters may carry a default value, as in add(a, b := 5). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
1 parent 8950776 commit 0c0180c

9 files changed

Lines changed: 317 additions & 0 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
import java.util.Locale;
9494
import java.util.Set;
9595
import java.util.function.Predicate;
96+
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
9697

9798
/**
9899
* Derives a {@link StatementFeatures} verdict from a statement tree.
@@ -584,6 +585,13 @@ public <S> Void visit(CopyStatement copyStatement, S context) {
584585
return null;
585586
}
586587

588+
@Override
589+
public <S> Void visit(CreateMacro createMacro, S context) {
590+
analysis.claimTopLevel();
591+
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
592+
return null;
593+
}
594+
587595
@Override
588596
public <S> Void visit(PurgeStatement purgeStatement, S context) {
589597
analysis.claimTopLevel();

‎src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import net.sf.jsqlparser.statement.update.ParenthesedUpdate;
6969
import net.sf.jsqlparser.statement.update.Update;
7070
import net.sf.jsqlparser.statement.upsert.Upsert;
71+
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
7172

7273
public interface StatementVisitor<T> {
7374

@@ -425,6 +426,12 @@ default void visit(CopyStatement copyStatement) {
425426
this.visit(copyStatement, null);
426427
}
427428

429+
<S> T visit(CreateMacro createMacro, S context);
430+
431+
default void visit(CreateMacro createMacro) {
432+
this.visit(createMacro, null);
433+
}
434+
428435
<S> T visit(PurgeStatement purgeStatement, S context);
429436

430437
default void visit(PurgeStatement purgeStatement) {

‎src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989

9090
import java.util.List;
9191
import net.sf.jsqlparser.util.TableDefinitionTraversal;
92+
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
9293

9394
@SuppressWarnings({"PMD.UncommentedEmptyMethodBody"})
9495
public class StatementVisitorAdapter<T> implements StatementVisitor<T> {
@@ -663,6 +664,11 @@ public <S> T visit(CopyStatement copyStatement, S context) {
663664
return null;
664665
}
665666

667+
@Override
668+
public <S> T visit(CreateMacro createMacro, S context) {
669+
return null;
670+
}
671+
666672
@Override
667673
public <S> T visit(PurgeStatement purgeStatement, S context) {
668674
return null;
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.create.macro;
11+
12+
import net.sf.jsqlparser.expression.Expression;
13+
import net.sf.jsqlparser.statement.Statement;
14+
import net.sf.jsqlparser.statement.StatementVisitor;
15+
import net.sf.jsqlparser.statement.select.Select;
16+
17+
import java.io.Serializable;
18+
import java.util.List;
19+
20+
/**
21+
* DuckDB's {@code CREATE [OR REPLACE] [TEMPORARY] MACRO name (parameters) AS [TABLE] body}, a named
22+
* expression or query that is inlined at call sites.
23+
*
24+
* @see <a href="https://duckdb.org/docs/stable/sql/statements/create_macro">CREATE MACRO</a>
25+
*/
26+
public class CreateMacro implements Statement {
27+
private boolean orReplace;
28+
private boolean temporary;
29+
private String name;
30+
private List<Parameter> parameters;
31+
private Expression expression;
32+
private Select select;
33+
34+
public boolean isOrReplace() {
35+
return orReplace;
36+
}
37+
38+
public void setOrReplace(boolean orReplace) {
39+
this.orReplace = orReplace;
40+
}
41+
42+
public CreateMacro withOrReplace(boolean orReplace) {
43+
setOrReplace(orReplace);
44+
return this;
45+
}
46+
47+
public boolean isTemporary() {
48+
return temporary;
49+
}
50+
51+
public void setTemporary(boolean temporary) {
52+
this.temporary = temporary;
53+
}
54+
55+
public CreateMacro withTemporary(boolean temporary) {
56+
setTemporary(temporary);
57+
return this;
58+
}
59+
60+
public String getName() {
61+
return name;
62+
}
63+
64+
public void setName(String name) {
65+
this.name = name;
66+
}
67+
68+
public CreateMacro withName(String name) {
69+
setName(name);
70+
return this;
71+
}
72+
73+
public List<Parameter> getParameters() {
74+
return parameters;
75+
}
76+
77+
public void setParameters(List<Parameter> parameters) {
78+
this.parameters = parameters;
79+
}
80+
81+
public CreateMacro withParameters(List<Parameter> parameters) {
82+
setParameters(parameters);
83+
return this;
84+
}
85+
86+
public Expression getExpression() {
87+
return expression;
88+
}
89+
90+
public void setExpression(Expression expression) {
91+
this.expression = expression;
92+
}
93+
94+
public CreateMacro withExpression(Expression expression) {
95+
setExpression(expression);
96+
return this;
97+
}
98+
99+
public Select getSelect() {
100+
return select;
101+
}
102+
103+
public void setSelect(Select select) {
104+
this.select = select;
105+
}
106+
107+
public CreateMacro withSelect(Select select) {
108+
setSelect(select);
109+
return this;
110+
}
111+
112+
/** A table macro returns a relation, a scalar macro an expression. */
113+
public boolean isTable() {
114+
return select != null;
115+
}
116+
117+
public StringBuilder appendTo(StringBuilder builder) {
118+
builder.append("CREATE ");
119+
if (orReplace) {
120+
builder.append("OR REPLACE ");
121+
}
122+
if (temporary) {
123+
builder.append("TEMPORARY ");
124+
}
125+
builder.append("MACRO ").append(name).append(" (");
126+
for (int i = 0; i < parameters.size(); i++) {
127+
if (i > 0) {
128+
builder.append(", ");
129+
}
130+
builder.append(parameters.get(i));
131+
}
132+
builder.append(") AS ");
133+
if (select != null) {
134+
builder.append("TABLE ").append(select);
135+
} else {
136+
builder.append(expression);
137+
}
138+
return builder;
139+
}
140+
141+
@Override
142+
public String toString() {
143+
return appendTo(new StringBuilder()).toString();
144+
}
145+
146+
@Override
147+
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
148+
return statementVisitor.visit(this, context);
149+
}
150+
151+
/** A macro parameter, optionally carrying a default value ({@code b := 5}). */
152+
public static class Parameter implements Serializable {
153+
private final String name;
154+
private final Expression defaultValue;
155+
156+
public Parameter(String name) {
157+
this(name, null);
158+
}
159+
160+
public Parameter(String name, Expression defaultValue) {
161+
this.name = name;
162+
this.defaultValue = defaultValue;
163+
}
164+
165+
public String getName() {
166+
return name;
167+
}
168+
169+
public Expression getDefaultValue() {
170+
return defaultValue;
171+
}
172+
173+
@Override
174+
public String toString() {
175+
return defaultValue == null ? name : name + " := " + defaultValue;
176+
}
177+
}
178+
}

‎src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@
218218
import net.sf.jsqlparser.statement.PrepareStatement;
219219
import net.sf.jsqlparser.statement.DeallocateStatement;
220220
import net.sf.jsqlparser.statement.CopyStatement;
221+
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
221222

222223

223224
/**
@@ -2580,6 +2581,19 @@ public void visit(CopyStatement copyStatement) {
25802581
StatementVisitor.super.visit(copyStatement);
25812582
}
25822583

2584+
@Override
2585+
public <S> Void visit(CreateMacro createMacro, S context) {
2586+
if (createMacro.getSelect() != null) {
2587+
createMacro.getSelect().accept((SelectVisitor<?>) this, context);
2588+
}
2589+
return null;
2590+
}
2591+
2592+
@Override
2593+
public void visit(CreateMacro createMacro) {
2594+
StatementVisitor.super.visit(createMacro);
2595+
}
2596+
25832597
@Override
25842598
public <S> Void visit(PurgeStatement purgeStatement, S context) {
25852599
if (purgeStatement.getPurgeObjectType() == PurgeObjectType.TABLE) {

‎src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@
106106
import net.sf.jsqlparser.statement.PrepareStatement;
107107
import net.sf.jsqlparser.statement.DeallocateStatement;
108108
import net.sf.jsqlparser.statement.CopyStatement;
109+
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
109110

110111
public class StatementDeParser extends AbstractDeParser<Statement>
111112
implements StatementVisitor<StringBuilder> {
@@ -619,6 +620,12 @@ public <S> StringBuilder visit(CopyStatement copyStatement, S context) {
619620
return builder;
620621
}
621622

623+
@Override
624+
public <S> StringBuilder visit(CreateMacro createMacro, S context) {
625+
createMacro.appendTo(builder);
626+
return builder;
627+
}
628+
622629
@Override
623630
public <S> StringBuilder visit(PurgeStatement purgeStatement, S context) {
624631
purgeStatement.appendTo(builder);

‎src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
import net.sf.jsqlparser.statement.PrepareStatement;
109109
import net.sf.jsqlparser.statement.DeallocateStatement;
110110
import net.sf.jsqlparser.statement.CopyStatement;
111+
import net.sf.jsqlparser.statement.create.macro.CreateMacro;
111112

112113
/**
113114
* @author gitmotte
@@ -540,6 +541,12 @@ public <S> Void visit(CopyStatement copyStatement, S context) {
540541
return null;
541542
}
542543

544+
@Override
545+
public <S> Void visit(CreateMacro createMacro, S context) {
546+
// TODO: not yet implemented
547+
return null;
548+
}
549+
543550
@Override
544551
public <S> Void visit(PurgeStatement purgeStatement, S context) {
545552
// TODO: not yet implemented
@@ -792,6 +799,10 @@ public void visit(CopyStatement copyStatement) {
792799
visit(copyStatement, null);
793800
}
794801

802+
public void visit(CreateMacro createMacro) {
803+
visit(createMacro, null);
804+
}
805+
795806
public void visit(PurgeStatement purgeStatement) {
796807
visit(purgeStatement, null);
797808
}

0 commit comments

Comments
 (0)