[jOOQ/jOOQ#3175] Apply SQL transformation to move nested CTE to top level where not supported
This commit is contained in:
parent
fbc41df271
commit
8733df26f4
@ -998,6 +998,7 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
static class ScopeStackElement {
|
||||
final int scopeLevel;
|
||||
int[] positions;
|
||||
int bindIndex;
|
||||
int indent;
|
||||
JoinNode joinNode;
|
||||
|
||||
|
||||
@ -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<RenderContext> implements Ren
|
||||
|
||||
final StringBuilder sql;
|
||||
private final QueryPartList<Param<?>> bindValues;
|
||||
private int params;
|
||||
private int alias;
|
||||
private int indent;
|
||||
private Deque<Integer> indentLock;
|
||||
@ -177,6 +173,7 @@ class DefaultRenderContext extends AbstractContext<RenderContext> 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<RenderContext> implements Ren
|
||||
|
||||
outer:
|
||||
for (ScopeStackElement e1 : scopeStack.iterable(e -> e.scopeLevel == scopeStack.scopeLevel())) {
|
||||
String replaced = null;
|
||||
String replacedSQL = null;
|
||||
QueryPartList<Param<?>> insertedBindValues = null;
|
||||
|
||||
if (e1.positions == null) {
|
||||
continue outer;
|
||||
@ -262,7 +260,7 @@ class DefaultRenderContext extends AbstractContext<RenderContext> 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<RenderContext> 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<RenderContext> 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<RenderContext> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,13 +101,12 @@ final class GenerateSeries extends AbstractTable<Record1<Integer>> implements Au
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
if (EMULATE_WITH_RECURSIVE.contains(ctx.dialect())) {
|
||||
Name v = unquotedName("v");
|
||||
Field<Integer> f = DSL.field(v, INTEGER);
|
||||
Field<Integer> 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
|
||||
);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user