[jOOQ/jOOQ#13033] DSL.function not replacing schema for function call in generated SQL

This includes:
- [jOOQ/jOOQ#13034] SchemaMapping should apply Settings.defaultCatalog even if Settings.defaultSchema doesn't apply
- [jOOQ/jOOQ#13035] SchemaMapping should cache result of Settings.defaultCatalog or Settings.defaultSchema application
This commit is contained in:
Lukas Eder 2022-02-10 10:40:16 +01:00
parent 9c1a9a2bb3
commit ed9f11659e
5 changed files with 77 additions and 92 deletions

View File

@ -40,6 +40,7 @@ package org.jooq;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.schema;
import static org.jooq.tools.StringUtils.isBlank;
import static org.jooq.tools.StringUtils.isEmpty;
import java.io.Serializable;
import java.util.HashMap;
@ -281,7 +282,8 @@ public class SchemaMapping implements Serializable {
String catalogName = result.getName();
// [#4642] Don't initialise catalog mapping if not necessary
if (!mapping().getCatalogs().isEmpty()) {
RenderMapping m = mapping();
if (!m.getCatalogs().isEmpty() || !isEmpty(m.getDefaultCatalog())) {
// Lazy initialise catalog mapping
if (!getCatalogs().containsKey(catalogName)) {
@ -290,7 +292,7 @@ public class SchemaMapping implements Serializable {
// want to use a Configuration and dependent objects in a "thread-safe" manner
synchronized (this) {
if (!getCatalogs().containsKey(catalogName)) {
for (MappedCatalog c : mapping().getCatalogs()) {
for (MappedCatalog c : m.getCatalogs()) {
// A configured mapping was found, add a renamed catalog
if (matches(c, catalogName)) {
@ -306,6 +308,11 @@ public class SchemaMapping implements Serializable {
}
}
// [#13035] Cache the application of the defaultCatalog
if ("".equals(result.getName())
|| result.getName().equals(m.getDefaultCatalog()))
result = null;
// Add mapped catalog or self if no mapping was found
getCatalogs().put(catalogName, result);
}
@ -315,11 +322,6 @@ public class SchemaMapping implements Serializable {
result = getCatalogs().get(catalogName);
}
// The configured default catalog is mapped to "null". This prevents
// it from being rendered to SQL
if ("".equals(result.getName()) || result.getName().equals(mapping().getDefaultCatalog()))
result = null;
return result;
}
@ -339,21 +341,26 @@ public class SchemaMapping implements Serializable {
else if (schema instanceof RenamedSchema) return schema;
Schema result = schema;
if (result == null)
result = schema(name(""));
Catalog catalog = result.getCatalog();
if (catalog == null)
catalog = DSL.catalog(name(""));
// [#2089] DefaultSchema has an empty schema name
// [#7498] But we're mapping those names as well
String catalogName = catalog.getName();
String schemaName = result.getName();
String key = StringUtils.isEmpty(catalogName) ? schemaName : catalogName + '.' + schemaName;
RenderMapping m = mapping();
// [#4642] Don't initialise schema mapping if not necessary
if (!mapping().getSchemata().isEmpty() || !mapping().getCatalogs().isEmpty()) {
if (!m.getSchemata().isEmpty() ||
!m.getCatalogs().isEmpty() ||
!isEmpty(m.getDefaultSchema()) ||
!isEmpty(m.getDefaultCatalog())) {
if (result == null)
result = schema(name(""));
Catalog catalog = result.getCatalog();
if (catalog == null)
catalog = DSL.catalog(name(""));
// [#2089] DefaultSchema has an empty schema name
// [#7498] But we're mapping those names as well
String catalogName = catalog.getName();
String schemaName = result.getName();
String key = StringUtils.isEmpty(catalogName) ? schemaName : catalogName + '.' + schemaName;
// Lazy initialise schema mapping
if (!getSchemata().containsKey(key)) {
@ -364,7 +371,7 @@ public class SchemaMapping implements Serializable {
if (!getSchemata().containsKey(key)) {
catalogLoop:
for (MappedCatalog c : mapping().getCatalogs()) {
for (MappedCatalog c : m.getCatalogs()) {
if (matches(c, catalogName)) {
for (MappedSchema s : c.getSchemata()) {
if (matches(s, schemaName)) {
@ -386,8 +393,8 @@ public class SchemaMapping implements Serializable {
}
}
if (!(result instanceof RenamedSchema))
for (MappedSchema s : mapping().getSchemata()) {
if (!(result instanceof RenamedSchema)) {
for (MappedSchema s : m.getSchemata()) {
// A configured mapping was found, add a renamed schema
if (matches(s, schemaName)) {
@ -395,13 +402,27 @@ public class SchemaMapping implements Serializable {
// Ignore self-mappings and void-mappings
if (!isBlank(s.getOutput()))
if (s.getInput() != null && !s.getOutput().equals(schemaName))
result = new RenamedSchema(catalog, result, s.getOutput());
result = new RenamedSchema(map(catalog), result, s.getOutput());
else if (s.getInputExpression() != null)
result = new RenamedSchema(catalog, result, s.getInputExpression().matcher(schemaName).replaceAll(s.getOutput()));
result = new RenamedSchema(map(catalog), result, s.getInputExpression().matcher(schemaName).replaceAll(s.getOutput()));
break;
}
}
}
// [#13034] Apply defaultCatalog irrespective of defaultSchema and the above mappings
if (result.getCatalog() != null && map(result.getCatalog()) == null)
result = new RenamedSchema(null, result, result.getName());
// [#13035] Cache the application of the defaultSchema
if ("".equals(result.getName()))
result = null;
else if (result.getName().equals(m.getDefaultSchema())
&& (result.getCatalog() == null
|| "".equals(result.getCatalog().getName())
|| result.getCatalog().getName().equals(m.getDefaultCatalog())))
result = null;
// Add mapped schema or self if no mapping was found
getSchemata().put(key, result);
@ -412,15 +433,6 @@ public class SchemaMapping implements Serializable {
result = getSchemata().get(key);
}
// The configured default schema is mapped to "null". This prevents
// it from being rendered to SQL
if ("".equals(result.getName())
|| result.getName().equals(mapping().getDefaultSchema())
&& (result.getCatalog() == null
|| "".equals(result.getCatalog().getName())
|| result.getCatalog().getName().equals(mapping().getDefaultCatalog())))
result = null;
return result;
}

View File

@ -240,7 +240,7 @@ implements
ctx.visit(getQualifiedName());
AbstractFunction.acceptFunctionName(ctx, true, getQualifiedName());
}
final void acceptArguments0(Context<?> ctx) {

View File

@ -2166,7 +2166,13 @@ implements
fields.add(getInValues().get(parameter));
}
Field<?> result = function(name != null ? name(name) : AbstractRoutine.this.getQualifiedName(ctx), returnType, fields.toArray(EMPTY_FIELD));
// [#13033] Schema mapping has already been applied, don't re-apply it
Field<?> result = new Function<>(
name != null ? name(name) : AbstractRoutine.this.getQualifiedName(ctx),
returnType,
false,
fields.toArray(EMPTY_FIELD)
);
// [#3592] Decrease SQL -> PL/SQL context switches with Oracle Scalar Subquery Caching
if (TRUE.equals(settings(ctx.configuration()).isRenderScalarSubqueriesForStoredFunctions()))

View File

@ -39,25 +39,18 @@ package org.jooq.impl;
import static org.jooq.impl.DSL.unquotedName;
import static org.jooq.impl.Tools.EMPTY_FIELD;
import static org.jooq.impl.Tools.camelCase;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Function1;
import org.jooq.Name;
import org.jooq.QueryPart;
// ...
// ...
import org.jooq.impl.QOM.UnmodifiableList;
/**
* @author Lukas Eder
*/
final class Function<T> extends AbstractField<T> implements QOM.Function<T> {
final class Function<T> extends AbstractFunction<T> {
private final QueryPartList<Field<?>> arguments;
@ -66,23 +59,18 @@ final class Function<T> extends AbstractField<T> implements QOM.Function<T> {
}
Function(Name name, DataType<T> type, Field<?>... arguments) {
super(name, type);
this(name, type, true, arguments);
}
Function(Name name, DataType<T> type, boolean applySchemaMapping, Field<?>... arguments) {
super(name, type, applySchemaMapping);
this.arguments = new QueryPartList<>(arguments);
}
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
default:
ctx.visit(getQualifiedName()).sql('(').visit(arguments).sql(')');
break;
}
final QueryPart arguments() {
return arguments;
}
// -------------------------------------------------------------------------
@ -104,15 +92,6 @@ final class Function<T> extends AbstractField<T> implements QOM.Function<T> {

View File

@ -37,44 +37,28 @@
*/
package org.jooq.impl;
import static org.jooq.impl.Tools.camelCase;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
import org.jooq.Name;
import org.jooq.QueryPart;
// ...
// ...
import org.jooq.impl.QOM.UnmodifiableList;
/**
* @author Lukas Eder
*/
final class Function1<T> extends AbstractField<T> implements QOM.Function<T> {
final class Function1<T> extends AbstractFunction<T> {
private final Field<?> argument;
Function1(Name name, DataType<T> type, Field<?> argument) {
super(name, type);
super(name, type, true);
this.argument = argument;
}
@Override
public final void accept(Context<?> ctx) {
switch (ctx.family()) {
default:
ctx.visit(getQualifiedName()).sql('(').visit(argument).sql(')');
break;
}
final QueryPart arguments() {
return argument;
}
// -------------------------------------------------------------------------
@ -100,12 +84,16 @@ final class Function1<T> extends AbstractField<T> implements QOM.Function<T> {
// -------------------------------------------------------------------------
// The Object API
// -------------------------------------------------------------------------
@Override
public boolean equals(Object that) {
if (that instanceof Function1)
return getQualifiedName().equals(((Function1<?>) that).getQualifiedName())
&& argument.equals(((Function1<?>) that).argument);
else
return super.equals(that);
}
}