From 2d128da1ebe7b477d6e1d9ccd12c7b4c78468ec6 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 23 Jun 2021 18:05:31 +0200 Subject: [PATCH] [jOOQ/jOOQ#12050] PL/SQL can't have length/precision/scale in casts This includes: - [jOOQ/jOOQ#11250] Add a language context to the ParserContext --- jOOQ/src/main/java/org/jooq/Context.java | 28 +++++++++++++++++- .../java/org/jooq/impl/AbstractContext.java | 29 +++++++++++++++++++ .../java/org/jooq/impl/AbstractDataType.java | 2 ++ .../main/java/org/jooq/impl/BlockImpl.java | 15 ++++++++++ jOOQ/src/main/java/org/jooq/impl/Cast.java | 14 +++++++++ .../org/jooq/impl/CreateFunctionImpl.java | 3 ++ .../org/jooq/impl/CreateProcedureImpl.java | 2 ++ .../java/org/jooq/impl/CreateTriggerImpl.java | 3 ++ .../org/jooq/impl/DefaultRenderContext.java | 1 + 9 files changed, 96 insertions(+), 1 deletion(-) diff --git a/jOOQ/src/main/java/org/jooq/Context.java b/jOOQ/src/main/java/org/jooq/Context.java index fa68ae3de9..7cb3098f6e 100644 --- a/jOOQ/src/main/java/org/jooq/Context.java +++ b/jOOQ/src/main/java/org/jooq/Context.java @@ -783,6 +783,32 @@ public interface Context> extends Scope { @NotNull C paramTypeIf(ParamType paramType, boolean condition, Consumer runnable); + /** + * The current language context. + */ + @NotNull + LanguageContext languageContext(); + + /** + * Set the new language context for {@link #languageContext()} + */ + @NotNull + C languageContext(LanguageContext languageContext); + + /** + * Set the new language context for {@link #languageContext()} for the scope + * of a {@link Consumer}. + */ + @NotNull + C languageContext(LanguageContext languageContext, Consumer consumer); + + /** + * Set the new language context for {@link #languageContext()}, if a + * condition is true. + */ + @NotNull + C languageContextIf(LanguageContext languageContext, boolean condition); + /** * The currently applied cast mode for bind values. */ @@ -800,7 +826,7 @@ public interface Context> extends Scope { * {@link Consumer}. */ @NotNull - C castMode(CastMode mode, Consumer runnable); + C castMode(CastMode mode, Consumer consumer); /** * Set the new cast mode for {@link #castMode()}, 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 e46c93e6d9..dc20bbe1a6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -74,6 +74,7 @@ import org.jooq.Context; import org.jooq.DSLContext; import org.jooq.ForeignKey; import org.jooq.JoinType; +import org.jooq.LanguageContext; // ... import org.jooq.QueryPart; import org.jooq.QueryPartInternal; @@ -92,6 +93,8 @@ import org.jooq.conf.SettingsTools; import org.jooq.conf.StatementType; import org.jooq.tools.StringUtils; +import org.jetbrains.annotations.NotNull; + /** * @author Lukas Eder */ @@ -134,6 +137,7 @@ abstract class AbstractContext> extends AbstractScope imple final ParamType forcedParamType; final boolean castModeOverride; CastMode castMode; + LanguageContext languageContext; ParamType paramType = ParamType.INDEXED; boolean quote = true; boolean qualifySchema = true; @@ -205,6 +209,7 @@ abstract class AbstractContext> extends AbstractScope imple : m == ParamCastMode.NEVER ? CastMode.NEVER : CastMode.DEFAULT; + this.languageContext = LanguageContext.QUERY; this.scopeStack = new ScopeStack(ScopeStackElement::new); } @@ -906,6 +911,30 @@ abstract class AbstractContext> extends AbstractScope imple return toggle(q, this::qualifyCatalog, this::qualifyCatalog, consumer); } + @Override + public final LanguageContext languageContext() { + return languageContext; + } + + @Override + public final C languageContext(LanguageContext context) { + this.languageContext = context; + return (C) this; + } + + @Override + public final C languageContext(LanguageContext context, Consumer consumer) { + return toggle(context, this::languageContext, this::languageContext, consumer); + } + + @Override + public final C languageContextIf(LanguageContext context, boolean condition) { + if (condition) + languageContext(context); + + return (C) this; + } + @Override public final CastMode castMode() { return castMode; diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java index 4bb6fc8e40..520c84e211 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java @@ -42,6 +42,7 @@ import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.FIREBIRD; // ... // ... +// ... import static org.jooq.impl.DSL.unquotedName; import static org.jooq.impl.Internal.arrayType; import static org.jooq.impl.SQLDataType.BLOB; @@ -82,6 +83,7 @@ import org.jooq.EnumType; import org.jooq.Field; import org.jooq.JSON; import org.jooq.JSONB; +import org.jooq.LanguageContext; import org.jooq.Name; import org.jooq.Nullability; // ... diff --git a/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java b/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java index e51ad3250d..1ab6033af9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/BlockImpl.java @@ -88,6 +88,7 @@ import org.jooq.Configuration; import org.jooq.Context; import org.jooq.DDLQuery; import org.jooq.Keyword; +import org.jooq.LanguageContext; import org.jooq.Name; // ... import org.jooq.Query; @@ -98,6 +99,8 @@ import org.jooq.conf.ParamType; import org.jooq.impl.ScopeMarker.ScopeContent; import org.jooq.impl.Tools.DataExtendedKey; +import org.jetbrains.annotations.NotNull; + /** * @author Lukas Eder */ @@ -341,6 +344,10 @@ final class BlockImpl extends AbstractRowCountQuery implements Block { if (wrapInBeginEnd) { boolean topLevel = ctx.scopeLevel() == -1; + LanguageContext language = ctx.languageContext(); + + if (topLevel && language == LanguageContext.QUERY) + ctx.languageContext(LanguageContext.BLOCK); @@ -375,6 +382,9 @@ final class BlockImpl extends AbstractRowCountQuery implements Block { scopeDeclarations(ctx, c -> accept1(c)); end(ctx, topLevel); } + + if (topLevel && language == LanguageContext.QUERY) + ctx.languageContext(language); } else accept1(ctx); @@ -408,6 +418,9 @@ final class BlockImpl extends AbstractRowCountQuery implements Block { 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; @@ -457,6 +470,8 @@ final class BlockImpl extends AbstractRowCountQuery implements Block { // [#11374] [#11367] TODO Improve this clunky semi colon decision logic if (position < (r != null ? r.sql.length() : 0)) semicolonAfterStatement(ctx, s); + + ctx.languageContext(language); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/Cast.java b/jOOQ/src/main/java/org/jooq/impl/Cast.java index c19b41e4d7..c9bca2acc4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Cast.java +++ b/jOOQ/src/main/java/org/jooq/impl/Cast.java @@ -37,6 +37,7 @@ */ package org.jooq.impl; +// ... import static org.jooq.impl.DSL.inline; import static org.jooq.impl.Keywords.K_AS; import static org.jooq.impl.Keywords.K_CAST; @@ -56,11 +57,13 @@ import static org.jooq.impl.SQLDataType.VARCHAR; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.util.regex.Pattern; import org.jooq.Context; import org.jooq.DataType; import org.jooq.Field; import org.jooq.Keyword; +import org.jooq.LanguageContext; // ... import org.jooq.QueryPart; import org.jooq.RenderContext.CastMode; @@ -291,6 +294,12 @@ final class Cast extends AbstractField { + + + + + + @@ -330,6 +339,11 @@ final class Cast extends AbstractField { if (typeAsKeyword != null) ctx.visit(typeAsKeyword); + + + + + else ctx.sql(type.getCastTypeName(ctx.configuration())); diff --git a/jOOQ/src/main/java/org/jooq/impl/CreateFunctionImpl.java b/jOOQ/src/main/java/org/jooq/impl/CreateFunctionImpl.java index 1c5b3c9eb1..b808a68bd8 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CreateFunctionImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CreateFunctionImpl.java @@ -318,6 +318,9 @@ package org.jooq.impl; + + + diff --git a/jOOQ/src/main/java/org/jooq/impl/CreateProcedureImpl.java b/jOOQ/src/main/java/org/jooq/impl/CreateProcedureImpl.java index 887a52dc06..8541f5eb78 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CreateProcedureImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CreateProcedureImpl.java @@ -302,6 +302,8 @@ package org.jooq.impl; + + diff --git a/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java b/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java index f84207910d..71cc2d7f86 100644 --- a/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/CreateTriggerImpl.java @@ -523,6 +523,9 @@ package org.jooq.impl; + + + diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index 1fadc1696a..e5ad943e95 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -63,6 +63,7 @@ import org.jooq.Configuration; import org.jooq.Constants; import org.jooq.Field; import org.jooq.ForeignKey; +import org.jooq.LanguageContext; import org.jooq.Param; import org.jooq.Query; import org.jooq.QueryPart;