Skip to content

Commit 5ff847a

Browse files
author
Nico Verwer
committed
Allow setting the namespace on result elements of sql:execute()
1 parent 2b35cf3 commit 5ff847a

File tree

1 file changed

+83
-32
lines changed

1 file changed

+83
-32
lines changed

extensions/modules/sql/src/main/java/org/exist/xquery/modules/sql/ExecuteFunction.java

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,17 @@
2121
*/
2222
package org.exist.xquery.modules.sql;
2323

24-
import org.apache.logging.log4j.LogManager;
25-
import org.apache.logging.log4j.Logger;
26-
27-
import org.exist.dom.memtree.*;
28-
import org.exist.util.XMLReaderPool;
29-
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
30-
import org.exist.xquery.*;
31-
import org.exist.xquery.value.*;
32-
import org.w3c.dom.Element;
33-
import org.w3c.dom.Node;
34-
import org.w3c.dom.NodeList;
35-
36-
import org.exist.Namespaces;
37-
import org.exist.dom.QName;
24+
import static java.nio.charset.StandardCharsets.UTF_8;
25+
import static org.exist.xquery.FunctionDSL.arities;
26+
import static org.exist.xquery.FunctionDSL.arity;
27+
import static org.exist.xquery.FunctionDSL.optParam;
28+
import static org.exist.xquery.FunctionDSL.param;
29+
import static org.exist.xquery.FunctionDSL.returnsOpt;
30+
import static org.exist.xquery.modules.sql.SQLModule.NAMESPACE_URI;
31+
import static org.exist.xquery.modules.sql.SQLModule.PREFIX;
3832

3933
import java.io.IOException;
4034
import java.io.PrintStream;
41-
4235
import java.io.Reader;
4336
import java.sql.Connection;
4437
import java.sql.PreparedStatement;
@@ -51,15 +44,38 @@
5144
import java.sql.Timestamp;
5245
import java.sql.Types;
5346

54-
import org.xml.sax.InputSource;
55-
import org.xml.sax.XMLReader;
56-
5747
import javax.annotation.Nullable;
5848

59-
import static java.nio.charset.StandardCharsets.UTF_8;
60-
import static org.exist.xquery.FunctionDSL.*;
61-
import static org.exist.xquery.modules.sql.SQLModule.NAMESPACE_URI;
62-
import static org.exist.xquery.modules.sql.SQLModule.PREFIX;
49+
import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
50+
import org.apache.logging.log4j.LogManager;
51+
import org.apache.logging.log4j.Logger;
52+
import org.exist.Namespaces;
53+
import org.exist.dom.QName;
54+
import org.exist.dom.memtree.AppendingSAXAdapter;
55+
import org.exist.dom.memtree.ElementImpl;
56+
import org.exist.dom.memtree.MemTreeBuilder;
57+
import org.exist.dom.memtree.ReferenceNode;
58+
import org.exist.dom.memtree.SAXAdapter;
59+
import org.exist.util.XMLReaderPool;
60+
import org.exist.xquery.BasicFunction;
61+
import org.exist.xquery.ErrorCodes;
62+
import org.exist.xquery.Expression;
63+
import org.exist.xquery.FunctionDSL;
64+
import org.exist.xquery.FunctionSignature;
65+
import org.exist.xquery.XPathException;
66+
import org.exist.xquery.XQueryContext;
67+
import org.exist.xquery.value.BooleanValue;
68+
import org.exist.xquery.value.DateTimeValue;
69+
import org.exist.xquery.value.FunctionParameterSequenceType;
70+
import org.exist.xquery.value.FunctionReturnSequenceType;
71+
import org.exist.xquery.value.IntegerValue;
72+
import org.exist.xquery.value.Sequence;
73+
import org.exist.xquery.value.Type;
74+
import org.w3c.dom.Element;
75+
import org.w3c.dom.Node;
76+
import org.w3c.dom.NodeList;
77+
import org.xml.sax.InputSource;
78+
import org.xml.sax.XMLReader;
6379

6480

6581
/**
@@ -85,6 +101,9 @@ public class ExecuteFunction extends BasicFunction {
85101
Type.BOOLEAN,
86102
"The flag that indicates whether the xml nodes should be formed from the column names" +
87103
" (in this mode a space in a Column Name will be replaced by an underscore!)");
104+
// Parameters for setting the namespace of result elements.
105+
private static final FunctionParameterSequenceType FS_PARAM_NAMESPACE_PREFIX = param("ns-prefix", Type.STRING, "The prefix of the result namespace.");
106+
private static final FunctionParameterSequenceType FS_PARAM_NAMESPACE_URI = param("ns-uri", Type.STRING, "The uri of the result namespace.");
88107

89108
static final FunctionSignature[] FS_EXECUTE = functionSignatures(
90109
FS_EXECUTE_NAME,
@@ -101,6 +120,20 @@ public class ExecuteFunction extends BasicFunction {
101120
param("statement-handle", Type.LONG, "The prepared statement handle"),
102121
optParam("parameters", Type.ELEMENT, "Parameters for the prepared statement. e.g. <sql:parameters><sql:param sql:type=\"long\">1234</sql:param><sql:param sql:type=\"varchar\"><sql:null/></sql:param></sql:parameters>"),
103122
FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME
123+
),
124+
// New function signatures for setting the namespace of result elements.
125+
arity(
126+
FS_PARAM_CONNECTION_HANDLE,
127+
param("sql-statement", Type.STRING, "The SQL statement"),
128+
FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME,
129+
FS_PARAM_NAMESPACE_PREFIX, FS_PARAM_NAMESPACE_URI
130+
),
131+
arity(
132+
FS_PARAM_CONNECTION_HANDLE,
133+
param("statement-handle", Type.LONG, "The prepared statement handle"),
134+
optParam("parameters", Type.ELEMENT, "Parameters for the prepared statement. e.g. <sql:parameters><sql:param sql:type=\"long\">1234</sql:param><sql:param sql:type=\"varchar\"><sql:null/></sql:param></sql:parameters>"),
135+
FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME,
136+
FS_PARAM_NAMESPACE_PREFIX, FS_PARAM_NAMESPACE_URI
104137
)
105138
)
106139
);
@@ -147,20 +180,30 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
147180

148181
try {
149182
final boolean makeNodeFromColumnName;
183+
final String namespacePrefix;
184+
final String namespaceUri;
150185
final boolean executeResult;
151186

152187
// Static SQL or PreparedStatement?
153-
if (args.length == 3) {
188+
if (args.length == 3 || args.length == 5) {
154189

155190
// get the static SQL statement
156191
sql = args[1].getStringValue();
157192
stmt = con.createStatement();
158193
makeNodeFromColumnName = ((BooleanValue) args[2].itemAt(0)).effectiveBooleanValue();
194+
if (args.length == 5) {
195+
namespacePrefix = args[3].itemAt(0).getStringValue();
196+
namespaceUri = args[4].itemAt(0).getStringValue();
197+
} else {
198+
// The default namespace for result elements.
199+
namespacePrefix = PREFIX;
200+
namespaceUri = NAMESPACE_URI;
201+
}
159202

160203
//execute the static SQL statement
161204
executeResult = stmt.execute(sql);
162205

163-
} else if (args.length == 4) {
206+
} else if (args.length == 4 || args.length == 6) {
164207
//get the prepared statement
165208
final long statementUID = ((IntegerValue) args[1].itemAt(0)).getLong();
166209
final PreparedStatementWithSQL stmtWithSQL = SQLModule.retrievePreparedStatement(context, statementUID);
@@ -172,6 +215,14 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
172215
}
173216

174217
makeNodeFromColumnName = ((BooleanValue) args[3].itemAt(0)).effectiveBooleanValue();
218+
if (args.length == 6) {
219+
namespacePrefix = args[4].itemAt(0).getStringValue();
220+
namespaceUri = args[5].itemAt(0).getStringValue();
221+
} else {
222+
// The default namespace for result elements.
223+
namespacePrefix = PREFIX;
224+
namespaceUri = NAMESPACE_URI;
225+
}
175226

176227
if (!args[2].isEmpty()) {
177228
parametersElement = (Element) args[2].itemAt(0);
@@ -186,7 +237,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
186237
}
187238

188239
// return the XML result set
189-
return resultAsElement(makeNodeFromColumnName, executeResult, stmt, this);
240+
return resultAsElement(makeNodeFromColumnName, namespacePrefix, namespaceUri, executeResult, stmt, this);
190241

191242
} catch (final SQLException sqle) {
192243
LOG.error("sql:execute() Caught SQLException \"{}\" for SQL: \"{}\"", sqle.getMessage(), sql, sqle);
@@ -264,15 +315,15 @@ private void setParametersOnPreparedStatement(final Statement stmt, final Elemen
264315
}
265316
}
266317

267-
private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
318+
private ElementImpl resultAsElement(final boolean makeNodeFromColumnName, final String namespacePrefix, final String namespaceUri,
268319
final boolean executeResult, final Statement stmt, final Expression expression) throws SQLException, XPathException {
269320
context.pushDocumentContext();
270321
try {
271322
final MemTreeBuilder builder = context.getDocumentBuilder();
272323

273324
builder.startDocument();
274325

275-
builder.startElement(new QName("result", NAMESPACE_URI, PREFIX), null);
326+
builder.startElement(new QName("result", namespaceUri, namespacePrefix), null);
276327
builder.addAttribute(new QName("count", null, null), "-1");
277328
builder.addAttribute(new QName("updateCount", null, null), String.valueOf(stmt.getUpdateCount()));
278329

@@ -301,7 +352,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
301352
final int iColumns = rsmd.getColumnCount();
302353

303354
while (rs.next()) {
304-
builder.startElement(new QName("row", NAMESPACE_URI, PREFIX), null);
355+
builder.startElement(new QName("row", namespaceUri, namespacePrefix), null);
305356
builder.addAttribute(new QName("index", null, null), String.valueOf(rs.getRow()));
306357

307358
// get each tuple in the row
@@ -322,7 +373,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
322373
colElement = SQLUtils.escapeXmlAttr(columnName.replace(' ', '_'));
323374
}
324375

325-
builder.startElement(new QName(colElement, NAMESPACE_URI, PREFIX), null);
376+
builder.startElement(new QName(colElement, namespaceUri, namespacePrefix), null);
326377

327378
if (!makeNodeFromColumnName || columnName.length() <= 0) {
328379
final String name;
@@ -335,7 +386,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
335386
builder.addAttribute(new QName("name", null, null), name);
336387
}
337388

338-
builder.addAttribute(new QName(TYPE_ATTRIBUTE_NAME, NAMESPACE_URI, PREFIX), rsmd.getColumnTypeName(i + 1));
389+
builder.addAttribute(new QName(TYPE_ATTRIBUTE_NAME, namespaceUri, namespacePrefix), rsmd.getColumnTypeName(i + 1));
339390
builder.addAttribute(new QName(TYPE_ATTRIBUTE_NAME, Namespaces.SCHEMA_NS, "xs"), Type.getTypeName(SQLUtils.sqlTypeToXMLType(rsmd.getColumnType(i + 1))));
340391

341392
//get the content
@@ -346,7 +397,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
346397

347398
if (rs.wasNull()) {
348399
// Add a null indicator attribute if the value was SQL Null
349-
builder.addAttribute(new QName("null", NAMESPACE_URI, PREFIX), "true");
400+
builder.addAttribute(new QName("null", namespaceUri, namespacePrefix), "true");
350401
} else {
351402
try (final Reader charStream = sqlXml.getCharacterStream()) {
352403
final InputSource src = new InputSource(charStream);
@@ -375,7 +426,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
375426

376427
if (rs.wasNull()) {
377428
// Add a null indicator attribute if the value was SQL Null
378-
builder.addAttribute(new QName("null", NAMESPACE_URI, PREFIX), "true");
429+
builder.addAttribute(new QName("null", namespaceUri, namespacePrefix), "true");
379430
} else {
380431
if (colValue != null) {
381432
builder.characters(colValue);

0 commit comments

Comments
 (0)