From 10dbb261168ba3f354d1a5832499ef9260ebebe7 Mon Sep 17 00:00:00 2001 From: minleejae Date: Wed, 14 Oct 2026 13:04:17 +0900 Subject: [PATCH] feat: parse PostgreSQL event triggers, REINDEX, SECURITY LABEL and schema elements Add typed CreateEventTrigger, AlterEventTrigger, Reindex and SecurityLabel statements. Event triggers reuse the CREATE TRIGGER routine invocation and the shared trigger and rule enable states; SECURITY LABEL addresses routines through RoutineReference. REINDEX becomes a non-reserved keyword. CREATE SCHEMA parses PostgreSQL schema elements (CREATE TABLE, VIEW, INDEX, SEQUENCE, TRIGGER and GRANT), keeps their names as written and renders them, together with the PATH clause, in toString() and StatementDeParser. Signed-off-by: minleejae --- README.md | 1 + .../sf/jsqlparser/parser/feature/Feature.java | 24 ++ .../statement/StatementFeatureVisitor.java | 27 ++ .../statement/StatementVisitor.java | 36 +++ .../statement/StatementVisitorAdapter.java | 31 ++ .../statement/alter/AlterEventTrigger.java | 131 ++++++++ .../statement/create/schema/CreateSchema.java | 31 +- .../create/trigger/CreateEventTrigger.java | 196 ++++++++++++ .../jsqlparser/statement/reindex/Reindex.java | 182 +++++++++++ .../securitylabel/SecurityLabel.java | 234 +++++++++++++++ .../sf/jsqlparser/util/TablesNamesFinder.java | 31 +- .../util/deparser/StatementDeParser.java | 31 +- .../validation/feature/FeaturesAllowed.java | 4 +- .../validation/feature/PostgresqlVersion.java | 6 +- .../validator/StatementValidator.java | 50 +++ .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 284 +++++++++++++++++- src/site/sphinx/index.rst | 2 + src/site/sphinx/usage.rst | 52 +++- .../create/PostgreSqlEventTriggerTest.java | 183 +++++++++++ .../create/schema/CreateSchemaTest.java | 106 +++++++ .../statement/reindex/ReindexTest.java | 166 ++++++++++ .../securitylabel/SecurityLabelTest.java | 218 ++++++++++++++ 22 files changed, 2001 insertions(+), 25 deletions(-) create mode 100644 src/main/java/net/sf/jsqlparser/statement/alter/AlterEventTrigger.java create mode 100644 src/main/java/net/sf/jsqlparser/statement/create/trigger/CreateEventTrigger.java create mode 100644 src/main/java/net/sf/jsqlparser/statement/reindex/Reindex.java create mode 100644 src/main/java/net/sf/jsqlparser/statement/securitylabel/SecurityLabel.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/create/PostgreSqlEventTriggerTest.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/reindex/ReindexTest.java create mode 100644 src/test/java/net/sf/jsqlparser/statement/securitylabel/SecurityLabelTest.java diff --git a/README.md b/README.md index d39e81ea68..0afa5b6d98 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,7 @@ and missing syntax gets added on demand — [open an issue](https://github.com/J | **DDL** | `CREATE …` · `ALTER …` · `DROP …` | | **MySQL table maintenance** | `ANALYZE` · `CHECK` · `OPTIMIZE` · `REPAIR TABLE` | | **PostgreSQL RLS** | `CREATE POLICY` · `ALTER TABLE … ENABLE`/`DISABLE`/`FORCE`/`NO FORCE ROW LEVEL SECURITY` | +| **PostgreSQL administration** | `CREATE`/`ALTER EVENT TRIGGER` · `REINDEX` · `SECURITY LABEL` | | **Informix constraints** | `ALTER TABLE … ADD CONSTRAINT` with trailing constraint names for primary, unique, foreign and check constraints; enable with `parser.withDialect(Dialect.INFORMIX)` | | **Salesforce SOQL** | `INCLUDES` · `EXCLUDES` | diff --git a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java index 0d3d080fbb..1439ac97a4 100644 --- a/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java +++ b/src/main/java/net/sf/jsqlparser/parser/feature/Feature.java @@ -29,6 +29,7 @@ import net.sf.jsqlparser.statement.TableMaintenanceStatement; import net.sf.jsqlparser.statement.UseStatement; import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.alter.AlterEventTrigger; import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; import net.sf.jsqlparser.statement.analyze.Analyze; import net.sf.jsqlparser.statement.comment.Comment; @@ -40,6 +41,7 @@ import net.sf.jsqlparser.statement.create.sequence.CreateSequence; import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.trigger.CreateEventTrigger; import net.sf.jsqlparser.statement.create.view.AlterView; import net.sf.jsqlparser.statement.create.view.CreateView; import net.sf.jsqlparser.statement.delete.Delete; @@ -48,6 +50,7 @@ import net.sf.jsqlparser.statement.grant.Grant; import net.sf.jsqlparser.statement.merge.Merge; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.reindex.Reindex; import net.sf.jsqlparser.statement.select.Fetch; import net.sf.jsqlparser.statement.select.First; import net.sf.jsqlparser.statement.select.KSQLWindow; @@ -60,6 +63,7 @@ import net.sf.jsqlparser.statement.select.TableFunction; import net.sf.jsqlparser.statement.select.Top; import net.sf.jsqlparser.statement.select.UnPivot; +import net.sf.jsqlparser.statement.securitylabel.SecurityLabel; import net.sf.jsqlparser.statement.show.*; import net.sf.jsqlparser.statement.truncate.Truncate; import net.sf.jsqlparser.statement.update.Update; @@ -607,6 +611,26 @@ public enum Feature { * SQL "CREATE TRIGGER" statement is allowed */ createTrigger, + /** + * PostgreSQL "CREATE EVENT TRIGGER" and "ALTER EVENT TRIGGER" statements are allowed + * + * @see CreateEventTrigger + * @see AlterEventTrigger + */ + createEventTrigger, alterEventTrigger, + /** + * PostgreSQL "REINDEX" statement is allowed; "REINDEX … CONCURRENTLY" additionally requires + * {@link #reindexConcurrently} + * + * @see Reindex + */ + reindex, reindexConcurrently, + /** + * PostgreSQL "SECURITY LABEL" statement is allowed + * + * @see SecurityLabel + */ + securityLabel, /** * SQL "COMMIT" statement is allowed * diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java index 91b2721249..ca3f100a95 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java @@ -36,6 +36,7 @@ import net.sf.jsqlparser.statement.alter.AlterSession; import net.sf.jsqlparser.statement.alter.AlterStatistics; import net.sf.jsqlparser.statement.alter.AlterSubscription; +import net.sf.jsqlparser.statement.alter.AlterEventTrigger; import net.sf.jsqlparser.statement.alter.AlterSystemStatement; import net.sf.jsqlparser.statement.alter.AlterTablespaceMove; import net.sf.jsqlparser.statement.alter.AlterTextSearchConfiguration; @@ -68,6 +69,7 @@ import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; import net.sf.jsqlparser.statement.create.table.CreateTable; import net.sf.jsqlparser.statement.create.textsearch.CreateTextSearchConfiguration; +import net.sf.jsqlparser.statement.create.trigger.CreateEventTrigger; import net.sf.jsqlparser.statement.create.trigger.CreateTrigger; import net.sf.jsqlparser.statement.create.type.CreateType; import net.sf.jsqlparser.statement.create.user.CreateUser; @@ -93,6 +95,8 @@ import net.sf.jsqlparser.statement.oracle.OracleBlock; import net.sf.jsqlparser.statement.oracle.OracleNullStatement; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.reindex.Reindex; +import net.sf.jsqlparser.statement.securitylabel.SecurityLabel; import net.sf.jsqlparser.statement.role.AlterRole; import net.sf.jsqlparser.statement.role.CreateRole; import net.sf.jsqlparser.statement.select.FromItemVisitor; @@ -1065,6 +1069,29 @@ public Void visit(CreateTrigger statement, S context) { return schemaOnly(); } + @Override + public Void visit(CreateEventTrigger statement, S context) { + return schemaOnly(); + } + + @Override + public Void visit(AlterEventTrigger statement, S context) { + return schemaOnly(); + } + + @Override + public Void visit(Reindex statement, S context) { + analysis.claimTopLevel(); + // rebuilds index storage from the indexed rows + analysis.certain(StmtFeature.MODIFIES_SCHEMA, StmtFeature.READS_DATA); + return null; + } + + @Override + public Void visit(SecurityLabel statement, S context) { + return schemaOnly(); + } + @Override public Void visit(CreateType statement, S context) { analysis.claimTopLevel(); diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java index bf0bf3e8b5..e1592dbbd9 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -52,6 +52,7 @@ import net.sf.jsqlparser.statement.alter.Alter; import net.sf.jsqlparser.statement.alter.AlterSession; +import net.sf.jsqlparser.statement.alter.AlterEventTrigger; import net.sf.jsqlparser.statement.alter.AlterSystemStatement; import net.sf.jsqlparser.statement.alter.RenameTableStatement; import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; @@ -66,6 +67,7 @@ import net.sf.jsqlparser.statement.create.sequence.CreateSequence; import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.trigger.CreateEventTrigger; import net.sf.jsqlparser.statement.create.trigger.CreateTrigger; import net.sf.jsqlparser.statement.create.user.CreateUser; import net.sf.jsqlparser.statement.create.view.AlterView; @@ -83,6 +85,8 @@ 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.reindex.Reindex; +import net.sf.jsqlparser.statement.securitylabel.SecurityLabel; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.show.ShowIndexStatement; import net.sf.jsqlparser.statement.show.ShowTablesStatement; @@ -223,6 +227,38 @@ default void visit(CreateTrigger createTrigger) { this.visit(createTrigger, null); } + default T visit(CreateEventTrigger statement, S context) { + return null; + } + + default void visit(CreateEventTrigger statement) { + this.visit(statement, null); + } + + default T visit(AlterEventTrigger statement, S context) { + return null; + } + + default void visit(AlterEventTrigger statement) { + this.visit(statement, null); + } + + default T visit(Reindex statement, S context) { + return null; + } + + default void visit(Reindex statement) { + this.visit(statement, null); + } + + default T visit(SecurityLabel statement, S context) { + return null; + } + + default void visit(SecurityLabel statement) { + this.visit(statement, null); + } + T visit(CreateView createView, S context); default void visit(CreateView createView) { diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java index 8a80763ca3..4a4c86ba03 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -54,6 +54,7 @@ import net.sf.jsqlparser.statement.alter.Alter; import net.sf.jsqlparser.statement.alter.AlterExpression; import net.sf.jsqlparser.statement.alter.AlterSession; +import net.sf.jsqlparser.statement.alter.AlterEventTrigger; import net.sf.jsqlparser.statement.alter.AlterSystemStatement; import net.sf.jsqlparser.statement.alter.RenameTableStatement; import net.sf.jsqlparser.statement.alter.sequence.AlterSequence; @@ -68,6 +69,7 @@ import net.sf.jsqlparser.statement.create.sequence.CreateSequence; import net.sf.jsqlparser.statement.create.synonym.CreateSynonym; import net.sf.jsqlparser.statement.create.table.CreateTable; +import net.sf.jsqlparser.statement.create.trigger.CreateEventTrigger; import net.sf.jsqlparser.statement.create.trigger.CreateTrigger; import net.sf.jsqlparser.statement.create.user.CreateUser; import net.sf.jsqlparser.statement.create.view.AlterView; @@ -88,6 +90,8 @@ import net.sf.jsqlparser.statement.merge.MergeOperationVisitor; import net.sf.jsqlparser.statement.merge.MergeOperationVisitorAdapter; import net.sf.jsqlparser.statement.refresh.RefreshMaterializedViewStatement; +import net.sf.jsqlparser.statement.reindex.Reindex; +import net.sf.jsqlparser.statement.securitylabel.SecurityLabel; import net.sf.jsqlparser.statement.select.FromItemVisitor; import net.sf.jsqlparser.statement.select.FromItemVisitorAdapter; import net.sf.jsqlparser.statement.select.PivotVisitor; @@ -383,6 +387,7 @@ public T visit(CreateIndex createIndex, S context) { @Override public T visit(CreateSchema createSchema, S context) { + createSchema.getStatements().forEach(element -> element.accept(this, context)); return null; } @@ -422,6 +427,32 @@ public T visit(CreateTrigger createTrigger, S context) { return null; } + @Override + public T visit(CreateEventTrigger statement, S context) { + statement.visitExpressions(expression -> expression.accept(expressionVisitor, context)); + return null; + } + + @Override + public T visit(AlterEventTrigger statement, S context) { + return null; + } + + @Override + public T visit(Reindex statement, S context) { + if (statement.getTable() != null) { + statement.getTable().accept(fromItemVisitor, context); + } + return null; + } + + @Override + public T visit(SecurityLabel statement, S context) { + statement.visitRelations(table -> table.accept(fromItemVisitor, context)); + expressionVisitor.visitExpression(statement.getLabel(), context); + return null; + } + @Override public T visit(CreateView createView, S context) { createView.getView().accept(fromItemVisitor, context); diff --git a/src/main/java/net/sf/jsqlparser/statement/alter/AlterEventTrigger.java b/src/main/java/net/sf/jsqlparser/statement/alter/AlterEventTrigger.java new file mode 100644 index 0000000000..0efb70b9bc --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/alter/AlterEventTrigger.java @@ -0,0 +1,131 @@ +/*- + * #%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.alter; + +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * A PostgreSQL {@code ALTER EVENT TRIGGER} statement. The enable states are shared with the trigger + * and rule actions of {@link RelationAlterAction}. Each non-null setter selects its action and + * discards the values of the other actions. + */ +public class AlterEventTrigger implements Statement { + public enum Action { + STATE, OWNER, RENAME + } + + private String name; + private Action action; + private RelationAlterAction.EnableState enableState; + private String owner; + private String newName; + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Action getAction() { + return action; + } + + /** {@code ENABLE}, {@code ENABLE REPLICA}, {@code ENABLE ALWAYS} or {@code DISABLE}. */ + public RelationAlterAction.EnableState getEnableState() { + return enableState; + } + + public void setEnableState(RelationAlterAction.EnableState enableState) { + if (enableState != null) { + select(Action.STATE); + } + this.enableState = enableState; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + if (owner != null) { + select(Action.OWNER); + } + this.owner = owner; + } + + public String getNewName() { + return newName; + } + + public void setNewName(String newName) { + if (newName != null) { + select(Action.RENAME); + } + this.newName = newName; + } + + private void select(Action action) { + this.action = action; + enableState = null; + owner = null; + newName = null; + } + + public StringBuilder appendTo(StringBuilder sql) { + sql.append("ALTER EVENT TRIGGER ").append(name); + switch (action) { + case STATE: + sql.append(' ').append(enableState.name().replace('_', ' ')); + break; + case OWNER: + sql.append(" OWNER TO ").append(owner); + break; + case RENAME: + sql.append(" RENAME TO ").append(newName); + break; + default: + throw new IllegalStateException("Unknown event trigger action: " + action); + } + return sql; + } + + @Override + public String toString() { + return appendTo(new StringBuilder()).toString(); + } + + public AlterEventTrigger withName(String name) { + setName(name); + return this; + } + + public AlterEventTrigger withEnableState(RelationAlterAction.EnableState enableState) { + setEnableState(enableState); + return this; + } + + public AlterEventTrigger withOwner(String owner) { + setOwner(owner); + return this; + } + + public AlterEventTrigger withNewName(String newName) { + setNewName(newName); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java b/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java index f8888132f3..6e8d2da999 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/schema/CreateSchema.java @@ -14,6 +14,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import java.util.stream.Collectors; import net.sf.jsqlparser.statement.Statement; @@ -36,7 +37,7 @@ public T accept(StatementVisitor statementVisitor, S context) { } /** - * Add a statement to the schema definition + * Add a schema element, for example a CREATE TABLE or GRANT statement, to the schema definition * * @param statement The statement to be added * @return true if the operation was successful @@ -109,7 +110,8 @@ public void setSchemaPath(List schemaPath) { } /** - * The statements executed as part of the schema creation + * The schema elements created as part of the schema, in source order. Their names are kept as + * written; unqualified names refer to the new schema. * * @return the statements */ @@ -139,7 +141,32 @@ public CreateSchema setIfNotExists(boolean hasIfNotExists) { return this; } + @Override public String toString() { + StringBuilder builder = new StringBuilder(); + return appendTo(builder, builder::append).toString(); + } + + /** + * Appends the schema definition, rendering each schema element with the given writer. + * + * @param builder the target + * @param elementWriter renders one schema element + * @return the target + */ + public StringBuilder appendTo(StringBuilder builder, Consumer elementWriter) { + builder.append(headerSql()); + if (schemaPath != null && !schemaPath.isEmpty()) { + builder.append(" PATH ").append(String.join(", ", schemaPath)); + } + for (Statement statement : statements) { + builder.append(' '); + elementWriter.accept(statement); + } + return builder; + } + + private String headerSql() { String sql = "CREATE SCHEMA"; if (hasIfNotExists) { sql += " IF NOT EXISTS"; diff --git a/src/main/java/net/sf/jsqlparser/statement/create/trigger/CreateEventTrigger.java b/src/main/java/net/sf/jsqlparser/statement/create/trigger/CreateEventTrigger.java new file mode 100644 index 0000000000..daa1ace7c9 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/create/trigger/CreateEventTrigger.java @@ -0,0 +1,196 @@ +/*- + * #%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.trigger; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** + * A PostgreSQL {@code CREATE EVENT TRIGGER} statement. Unlike {@link CreateTrigger}, it fires on a + * database event such as {@code ddl_command_start} rather than on table rows. The function is + * invoked like a {@link CreateTrigger} routine, with the same {@code FUNCTION} or {@code PROCEDURE} + * spelling, but never takes arguments. + */ +public class CreateEventTrigger implements Statement { + + private String name; + private String event; + private final List filters = new ArrayList<>(); + private CreateTrigger.ExecuteKeyword executeKeyword = CreateTrigger.ExecuteKeyword.FUNCTION; + private Function function; + + /** A {@code WHEN} condition, for example {@code TAG IN ('CREATE TABLE', 'DROP TABLE')}. */ + public static class Filter implements Serializable { + private String variable; + private final List values = new ArrayList<>(); + + public Filter() { + // empty constructor + } + + public Filter(String variable, Collection values) { + this.variable = variable; + setValues(values); + } + + /** The filter variable as written, for example {@code TAG}. */ + public String getVariable() { + return variable; + } + + public void setVariable(String variable) { + this.variable = variable; + } + + /** The accepted values in source order. */ + public List getValues() { + return Collections.unmodifiableList(values); + } + + public void setValues(Collection values) { + this.values.clear(); + if (values != null) { + this.values.addAll(values); + } + } + + public Filter addValues(StringValue... values) { + Collections.addAll(this.values, values); + return this; + } + + public StringBuilder appendTo(StringBuilder sql, Consumer expressionWriter) { + sql.append(variable).append(" IN ("); + for (int i = 0; i < values.size(); i++) { + if (i > 0) { + sql.append(", "); + } + expressionWriter.accept(values.get(i)); + } + return sql.append(')'); + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder(); + return appendTo(sql, sql::append).toString(); + } + } + + @Override + public T accept(StatementVisitor statementVisitor, S context) { + return statementVisitor.visit(this, context); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** The event name as written, for example {@code ddl_command_start} or {@code sql_drop}. */ + public String getEvent() { + return event; + } + + public void setEvent(String event) { + this.event = event; + } + + /** The {@code WHEN} conditions in source order; all of them must match. */ + public List getFilters() { + return Collections.unmodifiableList(filters); + } + + public void setFilters(Collection filters) { + this.filters.clear(); + if (filters != null) { + this.filters.addAll(filters); + } + } + + public CreateEventTrigger addFilters(Filter... filters) { + Collections.addAll(this.filters, filters); + return this; + } + + public CreateTrigger.ExecuteKeyword getExecuteKeyword() { + return executeKeyword; + } + + public void setExecuteKeyword(CreateTrigger.ExecuteKeyword executeKeyword) { + this.executeKeyword = executeKeyword; + } + + /** The invoked function, rendered with its empty argument list. */ + public Function getFunction() { + return function; + } + + public void setFunction(Function function) { + this.function = function; + } + + /** Visits the filter values and the invoked function. */ + public void visitExpressions(Consumer visitor) { + filters.forEach(filter -> filter.getValues().forEach(visitor)); + if (function != null) { + visitor.accept(function); + } + } + + public StringBuilder appendTo(StringBuilder sql, Consumer expressionWriter) { + sql.append("CREATE EVENT TRIGGER ").append(name).append(" ON ").append(event); + for (int i = 0; i < filters.size(); i++) { + sql.append(i == 0 ? " WHEN " : " AND "); + filters.get(i).appendTo(sql, expressionWriter); + } + sql.append(" EXECUTE ").append(executeKeyword).append(' '); + expressionWriter.accept(function); + return sql; + } + + @Override + public String toString() { + StringBuilder sql = new StringBuilder(); + return appendTo(sql, sql::append).toString(); + } + + public CreateEventTrigger withName(String name) { + setName(name); + return this; + } + + public CreateEventTrigger withEvent(String event) { + setEvent(event); + return this; + } + + public CreateEventTrigger withExecuteKeyword(CreateTrigger.ExecuteKeyword executeKeyword) { + setExecuteKeyword(executeKeyword); + return this; + } + + public CreateEventTrigger withFunction(Function function) { + setFunction(function); + return this; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/reindex/Reindex.java b/src/main/java/net/sf/jsqlparser/statement/reindex/Reindex.java new file mode 100644 index 0000000000..5402be1d84 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/reindex/Reindex.java @@ -0,0 +1,182 @@ +/*- + * #%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.reindex; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +/** A PostgreSQL {@code REINDEX} statement. */ +public class Reindex implements Statement { + + public enum Target { + INDEX, TABLE, SCHEMA, DATABASE, SYSTEM + } + + private final List