Skip to content

Commit bc18fdf

Browse files
authored
Support structured PostgreSQL publication and subscription DDL (#2554)
* Support structured PostgreSQL logical replication DDL * Exclude PostgreSQL fixtures from Transact-SQL linting
1 parent 5bdd4bf commit bc18fdf

22 files changed

Lines changed: 1556 additions & 0 deletions

File tree

.codacy.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
---
2+
engines:
3+
tsqllint:
4+
exclude_paths:
5+
# These fixtures use PostgreSQL grammar, not Transact-SQL.
6+
- "src/test/resources/postgresql/**"
27
exclude_paths:
38
- "site/**"

src/main/java/net/sf/jsqlparser/parser/feature/Feature.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ public enum Feature {
559559
* @see CreateSequence
560560
*/
561561
createSequence,
562+
/** Publication and subscription definitions. */
563+
createPublication, alterPublication, createSubscription, alterSubscription,
562564
/**
563565
* Structured type, domain and extension statements.
564566
*/

src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
import net.sf.jsqlparser.statement.alter.AlterDomain;
1616
import net.sf.jsqlparser.statement.create.extension.CreateExtension;
1717
import net.sf.jsqlparser.statement.alter.AlterExtension;
18+
import net.sf.jsqlparser.statement.create.publication.CreatePublication;
19+
import net.sf.jsqlparser.statement.alter.AlterPublication;
20+
import net.sf.jsqlparser.statement.create.subscription.CreateSubscription;
21+
import net.sf.jsqlparser.statement.create.subscription.SubscriptionOption;
22+
import net.sf.jsqlparser.statement.alter.AlterSubscription;
1823

1924
import net.sf.jsqlparser.JSQLParserException;
2025
import net.sf.jsqlparser.expression.Expression;
@@ -895,4 +900,48 @@ public <S> Void visit(AlterExtension statement, S context) {
895900
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
896901
return null;
897902
}
903+
904+
@Override
905+
public <S> Void visit(CreatePublication statement, S context) {
906+
analysis.claimTopLevel();
907+
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
908+
909+
return null;
910+
}
911+
912+
@Override
913+
public <S> Void visit(AlterPublication statement, S context) {
914+
analysis.claimTopLevel();
915+
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
916+
917+
return null;
918+
}
919+
920+
@Override
921+
public <S> Void visit(CreateSubscription statement, S context) {
922+
analysis.claimTopLevel();
923+
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
924+
if (statement.getOptions().stream()
925+
.noneMatch(option -> (option.getKind() == SubscriptionOption.Kind.CONNECT
926+
|| option.getKind() == SubscriptionOption.Kind.ENABLED)
927+
&& Boolean.FALSE.equals(option.getBooleanValue()))) {
928+
// A subscription may start asynchronous replication; the remote contents are unknown.
929+
analysis.possible(StmtFeature.MODIFIES_DATA);
930+
}
931+
return null;
932+
}
933+
934+
@Override
935+
public <S> Void visit(AlterSubscription statement, S context) {
936+
analysis.claimTopLevel();
937+
analysis.certain(StmtFeature.MODIFIES_SCHEMA);
938+
if (statement.getAction() == AlterSubscription.Action.ENABLE
939+
|| statement.getAction() == AlterSubscription.Action.REFRESH_PUBLICATION
940+
|| statement.getAction() == AlterSubscription.Action.SET_PUBLICATION
941+
|| statement.getAction() == AlterSubscription.Action.ADD_PUBLICATION
942+
|| statement.getAction() == AlterSubscription.Action.DROP_PUBLICATION) {
943+
analysis.possible(StmtFeature.MODIFIES_DATA);
944+
}
945+
return null;
946+
}
898947
}

src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
import net.sf.jsqlparser.statement.alter.AlterDomain;
1616
import net.sf.jsqlparser.statement.create.extension.CreateExtension;
1717
import net.sf.jsqlparser.statement.alter.AlterExtension;
18+
import net.sf.jsqlparser.statement.create.publication.CreatePublication;
19+
import net.sf.jsqlparser.statement.alter.AlterPublication;
20+
import net.sf.jsqlparser.statement.create.subscription.CreateSubscription;
21+
import net.sf.jsqlparser.statement.alter.AlterSubscription;
1822

1923
import net.sf.jsqlparser.statement.alter.Alter;
2024
import net.sf.jsqlparser.statement.alter.AlterSession;
@@ -447,4 +451,36 @@ default <S> T visit(AlterExtension statement, S context) {
447451
default void visit(AlterExtension statement) {
448452
visit(statement, null);
449453
}
454+
455+
default <S> T visit(CreatePublication statement, S context) {
456+
return null;
457+
}
458+
459+
default void visit(CreatePublication statement) {
460+
visit(statement, null);
461+
}
462+
463+
default <S> T visit(AlterPublication statement, S context) {
464+
return null;
465+
}
466+
467+
default void visit(AlterPublication statement) {
468+
visit(statement, null);
469+
}
470+
471+
default <S> T visit(CreateSubscription statement, S context) {
472+
return null;
473+
}
474+
475+
default void visit(CreateSubscription statement) {
476+
visit(statement, null);
477+
}
478+
479+
default <S> T visit(AlterSubscription statement, S context) {
480+
return null;
481+
}
482+
483+
default void visit(AlterSubscription statement) {
484+
visit(statement, null);
485+
}
450486
}

src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
import net.sf.jsqlparser.statement.alter.AlterDomain;
1616
import net.sf.jsqlparser.statement.create.extension.CreateExtension;
1717
import net.sf.jsqlparser.statement.alter.AlterExtension;
18+
import net.sf.jsqlparser.statement.create.publication.CreatePublication;
19+
import net.sf.jsqlparser.statement.alter.AlterPublication;
20+
import net.sf.jsqlparser.statement.create.subscription.CreateSubscription;
21+
import net.sf.jsqlparser.statement.alter.AlterSubscription;
1822

1923
import net.sf.jsqlparser.expression.ExpressionVisitor;
2024
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
@@ -656,4 +660,56 @@ public <S> T visit(AlterExtension statement, S context) {
656660
}
657661
return null;
658662
}
663+
664+
@Override
665+
public <S> T visit(CreatePublication statement, S context) {
666+
statement.getTargets().forEach(target -> target.visit(
667+
table -> table.accept(fromItemVisitor, context),
668+
expression -> expression.accept(expressionVisitor, context)));
669+
statement.getOptions().forEach(option -> {
670+
if (option.getValue() != null) {
671+
option.getValue().accept(expressionVisitor, context);
672+
}
673+
});
674+
return null;
675+
}
676+
677+
@Override
678+
public <S> T visit(AlterPublication statement, S context) {
679+
statement.getTargets().forEach(target -> target.visit(
680+
table -> table.accept(fromItemVisitor, context),
681+
expression -> expression.accept(expressionVisitor, context)));
682+
statement.getOptions().forEach(option -> {
683+
if (option.getValue() != null) {
684+
option.getValue().accept(expressionVisitor, context);
685+
}
686+
});
687+
return null;
688+
}
689+
690+
@Override
691+
public <S> T visit(CreateSubscription statement, S context) {
692+
if (statement.getConnection() != null) {
693+
statement.getConnection().accept(expressionVisitor, context);
694+
}
695+
statement.getOptions().forEach(option -> {
696+
if (option.getValue() != null) {
697+
option.getValue().accept(expressionVisitor, context);
698+
}
699+
});
700+
return null;
701+
}
702+
703+
@Override
704+
public <S> T visit(AlterSubscription statement, S context) {
705+
if (statement.getConnection() != null) {
706+
statement.getConnection().accept(expressionVisitor, context);
707+
}
708+
statement.getOptions().forEach(option -> {
709+
if (option.getValue() != null) {
710+
option.getValue().accept(expressionVisitor, context);
711+
}
712+
});
713+
return null;
714+
}
659715
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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 java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.function.Consumer;
15+
import net.sf.jsqlparser.expression.Expression;
16+
import net.sf.jsqlparser.statement.create.publication.PublicationTarget;
17+
import net.sf.jsqlparser.statement.create.publication.PublicationOption;
18+
import net.sf.jsqlparser.statement.Statement;
19+
import net.sf.jsqlparser.statement.StatementVisitor;
20+
21+
public class AlterPublication implements Statement {
22+
private String name;
23+
private Action action;
24+
private List<PublicationTarget> targets = new ArrayList<>();
25+
private List<PublicationOption> options = new ArrayList<>();
26+
private String newName;
27+
28+
public String getName() {
29+
return name;
30+
}
31+
32+
public void setName(String name) {
33+
this.name = name;
34+
}
35+
36+
public Action getAction() {
37+
return action;
38+
}
39+
40+
public void setAction(Action action) {
41+
this.action = action;
42+
}
43+
44+
public List<PublicationTarget> getTargets() {
45+
return targets;
46+
}
47+
48+
public void setTargets(List<PublicationTarget> targets) {
49+
this.targets = targets;
50+
}
51+
52+
public List<PublicationOption> getOptions() {
53+
return options;
54+
}
55+
56+
public void setOptions(List<PublicationOption> options) {
57+
this.options = options;
58+
}
59+
60+
public String getNewName() {
61+
return newName;
62+
}
63+
64+
public void setNewName(String newName) {
65+
this.newName = newName;
66+
}
67+
68+
@Override
69+
public <T, S> T accept(StatementVisitor<T> visitor, S context) {
70+
return visitor.visit(this, context);
71+
}
72+
73+
public enum Action {
74+
ADD, SET, DROP, SET_OPTIONS, OWNER, RENAME
75+
}
76+
77+
public void appendTo(StringBuilder sql, Consumer<Expression> expressions) {
78+
sql.append("ALTER PUBLICATION ").append(name).append(' ');
79+
switch (action) {
80+
case OWNER:
81+
sql.append("OWNER TO ").append(newName);
82+
break;
83+
case RENAME:
84+
sql.append("RENAME TO ").append(newName);
85+
break;
86+
case SET_OPTIONS:
87+
sql.append("SET (");
88+
for (int i = 0; i < options.size(); i++) {
89+
if (i > 0) {
90+
sql.append(", ");
91+
}
92+
options.get(i).appendTo(sql, expressions);
93+
}
94+
sql.append(')');
95+
break;
96+
case ADD:
97+
case SET:
98+
case DROP:
99+
sql.append(action).append(' ');
100+
for (int i = 0; i < targets.size(); i++) {
101+
if (i > 0) {
102+
sql.append(", ");
103+
}
104+
targets.get(i).appendTo(sql, expressions);
105+
}
106+
break;
107+
default:
108+
throw new IllegalStateException("Unknown publication alteration: " + action);
109+
}
110+
}
111+
112+
@Override
113+
public String toString() {
114+
StringBuilder sql = new StringBuilder();
115+
appendTo(sql, sql::append);
116+
return sql.toString();
117+
}
118+
}

0 commit comments

Comments
 (0)