Skip to content

Commit d170a11

Browse files
committed
feat: parse MySQL FLUSH, KILL, RESET PERSIST and HANDLER
FLUSH, KILL and HANDLER failed in every dialect, RESET PERSIST alone reset a parameter named PERSIST and RESET PERSIST name failed. FlushStatement keeps the binlog modifier, the flush options in source order (FlushOption, with the channel of RELAY LOGS) or the FLUSH TABLES form with its tables and WITH READ LOCK or FOR EXPORT. KillStatement keeps CONNECTION or QUERY and the identifier expression. ResetStatement gains isPersist() and isIfExists(), and the variable name may have a component or DEFAULT prefix. HandlerStatement keeps OPEN with the table and its alias, READ with the index, key comparison and values or direction, WHERE and LIMIT, and CLOSE; as in MySQL, a READ takes no subquery. The forms parse with the MySQL dialect and without one; other dialects keep rejecting them. HANDLER becomes a non-reserved keyword, and TablesNamesFinder no longer throws for RESET. The new statements and CHECK, CHECKSUM, OPTIMIZE and REPAIR TABLE now start a statement, so unsupported statement capture no longer takes them. Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 12548b9 commit d170a11

27 files changed

Lines changed: 2392 additions & 23 deletions

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J
150150
| **MySQL table maintenance** | `ANALYZE` · `CHECK` · `CHECKSUM` · `OPTIMIZE` · `REPAIR TABLE` |
151151
| **PostgreSQL maintenance** | `VACUUM` · `ANALYZE` · `CLUSTER` · `CHECKPOINT` |
152152
| **MySQL locks** | `LOCK TABLES` · `UNLOCK TABLES` · `LOCK INSTANCE FOR BACKUP` · `UNLOCK INSTANCE` |
153+
| **MySQL server administration** | `FLUSH` · `KILL` · `RESET PERSIST` · `HANDLER … OPEN`/`READ`/`CLOSE` |
153154
| **MySQL account management** | `CREATE`/`ALTER`/`RENAME USER` · `CREATE ROLE` · `SET ROLE` · `SET DEFAULT ROLE` · `SET PASSWORD` |
154155
| **Tablespaces** | MySQL and PostgreSQL `CREATE TABLESPACE` · `ALTER TABLESPACE` |
155156
| **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` |

‎src/main/java/net/sf/jsqlparser/parser/RoutineBodyBoundary.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ private void acceptStatementToken(Token token, Token next) {
248248
if (statementStart) {
249249
statementKind = kind;
250250
}
251-
if (statementKind == K_DECLARE && keyword(token, "HANDLER")) {
251+
if (statementKind == K_DECLARE && token.kind == K_HANDLER) {
252252
handlerAction = true;
253253
}
254254
statementStart = false;

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import net.sf.jsqlparser.statement.ShowStatement;
2929
import net.sf.jsqlparser.statement.TableMaintenanceStatement;
3030
import net.sf.jsqlparser.statement.UseStatement;
31+
import net.sf.jsqlparser.statement.admin.FlushStatement;
32+
import net.sf.jsqlparser.statement.admin.KillStatement;
3133
import net.sf.jsqlparser.statement.alter.Alter;
3234
import net.sf.jsqlparser.statement.alter.AlterEventTrigger;
3335
import net.sf.jsqlparser.statement.alter.sequence.AlterSequence;
@@ -51,6 +53,7 @@
5153
import net.sf.jsqlparser.statement.drop.Drop;
5254
import net.sf.jsqlparser.statement.execute.Execute;
5355
import net.sf.jsqlparser.statement.grant.Grant;
56+
import net.sf.jsqlparser.statement.handler.HandlerStatement;
5457
import net.sf.jsqlparser.statement.lock.BackupLockStatement;
5558
import net.sf.jsqlparser.statement.lock.UnlockStatement;
5659
import net.sf.jsqlparser.statement.merge.Merge;
@@ -490,6 +493,27 @@ public enum Feature {
490493
*/
491494
tableMaintenance,
492495

496+
/**
497+
* MySQL "FLUSH" statement is allowed
498+
*
499+
* @see FlushStatement
500+
*/
501+
flush,
502+
503+
/**
504+
* MySQL "KILL" statement is allowed
505+
*
506+
* @see KillStatement
507+
*/
508+
kill,
509+
510+
/**
511+
* MySQL "HANDLER ... OPEN", "HANDLER ... READ" and "HANDLER ... CLOSE" statements are allowed
512+
*
513+
* @see HandlerStatement
514+
*/
515+
handler,
516+
493517
/**
494518
* PostgreSQL "VACUUM" statement is allowed
495519
*
@@ -930,6 +954,12 @@ public enum Feature {
930954
* @see ResetStatement
931955
*/
932956
reset,
957+
/**
958+
* MySQL "RESET PERSIST" statement is allowed
959+
*
960+
* @see ResetStatement#isPersist()
961+
*/
962+
resetPersist,
933963
/**
934964
* @see Pivot
935965
*/

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

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@
1111

1212
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
1313

14+
/**
15+
* PostgreSQL {@code RESET configuration_parameter}, {@code RESET ALL} and {@code RESET TIME ZONE},
16+
* and MySQL {@code RESET PERSIST [[IF EXISTS] system_var_name]}, which {@link #isPersist()} marks.
17+
*
18+
* @see <a href="https://www.postgresql.org/docs/current/sql-reset.html">PostgreSQL RESET</a>
19+
* @see <a href="https://dev.mysql.com/doc/refman/8.4/en/reset-persist.html">MySQL RESET PERSIST</a>
20+
*/
1421
public final class ResetStatement extends ASTNodeAccessImpl implements Statement {
1522

1623
private String name = "";
24+
private boolean persist;
25+
private boolean ifExists;
1726

1827
public ResetStatement() {
1928
// empty constructor
@@ -27,6 +36,10 @@ public void add(String name) {
2736
this.name = name;
2837
}
2938

39+
/**
40+
* Returns the parameter or, for RESET PERSIST, the system variable; null when RESET PERSIST
41+
* names none and removes every persisted variable.
42+
*/
3043
public String getName() {
3144
return name;
3245
}
@@ -35,10 +48,52 @@ public void setName(String name) {
3548
this.name = name;
3649
}
3750

51+
/** Whether this is MySQL RESET PERSIST, which removes persisted global system variables. */
52+
public boolean isPersist() {
53+
return persist;
54+
}
55+
56+
public void setPersist(boolean persist) {
57+
this.persist = persist;
58+
}
59+
60+
public ResetStatement withPersist(boolean persist) {
61+
setPersist(persist);
62+
return this;
63+
}
64+
65+
/** Whether RESET PERSIST has IF EXISTS before the variable name. */
66+
public boolean isIfExists() {
67+
return ifExists;
68+
}
69+
70+
public void setIfExists(boolean ifExists) {
71+
this.ifExists = ifExists;
72+
}
73+
74+
public ResetStatement withIfExists(boolean ifExists) {
75+
setIfExists(ifExists);
76+
return this;
77+
}
78+
79+
public StringBuilder appendTo(StringBuilder builder) {
80+
builder.append("RESET ");
81+
if (!persist) {
82+
return builder.append(name);
83+
}
84+
builder.append("PERSIST");
85+
if (ifExists) {
86+
builder.append(" IF EXISTS");
87+
}
88+
if (name != null && !name.isEmpty()) {
89+
builder.append(' ').append(name);
90+
}
91+
return builder;
92+
}
93+
3894
@Override
3995
public String toString() {
40-
StringBuilder b = new StringBuilder("RESET ").append(name);
41-
return b.toString();
96+
return appendTo(new StringBuilder()).toString();
4297
}
4398

4499
@Override

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@
9898
import net.sf.jsqlparser.statement.insert.Insert;
9999
import net.sf.jsqlparser.statement.insert.InsertBulk;
100100
import net.sf.jsqlparser.statement.load.LoadDataStatement;
101+
import net.sf.jsqlparser.statement.admin.FlushStatement;
102+
import net.sf.jsqlparser.statement.admin.KillStatement;
103+
import net.sf.jsqlparser.statement.handler.HandlerStatement;
101104
import net.sf.jsqlparser.statement.lock.BackupLockStatement;
102105
import net.sf.jsqlparser.statement.lock.LockStatement;
103106
import net.sf.jsqlparser.statement.lock.UnlockStatement;
@@ -783,6 +786,40 @@ public <S> Void visit(TableMaintenanceStatement statement, S context) {
783786
return null;
784787
}
785788

789+
@Override
790+
public <S> Void visit(FlushStatement flush, S context) {
791+
// a server operation like ALTER SYSTEM FLUSH; WITH READ LOCK and FOR EXPORT also lock
792+
analysis.claimTopLevel();
793+
analysis.certain(StmtFeature.MODIFIES_SESSION);
794+
if (flush.getTableLock() != null) {
795+
analysis.certain(StmtFeature.MODIFIES_TRANSACTION);
796+
}
797+
return null;
798+
}
799+
800+
@Override
801+
public <S> Void visit(KillStatement kill, S context) {
802+
// ends another session or its statement, like ALTER SYSTEM KILL SESSION
803+
analysis.claimTopLevel();
804+
analysis.certain(StmtFeature.MODIFIES_SESSION);
805+
return null;
806+
}
807+
808+
@Override
809+
public <S> Void visit(HandlerStatement handler, S context) {
810+
if (handler.getAction() != HandlerStatement.Action.READ) {
811+
// OPEN and CLOSE change the handlers of the session
812+
analysis.claimTopLevel();
813+
analysis.certain(StmtFeature.MODIFIES_SESSION);
814+
return null;
815+
}
816+
if (analysis.claimTopLevel()) {
817+
analysis.certain(StmtFeature.RETURNS_RESULT_SET);
818+
}
819+
analysis.certain(StmtFeature.READS_DATA);
820+
return super.visit(handler, context);
821+
}
822+
786823
@Override
787824
public <S> Void visit(VacuumStatement vacuum, S context) {
788825
// reclaims storage and, with ANALYZE, rewrites statistics; FULL rewrites the tables

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@
9090
import net.sf.jsqlparser.statement.insert.Insert;
9191
import net.sf.jsqlparser.statement.insert.InsertBulk;
9292
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
93+
import net.sf.jsqlparser.statement.admin.FlushStatement;
94+
import net.sf.jsqlparser.statement.admin.KillStatement;
95+
import net.sf.jsqlparser.statement.handler.HandlerStatement;
9396
import net.sf.jsqlparser.statement.lock.BackupLockStatement;
9497
import net.sf.jsqlparser.statement.lock.LockStatement;
9598
import net.sf.jsqlparser.statement.lock.UnlockStatement;
@@ -376,6 +379,30 @@ default void visit(TableMaintenanceStatement statement) {
376379
this.visit(statement, null);
377380
}
378381

382+
default <S> T visit(FlushStatement flush, S context) {
383+
return null;
384+
}
385+
386+
default void visit(FlushStatement flush) {
387+
this.visit(flush, null);
388+
}
389+
390+
default <S> T visit(KillStatement kill, S context) {
391+
return null;
392+
}
393+
394+
default void visit(KillStatement kill) {
395+
this.visit(kill, null);
396+
}
397+
398+
default <S> T visit(HandlerStatement handler, S context) {
399+
return null;
400+
}
401+
402+
default void visit(HandlerStatement handler) {
403+
this.visit(handler, null);
404+
}
405+
379406
default <S> T visit(VacuumStatement vacuum, S context) {
380407
return null;
381408
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@
9393
import net.sf.jsqlparser.statement.insert.InsertBulk;
9494
import net.sf.jsqlparser.statement.insert.InsertConflictAction;
9595
import net.sf.jsqlparser.statement.insert.ParenthesedInsert;
96+
import net.sf.jsqlparser.statement.admin.FlushStatement;
97+
import net.sf.jsqlparser.statement.admin.KillStatement;
98+
import net.sf.jsqlparser.statement.handler.HandlerStatement;
9699
import net.sf.jsqlparser.statement.lock.BackupLockStatement;
97100
import net.sf.jsqlparser.statement.lock.LockStatement;
98101
import net.sf.jsqlparser.statement.lock.UnlockStatement;
@@ -723,6 +726,24 @@ public <S> T visit(TableMaintenanceStatement statement, S context) {
723726
return null;
724727
}
725728

729+
@Override
730+
public <S> T visit(FlushStatement flush, S context) {
731+
return null;
732+
}
733+
734+
@Override
735+
public <S> T visit(KillStatement kill, S context) {
736+
expressionVisitor.visitExpression(kill.getProcesslistId(), context);
737+
return null;
738+
}
739+
740+
@Override
741+
public <S> T visit(HandlerStatement handler, S context) {
742+
expressionVisitor.visitExpression(handler.getKeyValues(), context);
743+
expressionVisitor.visitExpression(handler.getWhere(), context);
744+
return null;
745+
}
746+
726747
@Override
727748
public <S> T visit(VacuumStatement vacuum, S context) {
728749
return null;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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.admin;
11+
12+
import java.util.Locale;
13+
import java.util.Objects;
14+
import net.sf.jsqlparser.expression.StringValue;
15+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
16+
17+
/**
18+
* One flush option of MySQL {@code FLUSH}, such as {@code BINARY LOGS} or
19+
* {@code RELAY LOGS FOR CHANNEL 'c1'}.
20+
*
21+
* @see <a href="https://dev.mysql.com/doc/refman/8.4/en/flush.html">MySQL FLUSH</a>
22+
*/
23+
public class FlushOption extends ASTNodeAccessImpl {
24+
private Kind kind;
25+
private StringValue channel;
26+
27+
/** The flush options of MySQL 8.4. */
28+
public enum Kind {
29+
BINARY_LOGS, ENGINE_LOGS, ERROR_LOGS, GENERAL_LOGS, LOGS, OPTIMIZER_COSTS, PRIVILEGES, RELAY_LOGS, SLOW_LOGS, STATUS, USER_RESOURCES;
30+
31+
/** Returns the SQL spelling, for example {@code BINARY LOGS}. */
32+
public String getKeyword() {
33+
return this == OPTIMIZER_COSTS || this == USER_RESOURCES ? name()
34+
: name().replace('_', ' ');
35+
}
36+
37+
/**
38+
* Returns the option spelled by the keyword, ignoring case, or null.
39+
*
40+
* @param keyword the words of the option separated by one space, such as
41+
* {@code binary logs}
42+
*/
43+
public static Kind fromKeyword(String keyword) {
44+
String upper = keyword == null ? null : keyword.toUpperCase(Locale.ROOT);
45+
for (Kind kind : values()) {
46+
if (kind.getKeyword().equals(upper)) {
47+
return kind;
48+
}
49+
}
50+
return null;
51+
}
52+
}
53+
54+
public FlushOption(Kind kind) {
55+
setKind(kind);
56+
}
57+
58+
public Kind getKind() {
59+
return kind;
60+
}
61+
62+
public void setKind(Kind kind) {
63+
this.kind = Objects.requireNonNull(kind);
64+
}
65+
66+
public FlushOption withKind(Kind kind) {
67+
setKind(kind);
68+
return this;
69+
}
70+
71+
/** Returns the channel of {@code RELAY LOGS FOR CHANNEL 'name'}, or null. */
72+
public StringValue getChannel() {
73+
return channel;
74+
}
75+
76+
public void setChannel(StringValue channel) {
77+
this.channel = channel;
78+
}
79+
80+
public FlushOption withChannel(StringValue channel) {
81+
setChannel(channel);
82+
return this;
83+
}
84+
85+
public StringBuilder appendTo(StringBuilder builder) {
86+
builder.append(kind.getKeyword());
87+
if (channel != null) {
88+
builder.append(" FOR CHANNEL ").append(channel);
89+
}
90+
return builder;
91+
}
92+
93+
@Override
94+
public String toString() {
95+
return appendTo(new StringBuilder()).toString();
96+
}
97+
}

0 commit comments

Comments
 (0)