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
/**
@@ -80,11 +96,31 @@ public class ExecuteFunction extends BasicFunction {
80
96
"connection-handle" ,
81
97
Type .LONG ,
82
98
"The connection handle" );
99
+ private static final FunctionParameterSequenceType FS_PARAM_SQL_STATEMENT = param (
100
+ "sql-statement" ,
101
+ Type .STRING ,
102
+ "The SQL statement" );
83
103
private static final FunctionParameterSequenceType FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME = param (
84
104
"make-node-from-column-name" ,
85
105
Type .BOOLEAN ,
86
106
"The flag that indicates whether the xml nodes should be formed from the column names" +
87
107
" (in this mode a space in a Column Name will be replaced by an underscore!)" );
108
+ private static final FunctionParameterSequenceType FS_PARAM_STATEMENT_HANDLE = param (
109
+ "statement-handle" ,
110
+ Type .LONG ,
111
+ "The prepared statement handle" );
112
+ private static final FunctionParameterSequenceType FS_PARAM_PARAMETERS = optParam (
113
+ "parameters" ,
114
+ Type .ELEMENT ,
115
+ "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>" );
116
+ private static final FunctionParameterSequenceType FS_PARAM_NAMESPACE_URI = param (
117
+ "ns-uri" ,
118
+ Type .STRING ,
119
+ "The uri of the result namespace." );
120
+ private static final FunctionParameterSequenceType FS_PARAM_NAMESPACE_PREFIX = param (
121
+ "ns-prefix" ,
122
+ Type .STRING ,
123
+ "The prefix of the result namespace." );
88
124
89
125
static final FunctionSignature [] FS_EXECUTE = functionSignatures (
90
126
FS_EXECUTE_NAME ,
@@ -93,14 +129,29 @@ public class ExecuteFunction extends BasicFunction {
93
129
arities (
94
130
arity (
95
131
FS_PARAM_CONNECTION_HANDLE ,
96
- param ( "sql-statement" , Type . STRING , "The SQL statement" ) ,
132
+ FS_PARAM_SQL_STATEMENT ,
97
133
FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME
98
134
),
99
135
arity (
100
136
FS_PARAM_CONNECTION_HANDLE ,
101
- param ( "statement-handle" , Type . LONG , "The prepared statement handle" ) ,
102
- 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>" ) ,
137
+ FS_PARAM_STATEMENT_HANDLE ,
138
+ FS_PARAM_PARAMETERS ,
103
139
FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME
140
+ ),
141
+ arity (
142
+ FS_PARAM_CONNECTION_HANDLE ,
143
+ FS_PARAM_SQL_STATEMENT ,
144
+ FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME ,
145
+ FS_PARAM_NAMESPACE_URI ,
146
+ FS_PARAM_NAMESPACE_PREFIX
147
+ ),
148
+ arity (
149
+ FS_PARAM_CONNECTION_HANDLE ,
150
+ FS_PARAM_STATEMENT_HANDLE ,
151
+ FS_PARAM_PARAMETERS ,
152
+ FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME ,
153
+ FS_PARAM_NAMESPACE_URI ,
154
+ FS_PARAM_NAMESPACE_PREFIX
104
155
)
105
156
)
106
157
);
@@ -147,20 +198,30 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
147
198
148
199
try {
149
200
final boolean makeNodeFromColumnName ;
201
+ final String namespacePrefix ;
202
+ final String namespaceUri ;
150
203
final boolean executeResult ;
151
204
152
205
// Static SQL or PreparedStatement?
153
- if (args .length == 3 ) {
206
+ if (args .length == 3 || args . length == 5 ) {
154
207
155
208
// get the static SQL statement
156
209
sql = args [1 ].getStringValue ();
157
210
stmt = con .createStatement ();
158
211
makeNodeFromColumnName = ((BooleanValue ) args [2 ].itemAt (0 )).effectiveBooleanValue ();
212
+ if (args .length == 5 ) {
213
+ namespaceUri = args [3 ].itemAt (0 ).getStringValue ();
214
+ namespacePrefix = args [4 ].itemAt (0 ).getStringValue ();
215
+ } else {
216
+ // The default namespace for result elements.
217
+ namespaceUri = NAMESPACE_URI ;
218
+ namespacePrefix = PREFIX ;
219
+ }
159
220
160
221
//execute the static SQL statement
161
222
executeResult = stmt .execute (sql );
162
223
163
- } else if (args .length == 4 ) {
224
+ } else if (args .length == 4 || args . length == 6 ) {
164
225
//get the prepared statement
165
226
final long statementUID = ((IntegerValue ) args [1 ].itemAt (0 )).getLong ();
166
227
final PreparedStatementWithSQL stmtWithSQL = SQLModule .retrievePreparedStatement (context , statementUID );
@@ -172,6 +233,14 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
172
233
}
173
234
174
235
makeNodeFromColumnName = ((BooleanValue ) args [3 ].itemAt (0 )).effectiveBooleanValue ();
236
+ if (args .length == 6 ) {
237
+ namespaceUri = args [4 ].itemAt (0 ).getStringValue ();
238
+ namespacePrefix = args [5 ].itemAt (0 ).getStringValue ();
239
+ } else {
240
+ // The default namespace for result elements.
241
+ namespaceUri = NAMESPACE_URI ;
242
+ namespacePrefix = PREFIX ;
243
+ }
175
244
176
245
if (!args [2 ].isEmpty ()) {
177
246
parametersElement = (Element ) args [2 ].itemAt (0 );
@@ -186,7 +255,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
186
255
}
187
256
188
257
// return the XML result set
189
- return resultAsElement (makeNodeFromColumnName , executeResult , stmt , this );
258
+ return resultAsElement (makeNodeFromColumnName , namespacePrefix , namespaceUri , executeResult , stmt , this );
190
259
191
260
} catch (final SQLException sqle ) {
192
261
LOG .error ("sql:execute() Caught SQLException \" {}\" for SQL: \" {}\" " , sqle .getMessage (), sql , sqle );
@@ -264,15 +333,15 @@ private void setParametersOnPreparedStatement(final Statement stmt, final Elemen
264
333
}
265
334
}
266
335
267
- private ElementImpl resultAsElement (final boolean makeNodeFromColumnName ,
336
+ private ElementImpl resultAsElement (final boolean makeNodeFromColumnName , final String namespacePrefix , final String namespaceUri ,
268
337
final boolean executeResult , final Statement stmt , final Expression expression ) throws SQLException , XPathException {
269
338
context .pushDocumentContext ();
270
339
try {
271
340
final MemTreeBuilder builder = context .getDocumentBuilder ();
272
341
273
342
builder .startDocument ();
274
343
275
- builder .startElement (new QName ("result" , NAMESPACE_URI , PREFIX ), null );
344
+ builder .startElement (new QName ("result" , namespaceUri , namespacePrefix ), null );
276
345
builder .addAttribute (new QName ("count" , null , null ), "-1" );
277
346
builder .addAttribute (new QName ("updateCount" , null , null ), String .valueOf (stmt .getUpdateCount ()));
278
347
@@ -301,7 +370,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
301
370
final int iColumns = rsmd .getColumnCount ();
302
371
303
372
while (rs .next ()) {
304
- builder .startElement (new QName ("row" , NAMESPACE_URI , PREFIX ), null );
373
+ builder .startElement (new QName ("row" , namespaceUri , namespacePrefix ), null );
305
374
builder .addAttribute (new QName ("index" , null , null ), String .valueOf (rs .getRow ()));
306
375
307
376
// get each tuple in the row
@@ -322,7 +391,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
322
391
colElement = SQLUtils .escapeXmlAttr (columnName .replace (' ' , '_' ));
323
392
}
324
393
325
- builder .startElement (new QName (colElement , NAMESPACE_URI , PREFIX ), null );
394
+ builder .startElement (new QName (colElement , namespaceUri , namespacePrefix ), null );
326
395
327
396
if (!makeNodeFromColumnName || columnName .length () <= 0 ) {
328
397
final String name ;
@@ -335,7 +404,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
335
404
builder .addAttribute (new QName ("name" , null , null ), name );
336
405
}
337
406
338
- builder .addAttribute (new QName (TYPE_ATTRIBUTE_NAME , NAMESPACE_URI , PREFIX ), rsmd .getColumnTypeName (i + 1 ));
407
+ builder .addAttribute (new QName (TYPE_ATTRIBUTE_NAME , namespaceUri , namespacePrefix ), rsmd .getColumnTypeName (i + 1 ));
339
408
builder .addAttribute (new QName (TYPE_ATTRIBUTE_NAME , Namespaces .SCHEMA_NS , "xs" ), Type .getTypeName (SQLUtils .sqlTypeToXMLType (rsmd .getColumnType (i + 1 ))));
340
409
341
410
//get the content
@@ -346,7 +415,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
346
415
347
416
if (rs .wasNull ()) {
348
417
// Add a null indicator attribute if the value was SQL Null
349
- builder .addAttribute (new QName ("null" , NAMESPACE_URI , PREFIX ), "true" );
418
+ builder .addAttribute (new QName ("null" , namespaceUri , namespacePrefix ), "true" );
350
419
} else {
351
420
try (final Reader charStream = sqlXml .getCharacterStream ()) {
352
421
final InputSource src = new InputSource (charStream );
@@ -375,7 +444,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
375
444
376
445
if (rs .wasNull ()) {
377
446
// Add a null indicator attribute if the value was SQL Null
378
- builder .addAttribute (new QName ("null" , NAMESPACE_URI , PREFIX ), "true" );
447
+ builder .addAttribute (new QName ("null" , namespaceUri , namespacePrefix ), "true" );
379
448
} else {
380
449
if (colValue != null ) {
381
450
builder .characters (colValue );
0 commit comments