~vcs-imports/xena/trunk

« back to all changes in this revision

Viewing changes to ext/src/xalan-j_2_7_1/src/org/apache/xalan/lib/sql/SQLQueryParser.java

  • Committer: matthewoliver
  • Date: 2009-12-10 03:18:07 UTC
  • Revision ID: vcs-imports@canonical.com-20091210031807-l086qguzdlljtkl9
Merged Xena Testing into Xena Stable for the Xena 5 release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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
 
9
 *
 
10
 *     http://www.apache.org/licenses/LICENSE-2.0
 
11
 *
 
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.
 
17
 */
 
18
/*
 
19
 * $Id: SQLQueryParser.java,v 1.2 2009/12/10 03:18:45 matthewoliver Exp $
 
20
 */
 
21
 
 
22
/**
 
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.
 
26
  *
 
27
 */
 
28
 
 
29
package org.apache.xalan.lib.sql;
 
30
 
 
31
import java.util.*;
 
32
import java.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;
 
37
 
 
38
 
 
39
 
 
40
public class SQLQueryParser
 
41
{
 
42
  /**
 
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.
 
45
   */
 
46
  private boolean           m_InlineVariables  = false;
 
47
 
 
48
  /**
 
49
   *
 
50
   */
 
51
  private boolean           m_IsCallable = false;
 
52
 
 
53
  /**
 
54
   *
 
55
   */
 
56
  private String            m_OrigQuery = null;
 
57
 
 
58
  /**
 
59
   *
 
60
   */
 
61
  private StringBuffer      m_ParsedQuery = null;
 
62
 
 
63
  /**
 
64
   *
 
65
   */
 
66
  private Vector            m_Parameters = null;
 
67
 
 
68
  /**
 
69
   *
 
70
   */
 
71
  private boolean           m_hasOutput = false;
 
72
 
 
73
  /**
 
74
   *
 
75
   */
 
76
  private boolean           m_HasParameters;
 
77
 
 
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;
 
81
 
 
82
  /**
 
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.
 
87
   */
 
88
  public SQLQueryParser()
 
89
  {
 
90
    init();
 
91
  }
 
92
 
 
93
  /**
 
94
   * Constructor, used to create a new parser entry
 
95
   */
 
96
  private SQLQueryParser(String query)
 
97
  {
 
98
    m_OrigQuery = query;
 
99
  }
 
100
 
 
101
  /**
 
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.
 
105
   */
 
106
  private void init()
 
107
  {
 
108
    // Do nothing for now.
 
109
  }
 
110
 
 
111
  /**
 
112
   * Produce an SQL Statement Parser based on the incomming query.
 
113
   *
 
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
 
116
   * String.
 
117
   *
 
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.
 
122
   */
 
123
  public SQLQueryParser parse(XConnection xconn, String query, int override)
 
124
  {
 
125
    SQLQueryParser parser = new SQLQueryParser(query);
 
126
 
 
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);
 
130
 
 
131
    return parser;
 
132
  }
 
133
 
 
134
 
 
135
 
 
136
  /**
 
137
   * Produce an SQL Statement Parser based on the incomming query.
 
138
   *
 
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
 
141
   * String.
 
142
   *
 
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.
 
147
   */
 
148
  private void parse(XConnection xconn, int override)
 
149
  {
 
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.
 
153
 
 
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;
 
157
 
 
158
    if (m_InlineVariables) inlineParser();
 
159
 
 
160
  }
 
161
 
 
162
  /**
 
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.
 
166
   */
 
167
  public boolean hasParameters()
 
168
  {
 
169
    return m_HasParameters;
 
170
  }
 
171
 
 
172
  /**
 
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.
 
176
   *
 
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.
 
181
   */
 
182
  public boolean isCallable()
 
183
  {
 
184
    return m_IsCallable;
 
185
  }
 
186
 
 
187
 
 
188
  /**
 
189
   *
 
190
   */
 
191
  public Vector getParameters()
 
192
  {
 
193
    return m_Parameters;
 
194
  }
 
195
 
 
196
  /**
 
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
 
200
   */
 
201
  public void setParameters(Vector p)
 
202
  {
 
203
    m_HasParameters = true;
 
204
    m_Parameters = p;
 
205
  }
 
206
 
 
207
  /**
 
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.
 
211
   */
 
212
  public String getSQLQuery()
 
213
  {
 
214
    if (m_InlineVariables) return m_ParsedQuery.toString();
 
215
    else return m_OrigQuery;
 
216
  }
 
217
 
 
218
 
 
219
  /**
 
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.
 
225
   *
 
226
   */
 
227
  public void populateStatement(PreparedStatement stmt, ExpressionContext ctx)
 
228
  {
 
229
    // Set input parameters from variables.
 
230
//    for ( int indx = returnParm ? 1 : 0 ; indx < m_Parameters.size() ; indx++ )
 
231
 
 
232
    for ( int indx = 0 ; indx < m_Parameters.size() ; indx++ )
 
233
    {
 
234
      QueryParameter parm = (QueryParameter) m_Parameters.elementAt(indx);
 
235
 
 
236
      try
 
237
      {
 
238
 
 
239
        if (m_InlineVariables)
 
240
        {
 
241
          XObject value = (XObject)ctx.getVariableOrParam(new QName(parm.getName()));
 
242
 
 
243
          if (value != null)
 
244
          {
 
245
            stmt.setObject(
 
246
              indx + 1,
 
247
              value.object(),
 
248
              parm.getType(), 4);       // Currently defaulting scale to 4 - should read this!
 
249
          }
 
250
          else
 
251
          {
 
252
            stmt.setNull(indx + 1, parm.getType());
 
253
          }
 
254
        }
 
255
        else
 
256
        {
 
257
          String value = parm.getValue();
 
258
 
 
259
          if (value != null)
 
260
          {
 
261
            stmt.setObject(
 
262
              indx + 1,
 
263
              value,
 
264
              parm.getType(), 4);       // Currently defaulting scale to 4 - should read this!
 
265
          }
 
266
          else
 
267
          {
 
268
            stmt.setNull(indx + 1, parm.getType());
 
269
          }
 
270
        }
 
271
      }
 
272
      catch (Exception tx)
 
273
      {
 
274
//        if ( ! parm.isOutput() ) throw new SQLException(tx.toString());
 
275
      }
 
276
    }
 
277
 
 
278
  }
 
279
 
 
280
  public void registerOutputParameters(CallableStatement cstmt) throws SQLException
 
281
  {
 
282
    // Register output parameters if call.
 
283
    if ( m_IsCallable && m_hasOutput )
 
284
    {
 
285
      for ( int indx = 0 ; indx < m_Parameters.size() ; indx++ )
 
286
      {
 
287
        QueryParameter parm = (QueryParameter) m_Parameters.elementAt(indx);
 
288
        if ( parm.isOutput() )
 
289
        {
 
290
          //System.out.println("chrysalisSQLStatement() Registering output parameter for parm " + indx);
 
291
          cstmt.registerOutParameter(indx + 1, parm.getType());
 
292
        }
 
293
      }
 
294
    }
 
295
 }
 
296
 
 
297
  /**
 
298
   *
 
299
   */
 
300
  protected void inlineParser()
 
301
  {
 
302
    QueryParameter  curParm = null;
 
303
    int             state = 0;
 
304
    StringBuffer    tok = new StringBuffer();
 
305
    boolean         firstword = true;
 
306
 
 
307
    if (m_Parameters == null) m_Parameters = new Vector();
 
308
 
 
309
    if (m_ParsedQuery == null) m_ParsedQuery = new StringBuffer();
 
310
 
 
311
    for ( int idx = 0 ; idx < m_OrigQuery.length() ; idx++ )
 
312
    {
 
313
      char ch = m_OrigQuery.charAt(idx);
 
314
      switch ( state )
 
315
      {
 
316
 
 
317
        case    0:      // Normal
 
318
          if ( ch == '\'' ) state = 1;
 
319
          else if ( ch == '?' ) state = 4;
 
320
          else if ( firstword && (Character.isLetterOrDigit(ch) || ch == '#') )
 
321
          {
 
322
            tok.append(ch);
 
323
            state = 3;
 
324
          }
 
325
          m_ParsedQuery.append(ch);
 
326
          break;
 
327
 
 
328
        case    1:      // In String
 
329
          if ( ch == '\'' ) state = 0;
 
330
          else if ( ch == '\\' ) state = 2;
 
331
          m_ParsedQuery.append(ch);
 
332
          break;
 
333
 
 
334
        case    2:      // In escape
 
335
          state = 1;
 
336
          m_ParsedQuery.append(ch);
 
337
          break;
 
338
 
 
339
        case    3:      // First word
 
340
          if ( Character.isLetterOrDigit(ch) || ch == '#' || ch == '_' ) tok.append(ch);
 
341
          else
 
342
          {
 
343
            if ( tok.toString().equalsIgnoreCase("call") )
 
344
            {
 
345
              m_IsCallable = true;
 
346
              if ( curParm != null )
 
347
              {
 
348
                // returnParm = true;
 
349
                curParm.setIsOutput(true);
 
350
                // hasOutput = true;
 
351
              }
 
352
            }
 
353
            firstword = false;
 
354
            tok = new StringBuffer();
 
355
            if ( ch == '\'' ) state = 1;
 
356
            else if ( ch == '?' ) state = 4;
 
357
            else state = 0;
 
358
          }
 
359
 
 
360
          m_ParsedQuery.append(ch);
 
361
          break;
 
362
 
 
363
        case    4:      // Get variable definition
 
364
          if ( ch == '[' ) state = 5;
 
365
          break;
 
366
 
 
367
        case    5:      // Read variable type.
 
368
          if ( !Character.isWhitespace(ch) && ch != '=' )
 
369
          {
 
370
            tok.append(Character.toUpperCase(ch));
 
371
          }
 
372
          else if ( tok.length() > 0 )
 
373
          {
 
374
            // OK we have at least one parameter.
 
375
            m_HasParameters = true;
 
376
 
 
377
            curParm = new QueryParameter();
 
378
 
 
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;
 
384
            else state = 6;
 
385
          }
 
386
          break;
 
387
 
 
388
        case    6:      // Look for '='
 
389
          if ( ch == '=' ) state = 7;
 
390
          break;
 
391
 
 
392
        case    7:      // Read variable name.
 
393
          if ( !Character.isWhitespace(ch) && ch != ']' ) tok.append(ch);
 
394
          else if ( tok.length() > 0 )
 
395
          {
 
396
            curParm.setName(tok.toString());
 
397
            tok = new StringBuffer();
 
398
            if ( ch == ']' )
 
399
            {
 
400
              //param_output.addElement(new Boolean(false));
 
401
              state = 0;
 
402
            }
 
403
            else state = 8;
 
404
          }
 
405
          break;
 
406
 
 
407
        case    8:      // Look for "OUTput.
 
408
          if ( !Character.isWhitespace(ch) && ch != ']' )
 
409
          {
 
410
            tok.append(ch);
 
411
          }
 
412
          else if ( tok.length() > 0 )
 
413
          {
 
414
            tok.setLength(3);
 
415
            if ( tok.toString().equalsIgnoreCase("OUT") )
 
416
            {
 
417
              curParm.setIsOutput(true);
 
418
              m_hasOutput = true;
 
419
            }
 
420
 
 
421
            tok = new StringBuffer();
 
422
            if ( ch == ']' )
 
423
            {
 
424
              state = 0;
 
425
            }
 
426
          }
 
427
          break;
 
428
      }
 
429
    }
 
430
 
 
431
 
 
432
    // Prepare statement or call.
 
433
    if ( m_IsCallable )
 
434
    {
 
435
      m_ParsedQuery.insert(0, '{');
 
436
      m_ParsedQuery.append('}');
 
437
    }
 
438
 
 
439
  }
 
440
 
 
441
}
 
442