[#1273] Simulate MySQL's / CUBRID's GROUP_CONCAT() aggregate function using Oracle's LISTAGG() function
[#1275] Simulate Sybase's LIST() aggregate function using Oracle's LISTAGG() function
This commit is contained in:
parent
e1220a5e75
commit
367befbe98
@ -733,17 +733,14 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
|
||||
public void testListAgg() throws Exception {
|
||||
switch (getDialect()) {
|
||||
case ASE:
|
||||
case CUBRID:
|
||||
case DB2:
|
||||
case DERBY:
|
||||
case H2:
|
||||
case HSQLDB:
|
||||
case INGRES:
|
||||
case MYSQL:
|
||||
case POSTGRES:
|
||||
case SQLITE:
|
||||
case SQLSERVER:
|
||||
case SYBASE:
|
||||
log.info("SKIPPING", "LISTAGG tests");
|
||||
return;
|
||||
}
|
||||
@ -751,7 +748,7 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
|
||||
Result<?> result1 = create().select(
|
||||
TAuthor_FIRST_NAME(),
|
||||
TAuthor_LAST_NAME(),
|
||||
listAgg(TBook_TITLE(), ", ").withinGroupOrderBy(TBook_ID().desc()).as("books"))
|
||||
listAgg(TBook_ID(), ", ").withinGroupOrderBy(TBook_ID().desc()).as("books"))
|
||||
.from(TAuthor())
|
||||
.join(TBook()).on(TAuthor_ID().equal(TBook_AUTHOR_ID()))
|
||||
.groupBy(
|
||||
@ -764,8 +761,16 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
|
||||
assertEquals(2, result1.size());
|
||||
assertEquals(AUTHOR_FIRST_NAMES, result1.getValues(TAuthor_FIRST_NAME()));
|
||||
assertEquals(AUTHOR_LAST_NAMES, result1.getValues(TAuthor_LAST_NAME()));
|
||||
assertEquals("Animal Farm, 1984", result1.getValue(0, "books"));
|
||||
assertEquals("Brida, O Alquimista", result1.getValue(1, "books"));
|
||||
assertEquals("2, 1", result1.getValue(0, "books"));
|
||||
assertEquals("4, 3", result1.getValue(1, "books"));
|
||||
|
||||
switch (getDialect()) {
|
||||
case CUBRID:
|
||||
case MYSQL:
|
||||
case SYBASE:
|
||||
log.info("SKIPPING", "LISTAGG window function tests");
|
||||
return;
|
||||
}
|
||||
|
||||
Result<?> result2 = create().select(
|
||||
TAuthor_FIRST_NAME(),
|
||||
|
||||
@ -75,7 +75,7 @@ public interface OrderedAggregateFunction<T> {
|
||||
* Add an <code>WITHIN GROUP (ORDER BY ..)</code> clause to the ordered
|
||||
* aggregate function
|
||||
*/
|
||||
@Support
|
||||
@Support(ORACLE)
|
||||
AggregateFunction<T> withinGroupOrderBy(SortField<?>... fields);
|
||||
|
||||
/**
|
||||
|
||||
@ -4248,18 +4248,35 @@ public class Factory implements FactoryOperations {
|
||||
|
||||
/**
|
||||
* Get the aggregated concatenation for a field.
|
||||
* <p>
|
||||
* This is natively supported by {@link SQLDialect#ORACLE}. It is simulated
|
||||
* by the following dialects:
|
||||
* <ul>
|
||||
* <li> {@link SQLDialect#CUBRID}: Using <code>GROUP_CONCAT()</code></li>
|
||||
* <li> {@link SQLDialect#MYSQL}: Using <code>GROUP_CONCAT()</code></li>
|
||||
* <li> {@link SQLDialect#SYBASE}: Using <code>LIST()</code></li>
|
||||
* </ul>
|
||||
*/
|
||||
@Support(ORACLE)
|
||||
@Support({ CUBRID, MYSQL, ORACLE, SYBASE })
|
||||
public static OrderedAggregateFunction<String> listAgg(Field<?> field) {
|
||||
return new Function<String>(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aggregated concatenation for a field.
|
||||
* <p>
|
||||
* This is natively supported by {@link SQLDialect#ORACLE}. It is simulated
|
||||
* by the following dialects:
|
||||
* <ul>
|
||||
* <li> {@link SQLDialect#CUBRID}: Using <code>GROUP_CONCAT</code></li>
|
||||
* <li> {@link SQLDialect#MYSQL}: Using <code>GROUP_CONCAT</code></li>
|
||||
* <li> {@link SQLDialect#SYBASE}: Using <code>LIST()</code></li>
|
||||
* </ul>
|
||||
*/
|
||||
@Support(ORACLE)
|
||||
@Support({ CUBRID, MYSQL, ORACLE, SYBASE })
|
||||
public static OrderedAggregateFunction<String> listAgg(Field<?> field, String delimiter) {
|
||||
return new Function<String>(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field), literal("'" + delimiter.replace("'", "''") + "'"));
|
||||
Field<String> literal = literal("'" + delimiter.replace("'", "''") + "'");
|
||||
return new Function<String>(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field), literal);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -36,7 +36,12 @@
|
||||
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
import static org.jooq.SQLDialect.SYBASE;
|
||||
import static org.jooq.impl.Factory.one;
|
||||
import static org.jooq.impl.Term.LIST_AGG;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -138,14 +143,87 @@ class Function<T> extends AbstractField<T> implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
context.sql(getFNName(context.getDialect()));
|
||||
toSQLArguments(context);
|
||||
toSQLWithinGroupClause(context);
|
||||
toSQLOverClause(context);
|
||||
public final void bind(BindContext context) {
|
||||
|
||||
if (term == LIST_AGG && asList(MYSQL, CUBRID).contains(context.getDialect())) {
|
||||
context.bind(arguments.get(0));
|
||||
context.bind((QueryPart) withinGroupOrderBy);
|
||||
|
||||
if (arguments.size() > 1) {
|
||||
context.bind(arguments.get(1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
context.bind((QueryPart) arguments)
|
||||
.bind((QueryPart) withinGroupOrderBy)
|
||||
.bind((QueryPart) partitionBy)
|
||||
.bind((QueryPart) orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
private void toSQLOverClause(RenderContext context) {
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
context.sql(getFNName(context.getDialect()));
|
||||
|
||||
if (term == LIST_AGG && asList(MYSQL, CUBRID).contains(context.getDialect())) {
|
||||
toSQLGroupConcat(context);
|
||||
}
|
||||
if (term == LIST_AGG && asList(SYBASE).contains(context.getDialect())) {
|
||||
toSQLList(context);
|
||||
}
|
||||
else {
|
||||
toSQLArguments(context);
|
||||
toSQLWithinGroupClause(context);
|
||||
toSQLOverClause(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [#1275] <code>LIST_AGG</code> simulation for MySQL and CUBRID
|
||||
*/
|
||||
private void toSQLList(RenderContext context) {
|
||||
context.sql("(");
|
||||
|
||||
if (distinct) {
|
||||
context.keyword("distinct ");
|
||||
}
|
||||
|
||||
context.sql(arguments);
|
||||
|
||||
if (!withinGroupOrderBy.isEmpty()) {
|
||||
context.keyword(" order by ")
|
||||
.sql(withinGroupOrderBy);
|
||||
}
|
||||
|
||||
context.sql(")");
|
||||
}
|
||||
|
||||
/**
|
||||
* [#1273] <code>LIST_AGG</code> simulation for MySQL and CUBRID
|
||||
*/
|
||||
private final void toSQLGroupConcat(RenderContext context) {
|
||||
context.sql("(");
|
||||
|
||||
if (distinct) {
|
||||
context.keyword("distinct ");
|
||||
}
|
||||
|
||||
context.sql(arguments.get(0));
|
||||
|
||||
if (!withinGroupOrderBy.isEmpty()) {
|
||||
context.keyword(" order by ")
|
||||
.sql(withinGroupOrderBy);
|
||||
}
|
||||
|
||||
if (arguments.size() > 1) {
|
||||
context.keyword(" separator ")
|
||||
.sql(arguments.get(1));
|
||||
}
|
||||
|
||||
context.sql(")");
|
||||
}
|
||||
|
||||
private final void toSQLOverClause(RenderContext context) {
|
||||
if (!over) return;
|
||||
|
||||
String glue = "";
|
||||
@ -214,7 +292,7 @@ class Function<T> extends AbstractField<T> implements
|
||||
/**
|
||||
* Render <code>WITHIN GROUP (ORDER BY ..)</code> clause
|
||||
*/
|
||||
private void toSQLWithinGroupClause(RenderContext context) {
|
||||
private final void toSQLWithinGroupClause(RenderContext context) {
|
||||
if (!withinGroupOrderBy.isEmpty()) {
|
||||
context.keyword(" within group (order by ")
|
||||
.sql(withinGroupOrderBy)
|
||||
@ -225,7 +303,7 @@ class Function<T> extends AbstractField<T> implements
|
||||
/**
|
||||
* Render function arguments and argument modifiers
|
||||
*/
|
||||
private void toSQLArguments(RenderContext context) {
|
||||
private final void toSQLArguments(RenderContext context) {
|
||||
context.sql("(");
|
||||
|
||||
if (distinct) {
|
||||
@ -285,14 +363,6 @@ class Function<T> extends AbstractField<T> implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void bind(BindContext context) {
|
||||
context.bind((QueryPart) arguments)
|
||||
.bind((QueryPart) withinGroupOrderBy)
|
||||
.bind((QueryPart) partitionBy)
|
||||
.bind((QueryPart) orderBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isNullLiteral() {
|
||||
return false;
|
||||
|
||||
@ -103,6 +103,18 @@ enum Term {
|
||||
LIST_AGG {
|
||||
@Override
|
||||
public String translate(SQLDialect dialect) {
|
||||
switch (dialect) {
|
||||
case CUBRID:
|
||||
case MYSQL:
|
||||
return "group_concat";
|
||||
|
||||
case ORACLE:
|
||||
return "listagg";
|
||||
|
||||
case SYBASE:
|
||||
return "list";
|
||||
}
|
||||
|
||||
return "listagg";
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user