21
21
*/
22
22
package org .exist .xquery .modules .sql ;
23
23
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 ;
38
32
39
33
import java .io .IOException ;
40
34
import java .io .PrintStream ;
41
-
42
35
import java .io .Reader ;
43
36
import java .sql .Connection ;
44
37
import java .sql .PreparedStatement ;
51
44
import java .sql .Timestamp ;
52
45
import java .sql .Types ;
53
46
54
- import org .xml .sax .InputSource ;
55
- import org .xml .sax .XMLReader ;
56
-
57
47
import javax .annotation .Nullable ;
58
48
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 ;
63
79
64
80
65
81
/**
@@ -85,6 +101,9 @@ public class ExecuteFunction extends BasicFunction {
85
101
Type .BOOLEAN ,
86
102
"The flag that indicates whether the xml nodes should be formed from the column names" +
87
103
" (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." );
88
107
89
108
static final FunctionSignature [] FS_EXECUTE = functionSignatures (
90
109
FS_EXECUTE_NAME ,
@@ -101,6 +120,20 @@ public class ExecuteFunction extends BasicFunction {
101
120
param ("statement-handle" , Type .LONG , "The prepared statement handle" ),
102
121
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>" ),
103
122
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
104
137
)
105
138
)
106
139
);
@@ -147,20 +180,30 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
147
180
148
181
try {
149
182
final boolean makeNodeFromColumnName ;
183
+ final String namespacePrefix ;
184
+ final String namespaceUri ;
150
185
final boolean executeResult ;
151
186
152
187
// Static SQL or PreparedStatement?
153
- if (args .length == 3 ) {
188
+ if (args .length == 3 || args . length == 5 ) {
154
189
155
190
// get the static SQL statement
156
191
sql = args [1 ].getStringValue ();
157
192
stmt = con .createStatement ();
158
193
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
+ }
159
202
160
203
//execute the static SQL statement
161
204
executeResult = stmt .execute (sql );
162
205
163
- } else if (args .length == 4 ) {
206
+ } else if (args .length == 4 || args . length == 6 ) {
164
207
//get the prepared statement
165
208
final long statementUID = ((IntegerValue ) args [1 ].itemAt (0 )).getLong ();
166
209
final PreparedStatementWithSQL stmtWithSQL = SQLModule .retrievePreparedStatement (context , statementUID );
@@ -172,6 +215,14 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
172
215
}
173
216
174
217
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
+ }
175
226
176
227
if (!args [2 ].isEmpty ()) {
177
228
parametersElement = (Element ) args [2 ].itemAt (0 );
@@ -186,7 +237,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
186
237
}
187
238
188
239
// return the XML result set
189
- return resultAsElement (makeNodeFromColumnName , executeResult , stmt , this );
240
+ return resultAsElement (makeNodeFromColumnName , namespacePrefix , namespaceUri , executeResult , stmt , this );
190
241
191
242
} catch (final SQLException sqle ) {
192
243
LOG .error ("sql:execute() Caught SQLException \" {}\" for SQL: \" {}\" " , sqle .getMessage (), sql , sqle );
@@ -264,15 +315,15 @@ private void setParametersOnPreparedStatement(final Statement stmt, final Elemen
264
315
}
265
316
}
266
317
267
- private ElementImpl resultAsElement (final boolean makeNodeFromColumnName ,
318
+ private ElementImpl resultAsElement (final boolean makeNodeFromColumnName , final String namespacePrefix , final String namespaceUri ,
268
319
final boolean executeResult , final Statement stmt , final Expression expression ) throws SQLException , XPathException {
269
320
context .pushDocumentContext ();
270
321
try {
271
322
final MemTreeBuilder builder = context .getDocumentBuilder ();
272
323
273
324
builder .startDocument ();
274
325
275
- builder .startElement (new QName ("result" , NAMESPACE_URI , PREFIX ), null );
326
+ builder .startElement (new QName ("result" , namespaceUri , namespacePrefix ), null );
276
327
builder .addAttribute (new QName ("count" , null , null ), "-1" );
277
328
builder .addAttribute (new QName ("updateCount" , null , null ), String .valueOf (stmt .getUpdateCount ()));
278
329
@@ -301,7 +352,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
301
352
final int iColumns = rsmd .getColumnCount ();
302
353
303
354
while (rs .next ()) {
304
- builder .startElement (new QName ("row" , NAMESPACE_URI , PREFIX ), null );
355
+ builder .startElement (new QName ("row" , namespaceUri , namespacePrefix ), null );
305
356
builder .addAttribute (new QName ("index" , null , null ), String .valueOf (rs .getRow ()));
306
357
307
358
// get each tuple in the row
@@ -322,7 +373,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
322
373
colElement = SQLUtils .escapeXmlAttr (columnName .replace (' ' , '_' ));
323
374
}
324
375
325
- builder .startElement (new QName (colElement , NAMESPACE_URI , PREFIX ), null );
376
+ builder .startElement (new QName (colElement , namespaceUri , namespacePrefix ), null );
326
377
327
378
if (!makeNodeFromColumnName || columnName .length () <= 0 ) {
328
379
final String name ;
@@ -335,7 +386,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
335
386
builder .addAttribute (new QName ("name" , null , null ), name );
336
387
}
337
388
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 ));
339
390
builder .addAttribute (new QName (TYPE_ATTRIBUTE_NAME , Namespaces .SCHEMA_NS , "xs" ), Type .getTypeName (SQLUtils .sqlTypeToXMLType (rsmd .getColumnType (i + 1 ))));
340
391
341
392
//get the content
@@ -346,7 +397,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
346
397
347
398
if (rs .wasNull ()) {
348
399
// 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" );
350
401
} else {
351
402
try (final Reader charStream = sqlXml .getCharacterStream ()) {
352
403
final InputSource src = new InputSource (charStream );
@@ -375,7 +426,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
375
426
376
427
if (rs .wasNull ()) {
377
428
// 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" );
379
430
} else {
380
431
if (colValue != null ) {
381
432
builder .characters (colValue );
0 commit comments