From 964513d065a511a980ae790b5a16f7e185b1b28d Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Tue, 7 Oct 2025 17:03:33 +0200 Subject: [PATCH 1/6] Add support for LOCK-Statements --- src/main/java/module-info.java | 1 + .../statement/StatementVisitor.java | 8 ++ .../statement/StatementVisitorAdapter.java | 7 ++ .../jsqlparser/statement/lock/LockMode.java | 31 ++++++ .../statement/lock/LockStatement.java | 103 ++++++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 12 ++ .../util/deparser/StatementDeParser.java | 7 ++ .../validator/StatementValidator.java | 7 ++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 29 +++++ .../jsqlparser/statement/lock/LockTest.java | 70 ++++++++++++ .../util/TablesNamesFinderTest.java | 7 ++ 11 files changed, 282 insertions(+) create mode 100644 src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java create mode 100644 src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 6765fe187..6487a6b97 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -40,6 +40,7 @@ exports net.sf.jsqlparser.statement.grant; exports net.sf.jsqlparser.statement.imprt; exports net.sf.jsqlparser.statement.insert; + exports net.sf.jsqlparser.statement.lock; exports net.sf.jsqlparser.statement.merge; exports net.sf.jsqlparser.statement.piped; exports net.sf.jsqlparser.statement.refresh; diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java index 0068e0cf6..4636cbc8e 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -32,6 +32,7 @@ import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; +import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; @@ -343,4 +344,11 @@ default void visit(Import imprt) { default void visit(Export export) { this.visit(export, null); } + + T visit(LockStatement lock, S context); + + default void visit(LockStatement lock) { + this.visit(lock, null); + } + } diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java index f034c1cbb..ce0f5c82c 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -37,6 +37,7 @@ import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.insert.InsertConflictAction; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; +import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.merge.MergeOperationVisitor; import net.sf.jsqlparser.statement.merge.MergeOperationVisitorAdapter; @@ -289,6 +290,12 @@ public T visit(Execute execute, S context) { return null; } + @Override + public T visit(LockStatement lock, S context) { + + return null; + } + @Override public T visit(SetStatement set, S context) { diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java new file mode 100644 index 000000000..11cfb3ef9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java @@ -0,0 +1,31 @@ +/*--- (C) 1999-2019 Techniker Krankenkasse ---*/ + +package net.sf.jsqlparser.statement.lock; + +/** + * Describes the LockMode of a LOCK TABLE-Statement. + */ +public enum LockMode { + // These two modes are more common + Share("SHARE"), + Exclusive("EXCLUSIVE"), + + // These are only Oracle specific, as far as I know + RowShare("ROW SHARE"), + RowExclusive("ROW EXCLUSIVE"), + ShareUpdate("SHARE UPDATE"), + ShareRowExclusive("SHARE ROW EXCLUSIVE"); + + private final String value; + + LockMode(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + +} + +/*--- Formatiert nach TK Code Konventionen vom 05.03.2002 ---*/ diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java new file mode 100644 index 000000000..dee6c3a42 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java @@ -0,0 +1,103 @@ +/*--- (C) 1999-2019 Techniker Krankenkasse ---*/ + +package net.sf.jsqlparser.statement.lock; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * Statement to Lock a specific table.
+ * Example:
+ * LOCK TABLE <TABLE> IN EXCLUSIVE MODE;
+ *
+ */ +public class LockStatement implements Statement { + + private Table table; + private LockMode lockMode; + private boolean noWait; + private Long waitSeconds; + + public LockStatement(Table table, LockMode lockMode, boolean noWait, Long waitSeconds) { + this.table = table; + this.lockMode = lockMode; + this.noWait = noWait; + this.waitSeconds = waitSeconds; + } + + private void checkValidState() { + if (noWait && waitSeconds != null) { + throw new IllegalStateException("A LOCK statement cannot have NOWAIT and WAIT at the same time"); + } + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public LockMode getLockMode() { + return lockMode; + } + + public void setLockMode(LockMode lockMode) { + this.lockMode = lockMode; + } + + public boolean isNoWait() { + return noWait; + } + + /** + * Sets the NOWAIT-Flag. Clears a WAIT-Timeout if one was set before. + * + * @param noWait The new value of the NOWAIT-Flag + */ + public void setNoWait(boolean noWait) { + this.waitSeconds = null; + this.noWait = noWait; + checkValidState(); + } + + public boolean isWait() { + return waitSeconds != null; + } + + /** + * Sets the WAIT-Timeout. If this value is set, the Statement is rendered with WAIT <timeoutSeconds> + * + * @param waitSeconds The number of seconds for the WAIT timeout + */ + public void setWaitSeconds(long waitSeconds) { + this.noWait = false; + this.waitSeconds = waitSeconds; + checkValidState(); + } + + @Override + public String toString() { + return + "LOCK TABLE " + + table.getFullyQualifiedName() + + " IN " + + lockMode.getValue() + + " MODE" + + (noWait ? " NOWAIT" : "") + + (waitSeconds != null ? " WAIT " + waitSeconds : ""); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public long getWaitTimeout() { + return waitSeconds; + } +} + +/*--- Formatiert nach TK Code Konventionen vom 05.03.2002 ---*/ diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 26568d644..0f2414dba 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -162,6 +162,7 @@ import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; +import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.piped.FromQuery; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; @@ -1874,4 +1875,15 @@ public Void visit(Export export, S context) { public void visit(Export export) { StatementVisitor.super.visit(export); } + + @Override + public Void visit(LockStatement lock, S context) { + lock.getTable().accept(this); + return null; + } + + @Override + public void visit(LockStatement lock) { + StatementVisitor.super.visit(lock); + } } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index ccfc0a92b..a27aee7af 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -57,6 +57,7 @@ import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; +import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; @@ -513,4 +514,10 @@ public StringBuilder visit(Export export, S context) { builder.append(export.toString()); return builder; } + + @Override + public StringBuilder visit(LockStatement lock, S context) { + builder.append(lock.toString()); + return builder; + } } diff --git a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java index af4ec9256..e6c42ab48 100644 --- a/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java +++ b/src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java @@ -55,6 +55,7 @@ import net.sf.jsqlparser.statement.imprt.Import; import net.sf.jsqlparser.statement.insert.Insert; import net.sf.jsqlparser.statement.insert.ParenthesedInsert; +import net.sf.jsqlparser.statement.lock.LockStatement; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; import net.sf.jsqlparser.statement.select.Select; @@ -399,6 +400,12 @@ public Void visit(Export export, S context) { return null; } + @Override + public Void visit(LockStatement lock, S context) { + // TODO: not yet implemented + return null; + } + public void visit(CreateIndex createIndex) { visit(createIndex, null); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 9998043a7..194888c06 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -70,6 +70,7 @@ import net.sf.jsqlparser.statement.merge.*; import net.sf.jsqlparser.statement.grant.*; import net.sf.jsqlparser.statement.imprt.*; import net.sf.jsqlparser.statement.export.*; +import net.sf.jsqlparser.statement.lock.*; import java.util.*; import java.util.AbstractMap.SimpleEntry; import net.sf.jsqlparser.statement.select.SetOperationList.SetOperationType; @@ -364,6 +365,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | /* Salesforce SOQL */ | +| | | | @@ -476,6 +478,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -1031,6 +1034,8 @@ Statement SingleStatement() : | stm = SessionStatement() | + stm = LockStatement() + | LOOKAHEAD({ Dialect.EXASOL.name().equals(getAsString(Feature.dialect)) }) ( stm = Import() | stm = Export() ) ) { return stm; } @@ -1208,6 +1213,30 @@ List error_skipto(int kind) { return tokenImages; } +LockStatement LockStatement(): { + Table table; + boolean noWait = false; + LockMode lockMode; + Token waitSecondsToken = null; + Long waitSeconds = null; +} { + table = Table() + ( + LOOKAHEAD(2) ( { lockMode = LockMode.RowShare; } ) | + ( { lockMode = LockMode.RowExclusive; } ) | + LOOKAHEAD(2) ( { lockMode = LockMode.ShareRowExclusive; } ) | + LOOKAHEAD(2) ( { lockMode = LockMode.ShareUpdate; } ) | + ( { lockMode = LockMode.Share; } ) | + ( { lockMode = LockMode.Exclusive; } ) + ) + + [ { noWait = true; } | waitSecondsToken = { waitSeconds = Long.valueOf(waitSecondsToken.image); } ] + + { + return new LockStatement(table, lockMode, noWait, waitSeconds); + } +} + LikeClause LikeClause(): { LikeClause likeClause = new LikeClause(); Table table; diff --git a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java new file mode 100644 index 000000000..8a4797b96 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java @@ -0,0 +1,70 @@ +package net.sf.jsqlparser.statement.lock; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.*; + +@Execution(ExecutionMode.CONCURRENT) +public class LockTest { + + @ParameterizedTest + @ValueSource(strings = { + "LOCK TABLE a IN EXCLUSIVE MODE", + "LOCK TABLE a IN ROW EXCLUSIVE MODE", + "LOCK TABLE a IN ROW SHARE MODE", + "LOCK TABLE a IN SHARE MODE", + "LOCK TABLE a IN SHARE UPDATE MODE", + "LOCK TABLE a IN SHARE ROW EXCLUSIVE MODE", + "LOCK TABLE a IN EXCLUSIVE MODE NOWAIT", + "LOCK TABLE a IN SHARE ROW EXCLUSIVE MODE NOWAIT", + "LOCK TABLE a IN SHARE ROW EXCLUSIVE MODE WAIT 10", + "LOCK TABLE a IN EXCLUSIVE MODE WAIT 23", + }) + void testLockStatementsParseDeparse(String sqlStr) throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr); + } + + @Test + void testLockExclusiveMode() throws JSQLParserException { + String sqlStr = "LOCK TABLE a IN EXCLUSIVE MODE"; + Statement statement = CCJSqlParserUtil.parse(sqlStr); + assertInstanceOf(LockStatement.class, statement); + + LockStatement ls = (LockStatement)statement; + assertEquals(LockMode.Exclusive, ls.getLockMode()); + assertFalse(ls.isNoWait()); + } + + @Test + void testNoWait() throws JSQLParserException { + String sqlStr = "LOCK TABLE a IN SHARE MODE NOWAIT"; + Statement statement = CCJSqlParserUtil.parse(sqlStr); + assertInstanceOf(LockStatement.class, statement); + + LockStatement ls = (LockStatement)statement; + assertEquals(LockMode.Share, ls.getLockMode()); + assertTrue(ls.isNoWait()); + } + + @Test + void testWaitTimeout() throws JSQLParserException { + String sqlStr = "LOCK TABLE a IN SHARE MODE WAIT 300"; + Statement statement = CCJSqlParserUtil.parse(sqlStr); + assertInstanceOf(LockStatement.class, statement); + + LockStatement ls = (LockStatement)statement; + assertEquals(LockMode.Share, ls.getLockMode()); + assertTrue(ls.isWait()); + assertEquals(300, ls.getWaitTimeout()); + } + + +} diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index a40d52509..c67cc240f 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -481,6 +481,13 @@ void testOtherSources() throws JSQLParserException { assertThat(tables).containsExactly("Datetimes"); } + @Test + void testLockStatement() throws JSQLParserException { + String sqlStr = "LOCK TABLE A IN EXCLUSIVE MODE"; + Set tables = TablesNamesFinder.findTablesOrOtherSources(sqlStr); + assertThat(tables).containsExactly("A"); + } + @Test void testSubqueryAliasesIssue1987() throws JSQLParserException { String sqlStr = "select * from (select * from a) as a1, b;"; From 973648c961e1d00810e997a44c4572d388be4f1f Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Tue, 7 Oct 2025 17:18:12 +0200 Subject: [PATCH 2/6] Fixed comments --- .../java/net/sf/jsqlparser/statement/lock/LockMode.java | 8 ++------ .../net/sf/jsqlparser/statement/lock/LockStatement.java | 4 ---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java index 11cfb3ef9..c103414dc 100644 --- a/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java @@ -1,5 +1,3 @@ -/*--- (C) 1999-2019 Techniker Krankenkasse ---*/ - package net.sf.jsqlparser.statement.lock; /** @@ -10,7 +8,7 @@ public enum LockMode { Share("SHARE"), Exclusive("EXCLUSIVE"), - // These are only Oracle specific, as far as I know + // These are Oracle specific, as far as I know RowShare("ROW SHARE"), RowExclusive("ROW EXCLUSIVE"), ShareUpdate("SHARE UPDATE"), @@ -26,6 +24,4 @@ public String getValue() { return value; } -} - -/*--- Formatiert nach TK Code Konventionen vom 05.03.2002 ---*/ +} \ No newline at end of file diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java index dee6c3a42..b60652949 100644 --- a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java @@ -1,5 +1,3 @@ -/*--- (C) 1999-2019 Techniker Krankenkasse ---*/ - package net.sf.jsqlparser.statement.lock; import net.sf.jsqlparser.schema.Table; @@ -99,5 +97,3 @@ public long getWaitTimeout() { return waitSeconds; } } - -/*--- Formatiert nach TK Code Konventionen vom 05.03.2002 ---*/ From 00b65455aceeecfa36244df9b54956b3ffc003c7 Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Tue, 7 Oct 2025 17:30:03 +0200 Subject: [PATCH 3/6] ran updateKeywords --- src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 194888c06..83bcacaa8 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -3267,7 +3267,7 @@ String RelObjectNameWithoutValue() : { Token tk = null; } { ( tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= - | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="AGGREGATE" | tk="ALGORITHM" | tk="ALIGN" | tk="ALTER" | tk="ALWAYS" | tk="ANALYZE" | tk="APPEND_ONLY" | tk="APPLY" | tk="APPROXIMATE" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="ASYMMETRIC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="AUTO_INCREMENT" | tk="AZURE" | tk="BASE64" | tk="BEFORE" | tk="BEGIN" | tk="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOBSTORAGE" | tk="BLOCK" | tk="BOOLEAN" | tk="BRANCH" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CERTIFICATE" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="CLOUD" | tk="COALESCE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMENTS" | tk="COMMIT" | tk="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="CORRESPONDING" | tk="COSTS" | tk="COUNT" | tk="CREATED" | tk="CS" | tk="CYCLE" | tk="DATA" | tk="DATABASE" | tk="DATETIME" | tk="DBA_RECYCLEBIN" | tk="DDL" | tk="DECLARE" | tk="DEFAULTS" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DELIMIT" | tk="DELIMITER" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCARD" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DRIVER" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="ENCODING" | tk="ENCRYPTION" | tk="END" | tk="ENFORCED" | tk="ENGINE" | tk="ERROR" | tk="ESCAPE" | tk="EXA" | tk="EXCHANGE" | tk="EXCLUDE" | tk="EXCLUDING" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXPORT" | tk="EXTENDED" | tk="EXTRACT" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GRANT" | tk="GROUP_CONCAT" | tk="GUARD" | tk="HASH" | tk="HIGH" | tk="HIGH_PRIORITY" | tk="HISTORY" | tk="HOPPING" | tk="IDENTIFIED" | tk="IDENTITY" | tk="INCLUDE" | tk="INCLUDE_NULL_VALUES" | tk="INCLUDING" | tk="INCREMENT" | tk="INDEX" | tk="INFORMATION" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="INVERSE" | tk="INVISIBLE" | tk="ISNULL" | tk="JDBC" | tk="JSON" | tk="JSON_ARRAY" | tk="JSON_ARRAYAGG" | tk="JSON_OBJECT" | tk="JSON_OBJECTAGG" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="KEY_BLOCK_SIZE" | tk="KILL" | tk="LAST" | tk="LEADING" | tk="LESS" | tk="LINK" | tk="LOCAL" | tk="LOCK" | tk="LOCKED" | tk="LOG" | tk="LONGTEXT" | tk="LOOP" | tk="LOW" | tk="LOW_PRIORITY" | tk="LTRIM" | tk="MATCH" | tk="MATCHED" | tk="MATCH_ALL" | tk="MATCH_ANY" | tk="MATCH_PHRASE" | tk="MATCH_PHRASE_PREFIX" | tk="MATCH_REGEXP" | tk="MATERIALIZED" | tk="MAX" | tk="MAXVALUE" | tk="MEDIUMTEXT" | tk="MEMBER" | tk="MERGE" | tk="MIN" | tk="MINVALUE" | tk="MODIFY" | tk="MOVEMENT" | tk="NAME" | tk="NAMES" | tk="NEVER" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NONE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="ORA" | tk="ORDINALITY" | tk="OVER" | tk="OVERFLOW" | tk="OVERLAPS" | tk="OVERRIDING" | tk="OVERWRITE" | tk="PADDING" | tk="PARALLEL" | tk="PARENT" | tk="PARSER" | tk="PARTITION" | tk="PARTITIONING" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PLAN" | tk="PLUS" | tk="PRECEDING" | tk="PRIMARY" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="REBUILD" | tk="RECURSIVE" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGEXP" | tk="REGEXP_LIKE" | tk="REGISTER" | tk="REJECT" | tk="REMOTE" | tk="REMOVE" | tk="RENAME" | tk="REORGANIZE" | tk="REPAIR" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESPECT" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="RTRIM" | tk="SAFE_CAST" | tk="SAFE_CONVERT" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SECURE" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="SPATIAL" | tk="STORED" | tk="STREAM" | tk="STRICT" | tk="STRING" | tk="STRUCT" | tk="SUMMARIZE" | tk="SUSPEND" | tk="SWITCH" | tk="SYMMETRIC" | tk="SYNONYM" | tk="SYSTEM" | tk="SYSTEM_TIME" | tk="SYSTEM_TIMESTAMP" | tk="SYSTEM_VERSION" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="TEXT" | tk="THAN" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TIMEZONE" | tk="TINYTEXT" | tk="TO" | tk="TRIGGER" | tk="TRUNCATE" | tk="TRY_CAST" | tk="TRY_CONVERT" | tk="TUMBLING" | tk="TYPE" | tk="UNLIMITED" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VALIDATION" | tk="VERBOSE" | tk="VERSION" | tk="VIEW" | tk="VISIBLE" | tk="VOLATILE" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WITHOUT_ARRAY_WRAPPER" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" ) + | tk="ACTION" | tk="ACTIVE" | tk="ADD" | tk="ADVANCE" | tk="ADVISE" | tk="AGAINST" | tk="AGGREGATE" | tk="ALGORITHM" | tk="ALIGN" | tk="ALTER" | tk="ALWAYS" | tk="ANALYZE" | tk="APPEND_ONLY" | tk="APPLY" | tk="APPROXIMATE" | tk="ARCHIVE" | tk="ARRAY" | tk="ASC" | tk="ASYMMETRIC" | tk="AT" | tk="AUTHORIZATION" | tk="AUTO" | tk="AUTO_INCREMENT" | tk="AZURE" | tk="BASE64" | tk="BEFORE" | tk="BEGIN" | tk="BERNOULLI" | tk="BINARY" | tk="BIT" | tk="BLOBSTORAGE" | tk="BLOCK" | tk="BOOLEAN" | tk="BRANCH" | tk="BROWSE" | tk="BUFFERS" | tk="BY" | tk="BYTE" | tk="BYTES" | tk="CACHE" | tk="CALL" | tk="CASCADE" | tk="CASE" | tk="CAST" | tk="CERTIFICATE" | tk="CHANGE" | tk="CHANGES" | tk="CHAR" | tk="CHARACTER" | tk="CHECKPOINT" | tk="CLOSE" | tk="CLOUD" | tk="COALESCE" | tk="COLLATE" | tk="COLUMN" | tk="COLUMNS" | tk="COMMENT" | tk="COMMENTS" | tk="COMMIT" | tk="CONCURRENTLY" | tk="CONFLICT" | tk="CONSTRAINTS" | tk="CONVERT" | tk="CORRESPONDING" | tk="COSTS" | tk="COUNT" | tk="CREATED" | tk="CS" | tk="CYCLE" | tk="DATA" | tk="DATABASE" | tk="DATETIME" | tk="DBA_RECYCLEBIN" | tk="DDL" | tk="DECLARE" | tk="DEFAULTS" | tk="DEFERRABLE" | tk="DELAYED" | tk="DELETE" | tk="DELIMIT" | tk="DELIMITER" | tk="DESC" | tk="DESCRIBE" | tk="DISABLE" | tk="DISCARD" | tk="DISCONNECT" | tk="DIV" | tk="DML" | tk="DO" | tk="DOMAIN" | tk="DRIVER" | tk="DROP" | tk="DUMP" | tk="DUPLICATE" | tk="ELEMENTS" | tk="EMIT" | tk="ENABLE" | tk="ENCODING" | tk="ENCRYPTION" | tk="END" | tk="ENFORCED" | tk="ENGINE" | tk="ERROR" | tk="ESCAPE" | tk="EXA" | tk="EXCHANGE" | tk="EXCLUDE" | tk="EXCLUDING" | tk="EXCLUSIVE" | tk="EXEC" | tk="EXECUTE" | tk="EXPLAIN" | tk="EXPLICIT" | tk="EXPORT" | tk="EXTENDED" | tk="EXTRACT" | tk="FILTER" | tk="FIRST" | tk="FLUSH" | tk="FN" | tk="FOLLOWING" | tk="FORMAT" | tk="FULLTEXT" | tk="FUNCTION" | tk="GRANT" | tk="GROUP_CONCAT" | tk="GUARD" | tk="HASH" | tk="HIGH" | tk="HIGH_PRIORITY" | tk="HISTORY" | tk="HOPPING" | tk="IDENTIFIED" | tk="IDENTITY" | tk="INCLUDE" | tk="INCLUDE_NULL_VALUES" | tk="INCLUDING" | tk="INCREMENT" | tk="INDEX" | tk="INFORMATION" | tk="INSERT" | tk="INTERLEAVE" | tk="INTERPRET" | tk="INVALIDATE" | tk="INVERSE" | tk="INVISIBLE" | tk="ISNULL" | tk="JDBC" | tk="JSON" | tk="JSON_ARRAY" | tk="JSON_ARRAYAGG" | tk="JSON_OBJECT" | tk="JSON_OBJECTAGG" | tk="KEEP" | tk="KEY" | tk="KEYS" | tk="KEY_BLOCK_SIZE" | tk="KILL" | tk="LAST" | tk="LEADING" | tk="LESS" | tk="LINK" | tk="LOCAL" | tk="LOCK" | tk="LOCKED" | tk="LOG" | tk="LONGTEXT" | tk="LOOP" | tk="LOW" | tk="LOW_PRIORITY" | tk="LTRIM" | tk="MATCH" | tk="MATCHED" | tk="MATCH_ALL" | tk="MATCH_ANY" | tk="MATCH_PHRASE" | tk="MATCH_PHRASE_PREFIX" | tk="MATCH_REGEXP" | tk="MATERIALIZED" | tk="MAX" | tk="MAXVALUE" | tk="MEDIUMTEXT" | tk="MEMBER" | tk="MERGE" | tk="MIN" | tk="MINVALUE" | tk="MODE" | tk="MODIFY" | tk="MOVEMENT" | tk="NAME" | tk="NAMES" | tk="NEVER" | tk="NEXT" | tk="NO" | tk="NOCACHE" | tk="NOKEEP" | tk="NOLOCK" | tk="NOMAXVALUE" | tk="NOMINVALUE" | tk="NONE" | tk="NOORDER" | tk="NOTHING" | tk="NOTNULL" | tk="NOVALIDATE" | tk="NOWAIT" | tk="NULLS" | tk="OF" | tk="OFF" | tk="OPEN" | tk="ORA" | tk="ORDINALITY" | tk="OVER" | tk="OVERFLOW" | tk="OVERLAPS" | tk="OVERRIDING" | tk="OVERWRITE" | tk="PADDING" | tk="PARALLEL" | tk="PARENT" | tk="PARSER" | tk="PARTITION" | tk="PARTITIONING" | tk="PATH" | tk="PERCENT" | tk="PLACING" | tk="PLAN" | tk="PLUS" | tk="PRECEDING" | tk="PRIMARY" | tk="PURGE" | tk="QUERY" | tk="QUICK" | tk="QUIESCE" | tk="RANGE" | tk="RAW" | tk="READ" | tk="REBUILD" | tk="RECURSIVE" | tk="RECYCLEBIN" | tk="REFERENCES" | tk="REFRESH" | tk="REGEXP" | tk="REGEXP_LIKE" | tk="REGISTER" | tk="REJECT" | tk="REMOTE" | tk="REMOVE" | tk="RENAME" | tk="REORGANIZE" | tk="REPAIR" | tk="REPEATABLE" | tk="REPLACE" | tk="RESET" | tk="RESPECT" | tk="RESTART" | tk="RESTRICT" | tk="RESTRICTED" | tk="RESUMABLE" | tk="RESUME" | tk="RETURN" | tk="RLIKE" | tk="ROLLBACK" | tk="ROLLUP" | tk="ROOT" | tk="ROW" | tk="ROWS" | tk="RR" | tk="RS" | tk="RTRIM" | tk="SAFE_CAST" | tk="SAFE_CONVERT" | tk="SAVEPOINT" | tk="SCHEMA" | tk="SECURE" | tk="SEED" | tk="SEPARATOR" | tk="SEQUENCE" | tk="SESSION" | tk="SETS" | tk="SHARE" | tk="SHOW" | tk="SHUTDOWN" | tk="SIBLINGS" | tk="SIGNED" | tk="SIMILAR" | tk="SIZE" | tk="SKIP" | tk="SPATIAL" | tk="STORED" | tk="STREAM" | tk="STRICT" | tk="STRING" | tk="STRUCT" | tk="SUMMARIZE" | tk="SUSPEND" | tk="SWITCH" | tk="SYMMETRIC" | tk="SYNONYM" | tk="SYSTEM" | tk="SYSTEM_TIME" | tk="SYSTEM_TIMESTAMP" | tk="SYSTEM_VERSION" | tk="TABLE" | tk="TABLESPACE" | tk="TEMP" | tk="TEMPORARY" | tk="TEXT" | tk="THAN" | tk="THEN" | tk="TIMEOUT" | tk="TIMESTAMPTZ" | tk="TIMEZONE" | tk="TINYTEXT" | tk="TO" | tk="TRIGGER" | tk="TRUNCATE" | tk="TRY_CAST" | tk="TRY_CONVERT" | tk="TUMBLING" | tk="TYPE" | tk="UNLIMITED" | tk="UNLOGGED" | tk="UNQIESCE" | tk="UNSIGNED" | tk="UPDATE" | tk="UPSERT" | tk="UR" | tk="USER" | tk="VALIDATE" | tk="VALIDATION" | tk="VERBOSE" | tk="VERSION" | tk="VIEW" | tk="VISIBLE" | tk="VOLATILE" | tk="WAIT" | tk="WITHIN" | tk="WITHOUT" | tk="WITHOUT_ARRAY_WRAPPER" | tk="WORK" | tk="XML" | tk="XMLAGG" | tk="XMLDATA" | tk="XMLSCHEMA" | tk="XMLTEXT" | tk="XSINIL" | tk="YAML" | tk="YES" | tk="ZONE" ) { return tk.image; } } From fc57aac28edde6ab657a6bd639371bb5c23e6c0e Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Tue, 7 Oct 2025 17:45:36 +0200 Subject: [PATCH 4/6] Added test --- .../statement/lock/LockStatement.java | 12 ++++++++++ .../jsqlparser/statement/lock/LockTest.java | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java index b60652949..bed8b1c59 100644 --- a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java @@ -17,7 +17,19 @@ public class LockStatement implements Statement { private boolean noWait; private Long waitSeconds; + /** + * Creates a new LockStatement + * + * @param table The table to lock + * @param lockMode The lock mode + */ + public LockStatement(Table table, LockMode lockMode) { + this.table = table; + this.lockMode = lockMode; + } + public LockStatement(Table table, LockMode lockMode, boolean noWait, Long waitSeconds) { + this(table, lockMode); this.table = table; this.lockMode = lockMode; this.noWait = noWait; diff --git a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java index 8a4797b96..f5e6858e6 100644 --- a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java @@ -2,6 +2,7 @@ import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.test.TestUtils; import org.junit.jupiter.api.Test; @@ -66,5 +67,28 @@ void testWaitTimeout() throws JSQLParserException { assertEquals(300, ls.getWaitTimeout()); } + @Test + void testCreateLockStatement() { + Table t = new Table("a"); + + LockStatement ls = new LockStatement(t, LockMode.Exclusive); + assertEquals("LOCK TABLE a IN EXCLUSIVE MODE", ls.toString()); + + ls.setLockMode(LockMode.Share); + assertEquals("LOCK TABLE a IN SHARE MODE", ls.toString()); + + ls.setNoWait(true); + assertEquals("LOCK TABLE a IN SHARE MODE NOWAIT", ls.toString()); + + ls.setWaitSeconds(60); + assertEquals("LOCK TABLE a IN SHARE MODE WAIT 60", ls.toString()); + + ls.setNoWait(false); + assertEquals("LOCK TABLE a IN SHARE MODE", ls.toString()); + + ls.setTable(new Table("b")); + assertEquals("LOCK TABLE b IN SHARE MODE", ls.toString()); + } + } From 16ccb75ad5fb7fbb57bc99d982e872fd33b88bf3 Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Tue, 7 Oct 2025 17:54:31 +0200 Subject: [PATCH 5/6] Checkstyle & Spotless --- .../jsqlparser/statement/lock/LockMode.java | 29 ++- .../statement/lock/LockStatement.java | 193 +++++++++--------- .../jsqlparser/statement/lock/LockTest.java | 6 +- 3 files changed, 113 insertions(+), 115 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java index c103414dc..552da946e 100644 --- a/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockMode.java @@ -4,24 +4,21 @@ * Describes the LockMode of a LOCK TABLE-Statement. */ public enum LockMode { - // These two modes are more common - Share("SHARE"), - Exclusive("EXCLUSIVE"), + // These two modes are more common + Share("SHARE"), Exclusive("EXCLUSIVE"), - // These are Oracle specific, as far as I know - RowShare("ROW SHARE"), - RowExclusive("ROW EXCLUSIVE"), - ShareUpdate("SHARE UPDATE"), - ShareRowExclusive("SHARE ROW EXCLUSIVE"); + // These are Oracle specific, as far as I know + RowShare("ROW SHARE"), RowExclusive("ROW EXCLUSIVE"), ShareUpdate( + "SHARE UPDATE"), ShareRowExclusive("SHARE ROW EXCLUSIVE"); - private final String value; + private final String value; - LockMode(String value) { - this.value = value; - } + LockMode(String value) { + this.value = value; + } - public String getValue() { - return value; - } + public String getValue() { + return value; + } -} \ No newline at end of file +} diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java index bed8b1c59..fe3365b5c 100644 --- a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java @@ -12,100 +12,101 @@ */ public class LockStatement implements Statement { - private Table table; - private LockMode lockMode; - private boolean noWait; - private Long waitSeconds; - - /** - * Creates a new LockStatement - * - * @param table The table to lock - * @param lockMode The lock mode - */ - public LockStatement(Table table, LockMode lockMode) { - this.table = table; - this.lockMode = lockMode; - } - - public LockStatement(Table table, LockMode lockMode, boolean noWait, Long waitSeconds) { - this(table, lockMode); - this.table = table; - this.lockMode = lockMode; - this.noWait = noWait; - this.waitSeconds = waitSeconds; - } - - private void checkValidState() { - if (noWait && waitSeconds != null) { - throw new IllegalStateException("A LOCK statement cannot have NOWAIT and WAIT at the same time"); - } - } - - public Table getTable() { - return table; - } - - public void setTable(Table table) { - this.table = table; - } - - public LockMode getLockMode() { - return lockMode; - } - - public void setLockMode(LockMode lockMode) { - this.lockMode = lockMode; - } - - public boolean isNoWait() { - return noWait; - } - - /** - * Sets the NOWAIT-Flag. Clears a WAIT-Timeout if one was set before. - * - * @param noWait The new value of the NOWAIT-Flag - */ - public void setNoWait(boolean noWait) { - this.waitSeconds = null; - this.noWait = noWait; - checkValidState(); - } - - public boolean isWait() { - return waitSeconds != null; - } - - /** - * Sets the WAIT-Timeout. If this value is set, the Statement is rendered with WAIT <timeoutSeconds> - * - * @param waitSeconds The number of seconds for the WAIT timeout - */ - public void setWaitSeconds(long waitSeconds) { - this.noWait = false; - this.waitSeconds = waitSeconds; - checkValidState(); - } - - @Override - public String toString() { - return - "LOCK TABLE " - + table.getFullyQualifiedName() - + " IN " - + lockMode.getValue() - + " MODE" - + (noWait ? " NOWAIT" : "") - + (waitSeconds != null ? " WAIT " + waitSeconds : ""); - } - - @Override - public T accept(StatementVisitor statementVisitor, S context) { - return statementVisitor.visit(this, context); - } - - public long getWaitTimeout() { - return waitSeconds; - } + private Table table; + private LockMode lockMode; + private boolean noWait; + private Long waitSeconds; + + /** + * Creates a new LockStatement + * + * @param table The table to lock + * @param lockMode The lock mode + */ + public LockStatement(Table table, LockMode lockMode) { + this.table = table; + this.lockMode = lockMode; + } + + public LockStatement(Table table, LockMode lockMode, boolean noWait, Long waitSeconds) { + this(table, lockMode); + this.table = table; + this.lockMode = lockMode; + this.noWait = noWait; + this.waitSeconds = waitSeconds; + } + + private void checkValidState() { + if (noWait && waitSeconds != null) { + throw new IllegalStateException( + "A LOCK statement cannot have NOWAIT and WAIT at the same time"); + } + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public LockMode getLockMode() { + return lockMode; + } + + public void setLockMode(LockMode lockMode) { + this.lockMode = lockMode; + } + + public boolean isNoWait() { + return noWait; + } + + /** + * Sets the NOWAIT-Flag. Clears a WAIT-Timeout if one was set before. + * + * @param noWait The new value of the NOWAIT-Flag + */ + public void setNoWait(boolean noWait) { + this.waitSeconds = null; + this.noWait = noWait; + checkValidState(); + } + + public boolean isWait() { + return waitSeconds != null; + } + + /** + * Sets the WAIT-Timeout. If this value is set, the Statement is rendered with WAIT + * <timeoutSeconds> + * + * @param waitSeconds The number of seconds for the WAIT timeout + */ + public void setWaitSeconds(long waitSeconds) { + this.noWait = false; + this.waitSeconds = waitSeconds; + checkValidState(); + } + + @Override + public String toString() { + return "LOCK TABLE " + + table.getFullyQualifiedName() + + " IN " + + lockMode.getValue() + + " MODE" + + (noWait ? " NOWAIT" : "") + + (waitSeconds != null ? " WAIT " + waitSeconds : ""); + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public long getWaitTimeout() { + return waitSeconds; + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java index f5e6858e6..57b574af9 100644 --- a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java @@ -39,7 +39,7 @@ void testLockExclusiveMode() throws JSQLParserException { Statement statement = CCJSqlParserUtil.parse(sqlStr); assertInstanceOf(LockStatement.class, statement); - LockStatement ls = (LockStatement)statement; + LockStatement ls = (LockStatement) statement; assertEquals(LockMode.Exclusive, ls.getLockMode()); assertFalse(ls.isNoWait()); } @@ -50,7 +50,7 @@ void testNoWait() throws JSQLParserException { Statement statement = CCJSqlParserUtil.parse(sqlStr); assertInstanceOf(LockStatement.class, statement); - LockStatement ls = (LockStatement)statement; + LockStatement ls = (LockStatement) statement; assertEquals(LockMode.Share, ls.getLockMode()); assertTrue(ls.isNoWait()); } @@ -61,7 +61,7 @@ void testWaitTimeout() throws JSQLParserException { Statement statement = CCJSqlParserUtil.parse(sqlStr); assertInstanceOf(LockStatement.class, statement); - LockStatement ls = (LockStatement)statement; + LockStatement ls = (LockStatement) statement; assertEquals(LockMode.Share, ls.getLockMode()); assertTrue(ls.isWait()); assertEquals(300, ls.getWaitTimeout()); From 85b85137ae0fab8b9392d2eefa16d40d735d9343 Mon Sep 17 00:00:00 2001 From: Andreas Neumann Date: Tue, 7 Oct 2025 18:25:53 +0200 Subject: [PATCH 6/6] Adjusted NOWAIT and WAIT-Setter --- .../statement/lock/LockStatement.java | 32 ++++++++++--------- .../jsqlparser/statement/lock/LockTest.java | 31 +++++++++++++++--- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java index fe3365b5c..3e88c6065 100644 --- a/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java +++ b/src/main/java/net/sf/jsqlparser/statement/lock/LockStatement.java @@ -7,7 +7,7 @@ /** * Statement to Lock a specific table.
* Example:
- * LOCK TABLE <TABLE> IN EXCLUSIVE MODE;
+ * LOCK TABLE t IN EXCLUSIVE MODE
*
*/ public class LockStatement implements Statement { @@ -59,37 +59,42 @@ public void setLockMode(LockMode lockMode) { this.lockMode = lockMode; } + /** + * @return True if the statement has a NOWAIT clause + */ public boolean isNoWait() { return noWait; } /** - * Sets the NOWAIT-Flag. Clears a WAIT-Timeout if one was set before. + * Sets the NOWAIT-Flag. * - * @param noWait The new value of the NOWAIT-Flag + * @param noWait True if the statement should have the NOWAIT clause */ public void setNoWait(boolean noWait) { - this.waitSeconds = null; this.noWait = noWait; checkValidState(); } - public boolean isWait() { - return waitSeconds != null; - } - /** * Sets the WAIT-Timeout. If this value is set, the Statement is rendered with WAIT - * <timeoutSeconds> + * <timeoutSeconds>
+ * If the value is set to NULL, the WAIT-clause is skipped * - * @param waitSeconds The number of seconds for the WAIT timeout + * @param waitSeconds The number of seconds for the WAIT timeout or NULL to skip the WAIT clause */ - public void setWaitSeconds(long waitSeconds) { - this.noWait = false; + public void setWaitSeconds(Long waitSeconds) { this.waitSeconds = waitSeconds; checkValidState(); } + /** + * @return The number of seconds in the WAIT clause, or NULL if the statement has no WAIT clause + */ + public Long getWaitSeconds() { + return waitSeconds; + } + @Override public String toString() { return "LOCK TABLE " @@ -106,7 +111,4 @@ public T accept(StatementVisitor statementVisitor, S context) { return statementVisitor.visit(this, context); } - public long getWaitTimeout() { - return waitSeconds; - } } diff --git a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java index 57b574af9..0e71c0107 100644 --- a/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/lock/LockTest.java @@ -63,8 +63,8 @@ void testWaitTimeout() throws JSQLParserException { LockStatement ls = (LockStatement) statement; assertEquals(LockMode.Share, ls.getLockMode()); - assertTrue(ls.isWait()); - assertEquals(300, ls.getWaitTimeout()); + assertNotNull(ls.getWaitSeconds()); + assertEquals(300, ls.getWaitSeconds()); } @Test @@ -80,15 +80,38 @@ void testCreateLockStatement() { ls.setNoWait(true); assertEquals("LOCK TABLE a IN SHARE MODE NOWAIT", ls.toString()); - ls.setWaitSeconds(60); + ls.setNoWait(false); + ls.setWaitSeconds(60L); assertEquals("LOCK TABLE a IN SHARE MODE WAIT 60", ls.toString()); - ls.setNoWait(false); + ls.setWaitSeconds(null); assertEquals("LOCK TABLE a IN SHARE MODE", ls.toString()); ls.setTable(new Table("b")); assertEquals("LOCK TABLE b IN SHARE MODE", ls.toString()); } + @Test + void testIllegalStateWaitSeconds() { + Table t = new Table("a"); + LockStatement ls = new LockStatement(t, LockMode.Exclusive); + + assertThrows(IllegalStateException.class, () -> { + ls.setNoWait(true); + ls.setWaitSeconds(60L); + }); + } + + @Test + void testIllegalStateNoWait() { + Table t = new Table("a"); + LockStatement ls = new LockStatement(t, LockMode.Exclusive); + + assertThrows(IllegalStateException.class, () -> { + ls.setWaitSeconds(60L); + ls.setNoWait(true); + }); + } + }