[jOOQ/jOOQ#15732] Fix MULTISET equality semantics

This commit is contained in:
Lukas Eder 2024-03-27 17:34:18 +01:00
parent 6bd3d9e110
commit 3b6045ff43
5 changed files with 58 additions and 67 deletions

View File

@ -178,6 +178,7 @@ final class ArrayDataType<T> extends DefaultDataType<T[]> {
switch (configuration.family()) {
case DUCKDB:
case POSTGRES:
case YUGABYTEDB:
return dataType + "[]";

View File

@ -296,6 +296,7 @@ import org.jooq.tools.jdbc.JDBCUtils;
import org.jooq.tools.jdbc.MockArray;
import org.jooq.tools.jdbc.MockResultSet;
import org.jooq.tools.json.JSONArray;
import org.jooq.tools.json.JSONValue;
import org.jooq.types.DayToSecond;
import org.jooq.types.UByte;
import org.jooq.types.UInteger;
@ -1420,7 +1421,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
// [#15732] Use JSON as a workaround to bind array types for now.
case DUCKDB: {
ctx.statement().setString(ctx.index(), new JSONArray(asList(value)).toString());
ctx.statement().setString(ctx.index(), JSONValue.toJSONString(value));
break;
}

View File

@ -45,6 +45,7 @@ import static org.jooq.SQLDialect.MYSQL;
import static org.jooq.SQLDialect.*;
import static org.jooq.SQLDialect.POSTGRES;
import static org.jooq.SQLDialect.YUGABYTEDB;
import static org.jooq.impl.DSL.arrayAgg;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.function;
import static org.jooq.impl.DSL.inline;
@ -97,6 +98,7 @@ import java.util.Set;
import java.util.function.Function;
import org.jooq.AggregateFilterStep;
import org.jooq.ArrayAggOrderByStep;
import org.jooq.Context;
import org.jooq.DataType;
import org.jooq.Field;
@ -119,6 +121,8 @@ import org.jooq.Result;
import org.jooq.SQLDialect;
import org.jooq.Scope;
import org.jooq.Select;
import org.jooq.SelectJoinStep;
import org.jooq.SelectOrderByStep;
import org.jooq.Spatial;
import org.jooq.Table;
import org.jooq.TableLike;
@ -126,6 +130,8 @@ import org.jooq.TableLike;
import org.jooq.XML;
import org.jooq.XMLAggOrderByStep;
import org.jetbrains.annotations.NotNull;
/**
* @author Lukas Eder
*/
@ -394,9 +400,14 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
case NATIVE:
switch (ctx.family()) {
case DUCKDB:
visitSubquery(ctx.visit(K_ARRAY), select(DSL.field(N_T)).from(select.asTable(N_T)));
case DUCKDB: {
SelectOrderByStep<?> s = select(DSL.field(N_T)).from(select.asTable(N_T));
visitSubquery(ctx.visit(K_ARRAY), multisetCondition
? s.orderBy(DSL.field(N_T))
: s
);
break;
}
default:
visitSubquery(ctx.visit(K_MULTISET), select);
@ -603,6 +614,14 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> implemen
);
}
static final ArrayAggOrderByStep<?> arrayAggEmulation(Fields fields, boolean agg) {
return arrayAgg(
new RowAsField<>(row(
map(fields.fields(), (f, i) -> agg ? f : DSL.field(fieldName(i), f.getDataType()))
))
);
}
// -------------------------------------------------------------------------
// XXX: Query Object Model
// -------------------------------------------------------------------------

View File

@ -39,31 +39,26 @@ package org.jooq.impl;
import static java.lang.Boolean.TRUE;
// ...
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.xmlelement;
import static org.jooq.impl.DSL.xmlserializeContent;
import static org.jooq.impl.Multiset.NO_SUPPORT_JSONB_COMPARE;
import static org.jooq.impl.Multiset.NO_SUPPORT_JSON_COMPARE;
import static org.jooq.impl.Multiset.NO_SUPPORT_XML_COMPARE;
import static org.jooq.impl.Multiset.arrayAggEmulation;
import static org.jooq.impl.Multiset.jsonArrayaggEmulation;
import static org.jooq.impl.Multiset.jsonbArrayaggEmulation;
import static org.jooq.impl.Multiset.nResult;
import static org.jooq.impl.Multiset.returningClob;
import static org.jooq.impl.Multiset.xmlaggEmulation;
import static org.jooq.impl.Names.N_ARRAY_AGG;
import static org.jooq.impl.Names.N_MULTISET_AGG;
import static org.jooq.impl.Names.N_RESULT;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.emulateMultiset;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONDITION;
import static org.jooq.impl.Tools.BooleanDataKey.DATA_MULTISET_CONTENT;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import org.jooq.ArrayAggOrderByStep;
import org.jooq.Context;
import org.jooq.Field;
import org.jooq.Function1;
import org.jooq.JSON;
import org.jooq.JSONArrayAggOrderByStep;
import org.jooq.JSONB;
@ -164,23 +159,16 @@ final class MultisetAgg<R extends Record> extends AbstractAggregateFunction<Resu
break;
}
case NATIVE:
switch (ctx.family()) {
case DUCKDB:
ctx.visit(N_ARRAY_AGG);
break;
default:
ctx.visit(N_MULTISET_AGG);
break;
}
case NATIVE: {
ArrayAggOrderByStep<?> order = arrayAggEmulation(row, true);
ctx.visit(multisetCondition
? fo(order.orderBy(row.fields()))
: ofo((AbstractAggregateFunction<?>) order)
);
ctx.sql('(');
acceptArguments1(ctx, new QueryPartListView<>(new RowAsField<>(DSL.row(arguments))));
acceptOrderBy(ctx);
ctx.sql(')');
acceptFilterClause(ctx);
acceptOverClause(ctx);
break;
}
}
}

View File

@ -22,7 +22,9 @@ package org.jooq.tools.json;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -43,26 +45,28 @@ public class JSONValue {
return;
}
if (value instanceof String) {
if (value instanceof String s) {
out.write('\"');
out.write(escape((String) value));
out.write(escape(s));
out.write('\"');
return;
}
if (value instanceof Double) {
if (((Double) value).isInfinite() || ((Double) value).isNaN())
if (value instanceof Double d) {
if (d.isInfinite() || d.isNaN())
out.write("null");
else
out.write(value.toString());
return;
}
if (value instanceof Float) {
if (((Float) value).isInfinite() || ((Float) value).isNaN())
if (value instanceof Float f) {
if (f.isInfinite() || f.isNaN())
out.write("null");
else
out.write(value.toString());
return;
}
@ -76,13 +80,18 @@ public class JSONValue {
return;
}
if (value instanceof Map) {
JSONObject.writeJSONString((Map<?, ?>) value, out);
if (value instanceof Map<?, ?> m) {
JSONObject.writeJSONString(m, out);
return;
}
if (value instanceof List) {
JSONArray.writeJSONString((List<?>) value, out);
if (value instanceof List<?> l) {
JSONArray.writeJSONString(l, out);
return;
}
if (value instanceof Object[] a) {
JSONArray.writeJSONString(Arrays.asList(a), out);
return;
}
@ -102,41 +111,14 @@ public class JSONValue {
* number.
*/
public static String toJSONString(Object value) {
if (value == null)
return "null";
Writer w = new StringWriter();
if (value instanceof String)
return "\"" + escape((String) value) + "\"";
if (value instanceof Double) {
if (((Double) value).isInfinite() || ((Double) value).isNaN())
return "null";
else
return value.toString();
try {
writeJSONString(value, w);
}
catch (IOException ignore) {}
if (value instanceof Float) {
if (((Float) value).isInfinite() || ((Float) value).isNaN())
return "null";
else
return value.toString();
}
if (value instanceof Number)
return value.toString();
if (value instanceof Boolean)
return value.toString();
if (value instanceof Map)
return JSONObject.toJSONString((Map<?, ?>) value);
if (value instanceof List)
return JSONArray.toJSONString((List<?>) value);
// Patched original according to issue 27 of JSON-simple
// http://code.google.com/p/json-simple/issues/detail?id=27
return "\"" + escape(value.toString()) + "\"";
return w.toString();
}
/**