diff --git a/jOOQ-test/src/org/jooq/test/_/testcases/AggregateWindowFunctionTests.java b/jOOQ-test/src/org/jooq/test/_/testcases/AggregateWindowFunctionTests.java index 3aca941004..d2d5f1face 100644 --- a/jOOQ-test/src/org/jooq/test/_/testcases/AggregateWindowFunctionTests.java +++ b/jOOQ-test/src/org/jooq/test/_/testcases/AggregateWindowFunctionTests.java @@ -734,7 +734,6 @@ extends BaseTestORDER BY 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 ORDER BY 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 ORDER BY clause to the query */ - @Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE }) + @Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE }) GroupConcatSeparatorStep orderBy(Collection> fields); } diff --git a/jOOQ/src/main/java/org/jooq/GroupConcatSeparatorStep.java b/jOOQ/src/main/java/org/jooq/GroupConcatSeparatorStep.java index f389ed25a9..619f1ab777 100644 --- a/jOOQ/src/main/java/org/jooq/GroupConcatSeparatorStep.java +++ b/jOOQ/src/main/java/org/jooq/GroupConcatSeparatorStep.java @@ -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 { /** * Specify the separator on the GROUP_CONCAT function */ - @Support({ CUBRID, H2, HSQLDB, MYSQL, ORACLE, SYBASE }) + @Support({ CUBRID, DB2, H2, HSQLDB, MYSQL, ORACLE, SYBASE }) AggregateFunction separator(String separator); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Factory.java b/jOOQ/src/main/java/org/jooq/impl/Factory.java index 6f4ef8f818..f71fa49f01 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Factory.java +++ b/jOOQ/src/main/java/org/jooq/impl/Factory.java @@ -4254,6 +4254,7 @@ public class Factory implements FactoryOperations { * by the following dialects: *
    *
  • {@link SQLDialect#CUBRID}: Using GROUP_CONCAT()
  • + *
  • {@link SQLDialect#DB2}: Using XMLAGG()
  • *
  • {@link SQLDialect#H2}: Using GROUP_CONCAT()
  • *
  • {@link SQLDialect#HSQLDB}: Using GROUP_CONCAT()
  • *
  • {@link SQLDialect#MYSQL}: Using GROUP_CONCAT()
  • @@ -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 listAgg(Field field) { return new Function(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field)); } @@ -4274,6 +4275,7 @@ public class Factory implements FactoryOperations { * by the following dialects: *
      *
    • {@link SQLDialect#CUBRID}: Using GROUP_CONCAT
    • + *
    • {@link SQLDialect#DB2}: Using XMLAGG()
    • *
    • {@link SQLDialect#H2}: Using GROUP_CONCAT
    • *
    • {@link SQLDialect#HSQLDB}: Using GROUP_CONCAT
    • *
    • {@link SQLDialect#MYSQL}: Using GROUP_CONCAT
    • @@ -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 listAgg(Field field, String separator) { Field literal = literal("'" + separator.replace("'", "''") + "'"); return new Function(Term.LIST_AGG, SQLDataType.VARCHAR, nullSafe(field), literal); @@ -4301,13 +4303,14 @@ public class Factory implements FactoryOperations { *

      * It is simulated by the following dialects: *

        + *
      • {@link SQLDialect#DB2}: Using XMLAGG()
      • *
      • {@link SQLDialect#ORACLE}: Using LISTAGG()
      • *
      • {@link SQLDialect#SYBASE}: Using LIST()
      • *
      * * @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)); } diff --git a/jOOQ/src/main/java/org/jooq/impl/Function.java b/jOOQ/src/main/java/org/jooq/impl/Function.java index 3929ce49e6..781d81f926 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Function.java +++ b/jOOQ/src/main/java/org/jooq/impl/Function.java @@ -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 extends AbstractField 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 extends AbstractField implements } } + /** + * [#1276] LIST_AGG 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] LIST_AGG 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 extends AbstractField implements * [#1273] LIST_AGG 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 extends AbstractField implements * Render function arguments and argument modifiers */ private final void toSQLArguments(RenderContext context) { + context.sql(getFNName(context.getDialect())); context.sql("("); if (distinct) { diff --git a/jOOQ/src/main/java/org/jooq/impl/Term.java b/jOOQ/src/main/java/org/jooq/impl/Term.java index 5463b6711c..0ebc617f35 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Term.java +++ b/jOOQ/src/main/java/org/jooq/impl/Term.java @@ -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";