diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java b/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java index dbd0d09d31..e9ed4de8a1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java @@ -284,7 +284,12 @@ implements final void acceptArguments3(Context ctx, QueryPartCollectionView> args, Function, ? extends Field> fun) { if (args.isEmpty() && this instanceof Count) - args = QueryPartListView.wrap(ASTERISK.get()); + + // [#7539] Work around https://github.com/ClickHouse/ClickHouse/issues/61004 + if (ctx.family() == CLICKHOUSE && filter.hasWhere()) + args = QueryPartListView.wrap(); + else + args = QueryPartListView.wrap(ASTERISK.get()); if (!filter.hasWhere() || supportsFilter(ctx)) ctx.visit(wrap(args).map(fun)); diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayAppend.java b/jOOQ/src/main/java/org/jooq/impl/ArrayAppend.java index 3732057769..f632ea638f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayAppend.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayAppend.java @@ -100,6 +100,9 @@ implements case TRINO: return false; + case CLICKHOUSE: + return false; + default: return true; } @@ -120,6 +123,10 @@ implements ctx.visit(arrayConcat(arg1, array(arg2))); break; + case CLICKHOUSE: + ctx.visit(function(N_arrayPushBack, getDataType(), arg1, arg2)); + break; + default: ctx.visit(function(N_ARRAY_APPEND, getDataType(), arg1, arg2)); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayConcat.java b/jOOQ/src/main/java/org/jooq/impl/ArrayConcat.java index 30db196f51..cac9c65dcb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayConcat.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayConcat.java @@ -92,6 +92,17 @@ implements // XXX: QueryPart API // ------------------------------------------------------------------------- + @Override + final boolean parenthesised(Context ctx) { + switch (ctx.family()) { + case CLICKHOUSE: + return true; + + default: + return false; + } + } + @Override public final void accept(Context ctx) { switch (ctx.family()) { @@ -101,6 +112,10 @@ implements + case CLICKHOUSE: + ctx.visit(function(N_arrayConcat, getDataType(), arg1, arg2)); + break; + default: ctx.sql('(').visit(arg1).sql(" || ").visit(arg2).sql(')'); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayPrepend.java b/jOOQ/src/main/java/org/jooq/impl/ArrayPrepend.java index b2d63e6b3c..1b885a8b04 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayPrepend.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayPrepend.java @@ -100,6 +100,9 @@ implements case TRINO: return false; + case CLICKHOUSE: + return false; + default: return true; } @@ -120,6 +123,10 @@ implements ctx.visit(arrayConcat(array(arg1), arg2)); break; + case CLICKHOUSE: + ctx.visit(function(N_arrayPushFront, getDataType(), arg2, arg1)); + break; + default: ctx.visit(function(N_ARRAY_PREPEND, getDataType(), arg1, arg2)); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/Cardinality.java b/jOOQ/src/main/java/org/jooq/impl/Cardinality.java index 51ff65636a..4117be6528 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Cardinality.java +++ b/jOOQ/src/main/java/org/jooq/impl/Cardinality.java @@ -103,6 +103,10 @@ implements + case CLICKHOUSE: + ctx.visit(function(N_LENGTH, getDataType(), array)); + break; + case DUCKDB: ctx.visit(function(N_ARRAY_LENGTH, getDataType(), array)); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/Choose.java b/jOOQ/src/main/java/org/jooq/impl/Choose.java index fc8d4d6a4e..56edbbf589 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Choose.java +++ b/jOOQ/src/main/java/org/jooq/impl/Choose.java @@ -96,6 +96,7 @@ final class Choose extends AbstractField implements QOM.Choose { + case CLICKHOUSE: case CUBRID: case DERBY: case DUCKDB: diff --git a/jOOQ/src/main/java/org/jooq/impl/Coth.java b/jOOQ/src/main/java/org/jooq/impl/Coth.java index 2be9e36af3..e0a7bcab7d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Coth.java +++ b/jOOQ/src/main/java/org/jooq/impl/Coth.java @@ -113,6 +113,7 @@ implements + case CLICKHOUSE: case CUBRID: case DERBY: case DUCKDB: @@ -162,6 +163,7 @@ implements + case CLICKHOUSE: case CUBRID: case DERBY: case DUCKDB: diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index 8858ac815d..800a518037 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -22108,7 +22108,7 @@ public class DSL { * Calculate the cardinality of an array field. */ @NotNull - @Support({ DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field cardinality(Field array) { return new Cardinality(array); } @@ -22121,7 +22121,7 @@ public class DSL { * @param index is wrapped as {@link DSL#val(Object)}. */ @NotNull - @Support({ DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayGet(Field array, int index) { return new ArrayGet<>(array, Tools.field(index)); } @@ -22132,7 +22132,7 @@ public class DSL { * Get an array element at a given index (1 based). */ @NotNull - @Support({ DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, DUCKDB, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayGet(Field array, Field index) { return new ArrayGet<>(array, index); } @@ -22143,7 +22143,7 @@ public class DSL { * Concatenate two arrays. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayConcat(T[] arg1, T[] arg2) { return new ArrayConcat<>(Tools.field(arg1), Tools.field(arg2)); } @@ -22154,7 +22154,7 @@ public class DSL { * Concatenate two arrays. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayConcat(T[] arg1, Field arg2) { return new ArrayConcat<>(Tools.field(arg1), arg2); } @@ -22165,7 +22165,7 @@ public class DSL { * Concatenate two arrays. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayConcat(Field arg1, T[] arg2) { return new ArrayConcat<>(arg1, Tools.field(arg2, arg1)); } @@ -22176,7 +22176,7 @@ public class DSL { * Concatenate two arrays. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayConcat(Field arg1, Field arg2) { return new ArrayConcat<>(arg1, arg2); } @@ -22189,7 +22189,7 @@ public class DSL { * @param arg2 is wrapped as {@link DSL#val(Object)}. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayAppend(T[] arg1, T arg2) { return new ArrayAppend<>(Tools.field(arg1), Tools.field(arg2)); } @@ -22200,7 +22200,7 @@ public class DSL { * Append an element to an array. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayAppend(T[] arg1, Field arg2) { return new ArrayAppend<>(Tools.field(arg1), arg2); } @@ -22213,7 +22213,7 @@ public class DSL { * @param arg2 is wrapped as {@link DSL#val(Object)}. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayAppend(Field arg1, T arg2) { return new ArrayAppend<>(arg1, Tools.field(arg2)); } @@ -22224,7 +22224,7 @@ public class DSL { * Append an element to an array. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayAppend(Field arg1, Field arg2) { return new ArrayAppend<>(arg1, arg2); } @@ -22237,7 +22237,7 @@ public class DSL { * @param arg1 is wrapped as {@link DSL#val(Object)}. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayPrepend(T arg1, T[] arg2) { return new ArrayPrepend<>(Tools.field(arg1), Tools.field(arg2)); } @@ -22250,7 +22250,7 @@ public class DSL { * @param arg1 is wrapped as {@link DSL#val(Object)}. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayPrepend(T arg1, Field arg2) { return new ArrayPrepend<>(Tools.field(arg1), arg2); } @@ -22261,7 +22261,7 @@ public class DSL { * Prepend an element to an array. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayPrepend(Field arg1, T[] arg2) { return new ArrayPrepend<>(arg1, Tools.field(arg2)); } @@ -22272,7 +22272,7 @@ public class DSL { * Prepend an element to an array. */ @NotNull - @Support({ H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) + @Support({ CLICKHOUSE, H2, HSQLDB, POSTGRES, TRINO, YUGABYTEDB }) public static Field arrayPrepend(Field arg1, Field arg2) { return new ArrayPrepend<>(arg1, arg2); } diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java index 885a9b5f37..f5b70e0c03 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultRenderContext.java @@ -48,6 +48,7 @@ import static org.jooq.impl.Identifiers.QUOTES; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED; import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER; +import static org.jooq.impl.JoinTable.NO_SUPPORT_NESTED_JOIN; import static org.jooq.impl.Tools.EMPTY_PARAM; import static org.jooq.impl.Tools.BooleanDataKey.DATA_COUNT_BIND_VALUES; import static org.jooq.impl.Tools.BooleanDataKey.DATA_FORCE_STATIC_STATEMENT; @@ -306,18 +307,24 @@ class DefaultRenderContext extends AbstractContext implements Ren // TODO: subqueryLevel() is lower than scopeLevel if we use implicit join in procedural logic else if (e1.joinNode != null && e1.joinNode.hasJoinPaths()) { DefaultRenderContext ctx = new DefaultRenderContext(this, false); - ctx.data(DATA_RENDER_IMPLICIT_JOIN, true); - replacedSQL = ctx - .declareTables(true) - .sql('(') - .formatIndentStart(e1.indent) - .formatIndentStart() - .formatNewLine() - .visit(e1.joinNode.joinTree()) - .formatNewLine() - .sql(')') - .render(); + boolean noNesting = !NO_SUPPORT_NESTED_JOIN.contains(ctx.dialect()); + ctx.data(DATA_RENDER_IMPLICIT_JOIN, true); + ctx.declareTables(true); + + if (noNesting) + ctx.sql('(') + .formatIndentStart(e1.indent) + .formatIndentStart() + .formatNewLine(); + + ctx.visit(e1.joinNode.joinTree()); + + if (noNesting) + ctx.formatNewLine() + .sql(')'); + + replacedSQL = ctx.render(); insertedBindValues = ctx.bindValues(); } else { diff --git a/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java b/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java index 3ccaf8c1e4..8147abb598 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/DerivedTable.java @@ -38,6 +38,7 @@ package org.jooq.impl; +import static org.jooq.SQLDialect.CLICKHOUSE; // ... import static org.jooq.SQLDialect.DERBY; import static org.jooq.SQLDialect.H2; @@ -66,7 +67,7 @@ import org.jooq.TableOptions; */ class DerivedTable extends AbstractTable implements QOM.DerivedTable { - static final Set NO_SUPPORT_CORRELATED_DERIVED_TABLE = SQLDialect.supportedUntil(DERBY, H2, MARIADB); + static final Set NO_SUPPORT_CORRELATED_DERIVED_TABLE = SQLDialect.supportedUntil(CLICKHOUSE, DERBY, H2, MARIADB); private final Lazy> query; DerivedTable(Select query) { diff --git a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java index d6ba873539..e1597bd412 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsDistinctFrom.java @@ -95,7 +95,8 @@ implements - static final Set EMULATE_DISTINCT_PREDICATE = SQLDialect.supportedUntil(CLICKHOUSE, CUBRID, DERBY); + static final Set EMULATE_DISTINCT_PREDICATE = SQLDialect.supportedUntil(CUBRID, DERBY); + static final Set EMULATE_WITH_ARRAYS = SQLDialect.supportedBy(CLICKHOUSE); static final Set SUPPORT_DISTINCT_WITH_ARROW = SQLDialect.supportedBy(MARIADB, MYSQL); @@ -123,6 +124,10 @@ implements else if (EMULATE_DISTINCT_PREDICATE.contains(ctx.dialect())) ctx.visit(notExists(select(arg1.as("x")).intersect(select(arg2.as("x"))))); + // [#7539] While INTERSECT is supported, correlating subqueries hardly is in ClickHouse + else if (EMULATE_WITH_ARRAYS.contains(ctx.dialect())) + ctx.visit(function(N_arrayUniq, INTEGER, array(arg1, arg2)).eq(inline(2))); + // MySQL knows the <=> operator else if (SUPPORT_DISTINCT_WITH_ARROW.contains(ctx.dialect())) ctx.visit(condition("{not}({0} <=> {1})", arg1, arg2)); diff --git a/jOOQ/src/main/java/org/jooq/impl/IsNotDistinctFrom.java b/jOOQ/src/main/java/org/jooq/impl/IsNotDistinctFrom.java index e6f8e9b78b..e17940d664 100644 --- a/jOOQ/src/main/java/org/jooq/impl/IsNotDistinctFrom.java +++ b/jOOQ/src/main/java/org/jooq/impl/IsNotDistinctFrom.java @@ -116,6 +116,10 @@ implements else if (IsDistinctFrom.EMULATE_DISTINCT_PREDICATE.contains(ctx.dialect())) ctx.visit(exists(select(arg1.as("x")).intersect(select(arg2.as("x"))))); + // [#7539] While INTERSECT is supported, correlating subqueries hardly is in ClickHouse + else if (IsDistinctFrom.EMULATE_WITH_ARRAYS.contains(ctx.dialect())) + ctx.visit(function(N_arrayUniq, INTEGER, array(arg1, arg2)).eq(inline(1))); + // MySQL knows the <=> operator else if (IsDistinctFrom.SUPPORT_DISTINCT_WITH_ARROW.contains(ctx.dialect())) ctx.visit(condition("{0} <=> {1}", arg1, arg2)); diff --git a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java index daaccd8d01..81bab5d332 100755 --- a/jOOQ/src/main/java/org/jooq/impl/JoinTable.java +++ b/jOOQ/src/main/java/org/jooq/impl/JoinTable.java @@ -178,6 +178,7 @@ abstract class JoinTable> extends AbstractJoinTable { static final Set EMULATE_JOIN_USING = SQLDialect.supportedBy(CUBRID, IGNITE); static final Set EMULATE_APPLY = SQLDialect.supportedBy(FIREBIRD, POSTGRES, TRINO, YUGABYTEDB); static final Set EMUlATE_SEMI_ANTI_JOIN = SQLDialect.supportedBy(CUBRID, DERBY, DUCKDB, FIREBIRD, H2, HSQLDB, IGNITE, MARIADB, MYSQL, POSTGRES, SQLITE, TRINO, YUGABYTEDB); + static final Set NO_SUPPORT_NESTED_JOIN = SQLDialect.supportedBy(CLICKHOUSE); final Table lhs; final Table rhs; diff --git a/jOOQ/src/main/java/org/jooq/impl/Names.java b/jOOQ/src/main/java/org/jooq/impl/Names.java index 47b5d3ea6b..ecf027e1ea 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Names.java +++ b/jOOQ/src/main/java/org/jooq/impl/Names.java @@ -73,6 +73,7 @@ final class Names { static final Name N_ARRAY_AGG = systemName("array_agg"); static final Name N_ARRAY_CONSTRUCT = systemName("array_construct"); static final Name N_ARRAY_CONSTRUCT_COMPACT = systemName("array_construct_compact"); + static final Name N_arrayUniq = systemName("arrayUniq"); static final Name N_BITCOUNT = systemName("bitcount"); static final Name N_bitCount = systemName("bitCount"); static final Name N_BITWISE_AND_AGG = systemName("bitwise_and_agg"); @@ -644,6 +645,9 @@ final class Names { static final Name N_XMLSERIALIZE = systemName("xmlserialize"); static final Name N_XMLSERIALIZE_CONTENT = systemName("xmlserialize_content"); static final Name N_XOR = systemName("xor"); + static final Name N_arrayConcat = systemName("arrayConcat"); + static final Name N_arrayPushBack = systemName("arrayPushBack"); + static final Name N_arrayPushFront = systemName("arrayPushFront"); static final Name N_bitAnd = systemName("bitAnd"); static final Name N_bitNot = systemName("bitNot"); static final Name N_bitOr = systemName("bitOr"); diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 233c7e95f6..a6094d30c9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -8738,16 +8738,18 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { return parseFieldAddDatePart(MINUTE); else if (parseFunctionNameIf("ADD_SECONDS")) return parseFieldAddDatePart(SECOND); - else if (parseFunctionNameIf("ARRAY_APPEND")) + else if (parseFunctionNameIf("ARRAY_APPEND", "arrayPushBack")) return parseFunctionArgs2((f1, f2) -> arrayAppend((Field) f1, (Field) f2)); - else if (parseFunctionNameIf("ARRAY_CAT", "ARRAY_CONCAT")) + else if (parseFunctionNameIf("ARRAY_CAT", "ARRAY_CONCAT", "arrayConcat")) return parseFunctionArgs2((f1, f2) -> arrayConcat(f1, f2)); - else if (parseFunctionNameIf("ARRAY_GET")) + else if (parseFunctionNameIf("ARRAY_GET", "arrayElement")) return parseFunctionArgs2((f1, f2) -> arrayGet(f1, f2)); else if (parseFunctionNameIf("ARRAY_OVERLAP", "ARRAYS_OVERLAP")) return parseFunctionArgs2((f1, f2) -> arrayOverlap((Field) f1, (Field) f2)); else if (parseFunctionNameIf("ARRAY_PREPEND")) return parseFunctionArgs2((f1, f2) -> arrayPrepend((Field) f1, (Field) f2)); + else if (parseFunctionNameIf("arrayPushFront")) + return parseFunctionArgs2((f1, f2) -> arrayPrepend((Field) f2, (Field) f1)); else if (parseFunctionNameIf("ARRAY_REMOVE")) return parseFunctionArgs2((f1, f2) -> arrayRemove((Field) f1, (Field) f2)); else if (parseFunctionNameIf("ARRAY_REPLACE")) diff --git a/jOOQ/src/main/java/org/jooq/impl/QOM.java b/jOOQ/src/main/java/org/jooq/impl/QOM.java index fe5670259e..ecd623cfe9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/QOM.java +++ b/jOOQ/src/main/java/org/jooq/impl/QOM.java @@ -1439,16 +1439,16 @@ public final class QOM { public /*sealed*/ interface ScalarSubquery extends - Field, - UOperator1>, ScalarSubquery> + org.jooq.Field, + UOperator1>, ScalarSubquery> /*permits ScalarSubquery*/ {} public /*sealed*/ interface RowSubquery extends - Row, - UOperator1, RowSubquery> + org.jooq.Row, + UOperator1, RowSubquery> {} public /*sealed*/ interface Neg diff --git a/jOOQ/src/main/java/org/jooq/impl/RowIsDistinctFrom.java b/jOOQ/src/main/java/org/jooq/impl/RowIsDistinctFrom.java index 166d81d3d6..21c60a99a1 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RowIsDistinctFrom.java +++ b/jOOQ/src/main/java/org/jooq/impl/RowIsDistinctFrom.java @@ -65,11 +65,17 @@ import static org.jooq.SQLDialect.SQLITE; import static org.jooq.SQLDialect.TRINO; // ... import static org.jooq.SQLDialect.YUGABYTEDB; +import static org.jooq.impl.DSL.array; import static org.jooq.impl.DSL.exists; +import static org.jooq.impl.DSL.function; +import static org.jooq.impl.DSL.inline; import static org.jooq.impl.DSL.notExists; import static org.jooq.impl.DSL.select; import static org.jooq.impl.Keywords.K_IS; import static org.jooq.impl.Keywords.K_NOT; +import static org.jooq.impl.Names.NQ_SELECT; +import static org.jooq.impl.Names.N_arrayUniq; +import static org.jooq.impl.SQLDataType.INTEGER; import static org.jooq.impl.SubqueryCharacteristics.PREDICAND; import static org.jooq.impl.Tools.visitSubquery; @@ -87,18 +93,19 @@ import org.jooq.impl.QOM.UNotYetImplemented; * @author Lukas Eder */ final class RowIsDistinctFrom extends AbstractCondition implements UNotYetImplemented { - private static final Set EMULATE_DISTINCT = SQLDialect.supportedBy(CLICKHOUSE, CUBRID, DERBY); + static final Set EMULATE_DISTINCT = SQLDialect.supportedBy(CUBRID, DERBY); + static final Set EMULATE_WITH_ARRAYS = SQLDialect.supportedBy(CLICKHOUSE); // An emulation may be required only for the version where a subquery is used // E.g. in HSQLDB: https://sourceforge.net/p/hsqldb/bugs/1579/ // Or in PostgreSQL: https://twitter.com/pg_xocolatl/status/1260344255035379714 - private static final Set EMULATE_DISTINCT_SELECT = SQLDialect.supportedBy(HSQLDB, POSTGRES, TRINO, YUGABYTEDB); - private static final Set SUPPORT_DISTINCT_WITH_ARROW = SQLDialect.supportedBy(MARIADB, MYSQL); + static final Set EMULATE_DISTINCT_SELECT = SQLDialect.supportedBy(HSQLDB, POSTGRES, TRINO, YUGABYTEDB); + static final Set SUPPORT_DISTINCT_WITH_ARROW = SQLDialect.supportedBy(MARIADB, MYSQL); - private final Row lhs; - private final Row rhsRow; - private final Select rhsSelect; - private final boolean not; + private final Row lhs; + private final Row rhsRow; + private final Select rhsSelect; + private final boolean not; RowIsDistinctFrom(Row lhs, Row rhs, boolean not) { this.lhs = lhs; @@ -147,6 +154,17 @@ final class RowIsDistinctFrom extends AbstractCondition implements UNotYetImplem ctx.sql(')'); } + else if (EMULATE_WITH_ARRAYS.contains(ctx.dialect())) { + ctx.visit( + function(N_arrayUniq, INTEGER, + array( + lhs, + rhsRow != null ? rhsRow : CustomField.of(NQ_SELECT, SQLDataType.RECORD, c -> visitSubquery(c, rhsSelect, PREDICAND)) + ) + ).eq(inline(not ? 1 : 2)) + ); + } + // SQLite knows the IS / IS NOT predicate else if (SQLITE == ctx.family()) { ctx.visit(lhs).sql(' ').visit(K_IS).sql(' '); diff --git a/jOOQ/src/main/java/org/jooq/impl/ScalarSubquery.java b/jOOQ/src/main/java/org/jooq/impl/ScalarSubquery.java index 1561129925..44311487ff 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ScalarSubquery.java +++ b/jOOQ/src/main/java/org/jooq/impl/ScalarSubquery.java @@ -38,6 +38,7 @@ package org.jooq.impl; +import static org.jooq.SQLDialect.CLICKHOUSE; // ... import static org.jooq.SQLDialect.HSQLDB; // ... @@ -63,6 +64,7 @@ import org.jooq.Select; @SuppressWarnings("unchecked") final class ScalarSubquery extends AbstractField implements QOM.ScalarSubquery { + static final Set NO_SUPPORT_CORRELATED_SUBQUERY = SQLDialect.supportedBy(CLICKHOUSE); static final Set NO_SUPPORT_WITH_IN_SCALAR_SUBQUERY = SQLDialect.supportedBy(HSQLDB); final Select query; final boolean predicandSubquery;