[jOOQ/jOOQ#10016] Improve H2's derived column list support

This commit is contained in:
Lukas Eder 2020-03-31 14:50:05 +02:00
parent 9b92617584
commit aa20f07f4e
2 changed files with 62 additions and 34 deletions

View File

@ -75,9 +75,12 @@ import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.Keywords.K_AS;
import static org.jooq.impl.QueryPartListView.wrap;
import static org.jooq.impl.Tools.fieldNames;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_AS_REQUIRED;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_UNALIAS_ALIASED_EXPRESSIONS;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.jooq.Clause;
@ -103,7 +106,8 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart {
private static final Clause[] CLAUSES_FIELD_ALIAS = { FIELD, FIELD_ALIAS };
private static final Set<SQLDialect> SUPPORT_AS_REQUIRED = SQLDialect.supportedBy(DERBY, HSQLDB, MARIADB, MYSQL, POSTGRES, SQLITE);
private static final Set<SQLDialect> SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL1 = SQLDialect.supportedBy(CUBRID, FIREBIRD, MYSQL);
private static final Set<SQLDialect> SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL2 = SQLDialect.supportedBy(H2, MARIADB, MYSQL, SQLITE);
private static final Set<SQLDialect> SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL2 = SQLDialect.supportedBy(MARIADB, MYSQL, SQLITE);
private static final Set<SQLDialect> SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL3 = SQLDialect.supportedBy(H2);
final Q wrapped;
final Q wrapping;
@ -179,49 +183,61 @@ final class Alias<Q extends QueryPart> extends AbstractQueryPart {
// [#1801] Some databases do not support "derived column names".
// They can be emulated by concatenating a dummy SELECT with no
// results using UNION ALL
else if (fieldAliases != null && SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL2.contains(family)) {
else if (fieldAliases != null && (
SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL2.contains(family)
|| SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL3.contains(family))) {
emulatedDerivedColumnList = true;
SelectFieldList<Field<?>> fields = new SelectFieldList<>();
for (Name fieldAlias : fieldAliases) {
switch (family) {
// [#3156] Do not SELECT * from derived tables to prevent ambiguously defined columns
// in those derived tables
Select<? extends Record> wrappedAsSelect =
wrapped instanceof Select
? (Select<?>) wrapped
: wrapped instanceof DerivedTable
? ((DerivedTable<?>) wrapped).query()
: select(asterisk()).from(((Table<?>) wrapped).as(alias));
// [#9486] H2 cannot handle duplicate column names in derived tables, despite derived column lists
// See: https://github.com/h2database/h2database/issues/2532
if (SUPPORT_DERIVED_COLUMN_NAMES_SPECIAL3.contains(family)) {
List<Name> names = fieldNames(wrappedAsSelect.getSelect());
default: {
fields.add(field("null").as(fieldAlias));
break;
}
if (names.size() > 0 && names.size() == new HashSet<>(names).size()) {
toSQLWrapped(context);
emulatedDerivedColumnList = false;
}
}
Select<Record> select = select(fields).where(falseCondition())
.unionAll(
if (emulatedDerivedColumnList) {
SelectFieldList<Field<?>> fields = new SelectFieldList<>();
for (Name fieldAlias : fieldAliases) {
switch (family) {
// [#3156] Do not SELECT * from derived tables to prevent ambiguously defined columns
// in those derived tables
wrapped instanceof Select
? (Select<?>) wrapped
: wrapped instanceof DerivedTable
? ((DerivedTable<?>) wrapped).query()
: select(asterisk()).from(((Table<?>) wrapped).as(alias))
);
context.sql('(')
.formatIndentStart().formatNewLine()
.subquery(true)
.visit(select)
.subquery(false)
.formatIndentEnd().formatNewLine()
.sql(')');
default: {
fields.add(field("null").as(fieldAlias));
break;
}
}
}
context.sql('(')
.formatIndentStart().formatNewLine()
.subquery(true)
.visit(select(fields).where(falseCondition()).unionAll(wrappedAsSelect))
.subquery(false)
.formatIndentEnd().formatNewLine()
.sql(')');
}
}
// The default behaviour

View File

@ -1273,6 +1273,18 @@ final class Tools {
return result;
}
static final List<Name> fieldNames(Collection<? extends Field<?>> fields) {
if (fields == null)
return null;
List<Name> result = new ArrayList<>(fields.size());
for (Field<?> field : fields)
result.add(field.getUnqualifiedName());
return result;
}
static final String[] fieldNameStrings(Field<?>[] fields) {
if (fields == null)
return null;