Skip to content

Commit 54b851c

Browse files
committed
feat: share PostgreSQL trigger and rule enable states
Signed-off-by: minleejae <mmj9808@gmail.com>
1 parent 78d3de8 commit 54b851c

3 files changed

Lines changed: 117 additions & 22 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;
@@ -36,7 +36,7 @@ public class RelationAlterAction extends AlterExpression {
3636
private List<String> resetOptions;
3737

3838
public enum Kind {
39-
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
39+
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
4040
}
4141

4242
public enum ColumnAction {
@@ -47,6 +47,12 @@ public enum ReplicaIdentity {
4747
DEFAULT, FULL, NOTHING, USING_INDEX
4848
}
4949

50+
/** State shared by trigger and rewrite-rule actions. */
51+
public enum EnableState {
52+
ENABLE, DISABLE, ENABLE_ALWAYS, ENABLE_REPLICA
53+
}
54+
55+
/** Compatibility names for existing trigger clients. */
5056
public enum TriggerState {
5157
ENABLE, DISABLE, ENABLE_ALWAYS, ENABLE_REPLICA
5258
}
@@ -55,12 +61,20 @@ public enum TriggerTarget {
5561
}
5662

5763

64+
public EnableState getEnableState() {
65+
return enableState;
66+
}
67+
68+
public void setEnableState(EnableState state) {
69+
enableState = state;
70+
}
71+
5872
public TriggerState getTriggerState() {
59-
return triggerState;
73+
return enableState == null ? null : TriggerState.valueOf(enableState.name());
6074
}
6175

62-
public void setTriggerState(TriggerState triggerState) {
63-
this.triggerState = triggerState;
76+
public void setTriggerState(TriggerState state) {
77+
enableState = state == null ? null : EnableState.valueOf(state.name());
6478
}
6579

6680
public TriggerTarget getTriggerTarget() {
@@ -219,9 +233,12 @@ private void appendDefinition(StringBuilder builder, Consumer<Expression> expres
219233
builder.append("OF ").append(value);
220234
break;
221235
case TRIGGER_STATE:
222-
builder.append(triggerState.name().replace('_', ' ')).append(" TRIGGER ")
236+
builder.append(enableState.name().replace('_', ' ')).append(" TRIGGER ")
223237
.append(triggerTarget == TriggerTarget.NAME ? value : triggerTarget);
224238
break;
239+
case RULE_STATE:
240+
builder.append(enableState.name().replace('_', ' ')).append(" RULE ").append(value);
241+
break;
225242
case RENAME:
226243
builder.append("RENAME TO ").append(newName);
227244
break;

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

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1821,8 +1821,12 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
18211821
}
18221822

18231823
private boolean isPostgreSqlRelationActionAhead() {
1824-
if (!Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect))) { return false; }
18251824
Token first = getToken(1);
1825+
if ((first.kind == K_ENABLE || first.kind == K_DISABLE)
1826+
&& ("RULE".equalsIgnoreCase(getToken(2).image)
1827+
|| (getToken(2).kind == K_ALWAYS || "REPLICA".equalsIgnoreCase(getToken(2).image))
1828+
&& "RULE".equalsIgnoreCase(getToken(3).image))) { return true; }
1829+
if (!Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect))) { return false; }
18261830
if (first.kind == K_VALIDATE || first.kind == K_RESET || isKeywordAhead("INHERIT")
18271831
|| isKeywordAhead("REPLICA") || isKeywordAhead("OWNER") || isKeywordAhead("CLUSTER")
18281832
|| first.kind == K_OF || first.kind == K_NOT && getToken(2).kind == K_OF
@@ -18559,6 +18563,18 @@ AlterRelation PostgreSqlAlterRelation():
1855918563
}
1856018564
}
1856118565

18566+
/** Shared state syntax; each caller applies its own target restrictions. */
18567+
RelationAlterAction.EnableState PostgreSqlEnableState():
18568+
{ RelationAlterAction.EnableState state; }
18569+
{
18570+
( <K_DISABLE> { state = RelationAlterAction.EnableState.DISABLE; }
18571+
| <K_ENABLE> { state = RelationAlterAction.EnableState.ENABLE; }
18572+
[ <K_ALWAYS> { state = RelationAlterAction.EnableState.ENABLE_ALWAYS; }
18573+
| LOOKAHEAD({ isKeywordAhead("REPLICA") }) ContextualKeyword("REPLICA")
18574+
{ state = RelationAlterAction.EnableState.ENABLE_REPLICA; } ] )
18575+
{ return state; }
18576+
}
18577+
1856218578
RelationAlterAction PostgreSqlRelationAction():
1856318579
{
1856418580
RelationAlterAction action = new RelationAlterAction();
@@ -18567,6 +18583,7 @@ RelationAlterAction PostgreSqlRelationAction():
1856718583
Table relation;
1856818584
Expression expression;
1856918585
Long statistics;
18586+
RelationAlterAction.EnableState enableState;
1857018587
List<Index.Option> options;
1857118588
List<String> names = new ArrayList<String>();
1857218589
}
@@ -18580,21 +18597,22 @@ RelationAlterAction PostgreSqlRelationAction():
1858018597
|
1858118598
<K_NOT> <K_OF> { action.setKind(RelationAlterAction.Kind.NOT_OF); }
1858218599
|
18583-
( <K_DISABLE> { action.setTriggerState(RelationAlterAction.TriggerState.DISABLE); }
18584-
| <K_ENABLE> { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE); }
18585-
[ <K_ALWAYS> { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE_ALWAYS); }
18586-
| ContextualKeyword("REPLICA") { action.setTriggerState(RelationAlterAction.TriggerState.ENABLE_REPLICA); } ] )
18587-
<K_TRIGGER>
18588-
( LOOKAHEAD(1) <K_ALL> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.ALL); }
18589-
| LOOKAHEAD(1) <K_USER> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.USER); }
18590-
| name=RelObjectName() { action.setTriggerTarget(RelationAlterAction.TriggerTarget.NAME); action.setValue(name); } )
18591-
{
18592-
action.setKind(RelationAlterAction.Kind.TRIGGER_STATE);
18593-
requireDdlSyntax(action.getTriggerTarget() == RelationAlterAction.TriggerTarget.NAME
18594-
|| action.getTriggerState() == RelationAlterAction.TriggerState.ENABLE
18595-
|| action.getTriggerState() == RelationAlterAction.TriggerState.DISABLE,
18596-
"ENABLE ALWAYS/REPLICA requires a trigger name");
18597-
}
18600+
enableState=PostgreSqlEnableState() { action.setEnableState(enableState); }
18601+
(
18602+
<K_TRIGGER>
18603+
( LOOKAHEAD(1) <K_ALL> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.ALL); }
18604+
| LOOKAHEAD(1) <K_USER> { action.setTriggerTarget(RelationAlterAction.TriggerTarget.USER); }
18605+
| name=RelObjectName() { action.setTriggerTarget(RelationAlterAction.TriggerTarget.NAME); action.setValue(name); } )
18606+
{
18607+
action.setKind(RelationAlterAction.Kind.TRIGGER_STATE);
18608+
requireDdlSyntax(action.getTriggerTarget() == RelationAlterAction.TriggerTarget.NAME
18609+
|| enableState == RelationAlterAction.EnableState.ENABLE
18610+
|| enableState == RelationAlterAction.EnableState.DISABLE,
18611+
"ENABLE ALWAYS/REPLICA requires a trigger name");
18612+
}
18613+
| ContextualKeyword("RULE") name=RelObjectName()
18614+
{ action.setKind(RelationAlterAction.Kind.RULE_STATE); action.setValue(name); }
18615+
)
1859818616
|
1859918617
<K_RENAME>
1860018618
(
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)