[jOOQ/jOOQ#11366] Collect nested DECLARE statements in Firebird and pull them up to the top level block

This commit is contained in:
Lukas Eder 2021-02-03 23:13:12 +01:00
parent 0073c3b5b9
commit 16cd34b158
7 changed files with 110 additions and 10 deletions

View File

@ -64,14 +64,17 @@ import static org.jooq.impl.Keywords.K_EXECUTE_IMMEDIATE;
import static org.jooq.impl.Keywords.K_EXECUTE_STATEMENT;
import static org.jooq.impl.Keywords.K_NOT;
import static org.jooq.impl.Keywords.K_PROCEDURE;
import static org.jooq.impl.ScopeMarkers.BEFORE_FIRST_TOP_LEVEL_DECLARATION;
import static org.jooq.impl.Tools.decrement;
import static org.jooq.impl.Tools.increment;
import static org.jooq.impl.Tools.toplevel;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_FORCE_STATIC_STATEMENT;
import static org.jooq.impl.Tools.DataKey.DATA_BLOCK_NESTING;
import static org.jooq.impl.Tools.DataKey.DATA_TOP_LEVEL_DECLARATIONS;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
@ -125,12 +128,13 @@ final class BlockImpl extends AbstractRowCountQuery implements Block {
case FIREBIRD: {
if (increment(ctx.data(), DATA_BLOCK_NESTING)) {
ctx.paramType(INLINED)
.visit(K_EXECUTE_BLOCK).sql(' ').visit(K_AS).sql(' ');
.visit(K_EXECUTE_BLOCK).sql(' ').visit(K_AS).formatSeparator();
ctx.data(DATA_FORCE_STATIC_STATEMENT, true);
topLevelDeclarations(ctx, () -> accept0(ctx));
}
accept0(ctx);
else
accept0(ctx);
decrement(ctx.data(), DATA_BLOCK_NESTING);
break;
@ -229,6 +233,21 @@ final class BlockImpl extends AbstractRowCountQuery implements Block {
}
}
static final void topLevelDeclarations(Context<?> ctx, Runnable runnable) {
runnable.run();
}
static final void bodyAsString(Context<?> ctx, Keyword keyword, Runnable runnable) {
ParamType previous = ctx.paramType();
@ -350,6 +369,7 @@ final class BlockImpl extends AbstractRowCountQuery implements Block {
ctx.sql(';');

View File

@ -286,6 +286,9 @@ package org.jooq.impl;

View File

@ -148,6 +148,35 @@ package org.jooq.impl;

View File

@ -50,9 +50,11 @@ import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER;
import static org.jooq.impl.Keywords.K_WITH;
import static org.jooq.impl.ScopeMarkers.AFTER_LAST_TOP_LEVEL_CTE;
import static org.jooq.impl.ScopeMarkers.BEFORE_FIRST_TOP_LEVEL_CTE;
import static org.jooq.impl.Tools.increment;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_COUNT_BIND_VALUES;
import static org.jooq.impl.Tools.DataKey.DATA_PREPEND_SQL;
import static org.jooq.impl.Tools.DataKey.DATA_TOP_LEVEL_CTE;
import static org.jooq.impl.Tools.DataKey.DATA_TOP_LEVEL_DECLARATIONS;
import java.util.ArrayDeque;
import java.util.ArrayList;
@ -60,6 +62,7 @@ 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;
@ -73,7 +76,9 @@ import org.jooq.QueryPart;
import org.jooq.QueryPartInternal;
import org.jooq.RenderContext;
import org.jooq.SQLDialect;
import org.jooq.Statement;
import org.jooq.Table;
// ...
import org.jooq.conf.RenderFormatting;
import org.jooq.conf.RenderKeywordCase;
import org.jooq.conf.RenderNameCase;
@ -82,6 +87,7 @@ import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.exception.ControlFlowSignal;
import org.jooq.exception.DataAccessException;
import org.jooq.impl.Tools.DataKey;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
@ -226,21 +232,33 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
return this;
}
@SuppressWarnings("unchecked")
@Override
void scopeEnd0() {
// TODO: Think about a more appropriate location for this logic, rather
// than the generic DefaultRenderContext, which shouldn't know anything
// about the individual query parts that it is rendering.
TopLevelCte cte = null;
ScopeStackElement beforeFirstCte = null;
ScopeStackElement afterLastCte = null;
if (subqueryLevel() == 0
&& (cte = (TopLevelCte) data(DATA_TOP_LEVEL_CTE)) != null
&& !cte.isEmpty()) {
beforeFirstCte = scopeStack.get(BEFORE_FIRST_TOP_LEVEL_CTE);
afterLastCte = scopeStack.get(AFTER_LAST_TOP_LEVEL_CTE);
if (subqueryLevel() == 0) {
if ((cte = (TopLevelCte) data(DATA_TOP_LEVEL_CTE)) != null && !cte.isEmpty()) {
beforeFirstCte = scopeStack.get(BEFORE_FIRST_TOP_LEVEL_CTE);
afterLastCte = scopeStack.get(AFTER_LAST_TOP_LEVEL_CTE);
}
}
outer:
@ -253,8 +271,25 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
else if (e1.positions == null) {
continue outer;
}
else if (e1 == beforeFirstCte) {
boolean single = cte != null && cte.size() == 1;
else if (e1 == beforeFirstCte && cte != null) {
boolean single = cte.size() == 1;
RenderContext render = configuration.dsl().renderContext();
// There is no WITH clause

View File

@ -343,6 +343,11 @@ package org.jooq.impl;

View File

@ -49,6 +49,7 @@ import org.jooq.RenderContext;
*/
enum ScopeMarkers implements QueryPartInternal {
BEFORE_FIRST_TOP_LEVEL_DECLARATION,
BEFORE_FIRST_TOP_LEVEL_CTE,
AFTER_LAST_TOP_LEVEL_CTE;

View File

@ -601,6 +601,13 @@ final class Tools {