Skip to content

Commit 9bae056

Browse files
committed
feat: support additional PostgreSQL COMMENT targets
1 parent 6312f9e commit 9bae056

13 files changed

Lines changed: 631 additions & 25 deletions

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,9 @@ public enum Feature {
610610
*/
611611
commentOnView,
612612

613+
/** Additional structured catalog targets of COMMENT ON. */
614+
commentOnIndex, commentOnSchema, commentOnSequence, commentOnDomain, commentOnType, commentOnMaterializedView, commentOnFunction, commentOnConstraint,
615+
613616
/**
614617
* SQL "DESCRIBE" statement is allowed
615618
*

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,8 @@ private void adoptAsStatementVisitor(SelectVisitor<T> visitor) {
184184

185185
@Override
186186
public <S> T visit(Comment comment, S context) {
187-
187+
comment.visitRelations(table -> table.accept(fromItemVisitor, context));
188+
expressionVisitor.visitExpression(comment.getComment(), context);
188189
return null;
189190
}
190191

src/main/java/net/sf/jsqlparser/statement/comment/Comment.java

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
package net.sf.jsqlparser.statement.comment;
1111

12+
import java.util.function.Consumer;
1213
import net.sf.jsqlparser.expression.StringValue;
1314
import net.sf.jsqlparser.schema.Column;
1415
import net.sf.jsqlparser.schema.Table;
@@ -20,6 +21,7 @@ public class Comment implements Statement {
2021
private Table table;
2122
private Column column;
2223
private Table view;
24+
private CommentTarget target;
2325
private StringValue comment;
2426

2527
@Override
@@ -33,6 +35,9 @@ public Table getTable() {
3335

3436
public void setTable(Table table) {
3537
this.table = table;
38+
if (table != null) {
39+
target = null;
40+
}
3641
}
3742

3843
public Column getColumn() {
@@ -41,6 +46,9 @@ public Column getColumn() {
4146

4247
public void setColumn(Column column) {
4348
this.column = column;
49+
if (column != null) {
50+
target = null;
51+
}
4452
}
4553

4654
public Table getView() {
@@ -49,6 +57,39 @@ public Table getView() {
4957

5058
public void setView(Table view) {
5159
this.view = view;
60+
if (view != null) {
61+
target = null;
62+
}
63+
}
64+
65+
/** Additional catalog targets; the existing table, column and view accessors remain intact. */
66+
public CommentTarget getTarget() {
67+
return target;
68+
}
69+
70+
public void setTarget(CommentTarget target) {
71+
this.target = target;
72+
if (target != null) {
73+
table = null;
74+
column = null;
75+
view = null;
76+
}
77+
}
78+
79+
public Comment withTarget(CommentTarget target) {
80+
setTarget(target);
81+
return this;
82+
}
83+
84+
/** Visits the relation explicitly named by this comment, without resolving catalog objects. */
85+
public void visitRelations(Consumer<Table> visitor) {
86+
Table relation = table != null ? table
87+
: column != null ? column.getTable()
88+
: view != null ? view
89+
: target != null ? target.getReferencedRelation() : null;
90+
if (relation != null) {
91+
visitor.accept(relation);
92+
}
5293
}
5394

5495
public StringValue getComment() {
@@ -61,17 +102,37 @@ public void setComment(StringValue comment) {
61102

62103
@Override
63104
public String toString() {
64-
String sql = "COMMENT ON ";
105+
StringBuilder builder = new StringBuilder();
106+
return appendTo(builder, builder::append, builder::append, builder::append).toString();
107+
}
108+
109+
public StringBuilder appendTo(StringBuilder builder, Consumer<Table> relationWriter,
110+
Consumer<Column> columnWriter, Consumer<StringValue> commentWriter) {
111+
builder.append("COMMENT ON ");
65112
if (table != null) {
66-
sql += "TABLE " + table + " ";
113+
builder.append("TABLE ");
114+
relationWriter.accept(table);
115+
builder.append(' ');
67116
} else if (column != null) {
68-
sql += "COLUMN " + column + " ";
117+
builder.append("COLUMN ");
118+
columnWriter.accept(column);
119+
builder.append(' ');
69120
} else if (view != null) {
70-
sql += "VIEW " + view + " ";
121+
builder.append("VIEW ");
122+
relationWriter.accept(view);
123+
builder.append(' ');
124+
} else if (target != null) {
125+
target.appendTo(builder, relationWriter);
126+
builder.append(' ');
71127
}
72128
// a null comment stands for PostgreSQL's COMMENT ON ... IS NULL, which removes the comment
73-
sql += "IS " + (comment != null ? comment : "NULL");
74-
return sql;
129+
builder.append("IS ");
130+
if (comment == null) {
131+
builder.append("NULL");
132+
} else {
133+
commentWriter.accept(comment);
134+
}
135+
return builder;
75136
}
76137

77138
public Comment withTable(Table table) {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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.comment;
11+
12+
import java.io.Serializable;
13+
import java.util.function.Consumer;
14+
import net.sf.jsqlparser.schema.Table;
15+
import net.sf.jsqlparser.statement.RoutineReference;
16+
17+
/** A catalog object addressed by COMMENT, rather than a function invocation or a query. */
18+
public class CommentTarget implements Serializable {
19+
public enum Kind {
20+
INDEX, SCHEMA, SEQUENCE, DOMAIN, TYPE, MATERIALIZED_VIEW, FUNCTION, CONSTRAINT
21+
}
22+
23+
private Kind kind;
24+
private Table name;
25+
private RoutineReference routine;
26+
private Table relation;
27+
private boolean onDomain;
28+
29+
public Kind getKind() {
30+
return kind;
31+
}
32+
33+
public void setKind(Kind kind) {
34+
this.kind = kind;
35+
}
36+
37+
/** The object's identifier; using Table preserves the individual name components. */
38+
public Table getName() {
39+
return name;
40+
}
41+
42+
public void setName(Table name) {
43+
this.name = name;
44+
}
45+
46+
public RoutineReference getRoutine() {
47+
return routine;
48+
}
49+
50+
public void setRoutine(RoutineReference routine) {
51+
this.routine = routine;
52+
}
53+
54+
/** The table or domain owning a constraint, distinguished by {@link #isOnDomain()}. */
55+
public Table getRelation() {
56+
return relation;
57+
}
58+
59+
public void setRelation(Table relation) {
60+
this.relation = relation;
61+
}
62+
63+
public boolean isOnDomain() {
64+
return onDomain;
65+
}
66+
67+
public void setOnDomain(boolean onDomain) {
68+
this.onDomain = onDomain;
69+
}
70+
71+
/** Returns only an explicitly named table/view, never an index, type, function or domain. */
72+
public Table getReferencedRelation() {
73+
if (kind == Kind.MATERIALIZED_VIEW) {
74+
return name;
75+
}
76+
return kind == Kind.CONSTRAINT && !onDomain ? relation : null;
77+
}
78+
79+
public StringBuilder appendTo(StringBuilder builder, Consumer<Table> relationWriter) {
80+
builder.append(kind.name().replace('_', ' ')).append(' ');
81+
if (kind == Kind.FUNCTION) {
82+
builder.append(routine);
83+
} else if (kind == Kind.MATERIALIZED_VIEW) {
84+
relationWriter.accept(name);
85+
} else {
86+
builder.append(name);
87+
if (kind == Kind.CONSTRAINT) {
88+
builder.append(" ON ");
89+
if (onDomain) {
90+
builder.append("DOMAIN ").append(relation);
91+
} else {
92+
relationWriter.accept(relation);
93+
}
94+
}
95+
}
96+
return builder;
97+
}
98+
99+
@Override
100+
public String toString() {
101+
StringBuilder builder = new StringBuilder();
102+
return appendTo(builder, builder::append).toString();
103+
}
104+
}

src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2006,15 +2006,7 @@ public void visit(Block block) {
20062006

20072007
@Override
20082008
public <S> Void visit(Comment comment, S context) {
2009-
if (comment.getTable() != null) {
2010-
visit(comment.getTable(), context);
2011-
}
2012-
if (comment.getColumn() != null) {
2013-
Table table = comment.getColumn().getTable();
2014-
if (table != null) {
2015-
visit(table, context);
2016-
}
2017-
}
2009+
comment.visitRelations(table -> visit(table, context));
20182010
return null;
20192011
}
20202012

src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,10 @@ public <S> StringBuilder visit(Block block, S context) {
436436

437437
@Override
438438
public <S> StringBuilder visit(Comment comment, S context) {
439-
builder.append(comment.toString());
440-
return builder;
439+
return comment.appendTo(builder,
440+
table -> table.accept(selectDeParser, context),
441+
column -> column.accept(expressionDeParser, context),
442+
literal -> literal.accept(expressionDeParser, context));
441443
}
442444

443445
@Override

src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ public enum PostgresqlVersion implements Version {
9090
Feature.commentOnTable,
9191
Feature.commentOnColumn,
9292
Feature.commentOnView,
93+
Feature.commentOnIndex, Feature.commentOnSchema, Feature.commentOnSequence,
94+
Feature.commentOnDomain, Feature.commentOnType,
95+
Feature.commentOnMaterializedView,
96+
Feature.commentOnFunction, Feature.commentOnConstraint,
9397

9498
// https://www.postgresql.org/docs/current/sql-createsequence.html
9599
Feature.createSequence,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.util.validation.validator;
11+
12+
import net.sf.jsqlparser.parser.feature.Feature;
13+
import net.sf.jsqlparser.statement.comment.Comment;
14+
import net.sf.jsqlparser.statement.comment.CommentTarget;
15+
import net.sf.jsqlparser.util.validation.ValidationCapability;
16+
17+
public class CommentValidator extends AbstractValidator<Comment> {
18+
@Override
19+
public void validate(Comment comment) {
20+
for (ValidationCapability capability : getCapabilities()) {
21+
validateFeature(capability, Feature.comment);
22+
validateOptionalFeature(capability, comment.getTable(), Feature.commentOnTable);
23+
validateOptionalFeature(capability, comment.getColumn(), Feature.commentOnColumn);
24+
validateOptionalFeature(capability, comment.getView(), Feature.commentOnView);
25+
if (comment.getTarget() != null) {
26+
validateFeature(capability, targetFeature(comment.getTarget().getKind()));
27+
}
28+
}
29+
}
30+
31+
private static Feature targetFeature(CommentTarget.Kind kind) {
32+
switch (kind) {
33+
case INDEX:
34+
return Feature.commentOnIndex;
35+
case SCHEMA:
36+
return Feature.commentOnSchema;
37+
case SEQUENCE:
38+
return Feature.commentOnSequence;
39+
case DOMAIN:
40+
return Feature.commentOnDomain;
41+
case TYPE:
42+
return Feature.commentOnType;
43+
case MATERIALIZED_VIEW:
44+
return Feature.commentOnMaterializedView;
45+
case FUNCTION:
46+
return Feature.commentOnFunction;
47+
case CONSTRAINT:
48+
return Feature.commentOnConstraint;
49+
default:
50+
throw new IllegalArgumentException("Unknown COMMENT target: " + kind);
51+
}
52+
}
53+
}

src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@
9797
import net.sf.jsqlparser.statement.update.ParenthesedUpdate;
9898
import net.sf.jsqlparser.statement.update.Update;
9999
import net.sf.jsqlparser.statement.upsert.Upsert;
100-
import net.sf.jsqlparser.util.validation.ValidationCapability;
101100
import net.sf.jsqlparser.util.validation.metadata.NamedObject;
102101

103102
/**
@@ -322,12 +321,7 @@ public <S> Void visit(Block block, S context) {
322321

323322
@Override
324323
public <S> Void visit(Comment comment, S context) {
325-
for (ValidationCapability c : getCapabilities()) {
326-
validateFeature(c, Feature.comment);
327-
validateOptionalFeature(c, comment.getTable(), Feature.commentOnTable);
328-
validateOptionalFeature(c, comment.getColumn(), Feature.commentOnColumn);
329-
validateOptionalFeature(c, comment.getView(), Feature.commentOnView);
330-
}
324+
getValidator(CommentValidator.class).validate(comment);
331325
return null;
332326
}
333327

0 commit comments

Comments
 (0)