[#1276] Simulate Oracle's LISTAGG() in DB2 using XMLAGG(), SUBSTR() and CONCAT()
This commit is contained in:
parent
ca32a0fa39
commit
003344ef32
@ -734,7 +734,6 @@ 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 DB2:
|
||||
case DERBY:
|
||||
case INGRES:
|
||||
case POSTGRES:
|
||||
@ -773,6 +772,7 @@ extends BaseTest<A, B, S, B2S, BS, L, X, DATE, BOOL, D, T, U, I, IPK, T658, T725
|
||||
|
||||
switch (getDialect()) {
|
||||
case CUBRID:
|
||||
case DB2:
|
||||
case H2:
|
||||
case HSQLDB:
|
||||
case MYSQL:
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
package org.jooq;
|
||||
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.DB2;
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
import static org.jooq.SQLDialect.HSQLDB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
@ -58,18 +59,18 @@ public interface GroupConcatOrderByStep extends GroupConcatSeparatorStep {
|
||||
/**
|
||||
* Add an <code>ORDER BY</code> clause to the query
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
GroupConcatSeparatorStep orderBy(Field<?>... fields);
|
||||
|
||||
/**
|
||||
* Add an <code>ORDER BY</code> clause to the query
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
GroupConcatSeparatorStep orderBy(SortField<?>... fields);
|
||||
|
||||
/**
|
||||
* Add an <code>ORDER BY</code> clause to the query
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
GroupConcatSeparatorStep orderBy(Collection<SortField<?>> fields);
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
package org.jooq;
|
||||
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.DB2;
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
import static org.jooq.SQLDialect.HSQLDB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
@ -55,6 +56,6 @@ public interface GroupConcatSeparatorStep extends AggregateFunction<String> {
|
||||
/**
|
||||
* Specify the separator on the <code>GROUP_CONCAT</code> function
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
AggregateFunction<String> separator(String separator);
|
||||
}
|
||||
|
||||
@ -4254,6 +4254,7 @@ public class Factory implements FactoryOperations {
|
||||
* by the following dialects:
|
||||
* <ul>
|
||||
* <li> {@link SQLDialect#CUBRID}: Using <code>GROUP_CONCAT()</code></li>
|
||||
* <li> {@link SQLDialect#DB2}: Using <code>XMLAGG()</code></li>
|
||||
* <li> {@link SQLDialect#H2}: Using <code>GROUP_CONCAT()</code></li>
|
||||
* <li> {@link SQLDialect#HSQLDB}: Using <code>GROUP_CONCAT()</code></li>
|
||||
* <li> {@link SQLDialect#MYSQL}: Using <code>GROUP_CONCAT()</code></li>
|
||||
@ -4262,7 +4263,7 @@ public class Factory implements FactoryOperations {
|
||||
*
|
||||
* @see #groupConcat(Field)
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
public static OrderedAggregateFunction<String> listAgg(Field<?> field) {
|
||||
return new Function<String>(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field));
|
||||
}
|
||||
@ -4274,6 +4275,7 @@ public class Factory implements FactoryOperations {
|
||||
* by the following dialects:
|
||||
* <ul>
|
||||
* <li> {@link SQLDialect#CUBRID}: Using <code>GROUP_CONCAT</code></li>
|
||||
* <li> {@link SQLDialect#DB2}: Using <code>XMLAGG()</code></li>
|
||||
* <li> {@link SQLDialect#H2}: Using <code>GROUP_CONCAT</code></li>
|
||||
* <li> {@link SQLDialect#HSQLDB}: Using <code>GROUP_CONCAT</code></li>
|
||||
* <li> {@link SQLDialect#MYSQL}: Using <code>GROUP_CONCAT</code></li>
|
||||
@ -4282,7 +4284,7 @@ public class Factory implements FactoryOperations {
|
||||
*
|
||||
* @see #groupConcat(Field, String)
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
public static OrderedAggregateFunction<String> listAgg(Field<?> field, String separator) {
|
||||
Field<String> literal = literal("'" + separator.replace("'", "''") + "'");
|
||||
return new Function<String>(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field), literal);
|
||||
@ -4301,13 +4303,14 @@ public class Factory implements FactoryOperations {
|
||||
* <p>
|
||||
* It is simulated by the following dialects:
|
||||
* <ul>
|
||||
* <li> {@link SQLDialect#DB2}: Using <code>XMLAGG()</code></li>
|
||||
* <li> {@link SQLDialect#ORACLE}: Using <code>LISTAGG()</code></li>
|
||||
* <li> {@link SQLDialect#SYBASE}: Using <code>LIST()</code></li>
|
||||
* </ul>
|
||||
*
|
||||
* @see #listAgg(Field)
|
||||
*/
|
||||
@Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
@Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE })
|
||||
public static GroupConcatOrderByStep groupConcat(Field<?> field) {
|
||||
return new GroupConcat(nullSafe(field));
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ package org.jooq.impl;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jooq.SQLDialect.CUBRID;
|
||||
import static org.jooq.SQLDialect.DB2;
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
import static org.jooq.SQLDialect.HSQLDB;
|
||||
import static org.jooq.SQLDialect.MYSQL;
|
||||
@ -66,6 +67,7 @@ import org.jooq.WindowOverStep;
|
||||
import org.jooq.WindowPartitionByStep;
|
||||
import org.jooq.WindowRowsAndStep;
|
||||
import org.jooq.WindowRowsStep;
|
||||
import org.jooq.util.db2.DB2DataType;
|
||||
|
||||
/**
|
||||
* A field that handles built-in functions, aggregate functions, and window
|
||||
@ -167,14 +169,15 @@ class Function<T> extends AbstractField<T> implements
|
||||
|
||||
@Override
|
||||
public final void toSQL(RenderContext context) {
|
||||
context.sql(getFNName(context.getDialect()));
|
||||
|
||||
if (term == LIST_AGG && asList(CUBRID, H2, HSQLDB, MYSQL).contains(context.getDialect())) {
|
||||
toSQLGroupConcat(context);
|
||||
}
|
||||
else if (term == LIST_AGG && asList(SYBASE).contains(context.getDialect())) {
|
||||
toSQLList(context);
|
||||
}
|
||||
else if (term == LIST_AGG && asList(DB2).contains(context.getDialect())) {
|
||||
toSQLXMLAGG(context);
|
||||
}
|
||||
else {
|
||||
toSQLArguments(context);
|
||||
toSQLWithinGroupClause(context);
|
||||
@ -182,10 +185,58 @@ class Function<T> extends AbstractField<T> implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [#1276] <code>LIST_AGG</code> simulation for DB2
|
||||
*/
|
||||
private void toSQLXMLAGG(RenderContext context) {
|
||||
|
||||
// This is a complete view of what the below SQL will render
|
||||
// substr(xmlserialize(xmlagg(xmltext(concat(', ', title)) order by id) as varchar(1024)), 3)
|
||||
if (arguments.size() > 1) {
|
||||
context.keyword("substr(");
|
||||
}
|
||||
|
||||
context.keyword("xmlserialize(xmlagg(xmltext(");
|
||||
|
||||
if (arguments.size() > 1) {
|
||||
context.keyword("concat(")
|
||||
.sql(arguments.get(1))
|
||||
.sql(", ");
|
||||
}
|
||||
|
||||
context.sql(arguments.get(0));
|
||||
|
||||
if (arguments.size() > 1) {
|
||||
context.sql(")"); // CONCAT
|
||||
}
|
||||
|
||||
context.sql(")"); // XMLTEXT
|
||||
|
||||
if (!withinGroupOrderBy.isEmpty()) {
|
||||
context.keyword(" order by ")
|
||||
.sql(withinGroupOrderBy);
|
||||
}
|
||||
|
||||
context.sql(")"); // XMLAGG
|
||||
context.keyword(" as ");
|
||||
context.sql(DB2DataType.VARCHAR.getCastTypeName());
|
||||
context.sql(")"); // XMLSERIALIZE
|
||||
|
||||
if (arguments.size() > 1) {
|
||||
context.sql(", ");
|
||||
|
||||
// The separator is of this form: [', '].
|
||||
// The example has length 4
|
||||
context.sql(arguments.get(1).toString().length() - 1);
|
||||
context.sql(")"); // SUBSTR
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [#1275] <code>LIST_AGG</code> simulation for CUBRID, HSQLDB, MySQL
|
||||
*/
|
||||
private void toSQLList(RenderContext context) {
|
||||
context.sql(getFNName(context.getDialect()));
|
||||
context.sql("(");
|
||||
|
||||
if (distinct) {
|
||||
@ -206,6 +257,7 @@ class Function<T> extends AbstractField<T> implements
|
||||
* [#1273] <code>LIST_AGG</code> simulation for MySQL and CUBRID
|
||||
*/
|
||||
private final void toSQLGroupConcat(RenderContext context) {
|
||||
context.sql(getFNName(context.getDialect()));
|
||||
context.sql("(");
|
||||
|
||||
if (distinct) {
|
||||
@ -308,6 +360,7 @@ class Function<T> extends AbstractField<T> implements
|
||||
* Render function arguments and argument modifiers
|
||||
*/
|
||||
private final void toSQLArguments(RenderContext context) {
|
||||
context.sql(getFNName(context.getDialect()));
|
||||
context.sql("(");
|
||||
|
||||
if (distinct) {
|
||||
|
||||
@ -110,6 +110,11 @@ enum Term {
|
||||
case MYSQL:
|
||||
return "group_concat";
|
||||
|
||||
// DB2 needs to do some rather complex XML manipulation to
|
||||
// achieve the same results. XMLAGG() itself cannot do it
|
||||
case DB2:
|
||||
return "xmlagg";
|
||||
|
||||
case ORACLE:
|
||||
return "listagg";
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user