KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.compile.CursorNode
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.C_NodeTypes;
27
28 import org.apache.derby.iapi.sql.conn.Authorizer;
29 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
30
31 import org.apache.derby.iapi.error.StandardException;
32 import org.apache.derby.iapi.reference.SQLState;
33
34 import org.apache.derby.iapi.sql.ResultColumnDescriptor;
35
36 import org.apache.derby.iapi.sql.dictionary.DataDictionary;
37 import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
38 import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
39 import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
40
41 import org.apache.derby.iapi.services.sanity.SanityManager;
42
43 import org.apache.derby.iapi.services.compiler.MethodBuilder;
44
45 import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
46
47 import org.apache.derby.impl.sql.CursorInfo;
48 import org.apache.derby.impl.sql.CursorTableReference;
49
50 import java.util.ArrayList JavaDoc;
51 import java.util.Vector JavaDoc;
52
53 /**
54  * A CursorNode represents a result set that can be returned to a client.
55  * A cursor can be a named cursor created by the DECLARE CURSOR statement,
56  * or it can be an unnamed cursor associated with a SELECT statement (more
57  * precisely, a table expression that returns rows to the client). In the
58  * latter case, the cursor does not have a name.
59  *
60  * @author Jeff Lichtman
61  */

62
63 public class CursorNode extends DMLStatementNode
64 {
65     public final static int UNSPECIFIED = 0;
66     public final static int READ_ONLY = 1;
67     public final static int UPDATE = 2;
68
69     private String JavaDoc name;
70     private OrderByList orderByList;
71     private String JavaDoc statementType;
72     private int updateMode;
73     private boolean needTarget;
74
75     /**
76     ** There can only be a list of updatable columns when FOR UPDATE
77     ** is specified as part of the cursor specification.
78     */

79     private Vector JavaDoc updatableColumns;
80     private FromTable updateTable;
81     private ResultColumnDescriptor[] targetColumnDescriptors;
82
83     //If cursor references session schema tables, save the list of those table names in savedObjects in compiler context
84
//Following is the position of the session table names list in savedObjects in compiler context
85
//At generate time, we save this position in activation for easy access to session table names list from compiler context
86
private int indexOfSessionTableNamesInSavedObjects = -1;
87
88     /**
89      * Initializer for a CursorNode
90      *
91      * @param statementType Type of statement (SELECT, UPDATE, INSERT)
92      * @param resultSet A ResultSetNode specifying the result set for
93      * the cursor
94      * @param name The name of the cursor, null if no name
95      * @param orderByList The order by list for the cursor, null if no
96      * order by list
97      * @param updateMode The user-specified update mode for the cursor,
98      * for example, CursorNode.READ_ONLY
99      * @param updatableColumns The list of updatable columns specified by
100      * the user in the FOR UPDATE clause, null if no
101      * updatable columns specified. May only be
102      * provided if the updateMode parameter is
103      * CursorNode.UPDATE.
104      */

105
106     public void init(
107         Object JavaDoc statementType,
108         Object JavaDoc resultSet,
109         Object JavaDoc name,
110         Object JavaDoc orderByList,
111         Object JavaDoc updateMode,
112         Object JavaDoc updatableColumns)
113     {
114         init(resultSet);
115         this.name = (String JavaDoc) name;
116         this.statementType = (String JavaDoc) statementType;
117         this.orderByList = (OrderByList) orderByList;
118
119         this.updateMode = ((Integer JavaDoc) updateMode).intValue();
120         this.updatableColumns = (Vector JavaDoc) updatableColumns;
121
122         /*
123         ** This is a sanity check and not an error since the parser
124         ** controls setting updatableColumns and updateMode.
125         */

126         if (SanityManager.DEBUG)
127         SanityManager.ASSERT(this.updatableColumns == null ||
128             this.updatableColumns.size() == 0 || this.updateMode == UPDATE,
129             "Can only have explicit updatable columns if update mode is UPDATE");
130     }
131
132     /**
133      * Convert this object to a String. See comments in QueryTreeNode.java
134      * for how this should be done for tree printing.
135      *
136      * @return This object as a String
137      */

138
139     public String JavaDoc toString()
140     {
141         if (SanityManager.DEBUG)
142         {
143             return "name: " + name + "\n" +
144                 "updateMode: " + updateModeString(updateMode) + "\n" +
145                 super.toString();
146         }
147         else
148         {
149             return "";
150         }
151     }
152
153     public String JavaDoc statementToString()
154     {
155         return statementType;
156     }
157
158     /**
159      * Support routine for translating an updateMode identifier to a String
160      *
161      * @param updateMode An updateMode identifier
162      *
163      * @return A String representing the update mode.
164      */

165
166     private static String JavaDoc updateModeString(int updateMode)
167     {
168         if (SanityManager.DEBUG)
169         {
170             switch (updateMode)
171             {
172               case UNSPECIFIED:
173                 return "UNSPECIFIED (" + UNSPECIFIED + ")";
174
175               case READ_ONLY:
176                 return "READ_ONLY (" + READ_ONLY + ")";
177
178               case UPDATE:
179                 return "UPDATE (" + UPDATE + ")";
180
181               default:
182                 return "UNKNOWN VALUE (" + updateMode + ")";
183             }
184         }
185         else
186         {
187             return "";
188         }
189     }
190
191     /**
192      * Prints the sub-nodes of this object. See QueryTreeNode.java for
193      * how tree printing is supposed to work.
194      *
195      * @param depth The depth of this node in the tree
196      */

197
198     public void printSubNodes(int depth)
199     {
200         if (SanityManager.DEBUG)
201         {
202             super.printSubNodes(depth);
203
204             printLabel(depth, "orderByList: ");
205             if (orderByList != null)
206                 orderByList.treePrint(depth + 1);
207         }
208     }
209
210     /**
211      * Bind this CursorNode. This means looking up tables and columns and
212      * getting their types, and figuring out the result types of all
213      * expressions, as well as doing view resolution, permissions checking,
214      * etc. It also includes determining whether an UNSPECIFIED cursor
215      * is updatable or not, and verifying that an UPDATE cursor actually is.
216      *
217      * @return The bound query tree
218      *
219      * @exception StandardException Thrown on error
220      */

221
222     public QueryTreeNode bind() throws StandardException
223     {
224         DataDictionary dataDictionary;
225
226         dataDictionary = getDataDictionary();
227
228         // This is how we handle queries like: SELECT A FROM T ORDER BY B.
229
// We pull up the order by columns (if they don't appear in the SELECT
230
// LIST) and let the bind() do the job. Note that the pullup is done
231
// before the bind() and we may avoid pulling up ORDERBY columns that
232
// would otherwise be avoided, e.g., "SELECT * FROM T ORDER BY B".
233
// Pulled-up ORDERBY columns that are duplicates (like the above "SELECT
234
// *" query will be removed in bindOrderByColumns().
235
// Finally, given that extra columns may be added to the SELECT list, we
236
// inject a ProjectRestrictNode so that only the user-specified columns
237
// will be returned (see genProjectRestrict() in SelectNode.java).
238
if (orderByList != null)
239         {
240             orderByList.pullUpOrderByColumns(resultSet);
241         }
242
243         getCompilerContext().pushCurrentPrivType(getPrivType());
244         try {
245             FromList fromList = (FromList) getNodeFactory().getNode(
246                     C_NodeTypes.FROM_LIST,
247                     getNodeFactory().doJoinOrderOptimization(),
248                     getContextManager());
249
250             /* Check for ? parameters directly under the ResultColums */
251             resultSet.rejectParameters();
252
253             super.bind(dataDictionary);
254
255             // bind the query expression
256
resultSet.bindResultColumns(fromList);
257
258             // this rejects any untyped nulls in the select list
259
// pass in null to indicate that we don't have any
260
// types for this node
261
resultSet.bindUntypedNullsToResultColumns(null);
262
263             // Reject any XML values in the select list; JDBC doesn't
264
// define how we bind these out, so we don't allow it.
265
resultSet.rejectXMLValues();
266
267             /* Verify that all underlying ResultSets reclaimed their FromList */
268             if (SanityManager.DEBUG) {
269                 SanityManager.ASSERT(fromList.size() == 0,
270                     "fromList.size() is expected to be 0, not "
271                             + fromList.size()
272                             + " on return from RS.bindExpressions()");
273             }
274         }
275         finally
276         {
277             getCompilerContext().popCurrentPrivType();
278         }
279
280         // bind the order by
281
if (orderByList != null)
282         {
283             orderByList.bindOrderByColumns(resultSet);
284         }
285
286         // bind the updatability
287

288         // if it says it is updatable, verify it.
289
if (updateMode == UPDATE)
290         {
291             int checkedUpdateMode;
292
293             checkedUpdateMode = determineUpdateMode(dataDictionary);
294             if (SanityManager.DEBUG)
295             SanityManager.DEBUG("DumpUpdateCheck","update mode is UPDATE ("+updateMode+") checked mode is "+checkedUpdateMode);
296             if (updateMode != checkedUpdateMode)
297                     throw StandardException.newException(SQLState.LANG_STMT_NOT_UPDATABLE);
298         }
299
300         // if it doesn't know if it is updatable, determine it
301
if (updateMode == UNSPECIFIED)
302         {
303             // If the statement is opened with CONCUR_READ_ONLY, the upgrade mode is
304
// set to read only.
305

306             // NOTE: THIS IS NOT COMPATIBLE WITH THE ISO/ANSI SQL STANDARD.
307

308             // According to the SQL-standard:
309
// If updatability is not specified, a SELECT * FROM T will be implicitely
310
// read only in the context of a cursor which is insensitive, scrollable or
311
// have an order by clause. Otherwise it is implicitely updatable.
312

313             // In Derby, we make a SELECT * FROM T updatable if the concurrency mode is
314
// ResultSet.CONCUR_UPDATE. If we do make all SELECT * FROM T updatable
315
// by default, we cannot use an index on any single-table select, unless it
316
// was declared FOR READ ONLY. This would be pretty terrible, so we are
317
// breaking the ANSI rules.
318

319             if (getLanguageConnectionContext().getStatementContext().isForReadOnly()) {
320             updateMode = READ_ONLY;
321             } else {
322             updateMode = determineUpdateMode(dataDictionary);
323             }
324                         
325             //if (SanityManager.DEBUG)
326
//SanityManager.DEBUG("DumpUpdateCheck","update mode is UNSPECIFIED ("+UNSPECIFIED+") checked mode is "+updateMode);
327
}
328         
329         if (updateMode == READ_ONLY) {
330             updatableColumns = null; // don't need them any more
331
}
332
333         // bind the update columns
334
if (updateMode == UPDATE)
335         {
336             bindUpdateColumns(updateTable);
337
338             // If the target table is a FromBaseTable, mark the updatable
339
// columns. (I can't think of a way that an updatable table
340
// could be anything but a FromBaseTable at this point, but
341
// it's better to be careful.
342
if (updateTable instanceof FromTable)
343             {
344                 ((FromTable) updateTable).markUpdatableByCursor(updatableColumns);
345                 //make sure that alongwith the FromTable, we keep other ResultSetLists
346
//in correct state too. ResultSetMetaData.isWritable looks at this to
347
//return the correct value.
348
resultSet.getResultColumns().markColumnsInSelectListUpdatableByCursor(
349                     updatableColumns);
350             }
351         }
352
353         resultSet.renameGeneratedResultNames();
354
355         //need to look for SESSION tables only if global temporary tables declared for the connection
356
if (getLanguageConnectionContext().checkIfAnyDeclaredGlobalTempTablesForThisConnection())
357         {
358             //If this cursor has references to session schema tables, save the names of those tables into compiler context
359
//so they can be passed to execution phase.
360
ArrayList JavaDoc sessionSchemaTableNames = getSessionSchemaTableNamesForCursor();
361             if (sessionSchemaTableNames != null)
362                 indexOfSessionTableNamesInSavedObjects = getCompilerContext().addSavedObject(sessionSchemaTableNames);
363         }
364
365         return this;
366     }
367
368     /**
369      * Return true if the node references SESSION schema tables (temporary or permanent)
370      *
371      * @return true if references SESSION schema tables, else false
372      *
373      * @exception StandardException Thrown on error
374      */

375     public boolean referencesSessionSchema()
376         throws StandardException
377     {
378         //If this node references a SESSION schema table, then return true.
379
return resultSet.referencesSessionSchema();
380     }
381
382     //Check if this cursor references any session schema tables. If so, pass those names to execution phase through savedObjects
383
//This list will be used to check if there are any holdable cursors referencing temporary tables at commit time.
384
//If yes, then the data in those temporary tables should be preserved even if they are declared with ON COMMIT DELETE ROWS option
385
protected ArrayList JavaDoc getSessionSchemaTableNamesForCursor()
386         throws StandardException
387     {
388         FromList fromList = resultSet.getFromList();
389         int fromListSize = fromList.size();
390         FromTable fromTable;
391         ArrayList JavaDoc sessionSchemaTableNames = null;
392
393         for( int i = 0; i < fromListSize; i++)
394         {
395             fromTable = (FromTable) fromList.elementAt(i);
396             if (fromTable instanceof FromBaseTable && isSessionSchema(fromTable.getTableDescriptor().getSchemaDescriptor()))
397             {
398                 if (sessionSchemaTableNames == null)
399                     sessionSchemaTableNames = new ArrayList JavaDoc();
400                 sessionSchemaTableNames.add(fromTable.getTableName().getTableName());
401             }
402         }
403
404         return sessionSchemaTableNames;
405     }
406
407     /**
408      * Take a cursor and determine if it is UPDATE
409      * or READ_ONLY based on the shape of the cursor specification.
410      * <p>
411      * The following conditions make a cursor read only:
412      * <UL>
413      * <LI>if it says FOR READ ONLY
414      * <LI>if it says ORDER BY
415      * <LI>if its query specification is not read only. At present this
416      * is explicitly tested here, with these conditions. At some future
417      * point in time, this checking ought to be moved into the
418      * ResultSet nodes themselves. The conditions for a query spec.
419      * not to be read only include:
420      * <UL>
421      * <LI>if it has a set operation such as UNION or INTERSECT, i.e.
422      * does not have a single outermost SELECT
423      * <LI>if it does not have exactly 1 table in its FROM list;
424      * 0 tables would occur if we ever support a SELECT without a
425      * FROM e.g., for generating a row without an underlying table
426      * (like what we do for an INSERT of a VALUES list); >1 tables
427      * occurs when joins are in the tree.
428      * <LI>if the table in its FROM list is not a base table (REMIND
429      * when views/from subqueries are added, this should be relaxed to
430      * be that the table is not updatable)
431      * <LI>if it has a GROUP BY or HAVING (NOTE I am assuming that if
432      * and aggregate is detected in a SELECT w/o a GROUP BY, one
433      * has been added to show that the whole table is a group)
434      * <LI> NOTE that cursors are updatable even if none of the columns
435      * in the select are updatable -- what they care about is the
436      * updatability of the columns of the target table.
437      * </UL>
438      * </UL>
439      *
440      * @return the known update mode for the cursor.
441      *
442      * @exception StandardException Thrown on error
443      */

444     private int determineUpdateMode(DataDictionary dataDictionary)
445         throws StandardException
446     {
447         SelectNode selectNode;
448         FromList tables;
449         FromTable targetTable;
450
451         if (updateMode == READ_ONLY)
452         {
453             return READ_ONLY;
454         }
455
456         if (orderByList != null)
457         {
458             if (SanityManager.DEBUG)
459             SanityManager.DEBUG("DumpUpdateCheck","cursor has order by");
460             return READ_ONLY;
461         }
462
463         // get the ResultSet to tell us what it thinks it is
464
// and the target table
465
if (! resultSet.isUpdatableCursor(dataDictionary))
466         {
467             return READ_ONLY;
468         }
469
470         // The FOR UPDATE clause has two uses:
471
//
472
// for positioned cursor updates
473
//
474
// to change locking behaviour of the select
475
// to reduce deadlocks on subsequent updates
476
// in the same transaction.
477
//
478
// We now support this latter case, without requiring
479
// that the source of the rows be able to implement
480
// a positioned update.
481

482         updateTable = resultSet.getCursorTargetTable();
483
484         /* Tell the table that it is the cursor target */
485         if (updateTable.markAsCursorTargetTable()) {
486             /* Cursor is updatable - remember to generate the position code */
487             needTarget = true;
488
489             /* We must generate the target column list at bind time
490              * because the optimizer may transform the FromBaseTable from
491              * a table scan into an index scan.
492              */

493             genTargetResultColList();
494         }
495
496
497
498
499         return UPDATE;
500     }
501
502     /**
503      * Optimize a DML statement (which is the only type of statement that
504      * should need optimizing, I think). This method over-rides the one
505      * in QueryTreeNode.
506      *
507      * This method takes a bound tree, and returns an optimized tree.
508      * It annotates the bound tree rather than creating an entirely
509      * new tree.
510      *
511      * Throws an exception if the tree is not bound, or if the binding
512      * is out of date.
513      *
514      * @return An optimized QueryTree
515      *
516      * @exception StandardException Thrown on error
517      */

518
519     public QueryTreeNode optimize() throws StandardException
520     {
521         // Push the order by list down to the ResultSet
522
if (orderByList != null)
523         {
524             // If we have more than 1 ORDERBY columns, we may be able to
525
// remove duplicate columns, e.g., "ORDER BY 1, 1, 2".
526
if (orderByList.size() > 1)
527             {
528                 orderByList.removeDupColumns();
529             }
530
531             resultSet.pushOrderByList(orderByList);
532             orderByList = null;
533         }
534         return super.optimize();
535     }
536
537     /**
538      * Returns the type of activation this class
539      * generates.
540      *
541      * @return either (NEED_CURSOR_ACTIVATION
542      *
543      * @exception StandardException Thrown on error
544      */

545      
546     int activationKind()
547     {
548         return NEED_CURSOR_ACTIVATION;
549     }
550
551     /**
552      * Do code generation for this CursorNode
553      *
554      * @param acb The ActivationClassBuilder for the class being built
555      * @param mb The method the generated code is to go into
556      *
557      * @exception StandardException Thrown on error
558      */

559
560     public void generate(ActivationClassBuilder acb,
561                                 MethodBuilder mb) throws StandardException
562     {
563         if (indexOfSessionTableNamesInSavedObjects != -1 ) //if this cursor references session schema tables, do following
564
{
565             MethodBuilder constructor = acb.getConstructor();
566             constructor.pushThis();
567             constructor.push(indexOfSessionTableNamesInSavedObjects);
568             constructor.putField(org.apache.derby.iapi.reference.ClassName.BaseActivation, "indexOfSessionTableNamesInSavedObjects", "int");
569             constructor.endStatement();
570     }
571
572         // generate the parameters
573
generateParameterValueSet(acb);
574
575         // tell the outermost result set that it is the outer
576
// result set of the statement.
577
resultSet.markStatementResultSet();
578
579         generateAuthorizeCheck(acb, mb,
580                 org.apache.derby.iapi.sql.conn.Authorizer.SQL_SELECT_OP);
581
582         // this will generate an expression that will be a ResultSet
583
resultSet.generate(acb, mb);
584
585         /*
586         ** Generate the position code if this cursor is updatable. This
587         ** involves generating methods to get the cursor result set, and
588         ** the target result set (which is for the base row). Also,
589         ** generate code to store the cursor result set in a generated
590         ** field.
591         */

592         if (needTarget)
593         {
594             // PUSHCOMPILE - could be put into a single method
595
acb.rememberCursor(mb);
596             acb.addCursorPositionCode();
597         }
598     }
599
600     // class interface
601

602     public String JavaDoc getUpdateBaseTableName()
603     {
604         return (updateTable == null) ? null : updateTable.getBaseTableName();
605     }
606
607     public String JavaDoc getUpdateExposedTableName()
608         throws StandardException
609     {
610         return (updateTable == null) ? null : updateTable.getExposedName();
611     }
612
613     public String JavaDoc getUpdateSchemaName()
614         throws StandardException
615     {
616         //we need to use the base table for the schema name
617
return (updateTable == null) ? null : ((FromBaseTable)updateTable).getTableNameField().getSchemaName();
618     }
619
620     public int getUpdateMode()
621     {
622         return updateMode;
623     }
624
625     /**
626      * Return String[] of names from the FOR UPDATE OF List
627      *
628      * @return String[] of names from the FOR UPDATE OF list.
629      */

630     private String JavaDoc[] getUpdatableColumns()
631     {
632         return (updatableColumns == null) ?
633                 (String JavaDoc[])null :
634                 getUpdateColumnNames();
635     }
636
637     /**
638         Positioned update needs to know what the target result set
639         looks like. This is generated from the UpdateColumnList
640         available for the cursor, to describe the rows coming from
641         the target result set under the cursor. This result set contains
642         a superset of the updatable columns; the caller must verify that
643         only those listed in the FOR UPDATE clause are used.
644
645         @return a result column list containing a description of
646         the target table (this may contain non-updatable columns).
647      * @exception StandardException Thrown on error
648      */

649     private ResultColumnDescriptor[] genTargetResultColList()
650         throws StandardException
651     {
652         ResultColumnList newList;
653
654         /*
655            updateTable holds the FromTable that is the target.
656            copy its ResultColumnList, making BaseColumn references
657            for use in the CurrentOfNode, which behaves as if it had
658            base columns for the statement it is in.
659
660             updateTable is null if the cursor is not updatable.
661          */

662         if (updateTable == null) return null;
663
664         if (targetColumnDescriptors != null) return targetColumnDescriptors;
665
666         newList = (ResultColumnList) getNodeFactory().getNode(
667                                         C_NodeTypes.RESULT_COLUMN_LIST,
668                                         getContextManager());
669         ResultColumnList rcl = updateTable.getResultColumns();
670         int rclSize = rcl.size();
671         for (int index = 0; index < rclSize; index++)
672         {
673             ResultColumn origCol, newCol;
674             ValueNode newNode;
675
676             origCol = (ResultColumn) rcl.elementAt(index);
677
678             // Build a ResultColumn/BaseColumnNode pair for the column
679
newNode = (ValueNode) getNodeFactory().getNode(
680                             C_NodeTypes.BASE_COLUMN_NODE,
681                             origCol.getName(),
682                             makeTableName(origCol.getSchemaName(),
683                                           origCol.getTableName()),
684                             origCol.getTypeServices(),
685                             getContextManager());
686             newCol = (ResultColumn) getNodeFactory().getNode(
687                                     C_NodeTypes.RESULT_COLUMN,
688                                     origCol.columnDescriptor,
689                                     newNode,
690                                     getContextManager());
691
692             /* Build the ResultColumnList to return */
693             newList.addResultColumn(newCol);
694         }
695
696         // we save the result so we only do this once
697
targetColumnDescriptors = newList.makeResultDescriptors();
698         return targetColumnDescriptors;
699     }
700
701     /**
702      * Returns whether or not this Statement requires a set/clear savepoint
703      * around its execution. The following statement "types" do not require them:
704      * Cursor - unnecessary and won't work in a read only environment
705      * Xact - savepoint will get blown away underneath us during commit/rollback
706      *
707      * @return boolean Whether or not this Statement requires a set/clear savepoint
708      */

709     public boolean needsSavepoint()
710     {
711         return false;
712     }
713
714     /**
715      * Get information about this cursor. For sps,
716      * this is info saved off of the original query
717      * tree (the one for the underlying query).
718      *
719      * @return the cursor info
720      * @exception StandardException thrown if generation fails
721      */

722     public Object JavaDoc getCursorInfo()
723         throws StandardException
724     {
725         if (!needTarget)
726             return null;
727
728         return new CursorInfo(updateMode,
729                                 new CursorTableReference(
730                                         getUpdateExposedTableName(),
731                                         getUpdateBaseTableName(),
732                                         getUpdateSchemaName()),
733                                 genTargetResultColList(),
734                                 getUpdatableColumns());
735     }
736
737     /**
738         Bind the update columns by their names to the target table
739         of the cursor specification.
740         Doesn't check for duplicates in the list, although it could...
741         REVISIT: If the list is empty, should it expand it out? at present,
742         it leaves it empty.
743     
744         @param targetTable The underlying target table
745     
746         @exception StandardException Thrown on error
747      */

748     private void bindUpdateColumns(FromTable targetTable)
749                     throws StandardException
750     {
751         int size = updatableColumns.size();
752         TableDescriptor tableDescriptor;
753         String JavaDoc columnName;
754         ResultColumnList rcls = resultSet.getResultColumns();
755
756         for (int index = 0; index < size; index++)
757         {
758             columnName = (String JavaDoc) updatableColumns.elementAt(index);
759             tableDescriptor = targetTable.getTableDescriptor();
760             if ( tableDescriptor.getColumnDescriptor(columnName) == null)
761             {
762                     throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND, columnName);
763             }
764
765             ResultColumn rc;
766             //make sure that we are not using correlation names for updatable columns.
767
//eg select c11 as col1, 2, c13 as col3 from t1 for update of c11, c12
768
//In the eg above, correlation name for c11 will cause exception because Derby does not support correlation name for updatable columns
769
//But correlation name for c13 is ok because it is a read only column
770
for (int rclsIndex = 0; rclsIndex < rcls.size(); rclsIndex++) {//look through each column in the resultset for cursor
771
rc = ((ResultColumn) rcls.elementAt(rclsIndex));
772                     if (rc.getSourceTableName() == null) //continue to look at the next column because this is derived column in the select list
773
continue;
774                     if (rc.getExpression() != null && rc.getExpression().getColumnName().equals(columnName) && !rc.getName().equals(columnName)) {
775                     throw StandardException.newException(SQLState.LANG_CORRELATION_NAME_FOR_UPDATABLE_COLUMN_DISALLOWED_IN_CURSOR, columnName);
776                     }
777             }
778         }
779     }
780
781     /**
782      * Get an array of strings for each updatable column
783      * in this list.
784      *
785      * @return an array of strings
786      */

787     private String JavaDoc[] getUpdateColumnNames()
788     {
789         int size = updatableColumns.size();
790         if (size == 0)
791         {
792             return (String JavaDoc[])null;
793         }
794
795         String JavaDoc[] names = new String JavaDoc[size];
796
797         updatableColumns.copyInto(names);
798
799         return names;
800     }
801     
802     public String JavaDoc getXML()
803     {
804         return null;
805     }
806 }
807
Popular Tags