Skip to content

Commit 4b91117

Browse files
committed
fix: preserve COLUMNS traversal and alias boundaries
Reuse alias grammar at each transformer boundary, preserve JJTree parents and lookahead visibility, and route all child expressions through visitor hooks. Add consumer and grammar regression coverage. Signed-off-by: 付典 <fudianchn@gmail.com>
1 parent 2fba7fa commit 4b91117

11 files changed

Lines changed: 449 additions & 203 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
140140
|---|---|
141141
| **Queries** | `SELECT` · `WITH …` · Piped SQL |
142142
| **ksqlDB windows** | JOIN `WITHIN`, window `GRACE PERIOD`, and `EMIT CHANGES`/`FINAL` |
143+
| **ClickHouse column selection** | `COLUMNS('regexp')` select items with chained `APPLY`, `EXCEPT`, and `REPLACE` transformers |
143144
| **DML** | `INSERT` · `UPDATE` · `UPSERT` · `MERGE` · `DELETE` · `TRUNCATE TABLE` |
144145
| **DDL** | `CREATE …` · `ALTER …` · `DROP …` |
145146
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |

src/main/java/net/sf/jsqlparser/expression/ColumnsExpression.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ public ColumnsExpression setTransformers(List<ColumnsTransformer> transformers)
5353
return this;
5454
}
5555

56+
public List<Expression> getAllExpressions() {
57+
List<Expression> expressions = new ArrayList<>();
58+
if (columns != null) {
59+
expressions.add(columns);
60+
}
61+
for (ColumnsTransformer transformer : transformers) {
62+
transformer.collectExpressions(expressions);
63+
}
64+
return expressions;
65+
}
66+
5667
public StringBuilder appendTo(StringBuilder builder) {
5768
builder.append(columns);
5869
for (ColumnsTransformer transformer : transformers) {

src/main/java/net/sf/jsqlparser/expression/ColumnsTransformer.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,30 @@ public ColumnsTransformer setReplaceItems(List<SelectItem<?>> replaceItems) {
7373
return this;
7474
}
7575

76+
void collectExpressions(List<Expression> expressions) {
77+
switch (type) {
78+
case APPLY:
79+
if (applyExpression != null) {
80+
expressions.add(applyExpression);
81+
}
82+
break;
83+
case EXCEPT:
84+
if (exceptColumns != null) {
85+
expressions.addAll(exceptColumns);
86+
}
87+
break;
88+
case REPLACE:
89+
if (replaceItems != null) {
90+
for (SelectItem<?> item : replaceItems) {
91+
expressions.add(item.getExpression());
92+
}
93+
}
94+
break;
95+
default:
96+
throw new IllegalStateException("Unhandled ColumnsTransformerType: " + type);
97+
}
98+
}
99+
76100
public StringBuilder appendTo(StringBuilder builder) {
77101
switch (type) {
78102
case APPLY:

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -796,7 +796,14 @@ default void visit(LambdaExpression lambdaExpression) {
796796
this.visit(lambdaExpression, null);
797797
}
798798

799-
<S> T visit(ColumnsExpression columnsExpression, S context);
799+
default <S> T visit(ColumnsExpression columnsExpression, S context) {
800+
for (Expression expression : columnsExpression.getAllExpressions()) {
801+
if (expression != null) {
802+
expression.accept(this, context);
803+
}
804+
}
805+
return null;
806+
}
800807

801808
default void visit(ColumnsExpression columnsExpression) {
802809
this.visit(columnsExpression, null);

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -877,22 +877,7 @@ public <S> T visit(LambdaExpression lambdaExpression, S context) {
877877

878878
@Override
879879
public <S> T visit(ColumnsExpression columnsExpression, S context) {
880-
if (columnsExpression.getColumns() != null) {
881-
columnsExpression.getColumns().accept(this, context);
882-
}
883-
for (ColumnsTransformer transformer : columnsExpression.getTransformers()) {
884-
if (transformer.getApplyExpression() != null) {
885-
transformer.getApplyExpression().accept(this, context);
886-
}
887-
if (transformer.getReplaceItems() != null) {
888-
for (SelectItem<?> selectItem : transformer.getReplaceItems()) {
889-
if (selectItem.getExpression() != null) {
890-
selectItem.getExpression().accept(this, context);
891-
}
892-
}
893-
}
894-
}
895-
return null;
880+
return visitExpressions(columnsExpression, context, columnsExpression.getAllExpressions());
896881
}
897882

898883
@Override

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,19 +2229,9 @@ public <S> Void visit(LambdaExpression lambdaExpression, S context) {
22292229

22302230
@Override
22312231
public <S> Void visit(ColumnsExpression columnsExpression, S context) {
2232-
if (columnsExpression.getColumns() != null) {
2233-
columnsExpression.getColumns().accept(this, context);
2234-
}
2235-
for (ColumnsTransformer transformer : columnsExpression.getTransformers()) {
2236-
if (transformer.getApplyExpression() != null) {
2237-
transformer.getApplyExpression().accept(this, context);
2238-
}
2239-
if (transformer.getReplaceItems() != null) {
2240-
for (SelectItem<?> selectItem : transformer.getReplaceItems()) {
2241-
if (selectItem.getExpression() != null) {
2242-
selectItem.getExpression().accept(this, context);
2243-
}
2244-
}
2232+
for (Expression expression : columnsExpression.getAllExpressions()) {
2233+
if (expression != null) {
2234+
expression.accept(this, context);
22452235
}
22462236
}
22472237
return null;

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import net.sf.jsqlparser.expression.CastExpression;
2727
import net.sf.jsqlparser.expression.CollateExpression;
2828
import net.sf.jsqlparser.expression.ColumnsExpression;
29+
import net.sf.jsqlparser.expression.ColumnsTransformer;
2930
import net.sf.jsqlparser.expression.ConnectByPriorOperator;
3031
import net.sf.jsqlparser.expression.ConnectByRootOperator;
3132
import net.sf.jsqlparser.expression.DateTimeLiteralExpression;
@@ -1929,7 +1930,39 @@ public <S> StringBuilder visit(LambdaExpression lambdaExpression, S context) {
19291930

19301931
@Override
19311932
public <S> StringBuilder visit(ColumnsExpression columnsExpression, S context) {
1932-
return columnsExpression.appendTo(builder);
1933+
columnsExpression.getColumns().accept(this, context);
1934+
for (ColumnsTransformer transformer : columnsExpression.getTransformers()) {
1935+
switch (transformer.getType()) {
1936+
case APPLY:
1937+
builder.append(" APPLY(");
1938+
transformer.getApplyExpression().accept(this, context);
1939+
builder.append(")");
1940+
break;
1941+
case EXCEPT:
1942+
builder.append(" EXCEPT ");
1943+
transformer.getExceptColumns().accept(this, context);
1944+
break;
1945+
case REPLACE:
1946+
builder.append(" REPLACE(");
1947+
boolean first = true;
1948+
for (SelectItem<?> item : transformer.getReplaceItems()) {
1949+
if (!first) {
1950+
builder.append(", ");
1951+
}
1952+
first = false;
1953+
item.getExpression().accept(this, context);
1954+
if (item.getAlias() != null) {
1955+
builder.append(item.getAlias());
1956+
}
1957+
}
1958+
builder.append(")");
1959+
break;
1960+
default:
1961+
throw new IllegalStateException(
1962+
"Unhandled ColumnsTransformerType: " + transformer.getType());
1963+
}
1964+
}
1965+
return builder;
19331966
}
19341967

19351968
@Override

src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import net.sf.jsqlparser.expression.CastExpression;
2222
import net.sf.jsqlparser.expression.CollateExpression;
2323
import net.sf.jsqlparser.expression.ColumnsExpression;
24-
import net.sf.jsqlparser.expression.ColumnsTransformer;
2524
import net.sf.jsqlparser.expression.ConnectByPriorOperator;
2625
import net.sf.jsqlparser.expression.ConnectByRootOperator;
2726
import net.sf.jsqlparser.expression.DateTimeLiteralExpression;
@@ -1222,19 +1221,9 @@ public <S> Void visit(LambdaExpression lambdaExpression, S context) {
12221221

12231222
@Override
12241223
public <S> Void visit(ColumnsExpression columnsExpression, S context) {
1225-
if (columnsExpression.getColumns() != null) {
1226-
columnsExpression.getColumns().accept(this, context);
1227-
}
1228-
for (ColumnsTransformer transformer : columnsExpression.getTransformers()) {
1229-
if (transformer.getApplyExpression() != null) {
1230-
transformer.getApplyExpression().accept(this, context);
1231-
}
1232-
if (transformer.getReplaceItems() != null) {
1233-
for (SelectItem<?> selectItem : transformer.getReplaceItems()) {
1234-
if (selectItem.getExpression() != null) {
1235-
selectItem.getExpression().accept(this, context);
1236-
}
1237-
}
1224+
for (Expression expression : columnsExpression.getAllExpressions()) {
1225+
if (expression != null) {
1226+
expression.accept(this, context);
12381227
}
12391228
}
12401229
return null;

0 commit comments

Comments
 (0)