[jOOQ/jOOQ#12134] Support deserialising binary data in MULTISET
emulations - Added SQL/JSON emulation support for H2, SQLite, MySQL, MariaDB - Added SQL/XML emulation support for Db2, Oracle
This commit is contained in:
parent
fd05a97f92
commit
ce4115dac9
@ -44,6 +44,7 @@ import static org.jooq.SQLDialect.MYSQL;
|
||||
import static org.jooq.impl.AbstractRowAsField.forceMultisetContent;
|
||||
import static org.jooq.impl.DSL.NULL;
|
||||
import static org.jooq.impl.DSL.case_;
|
||||
import static org.jooq.impl.DSL.castNull;
|
||||
import static org.jooq.impl.DSL.coalesce;
|
||||
import static org.jooq.impl.DSL.field;
|
||||
import static org.jooq.impl.DSL.function;
|
||||
@ -51,17 +52,22 @@ import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.inlined;
|
||||
import static org.jooq.impl.DSL.nvl;
|
||||
import static org.jooq.impl.DSL.toChar;
|
||||
import static org.jooq.impl.DSL.when;
|
||||
import static org.jooq.impl.DSL.xmlelement;
|
||||
import static org.jooq.impl.DSL.xmlquery;
|
||||
import static org.jooq.impl.Keywords.K_FORMAT;
|
||||
import static org.jooq.impl.Keywords.K_JSON;
|
||||
import static org.jooq.impl.Keywords.K_KEY;
|
||||
import static org.jooq.impl.Keywords.K_VALUE;
|
||||
import static org.jooq.impl.Names.N_HEX;
|
||||
import static org.jooq.impl.Names.N_JSON;
|
||||
import static org.jooq.impl.Names.N_JSON_EXTRACT;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE;
|
||||
import static org.jooq.impl.Names.N_JSON_MERGE_PRESERVE;
|
||||
import static org.jooq.impl.Names.N_JSON_QUERY;
|
||||
import static org.jooq.impl.Names.N_RAWTOHEX;
|
||||
import static org.jooq.impl.RowAsField.NO_NATIVE_SUPPORT;
|
||||
import static org.jooq.impl.SQLDataType.BIT;
|
||||
import static org.jooq.impl.SQLDataType.*;
|
||||
import static org.jooq.impl.SQLDataType.BOOLEAN;
|
||||
import static org.jooq.impl.SQLDataType.VARCHAR;
|
||||
import static org.jooq.impl.Tools.combine;
|
||||
@ -192,6 +198,10 @@ final class JSONEntryImpl<T> extends AbstractQueryPart implements JSONEntry<T>,
|
||||
else if (type.isTemporal())
|
||||
return field.cast(VARCHAR);
|
||||
|
||||
// [#12134] No auto conversion available yet
|
||||
else if (type.isBinary())
|
||||
return function(N_RAWTOHEX, VARCHAR, field);
|
||||
|
||||
break;
|
||||
|
||||
// [#11025] These don't have boolean support outside of JSON
|
||||
@ -210,6 +220,8 @@ final class JSONEntryImpl<T> extends AbstractQueryPart implements JSONEntry<T>,
|
||||
case SQLITE:
|
||||
if (isType(type, Boolean.class))
|
||||
return function(N_JSON, SQLDataType.JSON, case_((Field<Boolean>) field).when(inline(true), inline("true")).when(inline(false), inline("false")));
|
||||
else if (type.isBinary())
|
||||
return when(field.isNotNull(), function(N_HEX, VARCHAR, field));
|
||||
|
||||
break;
|
||||
|
||||
@ -241,6 +253,12 @@ final class JSONEntryImpl<T> extends AbstractQueryPart implements JSONEntry<T>,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -39,6 +39,15 @@ package org.jooq.impl;
|
||||
|
||||
import static java.lang.Integer.parseInt;
|
||||
import static java.util.Arrays.asList;
|
||||
// ...
|
||||
// ...
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.H2;
|
||||
import static org.jooq.SQLDialect.MARIADB;
|
||||
import static org.jooq.SQLDialect.POSTGRES;
|
||||
// ...
|
||||
import static org.jooq.SQLDialect.SQLITE;
|
||||
import static org.jooq.SQLDialect.YUGABYTEDB;
|
||||
import static org.jooq.impl.DSL.field;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DefaultDataType.getDataType;
|
||||
@ -56,12 +65,14 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Fields;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.tools.json.ContainerFactory;
|
||||
import org.jooq.tools.json.JSONParser;
|
||||
|
||||
@ -226,6 +237,9 @@ final class JSONReader<R extends Record> {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static final Set<SQLDialect> ENCODE_BINARY_AS_HEX = SQLDialect.supportedBy(H2, POSTGRES, SQLITE, YUGABYTEDB);
|
||||
private static final Set<SQLDialect> ENCODE_BINARY_AS_TEXT = SQLDialect.supportedBy(MARIADB);
|
||||
|
||||
private static final List<Object> patchRecord(DSLContext ctx, boolean multiset, Fields result, List<Object> record) {
|
||||
for (int i = 0; i < result.fields().length; i++) {
|
||||
Field<?> field = result.field(i);
|
||||
@ -235,9 +249,20 @@ final class JSONReader<R extends Record> {
|
||||
if (field.getType() == byte[].class && record.get(i) instanceof String) {
|
||||
String s = (String) record.get(i);
|
||||
|
||||
// [#12134] SQL/JSON MULTISET and other forms of serialisation may produce hex encoded binary data
|
||||
if (s.startsWith("\\x"))
|
||||
record.set(i, convertHexToBytes(s, 1, Integer.MAX_VALUE));
|
||||
// [#12134] PostgreSQL encodes binary data as hex
|
||||
if (ENCODE_BINARY_AS_HEX.contains(ctx.dialect()))
|
||||
if (s.startsWith("\\x"))
|
||||
record.set(i, convertHexToBytes(s, 1, Integer.MAX_VALUE));
|
||||
else
|
||||
record.set(i, convertHexToBytes(s));
|
||||
|
||||
// [#12134] MariaDB encodes binary data as text (?)
|
||||
else if (ENCODE_BINARY_AS_TEXT.contains(ctx.dialect()))
|
||||
record.set(i, s);
|
||||
|
||||
// [#12134] MySQL encodes binary data as prefixed base64
|
||||
else if (s.startsWith("base64:type15:"))
|
||||
record.set(i, Base64.getDecoder().decode(s.substring(14)));
|
||||
else
|
||||
record.set(i, Base64.getDecoder().decode(s));
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
// ...
|
||||
import static org.jooq.impl.DSL.field;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DefaultDataType.getDataType;
|
||||
@ -83,6 +84,7 @@ final class XMLHandler<R extends Record> extends DefaultHandler {
|
||||
private State<R> s;
|
||||
|
||||
private static class State<R extends Record> {
|
||||
final DSLContext ctx;
|
||||
AbstractRow<R> row;
|
||||
final Class<? extends R> recordType;
|
||||
boolean inResult;
|
||||
@ -95,7 +97,8 @@ final class XMLHandler<R extends Record> extends DefaultHandler {
|
||||
int column;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
State(AbstractRow<R> row, Class<? extends R> recordType) {
|
||||
State(DSLContext ctx, AbstractRow<R> row, Class<? extends R> recordType) {
|
||||
this.ctx = ctx;
|
||||
this.row = row;
|
||||
this.recordType = recordType != null ? recordType : (Class<? extends R>) Record.class;
|
||||
this.fields = new ArrayList<>();
|
||||
@ -108,6 +111,11 @@ final class XMLHandler<R extends Record> extends DefaultHandler {
|
||||
for (int i = 0; i < fields.size(); i++) {
|
||||
if (fields.get(i).getDataType().isBinary()) {
|
||||
if (values.get(i) instanceof String) { String s = (String) values.get(i);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
values.set(i, Base64.getDecoder().decode(s));
|
||||
}
|
||||
}
|
||||
@ -121,7 +129,7 @@ final class XMLHandler<R extends Record> extends DefaultHandler {
|
||||
XMLHandler(DSLContext ctx, AbstractRow<R> row, Class<? extends R> recordType) {
|
||||
this.ctx = ctx;
|
||||
this.states = new ArrayDeque<>();
|
||||
this.s = new State<>(row, recordType);
|
||||
this.s = new State<>(ctx, row, recordType);
|
||||
}
|
||||
|
||||
Result<R> read(String string) {
|
||||
@ -180,7 +188,7 @@ final class XMLHandler<R extends Record> extends DefaultHandler {
|
||||
|
||||
if (f.getDataType().isMultiset()) {
|
||||
states.push(s);
|
||||
s = new State<>((AbstractRow<R>) f.getDataType().getRow(), (Class<R>) f.getDataType().getRecordType());
|
||||
s = new State<>(ctx, (AbstractRow<R>) f.getDataType().getRow(), (Class<R>) f.getDataType().getRecordType());
|
||||
s.inResult = true;
|
||||
}
|
||||
else
|
||||
@ -207,7 +215,7 @@ final class XMLHandler<R extends Record> extends DefaultHandler {
|
||||
|
||||
if (f.getDataType().isRecord()) {
|
||||
states.push(s);
|
||||
s = new State<>((AbstractRow<R>) f.getDataType().getRow(), (Class<R>) f.getDataType().getRecordType());
|
||||
s = new State<>(ctx, (AbstractRow<R>) f.getDataType().getRow(), (Class<R>) f.getDataType().getRecordType());
|
||||
s.inResult = true;
|
||||
}
|
||||
else
|
||||
|
||||
Loading…
Reference in New Issue
Block a user