diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java index 76c865be1f..e252dc93eb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractField.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractField.java @@ -63,7 +63,6 @@ import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.EMPTY_STRING; import static org.jooq.tools.Convert.FALSE_VALUES; import static org.jooq.tools.Convert.TRUE_VALUES; -import static org.jooq.tools.StringUtils.defaultString; import java.math.BigDecimal; import java.sql.Timestamp; @@ -81,6 +80,7 @@ import org.jooq.Binding; import org.jooq.CaseValueStep; import org.jooq.CaseWhenStep; import org.jooq.Clause; +import org.jooq.Comment; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.Configuration; @@ -114,19 +114,23 @@ abstract class AbstractField extends AbstractQueryPart implements Field { private static final Clause[] CLAUSES = { FIELD }; private final Name name; - private final String comment; + private final Comment comment; private final DataType dataType; AbstractField(Name name, DataType type) { - this(name, type, null, type.getBinding()); + this(name, type, null); + } + + AbstractField(Name name, DataType type, Comment comment) { + this(name, type, comment, type.getBinding()); } @SuppressWarnings("unchecked") - AbstractField(Name name, DataType type, String comment, Binding binding) { + AbstractField(Name name, DataType type, Comment comment, Binding binding) { super(); this.name = name; - this.comment = defaultString(comment); + this.comment = comment == null ? CommentImpl.NO_COMMENT : comment; this.dataType = type.asConvertedDataType((Binding) binding); } @@ -226,7 +230,7 @@ abstract class AbstractField extends AbstractQueryPart implements Field { @Override public final String getComment() { - return comment; + return comment.getComment(); } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java index 5a076be5f1..3f72f97102 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTable.java @@ -68,6 +68,7 @@ import java.util.stream.Stream; import org.jooq.Binding; import org.jooq.Catalog; import org.jooq.Clause; +import org.jooq.Comment; import org.jooq.Comparator; import org.jooq.Condition; import org.jooq.Context; @@ -113,7 +114,7 @@ abstract class AbstractTable extends AbstractQueryPart impleme private Schema tableschema; private final Name tablename; - private final String tablecomment; + private final Comment tablecomment; private transient DataType type; /** @@ -137,7 +138,7 @@ abstract class AbstractTable extends AbstractQueryPart impleme */ @Deprecated AbstractTable(String name, Schema schema, String comment) { - this(DSL.name(name), schema, comment); + this(DSL.name(name), schema, DSL.comment(comment)); } AbstractTable(Name name) { @@ -148,10 +149,10 @@ abstract class AbstractTable extends AbstractQueryPart impleme this(name, schema, null); } - AbstractTable(Name name, Schema schema, String comment) { + AbstractTable(Name name, Schema schema, Comment comment) { this.tableschema = schema; this.tablename = name; - this.tablecomment = comment; + this.tablecomment = comment == null ? CommentImpl.NO_COMMENT : comment; } // ------------------------------------------------------------------------ @@ -406,7 +407,7 @@ abstract class AbstractTable extends AbstractQueryPart impleme @Override public final String getComment() { - return tablecomment; + return tablecomment.getComment(); } /** @@ -573,7 +574,7 @@ abstract class AbstractTable extends AbstractQueryPart impleme : type.asConvertedDataType(actualBinding); // [#5999] TODO: Allow for user-defined Names - final TableFieldImpl tableField = new TableFieldImpl(DSL.name(name), actualType, table, comment, actualBinding); + final TableFieldImpl tableField = new TableFieldImpl(DSL.name(name), actualType, table, DSL.comment(comment), actualBinding); // [#1199] The public API of Table returns immutable field lists if (table instanceof TableImpl) { diff --git a/jOOQ/src/main/java/org/jooq/impl/CommentImpl.java b/jOOQ/src/main/java/org/jooq/impl/CommentImpl.java index 2d024e25c6..3ec16fc417 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CommentImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CommentImpl.java @@ -52,10 +52,11 @@ final class CommentImpl extends AbstractQueryPart implements Comment { * Generated UID */ private static final long serialVersionUID = -5034168783226853829L; + static final CommentImpl NO_COMMENT = new CommentImpl(""); private final String comment; CommentImpl(String comment) { - this.comment = comment; + this.comment = comment == null ? "" : comment; } @Override diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 74e52dced4..3a17c3aa91 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -75,6 +75,7 @@ import static org.jooq.impl.Tools.EMPTY_FIELD; import static org.jooq.impl.Tools.EMPTY_QUERYPART; import static org.jooq.impl.Tools.combine; import static org.jooq.impl.Tools.configuration; +import static org.jooq.tools.StringUtils.isEmpty; import java.math.BigDecimal; import java.math.BigInteger; @@ -5815,7 +5816,7 @@ public class DSL { */ @Support public static Comment comment(String comment) { - return new CommentImpl(comment); + return isEmpty(comment) ? CommentImpl.NO_COMMENT : new CommentImpl(comment); } // ------------------------------------------------------------------------- @@ -9123,6 +9124,28 @@ public class DSL { return new TableImpl(name); } + /** + * Create a qualified table, given its table name. + *

+ * This constructs a table reference given the table's qualified name. jOOQ + * will render the table name according to your + * {@link Settings#getRenderNameStyle()} settings. Choose + * {@link RenderNameStyle#QUOTED} to prevent syntax errors and/or SQL + * injection. + *

+ * Example:

+     * // This table...
+     * tableByName("MY_SCHEMA", "MY_TABLE");
+     *
+     * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
+     * [MY_SCHEMA].[MY_TABLE]
+     * 
+ */ + @Support + public static Table table(Name name, Comment comment) { + return new TableImpl(name, null, null, null, comment); + } + /** * Create a qualified field, given its (qualified) field name. *

@@ -9329,6 +9352,37 @@ public class DSL { return new QualifiedField(name, type); } + /** + * Create a qualified field, given its (qualified) field name. + *

+ * This constructs a field reference given the field's qualified name. jOOQ + * will render the field name according to your + * {@link Settings#getRenderNameStyle()} settings. Choose + * {@link RenderNameStyle#QUOTED} to prevent syntax errors and/or SQL + * injection. + *

+ * Example:

+     * // This field...
+     * field(name("MY_SCHEMA", "MY_TABLE", "MY_FIELD"));
+     *
+     * // ... will render this SQL on SQL Server with RenderNameStyle.QUOTED set
+     * [MY_SCHEMA].[MY_TABLE].[MY_FIELD]
+     * 
+ *

+ * Another example:

+     * create.select(field("length({1})", Integer.class, field(name("TITLE"))))
+     *       .from(table(name("T_BOOK")))
+     *       .fetch();
+     *
+     * // ... will execute this SQL on SQL Server:
+     * select length([TITLE]) from [T_BOOK]
+     * 
+ */ + @Support + public static Field field(Name name, DataType type, Comment comment) { + return new QualifiedField(name, type, comment); + } + /** * Create a qualified index reference by name. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index d19f52b608..4ef6558572 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -251,6 +251,8 @@ import org.jooq.Block; import org.jooq.CaseConditionStep; import org.jooq.CaseValueStep; import org.jooq.CaseWhenStep; +import org.jooq.Clause; +import org.jooq.Comment; import org.jooq.CommentOnIsStep; import org.jooq.CommonTableExpression; import org.jooq.Comparator; @@ -259,6 +261,7 @@ import org.jooq.Configuration; import org.jooq.Constraint; import org.jooq.ConstraintForeignKeyOnStep; import org.jooq.ConstraintTypeStep; +import org.jooq.Context; import org.jooq.CreateIndexFinalStep; import org.jooq.CreateIndexStep; import org.jooq.CreateIndexWhereStep; @@ -1598,8 +1601,33 @@ final class ParserImpl implements Parser { } private static final DDLQuery parseCreateTable(ParserContext ctx, boolean temporary) { + final class MutableComment extends AbstractQueryPart implements Comment { + + /** + * Generated UID + */ + private static final long serialVersionUID = -5034168783226853829L; + private String comment = ""; + + @Override + public final void accept(Context c) { + c.visit(inline(comment)); + } + + @Override + public final Clause[] clauses(Context c) { + return null; + } + + @Override + public final String getComment() { + return comment; + } + } + boolean ifNotExists = !temporary && parseKeywordIf(ctx, "IF NOT EXISTS"); - Table tableName = parseTableName(ctx); + MutableComment tableComment = new MutableComment(); + Table tableName = DSL.table(parseTableName(ctx).getQualifiedName(), tableComment); CreateTableStorageStep storageStep; // [#5309] TODO: Move this after the column specification @@ -1624,11 +1652,13 @@ final class ParserImpl implements Parser { do { Name fieldName = parseIdentifier(ctx); DataType type = parseDataType(ctx); + Comment fieldComment = null; boolean nullable = false; boolean defaultValue = false; boolean unique = false; boolean identity = type.identity(); + boolean comment = false; for (;;) { if (!nullable) { @@ -1739,10 +1769,18 @@ final class ParserImpl implements Parser { } } + + if (!comment) { + if (parseKeywordIf(ctx, "COMMENT")) { + fieldComment = parseComment(ctx); + continue; + } + } + break; } - fields.add(field(fieldName, type)); + fields.add(field(fieldName, type, fieldComment)); } while (parseIf(ctx, ',') && (noConstraint = @@ -1844,7 +1882,7 @@ final class ParserImpl implements Parser { } else if ((keyword = parseAndGetKeywordIf(ctx, "COMMENT")) != null) { parseIf(ctx, '='); - storage.add(sql("{0} {1}", keyword, parseStringLiteral(ctx))); + tableComment.comment = parseStringLiteral(ctx); } else if ((keyword = parseAndGetKeywordIf(ctx, "COMPRESSION")) != null) { parseIf(ctx, '='); @@ -2066,10 +2104,12 @@ final class ParserImpl implements Parser { Name fieldName = parseIdentifier(ctx); DataType type = parseDataType(ctx); + Comment fieldComment = null; boolean nullable = false; boolean defaultValue = false; boolean unique = false; + boolean comment = false; for (;;) { if (!nullable) { @@ -2102,10 +2142,17 @@ final class ParserImpl implements Parser { if (parseKeywordIf(ctx, "CHECK")) throw ctx.unexpectedToken(); + if (!comment) { + if (parseKeywordIf(ctx, "COMMENT")) { + fieldComment = parseComment(ctx); + continue; + } + } + break; } - return s1.add(field(fieldName, type), type); + return s1.add(field(fieldName, type, fieldComment), type); } } else if (parseKeywordIf(ctx, "ALTER")) { @@ -5617,6 +5664,10 @@ final class ParserImpl implements Parser { } } + private static final Comment parseComment(ParserContext ctx) { + return DSL.comment(parseStringLiteral(ctx)); + } + private static final String parseStringLiteral(ParserContext ctx) { parseWhitespaceIf(ctx); diff --git a/jOOQ/src/main/java/org/jooq/impl/QualifiedField.java b/jOOQ/src/main/java/org/jooq/impl/QualifiedField.java index bbc41f8bee..d099ac492c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QualifiedField.java +++ b/jOOQ/src/main/java/org/jooq/impl/QualifiedField.java @@ -37,6 +37,7 @@ */ package org.jooq.impl; +import org.jooq.Comment; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Field; @@ -68,6 +69,12 @@ final class QualifiedField extends AbstractField implements TableField type, Comment comment) { + super(name, type, comment); + + this.name = name; + } + // ------------------------------------------------------------------------ // Field API // ------------------------------------------------------------------------ diff --git a/jOOQ/src/main/java/org/jooq/impl/RowField.java b/jOOQ/src/main/java/org/jooq/impl/RowField.java index d80edb549c..765e76f144 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowField.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowField.java @@ -68,7 +68,7 @@ final class RowField extends AbstractField< @SuppressWarnings({ "serial", "unchecked", "rawtypes" }) RowField(final ROW row, Name as) { - super(as, (DataType) SQLDataType.RECORD, "", binding(new Converter() { + super(as, (DataType) SQLDataType.RECORD, CommentImpl.NO_COMMENT, binding(new Converter() { @Override public REC from(final Object t) { // So far, this is only supported for PostgreSQL diff --git a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java index 50529098ec..d3c69fbd5e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableAlias.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableAlias.java @@ -97,7 +97,7 @@ final class TableAlias extends AbstractTable { ? fieldAliases[i] : field.getUnqualifiedName(); - result.add(new TableFieldImpl(name, field.getDataType(), this, field.getComment(), field.getBinding())); + result.add(new TableFieldImpl(name, field.getDataType(), this, DSL.comment(field.getComment()), field.getBinding())); } return new Fields(result); diff --git a/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java index c076e64408..b785d4c2a1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableFieldImpl.java @@ -44,6 +44,7 @@ import static org.jooq.impl.Tools.DataKey.DATA_OMIT_CLAUSE_EVENT_EMISSION; import org.jooq.Binding; import org.jooq.Clause; +import org.jooq.Comment; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Name; @@ -64,7 +65,7 @@ final class TableFieldImpl extends AbstractField impleme private final Table table; - TableFieldImpl(Name name, DataType type, Table table, String comment, Binding binding) { + TableFieldImpl(Name name, DataType type, Table table, Comment comment, Binding binding) { super(name, type, comment, binding); this.table = table; diff --git a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java index ae0826f53d..d361179ecd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/TableImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/TableImpl.java @@ -50,6 +50,7 @@ import java.util.Arrays; import java.util.EnumSet; import org.jooq.Clause; +import org.jooq.Comment; import org.jooq.Context; import org.jooq.Field; import org.jooq.Name; @@ -124,22 +125,30 @@ public class TableImpl extends AbstractTable { } public TableImpl(Name name) { - this(name, null, null, null, null); + this(name, null, null, null, (Comment) null); } public TableImpl(Name name, Schema schema) { - this(name, schema, null, null, null); + this(name, schema, null, null, (Comment) null); } public TableImpl(Name name, Schema schema, Table aliased) { - this(name, schema, aliased, null, null); + this(name, schema, aliased, null, (Comment) null); } public TableImpl(Name name, Schema schema, Table aliased, Field[] parameters) { - this(name, schema, aliased, parameters, null); + this(name, schema, aliased, parameters, (Comment) null); } + /** + * @deprecated - 3.11 - [#7027] - Use {@link #TableImpl(Name, Schema, Table, Field[], Comment)} instead. + */ + @Deprecated public TableImpl(Name name, Schema schema, Table aliased, Field[] parameters, String comment) { + this(name, schema, aliased, parameters, DSL.comment(comment)); + } + + public TableImpl(Name name, Schema schema, Table aliased, Field[] parameters, Comment comment) { super(name, schema, comment); this.fields = new Fields(); diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java index ad57467836..279b17c594 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTFieldImpl.java @@ -39,6 +39,7 @@ package org.jooq.impl; import org.jooq.Binding; +import org.jooq.Comment; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Name; @@ -57,7 +58,7 @@ final class UDTFieldImpl, T> extends AbstractField imp private final UDT udt; - UDTFieldImpl(Name name, DataType type, UDT udt, String comment, Binding binding) { + UDTFieldImpl(Name name, DataType type, UDT udt, Comment comment, Binding binding) { super(name, type, comment, binding); this.udt = udt; diff --git a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java index fa969aea7f..09c5a93fac 100644 --- a/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/UDTImpl.java @@ -285,7 +285,7 @@ public class UDTImpl> extends AbstractQueryPart implement : type.asConvertedDataType(actualBinding); // [#5999] TODO: Allow for user-defined Names - final UDTFieldImpl udtField = new UDTFieldImpl(DSL.name(name), actualType, udt, comment, actualBinding); + final UDTFieldImpl udtField = new UDTFieldImpl(DSL.name(name), actualType, udt, DSL.comment(comment), actualBinding); return udtField; }