diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index b3b3ed03f..60ac14125 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -25,6 +25,7 @@ exports net.sf.jsqlparser.statement.alter.sequence; exports net.sf.jsqlparser.statement.analyze; exports net.sf.jsqlparser.statement.comment; + exports net.sf.jsqlparser.statement.create.event; exports net.sf.jsqlparser.statement.create.function; exports net.sf.jsqlparser.statement.create.index; exports net.sf.jsqlparser.statement.create.policy; diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java index 823b8271f..9c4bdd3cd 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -17,6 +17,8 @@ import net.sf.jsqlparser.statement.analyze.Analyze; import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.database.CreateDatabase; +import net.sf.jsqlparser.statement.create.event.AlterEvent; +import net.sf.jsqlparser.statement.create.event.CreateEvent; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.policy.CreatePolicy; import net.sf.jsqlparser.statement.create.schema.CreateSchema; @@ -132,6 +134,12 @@ default void visit(CreateUser createUser) { this.visit(createUser, null); } + T visit(CreateEvent createEvent, S context); + + default void visit(CreateEvent createEvent) { + this.visit(createEvent, null); + } + T visit(CreateTable createTable, S context); default void visit(CreateTable createTable) { @@ -162,6 +170,12 @@ default void visit(Alter alter) { this.visit(alter, null); } + T visit(AlterEvent alterEvent, S context); + + default void visit(AlterEvent alterEvent) { + this.visit(alterEvent, null); + } + T visit(Statements statements, S context); default void visit(Statements statements) { diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java index a8abb46ee..ebc8b88e5 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -21,6 +21,8 @@ import net.sf.jsqlparser.statement.analyze.Analyze; import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.database.CreateDatabase; +import net.sf.jsqlparser.statement.create.event.AlterEvent; +import net.sf.jsqlparser.statement.create.event.CreateEvent; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.policy.CreatePolicy; import net.sf.jsqlparser.statement.create.schema.CreateSchema; @@ -273,6 +275,11 @@ public T visit(CreateUser createUser, S context) { return null; } + @Override + public T visit(CreateEvent createEvent, S context) { + return null; + } + @Override public T visit(CreateTable createTable, S context) { return createTable.getTable().accept(fromItemVisitor, context); @@ -289,6 +296,11 @@ public T visit(Alter alter, S context) { return null; } + @Override + public T visit(AlterEvent alterEvent, S context) { + return null; + } + @Override public T visit(Statements statements, S context) { for (Statement statement : statements) { diff --git a/src/main/java/net/sf/jsqlparser/statement/create/event/AlterEvent.java b/src/main/java/net/sf/jsqlparser/statement/create/event/AlterEvent.java new file mode 100644 index 000000000..c018be76b --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/event/AlterEvent.java @@ -0,0 +1,51 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.event; + +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** MySQL {@code ALTER EVENT} statement. */ +public class AlterEvent extends EventStatement { + + private Table renameTo; + + public Table getRenameTo() { + return renameTo; + } + + public void setRenameTo(Table renameTo) { + this.renameTo = renameTo; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("ALTER EVENT ").append(getEvent()); + appendScheduleAndCompletion(builder); + if (renameTo != null) { + builder.append(" RENAME TO ").append(renameTo); + } + appendStatusAndComment(builder); + if (getBody() != null) { + builder.append(" DO ").append(getBody()); + } + return builder.toString(); + } + + public AlterEvent withRenameTo(Table renameTo) { + setRenameTo(renameTo); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/event/CreateEvent.java b/src/main/java/net/sf/jsqlparser/statement/create/event/CreateEvent.java new file mode 100644 index 000000000..2cefc5d9e --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/event/CreateEvent.java @@ -0,0 +1,49 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.event; + +import net.sf.jsqlparser.statement.StatementVisitor; + +/** MySQL {@code CREATE EVENT} statement. */ +public class CreateEvent extends EventStatement { + + private boolean ifNotExists; + + public boolean isIfNotExists() { + return ifNotExists; + } + + public void setIfNotExists(boolean ifNotExists) { + this.ifNotExists = ifNotExists; + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("CREATE EVENT "); + if (ifNotExists) { + builder.append("IF NOT EXISTS "); + } + builder.append(getEvent()); + appendScheduleAndCompletion(builder); + appendStatusAndComment(builder); + builder.append(" DO ").append(getBody()); + return builder.toString(); + } + + public CreateEvent withIfNotExists(boolean ifNotExists) { + setIfNotExists(ifNotExists); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/event/EventSchedule.java b/src/main/java/net/sf/jsqlparser/statement/create/event/EventSchedule.java new file mode 100644 index 000000000..b3dba66f1 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/event/EventSchedule.java @@ -0,0 +1,123 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.event; + +import java.io.Serializable; +import net.sf.jsqlparser.expression.Expression; + +/** Structured {@code ON SCHEDULE} clause of a MySQL event. */ +public class EventSchedule implements Serializable { + + public enum Type { + AT, EVERY + } + + private Type type; + private Expression executeAt; + private Expression interval; + private String intervalUnit; + private Expression starts; + private Expression ends; + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public Expression getExecuteAt() { + return executeAt; + } + + public void setExecuteAt(Expression executeAt) { + this.executeAt = executeAt; + } + + public Expression getInterval() { + return interval; + } + + public void setInterval(Expression interval) { + this.interval = interval; + } + + public String getIntervalUnit() { + return intervalUnit; + } + + public void setIntervalUnit(String intervalUnit) { + this.intervalUnit = intervalUnit; + } + + public Expression getStarts() { + return starts; + } + + public void setStarts(Expression starts) { + this.starts = starts; + } + + public Expression getEnds() { + return ends; + } + + public void setEnds(Expression ends) { + this.ends = ends; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (type == Type.AT) { + builder.append("AT ").append(executeAt); + } else if (type == Type.EVERY) { + builder.append("EVERY ").append(interval).append(" ").append(intervalUnit); + if (starts != null) { + builder.append(" STARTS ").append(starts); + } + if (ends != null) { + builder.append(" ENDS ").append(ends); + } + } + return builder.toString(); + } + + public EventSchedule withType(Type type) { + setType(type); + return this; + } + + public EventSchedule withExecuteAt(Expression executeAt) { + setExecuteAt(executeAt); + return this; + } + + public EventSchedule withInterval(Expression interval) { + setInterval(interval); + return this; + } + + public EventSchedule withIntervalUnit(String intervalUnit) { + setIntervalUnit(intervalUnit); + return this; + } + + public EventSchedule withStarts(Expression starts) { + setStarts(starts); + return this; + } + + public EventSchedule withEnds(Expression ends) { + setEnds(ends); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/event/EventStatement.java b/src/main/java/net/sf/jsqlparser/statement/create/event/EventStatement.java new file mode 100644 index 000000000..bd66e95f2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/event/EventStatement.java @@ -0,0 +1,99 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.event; + +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; + +/** Common structured properties of MySQL {@code CREATE EVENT} and {@code ALTER EVENT}. */ +public abstract class EventStatement implements Statement { + + private Table event; + private EventSchedule schedule; + private Boolean onCompletionPreserve; + private EventStatus status; + private StringValue comment; + private Statement body; + + public Table getEvent() { + return event; + } + + public void setEvent(Table event) { + this.event = event; + } + + public EventSchedule getSchedule() { + return schedule; + } + + public void setSchedule(EventSchedule schedule) { + this.schedule = schedule; + } + + /** + * Returns {@code true} for {@code ON COMPLETION PRESERVE}, {@code false} for + * {@code ON COMPLETION NOT PRESERVE}, or {@code null} when omitted. + */ + public Boolean getOnCompletionPreserve() { + return onCompletionPreserve; + } + + public void setOnCompletionPreserve(Boolean onCompletionPreserve) { + this.onCompletionPreserve = onCompletionPreserve; + } + + public EventStatus getStatus() { + return status; + } + + public void setStatus(EventStatus status) { + this.status = status; + } + + public StringValue getComment() { + return comment; + } + + public void setComment(StringValue comment) { + this.comment = comment; + } + + public Statement getBody() { + return body; + } + + public void setBody(Statement body) { + this.body = body; + } + + protected void appendScheduleAndCompletion(StringBuilder builder) { + if (schedule != null) { + builder.append(" ON SCHEDULE ").append(schedule); + } + if (onCompletionPreserve != null) { + builder.append(" ON COMPLETION "); + if (!onCompletionPreserve) { + builder.append("NOT "); + } + builder.append("PRESERVE"); + } + } + + protected void appendStatusAndComment(StringBuilder builder) { + if (status != null) { + builder.append(" ").append(status); + } + if (comment != null) { + builder.append(" COMMENT ").append(comment); + } + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/event/EventStatus.java b/src/main/java/net/sf/jsqlparser/statement/create/event/EventStatus.java new file mode 100644 index 000000000..8418267e2 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/event/EventStatus.java @@ -0,0 +1,25 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.event; + +public enum EventStatus { + ENABLE("ENABLE"), DISABLE("DISABLE"), DISABLE_ON_SLAVE("DISABLE ON SLAVE"); + + private final String sql; + + EventStatus(String sql) { + this.sql = sql; + } + + @Override + public String toString() { + return sql; + } +} diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index 7ac65f4b5..d91bf06a3 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -98,6 +98,8 @@ import net.sf.jsqlparser.statement.analyze.Analyze; import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.database.CreateDatabase; +import net.sf.jsqlparser.statement.create.event.AlterEvent; +import net.sf.jsqlparser.statement.create.event.CreateEvent; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.policy.CreatePolicy; import net.sf.jsqlparser.statement.create.schema.CreateSchema; @@ -1566,6 +1568,19 @@ public void visit(CreateUser createUser) { StatementVisitor.super.visit(createUser); } + @Override + public Void visit(CreateEvent createEvent, S context) { + if (createEvent.getBody() != null) { + createEvent.getBody().accept(this, context); + } + return null; + } + + @Override + public void visit(CreateEvent createEvent) { + StatementVisitor.super.visit(createEvent); + } + @Override public Void visit(CreateTable create, S context) { visit(create.getTable(), null); @@ -1604,6 +1619,19 @@ public void visit(Alter alter) { alter.getTable().accept(this, null); } + @Override + public Void visit(AlterEvent alterEvent, S context) { + if (alterEvent.getBody() != null) { + alterEvent.getBody().accept(this, context); + } + return null; + } + + @Override + public void visit(AlterEvent alterEvent) { + StatementVisitor.super.visit(alterEvent); + } + @Override public Void visit(Statements statements, S context) { for (Statement statement : statements) { 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 34eb53dfd..9878fd4e8 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -42,6 +42,8 @@ import net.sf.jsqlparser.statement.analyze.Analyze; import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.database.CreateDatabase; +import net.sf.jsqlparser.statement.create.event.AlterEvent; +import net.sf.jsqlparser.statement.create.event.CreateEvent; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.policy.CreatePolicy; import net.sf.jsqlparser.statement.create.schema.CreateSchema; @@ -275,6 +277,12 @@ public StringBuilder visit(Alter alter, S context) { return builder; } + @Override + public StringBuilder visit(AlterEvent alterEvent, S context) { + builder.append(alterEvent); + return builder; + } + @Override public StringBuilder visit(Statements statements, S context) { statements.accept(this, context); @@ -444,6 +452,12 @@ public StringBuilder visit(CreateUser createUser, S context) { return builder; } + @Override + public StringBuilder visit(CreateEvent createEvent, S context) { + builder.append(createEvent); + return builder; + } + @Override public StringBuilder visit(CreateSequence createSequence, S context) { new CreateSequenceDeParser(builder).deParse(createSequence); 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 2d4fd3e0b..4a4f01905 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 @@ -38,6 +38,8 @@ import net.sf.jsqlparser.statement.analyze.Analyze; import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.database.CreateDatabase; +import net.sf.jsqlparser.statement.create.event.AlterEvent; +import net.sf.jsqlparser.statement.create.event.CreateEvent; import net.sf.jsqlparser.statement.create.function.CreateFunction; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.policy.CreatePolicy; @@ -174,6 +176,14 @@ public Void visit(Alter alter, S context) { return null; } + @Override + public Void visit(AlterEvent alterEvent, S context) { + if (alterEvent.getBody() != null) { + alterEvent.getBody().accept(this, context); + } + return null; + } + @Override public Void visit(Statements statements, S context) { statements.forEach(s -> s.accept(this, context)); @@ -313,6 +323,14 @@ public Void visit(CreateUser createUser, S context) { return null; } + @Override + public Void visit(CreateEvent createEvent, S context) { + if (createEvent.getBody() != null) { + createEvent.getBody().accept(this, context); + } + return null; + } + @Override public Void visit(CreateSequence createSequence, S context) { getValidator(CreateSequenceValidator.class).validate(createSequence); @@ -469,6 +487,10 @@ public void visit(Alter alter) { visit(alter, null); } + public void visit(AlterEvent alterEvent) { + visit(alterEvent, null); + } + public void visit(Statements statements) { visit(statements, null); } @@ -549,6 +571,10 @@ public void visit(CreateUser createUser) { visit(createUser, null); } + public void visit(CreateEvent createEvent) { + visit(createEvent, null); + } + public void visit(CreateSequence createSequence) { visit(createSequence, null); } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 3745afc27..003715701 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -48,6 +48,7 @@ import net.sf.jsqlparser.statement.alter.*; import net.sf.jsqlparser.statement.alter.sequence.*; import net.sf.jsqlparser.statement.comment.*; import net.sf.jsqlparser.statement.create.database.*; +import net.sf.jsqlparser.statement.create.event.*; import net.sf.jsqlparser.statement.create.function.*; import net.sf.jsqlparser.statement.create.index.*; import net.sf.jsqlparser.statement.create.policy.*; @@ -1575,6 +1576,7 @@ String NonReservedWord() : | tk= | tk= | tk= + | tk= | tk= | tk= | tk= @@ -1619,11 +1621,14 @@ String NonReservedWord() : | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk= + | tk= + | tk= | tk= | tk= | tk= @@ -1812,6 +1817,7 @@ String NonReservedWord() : | tk= | tk= | tk= + | tk= | tk= | tk= | tk= @@ -1828,8 +1834,10 @@ String NonReservedWord() : | tk= | tk= | tk= + | tk= | tk= | tk= + | tk= | tk= | tk= | tk= @@ -11624,6 +11632,131 @@ CreateUser CreateUser(): { return createUser; } } +EventSchedule MySqlEventSchedule(): +{ + EventSchedule schedule = new EventSchedule(); + Expression expression = null; + Token unitToken = null; + String unit = null; +} +{ + + ( + expression=Expression() { + schedule.setType(EventSchedule.Type.AT); + schedule.setExecuteAt(expression); + } + | + expression=MySqlEventIntervalValue() { + schedule.setType(EventSchedule.Type.EVERY); + schedule.setInterval(expression); + } + ( LOOKAHEAD({ getToken(1).kind == K_DATE_LITERAL }) + unitToken= { schedule.setIntervalUnit(unitToken.image); } + | unit=RelObjectName() { schedule.setIntervalUnit(unit); } ) + [ expression=Expression() { schedule.setStarts(expression); } ] + [ expression=Expression() { schedule.setEnds(expression); } ] + ) + { return schedule; } +} + +Expression MySqlEventIntervalValue(): +{ + Token value = null; + Expression expression = null; +} +{ + ( + value= { expression = new LongValue(value.image); } + | + value= { expression = new DoubleValue(value.image); } + | + value= { expression = new StringValue(value.image); } + ) + { return expression; } +} + +Boolean MySqlEventCompletion(): +{ + boolean preserve = true; +} +{ + [ { preserve = false; } ] + LOOKAHEAD({ "PRESERVE".equalsIgnoreCase(getToken(1).image) }) + + { return preserve; } +} + +EventStatus MySqlEventStatus(): +{ + EventStatus status = null; +} +{ + ( + { status = EventStatus.ENABLE; } + | + { status = EventStatus.DISABLE; } + [ LOOKAHEAD(2) { status = EventStatus.DISABLE_ON_SLAVE; } ] + ) + { return status; } +} + +CreateEvent CreateEvent(): +{ + CreateEvent createEvent = new CreateEvent(); + Table event = null; + EventSchedule schedule = null; + Boolean preserve = null; + EventStatus status = null; + Token comment = null; + Statement body = null; +} +{ + + [ LOOKAHEAD(3) { createEvent.setIfNotExists(true); } ] + event=Table() { createEvent.setEvent(event); } + schedule=MySqlEventSchedule() { createEvent.setSchedule(schedule); } + [ LOOKAHEAD(2) preserve=MySqlEventCompletion() { + createEvent.setOnCompletionPreserve(preserve); + } ] + [ LOOKAHEAD({ getToken(1).kind == K_ENABLE || getToken(1).kind == K_DISABLE }) + status=MySqlEventStatus() { createEvent.setStatus(status); } ] + [ LOOKAHEAD({ getToken(1).kind == K_COMMENT }) comment= { + createEvent.setComment(new StringValue(comment.image)); + } ] + body=SingleStatement() { createEvent.setBody(body); } + { return createEvent; } +} + +AlterEvent AlterEvent(): +{ + AlterEvent alterEvent = new AlterEvent(); + Table event = null; + Table renameTo = null; + EventSchedule schedule = null; + Boolean preserve = null; + EventStatus status = null; + Token comment = null; + Statement body = null; +} +{ + event=Table() { alterEvent.setEvent(event); } + [ LOOKAHEAD({ getToken(1).kind == K_ON && getToken(2).kind == K_SCHEDULE }) + schedule=MySqlEventSchedule() { alterEvent.setSchedule(schedule); } ] + [ LOOKAHEAD({ getToken(1).kind == K_ON && getToken(2).kind == K_COMPLETION }) + preserve=MySqlEventCompletion() { alterEvent.setOnCompletionPreserve(preserve); } ] + [ LOOKAHEAD({ getToken(1).kind == K_RENAME }) + renameTo=Table() { alterEvent.setRenameTo(renameTo); } ] + [ LOOKAHEAD({ getToken(1).kind == K_ENABLE || getToken(1).kind == K_DISABLE }) + status=MySqlEventStatus() { alterEvent.setStatus(status); } ] + [ LOOKAHEAD({ getToken(1).kind == K_COMMENT }) comment= { + alterEvent.setComment(new StringValue(comment.image)); + } ] + [ LOOKAHEAD({ getToken(1).kind == K_DO }) + body=SingleStatement() { alterEvent.setBody(body); } ] + { return alterEvent; } +} + CreateSchema CreateSchema(): { Token tk = null; @@ -14287,6 +14420,8 @@ Statement Alter(): ( ( + statement = AlterEvent() + | statement = AlterTable() | statement = AlterSession() @@ -14814,6 +14949,8 @@ Statement Create(): ( statement = CreateUser() | + statement = CreateEvent() + | statement = CreateFunctionStatement(isUsingOrReplace) | statement = CreateSchema() diff --git a/src/test/java/net/sf/jsqlparser/statement/create/event/EventStatementTest.java b/src/test/java/net/sf/jsqlparser/statement/create/event/EventStatementTest.java new file mode 100644 index 000000000..95f406388 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/create/event/EventStatementTest.java @@ -0,0 +1,95 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.create.event; + +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.statement.truncate.Truncate; +import net.sf.jsqlparser.statement.update.Update; +import org.junit.jupiter.api.Test; + +class EventStatementTest { + + @Test + void parsesCreateEventIntoStructuredProperties() throws JSQLParserException { + String sql = "CREATE EVENT hourly_rollup ON SCHEDULE EVERY 6 HOUR " + + "COMMENT 'Refresh aggregate' DO UPDATE app.metrics SET counter = counter + 1"; + CreateEvent event = + assertInstanceOf(CreateEvent.class, assertSqlCanBeParsedAndDeparsed(sql)); + + assertEquals("hourly_rollup", event.getEvent().getFullyQualifiedName()); + assertEquals(EventSchedule.Type.EVERY, event.getSchedule().getType()); + assertEquals(6, assertInstanceOf(LongValue.class, event.getSchedule().getInterval()) + .getValue()); + assertEquals("HOUR", event.getSchedule().getIntervalUnit()); + assertEquals("Refresh aggregate", event.getComment().getValue()); + assertInstanceOf(Update.class, event.getBody()); + } + + @Test + void parsesRecurringScheduleBounds() throws JSQLParserException { + String sql = "ALTER EVENT hourly_rollup ON SCHEDULE EVERY 12 HOUR " + + "STARTS CURRENT_TIMESTAMP + INTERVAL 4 HOUR " + + "ENDS CURRENT_TIMESTAMP + INTERVAL 2 DAY"; + AlterEvent event = + assertInstanceOf(AlterEvent.class, assertSqlCanBeParsedAndDeparsed(sql)); + + assertEquals(EventSchedule.Type.EVERY, event.getSchedule().getType()); + assertEquals("HOUR", event.getSchedule().getIntervalUnit()); + assertEquals("CURRENT_TIMESTAMP + INTERVAL 4 HOUR", + event.getSchedule().getStarts().toString()); + assertEquals("CURRENT_TIMESTAMP + INTERVAL 2 DAY", + event.getSchedule().getEnds().toString()); + } + + @Test + void parsesOneTimeScheduleAndReplacementBody() throws JSQLParserException { + String sql = "ALTER EVENT hourly_rollup ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY " + + "DO TRUNCATE TABLE app.metrics"; + AlterEvent event = + assertInstanceOf(AlterEvent.class, assertSqlCanBeParsedAndDeparsed(sql)); + + assertEquals(EventSchedule.Type.AT, event.getSchedule().getType()); + assertEquals("CURRENT_TIMESTAMP + INTERVAL 1 DAY", + event.getSchedule().getExecuteAt().toString()); + assertInstanceOf(Truncate.class, event.getBody()); + } + + @Test + void parsesStatusAndRenameOperations() throws JSQLParserException { + AlterEvent disabled = assertInstanceOf(AlterEvent.class, + assertSqlCanBeParsedAndDeparsed("ALTER EVENT hourly_rollup DISABLE")); + assertEquals(EventStatus.DISABLE, disabled.getStatus()); + assertNull(disabled.getRenameTo()); + + AlterEvent renamed = assertInstanceOf(AlterEvent.class, + assertSqlCanBeParsedAndDeparsed( + "ALTER EVENT hourly_rollup RENAME TO daily_rollup")); + assertEquals("daily_rollup", renamed.getRenameTo().getFullyQualifiedName()); + } + + @Test + void parsesCompletionPolicy() throws JSQLParserException { + String sql = "CREATE EVENT IF NOT EXISTS one_time_cleanup " + + "ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY " + + "ON COMPLETION NOT PRESERVE ENABLE DO TRUNCATE TABLE app.staging"; + CreateEvent event = + assertInstanceOf(CreateEvent.class, assertSqlCanBeParsedAndDeparsed(sql)); + + assertFalse(event.getOnCompletionPreserve()); + assertEquals(EventStatus.ENABLE, event.getStatus()); + } +}