2
* Licensed to the Apache Software Foundation (ASF) under one
3
* or more contributor license agreements. See the NOTICE file
4
* distributed with this work for additional information
5
* regarding copyright ownership. The ASF licenses this file
6
* to you under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
10
* http://www.apache.org/licenses/LICENSE-2.0
12
* Unless required by applicable law or agreed to in writing, software
13
* distributed under the License is distributed on an "AS IS" BASIS,
14
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
* See the License for the specific language governing permissions and
16
* limitations under the License.
19
* $Id: SQLQueryParser.java,v 1.2 2009/12/10 03:18:45 matthewoliver Exp $
23
* This is used by the SQLDocumentHandler for processing JDBC queries.
24
* This prepares JDBC PreparedStatement or CallableStatements and the
25
* input/output of parameters from/to variables.
29
package org.apache.xalan.lib.sql;
33
import org.apache.xpath.objects.*;
34
import org.apache.xalan.extensions.ExpressionContext;
35
import org.apache.xml.utils.QName;
36
import javax.xml.transform.TransformerException;
40
public class SQLQueryParser
43
* If the parser used inline parser to pull out variables then
44
* this will be true. The default is not to use the Inline Parser.
46
private boolean m_InlineVariables = false;
51
private boolean m_IsCallable = false;
56
private String m_OrigQuery = null;
61
private StringBuffer m_ParsedQuery = null;
66
private Vector m_Parameters = null;
71
private boolean m_hasOutput = false;
76
private boolean m_HasParameters;
78
public static final int NO_OVERRIDE = 0;
79
public static final int NO_INLINE_PARSER = 1;
80
public static final int INLINE_PARSER = 2;
83
* The SQLStatement Parser will be created as a psuedo SINGLETON per
84
* XConnection. Since we are only caching the Query and its parsed results
85
* we may be able to use this as a real SINGLETON. It all depends on how
86
* Statement Caching will play out.
88
public SQLQueryParser()
94
* Constructor, used to create a new parser entry
96
private SQLQueryParser(String query)
102
* On a per Xconnection basis, we will create a SQLStatemenetParser, from
103
* this parser, individual parsers will be created. The Init method is defined
104
* to initialize all the internal structures that maintains the pool of parsers.
108
// Do nothing for now.
112
* Produce an SQL Statement Parser based on the incomming query.
114
* For now we will just create a new object, in the future we may have this
115
* interface cache the queries so that we can take advantage of a preparsed
118
* If the Inline Parser is not enabled in the Options, no action will be
119
* taken on the parser. This option can be set by the Stylesheet. If the
120
* option is not set or cleared, a default value will be set determined
121
* by the way variables were passed into the system.
123
public SQLQueryParser parse(XConnection xconn, String query, int override)
125
SQLQueryParser parser = new SQLQueryParser(query);
127
// Try to implement caching here, if we found a parser in the cache
128
// then just return the instance otherwise
129
parser.parse(xconn, override);
137
* Produce an SQL Statement Parser based on the incomming query.
139
* For now we will just create a new object, in the future we may have this
140
* interface cache the queries so that we can take advantage of a preparsed
143
* If the Inline Parser is not enabled in the Options, no action will be
144
* taken on the parser. This option can be set by the Stylesheet. If the
145
* option is not set or cleared, a default value will be set determined
146
* by the way variables were passed into the system.
148
private void parse(XConnection xconn, int override)
150
// Grab the Feature here. We could maintain it from the Parent Parser
151
// but that may cause problems if a single XConnection wants to maintain
152
// both Inline Variable Statemens along with NON inline variable statements.
154
m_InlineVariables = "true".equals(xconn.getFeature("inline-variables"));
155
if (override == NO_INLINE_PARSER) m_InlineVariables = false;
156
else if (override == INLINE_PARSER) m_InlineVariables = true;
158
if (m_InlineVariables) inlineParser();
163
* If a SQL Statement does not have any parameters, then it can be executed
164
* directly. Most SQL Servers use this as a performance advantage since no
165
* parameters need to be parsed then bound.
167
public boolean hasParameters()
169
return m_HasParameters;
173
* If the Inline Parser is used, the parser will note if this stastement is
174
* a plain SQL Statement or a Called Procedure. Called Procudures generally
175
* have output parameters and require special handling.
177
* Called Procudures that are not processed with the Inline Parser will
178
* still be executed but under the context of a PreparedStatement and
179
* not a CallableStatement. Called Procudures that have output parameters
180
* MUST be handled with the Inline Parser.
182
public boolean isCallable()
191
public Vector getParameters()
197
* The XConnection will use this method to store the Parameters
198
* that were supplied by the style sheet in the case where the
199
* inline parser was not used
201
public void setParameters(Vector p)
203
m_HasParameters = true;
208
* Return a copy of the parsed SQL query that will be set to the
209
* Database system to execute. If the inline parser was not used,
210
* then the original query will be returned.
212
public String getSQLQuery()
214
if (m_InlineVariables) return m_ParsedQuery.toString();
215
else return m_OrigQuery;
220
* The SQL Statement Parser, when an Inline Parser is used, tracks the XSL
221
* variables used to populate a statement. The data use to popoulate a
222
* can also be provided. If the data is provided, it will overide the
223
* populastion using XSL variables. When the Inline PArser is not used, then
224
* the Data will always be provided.
227
public void populateStatement(PreparedStatement stmt, ExpressionContext ctx)
229
// Set input parameters from variables.
230
// for ( int indx = returnParm ? 1 : 0 ; indx < m_Parameters.size() ; indx++ )
232
for ( int indx = 0 ; indx < m_Parameters.size() ; indx++ )
234
QueryParameter parm = (QueryParameter) m_Parameters.elementAt(indx);
239
if (m_InlineVariables)
241
XObject value = (XObject)ctx.getVariableOrParam(new QName(parm.getName()));
248
parm.getType(), 4); // Currently defaulting scale to 4 - should read this!
252
stmt.setNull(indx + 1, parm.getType());
257
String value = parm.getValue();
264
parm.getType(), 4); // Currently defaulting scale to 4 - should read this!
268
stmt.setNull(indx + 1, parm.getType());
274
// if ( ! parm.isOutput() ) throw new SQLException(tx.toString());
280
public void registerOutputParameters(CallableStatement cstmt) throws SQLException
282
// Register output parameters if call.
283
if ( m_IsCallable && m_hasOutput )
285
for ( int indx = 0 ; indx < m_Parameters.size() ; indx++ )
287
QueryParameter parm = (QueryParameter) m_Parameters.elementAt(indx);
288
if ( parm.isOutput() )
290
//System.out.println("chrysalisSQLStatement() Registering output parameter for parm " + indx);
291
cstmt.registerOutParameter(indx + 1, parm.getType());
300
protected void inlineParser()
302
QueryParameter curParm = null;
304
StringBuffer tok = new StringBuffer();
305
boolean firstword = true;
307
if (m_Parameters == null) m_Parameters = new Vector();
309
if (m_ParsedQuery == null) m_ParsedQuery = new StringBuffer();
311
for ( int idx = 0 ; idx < m_OrigQuery.length() ; idx++ )
313
char ch = m_OrigQuery.charAt(idx);
318
if ( ch == '\'' ) state = 1;
319
else if ( ch == '?' ) state = 4;
320
else if ( firstword && (Character.isLetterOrDigit(ch) || ch == '#') )
325
m_ParsedQuery.append(ch);
329
if ( ch == '\'' ) state = 0;
330
else if ( ch == '\\' ) state = 2;
331
m_ParsedQuery.append(ch);
336
m_ParsedQuery.append(ch);
339
case 3: // First word
340
if ( Character.isLetterOrDigit(ch) || ch == '#' || ch == '_' ) tok.append(ch);
343
if ( tok.toString().equalsIgnoreCase("call") )
346
if ( curParm != null )
348
// returnParm = true;
349
curParm.setIsOutput(true);
354
tok = new StringBuffer();
355
if ( ch == '\'' ) state = 1;
356
else if ( ch == '?' ) state = 4;
360
m_ParsedQuery.append(ch);
363
case 4: // Get variable definition
364
if ( ch == '[' ) state = 5;
367
case 5: // Read variable type.
368
if ( !Character.isWhitespace(ch) && ch != '=' )
370
tok.append(Character.toUpperCase(ch));
372
else if ( tok.length() > 0 )
374
// OK we have at least one parameter.
375
m_HasParameters = true;
377
curParm = new QueryParameter();
379
curParm.setTypeName(tok.toString());
380
// curParm.type = map_type(curParm.typeName);
381
m_Parameters.addElement(curParm);
382
tok = new StringBuffer();
383
if ( ch == '=' ) state = 7;
388
case 6: // Look for '='
389
if ( ch == '=' ) state = 7;
392
case 7: // Read variable name.
393
if ( !Character.isWhitespace(ch) && ch != ']' ) tok.append(ch);
394
else if ( tok.length() > 0 )
396
curParm.setName(tok.toString());
397
tok = new StringBuffer();
400
//param_output.addElement(new Boolean(false));
407
case 8: // Look for "OUTput.
408
if ( !Character.isWhitespace(ch) && ch != ']' )
412
else if ( tok.length() > 0 )
415
if ( tok.toString().equalsIgnoreCase("OUT") )
417
curParm.setIsOutput(true);
421
tok = new StringBuffer();
432
// Prepare statement or call.
435
m_ParsedQuery.insert(0, '{');
436
m_ParsedQuery.append('}');