Skip to content

Commit 4ff1a6f

Browse files
authored
feat: share PostgreSQL trigger and rule enable states (#2718)
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 0de0e09 commit 4ff1a6f

3 files changed

Lines changed: 116 additions & 21 deletions

File tree

‎src/main/java/net/sf/jsqlparser/statement/alter/RelationAlterAction.java‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

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

4848
public enum Kind {
49-
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
49+
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
5050
}
5151

5252
public enum ColumnAction {
@@ -57,6 +57,12 @@ public enum ReplicaIdentity {
5757
DEFAULT, FULL, NOTHING, USING_INDEX
5858
}
5959

60+
/** State shared by trigger and rewrite-rule actions. */
61+
public enum EnableState {
62+
ENABLE, DISABLE, ENABLE_ALWAYS, ENABLE_REPLICA
63+
}
64+
65+
/** Compatibility names for existing trigger clients. */
6066
public enum TriggerState {
6167
ENABLE, DISABLE, ENABLE_ALWAYS, ENABLE_REPLICA
6268
}
@@ -65,12 +71,20 @@ public enum TriggerTarget {
6571
}
6672

6773

74+
public EnableState getEnableState() {
75+
return enableState;
76+
}
77+
78+
public void setEnableState(EnableState state) {
79+
enableState = state;
80+
}
81+
6882
public TriggerState getTriggerState() {
69-
return triggerState;
83+
return enableState == null ? null : TriggerState.valueOf(enableState.name());
7084
}
7185

72-
public void setTriggerState(TriggerState triggerState) {
73-
this.triggerState = triggerState;
86+
public void setTriggerState(TriggerState state) {
87+
enableState = state == null ? null : EnableState.valueOf(state.name());
7488
}
7589

7690
public TriggerTarget getTriggerTarget() {
@@ -229,9 +243,12 @@ private void appendDefinition(StringBuilder builder, Consumer<Expression> expres
229243
builder.append("OF ").append(value);
230244
break;
231245
case TRIGGER_STATE:
232-
builder.append(triggerState.name().replace('_', ' ')).append(" TRIGGER ")
246+
builder.append(enableState.name().replace('_', ' ')).append(" TRIGGER ")
233247
.append(triggerTarget == TriggerTarget.NAME ? value : triggerTarget);
234248
break;
249+
case RULE_STATE:
250+
builder.append(enableState.name().replace('_', ' ')).append(" RULE ").append(value);
251+
break;
235252
case RENAME:
236253
builder.append("RENAME TO ").append(newName);
237254
break;

‎src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt‎

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,6 +1842,10 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
18421842

18431843
private boolean isPostgreSqlRelationActionAhead() {
18441844
Token first = getToken(1);
1845+
if ((first.kind == K_ENABLE || first.kind == K_DISABLE)
1846+
&& ("RULE".equalsIgnoreCase(getToken(2).image)
1847+
|| (getToken(2).kind == K_ALWAYS || "REPLICA".equalsIgnoreCase(getToken(2).image))
1848+
&& "RULE".equalsIgnoreCase(getToken(3).image))) { return true; }
18451849
int columnActionOffset = getToken(2).kind == K_COLUMN ? 4 : 3;
18461850
if (first.kind == K_ALTER && getToken(columnActionOffset).kind == K_SET
18471851
&& "EXPRESSION".equalsIgnoreCase(getToken(columnActionOffset + 1).image)) {
@@ -18608,6 +18612,18 @@ AlterRelation PostgreSqlAlterRelation():
1860818612
}
1860918613
}
1861018614

18615+
/** Shared state syntax; each caller applies its own target restrictions. */
18616+
RelationAlterAction.EnableState PostgreSqlEnableState():
18617+
{ RelationAlterAction.EnableState state; }
18618+
{
18619+
( <K_DISABLE> { state = RelationAlterAction.EnableState.DISABLE; }
18620+
| <K_ENABLE> { state = RelationAlterAction.EnableState.ENABLE; }
18621+
[ <K_ALWAYS> { state = RelationAlterAction.EnableState.ENABLE_ALWAYS; }
18622+
| LOOKAHEAD({ isKeywordAhead("REPLICA") }) ContextualKeyword("REPLICA")
18623+
{ state = RelationAlterAction.EnableState.ENABLE_REPLICA; } ] )
18624+
{ return state; }
18625+
}
18626+
1861118627
RelationAlterAction PostgreSqlRelationAction():
1861218628
{
1861318629
RelationAlterAction action = new RelationAlterAction();
@@ -18616,6 +18632,7 @@ RelationAlterAction PostgreSqlRelationAction():
1861618632
Table relation;
1861718633
Expression expression;
1861818634
Long statistics;
18635+
RelationAlterAction.EnableState enableState;
1861918636
List<Index.Option> options;
1862018637
List<String> names = new ArrayList<String>();
1862118638
}
@@ -18629,21 +18646,22 @@ RelationAlterAction PostgreSqlRelationAction():
1862918646
|
1863018647
<K_NOT> <K_OF> { action.setKind(RelationAlterAction.Kind.NOT_OF); }
1863118648
|
18632-
( <K_DISABLE> { action.setTriggerState(RelationAlterAction.TriggerState.DISABLE); }
18633-
| <K_ENABLE> { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE); }
18634-
[ <K_ALWAYS> { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE_ALWAYS); }
18635-
| ContextualKeyword("REPLICA") { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE_REPLICA); } ] )
18636-
<K_TRIGGER>
18637-
( LOOKAHEAD(1) <K_ALL> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.ALL); }
18638-
| LOOKAHEAD(1) <K_USER> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.USER); }
18639-
| name=RelObjectName() { action.setTriggerTarget(RelationAlterAction.TriggerTarget.NAME); action.setValue(name); } )
18640-
{
18641-
action.setKind(RelationAlterAction.Kind.TRIGGER_STATE);
18642-
requireDdlSyntax(action.getTriggerTarget() == RelationAlterAction.TriggerTarget.NAME
18643-
|| action.getTriggerState() == RelationAlterAction.TriggerState.ENABLE
18644-
|| action.getTriggerState() == RelationAlterAction.TriggerState.DISABLE,
18645-
"ENABLE ALWAYS/REPLICA requires a trigger name");
18646-
}
18649+
enableState=PostgreSqlEnableState() { action.setEnableState(enableState); }
18650+
(
18651+
<K_TRIGGER>
18652+
( LOOKAHEAD(1) <K_ALL> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.ALL); }
18653+
| LOOKAHEAD(1) <K_USER> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.USER); }
18654+
| name=RelObjectName() { action.setTriggerTarget(RelationAlterAction.TriggerTarget.NAME); action.setValue(name); } )
18655+
{
18656+
action.setKind(RelationAlterAction.Kind.TRIGGER_STATE);
18657+
requireDdlSyntax(action.getTriggerTarget() == RelationAlterAction.TriggerTarget.NAME
18658+
|| enableState == RelationAlterAction.EnableState.ENABLE
18659+
|| enableState == RelationAlterAction.EnableState.DISABLE,
18660+
"ENABLE ALWAYS/REPLICA requires a trigger name");
18661+
}
18662+
| ContextualKeyword("RULE") name=RelObjectName()
18663+
{ action.setKind(RelationAlterAction.Kind.RULE_STATE); action.setValue(name); }
18664+
)
1864718665
|
1864818666
<K_RENAME>
1864918667
(
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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.alter;
11+
12+
import static org.junit.jupiter.api.Assertions.*;
13+
import java.util.Set;
14+
import net.sf.jsqlparser.JSQLParserException;
15+
import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect;
16+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
17+
import net.sf.jsqlparser.util.TablesNamesFinder;
18+
import net.sf.jsqlparser.util.deparser.StatementDeParser;
19+
import org.junit.jupiter.params.ParameterizedTest;
20+
import org.junit.jupiter.params.provider.ValueSource;
21+
22+
class PostgreSqlRuleStateTest {
23+
@ParameterizedTest
24+
@ValueSource(strings = {"ENABLE", "DISABLE", "ENABLE ALWAYS", "ENABLE REPLICA"})
25+
void ruleAndTriggerActionsShareStates(String state) throws JSQLParserException {
26+
Alter rule = (Alter) CCJSqlParserUtil.parse("ALTER TABLE t " + state
27+
+ " RULE \"my rule\", ADD COLUMN z INT");
28+
RelationAlterAction action = assertInstanceOf(RelationAlterAction.class,
29+
rule.getAlterExpressions().get(0));
30+
assertEquals(RelationAlterAction.Kind.RULE_STATE, action.getKind());
31+
assertEquals(state.replace(' ', '_'), action.getEnableState().name());
32+
assertEquals("\"my rule\"", action.getValue());
33+
assertEquals(2, rule.getAlterExpressions().size());
34+
assertEquals(Set.of("t"), new TablesNamesFinder().getTables(rule));
35+
action.setEnableState(RelationAlterAction.EnableState.DISABLE);
36+
action.setValue("new_rule");
37+
StringBuilder output = new StringBuilder();
38+
rule.accept(new StatementDeParser(output));
39+
assertEquals(rule.toString(), output.toString());
40+
assertTrue(output.toString().contains("DISABLE RULE new_rule"));
41+
assertEquals(output.toString(), CCJSqlParserUtil.parse(output.toString()).toString());
42+
Alter trigger = (Alter) CCJSqlParserUtil.parse("ALTER TABLE t " + state + " TRIGGER trg",
43+
p -> p.withDialect(Dialect.POSTGRESQL));
44+
RelationAlterAction triggerAction =
45+
(RelationAlterAction) trigger.getAlterExpressions().get(0);
46+
assertEquals(state.replace(' ', '_'), triggerAction.getTriggerState().name());
47+
triggerAction.setTriggerState(RelationAlterAction.TriggerState.ENABLE_ALWAYS);
48+
assertEquals(RelationAlterAction.EnableState.ENABLE_ALWAYS, triggerAction.getEnableState());
49+
assertEquals(2, CCJSqlParserUtil.parseStatements(rule + "; SELECT 1").size());
50+
}
51+
52+
@ParameterizedTest
53+
@ValueSource(strings = {"ALTER TABLE t ENABLE RULE", "ALTER TABLE t DISABLE ALWAYS RULE r",
54+
"ALTER TABLE t DISABLE REPLICA RULE r", "ALTER INDEX ix ENABLE RULE r",
55+
"ALTER TABLE t ENABLE ALWAYS TRIGGER ALL", "ALTER TABLE t ENABLE REPLICA TRIGGER USER"})
56+
void rejectsWrongStateTargets(String sql) {
57+
assertThrows(JSQLParserException.class,
58+
() -> CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.POSTGRESQL)));
59+
}
60+
}

0 commit comments

Comments
 (0)