diff --git a/src/main/java/net/sf/jsqlparser/statement/create/macro/CreateMacro.java b/src/main/java/net/sf/jsqlparser/statement/create/macro/CreateMacro.java
index 11aa3f5b1..addf28daa 100644
--- a/src/main/java/net/sf/jsqlparser/statement/create/macro/CreateMacro.java
+++ b/src/main/java/net/sf/jsqlparser/statement/create/macro/CreateMacro.java
@@ -12,25 +12,37 @@
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.StatementVisitor;
+import net.sf.jsqlparser.statement.create.table.ColDataType;
import net.sf.jsqlparser.statement.select.Select;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
- * DuckDB's {@code CREATE [OR REPLACE] [TEMPORARY] MACRO name (parameters) AS [TABLE] body}, a named
- * expression or query that is inlined at call sites.
+ * DuckDB's
+ * {@code CREATE [OR REPLACE] [TEMPORARY] {MACRO | FUNCTION} [IF NOT EXISTS] name (parameters) AS [TABLE] body [, (parameters) AS [TABLE] body ...]},
+ * a named expression or query that is inlined at call sites, optionally overloaded on its
+ * parameters.
+ *
+ *
+ * {@link #getParameters()}, {@link #getExpression()} and {@link #getSelect()} and their setters
+ * address the first overload.
+ *
+ *
+ * As in DuckDB, a {@code ,} after a {@code TABLE} body that ends with a list (FROM, GROUP BY, ORDER
+ * BY ...) continues that list rather than starting a new overload.
*
* @see CREATE MACRO
*/
public class CreateMacro implements Statement {
private boolean orReplace;
private boolean temporary;
+ private boolean functionKeyword;
+ private boolean ifNotExists;
private String name;
- private List parameters;
- private Expression expression;
- private Select select;
+ private List overloads = new ArrayList<>();
public boolean isOrReplace() {
return orReplace;
@@ -58,6 +70,33 @@ public CreateMacro withTemporary(boolean temporary) {
return this;
}
+ /** Whether it was written {@code CREATE FUNCTION}, DuckDB's alias of {@code CREATE MACRO}. */
+ public boolean isFunctionKeyword() {
+ return functionKeyword;
+ }
+
+ public void setFunctionKeyword(boolean functionKeyword) {
+ this.functionKeyword = functionKeyword;
+ }
+
+ public CreateMacro withFunctionKeyword(boolean functionKeyword) {
+ setFunctionKeyword(functionKeyword);
+ return this;
+ }
+
+ public boolean isIfNotExists() {
+ return ifNotExists;
+ }
+
+ public void setIfNotExists(boolean ifNotExists) {
+ this.ifNotExists = ifNotExists;
+ }
+
+ public CreateMacro withIfNotExists(boolean ifNotExists) {
+ setIfNotExists(ifNotExists);
+ return this;
+ }
+
public String getName() {
return name;
}
@@ -71,12 +110,33 @@ public CreateMacro withName(String name) {
return this;
}
+ public List getOverloads() {
+ return overloads;
+ }
+
+ /** Copies {@code overloads}, so that the first-overload setters can add to an empty list. */
+ public void setOverloads(List overloads) {
+ this.overloads = overloads == null ? new ArrayList<>() : new ArrayList<>(overloads);
+ }
+
+ public CreateMacro withOverloads(List overloads) {
+ setOverloads(overloads);
+ return this;
+ }
+
+ private Overload firstOverload() {
+ if (overloads.isEmpty()) {
+ overloads.add(new Overload());
+ }
+ return overloads.get(0);
+ }
+
public List getParameters() {
- return parameters;
+ return overloads.isEmpty() ? null : overloads.get(0).getParameters();
}
public void setParameters(List parameters) {
- this.parameters = parameters;
+ firstOverload().setParameters(parameters);
}
public CreateMacro withParameters(List parameters) {
@@ -85,11 +145,11 @@ public CreateMacro withParameters(List parameters) {
}
public Expression getExpression() {
- return expression;
+ return overloads.isEmpty() ? null : overloads.get(0).getExpression();
}
public void setExpression(Expression expression) {
- this.expression = expression;
+ firstOverload().setExpression(expression);
}
public CreateMacro withExpression(Expression expression) {
@@ -98,11 +158,11 @@ public CreateMacro withExpression(Expression expression) {
}
public Select getSelect() {
- return select;
+ return overloads.isEmpty() ? null : overloads.get(0).getSelect();
}
public void setSelect(Select select) {
- this.select = select;
+ firstOverload().setSelect(select);
}
public CreateMacro withSelect(Select select) {
@@ -112,7 +172,7 @@ public CreateMacro withSelect(Select select) {
/** A table macro returns a relation, a scalar macro an expression. */
public boolean isTable() {
- return select != null;
+ return getSelect() != null;
}
public StringBuilder appendTo(StringBuilder builder) {
@@ -129,19 +189,16 @@ public StringBuilder appendTo(StringBuilder builder, Consumer expres
if (temporary) {
builder.append("TEMPORARY ");
}
- builder.append("MACRO ").append(name).append(" (");
- for (int i = 0; i < parameters.size(); i++) {
+ builder.append(functionKeyword ? "FUNCTION " : "MACRO ");
+ if (ifNotExists) {
+ builder.append("IF NOT EXISTS ");
+ }
+ builder.append(name).append(" ");
+ for (int i = 0; i < overloads.size(); i++) {
if (i > 0) {
builder.append(", ");
}
- parameters.get(i).appendTo(builder, expressionRenderer);
- }
- builder.append(") AS ");
- if (select != null) {
- builder.append("TABLE ");
- selectRenderer.accept(select);
- } else {
- expressionRenderer.accept(expression);
+ overloads.get(i).appendTo(builder, expressionRenderer, selectRenderer);
}
return builder;
}
@@ -156,17 +213,102 @@ public T accept(StatementVisitor statementVisitor, S context) {
return statementVisitor.visit(this, context);
}
- /** A macro parameter, optionally carrying a default value ({@code b := 5}). */
+ /** One {@code (parameters) AS [TABLE] body} of a macro. */
+ public static class Overload implements Serializable {
+ private List parameters = new ArrayList<>();
+ private Expression expression;
+ private Select select;
+
+ public List getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List parameters) {
+ this.parameters = parameters;
+ }
+
+ public Overload withParameters(List parameters) {
+ setParameters(parameters);
+ return this;
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ public void setExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ public Overload withExpression(Expression expression) {
+ setExpression(expression);
+ return this;
+ }
+
+ public Select getSelect() {
+ return select;
+ }
+
+ public void setSelect(Select select) {
+ this.select = select;
+ }
+
+ public Overload withSelect(Select select) {
+ setSelect(select);
+ return this;
+ }
+
+ /** A table overload returns a relation, a scalar one an expression. */
+ public boolean isTable() {
+ return select != null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ return appendTo(builder, builder::append, builder::append).toString();
+ }
+
+ private StringBuilder appendTo(StringBuilder builder,
+ Consumer expressionRenderer, Consumer