KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > compile > CurrentOfNode


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.CurrentOfNode
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.compile;
23
24 import org.apache.derby.iapi.services.context.ContextManager;
25
26 import org.apache.derby.iapi.sql.compile.CompilerContext;
27 import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
28 import org.apache.derby.iapi.sql.compile.Optimizer;
29 import org.apache.derby.iapi.sql.compile.CostEstimate;
30 import org.apache.derby.iapi.sql.compile.OptimizableList;
31 import org.apache.derby.iapi.sql.compile.Optimizable;
32 import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
33 import org.apache.derby.iapi.sql.compile.RowOrdering;
34 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
35
36 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
37
38 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
39 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
40 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
41 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
42 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
43 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
44
45 import org.apache.derby.iapi.types.TypeId;
46
47 import org.apache.derby.iapi.sql.execute.ExecCursorTableReference;
48 import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
49
50 import org.apache.derby.iapi.types.DataValueDescriptor;
51 import org.apache.derby.iapi.sql.ResultSet;
52 import org.apache.derby.iapi.sql.Activation;
53
54 import org.apache.derby.iapi.reference.SQLState;
55
56 import org.apache.derby.iapi.sql.execute.CursorResultSet;
57
58 import org.apache.derby.iapi.types.RowLocation;
59
60 import org.apache.derby.iapi.store.access.TransactionController;
61 import org.apache.derby.iapi.reference.ClassName;
62
63 import org.apache.derby.iapi.error.StandardException;
64
65 import org.apache.derby.iapi.services.compiler.MethodBuilder;
66
67 import org.apache.derby.iapi.services.sanity.SanityManager;
68
69 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
70
71 import org.apache.derby.iapi.util.JBitSet;
72 import org.apache.derby.iapi.services.classfile.VMOpcode;
73
74 import java.util.Properties JavaDoc;
75
76 /**
77  * The CurrentOf operator is used by positioned DELETE
78  * and UPDATE to get the current row and location
79  * for the target cursor. The bind() operations for
80  * positioned DELETE and UPDATE add a column to
81  * the select list under the statement for the row location
82  * accessible from this node.
83  *
84  * This node is placed in the from clause of the select
85  * generated for the delete or update operation. It acts
86  * much like a FromBaseTable, using the information about
87  * the target table of the cursor to provide information.
88  *
89  * @author ames
90  */

91 public final class CurrentOfNode extends FromTable {
92
93     private String JavaDoc cursorName;
94     private ExecPreparedStatement preStmt;
95     private TableName exposedTableName;
96     private TableName baseTableName;
97     private CostEstimate singleScanCostEstimate;
98
99     //
100
// initializers
101
//
102
public void init( Object JavaDoc correlationName, Object JavaDoc cursor, Object JavaDoc tableProperties)
103     {
104         super.init(correlationName, tableProperties);
105         cursorName = (String JavaDoc) cursor;
106     }
107
108     /*
109      * Optimizable interface
110      */

111
112     /**
113      * @see Optimizable#estimateCost
114      *
115      * @exception StandardException Thrown on error
116      */

117     public CostEstimate estimateCost(OptimizablePredicateList predList,
118                                     ConglomerateDescriptor cd,
119                                     CostEstimate outerCost,
120                                     Optimizer optimizer,
121                                     RowOrdering rowOrdering)
122             throws StandardException
123     {
124         /*
125         ** Get the cost of a single scan of this result set.
126         **
127         ** Assume for now that the cost of a CURRENT OF is zero, with one row
128         ** fetched. Is this true, and if not, does it make a difference?
129         ** CURRENT OF can only occur when there is only one table in the
130         ** FROM list, and when the only "predicate" is the WHERE CURRENT OF,
131         ** so there's nothing to optimize in this case.
132         */

133         if (singleScanCostEstimate == null)
134         {
135             singleScanCostEstimate = optimizer.newCostEstimate();
136         }
137
138         singleScanCostEstimate.setCost(0.0d, 1.0d, 1.0d);
139         getBestAccessPath().setCostEstimate(singleScanCostEstimate);
140         getBestSortAvoidancePath().setCostEstimate(singleScanCostEstimate);
141
142         return singleScanCostEstimate;
143     }
144
145     //
146
// FromTable interface
147
//
148

149     /**
150      * Binding this FromTable means finding the prepared statement
151      * for the cursor and creating the result columns (the columns
152      * updatable on that cursor).
153      *
154      * We expect someone else to verify that the target table
155      * of the positioned update or delete is the table under this cursor.
156      *
157      * @param dataDictionary The DataDictionary to use for binding
158      * @param fromListParam FromList to use/append to.
159      *
160      * @return ResultSetNode Returns this.
161      *
162      * @exception StandardException Thrown on error
163      */

164     public ResultSetNode bindNonVTITables(DataDictionary dataDictionary,
165                            FromList fromListParam)
166         throws StandardException {
167
168         // verify that the cursor exists
169
// and create a dependency on it
170

171         preStmt = getCursorStatement();
172         if ((preStmt!=null) && (! preStmt.upToDate())) {
173             preStmt.makeValid(getLanguageConnectionContext()); // need to have the query tree
174
if (! preStmt.isValid()) // can't make it valid, say not found
175
preStmt = null;
176         }
177
178         if (preStmt == null) {
179             throw StandardException.newException(SQLState.LANG_CURSOR_NOT_FOUND,
180                         cursorName);
181         }
182         
183
184         // verify that the cursor is updatable (UPDATE is responsible
185
// for checking that the right columns are updatable)
186
if (preStmt.getUpdateMode() != CursorNode.UPDATE)
187         {
188             String JavaDoc printableString = (cursorName == null) ? "" : cursorName;
189             throw StandardException.newException(SQLState.LANG_CURSOR_NOT_UPDATABLE, printableString);
190         }
191
192         getCompilerContext().createDependency(preStmt);
193
194         ExecCursorTableReference refTab = preStmt.getTargetTable();
195         String JavaDoc schemaName = refTab.getSchemaName();
196         exposedTableName = makeTableName(null, refTab.getExposedName());
197         baseTableName = makeTableName(schemaName,
198                                       refTab.getBaseName());
199         SchemaDescriptor tableSchema = null;
200         tableSchema = getSchemaDescriptor(refTab.getSchemaName());
201
202         /*
203         ** This will only happen when we are binding against a publication
204         ** dictionary w/o the schema we are interested in.
205         */

206         if (tableSchema == null)
207         {
208             throw StandardException.newException(SQLState.LANG_SCHEMA_DOES_NOT_EXIST, refTab.getSchemaName());
209         }
210
211         /* Create dependency on target table, in case table not named in
212          * positioned update/delete. Make sure we find the table descriptor,
213          * we may fail to find it if we are binding a publication.
214          */

215         TableDescriptor td = getTableDescriptor(refTab.getBaseName(), tableSchema);
216
217         if (td == null)
218         {
219             throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND, refTab.getBaseName());
220         }
221
222
223         /*
224         ** Add all the result columns from the target table.
225         ** For now, all updatable cursors have all columns
226         ** from the target table. In the future, we should
227         ** relax this so that the cursor may do a partial
228         ** read and then the current of should make sure that
229         ** it can go to the base table to get all of the
230         ** columns needed by the referencing positioned
231         ** DML. In the future, we'll probably need to get
232         ** the result columns from preparedStatement and
233         ** turn them into an RCL that we can run with.
234         */

235         resultColumns = (ResultColumnList) getNodeFactory().getNode(
236                                             C_NodeTypes.RESULT_COLUMN_LIST,
237                                             getContextManager());
238         ColumnDescriptorList cdl = td.getColumnDescriptorList();
239         int cdlSize = cdl.size();
240
241         for (int index = 0; index < cdlSize; index++)
242         {
243             /* Build a ResultColumn/BaseColumnNode pair for the column */
244             ColumnDescriptor colDesc = (ColumnDescriptor) cdl.elementAt(index);
245
246             BaseColumnNode bcn = (BaseColumnNode) getNodeFactory().getNode(
247                                             C_NodeTypes.BASE_COLUMN_NODE,
248                                             colDesc.getColumnName(),
249                                             exposedTableName,
250                                             colDesc.getType(),
251                                             getContextManager());
252             ResultColumn rc = (ResultColumn) getNodeFactory().getNode(
253                                             C_NodeTypes.RESULT_COLUMN,
254                                             colDesc,
255                                             bcn,
256                                             getContextManager());
257
258             /* Build the ResultColumnList to return */
259             resultColumns.addResultColumn(rc);
260         }
261
262         /* Assign the tableNumber */
263         if (tableNumber == -1) // allow re-bind, in which case use old number
264
tableNumber = getCompilerContext().getNextTableNumber();
265
266         return this;
267     }
268
269     /**
270      * Bind the expressions in this ResultSetNode. This means binding the
271      * sub-expressions, as well as figuring out what the return type is for
272      * each expression.
273      *
274      * @param fromListParam FromList to use/append to.
275      */

276     public void bindExpressions(FromList fromListParam)
277     {
278         /* No expressions to bind for a CurrentOfNode.
279          * NOTE - too involved to optimize so that this method
280          * doesn't get called, so just do nothing.
281          */

282     }
283
284     /**
285      * Try to find a ResultColumn in the table represented by this CurrentOfNode
286      * that matches the name in the given ColumnReference.
287      *
288      * @param columnReference The columnReference whose name we're looking
289      * for in the given table.
290      *
291      * @return A ResultColumn whose expression is the ColumnNode
292      * that matches the ColumnReference.
293      * Returns null if there is no match.
294      *
295      * @exception StandardException Thrown on error
296      */

297
298     public ResultColumn getMatchingColumn(ColumnReference columnReference)
299                         throws StandardException {
300
301         ResultColumn resultColumn = null;
302         TableName columnsTableName;
303
304         columnsTableName = columnReference.getTableNameNode();
305
306         if(columnsTableName != null)
307             if(columnsTableName.getSchemaName() == null && correlationName == null)
308                 columnsTableName.bind(this.getDataDictionary());
309
310         if (SanityManager.DEBUG)
311         {
312             SanityManager.ASSERT(preStmt!=null, "must have prepared statement");
313         }
314
315         /*
316          * We use the base table name of the target table.
317          * This is necessary since we will be comparing with the table in
318          * the delete or update statement which doesn't have a correlation
319          * name. The select for which this column is created might have a
320          * correlation name and so we won't find it if we look for exposed names
321          * We shouldn't have to worry about multiple table since there should be
322          * only one table. Beetle 4419
323          */

324         if (SanityManager.DEBUG)
325         {
326             SanityManager.ASSERT(baseTableName!=null,"no name on target table");
327         }
328
329         if(baseTableName != null)
330             if(baseTableName.getSchemaName() == null && correlationName == null)
331                 baseTableName.bind(this.getDataDictionary());
332
333         /*
334          * If the column did not specify a name, or the specified name
335          * matches the table we're looking at, see whether the column
336          * is in this table, and also whether it is in the for update list.
337         */

338         if (
339                (columnsTableName == null) ||
340                (columnsTableName.getFullTableName().equals(baseTableName.getFullTableName())) ||
341                ((correlationName != null) && correlationName.equals( columnsTableName.getTableName()))
342            )
343         {
344             boolean notfound = false;
345
346             resultColumn =
347                 resultColumns.getResultColumn(columnReference.getColumnName());
348
349             if (resultColumn != null)
350             {
351                 // If we found the ResultColumn, set the ColumnReference's
352
// table number accordingly. Note: we used to only set
353
// the tableNumber for correlated references (as part of
354
// changes for DERBY-171) but inspection of code (esp.
355
// the comments in FromList.bindColumnReferences() and
356
// the getMatchingColumn() methods on other FromTables)
357
// suggests that we should always set the table number
358
// if we've found the ResultColumn. So we do that here.
359
columnReference.setTableNumber( tableNumber );
360
361                 // If there is a result column, are we really updating it?
362
// If so, verify that the column is updatable as well
363
notfound =
364                     (resultColumn.updatableByCursor() &&
365                     !foundString(
366                             preStmt.getUpdateColumns(),
367                             columnReference.getColumnName()));
368             }
369             else
370             {
371                 notfound = true;
372             }
373
374             if (notfound)
375             {
376                 String JavaDoc printableString = (cursorName == null) ? "" : cursorName;
377                 throw StandardException.newException(SQLState.LANG_COLUMN_NOT_UPDATABLE_IN_CURSOR,
378                          columnReference.getColumnName(), printableString);
379             }
380         }
381
382         return resultColumn;
383     }
384
385     /**
386      * Preprocess a CurrentOfNode. For a CurrentOfNode, this simply means allocating
387      * a referenced table map to avoid downstream NullPointerExceptions.
388      * NOTE: There are no bits set in the referenced table map.
389      *
390      * @param numTables The number of tables in the DML Statement
391      * @param gbl The group by list, if any
392      * @param fromList The from list, if any
393      *
394      * @return ResultSetNode at top of preprocessed tree.
395      *
396      * @exception StandardException Thrown on error
397      */

398
399     public ResultSetNode preprocess(int numTables,
400                                     GroupByList gbl,
401                                     FromList fromList)
402                                 throws StandardException
403     {
404         /* Generate an empty referenced table map */
405         referencedTableMap = new JBitSet(numTables);
406         return this;
407     }
408
409     /**
410      * Optimize this CurrentOfNode. Nothing to do.
411      *
412      * @param dataDictionary The DataDictionary to use for optimization
413      * @param predicateList The PredicateList to optimize. This should
414      * be a single-table predicate with the table
415      * the same as the table in this FromTable.
416      * @param outerRows The number of outer joining rows
417      *
418      * @return ResultSetNode The top of the optimized subtree.
419      *
420      * @exception StandardException Thrown on error
421      */

422     public ResultSetNode optimize(DataDictionary dataDictionary,
423                          PredicateList predicateList,
424                          double outerRows)
425                         throws StandardException {
426         /* Get an optimizer so we can get a cost */
427         Optimizer optimizer = getOptimizer(
428                                 (FromList) getNodeFactory().getNode(
429                                     C_NodeTypes.FROM_LIST,
430                                     getNodeFactory().doJoinOrderOptimization(),
431                                     this,
432                                     getContextManager()),
433                                 predicateList,
434                                 dataDictionary,
435                                 (RequiredRowOrdering) null);
436
437         /* Assume there is no cost associated with fetching the current row */
438         bestCostEstimate = optimizer.newCostEstimate();
439         bestCostEstimate.setCost(0.0d, outerRows, outerRows);
440
441         return this;
442     }
443
444     /**
445      * Generation on a CurrentOfNode creates a scan on the
446      * cursor, CurrentOfResultSet.
447      * <p>
448      * This routine will generate and return a call of the form:
449      * <pre><verbatim>
450         ResultSetFactory.getCurrentOfResultSet(cursorName)
451        </verbatim></pre>
452      *
453      * @param acb The ActivationClassBuilder for the class being built
454      * @param mb The execute() method to be built
455      *
456      * @exception StandardException Thrown on error
457      */

458     public void generate(ActivationClassBuilder acb,
459                                 MethodBuilder mb)
460                             throws StandardException {
461
462         if (SanityManager.DEBUG)
463         SanityManager.ASSERT(!statementResultSet,
464             "CurrentOfNode not expected to be statement node");
465
466         /* Get the next ResultSet #, so that we can number this ResultSetNode, its
467          * ResultColumnList and ResultSet.
468          */

469         assignResultSetNumber();
470
471         mb.pushThis(); // for the putField
472

473         // The generated java returned by this method is the expression:
474
// ResultSetFactory.getCurrentOfResultSet(
475
// #cursorName(), this, resultSetNumber)
476

477         acb.pushGetResultSetFactoryExpression(mb);
478
479           mb.push(cursorName);
480           acb.pushThisAsActivation(mb);
481           mb.push(resultSetNumber);
482           mb.push(preStmt.getObjectName());
483         
484         mb.callMethod(VMOpcode.INVOKEINTERFACE, (String JavaDoc) null, "getCurrentOfResultSet",
485                         ClassName.NoPutResultSet, 4);
486
487         mb.cast(ClassName.CursorResultSet);
488
489         // the current of scan generator is what we return
490
/* This table is the target of an update or a delete, so we must
491          * wrap the Expression up in an assignment expression before
492          * returning. Delete or update use the field that is set
493          * to calculate the CurrentRowLocation value.
494          * NOTE - scanExpress is a ResultSet. We will need to cast it to the
495          * appropriate subclass.
496          * For example, for a DELETE, instead of returning a call to the
497          * ResultSetFactory, we will generate and return:
498          * this.SCANRESULTSET = (cast to appropriate ResultSet type)
499          * The outer cast back to ResultSet is needed so that
500          * we invoke the appropriate method in the call to the ResultSetFactory
501          */

502
503         mb.putField((String JavaDoc) null, acb.getRowLocationScanResultSetName(), ClassName.CursorResultSet);
504         mb.cast(ClassName.NoPutResultSet);
505
506         // add a check at activation reset time to see if the cursor has
507
// changed underneath us. Doing it in the constructor allows the
508
// compilation to happen
509
MethodBuilder rmb = acb.startResetMethod();
510
511         rmb.pushThis();
512         rmb.push(cursorName);
513         rmb.push(preStmt.getObjectName());
514         rmb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "checkPositionedStatement",
515                         "void", 2);
516
517         rmb.methodReturn();
518         rmb.complete();
519     }
520
521     /**
522      * Prints the sub-nodes of this object. See QueryTreeNode.java for
523      * how tree printing is supposed to work.
524      *
525      * @param depth The depth of this node in the tree
526      */

527     public void printSubNodes(int depth) {
528         if (SanityManager.DEBUG) {
529             super.printSubNodes(depth);
530
531             printLabel(depth, "cursor: ");
532         }
533     }
534
535     /**
536      * Convert this object to a String. See comments in QueryTreeNode.java
537      * for how this should be done for tree printing.
538      *
539      * @return This object as a String
540      */

541     public String JavaDoc toString() {
542         if (SanityManager.DEBUG) {
543             return "preparedStatement: " +
544                 (preStmt == null? "no prepared statement yet\n" :
545                 preStmt.toString() + "\n")+
546                 cursorName + "\n" +
547                 super.toString();
548         } else {
549             return "";
550         }
551     }
552
553     //
554
// class interface
555
//
556

557     public String JavaDoc getExposedName()
558     {
559         return exposedTableName.getFullTableName();
560     }
561     public TableName getExposedTableName()
562     {
563         return exposedTableName;
564     }
565
566     public TableName getBaseCursorTargetTableName()
567     {
568         return baseTableName;
569     }
570
571     public String JavaDoc getCursorName()
572     {
573         return cursorName;
574     }
575
576     /**
577      * Return the CursorNode associated with a positioned update/delete.
578      *
579      * @return CursorNode The associated CursorNode.
580      *
581      */

582     ExecPreparedStatement getCursorStatement()
583     {
584         Activation activation = getLanguageConnectionContext().lookupCursorActivation(cursorName);
585
586         if (activation == null)
587             return null;
588
589         return activation.getPreparedStatement();
590     }
591
592     /**
593      * Get the lock mode for this table as the target of an update statement
594      * (a delete or update). This is implemented only for base tables and
595      * CurrentOfNodes.
596      *
597      * @see TransactionController
598      *
599      * @return The lock mode
600      */

601     public int updateTargetLockMode()
602     {
603         /* Do row locking for positioned update/delete */
604         return TransactionController.MODE_RECORD;
605     }
606 }
607
Popular Tags