diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java index b9497c5ff9..f3501b3146 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractContext.java @@ -998,6 +998,7 @@ abstract class AbstractContext> extends AbstractScope imple static class ScopeStackElement { final int scopeLevel; int[] positions; + int bindIndex; int indent; JoinNode joinNode; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index bfc8d781db..590a57c807 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -39,9 +39,7 @@ package org.jooq.impl; import static java.lang.Boolean.TRUE; import static org.jooq.SQLDialect.SQLITE; -import static org.jooq.conf.ParamType.INDEXED; import static org.jooq.conf.ParamType.INLINED; -import static org.jooq.conf.ParamType.NAMED; import static org.jooq.conf.SettingsTools.renderLocale; import static org.jooq.impl.Identifiers.QUOTES; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER; @@ -56,7 +54,6 @@ import java.util.Arrays; import java.util.Deque; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -96,7 +93,6 @@ class DefaultRenderContext extends AbstractContext implements Ren final StringBuilder sql; private final QueryPartList> bindValues; - private int params; private int alias; private int indent; private Deque indentLock; @@ -177,6 +173,7 @@ class DefaultRenderContext extends AbstractContext implements Ren applyNewLine(); ScopeStackElement e = scopeStack.getOrCreate(part); e.positions = new int[] { sql.length(), -1 }; + e.bindIndex = peekIndex(); e.indent = indent; resetSeparatorFlags(); } @@ -253,7 +250,8 @@ class DefaultRenderContext extends AbstractContext implements Ren outer: for (ScopeStackElement e1 : scopeStack.iterable(e -> e.scopeLevel == scopeStack.scopeLevel())) { - String replaced = null; + String replacedSQL = null; + QueryPartList> insertedBindValues = null; if (e1.positions == null) { continue outer; @@ -262,7 +260,7 @@ class DefaultRenderContext extends AbstractContext implements Ren // [#11367] TODO: Move this logic into a ScopeMarker as well // TODO: subqueryLevel() is lower than scopeLevel if we use implicit join in procedural logic else if (e1.joinNode != null && !e1.joinNode.children.isEmpty()) { - replaced = configuration + replacedSQL = configuration .dsl() .renderContext() .declareTables(true) @@ -283,21 +281,24 @@ class DefaultRenderContext extends AbstractContext implements Ren ScopeContent c = content[i]; if (e1 == e && c != null) { - replaced = markers[i].renderer.render( - configuration.dsl().renderContext().formatIndentStart(e.indent), + DefaultRenderContext ctx = (DefaultRenderContext) configuration.dsl().renderContext(); + markers[i].renderer.render( + (DefaultRenderContext) ctx.formatIndentStart(e.indent), e, afterLast[i], c ); + replacedSQL = ctx.render(); + insertedBindValues = ctx.bindValues(); break elementLoop; } } } - if (replaced != null) { - sql.replace(e1.positions[0], e1.positions[1], replaced); - int shift = replaced.length() - (e1.positions[1] - e1.positions[0]); + if (replacedSQL != null) { + sql.replace(e1.positions[0], e1.positions[1], replacedSQL); + int shift = replacedSQL.length() - (e1.positions[1] - e1.positions[0]); inner: for (ScopeStackElement e2 : scopeStack) { @@ -309,6 +310,19 @@ class DefaultRenderContext extends AbstractContext implements Ren e2.positions[1] = e2.positions[1] + shift; } } + + if (insertedBindValues != null) { + bindValues.addAll(e1.bindIndex - 1, insertedBindValues); + + inner: + for (ScopeStackElement e2 : scopeStack) { + if (e2.positions == null) + continue inner; + + if (e2.bindIndex > e1.bindIndex) + e2.bindIndex = e2.bindIndex + insertedBindValues.size(); + } + } } } } @@ -938,7 +952,7 @@ class DefaultRenderContext extends AbstractContext implements Ren public ForceInlineSignal() { if (log.isDebugEnabled()) - log.debug("Re-render query", "Forcing bind variable inlining as " + configuration().dialect() + " does not support " + params + " bind variables (or more) in a single query"); + log.debug("Re-render query", "Forcing bind variable inlining as " + configuration().dialect() + " does not support " + peekIndex() + " bind variables (or more) in a single query"); } } } diff --git a/jOOQ/src/main/java/org/jooq/impl/GenerateSeries.java b/jOOQ/src/main/java/org/jooq/impl/GenerateSeries.java index c33ec51c08..e97c93ae65 100644 --- a/jOOQ/src/main/java/org/jooq/impl/GenerateSeries.java +++ b/jOOQ/src/main/java/org/jooq/impl/GenerateSeries.java @@ -101,13 +101,12 @@ final class GenerateSeries extends AbstractTable> implements Au @Override public final void accept(Context ctx) { if (EMULATE_WITH_RECURSIVE.contains(ctx.dialect())) { - Name v = unquotedName("v"); - Field f = DSL.field(v, INTEGER); + Field f = DSL.field(N_GENERATE_SERIES, INTEGER); visitSubquery( ctx, - withRecursive(N_GENERATE_SERIES, v) + withRecursive(N_GENERATE_SERIES, N_GENERATE_SERIES) .as(select(from).unionAll(select(iadd(f, step == null ? inline(1) : step)).from(N_GENERATE_SERIES).where(f.lt(to)))) - .select(f.as(N_GENERATE_SERIES)).from(N_GENERATE_SERIES), + .select(f).from(N_GENERATE_SERIES), true ); } diff --git a/jOOQ/src/main/java/org/jooq/impl/ScopeMarker.java b/jOOQ/src/main/java/org/jooq/impl/ScopeMarker.java index 2a82087f8e..8a6716846b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ScopeMarker.java +++ b/jOOQ/src/main/java/org/jooq/impl/ScopeMarker.java @@ -107,10 +107,6 @@ enum ScopeMarker { - - - - @@ -122,23 +118,30 @@ enum ScopeMarker { (ctx, beforeFirst, afterLast, object) -> { TopLevelCte cte = (TopLevelCte) object; boolean single = cte.size() == 1; + boolean noWith = afterLast != null && beforeFirst.positions[0] == afterLast.positions[0]; - // There is no WITH clause - if (afterLast != null && beforeFirst.positions[0] == afterLast.positions[0]) + if (noWith) { ctx.visit(K_WITH); - if (single) - ctx.formatIndentStart() - .formatSeparator(); - else - ctx.sql(' '); + if (single) + ctx.formatIndentStart() + .formatSeparator(); + else + ctx.sql(' '); + } ctx.declareCTE(true).visit(cte).declareCTE(false); - if (single) - ctx.formatIndentEnd(); + if (noWith) { + if (single) + ctx.formatIndentEnd(); + } - return ctx.render(); + // Top level CTE are inserted before all other CTEs + else + ctx.sql(','); + + ctx.formatSeparator().sql(""); } ); @@ -158,8 +161,8 @@ enum ScopeMarker { @FunctionalInterface interface ReplacementRenderer { - String render( - Context ctx, + void render( + DefaultRenderContext ctx, ScopeStackElement beforeFirst, ScopeStackElement afterLast, ScopeContent content diff --git a/jOOQ/src/main/java/org/jooq/impl/WithImpl.java b/jOOQ/src/main/java/org/jooq/impl/WithImpl.java index 6ce92a0035..6780138970 100644 --- a/jOOQ/src/main/java/org/jooq/impl/WithImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/WithImpl.java @@ -51,6 +51,7 @@ import static org.jooq.impl.Keywords.K_RECURSIVE; import static org.jooq.impl.Keywords.K_WITH; import static org.jooq.impl.Tools.EMPTY_NAME; import static org.jooq.impl.Tools.BooleanDataKey.DATA_LIST_ALREADY_INDENTED; +import static org.jooq.impl.Tools.DataKey.DATA_TOP_LEVEL_CTE; import java.util.Arrays; import java.util.Collection; @@ -176,6 +177,8 @@ implements + + private final CommonTableExpressionList ctes; private final boolean recursive; private Configuration configuration; @@ -214,6 +217,13 @@ implements list = ctes; if (!list.isEmpty()) { + + + + + + + ctx.visit(K_WITH); if (recursive