Skip to content

Commit f457a94

Browse files
committed
feat: parse SET TRANSACTION, SET CONSTRAINTS, COMMIT RELEASE and PostgreSQL END and ABORT
MySQL SET [scope] TRANSACTION, PostgreSQL SET [scope] TRANSACTION, SET TRANSACTION SNAPSHOT, SET SESSION CHARACTERISTICS AS TRANSACTION, SET CONSTRAINTS, MySQL COMMIT/ROLLBACK ... [NO] RELEASE and PostgreSQL END and ABORT failed to parse; SET CONSTRAINTS c1, c2 IMMEDIATE was read as two variable assignments. Move the transaction characteristics to a top-level TransactionMode enum, parsed by one TransactionModeList production whose context (BEGIN, START TRANSACTION, SET TRANSACTION) selects the dialect rules. Add SetTransactionStatement and SetConstraintsStatement, dispatched before the generic SET, which keeps variable assignments. They reuse TransactionMode, a new SetScope enum that also types the scope of SetStatement, and ConstraintAttributes.Initially. SET TRANSACTION takes the scopes of each server's grammar: GLOBAL, SESSION, LOCAL, PERSIST and PERSIST_ONLY in MySQL, the first three in MariaDB, and SESSION and LOCAL in PostgreSQL, where they also precede SNAPSHOT and SESSION CHARACTERISTICS. Commit and RollbackStatement share TransactionCompletionStatement for the keyword, chain and the new TransactionRelease, and record the END and ABORT spellings in Command enums. END is a statement only with the PostgreSQL dialect and outside BEGIN ... END blocks. The new statements and TransactionCompletionStatement extend ASTNodeAccessImpl, so each parsed statement keeps the node spanning its source. Wire the statements into the deparser, visitors, table finder, feature analysis and validation, adding the rollback and savepoint features and the MySQL and PostgreSQL capabilities. Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 7123d25 commit f457a94

29 files changed

Lines changed: 1912 additions & 255 deletions

‎src/main/java/net/sf/jsqlparser/parser/feature/Feature.java‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
import net.sf.jsqlparser.statement.DescribeStatement;
2424
import net.sf.jsqlparser.statement.ExplainStatement;
2525
import net.sf.jsqlparser.statement.ResetStatement;
26+
import net.sf.jsqlparser.statement.RollbackStatement;
27+
import net.sf.jsqlparser.statement.SavepointStatement;
28+
import net.sf.jsqlparser.statement.SetConstraintsStatement;
2629
import net.sf.jsqlparser.statement.SetStatement;
30+
import net.sf.jsqlparser.statement.SetTransactionStatement;
2731
import net.sf.jsqlparser.statement.ShowColumnsStatement;
2832
import net.sf.jsqlparser.statement.ShowStatement;
2933
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
@@ -664,6 +668,18 @@ public enum Feature {
664668
* @see Commit
665669
*/
666670
commit,
671+
/**
672+
* SQL "ROLLBACK" statement is allowed
673+
*
674+
* @see RollbackStatement
675+
*/
676+
rollback,
677+
/**
678+
* SQL "SAVEPOINT" statement and "ROLLBACK TO [SAVEPOINT]" are allowed
679+
*
680+
* @see SavepointStatement
681+
*/
682+
savepoint,
667683
/** START TRANSACTION and dialect-specific BEGIN transaction statements. */
668684
startTransaction,
669685
/** RELEASE [SAVEPOINT] statements. */
@@ -676,6 +692,30 @@ public enum Feature {
676692
transactionDeferrable,
677693
/** MySQL WITH CONSISTENT SNAPSHOT transaction characteristic. */
678694
transactionConsistentSnapshot,
695+
/** MySQL [NO] RELEASE after COMMIT or ROLLBACK. */
696+
transactionRelease,
697+
/** PostgreSQL END and ABORT, the alternative spellings of COMMIT and ROLLBACK. */
698+
transactionEndAbort,
699+
/**
700+
* SET TRANSACTION characteristics
701+
*
702+
* @see SetTransactionStatement
703+
*/
704+
setTransaction,
705+
/** MySQL SET GLOBAL TRANSACTION. */
706+
setTransactionGlobal,
707+
/** MySQL SET PERSIST TRANSACTION and SET PERSIST_ONLY TRANSACTION. */
708+
setTransactionPersist,
709+
/** PostgreSQL SET SESSION CHARACTERISTICS AS TRANSACTION. */
710+
setTransactionSessionCharacteristics,
711+
/** PostgreSQL SET TRANSACTION SNAPSHOT. */
712+
setTransactionSnapshot,
713+
/**
714+
* SET CONSTRAINTS {ALL | names} {DEFERRED | IMMEDIATE}
715+
*
716+
* @see SetConstraintsStatement
717+
*/
718+
setConstraints,
679719
/**
680720
* SQL "COMMENT ON" statement is allowed
681721
*

‎src/main/java/net/sf/jsqlparser/statement/Commit.java‎

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,57 +9,59 @@
99
*/
1010
package net.sf.jsqlparser.statement;
1111

12-
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
12+
import java.util.Objects;
1313

14-
public class Commit extends ASTNodeAccessImpl implements Statement {
15-
private TransactionKeyword keyword;
16-
private TransactionChain chain;
17-
18-
public TransactionKeyword getKeyword() {
19-
return keyword;
14+
/** COMMIT, or its PostgreSQL spelling END. */
15+
public class Commit extends TransactionCompletionStatement {
16+
/** The keyword that starts the statement; both commit the current transaction. */
17+
public enum Command {
18+
COMMIT, END
2019
}
2120

22-
public void setKeyword(TransactionKeyword keyword) {
23-
this.keyword = keyword;
21+
private Command command = Command.COMMIT;
22+
23+
public Command getCommand() {
24+
return command;
2425
}
2526

26-
public Commit withKeyword(TransactionKeyword keyword) {
27-
setKeyword(keyword);
28-
return this;
27+
public void setCommand(Command command) {
28+
this.command = Objects.requireNonNull(command, "command");
2929
}
3030

31-
public TransactionChain getChain() {
32-
return chain;
31+
public Commit withCommand(Command command) {
32+
setCommand(command);
33+
return this;
3334
}
3435

35-
public void setChain(TransactionChain chain) {
36-
this.chain = chain;
36+
public Commit withKeyword(TransactionKeyword keyword) {
37+
setKeyword(keyword);
38+
return this;
3739
}
3840

3941
public Commit withChain(TransactionChain chain) {
4042
setChain(chain);
4143
return this;
4244
}
4345

46+
public Commit withRelease(TransactionRelease release) {
47+
setRelease(release);
48+
return this;
49+
}
4450

4551
@Override
46-
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
47-
return statementVisitor.visit(this, context);
52+
public boolean isPostgresSpelling() {
53+
return command == Command.END;
4854
}
4955

50-
public StringBuilder appendTo(StringBuilder builder) {
51-
builder.append("COMMIT");
52-
if (keyword != null) {
53-
builder.append(' ').append(keyword);
54-
}
55-
if (chain != null) {
56-
builder.append(' ').append(chain);
57-
}
58-
return builder;
56+
@Override
57+
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
58+
return statementVisitor.visit(this, context);
5959
}
6060

6161
@Override
62-
public String toString() {
63-
return appendTo(new StringBuilder()).toString();
62+
public StringBuilder appendTo(StringBuilder builder) {
63+
builder.append(command);
64+
appendKeyword(builder);
65+
return appendCompletion(builder);
6466
}
6567
}

‎src/main/java/net/sf/jsqlparser/statement/RollbackStatement.java‎

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,53 +25,61 @@
2525

2626
package net.sf.jsqlparser.statement;
2727

28-
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
28+
import java.util.Objects;
2929

3030
/**
31+
* ROLLBACK, or its PostgreSQL spelling ABORT.
32+
*
3133
* @author are
3234
*/
33-
public class RollbackStatement extends ASTNodeAccessImpl implements Statement {
34-
private TransactionKeyword keyword;
35-
private TransactionChain chain;
35+
public class RollbackStatement extends TransactionCompletionStatement {
36+
/** The keyword that starts the statement; ABORT has no savepoint or FORCE form. */
37+
public enum Command {
38+
ROLLBACK, ABORT
39+
}
40+
41+
private Command command = Command.ROLLBACK;
3642
private boolean usingSavepointKeyword = false;
3743
private String savepointName = null;
3844
private String forceDistributedTransactionIdentifier = null;
3945

40-
public TransactionKeyword getKeyword() {
41-
return keyword;
46+
public Command getCommand() {
47+
return command;
4248
}
4349

44-
public void setKeyword(TransactionKeyword keyword) {
45-
this.keyword = keyword;
50+
public void setCommand(Command command) {
51+
this.command = Objects.requireNonNull(command, "command");
4652
}
4753

48-
public RollbackStatement withKeyword(TransactionKeyword keyword) {
49-
setKeyword(keyword);
54+
public RollbackStatement withCommand(Command command) {
55+
setCommand(command);
5056
return this;
5157
}
5258

53-
public TransactionChain getChain() {
54-
return chain;
55-
}
56-
57-
public void setChain(TransactionChain chain) {
58-
this.chain = chain;
59+
public RollbackStatement withKeyword(TransactionKeyword keyword) {
60+
setKeyword(keyword);
61+
return this;
5962
}
6063

6164
public RollbackStatement withChain(TransactionChain chain) {
6265
setChain(chain);
6366
return this;
6467
}
6568

69+
public RollbackStatement withRelease(TransactionRelease release) {
70+
setRelease(release);
71+
return this;
72+
}
73+
6674
public boolean isUsingWorkKeyword() {
67-
return keyword == TransactionKeyword.WORK;
75+
return getKeyword() == TransactionKeyword.WORK;
6876
}
6977

7078
public void setUsingWorkKeyword(boolean usingWorkKeyword) {
7179
if (usingWorkKeyword) {
72-
keyword = TransactionKeyword.WORK;
73-
} else if (keyword == TransactionKeyword.WORK) {
74-
keyword = null;
80+
setKeyword(TransactionKeyword.WORK);
81+
} else if (getKeyword() == TransactionKeyword.WORK) {
82+
setKeyword(null);
7583
}
7684
}
7785

@@ -121,27 +129,23 @@ public RollbackStatement withForceDistributedTransactionIdentifier(
121129
return this;
122130
}
123131

132+
@Override
133+
public boolean isPostgresSpelling() {
134+
return command == Command.ABORT;
135+
}
136+
137+
@Override
124138
public StringBuilder appendTo(StringBuilder builder) {
125-
builder.append("ROLLBACK");
126-
if (keyword != null) {
127-
builder.append(' ').append(keyword);
128-
}
139+
builder.append(command);
140+
appendKeyword(builder);
129141
if (savepointName != null && !savepointName.trim().isEmpty()) {
130142
builder.append(" TO ").append(usingSavepointKeyword ? "SAVEPOINT " : "")
131143
.append(savepointName);
132144
} else if (forceDistributedTransactionIdentifier != null
133145
&& !forceDistributedTransactionIdentifier.trim().isEmpty()) {
134146
builder.append(" FORCE ").append(forceDistributedTransactionIdentifier);
135147
}
136-
if (chain != null) {
137-
builder.append(' ').append(chain);
138-
}
139-
return builder;
140-
}
141-
142-
@Override
143-
public String toString() {
144-
return appendTo(new StringBuilder()).toString();
148+
return appendCompletion(builder);
145149
}
146150

147151
@Override
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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.statement;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.Objects;
15+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
16+
import net.sf.jsqlparser.statement.create.table.ConstraintAttributes;
17+
18+
/**
19+
* {@code SET CONSTRAINTS {ALL | name [, ...]} {DEFERRED | IMMEDIATE}}, which changes when the
20+
* deferrable constraints of the current transaction are checked.
21+
*/
22+
public class SetConstraintsStatement extends ASTNodeAccessImpl implements Statement {
23+
private final List<String> constraintNames = new ArrayList<>();
24+
private ConstraintAttributes.Initially timing;
25+
26+
public SetConstraintsStatement() {
27+
this(ConstraintAttributes.Initially.IMMEDIATE);
28+
}
29+
30+
public SetConstraintsStatement(ConstraintAttributes.Initially timing) {
31+
setTiming(timing);
32+
}
33+
34+
/**
35+
* Mutable list of the named constraints, each possibly schema-qualified and kept as written. An
36+
* empty list means ALL.
37+
*/
38+
public List<String> getConstraintNames() {
39+
return constraintNames;
40+
}
41+
42+
public SetConstraintsStatement addConstraintName(String name) {
43+
constraintNames.add(Objects.requireNonNull(name, "name"));
44+
return this;
45+
}
46+
47+
/** Whether the statement applies to all deferrable constraints. */
48+
public boolean isAll() {
49+
return constraintNames.isEmpty();
50+
}
51+
52+
/** The check timing, shared with the INITIALLY clause of constraint definitions. */
53+
public ConstraintAttributes.Initially getTiming() {
54+
return timing;
55+
}
56+
57+
public void setTiming(ConstraintAttributes.Initially timing) {
58+
this.timing = Objects.requireNonNull(timing, "timing");
59+
}
60+
61+
public SetConstraintsStatement withTiming(ConstraintAttributes.Initially timing) {
62+
setTiming(timing);
63+
return this;
64+
}
65+
66+
public StringBuilder appendTo(StringBuilder builder) {
67+
builder.append("SET CONSTRAINTS ");
68+
if (isAll()) {
69+
builder.append("ALL");
70+
} else {
71+
builder.append(String.join(", ", constraintNames));
72+
}
73+
return builder.append(' ').append(timing);
74+
}
75+
76+
@Override
77+
public String toString() {
78+
return appendTo(new StringBuilder()).toString();
79+
}
80+
81+
@Override
82+
public <T, S> T accept(StatementVisitor<T> visitor, S context) {
83+
return visitor.visit(this, context);
84+
}
85+
}

0 commit comments

Comments
 (0)