[jOOQ/jOOQ#3884] Support nesting MULTISET
This includes: - [jOOQ/jOOQ#10170] Added some missing FOR XML API - [jOOQ/jOOQ#12012] Internal API added - [jOOQ/jOOQ#12020] Result.formatXML() does not correctly format nested records
This commit is contained in:
parent
2648781a47
commit
ed6d56b3c9
@ -73,6 +73,7 @@ public final class JSONFormat {
|
||||
|
||||
final boolean format;
|
||||
final String newline;
|
||||
final int globalIndent;
|
||||
final int indent;
|
||||
final String[] indented;
|
||||
final boolean header;
|
||||
@ -84,6 +85,7 @@ public final class JSONFormat {
|
||||
this(
|
||||
false,
|
||||
"\n",
|
||||
0,
|
||||
2,
|
||||
null,
|
||||
true,
|
||||
@ -96,6 +98,7 @@ public final class JSONFormat {
|
||||
private JSONFormat(
|
||||
boolean format,
|
||||
String newline,
|
||||
int globalIndent,
|
||||
int indent,
|
||||
String[] indented,
|
||||
boolean header,
|
||||
@ -105,6 +108,7 @@ public final class JSONFormat {
|
||||
) {
|
||||
this.format = format;
|
||||
this.newline = newline;
|
||||
this.globalIndent = globalIndent;
|
||||
this.indent = indent;
|
||||
this.indented = indented != null ? indented : new String[] {
|
||||
"",
|
||||
@ -125,6 +129,7 @@ public final class JSONFormat {
|
||||
return new JSONFormat(
|
||||
newFormat,
|
||||
newline,
|
||||
globalIndent,
|
||||
indent,
|
||||
null,
|
||||
header,
|
||||
@ -148,6 +153,7 @@ public final class JSONFormat {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newNewline,
|
||||
globalIndent,
|
||||
indent,
|
||||
indented,
|
||||
header,
|
||||
@ -165,12 +171,37 @@ public final class JSONFormat {
|
||||
}
|
||||
|
||||
/**
|
||||
* The new indentation value, defaulting to <code>2</code>.
|
||||
* The new global indentation size applied on all levels, defaulting to <code>0</code>.
|
||||
*/
|
||||
public final JSONFormat globalIndent(int newGlobalIndent) {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newline,
|
||||
newGlobalIndent,
|
||||
indent,
|
||||
null,
|
||||
header,
|
||||
recordFormat,
|
||||
wrapSingleColumnRecords,
|
||||
quoteNested
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The global indentation applied on all levels.
|
||||
*/
|
||||
public final int globalIndent() {
|
||||
return globalIndent;
|
||||
}
|
||||
|
||||
/**
|
||||
* The new indentation size per level value, defaulting to <code>2</code>.
|
||||
*/
|
||||
public final JSONFormat indent(int newIndent) {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newline,
|
||||
globalIndent,
|
||||
newIndent,
|
||||
null,
|
||||
header,
|
||||
@ -181,7 +212,7 @@ public final class JSONFormat {
|
||||
}
|
||||
|
||||
/**
|
||||
* The indentation.
|
||||
* The indentation size per level.
|
||||
*/
|
||||
public final int indent() {
|
||||
return indent;
|
||||
@ -191,10 +222,12 @@ public final class JSONFormat {
|
||||
* Convenience method to get an indentation string at a given level.
|
||||
*/
|
||||
public final String indentString(int level) {
|
||||
if (level < indented.length)
|
||||
return indented[level];
|
||||
int i = level + globalIndent / indent;
|
||||
|
||||
if (i < indented.length)
|
||||
return indented[i];
|
||||
else if (format)
|
||||
return rightPad("", indent * level);
|
||||
return rightPad("", globalIndent + indent * level);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
@ -207,6 +240,7 @@ public final class JSONFormat {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newline,
|
||||
globalIndent,
|
||||
indent,
|
||||
indented,
|
||||
newHeader,
|
||||
@ -232,6 +266,7 @@ public final class JSONFormat {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newline,
|
||||
globalIndent,
|
||||
indent,
|
||||
indented,
|
||||
header,
|
||||
@ -256,6 +291,7 @@ public final class JSONFormat {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newline,
|
||||
globalIndent,
|
||||
indent,
|
||||
indented,
|
||||
header,
|
||||
@ -280,6 +316,7 @@ public final class JSONFormat {
|
||||
return new JSONFormat(
|
||||
format,
|
||||
newline,
|
||||
globalIndent,
|
||||
indent,
|
||||
indented,
|
||||
header,
|
||||
|
||||
@ -121,6 +121,14 @@ import static org.jooq.SQLDialect.POSTGRES;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -101,6 +101,22 @@ import org.jetbrains.annotations.*;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -106,6 +106,22 @@ import static org.jooq.SQLDialect.POSTGRES;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1305,6 +1305,27 @@ public interface SelectQuery<R extends Record> extends Select<R>, ConditionProvi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -461,6 +461,7 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
try {
|
||||
String separator;
|
||||
int recordLevel = format.header() ? 2 : 1;
|
||||
boolean hasRecords = false;
|
||||
|
||||
if (format.header()) {
|
||||
if (format.format())
|
||||
@ -552,6 +553,7 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
switch (format.recordFormat()) {
|
||||
case ARRAY:
|
||||
for (Record record : this) {
|
||||
hasRecords = true;
|
||||
writer.append(separator);
|
||||
|
||||
if (format.format())
|
||||
@ -564,6 +566,7 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
break;
|
||||
case OBJECT:
|
||||
for (Record record : this) {
|
||||
hasRecords = true;
|
||||
writer.append(separator);
|
||||
|
||||
if (format.format())
|
||||
@ -578,17 +581,23 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
throw new IllegalArgumentException("Format not supported: " + format);
|
||||
}
|
||||
|
||||
if (format.format()) {
|
||||
if (format.format() && hasRecords) {
|
||||
writer.append(format.newline());
|
||||
|
||||
if (format.header())
|
||||
writer.append(format.indentString(1));
|
||||
else
|
||||
writer.append(format.indentString(0));
|
||||
}
|
||||
|
||||
writer.append(']');
|
||||
|
||||
if (format.header())
|
||||
writer.append(format.newline()).append('}');
|
||||
if (format.header()) {
|
||||
if (format.format())
|
||||
writer.append(format.newline()).append(format.indentString(0));
|
||||
|
||||
writer.append('}');
|
||||
}
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
@ -647,17 +656,20 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
int size = fields.size();
|
||||
boolean wrapRecords = format.wrapSingleColumnRecords() || size > 1;
|
||||
|
||||
if (format.format())
|
||||
writer.append(format.indentString(recordLevel));
|
||||
|
||||
if (wrapRecords)
|
||||
if (format.format())
|
||||
writer.append(format.indentString(recordLevel)).append('{');
|
||||
else
|
||||
writer.append('{');
|
||||
writer.append('{');
|
||||
|
||||
for (int index = 0; index < size; index++) {
|
||||
writer.append(separator);
|
||||
|
||||
if (format.format())
|
||||
writer.append(format.newline()).append(format.indentString(recordLevel + 1));
|
||||
if (size > 1)
|
||||
writer.append(format.newline()).append(format.indentString(recordLevel + 1));
|
||||
else if (format.wrapSingleColumnRecords())
|
||||
writer.append(' ');
|
||||
|
||||
if (wrapRecords) {
|
||||
JSONValue.writeJSONString(record.field(index).getName(), writer);
|
||||
@ -667,12 +679,16 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
writer.append(' ');
|
||||
}
|
||||
|
||||
formatJSON0(record.get(index), writer, format);
|
||||
formatJSON0(record.get(index), writer, format.globalIndent(format.globalIndent() + format.indent() * (recordLevel + 1)));
|
||||
|
||||
if (format.format() && format.wrapSingleColumnRecords() && size == 1)
|
||||
writer.append(' ');
|
||||
|
||||
separator = ",";
|
||||
}
|
||||
|
||||
if (wrapRecords)
|
||||
if (format.format())
|
||||
if (format.format() && size > 1)
|
||||
writer.append(format.newline()).append(format.indentString(recordLevel)).append('}');
|
||||
else
|
||||
writer.append('}');
|
||||
@ -686,26 +702,34 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
Writer writer
|
||||
) throws java.io.IOException {
|
||||
String separator = "";
|
||||
|
||||
int size = fields.size();
|
||||
if (format.wrapSingleColumnRecords() || size > 1)
|
||||
if (format.format())
|
||||
writer.append(format.indentString(recordLevel)).append('[');
|
||||
else
|
||||
writer.append('[');
|
||||
boolean wrapRecords = format.wrapSingleColumnRecords() || size > 1;
|
||||
|
||||
if (format.format())
|
||||
writer.append(format.indentString(recordLevel));
|
||||
|
||||
if (wrapRecords)
|
||||
writer.append('[');
|
||||
|
||||
for (int index = 0; index < size; index++) {
|
||||
writer.append(separator);
|
||||
|
||||
if (format.format())
|
||||
writer.append(format.newline()).append(format.indentString(recordLevel + 1));
|
||||
if (size > 1)
|
||||
writer.append(format.newline()).append(format.indentString(recordLevel + 1));
|
||||
else if (format.wrapSingleColumnRecords())
|
||||
writer.append(' ');
|
||||
|
||||
formatJSON0(record.get(index), writer, format.globalIndent(format.globalIndent() + format.indent() * (recordLevel + 1)));
|
||||
|
||||
if (format.format() && format.wrapSingleColumnRecords() && size == 1)
|
||||
writer.append(' ');
|
||||
|
||||
formatJSON0(record.get(index), writer, format);
|
||||
separator = ",";
|
||||
}
|
||||
|
||||
if (format.wrapSingleColumnRecords() || size > 1)
|
||||
if (format.format())
|
||||
if (wrapRecords)
|
||||
if (format.format() && size > 1)
|
||||
writer.append(format.newline()).append(format.indentString(recordLevel)).append(']');
|
||||
else
|
||||
writer.append(']');
|
||||
@ -771,7 +795,7 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
if (format.header())
|
||||
writer.append(newline).append(format.indentString(1)).append("</records>");
|
||||
|
||||
writer.append(newline).append("</result>");
|
||||
writer.append(newline).append(format.indentString(0)).append("</result>");
|
||||
writer.flush();
|
||||
}
|
||||
catch (java.io.IOException e) {
|
||||
@ -818,7 +842,7 @@ abstract class AbstractResult<R extends Record> extends AbstractFormattable impl
|
||||
|
||||
if (value instanceof Formattable) {
|
||||
writer.append(newline).append(format.indentString(recordLevel + 2));
|
||||
((Formattable) value).formatXML(writer, format.globalIndent(format.indent() * (recordLevel + 2)));
|
||||
((Formattable) value).formatXML(writer, format.globalIndent(format.globalIndent() + format.indent() * (recordLevel + 2)));
|
||||
writer.append(newline).append(format.indentString(recordLevel + 1));
|
||||
}
|
||||
else if (value instanceof XML && !format.quoteNested())
|
||||
|
||||
@ -93,6 +93,19 @@ package org.jooq.impl;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -452,6 +452,7 @@ final class Keywords {
|
||||
static final Keyword K_XML = keyword("xml");
|
||||
static final Keyword K_XMLEXISTS = keyword("xmlexists");
|
||||
static final Keyword K_XMLTABLE = keyword("xmltable");
|
||||
static final Keyword K_XSINIL = keyword("xsinil");
|
||||
static final Keyword K_YEAR = keyword("year");
|
||||
static final Keyword K_YEAR_MONTH = keyword("year_month");
|
||||
static final Keyword K_YEAR_TO_DAY = keyword("year to day");
|
||||
|
||||
@ -77,7 +77,6 @@ final class Multiset<R extends Record> extends AbstractField<Result<R>> {
|
||||
this.select = select;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final void accept(Context<?> ctx) {
|
||||
switch (emulateMultiset(ctx.configuration())) {
|
||||
|
||||
158
jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java
Normal file
158
jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Other licenses:
|
||||
* -----------------------------------------------------------------------------
|
||||
* Commercial licenses for this work are available. These replace the above
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: http://www.jooq.org/licenses
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static org.jooq.impl.Tools.CTX;
|
||||
import static org.jooq.impl.Tools.newRecord;
|
||||
import static org.jooq.impl.Tools.recordType;
|
||||
import static org.jooq.impl.Tools.row0;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jooq.CharacterSet;
|
||||
import org.jooq.Collation;
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Nullability;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Result;
|
||||
import org.jooq.Row;
|
||||
import org.jooq.Select;
|
||||
import org.jooq.impl.AbstractRecord.TransferRecordState;
|
||||
|
||||
/**
|
||||
* A wrapper for anonymous multiset data types.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class MultisetDataType<R extends Record> extends DefaultDataType<Result<R>> {
|
||||
|
||||
final AbstractRow<R> row;
|
||||
final Class<? extends R> recordType;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public MultisetDataType(AbstractRow<R> row, Class<? extends R> recordType) {
|
||||
// [#11829] TODO: Implement this correctly for ArrayRecord
|
||||
super(null, (Class) Result.class, "multiset", "multiset");
|
||||
|
||||
this.row = row;
|
||||
this.recordType = recordType;
|
||||
}
|
||||
|
||||
/**
|
||||
* [#3225] Performant constructor for creating derived types.
|
||||
*/
|
||||
MultisetDataType(
|
||||
DefaultDataType<Result<R>> t,
|
||||
AbstractRow<R> row,
|
||||
Class<? extends R> recordType,
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
Integer length,
|
||||
Nullability nullability,
|
||||
Collation collation,
|
||||
CharacterSet characterSet,
|
||||
boolean identity,
|
||||
Field<Result<R>> defaultValue
|
||||
) {
|
||||
super(t, precision, scale, length, nullability, collation, characterSet, identity, defaultValue);
|
||||
|
||||
this.row = row;
|
||||
this.recordType = recordType;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
DefaultDataType<Result<R>> construct(
|
||||
Integer newPrecision,
|
||||
Integer newScale,
|
||||
Integer newLength,
|
||||
Nullability
|
||||
newNullability,
|
||||
Collation newCollation,
|
||||
CharacterSet newCharacterSet,
|
||||
boolean newIdentity,
|
||||
Field<Result<R>> newDefaultValue
|
||||
) {
|
||||
return new MultisetDataType<>(
|
||||
this,
|
||||
row,
|
||||
recordType,
|
||||
newPrecision,
|
||||
newScale,
|
||||
newLength,
|
||||
newNullability,
|
||||
newCollation,
|
||||
newCharacterSet,
|
||||
newIdentity,
|
||||
(Field) newDefaultValue
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Row getRow() {
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result<R> convert(Object object) {
|
||||
|
||||
// [#3884] TODO: Move this logic into JSONReader to make it more generally useful
|
||||
if (object instanceof List) {
|
||||
ResultImpl<R> result = new ResultImpl<>(CTX.configuration(), row);
|
||||
|
||||
for (Object record : (List) object)
|
||||
result.add(newRecord(true, recordType, row, CTX.configuration())
|
||||
.operate(r -> {
|
||||
|
||||
// [#12014] TODO: Fix this and remove workaround
|
||||
if (record instanceof Record)
|
||||
((AbstractRecord) r).from((Record) record);
|
||||
else
|
||||
r.from(record);
|
||||
|
||||
return r;
|
||||
}));
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (object == null)
|
||||
return new ResultImpl<>(CTX.configuration(), row);
|
||||
else
|
||||
return super.convert(object);
|
||||
}
|
||||
}
|
||||
@ -1017,6 +1017,13 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1947,6 +1947,36 @@ implements
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1524,6 +1524,13 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -3575,6 +3582,21 @@ final class SelectQueryImpl<R extends Record> extends AbstractResultQuery<R> imp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user