KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > CompiledStatement


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb;
33
34 import org.hsqldb.HsqlNameManager.HsqlName;
35
36 /**
37  * A simple structure class for holding the products of
38  * statement compilation for later execution.
39  *
40  * @author boucherb@users
41  * @version 1.7.2
42  * @since 1.7.2
43  */

44
45 // fredt@users 20040404 - patch 1.7.2 - fixed type resolution for parameters
46
// boucherb@users 200404xx - patch 1.7.2 - changed parameter naming scheme for SQLCI client usability/support
47
// fredt@users 20050609 - 1.8.0 - fixed EXPLAIN PLAN by implementing describe(Session)
48
final class CompiledStatement {
49
50     static final String JavaDoc PCOL_PREFIX = "@p";
51     static final String JavaDoc RETURN_COLUMN_NAME = "@p0";
52     static final int UNKNOWN = 0;
53
54     // enumeration of allowable CompiledStatement types
55
static final int INSERT_VALUES = 1;
56     static final int INSERT_SELECT = 2;
57     static final int UPDATE = 3;
58     static final int DELETE = 4;
59     static final int SELECT = 5;
60     static final int SELECT_INTO = 6;
61     static final int CALL = 7;
62
63     // enumeration of catagories
64
static final int DML = 7;
65     static final int DQL = 8;
66     static final int DDL = 9;
67
68     /** id in CompiledStatementManager */
69     int id;
70
71     /** false when cleared */
72     boolean isValid = true;
73
74     /** target table for INSERT_XXX, UPDATE and DELETE */
75     Table targetTable;
76
77     /** table filter for UPDATE and DELETE */
78     TableFilter targetFilter;
79
80     /** condition expression for UPDATE and DELETE */
81     Expression condition;
82
83     /** column map for INSERT_XXX, UPDATE */
84     int[] columnMap;
85
86     /** Column value Expressions for INSERT_VALUES and UPDATE. */
87     Expression[] columnValues;
88
89     /**
90      * Flags indicating which columns' values will/will not be
91      * explicitly set.
92      */

93     boolean[] checkColumns;
94
95     /** Expression to be evaluated when this is a CALL statement. */
96     Expression expression;
97
98     /**
99      * Select to be evaluated when this is an INSERT_SELECT or
100      * SELECT statement
101      */

102     Select select;
103
104     /**
105      * Parse-order array of Expression objects, all of iType == PARAM ,
106      * involved in some way in any INSERT_XXX, UPDATE, DELETE, SELECT or
107      * CALL CompiledStatement
108      */

109     Expression[] parameters;
110
111     /**
112      * int[] contains type of each parameter
113      */

114     int[] paramTypes;
115
116     /**
117      * Subqueries inverse parse depth order
118      */

119     SubQuery[] subqueries;
120
121     /**
122      * The type of this CompiledStatement. <p>
123      *
124      * One of: <p>
125      *
126      * <ol>
127      * <li>UNKNOWN
128      * <li>INSERT_VALUES
129      * <li>INSERT_SELECT
130      * <li>UPDATE
131      * <li>DELETE
132      * <li>SELECT
133      * <li>CALL
134      * <li>DDL
135      * </ol>
136      */

137     int type;
138
139     /**
140      * The SQL string that produced this compiled statement
141      */

142     String JavaDoc sql;
143
144     /**
145      * The default schema name used to resolve names in the sql
146      */

147     final HsqlName schemaHsqlName;
148
149     /**
150      * Creates a new instance of CompiledStatement for DDL
151      *
152      */

153     CompiledStatement(HsqlName schema) {
154
155         parameters = new Expression[0];
156         paramTypes = new int[0];
157         subqueries = new SubQuery[0];
158         type = DDL;
159         schemaHsqlName = schema;
160     }
161
162     /**
163      * Initializes this as a DELETE statement
164      *
165      * @param targetFilter
166      * @param deleteCondition
167      * @param parameters
168      */

169     CompiledStatement(Session session, Database database, HsqlName schema,
170                       TableFilter targetFilter, Expression deleteCondition,
171                       SubQuery[] subqueries,
172                       Expression[] params) throws HsqlException {
173
174         schemaHsqlName = schema;
175         this.targetFilter = targetFilter;
176         targetTable = targetFilter.filterTable;
177
178         if (deleteCondition != null) {
179             condition = new Expression(deleteCondition);
180
181             condition.resolveTables(targetFilter);
182             condition.resolveTypes(session);
183             targetFilter.setConditions(session, condition);
184         }
185
186         setParameters(params);
187         setSubqueries(subqueries);
188
189         type = DELETE;
190     }
191
192     /**
193      * Instantiate this as an UPDATE statement.
194      *
195      * @param targetTable
196      * @param columnMap
197      * @param columnValues
198      * @param updateCondition
199      * @param params
200      */

201     CompiledStatement(Session session, Database database, HsqlName schema,
202                       TableFilter targetFilter, int[] columnMap,
203                       Expression[] columnValues, Expression updateCondition,
204                       SubQuery[] subqueries,
205                       Expression[] params) throws HsqlException {
206
207         schemaHsqlName = schema;
208         this.targetFilter = targetFilter;
209         targetTable = targetFilter.filterTable;
210         this.columnMap = columnMap;
211         this.columnValues = columnValues;
212
213         for (int i = 0; i < columnValues.length; i++) {
214             Expression cve = columnValues[i];
215
216             if (cve.isParam()) {
217                 cve.setTableColumnAttributes(targetTable, columnMap[i]);
218             } else {
219                 cve.resolveTables(targetFilter);
220                 cve.resolveTypes(session);
221             }
222         }
223
224         if (updateCondition != null) {
225             condition = new Expression(updateCondition);
226
227             condition.resolveTables(targetFilter);
228             condition.resolveTypes(session);
229             targetFilter.setConditions(session, condition);
230         }
231
232         setParameters(params);
233         setSubqueries(subqueries);
234
235         type = UPDATE;
236     }
237
238     /**
239      * Instantiate this as an INSERT_VALUES statement.
240      *
241      * @param targetTable
242      * @param columnMap
243      * @param columnValues
244      * @param checkColumns
245      * @param params
246      */

247     CompiledStatement(HsqlName schema, Table targetTable, int[] columnMap,
248                       Expression[] columnValues, boolean[] checkColumns,
249                       SubQuery[] subqueries,
250                       Expression[] params) throws HsqlException {
251
252         schemaHsqlName = schema;
253         this.targetTable = targetTable;
254         this.columnMap = columnMap;
255         this.checkColumns = checkColumns;
256         this.columnValues = columnValues;
257
258         for (int i = 0; i < columnValues.length; i++) {
259             Expression cve = columnValues[i];
260
261             // If its not a param, it's already been resolved in
262
// Parser.getColumnValueExpressions
263
if (cve.isParam()) {
264                 cve.setTableColumnAttributes(targetTable, columnMap[i]);
265             }
266         }
267
268         setParameters(params);
269         setSubqueries(subqueries);
270
271         type = INSERT_VALUES;
272     }
273
274     /**
275      * Instantiate this as an INSERT_SELECT statement.
276      *
277      * @param targetTable
278      * @param columnMap
279      * @param checkColumns
280      * @param select
281      * @param params
282      */

283     CompiledStatement(Session session, Database database, HsqlName schema,
284                       Table targetTable, int[] columnMap,
285                       boolean[] checkColumns, Select select,
286                       SubQuery[] subqueries,
287                       Expression[] params) throws HsqlException {
288
289         schemaHsqlName = schema;
290         this.targetTable = targetTable;
291         this.columnMap = columnMap;
292         this.checkColumns = checkColumns;
293         this.select = select;
294
295         // resolve any parameters in SELECT
296
resolveInsertParameterTypes();
297
298         // set select result metadata etc.
299
select.prepareResult(session);
300         setParameters(params);
301         setSubqueries(subqueries);
302
303         type = INSERT_SELECT;
304     }
305
306     /**
307      * Instantiate this as a SELECT statement.
308      *
309      * @param select
310      * @param params
311      */

312     CompiledStatement(Session session, Database database, HsqlName schema,
313                       Select select, SubQuery[] subqueries,
314                       Expression[] params) throws HsqlException {
315
316         schemaHsqlName = schema;
317         this.select = select;
318
319         // resolve any parameters in SELECT as VARCHAR
320
for (int i = 0; i < select.iResultLen; i++) {
321             Expression colexpr = select.exprColumns[i];
322
323             if (colexpr.getDataType() == Types.NULL) {
324                 colexpr.setDataType(Types.VARCHAR);
325             }
326         }
327
328         // set select result metadata etc.
329
select.prepareResult(session);
330         setParameters(params);
331         setSubqueries(subqueries);
332
333         type = SELECT;
334     }
335
336     /**
337      * Instantiate this as a CALL statement.
338      *
339      * @param expression
340      * @param params
341      */

342     CompiledStatement(Session session, Database database, HsqlName schema,
343                       Expression expression, SubQuery[] subqueries,
344                       Expression[] params) throws HsqlException {
345
346         schemaHsqlName = schema;
347         this.expression = expression;
348
349         expression.resolveTypes(session);
350
351         expression.paramMode = Expression.PARAM_OUT;
352
353         setParameters(params);
354         setSubqueries(subqueries);
355
356         type = CALL;
357     }
358
359     /**
360      * For parameters in INSERT_VALUES and INSERT_SELECT lists
361      */

362     private void resolveInsertParameterTypes() {
363
364         for (int i = 0; i < select.iResultLen; i++) {
365             Expression colexpr = select.exprColumns[i];
366
367             if (colexpr.getDataType() == Types.NULL) {
368                 Column col = targetTable.getColumn(columnMap[i]);
369
370                 colexpr.setDataType(col.getType());
371             }
372         }
373     }
374
375     private void setParameters(Expression[] params) {
376
377         this.parameters = params;
378
379         int[] types = new int[parameters.length];
380
381         for (int i = 0; i < parameters.length; i++) {
382             types[i] = parameters[i].getDataType();
383         }
384
385         this.paramTypes = types;
386     }
387
388     private void setSubqueries(SubQuery[] subqueries) {
389         this.subqueries = subqueries;
390     }
391
392     void materializeSubQueries(Session session) throws HsqlException {
393
394         for (int i = 0; i < subqueries.length; i++) {
395             SubQuery sq = subqueries[i];
396
397             // VIEW working table contents are filled only once per query and reused
398
if (sq.isMaterialised) {
399                 continue;
400             }
401
402             if (sq.isResolved) {
403                 sq.populateTable(session);
404
405                 sq.isMaterialised = true;
406             }
407         }
408     }
409
410     void dematerializeSubQueries(Session session) {
411
412         if (subqueries == null) {
413             return;
414         }
415
416         for (int i = 0; i < subqueries.length; i++) {
417             subqueries[i].table.clearAllRows(session);
418
419             subqueries[i].isMaterialised = false;
420         }
421     }
422
423     void clearVariables() {
424
425         isValid = false;
426         targetTable = null;
427         targetFilter = null;
428         condition = null;
429         columnMap = null;
430         columnValues = null;
431         checkColumns = null;
432         expression = null;
433         select = null;
434         parameters = null;
435         paramTypes = null;
436         subqueries = null;
437     }
438
439     boolean canExecute(Session session) throws HsqlException {
440
441         switch (type) {
442
443             case CALL : {}
444             case SELECT :
445                 for (int i = 0; i < select.tFilter.length; i++) {
446                     HsqlName name = select.tFilter[i].filterTable.getName();
447
448                     session.check(name, UserManager.SELECT);
449                 }
450                 break;
451
452             case INSERT_SELECT :
453                 break;
454
455             case DELETE :
456                 session.check(targetTable.getName(), UserManager.DELETE);
457                 break;
458
459             case INSERT_VALUES :
460                 session.check(targetTable.getName(), UserManager.INSERT);
461                 break;
462
463             case UPDATE :
464                 session.check(targetTable.getName(), UserManager.UPDATE);
465                 break;
466
467             case DDL :
468         }
469
470         return true;
471     }
472
473     void checkTableWriteAccess(Session session,
474                                Table table) throws HsqlException {
475
476         // session level user rights
477
session.checkReadWrite();
478
479         // object type
480
if (table.isView()) {
481             throw Trace.error(Trace.NOT_A_TABLE, table.getName().name);
482         }
483
484         // object readonly
485
table.checkDataReadOnly();
486     }
487
488     private static final Result updateCountResult =
489         new Result(ResultConstants.UPDATECOUNT);
490
491     Result describeResult() {
492
493         switch (type) {
494
495             case CALL : {
496
497                 // TODO:
498
//
499
// 1.) standard to register metadata for columns of
500
// the primary result set, if any, generated by call
501
//
502
// 2.) Represent the return value, if any (which is
503
// not, in truth, a result set), as an OUT parameter
504
//
505
// For now, I've reverted a bunch of code I had in place
506
// and instead simply reflect things as the are, describing
507
// a single column result set that communicates
508
// the return value. If the expression generating the
509
// return value has a void return type, a result set
510
// is described whose single column is of type NULL
511
Expression e;
512                 Result r;
513
514                 e = expression;
515                 r = Result.newSingleColumnResult(
516                     CompiledStatement.RETURN_COLUMN_NAME, e.getDataType());
517                 r.metaData.classNames[0] = e.getValueClassName();
518
519                 // no more setup for r; all the defaults apply
520
return r;
521             }
522             case SELECT :
523                 return select.sIntoTable == null ? select.describeResult()
524                                                  : updateCountResult;
525
526             case DELETE :
527             case INSERT_SELECT :
528             case INSERT_VALUES :
529             case UPDATE :
530             case DDL :
531
532                 // will result in
533
return updateCountResult;
534
535             default :
536                 return new Result(
537                     Trace.runtimeError(
538                         Trace.UNSUPPORTED_INTERNAL_OPERATION,
539                         "CompiledStatement.describeResult()"), null);
540         }
541     }
542
543     Result describeParameters() {
544
545         Result out;
546         Expression e;
547         int outlen;
548         int offset;
549         int idx;
550         boolean hasReturnValue;
551
552         outlen = parameters.length;
553         offset = 0;
554
555 // NO: Not yet
556
// hasReturnValue = (type == CALL && !expression.isProcedureCall());
557
//
558
// if (hasReturnValue) {
559
// outlen++;
560
// offset = 1;
561
// }
562
out = Result.newParameterDescriptionResult(outlen);
563
564 // NO: Not yet
565
// if (hasReturnValue) {
566
// e = expression;
567
// out.sName[0] = DIProcedureInfo.RETURN_COLUMN_NAME;
568
// out.sClassName[0] = e.getValueClassName();
569
// out.colType[0] = e.getDataType();
570
// out.colSize[0] = e.getColumnSize();
571
// out.colScale[0] = e.getColumnScale();
572
// out.nullability[0] = e.nullability;
573
// out.isIdentity[0] = false;
574
// out.paramMode[0] = expression.PARAM_OUT;
575
// }
576
for (int i = 0; i < parameters.length; i++) {
577             e = parameters[i];
578             idx = i + offset;
579
580             // always i + 1. We currently use the convention of @p0 to name the
581
// return value OUT parameter
582
out.metaData.colNames[idx] = CompiledStatement.PCOL_PREFIX
583                                          + (i + 1);
584
585             // sLabel is meaningless in this context.
586
out.metaData.classNames[idx] = e.getValueClassName();
587             out.metaData.colTypes[idx] = e.getDataType();
588             out.metaData.colSizes[idx] = e.getColumnSize();
589             out.metaData.colScales[idx] = e.getColumnScale();
590             out.metaData.colNullable[idx] = e.nullability;
591             out.metaData.isIdentity[idx] = e.isIdentity;
592
593             // currently will always be Expression.PARAM_IN
594
out.metaData.paramMode[idx] = e.paramMode;
595         }
596
597         return out;
598     }
599
600     /**
601      * Retrieves a String representation of this object.
602      *
603      * @return the String representation of this object
604      */

605     public String JavaDoc describe(Session session) {
606
607         try {
608             return describeImpl(session);
609         } catch (Exception JavaDoc e) {
610             return e.toString();
611         }
612     }
613
614     /**
615      * Provides the toString() implementation.
616      *
617      * @throws Exception if a database access or io error occurs
618      * @return the String representation of this object
619      */

620     private String JavaDoc describeImpl(Session session) throws Exception JavaDoc {
621
622         StringBuffer JavaDoc sb;
623
624         sb = new StringBuffer JavaDoc();
625
626         switch (type) {
627
628             case SELECT : {
629                 sb.append(select.describe(session));
630                 appendParms(sb).append('\n');
631                 appendSubqueries(sb);
632
633                 return sb.toString();
634             }
635             case INSERT_VALUES : {
636                 sb.append("INSERT VALUES");
637                 sb.append('[').append('\n');
638                 appendColumns(sb).append('\n');
639                 appendTable(sb).append('\n');
640                 appendParms(sb).append('\n');
641                 appendSubqueries(sb).append(']');
642
643                 return sb.toString();
644             }
645             case INSERT_SELECT : {
646                 sb.append("INSERT SELECT");
647                 sb.append('[').append('\n');
648                 appendColumns(sb).append('\n');
649                 appendTable(sb).append('\n');
650                 sb.append(select.describe(session)).append('\n');
651                 appendParms(sb).append('\n');
652                 appendSubqueries(sb).append(']');
653
654                 return sb.toString();
655             }
656             case UPDATE : {
657                 sb.append("UPDATE");
658                 sb.append('[').append('\n');
659                 appendColumns(sb).append('\n');
660                 appendTable(sb).append('\n');
661                 appendCondition(session, sb);
662                 sb.append(targetFilter.describe(session)).append('\n');
663                 appendParms(sb).append('\n');
664                 appendSubqueries(sb).append(']');
665
666                 return sb.toString();
667             }
668             case DELETE : {
669                 sb.append("DELETE");
670                 sb.append('[').append('\n');
671                 appendTable(sb).append('\n');
672                 appendCondition(session, sb);
673                 sb.append(targetFilter.describe(session)).append('\n');
674                 appendParms(sb).append('\n');
675                 appendSubqueries(sb).append(']');
676
677                 return sb.toString();
678             }
679             case CALL : {
680                 sb.append("CALL");
681                 sb.append('[');
682                 sb.append(expression.describe(session)).append('\n');
683                 appendParms(sb).append('\n');
684                 appendSubqueries(sb).append(']');
685
686                 return sb.toString();
687             }
688             default : {
689                 return "UNKNOWN";
690             }
691         }
692     }
693
694     private StringBuffer JavaDoc appendSubqueries(StringBuffer JavaDoc sb) {
695
696         sb.append("SUBQUERIES[");
697
698         for (int i = 0; i < subqueries.length; i++) {
699             sb.append("\n[level=").append(subqueries[i].level).append(
700                 '\n').append("hasParams=").append(
701                 subqueries[i].hasParams).append('\n');
702
703             if (subqueries[i].select != null) {
704                 sb.append("org.hsqldb.Select@").append(
705                     Integer.toHexString(subqueries[i].select.hashCode()));
706             }
707
708             sb.append("]");
709         }
710
711         sb.append(']');
712
713         return sb;
714     }
715
716     private StringBuffer JavaDoc appendTable(StringBuffer JavaDoc sb) {
717
718         sb.append("TABLE[").append(targetTable.getName().name).append(']');
719
720         return sb;
721     }
722
723     private StringBuffer JavaDoc appendColumns(StringBuffer JavaDoc sb) {
724
725         sb.append("COLUMNS=[");
726
727         for (int i = 0; i < columnMap.length; i++) {
728             sb.append('\n').append(columnMap[i]).append(':').append(
729                 ' ').append(
730                 targetTable.getColumn(columnMap[i]).columnName.name).append(
731                 '[').append(columnValues[i]).append(']');
732         }
733
734         sb.append(']');
735
736         return sb;
737     }
738
739     private StringBuffer JavaDoc appendParms(StringBuffer JavaDoc sb) {
740
741         sb.append("PARAMETERS=[");
742
743         for (int i = 0; i < parameters.length; i++) {
744             sb.append('\n').append('@').append(i).append('[').append(
745                 parameters[i]).append(']');
746         }
747
748         sb.append(']');
749
750         return sb;
751     }
752
753     private StringBuffer JavaDoc appendCondition(Session session, StringBuffer JavaDoc sb) {
754
755         return condition == null ? sb.append("CONDITION[]\n")
756                                  : sb.append("CONDITION[").append(
757                                      condition.describe(session)).append(
758                                      "]\n");
759     }
760 }
761
Popular Tags