From 2bd740eccae0338620c176136b9f1c5c7405f29f Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 2 Jun 2022 09:50:13 +0200 Subject: [PATCH] [jOOQ/jOOQ#13631] Add Context.topLevel() and Context.topLevelForLanguageContext() to give access to the top level QueryPart type that is being rendered --- jOOQ/src/main/java/org/jooq/Context.java | 33 +++++ .../java/org/jooq/impl/AbstractContext.java | 49 +++++++- .../main/java/org/jooq/impl/BlockImpl.java | 113 +++++++++--------- .../org/jooq/impl/DefaultRenderContext.java | 12 +- 4 files changed, 137 insertions(+), 70 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/Context.java b/jOOQ/src/main/java/org/jooq/Context.java index 31bdb8623d..14c73131b8 100644 --- a/jOOQ/src/main/java/org/jooq/Context.java +++ b/jOOQ/src/main/java/org/jooq/Context.java @@ -272,6 +272,32 @@ public interface Context> extends ExecuteScope { @NotNull C declareCTE(boolean declareCTE, Consumer consumer); + /** + * The top level {@link QueryPart} that is being rendered. + */ + @Nullable + QueryPart topLevel(); + + /** + * Set the top level {@link QueryPart} that is being rendered. + */ + @NotNull + C topLevel(QueryPart topLevel); + + /** + * The top level {@link QueryPart} that is being rendered in the current + * {@link #languageContext()}. + */ + @Nullable + QueryPart topLevelForLanguageContext(); + + /** + * Set the top level {@link QueryPart} that is being rendered in the current + * {@link #languageContext()}. + */ + @NotNull + C topLevelForLanguageContext(QueryPart topLevelForLanguageContext); + /** * Whether the current context is rendering a subquery (nested query). */ @@ -895,6 +921,13 @@ public interface Context> extends ExecuteScope { @NotNull C languageContext(LanguageContext languageContext, Consumer consumer); + /** + * Set the new language context for {@link #languageContext()} for the scope + * of a {@link Consumer}. + */ + @NotNull + C languageContext(LanguageContext languageContext, QueryPart topLevelForLanguageContext, Consumer consumer); + /** * Set the new language context for {@link #languageContext()}, if a * condition is true. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index d41088b2a9..3db5c0fcba 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -104,8 +104,6 @@ import org.jooq.impl.Tools.DataKey; import org.jooq.impl.Tools.DataKeyScopeStackPart; import org.jooq.tools.StringUtils; -import org.jetbrains.annotations.Nullable; - /** * @author Lukas Eder @@ -158,6 +156,8 @@ abstract class AbstractContext> extends AbstractScope imple boolean quote = true; boolean qualifySchema = true; boolean qualifyCatalog = true; + QueryPart topLevel; + QueryPart topLevelForLanguageContext; // [#11711] Enforcing scientific notation private transient DecimalFormat doubleFormat; @@ -257,6 +257,15 @@ abstract class AbstractContext> extends AbstractScope imple @Override public final C visit(QueryPart part) { if (part != null) { + if (topLevel == null) { + topLevel = topLevelForLanguageContext = part; + + if (TRUE.equals(settings().isTransformPatterns()) && configuration().requireCommercial(() -> "SQL transformations are a commercial only feature. Please consider upgrading to the jOOQ Professional Edition or jOOQ Enterprise Edition.")) { + + + + } + } // Issue start clause events // ----------------------------------------------------------------- @@ -680,6 +689,28 @@ abstract class AbstractContext> extends AbstractScope imple return scopeStack.scopeLevel(); } + @Override + public final QueryPart topLevel() { + return topLevel; + } + + @Override + public final C topLevel(QueryPart t) { + topLevel = t; + return (C) this; + } + + @Override + public final QueryPart topLevelForLanguageContext() { + return topLevelForLanguageContext; + } + + @Override + public final C topLevelForLanguageContext(QueryPart t) { + topLevelForLanguageContext = t; + return (C) this; + } + @Override public final int subqueryLevel() { return subquery; @@ -1036,6 +1067,20 @@ abstract class AbstractContext> extends AbstractScope imple return toggle(context, this::languageContext, this::languageContext, consumer); } + @Override + public final C languageContext(LanguageContext context, QueryPart newTopLevelForLanguageContext, Consumer consumer) { + return toggle(context, + this::languageContext, + this::languageContext, + c -> toggle( + newTopLevelForLanguageContext, + this::topLevelForLanguageContext, + this::topLevelForLanguageContext, + consumer + ) + ); + } + @Override public final C languageContextIf(LanguageContext context, boolean condition) { if (condition) diff --git a/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java b/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java index c859c11d85..a375b42494 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java @@ -410,73 +410,72 @@ final class BlockImpl extends AbstractRowCountQuery implements Block { } } else { - DefaultRenderContext r = ctx instanceof DefaultRenderContext d - ? d - : null; statementLoop: for (Statement s : statements) { if (s instanceof NullStatement && !SUPPORTS_NULL_STATEMENT.contains(ctx.dialect())) continue statementLoop; - LanguageContext language = ctx.languageContext(); - ctx.languageContextIf(LanguageContext.QUERY, s instanceof Query && !(s instanceof Block)); - - ctx.formatSeparator(); - int position = r != null ? r.sql.length() : 0; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ctx.visit(s); - - - - - - - // [#11374] [#11367] TODO Improve this clunky semi colon decision logic - if (position < (r != null ? r.sql.length() : 0)) - semicolonAfterStatement(ctx, s); - - ctx.languageContext(language); + if (s instanceof Query && !(s instanceof Block)) + ctx.languageContext(LanguageContext.QUERY, s, c -> accept2(c, s)); + else + accept2(ctx, s); } } } + private static final void accept2(Context ctx, Statement s) { + ctx.formatSeparator(); + int position = ctx instanceof DefaultRenderContext d ? d.sql.length() : 0; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ctx.visit(s); + + + + + + + // [#11374] [#11367] TODO Improve this clunky semi colon decision logic + if (position < (ctx instanceof DefaultRenderContext d ? d.sql.length() : 0)) + semicolonAfterStatement(ctx, s); + } + private static final void semicolonAfterStatement(Context ctx, Statement s) { if (s instanceof Block) return; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index 9c839a5a01..18328847f5 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -104,7 +104,6 @@ class DefaultRenderContext extends AbstractContext implements Ren private boolean separatorRequired; private boolean separator; private boolean newline; - private Boolean isQuery; // [#1632] Cached values from Settings RenderKeywordCase cachedRenderKeywordCase; @@ -385,7 +384,7 @@ class DefaultRenderContext extends AbstractContext implements Ren String prepend = null; String append = null; - if (TRUE.equals(isQuery)) { + if (topLevel instanceof Query) { prepend = (String) data(DATA_PREPEND_SQL); append = (String) data(DATA_APPEND_SQL); } @@ -712,15 +711,6 @@ class DefaultRenderContext extends AbstractContext implements Ren @Override protected final void visit0(QueryPartInternal internal) { - if (isQuery == null) { - isQuery = internal instanceof Query; - - if (TRUE.equals(settings().isTransformPatterns()) && configuration().requireCommercial(() -> "SQL transformations are a commercial only feature. Please consider upgrading to the jOOQ Professional Edition or jOOQ Enterprise Edition.")) { - - - - } - }