| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id$ |
| */ |
| |
| /** |
| * This is used by the SQLDocumentHandler for processing JDBC queries. |
| * This prepares JDBC PreparedStatement or CallableStatements and the |
| * input/output of parameters from/to variables. |
| * |
| */ |
| |
| package org.apache.xalan.lib.sql; |
| |
| import java.util.*; |
| import java.sql.*; |
| import org.apache.xpath.objects.*; |
| import org.apache.xalan.extensions.ExpressionContext; |
| import org.apache.xml.utils.QName; |
| import javax.xml.transform.TransformerException; |
| |
| |
| |
| public class SQLQueryParser |
| { |
| /** |
| * If the parser used inline parser to pull out variables then |
| * this will be true. The default is not to use the Inline Parser. |
| */ |
| private boolean m_InlineVariables = false; |
| |
| /** |
| * |
| */ |
| private boolean m_IsCallable = false; |
| |
| /** |
| * |
| */ |
| private String m_OrigQuery = null; |
| |
| /** |
| * |
| */ |
| private StringBuffer m_ParsedQuery = null; |
| |
| /** |
| * |
| */ |
| private Vector m_Parameters = null; |
| |
| /** |
| * |
| */ |
| private boolean m_hasOutput = false; |
| |
| /** |
| * |
| */ |
| private boolean m_HasParameters; |
| |
| public static final int NO_OVERRIDE = 0; |
| public static final int NO_INLINE_PARSER = 1; |
| public static final int INLINE_PARSER = 2; |
| |
| /** |
| * The SQLStatement Parser will be created as a psuedo SINGLETON per |
| * XConnection. Since we are only caching the Query and its parsed results |
| * we may be able to use this as a real SINGLETON. It all depends on how |
| * Statement Caching will play out. |
| */ |
| public SQLQueryParser() |
| { |
| init(); |
| } |
| |
| /** |
| * Constructor, used to create a new parser entry |
| */ |
| private SQLQueryParser(String query) |
| { |
| m_OrigQuery = query; |
| } |
| |
| /** |
| * On a per Xconnection basis, we will create a SQLStatemenetParser, from |
| * this parser, individual parsers will be created. The Init method is defined |
| * to initialize all the internal structures that maintains the pool of parsers. |
| */ |
| private void init() |
| { |
| // Do nothing for now. |
| } |
| |
| /** |
| * Produce an SQL Statement Parser based on the incomming query. |
| * |
| * For now we will just create a new object, in the future we may have this |
| * interface cache the queries so that we can take advantage of a preparsed |
| * String. |
| * |
| * If the Inline Parser is not enabled in the Options, no action will be |
| * taken on the parser. This option can be set by the Stylesheet. If the |
| * option is not set or cleared, a default value will be set determined |
| * by the way variables were passed into the system. |
| */ |
| public SQLQueryParser parse(XConnection xconn, String query, int override) |
| { |
| SQLQueryParser parser = new SQLQueryParser(query); |
| |
| // Try to implement caching here, if we found a parser in the cache |
| // then just return the instance otherwise |
| parser.parse(xconn, override); |
| |
| return parser; |
| } |
| |
| |
| |
| /** |
| * Produce an SQL Statement Parser based on the incomming query. |
| * |
| * For now we will just create a new object, in the future we may have this |
| * interface cache the queries so that we can take advantage of a preparsed |
| * String. |
| * |
| * If the Inline Parser is not enabled in the Options, no action will be |
| * taken on the parser. This option can be set by the Stylesheet. If the |
| * option is not set or cleared, a default value will be set determined |
| * by the way variables were passed into the system. |
| */ |
| private void parse(XConnection xconn, int override) |
| { |
| // Grab the Feature here. We could maintain it from the Parent Parser |
| // but that may cause problems if a single XConnection wants to maintain |
| // both Inline Variable Statemens along with NON inline variable statements. |
| |
| m_InlineVariables = "true".equals(xconn.getFeature("inline-variables")); |
| if (override == NO_INLINE_PARSER) m_InlineVariables = false; |
| else if (override == INLINE_PARSER) m_InlineVariables = true; |
| |
| if (m_InlineVariables) inlineParser(); |
| |
| } |
| |
| /** |
| * If a SQL Statement does not have any parameters, then it can be executed |
| * directly. Most SQL Servers use this as a performance advantage since no |
| * parameters need to be parsed then bound. |
| */ |
| public boolean hasParameters() |
| { |
| return m_HasParameters; |
| } |
| |
| /** |
| * If the Inline Parser is used, the parser will note if this stastement is |
| * a plain SQL Statement or a Called Procedure. Called Procudures generally |
| * have output parameters and require special handling. |
| * |
| * Called Procudures that are not processed with the Inline Parser will |
| * still be executed but under the context of a PreparedStatement and |
| * not a CallableStatement. Called Procudures that have output parameters |
| * MUST be handled with the Inline Parser. |
| */ |
| public boolean isCallable() |
| { |
| return m_IsCallable; |
| } |
| |
| |
| /** |
| * |
| */ |
| public Vector getParameters() |
| { |
| return m_Parameters; |
| } |
| |
| /** |
| * The XConnection will use this method to store the Parameters |
| * that were supplied by the style sheet in the case where the |
| * inline parser was not used |
| */ |
| public void setParameters(Vector p) |
| { |
| m_HasParameters = true; |
| m_Parameters = p; |
| } |
| |
| /** |
| * Return a copy of the parsed SQL query that will be set to the |
| * Database system to execute. If the inline parser was not used, |
| * then the original query will be returned. |
| */ |
| public String getSQLQuery() |
| { |
| if (m_InlineVariables) return m_ParsedQuery.toString(); |
| else return m_OrigQuery; |
| } |
| |
| |
| /** |
| * The SQL Statement Parser, when an Inline Parser is used, tracks the XSL |
| * variables used to populate a statement. The data use to popoulate a |
| * can also be provided. If the data is provided, it will overide the |
| * populastion using XSL variables. When the Inline PArser is not used, then |
| * the Data will always be provided. |
| * |
| */ |
| public void populateStatement(PreparedStatement stmt, ExpressionContext ctx) |
| { |
| // Set input parameters from variables. |
| // for ( int indx = returnParm ? 1 : 0 ; indx < m_Parameters.size() ; indx++ ) |
| |
| for ( int indx = 0 ; indx < m_Parameters.size() ; indx++ ) |
| { |
| QueryParameter parm = (QueryParameter) m_Parameters.elementAt(indx); |
| |
| try |
| { |
| |
| if (m_InlineVariables) |
| { |
| XObject value = (XObject)ctx.getVariableOrParam(new QName(parm.getName())); |
| |
| if (value != null) |
| { |
| stmt.setObject( |
| indx + 1, |
| value.object(), |
| parm.getType(), 4); // Currently defaulting scale to 4 - should read this! |
| } |
| else |
| { |
| stmt.setNull(indx + 1, parm.getType()); |
| } |
| } |
| else |
| { |
| String value = parm.getValue(); |
| |
| if (value != null) |
| { |
| stmt.setObject( |
| indx + 1, |
| value, |
| parm.getType(), 4); // Currently defaulting scale to 4 - should read this! |
| } |
| else |
| { |
| stmt.setNull(indx + 1, parm.getType()); |
| } |
| } |
| } |
| catch (Exception tx) |
| { |
| // if ( ! parm.isOutput() ) throw new SQLException(tx.toString()); |
| } |
| } |
| |
| } |
| |
| public void registerOutputParameters(CallableStatement cstmt) throws SQLException |
| { |
| // Register output parameters if call. |
| if ( m_IsCallable && m_hasOutput ) |
| { |
| for ( int indx = 0 ; indx < m_Parameters.size() ; indx++ ) |
| { |
| QueryParameter parm = (QueryParameter) m_Parameters.elementAt(indx); |
| if ( parm.isOutput() ) |
| { |
| //System.out.println("chrysalisSQLStatement() Registering output parameter for parm " + indx); |
| cstmt.registerOutParameter(indx + 1, parm.getType()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| protected void inlineParser() |
| { |
| QueryParameter curParm = null; |
| int state = 0; |
| StringBuffer tok = new StringBuffer(); |
| boolean firstword = true; |
| |
| if (m_Parameters == null) m_Parameters = new Vector(); |
| |
| if (m_ParsedQuery == null) m_ParsedQuery = new StringBuffer(); |
| |
| for ( int idx = 0 ; idx < m_OrigQuery.length() ; idx++ ) |
| { |
| char ch = m_OrigQuery.charAt(idx); |
| switch ( state ) |
| { |
| |
| case 0: // Normal |
| if ( ch == '\'' ) state = 1; |
| else if ( ch == '?' ) state = 4; |
| else if ( firstword && (Character.isLetterOrDigit(ch) || ch == '#') ) |
| { |
| tok.append(ch); |
| state = 3; |
| } |
| m_ParsedQuery.append(ch); |
| break; |
| |
| case 1: // In String |
| if ( ch == '\'' ) state = 0; |
| else if ( ch == '\\' ) state = 2; |
| m_ParsedQuery.append(ch); |
| break; |
| |
| case 2: // In escape |
| state = 1; |
| m_ParsedQuery.append(ch); |
| break; |
| |
| case 3: // First word |
| if ( Character.isLetterOrDigit(ch) || ch == '#' || ch == '_' ) tok.append(ch); |
| else |
| { |
| if ( tok.toString().equalsIgnoreCase("call") ) |
| { |
| m_IsCallable = true; |
| if ( curParm != null ) |
| { |
| // returnParm = true; |
| curParm.setIsOutput(true); |
| // hasOutput = true; |
| } |
| } |
| firstword = false; |
| tok = new StringBuffer(); |
| if ( ch == '\'' ) state = 1; |
| else if ( ch == '?' ) state = 4; |
| else state = 0; |
| } |
| |
| m_ParsedQuery.append(ch); |
| break; |
| |
| case 4: // Get variable definition |
| if ( ch == '[' ) state = 5; |
| break; |
| |
| case 5: // Read variable type. |
| if ( !Character.isWhitespace(ch) && ch != '=' ) |
| { |
| tok.append(Character.toUpperCase(ch)); |
| } |
| else if ( tok.length() > 0 ) |
| { |
| // OK we have at least one parameter. |
| m_HasParameters = true; |
| |
| curParm = new QueryParameter(); |
| |
| curParm.setTypeName(tok.toString()); |
| // curParm.type = map_type(curParm.typeName); |
| m_Parameters.addElement(curParm); |
| tok = new StringBuffer(); |
| if ( ch == '=' ) state = 7; |
| else state = 6; |
| } |
| break; |
| |
| case 6: // Look for '=' |
| if ( ch == '=' ) state = 7; |
| break; |
| |
| case 7: // Read variable name. |
| if ( !Character.isWhitespace(ch) && ch != ']' ) tok.append(ch); |
| else if ( tok.length() > 0 ) |
| { |
| curParm.setName(tok.toString()); |
| tok = new StringBuffer(); |
| if ( ch == ']' ) |
| { |
| //param_output.addElement(new Boolean(false)); |
| state = 0; |
| } |
| else state = 8; |
| } |
| break; |
| |
| case 8: // Look for "OUTput. |
| if ( !Character.isWhitespace(ch) && ch != ']' ) |
| { |
| tok.append(ch); |
| } |
| else if ( tok.length() > 0 ) |
| { |
| tok.setLength(3); |
| if ( tok.toString().equalsIgnoreCase("OUT") ) |
| { |
| curParm.setIsOutput(true); |
| m_hasOutput = true; |
| } |
| |
| tok = new StringBuffer(); |
| if ( ch == ']' ) |
| { |
| state = 0; |
| } |
| } |
| break; |
| } |
| } |
| |
| |
| // Prepare statement or call. |
| if ( m_IsCallable ) |
| { |
| m_ParsedQuery.insert(0, '{'); |
| m_ParsedQuery.append('}'); |
| } |
| |
| } |
| |
| } |
| |