Skip to content

Commit 5c2489c

Browse files
committed
fix: visit expressions in macro, assertion and export statement output
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 2fc6e3e commit 5c2489c

5 files changed

Lines changed: 155 additions & 11 deletions

File tree

src/main/java/net/sf/jsqlparser/statement/AssertStatement.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import net.sf.jsqlparser.expression.Expression;
1313
import net.sf.jsqlparser.expression.StringValue;
1414

15+
import java.util.function.Consumer;
16+
1517
/**
1618
* BigQuery's {@code ASSERT expression [AS description]}, which fails the script when the expression
1719
* does not evaluate to {@code TRUE}.
@@ -62,9 +64,16 @@ public AssertStatement withDescription(StringValue description) {
6264
}
6365

6466
public StringBuilder appendTo(StringBuilder builder) {
65-
builder.append("ASSERT ").append(expression);
67+
return appendTo(builder, builder::append);
68+
}
69+
70+
/** Shares statement syntax with deparsers while visiting both expression and description. */
71+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionRenderer) {
72+
builder.append("ASSERT ");
73+
expressionRenderer.accept(expression);
6674
if (description != null) {
67-
builder.append(" AS ").append(description);
75+
builder.append(" AS ");
76+
expressionRenderer.accept(description);
6877
}
6978
return builder;
7079
}

src/main/java/net/sf/jsqlparser/statement/create/macro/CreateMacro.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import java.io.Serializable;
1818
import java.util.List;
19+
import java.util.function.Consumer;
1920

2021
/**
2122
* DuckDB's {@code CREATE [OR REPLACE] [TEMPORARY] MACRO name (parameters) AS [TABLE] body}, a named
@@ -115,6 +116,12 @@ public boolean isTable() {
115116
}
116117

117118
public StringBuilder appendTo(StringBuilder builder) {
119+
return appendTo(builder, builder::append, builder::append);
120+
}
121+
122+
/** Shares punctuation while exposing parameter defaults and both kinds of macro body. */
123+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionRenderer,
124+
Consumer<Select> selectRenderer) {
118125
builder.append("CREATE ");
119126
if (orReplace) {
120127
builder.append("OR REPLACE ");
@@ -127,13 +134,14 @@ public StringBuilder appendTo(StringBuilder builder) {
127134
if (i > 0) {
128135
builder.append(", ");
129136
}
130-
builder.append(parameters.get(i));
137+
parameters.get(i).appendTo(builder, expressionRenderer);
131138
}
132139
builder.append(") AS ");
133140
if (select != null) {
134-
builder.append("TABLE ").append(select);
141+
builder.append("TABLE ");
142+
selectRenderer.accept(select);
135143
} else {
136-
builder.append(expression);
144+
expressionRenderer.accept(expression);
137145
}
138146
return builder;
139147
}
@@ -172,7 +180,18 @@ public Expression getDefaultValue() {
172180

173181
@Override
174182
public String toString() {
175-
return defaultValue == null ? name : name + " := " + defaultValue;
183+
StringBuilder builder = new StringBuilder();
184+
return appendTo(builder, builder::append).toString();
185+
}
186+
187+
private StringBuilder appendTo(StringBuilder builder,
188+
Consumer<Expression> expressionRenderer) {
189+
builder.append(name);
190+
if (defaultValue != null) {
191+
builder.append(" := ");
192+
expressionRenderer.accept(defaultValue);
193+
}
194+
return builder;
176195
}
177196
}
178197
}

src/main/java/net/sf/jsqlparser/statement/export/ExportDataStatement.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import net.sf.jsqlparser.statement.StatementVisitor;
1616
import net.sf.jsqlparser.statement.select.Select;
1717

18+
import java.util.function.Consumer;
19+
1820
/**
1921
* BigQuery's {@code EXPORT DATA [WITH CONNECTION connection] OPTIONS (option_list) AS query}.
2022
*
@@ -67,14 +69,28 @@ public ExportDataStatement withSelect(Select select) {
6769
}
6870

6971
public StringBuilder appendTo(StringBuilder builder) {
72+
return appendTo(builder, builder::append, builder::append);
73+
}
74+
75+
/** Renders options and the query through the caller's expression and select visitors. */
76+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionRenderer,
77+
Consumer<Select> selectRenderer) {
7078
builder.append("EXPORT DATA");
7179
if (connectionName != null) {
7280
builder.append(" WITH CONNECTION ").append(connectionName);
7381
}
7482
if (options != null) {
75-
builder.append(" OPTIONS (").append(options).append(")");
83+
builder.append(" OPTIONS (");
84+
for (int i = 0; i < options.size(); i++) {
85+
if (i > 0) {
86+
builder.append(", ");
87+
}
88+
expressionRenderer.accept(options.get(i));
89+
}
90+
builder.append(")");
7691
}
77-
builder.append(" AS ").append(select);
92+
builder.append(" AS ");
93+
selectRenderer.accept(select);
7894
return builder;
7995
}
8096

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -570,7 +570,8 @@ public <S> StringBuilder visit(PragmaStatement pragmaStatement, S context) {
570570

571571
@Override
572572
public <S> StringBuilder visit(AssertStatement assertStatement, S context) {
573-
assertStatement.appendTo(builder);
573+
assertStatement.appendTo(builder,
574+
expression -> expression.accept(expressionDeParser, context));
574575
return builder;
575576
}
576577

@@ -582,7 +583,9 @@ public <S> StringBuilder visit(ExtensionStatement extensionStatement, S context)
582583

583584
@Override
584585
public <S> StringBuilder visit(ExportDataStatement exportDataStatement, S context) {
585-
exportDataStatement.appendTo(builder);
586+
exportDataStatement.appendTo(builder,
587+
expression -> expression.accept(expressionDeParser, context),
588+
select -> select.accept((SelectVisitor<?>) selectDeParser, context));
586589
return builder;
587590
}
588591

@@ -630,7 +633,9 @@ public <S> StringBuilder visit(CopyStatement copyStatement, S context) {
630633

631634
@Override
632635
public <S> StringBuilder visit(CreateMacro createMacro, S context) {
633-
createMacro.appendTo(builder);
636+
createMacro.appendTo(builder,
637+
expression -> expression.accept(expressionDeParser, context),
638+
select -> select.accept((SelectVisitor<?>) selectDeParser, context));
634639
return builder;
635640
}
636641

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.util.deparser;
11+
12+
import net.sf.jsqlparser.expression.LongValue;
13+
import net.sf.jsqlparser.expression.StringValue;
14+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
15+
import net.sf.jsqlparser.statement.Statement;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.params.ParameterizedTest;
18+
import org.junit.jupiter.params.provider.ValueSource;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
class StructuredStatementVisitorTest {
26+
@ParameterizedTest
27+
@ValueSource(strings = {
28+
"CREATE MACRO m(x, y := 7) AS x + y + 8",
29+
"CREATE MACRO m() AS TABLE WITH c AS (SELECT 7 AS id) SELECT id FROM c UNION ALL SELECT 8",
30+
"ASSERT (1 = 1) AS 'expected'",
31+
"ASSERT EXISTS (SELECT 7 FROM users WHERE id = 8)",
32+
"EXPORT DATA OPTIONS(uri='gs://example/*.csv',format='CSV') AS SELECT 7 FROM users",
33+
"EXPORT DATA OPTIONS(uri='gs://example/*.csv',format='CSV') AS WITH c AS (SELECT 7 AS id) SELECT id FROM c UNION ALL SELECT 8"
34+
})
35+
void defaultDeparserMatchesStatementOutput(String sql) throws Exception {
36+
Statement statement = CCJSqlParserUtil.parse(sql);
37+
StringBuilder output = new StringBuilder();
38+
statement.accept(new StatementDeParser(output), null);
39+
assertEquals(statement.toString(), output.toString());
40+
assertEquals(output.toString(), CCJSqlParserUtil.parse(output.toString()).toString());
41+
}
42+
43+
@Test
44+
void visitsMacroDefaultsAndScalarBody() throws Exception {
45+
assertVisited("CREATE MACRO m(x, y := 7) AS x + y + 8", List.of("7", "8"),
46+
"CREATE MACRO m (x, y := 107) AS x + y + 108");
47+
}
48+
49+
@Test
50+
void visitsMacroQueriesAndCtes() throws Exception {
51+
assertVisited(
52+
"CREATE MACRO m() AS TABLE WITH c AS (SELECT 7 AS id) SELECT id FROM c UNION ALL SELECT 8",
53+
List.of("7", "8"),
54+
"CREATE MACRO m () AS TABLE WITH c AS (SELECT 107 AS id) SELECT id FROM c UNION ALL SELECT 108");
55+
}
56+
57+
@Test
58+
void visitsAssertionAndDescription() throws Exception {
59+
assertVisited("ASSERT (1 = 1) AS 'expected'", List.of("1", "1", "expected"),
60+
"ASSERT (101 = 101) AS 'changed'");
61+
}
62+
63+
@Test
64+
void visitsExportOptionsAndQuery() throws Exception {
65+
assertVisited(
66+
"EXPORT DATA OPTIONS(uri='gs://example/*.csv',format='CSV') AS SELECT 7 FROM users WHERE id = 8",
67+
List.of("gs://example/*.csv", "CSV", "7", "8"),
68+
"EXPORT DATA OPTIONS (uri = 'changed', format = 'changed') AS SELECT 107 FROM users WHERE id = 108");
69+
}
70+
71+
private void assertVisited(String sql, List<String> expectedVisits, String expectedOutput)
72+
throws Exception {
73+
List<String> visited = new ArrayList<>();
74+
ExpressionDeParser expressions = new ExpressionDeParser() {
75+
@Override
76+
public <S> StringBuilder visit(LongValue value, S context) {
77+
visited.add(value.getStringValue());
78+
return getBuilder().append(value.getValue() + 100);
79+
}
80+
81+
@Override
82+
public <S> StringBuilder visit(StringValue value, S context) {
83+
visited.add(value.getValue());
84+
return getBuilder().append("'changed'");
85+
}
86+
};
87+
StringBuilder output = new StringBuilder();
88+
Statement statement = CCJSqlParserUtil.parse(sql);
89+
statement.accept(new StatementDeParser(expressions, new SelectDeParser(), output),
90+
"context");
91+
assertEquals(expectedVisits, visited);
92+
assertEquals(expectedOutput, output.toString());
93+
assertEquals(expectedOutput, CCJSqlParserUtil.parse(output.toString()).toString());
94+
}
95+
}

0 commit comments

Comments
 (0)