Skip to content

Commit 07576db

Browse files
committed
feat: parse PostgreSQL inheritance targets, WHERE CURRENT OF, row sources and MERGE DO NOTHING
UPDATE, DELETE and MERGE take PostgreSQL's relation_expr_opt_alias as the target: ONLY t, ONLY (t) and t * with an alias, kept in a TableReference next to getTable(). UPDATE and DELETE read WHERE CURRENT OF cursor into a cursor name. A SET column list takes any expression as its source, such as ROW(...), through one UpdateSet production. MERGE reads DO NOTHING for every WHEN condition, INSERT DEFAULT VALUES and INSERT ... OVERRIDING {SYSTEM | USER} VALUE; the WHEN prefix is rendered by MergeWhen. Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent e27929f commit 07576db

23 files changed

Lines changed: 1230 additions & 145 deletions

File tree

‎src/main/java/net/sf/jsqlparser/schema/TableReference.java‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
import java.io.Serializable;
1313

14-
/** A relation target with optional PostgreSQL inheritance scope, shared by LOCK and TRUNCATE. */
14+
/**
15+
* A relation target with optional PostgreSQL inheritance scope, shared by LOCK, TRUNCATE and the
16+
* targets of UPDATE, DELETE and MERGE.
17+
*/
1518
public class TableReference implements Serializable {
1619
private Table table;
1720
private boolean only;
@@ -84,6 +87,27 @@ public StringBuilder appendTo(StringBuilder builder) {
8487
return builder;
8588
}
8689

90+
/**
91+
* Appends the target of UPDATE, DELETE or MERGE (PostgreSQL's relation_expr_opt_alias): the
92+
* inheritance scope surrounds the table name and the alias of the table follows it. A target
93+
* without a scope is the table as it renders itself, with its alias and hints.
94+
*/
95+
public StringBuilder appendWithAliasTo(StringBuilder builder) {
96+
if (!only && !includeDescendants || table == null) {
97+
return builder.append(table);
98+
}
99+
appendTo(builder);
100+
if (table.getAlias() != null) {
101+
builder.append(table.getAlias());
102+
}
103+
return builder;
104+
}
105+
106+
/** Whether ONLY or * describes the inheritance scope of the table. */
107+
public boolean hasInheritanceScope() {
108+
return only || includeDescendants;
109+
}
110+
87111
@Override
88112
public String toString() {
89113
return appendTo(new StringBuilder()).toString();

‎src/main/java/net/sf/jsqlparser/statement/delete/Delete.java‎

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.sf.jsqlparser.expression.PreferringClause;
1515
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
1616
import net.sf.jsqlparser.schema.Table;
17+
import net.sf.jsqlparser.schema.TableReference;
1718
import net.sf.jsqlparser.statement.ForPortionClause;
1819
import net.sf.jsqlparser.statement.OutputClause;
1920
import net.sf.jsqlparser.statement.ReturningClause;
@@ -32,6 +33,7 @@
3233
import java.util.Collections;
3334
import java.util.Iterator;
3435
import java.util.List;
36+
import java.util.Objects;
3537
import java.util.Optional;
3638
import java.util.stream.Collectors;
3739

@@ -40,14 +42,15 @@
4042
public class Delete extends ASTNodeAccessImpl implements Statement {
4143

4244
private List<WithItem<?>> withItemsList;
43-
private Table table;
45+
private TableReference tableReference = new TableReference(null);
4446
private ForPortionClause forPortionClause;
4547
private OracleHint oracleHint = null;
4648
private List<Table> tables;
4749
private List<FromItem> usingFromItemList;
4850
private List<Join> usingJoins;
4951
private List<Join> joins;
5052
private Expression where;
53+
private String cursorName;
5154
private PreferringClause preferringClause;
5255
private Limit limit;
5356
private OptionClause option;
@@ -131,11 +134,46 @@ public Delete withForPortionClause(ForPortionClause forPortionClause) {
131134
}
132135

133136
public Table getTable() {
134-
return table;
137+
return tableReference.getTable();
135138
}
136139

140+
/** Replaces the target table and keeps the inheritance scope of the target. */
137141
public void setTable(Table name) {
138-
table = name;
142+
tableReference.setTable(name);
143+
}
144+
145+
/**
146+
* The target as PostgreSQL's relation_expr_opt_alias: the table of {@link #getTable()} with its
147+
* alias, and whether ONLY or * describes its inheritance scope.
148+
*/
149+
public TableReference getTableReference() {
150+
return tableReference;
151+
}
152+
153+
public void setTableReference(TableReference tableReference) {
154+
this.tableReference = Objects.requireNonNull(tableReference);
155+
}
156+
157+
public Delete withTableReference(TableReference tableReference) {
158+
this.setTableReference(tableReference);
159+
return this;
160+
}
161+
162+
/**
163+
* The cursor of {@code WHERE CURRENT OF cursor}, which is written in place of the condition of
164+
* {@link #getWhere()}.
165+
*/
166+
public String getCursorName() {
167+
return cursorName;
168+
}
169+
170+
public void setCursorName(String cursorName) {
171+
this.cursorName = cursorName;
172+
}
173+
174+
public Delete withCursorName(String cursorName) {
175+
this.setCursorName(cursorName);
176+
return this;
139177
}
140178

141179
public Expression getWhere() {
@@ -277,7 +315,7 @@ public String toString() {
277315
if (hasFrom) {
278316
b.append(" FROM");
279317
}
280-
b.append(" ").append(table);
318+
tableReference.appendWithAliasTo(b.append(" "));
281319
if (forPortionClause != null) {
282320
b.append(' ').append(forPortionClause);
283321
}
@@ -292,7 +330,9 @@ public String toString() {
292330
appendJoinsTo(b, usingJoins);
293331
}
294332

295-
if (where != null) {
333+
if (cursorName != null) {
334+
b.append(" WHERE CURRENT OF ").append(cursorName);
335+
} else if (where != null) {
296336
b.append(" WHERE ").append(where);
297337
}
298338

‎src/main/java/net/sf/jsqlparser/statement/merge/Merge.java‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import net.sf.jsqlparser.expression.OracleHint;
1515
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
1616
import net.sf.jsqlparser.schema.Table;
17+
import net.sf.jsqlparser.schema.TableReference;
1718
import net.sf.jsqlparser.statement.OutputClause;
1819
import net.sf.jsqlparser.statement.ReturningClause;
1920
import net.sf.jsqlparser.statement.Statement;
@@ -28,13 +29,14 @@
2829
import java.util.Collections;
2930
import java.util.Iterator;
3031
import java.util.List;
32+
import java.util.Objects;
3133
import java.util.Optional;
3234
import java.util.stream.Collectors;
3335

3436
public class Merge extends ASTNodeAccessImpl implements Statement {
3537

3638
private List<WithItem<?>> withItemsList;
37-
private Table table;
39+
private TableReference tableReference = new TableReference(null);
3840
private OracleHint oracleHint = null;
3941
private FromItem fromItem;
4042
private Expression onCondition;
@@ -121,11 +123,29 @@ public Merge addWithItemsList(Collection<? extends WithItem<?>> withItemsList) {
121123
}
122124

123125
public Table getTable() {
124-
return table;
126+
return tableReference.getTable();
125127
}
126128

129+
/** Replaces the target table and keeps the inheritance scope of the target. */
127130
public void setTable(Table name) {
128-
table = name;
131+
tableReference.setTable(name);
132+
}
133+
134+
/**
135+
* The target as PostgreSQL's relation_expr_opt_alias: the table of {@link #getTable()} with its
136+
* alias, and whether ONLY or * describes its inheritance scope.
137+
*/
138+
public TableReference getTableReference() {
139+
return tableReference;
140+
}
141+
142+
public void setTableReference(TableReference tableReference) {
143+
this.tableReference = Objects.requireNonNull(tableReference);
144+
}
145+
146+
public Merge withTableReference(TableReference tableReference) {
147+
this.setTableReference(tableReference);
148+
return this;
129149
}
130150

131151
public OracleHint getOracleHint() {
@@ -265,7 +285,7 @@ public String toString() {
265285
b.append(oracleHint).append(" ");
266286
}
267287
b.append("INTO ");
268-
b.append(table);
288+
tableReference.appendWithAliasTo(b);
269289
b.append(" USING ");
270290
b.append(fromItem);
271291
b.append(" ON ");

‎src/main/java/net/sf/jsqlparser/statement/merge/MergeDelete.java‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public MergeDelete setSide(MergeSide side) {
3434
return this;
3535
}
3636

37+
/** {@code WHEN MATCHED}, or {@code WHEN NOT MATCHED BY SOURCE} with that side. */
38+
public MergeWhen getWhen() {
39+
return MergeWhen.ofTargetRow(side);
40+
}
41+
3742
public MergeDelete withAndPredicate(Expression andPredicate) {
3843
this.setAndPredicate(andPredicate);
3944
return this;
@@ -52,11 +57,7 @@ public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context
5257
@Override
5358
public String toString() {
5459
StringBuilder b = new StringBuilder();
55-
b.append(side == MergeSide.SOURCE ? " WHEN NOT MATCHED BY SOURCE" : " WHEN MATCHED");
56-
if (andPredicate != null) {
57-
b.append(" AND ").append(andPredicate.toString());
58-
}
59-
b.append(" THEN DELETE");
60+
getWhen().appendTo(b, andPredicate, b::append).append("DELETE");
6061
return b.toString();
6162
}
6263
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2024 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.merge;
11+
12+
import net.sf.jsqlparser.expression.Expression;
13+
14+
import java.io.Serializable;
15+
import java.util.Objects;
16+
17+
/** PostgreSQL's {@code WHEN [NOT] MATCHED [BY SOURCE | BY TARGET] [AND ...] THEN DO NOTHING}. */
18+
public class MergeDoNothing implements Serializable, MergeOperation {
19+
private MergeWhen when = MergeWhen.MATCHED;
20+
private Expression andPredicate;
21+
22+
public MergeDoNothing() {}
23+
24+
public MergeDoNothing(MergeWhen when) {
25+
setWhen(when);
26+
}
27+
28+
public MergeWhen getWhen() {
29+
return when;
30+
}
31+
32+
public void setWhen(MergeWhen when) {
33+
this.when = Objects.requireNonNull(when);
34+
}
35+
36+
public MergeDoNothing withWhen(MergeWhen when) {
37+
this.setWhen(when);
38+
return this;
39+
}
40+
41+
public Expression getAndPredicate() {
42+
return andPredicate;
43+
}
44+
45+
public void setAndPredicate(Expression andPredicate) {
46+
this.andPredicate = andPredicate;
47+
}
48+
49+
public MergeDoNothing withAndPredicate(Expression andPredicate) {
50+
this.setAndPredicate(andPredicate);
51+
return this;
52+
}
53+
54+
@Override
55+
public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context) {
56+
return mergeOperationVisitor.visit(this, context);
57+
}
58+
59+
@Override
60+
public String toString() {
61+
StringBuilder b = new StringBuilder();
62+
when.appendTo(b, andPredicate, b::append).append("DO NOTHING");
63+
return b.toString();
64+
}
65+
}

‎src/main/java/net/sf/jsqlparser/statement/merge/MergeInsert.java‎

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@
1212
import net.sf.jsqlparser.expression.Expression;
1313
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
1414
import net.sf.jsqlparser.schema.Column;
15+
import net.sf.jsqlparser.statement.insert.Insert;
1516

1617
import java.io.Serializable;
1718
import java.util.Arrays;
1819
import java.util.Collection;
20+
import java.util.Objects;
1921
import java.util.Optional;
22+
import java.util.function.Consumer;
2023

2124
public class MergeInsert implements Serializable, MergeOperation {
2225

@@ -25,6 +28,7 @@ public class MergeInsert implements Serializable, MergeOperation {
2528
private ExpressionList<Expression> values;
2629
private Expression whereCondition;
2730
private MergeSide side;
31+
private Insert.OverridingMode overridingMode = Insert.OverridingMode.NONE;
2832

2933
public MergeSide getSide() {
3034
return side;
@@ -66,6 +70,52 @@ public void setWhereCondition(Expression whereCondition) {
6670
this.whereCondition = whereCondition;
6771
}
6872

73+
/** {@code WHEN NOT MATCHED}, or {@code WHEN NOT MATCHED BY TARGET} with that side. */
74+
public MergeWhen getWhen() {
75+
return MergeWhen.ofSourceRow(side);
76+
}
77+
78+
/** The mode of {@code OVERRIDING {SYSTEM | USER} VALUE}, {@code NONE} without the clause. */
79+
public Insert.OverridingMode getOverridingMode() {
80+
return overridingMode;
81+
}
82+
83+
public void setOverridingMode(Insert.OverridingMode overridingMode) {
84+
this.overridingMode = Objects.requireNonNull(overridingMode);
85+
}
86+
87+
public MergeInsert withOverridingMode(Insert.OverridingMode overridingMode) {
88+
this.setOverridingMode(overridingMode);
89+
return this;
90+
}
91+
92+
/** Whether the clause is {@code INSERT DEFAULT VALUES}: an insert without a values list. */
93+
public boolean isDefaultValues() {
94+
return values == null;
95+
}
96+
97+
/**
98+
* Appends {@code INSERT DEFAULT VALUES} or {@code INSERT [(columns)] [OVERRIDING ... VALUE]
99+
* VALUES (...)}, letting the renderer write the column and value lists.
100+
*/
101+
public StringBuilder appendInsertTo(StringBuilder builder,
102+
Consumer<Expression> expressionRenderer) {
103+
builder.append("INSERT ");
104+
if (isDefaultValues()) {
105+
return builder.append("DEFAULT VALUES");
106+
}
107+
if (columns != null) {
108+
expressionRenderer.accept(columns);
109+
}
110+
if (overridingMode != Insert.OverridingMode.NONE) {
111+
builder.append(columns != null ? " " : "").append("OVERRIDING ")
112+
.append(overridingMode).append(" VALUE");
113+
}
114+
builder.append(" VALUES ");
115+
expressionRenderer.accept(values);
116+
return builder;
117+
}
118+
69119
@Override
70120
public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context) {
71121
return mergeOperationVisitor.visit(this, context);
@@ -74,18 +124,8 @@ public <S, T> T accept(MergeOperationVisitor<T> mergeOperationVisitor, S context
74124
@Override
75125
public String toString() {
76126
StringBuilder b = new StringBuilder();
77-
b.append(" WHEN NOT MATCHED");
78-
if (side == MergeSide.TARGET) {
79-
b.append(" BY TARGET");
80-
}
81-
if (andPredicate != null) {
82-
b.append(" AND ").append(andPredicate);
83-
}
84-
b.append(" THEN INSERT ");
85-
if (columns != null) {
86-
b.append(columns);
87-
}
88-
b.append(" VALUES ").append(values.toString());
127+
getWhen().appendTo(b, andPredicate, b::append);
128+
appendInsertTo(b, b::append);
89129
if (whereCondition != null) {
90130
b.append(" WHERE ").append(whereCondition);
91131
}

0 commit comments

Comments
 (0)