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
197 changes: 173 additions & 24 deletions src/main/java/net/sf/jsqlparser/statement/create/macro/CreateMacro.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>
* {@link #getParameters()}, {@link #getExpression()} and {@link #getSelect()} and their setters
* address the first overload.
*
* <p>
* 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 <a href="https://duckdb.org/docs/stable/sql/statements/create_macro">CREATE MACRO</a>
*/
public class CreateMacro implements Statement {
private boolean orReplace;
private boolean temporary;
private boolean functionKeyword;
private boolean ifNotExists;
private String name;
private List<Parameter> parameters;
private Expression expression;
private Select select;
private List<Overload> overloads = new ArrayList<>();

public boolean isOrReplace() {
return orReplace;
Expand Down Expand Up @@ -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;
}
Expand All @@ -71,12 +110,33 @@ public CreateMacro withName(String name) {
return this;
}

public List<Overload> getOverloads() {
return overloads;
}

/** Copies {@code overloads}, so that the first-overload setters can add to an empty list. */
public void setOverloads(List<Overload> overloads) {
this.overloads = overloads == null ? new ArrayList<>() : new ArrayList<>(overloads);
}

public CreateMacro withOverloads(List<Overload> overloads) {
setOverloads(overloads);
return this;
}

private Overload firstOverload() {
if (overloads.isEmpty()) {
overloads.add(new Overload());
}
return overloads.get(0);
}

public List<Parameter> getParameters() {
return parameters;
return overloads.isEmpty() ? null : overloads.get(0).getParameters();
}

public void setParameters(List<Parameter> parameters) {
this.parameters = parameters;
firstOverload().setParameters(parameters);
}

public CreateMacro withParameters(List<Parameter> parameters) {
Expand All @@ -85,11 +145,11 @@ public CreateMacro withParameters(List<Parameter> 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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -129,19 +189,16 @@ public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> 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;
}
Expand All @@ -156,24 +213,113 @@ public <T, S> T accept(StatementVisitor<T> 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<Parameter> parameters = new ArrayList<>();
private Expression expression;
private Select select;

public List<Parameter> getParameters() {
return parameters;
}

public void setParameters(List<Parameter> parameters) {
this.parameters = parameters;
}

public Overload withParameters(List<Parameter> 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<Expression> expressionRenderer, Consumer<Select> selectRenderer) {
builder.append("(");
for (int i = 0; i < parameters.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);
}
return builder;
}
}

/**
* A macro parameter, optionally typed ({@code a INTEGER}) and carrying a default value
* ({@code b := 5}).
*/
public static class Parameter implements Serializable {
private final String name;
private final ColDataType type;
private final Expression defaultValue;

public Parameter(String name) {
this(name, null);
this(name, null, null);
}

public Parameter(String name, Expression defaultValue) {
this(name, null, defaultValue);
}

public Parameter(String name, ColDataType type, Expression defaultValue) {
this.name = name;
this.type = type;
this.defaultValue = defaultValue;
}

public String getName() {
return name;
}

public ColDataType getType() {
return type;
}

public Expression getDefaultValue() {
return defaultValue;
}
Expand All @@ -187,6 +333,9 @@ public String toString() {
private StringBuilder appendTo(StringBuilder builder,
Consumer<Expression> expressionRenderer) {
builder.append(name);
if (type != null) {
builder.append(" ").append(type);
}
if (defaultValue != null) {
builder.append(" := ");
expressionRenderer.accept(defaultValue);
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2722,8 +2722,10 @@ public void visit(CopyStatement copyStatement) {

@Override
public <S> Void visit(CreateMacro createMacro, S context) {
if (createMacro.getSelect() != null) {
createMacro.getSelect().accept((SelectVisitor<?>) this, context);
for (CreateMacro.Overload overload : createMacro.getOverloads()) {
if (overload.getSelect() != null) {
overload.getSelect().accept((SelectVisitor<?>) this, context);
}
}
return null;
}
Expand Down
Loading
Loading