diff --git a/jOOQ/src/main/java/org/jooq/XMLFormat.java b/jOOQ/src/main/java/org/jooq/XMLFormat.java
index fad3553f78..1806c24374 100644
--- a/jOOQ/src/main/java/org/jooq/XMLFormat.java
+++ b/jOOQ/src/main/java/org/jooq/XMLFormat.java
@@ -546,7 +546,9 @@ public final class XMLFormat {
ABSENT_ELEMENT,
/**
- * A null value is represented by a xsi:nil="true" attribute.
+ * A null value is represented by a
+ * xsi:nil="true" attribute if {@link XMLFormat#xmlns()} is
+ * set, or nil="true", if it is not set.
*/
XSI_NIL
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractResult.java b/jOOQ/src/main/java/org/jooq/impl/AbstractResult.java
index a4d7853d21..07acd72d80 100644
--- a/jOOQ/src/main/java/org/jooq/impl/AbstractResult.java
+++ b/jOOQ/src/main/java/org/jooq/impl/AbstractResult.java
@@ -123,7 +123,8 @@ import org.xml.sax.helpers.DefaultHandler;
*/
abstract class AbstractResult extends AbstractFormattable implements FieldsTrait, Iterable {
- final AbstractRow fields;
+ private static final String XSI_SCHEMA = "http://www.w3.org/2001/XMLSchema-instance";
+ final AbstractRow fields;
AbstractResult(Configuration configuration, AbstractRow row) {
super(configuration);
@@ -804,7 +805,11 @@ abstract class AbstractResult extends AbstractFormattable impl
if (format.xmlns()) {
format = format.xmlns(false);
writer.append(" xmlns=\"" + Constants.NS_EXPORT + "\"");
+
+ if (format.nullFormat() == XSI_NIL)
+ writer.append(" xsi:xmlns=\"" + XSI_SCHEMA + "\"");
}
+
writer.append(">");
if (format.header()) {
@@ -878,6 +883,9 @@ abstract class AbstractResult extends AbstractFormattable impl
if (format.xmlns()) {
format = format.xmlns(false);
writer.append(" xmlns=\"" + Constants.NS_EXPORT + "\"");
+
+ if (format.nullFormat() == XSI_NIL)
+ writer.append(" xsi:xmlns=\"" + XSI_SCHEMA + "\"");
}
if (record == null) {
@@ -906,7 +914,7 @@ abstract class AbstractResult extends AbstractFormattable impl
if (value == null) {
if (format.nullFormat() == XSI_NIL)
- writer.append(" xsi:nil=\"true\"");
+ writer.append(" ").append(nil(format)).append("=\"true\"");
writer.append("/>");
}
@@ -943,7 +951,7 @@ abstract class AbstractResult extends AbstractFormattable impl
if (o == null) {
if (format.nullFormat() == XSI_NIL)
- writer.append(" xsi:nil=\"true\"");
+ writer.append(" ").append(nil(format)).append("=\"true\"");
writer.append("/>");
}
@@ -1233,11 +1241,12 @@ abstract class AbstractResult extends AbstractFormattable impl
Element eResult = document.createElement("result");
- if (format.xmlns())
+ if (format.xmlns()) {
eResult.setAttribute("xmlns", Constants.NS_EXPORT);
- if (format.nullFormat() == XSI_NIL)
- eResult.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ if (format.nullFormat() == XSI_NIL)
+ eResult.setAttribute("xmlns:xsi", XSI_SCHEMA);
+ }
document.appendChild(eResult);
@@ -1318,7 +1327,7 @@ abstract class AbstractResult extends AbstractFormattable impl
) {
if (value == null) {
if (format.nullFormat() == XSI_NIL)
- eParent.setAttribute("xsi:nil", "true");
+ eParent.setAttribute(nil(format), "true");
}
else if (value instanceof Formattable f) {
Document d = f.intoXML(format);
@@ -1338,7 +1347,7 @@ abstract class AbstractResult extends AbstractFormattable impl
if (o == null) {
if (format.nullFormat() == XSI_NIL)
- eElement.setAttribute("xsi:nil", "true");
+ eElement.setAttribute(nil(format), "true");
}
else
intoXMLContent(format, builder, document, o, eElement);
@@ -1357,7 +1366,11 @@ abstract class AbstractResult extends AbstractFormattable impl
eParent.setTextContent(format0(value, false, false));
}
- private final Node childElement(Node n) {
+ private static final String nil(XMLFormat format) {
+ return format.xmlns() ? "xsi:nil" : "nil";
+ }
+
+ private static final Node childElement(Node n) {
NodeList l = n.getChildNodes();
for (int i = 0; i < l.getLength(); i++) {
@@ -1431,9 +1444,13 @@ abstract class AbstractResult extends AbstractFormattable impl
handler.startDocument();
- if (format.xmlns())
+ if (format.xmlns()) {
handler.startPrefixMapping("", Constants.NS_EXPORT);
+ if (format.nullFormat() == XSI_NIL)
+ handler.startPrefixMapping("xsi", XSI_SCHEMA);
+ }
+
handler.startElement("", "", "result", empty);
if (format.header()) {
handler.startElement("", "", "fields", empty);
@@ -1501,8 +1518,12 @@ abstract class AbstractResult extends AbstractFormattable impl
if (format.header())
handler.endElement("", "", "records");
- if (format.xmlns())
+ if (format.xmlns()) {
+ if (format.nullFormat() == XSI_NIL)
+ handler.endPrefixMapping("xsi");
+
handler.endPrefixMapping("");
+ }
handler.endDocument();
return handler;
@@ -1564,7 +1585,7 @@ abstract class AbstractResult extends AbstractFormattable impl
return formatted;
}
- private static final String escapeXML(String string) {
+ static final String escapeXML(String string) {
return StringUtils.replaceEach(string,
new String[] { "\"", "'", "<", ">", "&" },
new String[] { """, "'", "<", ">", "&"});
diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java
index 77510049e1..3c55aa7dc7 100644
--- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java
+++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java
@@ -107,6 +107,7 @@ import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.log;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.using;
+import static org.jooq.impl.DSL.xmlserializeContent;
import static org.jooq.impl.DefaultBinding.DefaultDoubleBinding.REQUIRES_LITERAL_CAST;
import static org.jooq.impl.DefaultBinding.DefaultDoubleBinding.infinity;
import static org.jooq.impl.DefaultBinding.DefaultDoubleBinding.nan;
@@ -146,11 +147,14 @@ import static org.jooq.impl.Keywords.K_TRUE;
import static org.jooq.impl.Keywords.K_YEAR_TO_DAY;
import static org.jooq.impl.Keywords.K_YEAR_TO_FRACTION;
import static org.jooq.impl.Names.N_BYTEA;
+import static org.jooq.impl.Names.N_CREATEXML;
+import static org.jooq.impl.Names.N_HEX;
import static org.jooq.impl.Names.N_JSON_PARSE;
import static org.jooq.impl.Names.N_PARSE_JSON;
import static org.jooq.impl.Names.N_ST_GEOMFROMTEXT;
import static org.jooq.impl.Names.N_ST_GEOMFROMWKB;
import static org.jooq.impl.Names.N_TO_BINARY;
+import static org.jooq.impl.Names.N_XMLTYPE;
import static org.jooq.impl.R2DBC.isR2dbc;
import static org.jooq.impl.SQLDataType.BIGINT;
import static org.jooq.impl.SQLDataType.BLOB;
@@ -333,6 +337,8 @@ import org.jooq.types.YearToMonth;
import org.jooq.types.YearToSecond;
import org.jooq.util.postgres.PostgresUtils;
+import org.jetbrains.annotations.Nullable;
+
// ...
// ...
@@ -934,6 +940,15 @@ public class DefaultBinding implements Binding {
}
}
+
+
+
+
+
+
+
+
+
if (dataType.isUUID()) {
switch (ctx.family()) {
@@ -1220,7 +1235,7 @@ public class DefaultBinding implements Binding {
private final void sql(BindingSQLContext ctx, T value) throws SQLException {
if (ctx.render().paramType() == INLINED)
if (value == null)
- ctx.render().visit(K_NULL);
+ sqlInlineNull0(ctx);
else
sqlInline0(ctx, value);
else
@@ -1330,7 +1345,10 @@ public class DefaultBinding implements Binding {
ctx.statement().registerOutParameter(ctx.index(), sqltype(ctx.statement(), ctx.configuration()));
}
- @SuppressWarnings("unused")
+ /* non-final */ void sqlInlineNull0(BindingSQLContext ctx) {
+ ctx.render().visit(K_NULL);
+ }
+
/* non-final */ void sqlInline0(BindingSQLContext ctx, T value) throws SQLException {
sqlInline1(ctx, value);
}
@@ -6554,6 +6572,54 @@ public class DefaultBinding implements Binding {
super(dataType, converter);
}
+ @Override
+ final void sqlInlineNull0(BindingSQLContext ctx) {
+ switch (ctx.family()) {
+
+
+
+
+
+ default:
+ super.sqlInlineNull0(ctx);
+ break;
+ }
+ }
+
+ @Override
+ final void sqlInline0(BindingSQLContext ctx, XML value) throws SQLException {
+ switch (ctx.family()) {
+
+
+
+
+
+
+
+
+ default:
+ super.sqlInline0(ctx, value);
+ break;
+ }
+ }
+
+ @Override
+ final void sqlBind0(BindingSQLContext ctx, XML value) throws SQLException {
+ switch (ctx.family()) {
+
+
+
+
+
+
+
+
+ default:
+ super.sqlBind0(ctx, value);
+ break;
+ }
+ }
+
@Override
final void setNull0(BindingSetStatementContext ctx) throws SQLException {
diff --git a/jOOQ/src/main/java/org/jooq/impl/Multiset.java b/jOOQ/src/main/java/org/jooq/impl/Multiset.java
index 950c27761b..a2a8db4c61 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Multiset.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Multiset.java
@@ -628,6 +628,12 @@ final class Multiset extends AbstractField> implemen
+
+
+
+
+
+
diff --git a/jOOQ/src/main/java/org/jooq/impl/Names.java b/jOOQ/src/main/java/org/jooq/impl/Names.java
index 611a4cac01..68d206a08e 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Names.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Names.java
@@ -97,6 +97,7 @@ final class Names {
static final Name N_COUNT_IF = systemName("count_if");
static final Name N_covarPop = systemName("covarPop");
static final Name N_covarSamp = systemName("covarSamp");
+ static final Name N_CREATEXML = systemName("createxml");
static final Name N_CUBE = systemName("cube");
static final Name N_CURRENT_BIGDATETIME = systemName("current_bigdatetime");
static final Name N_CURRENT_DATE = systemName("current_date");
diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java
index 79622bb10d..bce132221c 100644
--- a/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java
+++ b/jOOQ/src/main/java/org/jooq/impl/XMLHandler.java
@@ -38,6 +38,8 @@
package org.jooq.impl;
// ...
+import static org.jooq.XML.xml;
+import static org.jooq.impl.AbstractResult.escapeXML;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DefaultDataType.getDataType;
@@ -51,18 +53,21 @@ import static org.jooq.impl.Tools.row0;
import static org.jooq.tools.StringUtils.defaultIfBlank;
import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Deque;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.jooq.ContextConverter;
-import org.jooq.Converter;
import org.jooq.ConverterContext;
import org.jooq.DSLContext;
import org.jooq.DataType;
@@ -71,22 +76,181 @@ import org.jooq.Record;
import org.jooq.Result;
import org.jooq.exception.DataAccessException;
import org.jooq.tools.JooqLogger;
+import org.jooq.tools.StringUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
/**
* @author Lukas Eder
*/
-final class XMLHandler extends DefaultHandler {
+final class XMLHandler
+extends
+ DefaultHandler
+implements
+ LexicalHandler
+{
private static final JooqLogger log = JooqLogger.getLogger(XMLHandler.class);
private static final boolean debug = false;
private final DSLContext ctx;
private final Deque> states;
private State s;
+ static class XMLWriter extends DefaultHandler implements LexicalHandler {
+ final StringWriter out;
+ int level;
+ String lastElement;
+ String[] lastAttributes;
+ boolean cdata;
+
+ XMLWriter() {
+ out = new StringWriter();
+
+ // [#19229] TODO: StringWriter seems good enough for our test cases. Perhaps, switch to XMLStreamWriter, instead?
+ }
+
+ private boolean flushLastElement(boolean end) {
+ if (lastElement != null) {
+ out.write('<');
+ out.write(lastElement);
+
+ if (lastAttributes != null) {
+ for (int i = 0; i < lastAttributes.length; i += 2) {
+ out.write(' ');
+ out.write(lastAttributes[i]);
+ out.write("=\"");
+ out.write(escapeXML(lastAttributes[i + 1]));
+ out.write("\"");
+ }
+ }
+
+ if (end)
+ out.write("/>");
+ else
+ out.write('>');
+
+ lastElement = null;
+ lastAttributes = null;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ // --------------------------------------------------------------------
+ // ContentHandler API
+ // --------------------------------------------------------------------
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+ level++;
+
+ flushLastElement(false);
+ lastElement = qName;
+
+ // [#19229] Attributes is a mutable object in some parsers (e.g. ojdbc ships its own),
+ // so we have to copy its contents
+ if (atts != null && atts.getLength() > 0) {
+ lastAttributes = new String[atts.getLength() * 2];
+
+ for (int i = 0; i < atts.getLength(); i++) {
+ lastAttributes[i * 2] = atts.getQName(i);
+ lastAttributes[i * 2 + 1] = atts.getValue(i);
+ }
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!flushLastElement(true)) {
+ out.write("");
+ out.write(qName);
+ out.write('>');
+ }
+
+ level--;
+ }
+
+ @Override
+ public void processingInstruction(String target, String data) throws SAXException {
+ flushLastElement(false);
+ out.write("");
+ out.write(target);
+
+ if (!StringUtils.isEmpty(data)) {
+ out.write(' ');
+ out.write(data);
+ }
+
+ out.write("?>");
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ flushLastElement(false);
+
+ if (cdata)
+ out.write(ch, start, length);
+ else
+ out.write(escapeXML(new String(ch, start, length)));
+ }
+
+ @Override
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ flushLastElement(false);
+ out.write(ch, start, length);
+ }
+
+ // --------------------------------------------------------------------
+ // LexicalHandler API
+ // --------------------------------------------------------------------
+
+ @Override
+ public void startCDATA() throws SAXException {
+ cdata = true;
+ flushLastElement(false);
+ out.write("");
+ cdata = false;
+ }
+
+ @Override
+ public void comment(char[] ch, int start, int length) throws SAXException {
+ flushLastElement(false);
+
+ out.write("");
+ }
+
+ // [#19229] TODO: Implement these if needed
+
+ @Override
+ public void startDTD(String name, String publicId, String systemId) throws SAXException {
+ }
+
+ @Override
+ public void endDTD() throws SAXException {
+ }
+
+ @Override
+ public void startEntity(String name) throws SAXException {
+ }
+
+ @Override
+ public void endEntity(String name) throws SAXException {
+ }
+ }
+
private static class State {
final DSLContext ctx;
AbstractRow row;
@@ -101,6 +265,7 @@ final class XMLHandler extends DefaultHandler {
final List