From 64e52efcdc070365b7dd7b19d0d319b67d5dd82b Mon Sep 17 00:00:00 2001 From: lukaseder Date: Thu, 14 Feb 2019 14:33:58 +0100 Subject: [PATCH] [#8325] Add a special comment syntax to the Parser and DDLDatabase that allows for ignoring certain statements --- .../resources/org/jooq/web/manual-3.12.xml | 104 +++++++++++++ .../jooq/meta/extensions/ddl/DDLDatabase.java | 11 +- .../src/main/java/org/jooq/conf/Settings.java | 138 ++++++++++++++++++ .../main/java/org/jooq/impl/ParserImpl.java | 92 ++++++++---- .../resources/xsd/jooq-runtime-3.12.0.xsd | 12 ++ 5 files changed, 326 insertions(+), 31 deletions(-) diff --git a/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.12.xml b/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.12.xml index 82e077d336..b7c4ddab33 100644 --- a/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.12.xml +++ b/jOOQ-manual/src/main/resources/org/jooq/web/manual-3.12.xml @@ -19933,6 +19933,110 @@ CREATE TABLE book_to_book_store ( } } }]]> + +

Ignoring unsuppored content

+ +

+ The supports parsing everything that is representable through the jOOQ API, as well as ignores some well known vendor specific syntax. But RDBMS have a lot more features and syntax that are not known to jOOQ. In this case, you can specify two comment tokens around the SQL syntax that jOOQ should ignore. The tokens are located in ordinary single line or multi line comments, so they do not affect your DDL scripts in any other way. For example: +

+ + + +

+ The tokens can be overridden, or the feature can be turned off entirely using the following properties: +

+ + + +

+ XML configuration (standalone and Maven) +

+ + + + + org.jooq.meta.extensions.ddl.DDLDatabase + + + + + parseIgnoreComments + true + + + + + parseIgnoreCommentStart + [jooq ignore start] + + + + + parseIgnoreCommentStop + [jooq ignore stop] + + + + +]]> + +

+ Programmatic configuration +

+ + + +

+ Gradle configuration +

+ + +

Dependencies

diff --git a/jOOQ-meta-extensions/src/main/java/org/jooq/meta/extensions/ddl/DDLDatabase.java b/jOOQ-meta-extensions/src/main/java/org/jooq/meta/extensions/ddl/DDLDatabase.java index 2c95c5b606..7798364213 100644 --- a/jOOQ-meta-extensions/src/main/java/org/jooq/meta/extensions/ddl/DDLDatabase.java +++ b/jOOQ-meta-extensions/src/main/java/org/jooq/meta/extensions/ddl/DDLDatabase.java @@ -61,6 +61,7 @@ import org.jooq.Name.Quoted; import org.jooq.Queries; import org.jooq.Query; import org.jooq.VisitContext; +import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.impl.DSL; import org.jooq.impl.DefaultVisitListener; @@ -98,11 +99,16 @@ public class DDLDatabase extends H2Database { @Override protected DSLContext create0() { if (connection == null) { + Settings defaultSettings = new Settings(); + String scripts = getProperties().getProperty("scripts"); String encoding = getProperties().getProperty("encoding", "UTF-8"); String sort = getProperties().getProperty("sort", "semantic").toLowerCase(); String unqualifiedSchema = getProperties().getProperty("unqualifiedSchema", "none").toLowerCase(); String defaultNameCase = getProperties().getProperty("defaultNameCase", "as_is").toUpperCase(); + boolean parseIgnoreComments = !"false".equalsIgnoreCase(getProperties().getProperty("parseIgnoreComments")); + String parseIgnoreCommentStart = getProperties().getProperty("parseIgnoreCommentStart", defaultSettings.getParseIgnoreCommentStart()); + String parseIgnoreCommentStop = getProperties().getProperty("parseIgnoreCommentStop", defaultSettings.getParseIgnoreCommentStop()); publicIsDefault = "none".equals(unqualifiedSchema); Comparator fileComparator = FilePattern.fileComparator(sort); @@ -117,7 +123,10 @@ public class DDLDatabase extends H2Database { info.put("user", "sa"); info.put("password", ""); connection = new org.h2.Driver().connect("jdbc:h2:mem:jooq-meta-extensions-" + UUID.randomUUID(), info); - ctx = DSL.using(connection); + ctx = DSL.using(connection, new Settings() + .withParseIgnoreComments(parseIgnoreComments) + .withParseIgnoreCommentStart(parseIgnoreCommentStart) + .withParseIgnoreCommentStop(parseIgnoreCommentStop)); // [#7771] [#8011] Ignore all parsed storage clauses when executing the statements ctx.data("org.jooq.meta.extensions.ddl.ignore-storage-clauses", true); diff --git a/jOOQ/src/main/java/org/jooq/conf/Settings.java b/jOOQ/src/main/java/org/jooq/conf/Settings.java index c106200eac..282fb352df 100644 --- a/jOOQ/src/main/java/org/jooq/conf/Settings.java +++ b/jOOQ/src/main/java/org/jooq/conf/Settings.java @@ -152,6 +152,12 @@ public class Settings @XmlElement(defaultValue = "FAIL") @XmlSchemaType(name = "string") protected ParseUnknownFunctions parseUnknownFunctions = ParseUnknownFunctions.FAIL; + @XmlElement(defaultValue = "true") + protected Boolean parseIgnoreComments = true; + @XmlElement(defaultValue = "[jooq ignore start]") + protected String parseIgnoreCommentStart = "[jooq ignore start]"; + @XmlElement(defaultValue = "[jooq ignore stop]") + protected String parseIgnoreCommentStop = "[jooq ignore stop]"; /** * Whether any catalog name should be rendered at all. @@ -1407,6 +1413,78 @@ public class Settings this.parseUnknownFunctions = value; } + /** + * [#8325] Whether the parser should ignore content between ignore comment tokens. + * + * @return + * possible object is + * {@link Boolean } + * + */ + public Boolean isParseIgnoreComments() { + return parseIgnoreComments; + } + + /** + * Sets the value of the parseIgnoreComments property. + * + * @param value + * allowed object is + * {@link Boolean } + * + */ + public void setParseIgnoreComments(Boolean value) { + this.parseIgnoreComments = value; + } + + /** + * [#8325] The ignore comment start token + * + * @return + * possible object is + * {@link String } + * + */ + public String getParseIgnoreCommentStart() { + return parseIgnoreCommentStart; + } + + /** + * Sets the value of the parseIgnoreCommentStart property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setParseIgnoreCommentStart(String value) { + this.parseIgnoreCommentStart = value; + } + + /** + * [#8325] The ignore comment stop token + * + * @return + * possible object is + * {@link String } + * + */ + public String getParseIgnoreCommentStop() { + return parseIgnoreCommentStop; + } + + /** + * Sets the value of the parseIgnoreCommentStop property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setParseIgnoreCommentStop(String value) { + this.parseIgnoreCommentStop = value; + } + public Settings withRenderCatalog(Boolean value) { setRenderCatalog(value); return this; @@ -1652,6 +1730,21 @@ public class Settings return this; } + public Settings withParseIgnoreComments(Boolean value) { + setParseIgnoreComments(value); + return this; + } + + public Settings withParseIgnoreCommentStart(String value) { + setParseIgnoreCommentStart(value); + return this; + } + + public Settings withParseIgnoreCommentStop(String value) { + setParseIgnoreCommentStop(value); + return this; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -1900,6 +1993,21 @@ public class Settings sb.append(parseUnknownFunctions); sb.append(""); } + if (parseIgnoreComments!= null) { + sb.append(""); + sb.append(parseIgnoreComments); + sb.append(""); + } + if (parseIgnoreCommentStart!= null) { + sb.append(""); + sb.append(parseIgnoreCommentStart); + sb.append(""); + } + if (parseIgnoreCommentStop!= null) { + sb.append(""); + sb.append(parseIgnoreCommentStop); + sb.append(""); + } return sb.toString(); } @@ -2356,6 +2464,33 @@ public class Settings return false; } } + if (parseIgnoreComments == null) { + if (other.parseIgnoreComments!= null) { + return false; + } + } else { + if (!parseIgnoreComments.equals(other.parseIgnoreComments)) { + return false; + } + } + if (parseIgnoreCommentStart == null) { + if (other.parseIgnoreCommentStart!= null) { + return false; + } + } else { + if (!parseIgnoreCommentStart.equals(other.parseIgnoreCommentStart)) { + return false; + } + } + if (parseIgnoreCommentStop == null) { + if (other.parseIgnoreCommentStop!= null) { + return false; + } + } else { + if (!parseIgnoreCommentStop.equals(other.parseIgnoreCommentStop)) { + return false; + } + } return true; } @@ -2412,6 +2547,9 @@ public class Settings result = ((prime*result)+((parseWithMetaLookups == null)? 0 :parseWithMetaLookups.hashCode())); result = ((prime*result)+((parseUnsupportedSyntax == null)? 0 :parseUnsupportedSyntax.hashCode())); result = ((prime*result)+((parseUnknownFunctions == null)? 0 :parseUnknownFunctions.hashCode())); + result = ((prime*result)+((parseIgnoreComments == null)? 0 :parseIgnoreComments.hashCode())); + result = ((prime*result)+((parseIgnoreCommentStart == null)? 0 :parseIgnoreCommentStart.hashCode())); + result = ((prime*result)+((parseIgnoreCommentStop == null)? 0 :parseIgnoreCommentStop.hashCode())); return result; } diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index ceeb9fb7d9..55d293893d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -455,6 +455,7 @@ import org.jooq.WindowSpecificationRowsStep; import org.jooq.conf.ParseUnknownFunctions; import org.jooq.conf.ParseUnsupportedSyntax; import org.jooq.conf.ParseWithMetaLookups; +import org.jooq.conf.Settings; import org.jooq.tools.reflect.Reflect; import org.jooq.types.DayToSecond; import org.jooq.types.Interval; @@ -9597,13 +9598,17 @@ final class ParserImpl implements Parser { } private static final boolean peek(ParserContext ctx, String string) { + return peek(ctx, string, ctx.position()); + } + + private static final boolean peek(ParserContext ctx, String string, int position) { int length = string.length(); - if (ctx.sql.length < ctx.position() + length) + if (ctx.sql.length < position + length) return false; for (int i = 0; i < length; i++) - if (ctx.sql[ctx.position() + i] != string.charAt(i)) + if (ctx.sql[position + i] != string.charAt(i)) return false; return true; @@ -9695,6 +9700,10 @@ final class ParserImpl implements Parser { // [#8074] The SQL standard and some implementations (e.g. PostgreSQL, // SQL Server) support nesting block comments int blockCommentNestLevel = 0; + boolean ignoreComment = false; + String ignoreCommentStart = ctx.settings().getParseIgnoreCommentStart(); + String ignoreCommentStop = ctx.settings().getParseIgnoreCommentStop(); + boolean checkIgnoreComment = !FALSE.equals(ctx.settings().isParseIgnoreComments()); loop: for (int i = position; i < ctx.sql.length; i++) { @@ -9712,32 +9721,40 @@ final class ParserImpl implements Parser { blockCommentNestLevel++; while (i < ctx.sql.length) { - switch (ctx.sql[i]) { - case '/': - if (i + 1 < ctx.sql.length && ctx.sql[i + 1] == '*') { - i = i + 2; - blockCommentNestLevel++; - } + if (checkIgnoreComment) + if (!ignoreComment) + ignoreComment = peek(ctx, ignoreCommentStart, i); + else + ignoreComment = !peek(ctx, ignoreCommentStop, i); - break; + if (!ignoreComment) { + switch (ctx.sql[i]) { + case '/': + if (i + 1 < ctx.sql.length && ctx.sql[i + 1] == '*') { + i = i + 2; + blockCommentNestLevel++; + } - case '+': - if (!ctx.ignoreHints() && i + 1 < ctx.sql.length && ((ctx.sql[i + 1] >= 'A' && ctx.sql[i + 1] <= 'Z') || (ctx.sql[i + 1] >= 'a' && ctx.sql[i + 1] <= 'z'))) { - blockCommentNestLevel = 0; - break loop; - } + break; - break; + case '+': + if (!ctx.ignoreHints() && i + 1 < ctx.sql.length && ((ctx.sql[i + 1] >= 'A' && ctx.sql[i + 1] <= 'Z') || (ctx.sql[i + 1] >= 'a' && ctx.sql[i + 1] <= 'z'))) { + blockCommentNestLevel = 0; + break loop; + } - case '*': - if (i + 1 < ctx.sql.length && ctx.sql[i + 1] == '/') { - position = (i = i + 1) + 1; + break; - if (--blockCommentNestLevel == 0) - continue loop; - } + case '*': + if (i + 1 < ctx.sql.length && ctx.sql[i + 1] == '/') { + position = (i = i + 1) + 1; - break; + if (--blockCommentNestLevel == 0) + continue loop; + } + + break; + } } i++; @@ -9751,15 +9768,22 @@ final class ParserImpl implements Parser { i = i + 2; while (i < ctx.sql.length) { - switch (ctx.sql[i]) { - case '\r': - case '\n': - position = i; - continue loop; + if (checkIgnoreComment) + if (!ignoreComment) + ignoreComment = peek(ctx, ignoreCommentStart, i); + else + ignoreComment = !peek(ctx, ignoreCommentStop, i); - default: - i++; + if (!ignoreComment) { + switch (ctx.sql[i]) { + case '\r': + case '\n': + position = i; + continue loop; + } } + + i++; } position = i; @@ -9962,8 +9986,16 @@ final class ParserContext { this.bindings = bindings; } + Configuration configuration() { + return dsl.configuration(); + } + + Settings settings() { + return configuration().settings(); + } + SQLDialect dialect() { - SQLDialect result = dsl.configuration().settings().getParseDialect(); + SQLDialect result = settings().getParseDialect(); if (result == null) result = SQLDialect.DEFAULT; diff --git a/jOOQ/src/main/resources/xsd/jooq-runtime-3.12.0.xsd b/jOOQ/src/main/resources/xsd/jooq-runtime-3.12.0.xsd index f5ee96d681..1c29ead3d5 100644 --- a/jOOQ/src/main/resources/xsd/jooq-runtime-3.12.0.xsd +++ b/jOOQ/src/main/resources/xsd/jooq-runtime-3.12.0.xsd @@ -285,6 +285,18 @@ jOOQ queries, for which no specific fetchSize value was specified.]]> + + + + + + + + + + + +