Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

/** Structured PostgreSQL property actions shared by tables, indexes and views. */
public class RelationAlterAction extends AlterExpression {
private TriggerState triggerState;
private EnableState enableState;
private TriggerTarget triggerTarget;
private Kind kind;
private ColumnAction columnAction;
Expand Down Expand Up @@ -46,7 +46,7 @@ public void setGenerationExpression(Expression expression) {
private List<String> resetOptions;

public enum Kind {
RENAME, RENAME_COLUMN, OWNER, SET_SCHEMA, SET_TABLESPACE, SET_ACCESS_METHOD, SET_OPTIONS, RESET_OPTIONS, ALTER_COLUMN, ATTACH_PARTITION, DEPENDS_ON_EXTENSION, VALIDATE_CONSTRAINT, INHERIT, ALTER_CONSTRAINT_INHERIT, REPLICA_IDENTITY, CLUSTER_ON, SET_WITHOUT_CLUSTER, SET_WITHOUT_OIDS, SET_LOGGED, SET_UNLOGGED, OF, NOT_OF, TRIGGER_STATE
RENAME, RENAME_COLUMN, OWNER, SET_SCHEMA, SET_TABLESPACE, SET_ACCESS_METHOD, SET_OPTIONS, RESET_OPTIONS, ALTER_COLUMN, ATTACH_PARTITION, DEPENDS_ON_EXTENSION, VALIDATE_CONSTRAINT, INHERIT, ALTER_CONSTRAINT_INHERIT, REPLICA_IDENTITY, CLUSTER_ON, SET_WITHOUT_CLUSTER, SET_WITHOUT_OIDS, SET_LOGGED, SET_UNLOGGED, OF, NOT_OF, TRIGGER_STATE, RULE_STATE
}

public enum ColumnAction {
Expand All @@ -57,6 +57,12 @@ public enum ReplicaIdentity {
DEFAULT, FULL, NOTHING, USING_INDEX
}

/** State shared by trigger and rewrite-rule actions. */
public enum EnableState {
ENABLE, DISABLE, ENABLE_ALWAYS, ENABLE_REPLICA
}

/** Compatibility names for existing trigger clients. */
public enum TriggerState {
ENABLE, DISABLE, ENABLE_ALWAYS, ENABLE_REPLICA
}
Expand All @@ -65,12 +71,20 @@ public enum TriggerTarget {
}


public EnableState getEnableState() {
return enableState;
}

public void setEnableState(EnableState state) {
enableState = state;
}

public TriggerState getTriggerState() {
return triggerState;
return enableState == null ? null : TriggerState.valueOf(enableState.name());
}

public void setTriggerState(TriggerState triggerState) {
this.triggerState = triggerState;
public void setTriggerState(TriggerState state) {
enableState = state == null ? null : EnableState.valueOf(state.name());
}

public TriggerTarget getTriggerTarget() {
Expand Down Expand Up @@ -229,9 +243,12 @@ private void appendDefinition(StringBuilder builder, Consumer<Expression> expres
builder.append("OF ").append(value);
break;
case TRIGGER_STATE:
builder.append(triggerState.name().replace('_', ' ')).append(" TRIGGER ")
builder.append(enableState.name().replace('_', ' ')).append(" TRIGGER ")
.append(triggerTarget == TriggerTarget.NAME ? value : triggerTarget);
break;
case RULE_STATE:
builder.append(enableState.name().replace('_', ' ')).append(" RULE ").append(value);
break;
case RENAME:
builder.append("RENAME TO ").append(newName);
break;
Expand Down
48 changes: 33 additions & 15 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -1842,6 +1842,10 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {

private boolean isPostgreSqlRelationActionAhead() {
Token first = getToken(1);
if ((first.kind == K_ENABLE || first.kind == K_DISABLE)
&& ("RULE".equalsIgnoreCase(getToken(2).image)
|| (getToken(2).kind == K_ALWAYS || "REPLICA".equalsIgnoreCase(getToken(2).image))
&& "RULE".equalsIgnoreCase(getToken(3).image))) { return true; }
int columnActionOffset = getToken(2).kind == K_COLUMN ? 4 : 3;
if (first.kind == K_ALTER && getToken(columnActionOffset).kind == K_SET
&& "EXPRESSION".equalsIgnoreCase(getToken(columnActionOffset + 1).image)) {
Expand Down Expand Up @@ -18608,6 +18612,18 @@ AlterRelation PostgreSqlAlterRelation():
}
}

/** Shared state syntax; each caller applies its own target restrictions. */
RelationAlterAction.EnableState PostgreSqlEnableState():
{ RelationAlterAction.EnableState state; }
{
( <K_DISABLE> { state = RelationAlterAction.EnableState.DISABLE; }
| <K_ENABLE> { state = RelationAlterAction.EnableState.ENABLE; }
[ <K_ALWAYS> { state = RelationAlterAction.EnableState.ENABLE_ALWAYS; }
| LOOKAHEAD({ isKeywordAhead("REPLICA") }) ContextualKeyword("REPLICA")
{ state = RelationAlterAction.EnableState.ENABLE_REPLICA; } ] )
{ return state; }
}

RelationAlterAction PostgreSqlRelationAction():
{
RelationAlterAction action = new RelationAlterAction();
Expand All @@ -18616,6 +18632,7 @@ RelationAlterAction PostgreSqlRelationAction():
Table relation;
Expression expression;
Long statistics;
RelationAlterAction.EnableState enableState;
List<Index.Option> options;
List<String> names = new ArrayList<String>();
}
Expand All @@ -18629,21 +18646,22 @@ RelationAlterAction PostgreSqlRelationAction():
|
<K_NOT> <K_OF> { action.setKind(RelationAlterAction.Kind.NOT_OF); }
|
( <K_DISABLE> { action.setTriggerState(RelationAlterAction.TriggerState.DISABLE); }
| <K_ENABLE> { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE); }
[ <K_ALWAYS> { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE_ALWAYS); }
| ContextualKeyword("REPLICA") { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE_REPLICA); } ] )
<K_TRIGGER>
( LOOKAHEAD(1) <K_ALL> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.ALL); }
| LOOKAHEAD(1) <K_USER> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.USER); }
| name=RelObjectName() { action.setTriggerTarget(RelationAlterAction.TriggerTarget.NAME); action.setValue(name); } )
{
action.setKind(RelationAlterAction.Kind.TRIGGER_STATE);
requireDdlSyntax(action.getTriggerTarget() == RelationAlterAction.TriggerTarget.NAME
|| action.getTriggerState() == RelationAlterAction.TriggerState.ENABLE
|| action.getTriggerState() == RelationAlterAction.TriggerState.DISABLE,
"ENABLE ALWAYS/REPLICA requires a trigger name");
}
enableState=PostgreSqlEnableState() { action.setEnableState(enableState); }
(
<K_TRIGGER>
( LOOKAHEAD(1) <K_ALL> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.ALL); }
| LOOKAHEAD(1) <K_USER> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.USER); }
| name=RelObjectName() { action.setTriggerTarget(RelationAlterAction.TriggerTarget.NAME); action.setValue(name); } )
{
action.setKind(RelationAlterAction.Kind.TRIGGER_STATE);
requireDdlSyntax(action.getTriggerTarget() == RelationAlterAction.TriggerTarget.NAME
|| enableState == RelationAlterAction.EnableState.ENABLE
|| enableState == RelationAlterAction.EnableState.DISABLE,
"ENABLE ALWAYS/REPLICA requires a trigger name");
}
| ContextualKeyword("RULE") name=RelObjectName()
{ action.setKind(RelationAlterAction.Kind.RULE_STATE); action.setValue(name); }
)
|
<K_RENAME>
(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*-
* #%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 static org.junit.jupiter.api.Assertions.*;
import java.util.Set;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.util.TablesNamesFinder;
import net.sf.jsqlparser.util.deparser.StatementDeParser;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

class PostgreSqlRuleStateTest {
@ParameterizedTest
@ValueSource(strings = {"ENABLE", "DISABLE", "ENABLE ALWAYS", "ENABLE REPLICA"})
void ruleAndTriggerActionsShareStates(String state) throws JSQLParserException {
Alter rule = (Alter) CCJSqlParserUtil.parse("ALTER TABLE t " + state
+ " RULE \"my rule\", ADD COLUMN z INT");
RelationAlterAction action = assertInstanceOf(RelationAlterAction.class,
rule.getAlterExpressions().get(0));
assertEquals(RelationAlterAction.Kind.RULE_STATE, action.getKind());
assertEquals(state.replace(' ', '_'), action.getEnableState().name());
assertEquals("\"my rule\"", action.getValue());
assertEquals(2, rule.getAlterExpressions().size());
assertEquals(Set.of("t"), new TablesNamesFinder().getTables(rule));
action.setEnableState(RelationAlterAction.EnableState.DISABLE);
action.setValue("new_rule");
StringBuilder output = new StringBuilder();
rule.accept(new StatementDeParser(output));
assertEquals(rule.toString(), output.toString());
assertTrue(output.toString().contains("DISABLE RULE new_rule"));
assertEquals(output.toString(), CCJSqlParserUtil.parse(output.toString()).toString());
Alter trigger = (Alter) CCJSqlParserUtil.parse("ALTER TABLE t " + state + " TRIGGER trg",
p -> p.withDialect(Dialect.POSTGRESQL));
RelationAlterAction triggerAction =
(RelationAlterAction) trigger.getAlterExpressions().get(0);
assertEquals(state.replace(' ', '_'), triggerAction.getTriggerState().name());
triggerAction.setTriggerState(RelationAlterAction.TriggerState.ENABLE_ALWAYS);
assertEquals(RelationAlterAction.EnableState.ENABLE_ALWAYS, triggerAction.getEnableState());
assertEquals(2, CCJSqlParserUtil.parseStatements(rule + "; SELECT 1").size());
}

@ParameterizedTest
@ValueSource(strings = {"ALTER TABLE t ENABLE RULE", "ALTER TABLE t DISABLE ALWAYS RULE r",
"ALTER TABLE t DISABLE REPLICA RULE r", "ALTER INDEX ix ENABLE RULE r",
"ALTER TABLE t ENABLE ALWAYS TRIGGER ALL", "ALTER TABLE t ENABLE REPLICA TRIGGER USER"})
void rejectsWrongStateTargets(String sql) {
assertThrows(JSQLParserException.class,
() -> CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.POSTGRESQL)));
}
}
Loading