[jOOQ/jOOQ#13560] Use MULTISET emulation for nested rows with LIMIT
In older Oracle versions, we emulate LIMIT with ROWNUM calculations in derived tables. In those cases, it seems very hard to keep the flattening nested record emulation working. Better work with the MULTISET emulation of nested records, instead. This includes parts of the implementation of [jOOQ/jOOQ#13599]
This commit is contained in:
parent
a3a3b94592
commit
6b03c3f0de
@ -42,6 +42,7 @@ import java.sql.SQLException;
|
||||
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.QueryPart;
|
||||
import org.jooq.QueryPartInternal;
|
||||
@ -54,8 +55,8 @@ import org.jooq.exception.DataAccessException;
|
||||
*/
|
||||
abstract class AbstractBindContext extends AbstractContext<BindContext> implements BindContext {
|
||||
|
||||
AbstractBindContext(Configuration configuration, PreparedStatement stmt) {
|
||||
super(configuration, stmt);
|
||||
AbstractBindContext(Configuration configuration, ExecuteContext ctx, PreparedStatement stmt) {
|
||||
super(configuration, ctx, stmt);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
@ -54,6 +54,7 @@ import static org.jooq.impl.Tools.EMPTY_QUERYPART;
|
||||
import static org.jooq.impl.Tools.lazy;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_NESTED_SET_OPERATIONS;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_OMIT_CLAUSE_EVENT_EMISSION;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_EXECUTE_CONTEXT;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.text.DecimalFormat;
|
||||
@ -79,6 +80,7 @@ import org.jooq.Condition;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.JoinType;
|
||||
@ -159,8 +161,12 @@ abstract class AbstractContext<C extends Context<C>> extends AbstractScope imple
|
||||
private transient DecimalFormat doubleFormat;
|
||||
private transient DecimalFormat floatFormat;
|
||||
|
||||
AbstractContext(Configuration configuration, PreparedStatement stmt) {
|
||||
AbstractContext(Configuration configuration, ExecuteContext ctx, PreparedStatement stmt) {
|
||||
super(configuration);
|
||||
|
||||
if (ctx != null)
|
||||
data(DATA_EXECUTE_CONTEXT, ctx);
|
||||
|
||||
this.stmt = stmt;
|
||||
|
||||
VisitListenerProvider[] providers = configuration.visitListenerProviders();
|
||||
|
||||
@ -465,23 +465,23 @@ abstract class AbstractQuery<R extends Record> extends AbstractAttachableQueryPa
|
||||
// [#6474] [#6929] Can this be communicated in a leaner way?
|
||||
if (ctx.type() == DDL) {
|
||||
ctx.data(DATA_FORCE_STATIC_STATEMENT, true);
|
||||
render = new DefaultRenderContext(c);
|
||||
render = new DefaultRenderContext(c, ctx);
|
||||
result = new Rendered(render.paramType(INLINED).visit(this).render(), null, render.skipUpdateCounts());
|
||||
}
|
||||
else if (executePreparedStatements(configuration().settings())) {
|
||||
try {
|
||||
render = new DefaultRenderContext(c);
|
||||
render = new DefaultRenderContext(c, ctx);
|
||||
render.data(DATA_COUNT_BIND_VALUES, true);
|
||||
result = new Rendered(render.visit(this).render(), render.bindValues(), render.skipUpdateCounts());
|
||||
}
|
||||
catch (DefaultRenderContext.ForceInlineSignal e) {
|
||||
ctx.data(DATA_FORCE_STATIC_STATEMENT, true);
|
||||
render = new DefaultRenderContext(c);
|
||||
render = new DefaultRenderContext(c, ctx);
|
||||
result = new Rendered(render.paramType(INLINED).visit(this).render(), null, render.skipUpdateCounts());
|
||||
}
|
||||
}
|
||||
else {
|
||||
render = new DefaultRenderContext(c);
|
||||
render = new DefaultRenderContext(c, ctx);
|
||||
result = new Rendered(render.paramType(INLINED).visit(this).render(), null, render.skipUpdateCounts());
|
||||
}
|
||||
|
||||
|
||||
@ -218,7 +218,7 @@ final class BatchSingle extends AbstractBatch implements BatchBindStep {
|
||||
// list to preserve type information
|
||||
// [#3547] The original query may have no Params specified - e.g. when it was constructed with
|
||||
// plain SQL. In that case, infer the bind value type directly from the bind value
|
||||
visitAll(new DefaultBindContext(configuration, ctx.statement()),
|
||||
visitAll(new DefaultBindContext(configuration, ctx, ctx.statement()),
|
||||
(params.length > 0)
|
||||
? fields(bindValues, params)
|
||||
: fields(bindValues));
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Collections.emptyList;
|
||||
// ...
|
||||
import static org.jooq.impl.RowAsField.NO_NATIVE_SUPPORT;
|
||||
@ -44,6 +45,7 @@ import static org.jooq.impl.Tools.embeddedFields;
|
||||
import static org.jooq.impl.Tools.embeddedRecordType;
|
||||
import static org.jooq.impl.Tools.recordFactory;
|
||||
import static org.jooq.impl.Tools.uncoerce;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
@ -78,7 +80,6 @@ import org.jooq.BindingGetResultSetContext;
|
||||
import org.jooq.Converter;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.ExecuteListener;
|
||||
import org.jooq.ExecuteType;
|
||||
import org.jooq.Field;
|
||||
// ...
|
||||
import org.jooq.Record;
|
||||
@ -1548,7 +1549,11 @@ final class CursorImpl<R extends Record> extends AbstractCursor<R> {
|
||||
// RowField may have a Row[N].mapping(...) applied
|
||||
Field<?> f = uncoerce(field);
|
||||
|
||||
if (f instanceof AbstractRowAsField && NO_NATIVE_SUPPORT.contains(ctx.dialect())) {
|
||||
// [#13560] Queries may decide themselves to replace the
|
||||
// flattening emulation by the MULTISET emulation
|
||||
if (f instanceof AbstractRowAsField
|
||||
&& NO_NATIVE_SUPPORT.contains(ctx.dialect())
|
||||
&& !TRUE.equals(ctx.data(DATA_MULTISET_CONTENT))) {
|
||||
nested = ((AbstractRowAsField<?>) f).emulatedFields(configuration);
|
||||
recordType = (Class<? extends AbstractRecord>) ((AbstractRowAsField<?>) f).getRecordType();
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ import java.sql.SQLException;
|
||||
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.Field;
|
||||
|
||||
/**
|
||||
@ -49,8 +50,8 @@ import org.jooq.Field;
|
||||
*/
|
||||
final class DefaultBindContext extends AbstractBindContext {
|
||||
|
||||
DefaultBindContext(Configuration configuration, PreparedStatement stmt) {
|
||||
super(configuration, stmt);
|
||||
DefaultBindContext(Configuration configuration, ExecuteContext ctx, PreparedStatement stmt) {
|
||||
super(configuration, ctx, stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -716,7 +716,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri
|
||||
|
||||
@Override
|
||||
public RenderContext renderContext() {
|
||||
return new DefaultRenderContext(configuration());
|
||||
return new DefaultRenderContext(configuration(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -760,7 +760,7 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri
|
||||
|
||||
@Override
|
||||
public BindContext bindContext(PreparedStatement stmt) {
|
||||
return new DefaultBindContext(configuration(), stmt);
|
||||
return new DefaultBindContext(configuration(), null, stmt);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -46,10 +46,9 @@ import static org.jooq.impl.Identifiers.QUOTES;
|
||||
import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER;
|
||||
import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED;
|
||||
import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER;
|
||||
import static org.jooq.impl.Tools.DATAKEY_RESET_IN_SUBQUERY_SCOPE;
|
||||
import static org.jooq.impl.Tools.lazy;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_COUNT_BIND_VALUES;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_APPEND_SQL;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_EXECUTE_CONTEXT;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_PREPEND_SQL;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
@ -64,6 +63,7 @@ import java.util.regex.Pattern;
|
||||
import org.jooq.BindContext;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Constants;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.Param;
|
||||
@ -82,9 +82,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.AbstractContext.ScopeStackElement;
|
||||
import org.jooq.impl.ScopeMarker.ScopeContent;
|
||||
import org.jooq.impl.Tools.DataKey;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StringUtils;
|
||||
|
||||
@ -121,8 +119,8 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
|
||||
String cachedNewline;
|
||||
int cachedPrintMargin;
|
||||
|
||||
DefaultRenderContext(Configuration configuration) {
|
||||
super(configuration, null);
|
||||
DefaultRenderContext(Configuration configuration, ExecuteContext ctx) {
|
||||
super(configuration, ctx, null);
|
||||
|
||||
Settings settings = configuration.settings();
|
||||
|
||||
@ -148,7 +146,7 @@ class DefaultRenderContext extends AbstractContext<RenderContext> implements Ren
|
||||
}
|
||||
|
||||
DefaultRenderContext(RenderContext context, boolean copyLocalState) {
|
||||
this(context.configuration());
|
||||
this(context.configuration(), (ExecuteContext) context.data(DATA_EXECUTE_CONTEXT));
|
||||
|
||||
paramType(context.paramType());
|
||||
qualifyCatalog(context.qualifyCatalog());
|
||||
|
||||
@ -68,7 +68,7 @@ final class ParamCollector extends AbstractBindContext {
|
||||
private final boolean includeInlinedParams;
|
||||
|
||||
ParamCollector(Configuration configuration, boolean includeInlinedParams) {
|
||||
super(configuration, null);
|
||||
super(configuration, null, null);
|
||||
|
||||
this.includeInlinedParams = includeInlinedParams;
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ final class ParsingConnection extends DefaultConnection {
|
||||
if (i > 0)
|
||||
rendered = translate(configuration, sql, p.get(i).toArray(EMPTY_PARAM));
|
||||
|
||||
new DefaultBindContext(configuration, s).visit(rendered.bindValues);
|
||||
new DefaultBindContext(configuration, null, s).visit(rendered.bindValues);
|
||||
|
||||
// TODO: Find a less hacky way to signal that we're batching. Currently:
|
||||
// - ArrayList<Arraylist<Param<?>>> = batching
|
||||
|
||||
@ -400,7 +400,7 @@ final class R2DBC {
|
||||
try {
|
||||
Rendered rendered = rendered(configuration, query);
|
||||
Statement stmt = c.createStatement(sql = rendered.sql);
|
||||
new DefaultBindContext(configuration, new R2DBCPreparedStatement(configuration, stmt)).visit(rendered.bindValues);
|
||||
new DefaultBindContext(configuration, null, new R2DBCPreparedStatement(configuration, stmt)).visit(rendered.bindValues);
|
||||
|
||||
// TODO: Reuse org.jooq.impl.Tools.setFetchSize(ExecuteContext ctx, int fetchSize)
|
||||
AbstractResultQuery<?> q1 = abstractResultQuery(query);
|
||||
@ -501,7 +501,7 @@ final class R2DBC {
|
||||
// list to preserve type information
|
||||
// [#3547] The original query may have no Params specified - e.g. when it was constructed with
|
||||
// plain SQL. In that case, infer the bind value type directly from the bind value
|
||||
visitAll(new DefaultBindContext(batch.configuration, new R2DBCPreparedStatement(batch.query.configuration(), stmt)),
|
||||
visitAll(new DefaultBindContext(batch.configuration, null, new R2DBCPreparedStatement(batch.query.configuration(), stmt)),
|
||||
(params.length > 0)
|
||||
? fields(bindValues, params)
|
||||
: fields(bindValues));
|
||||
@ -728,7 +728,7 @@ final class R2DBC {
|
||||
static final Rendered rendered(Configuration configuration, Query query) {
|
||||
DefaultRenderContext render = new DefaultRenderContext(configuration.deriveSettings(s ->
|
||||
setParamType(configuration.dialect(), s)
|
||||
));
|
||||
), null);
|
||||
|
||||
return new Rendered(render.paramType(NAMED).visit(query).render(), render.bindValues(), render.skipUpdateCounts());
|
||||
}
|
||||
|
||||
@ -208,6 +208,7 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_COLLECT_SEMI_ANTI_JOIN;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_FORCE_LIMIT_WITH_ORDER_BY;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_INSERT_SELECT;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_INSERT_SELECT_WITHOUT_INSERT_COLUMN_LIST;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_NESTED_SET_OPERATIONS;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_OMIT_INTO_CLAUSE;
|
||||
import static org.jooq.impl.Tools.BooleanDataKey.DATA_RENDER_TRAILING_LIMIT_IF_APPLICABLE;
|
||||
@ -216,6 +217,7 @@ import static org.jooq.impl.Tools.BooleanDataKey.DATA_WRAP_DERIVED_TABLES_IN_PAR
|
||||
import static org.jooq.impl.Tools.ExtendedDataKey.DATA_TRANSFORM_ROWNUM_TO_LIMIT;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_COLLECTED_SEMI_ANTI_JOIN;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_DML_TARGET_TABLE;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_EXECUTE_CONTEXT;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_OVERRIDE_ALIASES_IN_ORDER_BY;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_RENDERING_DATA_CHANGE_DELTA_TABLE;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_SELECT_ALIASES;
|
||||
@ -248,6 +250,7 @@ import org.jooq.Condition;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Context;
|
||||
import org.jooq.DataType;
|
||||
import org.jooq.ExecuteContext;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.ForeignKey;
|
||||
import org.jooq.GeneratorStatementType;
|
||||
@ -2052,6 +2055,9 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -188,6 +188,7 @@ import static org.jooq.impl.SQLDataType.VARCHAR;
|
||||
import static org.jooq.impl.SQLDataType.XML;
|
||||
import static org.jooq.impl.Tools.anyMatch;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_BLOCK_NESTING;
|
||||
import static org.jooq.impl.Tools.SimpleDataKey.DATA_EXECUTE_CONTEXT;
|
||||
import static org.jooq.tools.StringUtils.defaultIfNull;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@ -673,6 +674,12 @@ final class Tools {
|
||||
*/
|
||||
enum SimpleDataKey implements DataKey {
|
||||
|
||||
/**
|
||||
* [#13560] [#13599] The {@link ExecuteContext} in whose scope another
|
||||
* {@link Scope} may have been created.
|
||||
*/
|
||||
DATA_EXECUTE_CONTEXT,
|
||||
|
||||
/**
|
||||
* The level of anonymous block nesting, in case we're generating a block.
|
||||
*/
|
||||
@ -2776,8 +2783,7 @@ final class Tools {
|
||||
char[] sqlChars = sql.toCharArray();
|
||||
|
||||
// [#1593] Create a dummy renderer if we're in bind mode
|
||||
if (render == null) render = new DefaultRenderContext(bind.configuration());
|
||||
|
||||
if (render == null) render = new DefaultRenderContext(bind.configuration(), (ExecuteContext) ctx.data(DATA_EXECUTE_CONTEXT));
|
||||
SQLDialect family = render.family();
|
||||
boolean mysql = SUPPORTS_HASH_COMMENT_SYNTAX.contains(render.dialect());
|
||||
char[][][] quotes = QUOTES.get(family);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user