Skip to content

Commit 964513d

Browse files
committed
Add support for LOCK-Statements
1 parent 157988d commit 964513d

File tree

11 files changed

+282
-0
lines changed

11 files changed

+282
-0
lines changed

src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
exports net.sf.jsqlparser.statement.grant;
4141
exports net.sf.jsqlparser.statement.imprt;
4242
exports net.sf.jsqlparser.statement.insert;
43+
exports net.sf.jsqlparser.statement.lock;
4344
exports net.sf.jsqlparser.statement.merge;
4445
exports net.sf.jsqlparser.statement.piped;
4546
exports net.sf.jsqlparser.statement.refresh;

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import net.sf.jsqlparser.statement.imprt.Import;
3333
import net.sf.jsqlparser.statement.insert.Insert;
3434
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
35+
import net.sf.jsqlparser.statement.lock.LockStatement;
3536
import net.sf.jsqlparser.statement.merge.Merge;
3637
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
3738
import net.sf.jsqlparser.statement.select.Select;
@@ -343,4 +344,11 @@ default void visit(Import imprt) {
343344
default void visit(Export export) {
344345
this.visit(export, null);
345346
}
347+
348+
<S> T visit(LockStatement lock, S context);
349+
350+
default void visit(LockStatement lock) {
351+
this.visit(lock, null);
352+
}
353+
346354
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import net.sf.jsqlparser.statement.insert.Insert;
3838
import net.sf.jsqlparser.statement.insert.InsertConflictAction;
3939
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
40+
import net.sf.jsqlparser.statement.lock.LockStatement;
4041
import net.sf.jsqlparser.statement.merge.Merge;
4142
import net.sf.jsqlparser.statement.merge.MergeOperationVisitor;
4243
import net.sf.jsqlparser.statement.merge.MergeOperationVisitorAdapter;
@@ -289,6 +290,12 @@ public <S> T visit(Execute execute, S context) {
289290
return null;
290291
}
291292

293+
@Override
294+
public <S> T visit(LockStatement lock, S context) {
295+
296+
return null;
297+
}
298+
292299
@Override
293300
public <S> T visit(SetStatement set, S context) {
294301

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*--- (C) 1999-2019 Techniker Krankenkasse ---*/
2+
3+
package net.sf.jsqlparser.statement.lock;
4+
5+
/**
6+
* Describes the LockMode of a LOCK TABLE-Statement.
7+
*/
8+
public enum LockMode {
9+
// These two modes are more common
10+
Share("SHARE"),
11+
Exclusive("EXCLUSIVE"),
12+
13+
// These are only Oracle specific, as far as I know
14+
RowShare("ROW SHARE"),
15+
RowExclusive("ROW EXCLUSIVE"),
16+
ShareUpdate("SHARE UPDATE"),
17+
ShareRowExclusive("SHARE ROW EXCLUSIVE");
18+
19+
private final String value;
20+
21+
LockMode(String value) {
22+
this.value = value;
23+
}
24+
25+
public String getValue() {
26+
return value;
27+
}
28+
29+
}
30+
31+
/*--- Formatiert nach TK Code Konventionen vom 05.03.2002 ---*/
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*--- (C) 1999-2019 Techniker Krankenkasse ---*/
2+
3+
package net.sf.jsqlparser.statement.lock;
4+
5+
import net.sf.jsqlparser.schema.Table;
6+
import net.sf.jsqlparser.statement.Statement;
7+
import net.sf.jsqlparser.statement.StatementVisitor;
8+
9+
/**
10+
* Statement to Lock a specific table.<br>
11+
* Example:<br>
12+
* LOCK TABLE &lt;TABLE&gt; IN EXCLUSIVE MODE;<br>
13+
* <br>
14+
*/
15+
public class LockStatement implements Statement {
16+
17+
private Table table;
18+
private LockMode lockMode;
19+
private boolean noWait;
20+
private Long waitSeconds;
21+
22+
public LockStatement(Table table, LockMode lockMode, boolean noWait, Long waitSeconds) {
23+
this.table = table;
24+
this.lockMode = lockMode;
25+
this.noWait = noWait;
26+
this.waitSeconds = waitSeconds;
27+
}
28+
29+
private void checkValidState() {
30+
if (noWait && waitSeconds != null) {
31+
throw new IllegalStateException("A LOCK statement cannot have NOWAIT and WAIT at the same time");
32+
}
33+
}
34+
35+
public Table getTable() {
36+
return table;
37+
}
38+
39+
public void setTable(Table table) {
40+
this.table = table;
41+
}
42+
43+
public LockMode getLockMode() {
44+
return lockMode;
45+
}
46+
47+
public void setLockMode(LockMode lockMode) {
48+
this.lockMode = lockMode;
49+
}
50+
51+
public boolean isNoWait() {
52+
return noWait;
53+
}
54+
55+
/**
56+
* Sets the NOWAIT-Flag. Clears a WAIT-Timeout if one was set before.
57+
*
58+
* @param noWait The new value of the NOWAIT-Flag
59+
*/
60+
public void setNoWait(boolean noWait) {
61+
this.waitSeconds = null;
62+
this.noWait = noWait;
63+
checkValidState();
64+
}
65+
66+
public boolean isWait() {
67+
return waitSeconds != null;
68+
}
69+
70+
/**
71+
* Sets the WAIT-Timeout. If this value is set, the Statement is rendered with WAIT &lt;timeoutSeconds&gt;
72+
*
73+
* @param waitSeconds The number of seconds for the WAIT timeout
74+
*/
75+
public void setWaitSeconds(long waitSeconds) {
76+
this.noWait = false;
77+
this.waitSeconds = waitSeconds;
78+
checkValidState();
79+
}
80+
81+
@Override
82+
public String toString() {
83+
return
84+
"LOCK TABLE "
85+
+ table.getFullyQualifiedName()
86+
+ " IN "
87+
+ lockMode.getValue()
88+
+ " MODE"
89+
+ (noWait ? " NOWAIT" : "")
90+
+ (waitSeconds != null ? " WAIT " + waitSeconds : "");
91+
}
92+
93+
@Override
94+
public <T, S> T accept(StatementVisitor<T> statementVisitor, S context) {
95+
return statementVisitor.visit(this, context);
96+
}
97+
98+
public long getWaitTimeout() {
99+
return waitSeconds;
100+
}
101+
}
102+
103+
/*--- Formatiert nach TK Code Konventionen vom 05.03.2002 ---*/

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
import net.sf.jsqlparser.statement.imprt.Import;
163163
import net.sf.jsqlparser.statement.insert.Insert;
164164
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
165+
import net.sf.jsqlparser.statement.lock.LockStatement;
165166
import net.sf.jsqlparser.statement.merge.Merge;
166167
import net.sf.jsqlparser.statement.piped.FromQuery;
167168
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
@@ -1874,4 +1875,15 @@ public <S> Void visit(Export export, S context) {
18741875
public void visit(Export export) {
18751876
StatementVisitor.super.visit(export);
18761877
}
1878+
1879+
@Override
1880+
public <S> Void visit(LockStatement lock, S context) {
1881+
lock.getTable().accept(this);
1882+
return null;
1883+
}
1884+
1885+
@Override
1886+
public void visit(LockStatement lock) {
1887+
StatementVisitor.super.visit(lock);
1888+
}
18771889
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import net.sf.jsqlparser.statement.imprt.Import;
5858
import net.sf.jsqlparser.statement.insert.Insert;
5959
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
60+
import net.sf.jsqlparser.statement.lock.LockStatement;
6061
import net.sf.jsqlparser.statement.merge.Merge;
6162
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
6263
import net.sf.jsqlparser.statement.select.Select;
@@ -513,4 +514,10 @@ public <S> StringBuilder visit(Export export, S context) {
513514
builder.append(export.toString());
514515
return builder;
515516
}
517+
518+
@Override
519+
public <S> StringBuilder visit(LockStatement lock, S context) {
520+
builder.append(lock.toString());
521+
return builder;
522+
}
516523
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import net.sf.jsqlparser.statement.imprt.Import;
5656
import net.sf.jsqlparser.statement.insert.Insert;
5757
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
58+
import net.sf.jsqlparser.statement.lock.LockStatement;
5859
import net.sf.jsqlparser.statement.merge.Merge;
5960
import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement;
6061
import net.sf.jsqlparser.statement.select.Select;
@@ -399,6 +400,12 @@ public <S> Void visit(Export export, S context) {
399400
return null;
400401
}
401402

403+
@Override
404+
public <S> Void visit(LockStatement lock, S context) {
405+
// TODO: not yet implemented
406+
return null;
407+
}
408+
402409
public void visit(CreateIndex createIndex) {
403410
visit(createIndex, null);
404411
}

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import net.sf.jsqlparser.statement.merge.*;
7070
import net.sf.jsqlparser.statement.grant.*;
7171
import net.sf.jsqlparser.statement.imprt.*;
7272
import net.sf.jsqlparser.statement.export.*;
73+
import net.sf.jsqlparser.statement.lock.*;
7374
import java.util.*;
7475
import java.util.AbstractMap.SimpleEntry;
7576
import net.sf.jsqlparser.statement.select.SetOperationList.SetOperationType;
@@ -364,6 +365,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
364365
| <K_EXCLUDE: "EXCLUDE">
365366
| <K_EXCLUDES: "EXCLUDES"> /* Salesforce SOQL */
366367
| <K_EXCLUDING: "EXCLUDING">
368+
| <K_EXCLUSIVE: "EXCLUSIVE">
367369
| <K_EXEC: "EXEC">
368370
| <K_EXECUTE: "EXECUTE">
369371
| <K_EXISTS:"EXISTS">
@@ -476,6 +478,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
476478
| <K_MIN:"MIN">
477479
| <K_MINUS:"MINUS">
478480
| <K_MINVALUE:"MINVALUE">
481+
| <K_MODE: "MODE">
479482
| <K_MODIFY: "MODIFY">
480483
| <K_MOVEMENT: "MOVEMENT">
481484
| <K_NAMES:"NAMES">
@@ -1031,6 +1034,8 @@ Statement SingleStatement() :
10311034
|
10321035
stm = SessionStatement()
10331036
|
1037+
stm = LockStatement()
1038+
|
10341039
LOOKAHEAD({ Dialect.EXASOL.name().equals(getAsString(Feature.dialect)) }) ( stm = Import() | stm = Export() )
10351040
)
10361041
{ return stm; }
@@ -1208,6 +1213,30 @@ List<String> error_skipto(int kind) {
12081213
return tokenImages;
12091214
}
12101215

1216+
LockStatement LockStatement(): {
1217+
Table table;
1218+
boolean noWait = false;
1219+
LockMode lockMode;
1220+
Token waitSecondsToken = null;
1221+
Long waitSeconds = null;
1222+
} {
1223+
<K_LOCK> <K_TABLE> table = Table() <K_IN>
1224+
(
1225+
LOOKAHEAD(2) ( <K_ROW> <K_SHARE> { lockMode = LockMode.RowShare; } ) |
1226+
( <K_ROW> <K_EXCLUSIVE> { lockMode = LockMode.RowExclusive; } ) |
1227+
LOOKAHEAD(2) ( <K_SHARE> <K_ROW> <K_EXCLUSIVE> { lockMode = LockMode.ShareRowExclusive; } ) |
1228+
LOOKAHEAD(2) ( <K_SHARE> <K_UPDATE> { lockMode = LockMode.ShareUpdate; } ) |
1229+
( <K_SHARE> { lockMode = LockMode.Share; } ) |
1230+
( <K_EXCLUSIVE > { lockMode = LockMode.Exclusive; } )
1231+
)
1232+
<K_MODE>
1233+
[ <K_NOWAIT> { noWait = true; } | <K_WAIT> waitSecondsToken = <S_LONG> { waitSeconds = Long.valueOf(waitSecondsToken.image); } ]
1234+
1235+
{
1236+
return new LockStatement(table, lockMode, noWait, waitSeconds);
1237+
}
1238+
}
1239+
12111240
LikeClause LikeClause(): {
12121241
LikeClause likeClause = new LikeClause();
12131242
Table table;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package net.sf.jsqlparser.statement.lock;
2+
3+
import net.sf.jsqlparser.JSQLParserException;
4+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
5+
import net.sf.jsqlparser.statement.Statement;
6+
import net.sf.jsqlparser.test.TestUtils;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.api.parallel.Execution;
9+
import org.junit.jupiter.api.parallel.ExecutionMode;
10+
import org.junit.jupiter.params.ParameterizedTest;
11+
import org.junit.jupiter.params.provider.ValueSource;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
15+
@Execution(ExecutionMode.CONCURRENT)
16+
public class LockTest {
17+
18+
@ParameterizedTest
19+
@ValueSource(strings = {
20+
"LOCK TABLE a IN EXCLUSIVE MODE",
21+
"LOCK TABLE a IN ROW EXCLUSIVE MODE",
22+
"LOCK TABLE a IN ROW SHARE MODE",
23+
"LOCK TABLE a IN SHARE MODE",
24+
"LOCK TABLE a IN SHARE UPDATE MODE",
25+
"LOCK TABLE a IN SHARE ROW EXCLUSIVE MODE",
26+
"LOCK TABLE a IN EXCLUSIVE MODE NOWAIT",
27+
"LOCK TABLE a IN SHARE ROW EXCLUSIVE MODE NOWAIT",
28+
"LOCK TABLE a IN SHARE ROW EXCLUSIVE MODE WAIT 10",
29+
"LOCK TABLE a IN EXCLUSIVE MODE WAIT 23",
30+
})
31+
void testLockStatementsParseDeparse(String sqlStr) throws JSQLParserException {
32+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr);
33+
}
34+
35+
@Test
36+
void testLockExclusiveMode() throws JSQLParserException {
37+
String sqlStr = "LOCK TABLE a IN EXCLUSIVE MODE";
38+
Statement statement = CCJSqlParserUtil.parse(sqlStr);
39+
assertInstanceOf(LockStatement.class, statement);
40+
41+
LockStatement ls = (LockStatement)statement;
42+
assertEquals(LockMode.Exclusive, ls.getLockMode());
43+
assertFalse(ls.isNoWait());
44+
}
45+
46+
@Test
47+
void testNoWait() throws JSQLParserException {
48+
String sqlStr = "LOCK TABLE a IN SHARE MODE NOWAIT";
49+
Statement statement = CCJSqlParserUtil.parse(sqlStr);
50+
assertInstanceOf(LockStatement.class, statement);
51+
52+
LockStatement ls = (LockStatement)statement;
53+
assertEquals(LockMode.Share, ls.getLockMode());
54+
assertTrue(ls.isNoWait());
55+
}
56+
57+
@Test
58+
void testWaitTimeout() throws JSQLParserException {
59+
String sqlStr = "LOCK TABLE a IN SHARE MODE WAIT 300";
60+
Statement statement = CCJSqlParserUtil.parse(sqlStr);
61+
assertInstanceOf(LockStatement.class, statement);
62+
63+
LockStatement ls = (LockStatement)statement;
64+
assertEquals(LockMode.Share, ls.getLockMode());
65+
assertTrue(ls.isWait());
66+
assertEquals(300, ls.getWaitTimeout());
67+
}
68+
69+
70+
}

0 commit comments

Comments
 (0)