2121 */
2222package 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
3933import java .io .IOException ;
4034import java .io .PrintStream ;
41-
4235import java .io .Reader ;
4336import java .sql .Connection ;
4437import java .sql .PreparedStatement ;
5144import java .sql .Timestamp ;
5245import java .sql .Types ;
5346
54- import org .xml .sax .InputSource ;
55- import org .xml .sax .XMLReader ;
56-
5747import 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/**
@@ -80,11 +96,31 @@ public class ExecuteFunction extends BasicFunction {
8096 "connection-handle" ,
8197 Type .LONG ,
8298 "The connection handle" );
99+ private static final FunctionParameterSequenceType FS_PARAM_SQL_STATEMENT = param (
100+ "sql-statement" ,
101+ Type .STRING ,
102+ "The SQL statement" );
83103 private static final FunctionParameterSequenceType FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME = param (
84104 "make-node-from-column-name" ,
85105 Type .BOOLEAN ,
86106 "The flag that indicates whether the xml nodes should be formed from the column names" +
87107 " (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." );
88124
89125 static final FunctionSignature [] FS_EXECUTE = functionSignatures (
90126 FS_EXECUTE_NAME ,
@@ -93,14 +129,29 @@ public class ExecuteFunction extends BasicFunction {
93129 arities (
94130 arity (
95131 FS_PARAM_CONNECTION_HANDLE ,
96- param ( "sql-statement" , Type . STRING , "The SQL statement" ) ,
132+ FS_PARAM_SQL_STATEMENT ,
97133 FS_PARAM_MAKE_NODE_FROM_COLUMN_NAME
98134 ),
99135 arity (
100136 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 ,
103139 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
104155 )
105156 )
106157 );
@@ -147,20 +198,30 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
147198
148199 try {
149200 final boolean makeNodeFromColumnName ;
201+ final String namespacePrefix ;
202+ final String namespaceUri ;
150203 final boolean executeResult ;
151204
152205 // Static SQL or PreparedStatement?
153- if (args .length == 3 ) {
206+ if (args .length == 3 || args . length == 5 ) {
154207
155208 // get the static SQL statement
156209 sql = args [1 ].getStringValue ();
157210 stmt = con .createStatement ();
158211 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+ }
159220
160221 //execute the static SQL statement
161222 executeResult = stmt .execute (sql );
162223
163- } else if (args .length == 4 ) {
224+ } else if (args .length == 4 || args . length == 6 ) {
164225 //get the prepared statement
165226 final long statementUID = ((IntegerValue ) args [1 ].itemAt (0 )).getLong ();
166227 final PreparedStatementWithSQL stmtWithSQL = SQLModule .retrievePreparedStatement (context , statementUID );
@@ -172,6 +233,14 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
172233 }
173234
174235 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+ }
175244
176245 if (!args [2 ].isEmpty ()) {
177246 parametersElement = (Element ) args [2 ].itemAt (0 );
@@ -186,7 +255,7 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
186255 }
187256
188257 // return the XML result set
189- return resultAsElement (makeNodeFromColumnName , executeResult , stmt , this );
258+ return resultAsElement (makeNodeFromColumnName , namespacePrefix , namespaceUri , executeResult , stmt , this );
190259
191260 } catch (final SQLException sqle ) {
192261 LOG .error ("sql:execute() Caught SQLException \" {}\" for SQL: \" {}\" " , sqle .getMessage (), sql , sqle );
@@ -264,15 +333,15 @@ private void setParametersOnPreparedStatement(final Statement stmt, final Elemen
264333 }
265334 }
266335
267- private ElementImpl resultAsElement (final boolean makeNodeFromColumnName ,
336+ private ElementImpl resultAsElement (final boolean makeNodeFromColumnName , final String namespacePrefix , final String namespaceUri ,
268337 final boolean executeResult , final Statement stmt , final Expression expression ) throws SQLException , XPathException {
269338 context .pushDocumentContext ();
270339 try {
271340 final MemTreeBuilder builder = context .getDocumentBuilder ();
272341
273342 builder .startDocument ();
274343
275- builder .startElement (new QName ("result" , NAMESPACE_URI , PREFIX ), null );
344+ builder .startElement (new QName ("result" , namespaceUri , namespacePrefix ), null );
276345 builder .addAttribute (new QName ("count" , null , null ), "-1" );
277346 builder .addAttribute (new QName ("updateCount" , null , null ), String .valueOf (stmt .getUpdateCount ()));
278347
@@ -301,7 +370,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
301370 final int iColumns = rsmd .getColumnCount ();
302371
303372 while (rs .next ()) {
304- builder .startElement (new QName ("row" , NAMESPACE_URI , PREFIX ), null );
373+ builder .startElement (new QName ("row" , namespaceUri , namespacePrefix ), null );
305374 builder .addAttribute (new QName ("index" , null , null ), String .valueOf (rs .getRow ()));
306375
307376 // get each tuple in the row
@@ -322,7 +391,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
322391 colElement = SQLUtils .escapeXmlAttr (columnName .replace (' ' , '_' ));
323392 }
324393
325- builder .startElement (new QName (colElement , NAMESPACE_URI , PREFIX ), null );
394+ builder .startElement (new QName (colElement , namespaceUri , namespacePrefix ), null );
326395
327396 if (!makeNodeFromColumnName || columnName .length () <= 0 ) {
328397 final String name ;
@@ -335,7 +404,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
335404 builder .addAttribute (new QName ("name" , null , null ), name );
336405 }
337406
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 ));
339408 builder .addAttribute (new QName (TYPE_ATTRIBUTE_NAME , Namespaces .SCHEMA_NS , "xs" ), Type .getTypeName (SQLUtils .sqlTypeToXMLType (rsmd .getColumnType (i + 1 ))));
340409
341410 //get the content
@@ -346,7 +415,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
346415
347416 if (rs .wasNull ()) {
348417 // 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" );
350419 } else {
351420 try (final Reader charStream = sqlXml .getCharacterStream ()) {
352421 final InputSource src = new InputSource (charStream );
@@ -375,7 +444,7 @@ private ElementImpl resultAsElement(final boolean makeNodeFromColumnName,
375444
376445 if (rs .wasNull ()) {
377446 // 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" );
379448 } else {
380449 if (colValue != null ) {
381450 builder .characters (colValue );
0 commit comments