Skip to content
Open
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
3 changes: 3 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,9 @@ public enum Feature {
*/
commentOnView,

/** Additional structured catalog targets of COMMENT ON. */
commentOnIndex, commentOnSchema, commentOnSequence, commentOnDomain, commentOnType, commentOnMaterializedView, commentOnFunction, commentOnConstraint,

/**
* SQL "DESCRIBE" statement is allowed
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,8 @@ private void adoptAsStatementVisitor(SelectVisitor<T> visitor) {

@Override
public <S> T visit(Comment comment, S context) {

comment.visitRelations(table -> table.accept(fromItemVisitor, context));
expressionVisitor.visitExpression(comment.getComment(), context);
return null;
}

Expand Down
73 changes: 67 additions & 6 deletions src/main/java/net/sf/jsqlparser/statement/comment/Comment.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package net.sf.jsqlparser.statement.comment;

import java.util.function.Consumer;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
Expand All @@ -20,6 +21,7 @@ public class Comment implements Statement {
private Table table;
private Column column;
private Table view;
private CommentTarget target;
private StringValue comment;

@Override
Expand All @@ -33,6 +35,9 @@ public Table getTable() {

public void setTable(Table table) {
this.table = table;
if (table != null) {
target = null;
}
}

public Column getColumn() {
Expand All @@ -41,6 +46,9 @@ public Column getColumn() {

public void setColumn(Column column) {
this.column = column;
if (column != null) {
target = null;
}
}

public Table getView() {
Expand All @@ -49,6 +57,39 @@ public Table getView() {

public void setView(Table view) {
this.view = view;
if (view != null) {
target = null;
}
}

/** Additional catalog targets; the existing table, column and view accessors remain intact. */
public CommentTarget getTarget() {
return target;
}

public void setTarget(CommentTarget target) {
this.target = target;
if (target != null) {
table = null;
column = null;
view = null;
}
}

public Comment withTarget(CommentTarget target) {
setTarget(target);
return this;
}

/** Visits the relation explicitly named by this comment, without resolving catalog objects. */
public void visitRelations(Consumer<Table> visitor) {
Table relation = table != null ? table
: column != null ? column.getTable()
: view != null ? view
: target != null ? target.getReferencedRelation() : null;
if (relation != null) {
visitor.accept(relation);
}
}

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

@Override
public String toString() {
String sql = "COMMENT ON ";
StringBuilder builder = new StringBuilder();
return appendTo(builder, builder::append, builder::append, builder::append).toString();
}

public StringBuilder appendTo(StringBuilder builder, Consumer<Table> relationWriter,
Consumer<Column> columnWriter, Consumer<StringValue> commentWriter) {
builder.append("COMMENT ON ");
if (table != null) {
sql += "TABLE " + table + " ";
builder.append("TABLE ");
relationWriter.accept(table);
builder.append(' ');
} else if (column != null) {
sql += "COLUMN " + column + " ";
builder.append("COLUMN ");
columnWriter.accept(column);
builder.append(' ');
} else if (view != null) {
sql += "VIEW " + view + " ";
builder.append("VIEW ");
relationWriter.accept(view);
builder.append(' ');
} else if (target != null) {
target.appendTo(builder, relationWriter);
builder.append(' ');
}
// a null comment stands for PostgreSQL's COMMENT ON ... IS NULL, which removes the comment
sql += "IS " + (comment != null ? comment : "NULL");
return sql;
builder.append("IS ");
if (comment == null) {
builder.append("NULL");
} else {
commentWriter.accept(comment);
}
return builder;
}

public Comment withTable(Table table) {
Expand Down
104 changes: 104 additions & 0 deletions src/main/java/net/sf/jsqlparser/statement/comment/CommentTarget.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*-
* #%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.comment;

import java.io.Serializable;
import java.util.function.Consumer;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.RoutineReference;

/** A catalog object addressed by COMMENT, rather than a function invocation or a query. */
public class CommentTarget implements Serializable {
public enum Kind {
INDEX, SCHEMA, SEQUENCE, DOMAIN, TYPE, MATERIALIZED_VIEW, FUNCTION, CONSTRAINT
}

private Kind kind;
private Table name;
private RoutineReference routine;
private Table relation;
private boolean onDomain;

public Kind getKind() {
return kind;
}

public void setKind(Kind kind) {
this.kind = kind;
}

/** The object's identifier; using Table preserves the individual name components. */
public Table getName() {
return name;
}

public void setName(Table name) {
this.name = name;
}

public RoutineReference getRoutine() {
return routine;
}

public void setRoutine(RoutineReference routine) {
this.routine = routine;
}

/** The table or domain owning a constraint, distinguished by {@link #isOnDomain()}. */
public Table getRelation() {
return relation;
}

public void setRelation(Table relation) {
this.relation = relation;
}

public boolean isOnDomain() {
return onDomain;
}

public void setOnDomain(boolean onDomain) {
this.onDomain = onDomain;
}

/** Returns only an explicitly named table/view, never an index, type, function or domain. */
public Table getReferencedRelation() {
if (kind == Kind.MATERIALIZED_VIEW) {
return name;
}
return kind == Kind.CONSTRAINT && !onDomain ? relation : null;
}

public StringBuilder appendTo(StringBuilder builder, Consumer<Table> relationWriter) {
builder.append(kind.name().replace('_', ' ')).append(' ');
if (kind == Kind.FUNCTION) {
builder.append(routine);
} else if (kind == Kind.MATERIALIZED_VIEW) {
relationWriter.accept(name);
} else {
builder.append(name);
if (kind == Kind.CONSTRAINT) {
builder.append(" ON ");
if (onDomain) {
builder.append("DOMAIN ").append(relation);
} else {
relationWriter.accept(relation);
}
}
}
return builder;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return appendTo(builder, builder::append).toString();
}
}
10 changes: 1 addition & 9 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2006,15 +2006,7 @@ public void visit(Block block) {

@Override
public <S> Void visit(Comment comment, S context) {
if (comment.getTable() != null) {
visit(comment.getTable(), context);
}
if (comment.getColumn() != null) {
Table table = comment.getColumn().getTable();
if (table != null) {
visit(table, context);
}
}
comment.visitRelations(table -> visit(table, context));
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,10 @@ public <S> StringBuilder visit(Block block, S context) {

@Override
public <S> StringBuilder visit(Comment comment, S context) {
builder.append(comment.toString());
return builder;
return comment.appendTo(builder,
table -> table.accept(selectDeParser, context),
column -> column.accept(expressionDeParser, context),
literal -> literal.accept(expressionDeParser, context));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public enum PostgresqlVersion implements Version {
Feature.commentOnTable,
Feature.commentOnColumn,
Feature.commentOnView,
Feature.commentOnIndex, Feature.commentOnSchema, Feature.commentOnSequence,
Feature.commentOnDomain, Feature.commentOnType,
Feature.commentOnMaterializedView,
Feature.commentOnFunction, Feature.commentOnConstraint,

// https://www.postgresql.org/docs/current/sql-createsequence.html
Feature.createSequence,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*-
* #%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.util.validation.validator;

import net.sf.jsqlparser.parser.feature.Feature;
import net.sf.jsqlparser.statement.comment.Comment;
import net.sf.jsqlparser.statement.comment.CommentTarget;
import net.sf.jsqlparser.util.validation.ValidationCapability;

public class CommentValidator extends AbstractValidator<Comment> {
@Override
public void validate(Comment comment) {
for (ValidationCapability capability : getCapabilities()) {
validateFeature(capability, Feature.comment);
validateOptionalFeature(capability, comment.getTable(), Feature.commentOnTable);
validateOptionalFeature(capability, comment.getColumn(), Feature.commentOnColumn);
validateOptionalFeature(capability, comment.getView(), Feature.commentOnView);
if (comment.getTarget() != null) {
validateFeature(capability, targetFeature(comment.getTarget().getKind()));
}
}
}

private static Feature targetFeature(CommentTarget.Kind kind) {
switch (kind) {
case INDEX:
return Feature.commentOnIndex;
case SCHEMA:
return Feature.commentOnSchema;
case SEQUENCE:
return Feature.commentOnSequence;
case DOMAIN:
return Feature.commentOnDomain;
case TYPE:
return Feature.commentOnType;
case MATERIALIZED_VIEW:
return Feature.commentOnMaterializedView;
case FUNCTION:
return Feature.commentOnFunction;
case CONSTRAINT:
return Feature.commentOnConstraint;
default:
throw new IllegalArgumentException("Unknown COMMENT target: " + kind);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
import net.sf.jsqlparser.statement.update.ParenthesedUpdate;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.upsert.Upsert;
import net.sf.jsqlparser.util.validation.ValidationCapability;
import net.sf.jsqlparser.util.validation.metadata.NamedObject;

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

@Override
public <S> Void visit(Comment comment, S context) {
for (ValidationCapability c : getCapabilities()) {
validateFeature(c, Feature.comment);
validateOptionalFeature(c, comment.getTable(), Feature.commentOnTable);
validateOptionalFeature(c, comment.getColumn(), Feature.commentOnColumn);
validateOptionalFeature(c, comment.getView(), Feature.commentOnView);
}
getValidator(CommentValidator.class).validate(comment);
return null;
}

Expand Down
Loading
Loading