[jOOQ/jOOQ#3564] Emulate PostgreSQL's DISTINCT ON clause
This commit is contained in:
parent
57322fae99
commit
690f1f9002
@ -37,16 +37,31 @@
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.FIREBIRD;
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.SQLITE;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* This type is used for the {@link Select}'s DSL API when selecting generic
|
||||
* {@link Record} types.
|
||||
@ -117,7 +132,7 @@ public interface SelectDistinctOnStep<R extends Record> extends SelectIntoStep<R
|
||||
* it is added explicitly via the jOOQ API.
|
||||
*/
|
||||
@NotNull
|
||||
@Support({ H2, POSTGRES })
|
||||
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
|
||||
SelectIntoStep<R> on(SelectFieldOrAsterisk... fields);
|
||||
|
||||
/**
|
||||
@ -128,7 +143,7 @@ public interface SelectDistinctOnStep<R extends Record> extends SelectIntoStep<R
|
||||
* it is added explicitly via the jOOQ API.
|
||||
*/
|
||||
@NotNull
|
||||
@Support({ H2, POSTGRES })
|
||||
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
|
||||
SelectIntoStep<R> on(Collection<? extends SelectFieldOrAsterisk> fields);
|
||||
|
||||
/**
|
||||
@ -136,7 +151,7 @@ public interface SelectDistinctOnStep<R extends Record> extends SelectIntoStep<R
|
||||
* <code>SELECT DISTINCT ON (...)</code> statement.
|
||||
*/
|
||||
@NotNull
|
||||
@Support({ H2, POSTGRES })
|
||||
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
|
||||
SelectIntoStep<R> distinctOn(SelectFieldOrAsterisk... fields);
|
||||
|
||||
/**
|
||||
@ -144,6 +159,6 @@ public interface SelectDistinctOnStep<R extends Record> extends SelectIntoStep<R
|
||||
* <code>SELECT DISTINCT ON (...)</code> statement.
|
||||
*/
|
||||
@NotNull
|
||||
@Support({ H2, POSTGRES })
|
||||
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
|
||||
SelectIntoStep<R> distinctOn(Collection<? extends SelectFieldOrAsterisk> fields);
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
|
||||
* <p>
|
||||
* This also sets the <code>distinct</code> flag to <code>true</code>
|
||||
*/
|
||||
@Support({ H2, POSTGRES })
|
||||
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
|
||||
void addDistinctOn(SelectFieldOrAsterisk... fields);
|
||||
|
||||
/**
|
||||
@ -125,7 +125,7 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
|
||||
* <p>
|
||||
* This also sets the <code>distinct</code> flag to <code>true</code>
|
||||
*/
|
||||
@Support({ H2, POSTGRES })
|
||||
@Support({ CUBRID, FIREBIRD, H2, MARIADB, MYSQL, POSTGRES, SQLITE })
|
||||
void addDistinctOn(Collection<? extends SelectFieldOrAsterisk> fields);
|
||||
|
||||
/**
|
||||
|
||||
@ -507,4 +507,11 @@ final class Limit extends AbstractQueryPart {
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
final void clear() {
|
||||
offset = null;
|
||||
numberOfRows = null;
|
||||
withTies = false;
|
||||
percent = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,6 +114,7 @@ import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.noCondition;
|
||||
import static org.jooq.impl.DSL.one;
|
||||
import static org.jooq.impl.DSL.orderBy;
|
||||
import static org.jooq.impl.DSL.partitionBy;
|
||||
import static org.jooq.impl.DSL.regexpReplaceAll;
|
||||
import static org.jooq.impl.DSL.row;
|
||||
import static org.jooq.impl.DSL.rowNumber;
|
||||
@ -217,7 +218,11 @@ import org.jooq.Row;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.SelectFieldOrAsterisk;
|
||||
import org.jooq.SelectLimitPercentStep;
|
||||
import org.jooq.SelectLimitStep;
|
||||
import org.jooq.SelectOffsetStep;
|
||||
import org.jooq.SelectQuery;
|
||||
import org.jooq.SelectWithTiesStep;
|
||||
import org.jooq.SortField;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableField;
|
||||
@ -265,6 +270,7 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
private static final Set<SQLDialect> EMULATE_EMPTY_GROUP_BY_CONSTANT = SQLDialect.supportedUntil(DERBY, HSQLDB);
|
||||
private static final Set<SQLDialect> EMULATE_EMPTY_GROUP_BY_OTHER = SQLDialect.supportedUntil(FIREBIRD, MARIADB, MYSQL, SQLITE);
|
||||
private static final Set<SQLDialect> SUPPORT_FULL_WITH_TIES = SQLDialect.supportedBy(H2);
|
||||
private static final Set<SQLDialect> EMULATE_DISTINCT_ON = SQLDialect.supportedBy(DERBY, FIREBIRD, HSQLDB, MARIADB, MYSQL, SQLITE);
|
||||
|
||||
|
||||
|
||||
@ -1119,6 +1125,41 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
final Select<?> distinctOnEmulation() {
|
||||
|
||||
// [#3564] TODO: Extract and merge this with getSelectResolveSomeAsterisks0()
|
||||
List<Field<?>> partitionBy = new ArrayList<>(distinctOn.size());
|
||||
|
||||
for (SelectFieldOrAsterisk f : distinctOn)
|
||||
if (f instanceof Field)
|
||||
partitionBy.add((Field<?>) f);
|
||||
|
||||
Field<Integer> rn = rowNumber().over(partitionBy(partitionBy).orderBy(orderBy)).as("rn");
|
||||
|
||||
SelectQueryImpl<R> copy = copy();
|
||||
copy.distinctOn = null;
|
||||
copy.select.add(rn);
|
||||
copy.orderBy.clear();
|
||||
copy.limit.clear();
|
||||
|
||||
SelectLimitStep<?> s1 =
|
||||
DSL.select(qualify(table(name("t")), select))
|
||||
.from(copy.asTable("t"))
|
||||
.where(rn.eq(one()))
|
||||
.orderBy(unqualified(orderBy.toArray(EMPTY_SORTFIELD)));
|
||||
|
||||
if (limit.numberOfRows != null) {
|
||||
SelectLimitPercentStep<?> s2 = s1.limit((Param) limit.numberOfRows);
|
||||
SelectWithTiesStep<?> s3 = limit.percent ? s2.percent() : s2;
|
||||
SelectOffsetStep<?> s4 = limit.withTies ? s3.withTies() : s3;
|
||||
return limit.offset != null ? s4.offset((Param) limit.offset) : s4;
|
||||
}
|
||||
else
|
||||
return limit.offset != null ? s1.offset((Param) limit.offset) : s1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
@ -1131,6 +1172,11 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
&& containsTable(dmlTable)) {
|
||||
ctx.visit(DSL.select(asterisk()).from(asTable("t")));
|
||||
}
|
||||
|
||||
// [#3564] Emulate DISINTCT ON queries at the top level
|
||||
else if (Tools.isNotEmpty(distinctOn) && EMULATE_DISTINCT_ON.contains(ctx.dialect())) {
|
||||
ctx.visit(distinctOnEmulation());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1397,8 +1397,8 @@ final class Tools {
|
||||
|
||||
SortField<?>[] result = new SortField[fields.length];
|
||||
|
||||
for (SortField<?> field : fields)
|
||||
result[0] = unqualified(field);
|
||||
for (int i = 0; i < result.length; i++)
|
||||
result[i] = unqualified(fields[i]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user