KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > conn > GenericStatementContext


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.conn.GenericStatementContext
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.conn;
23
24 import org.apache.derby.iapi.services.context.Context;
25
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.services.monitor.Monitor;
29
30 import org.apache.derby.iapi.services.timer.TimerFactory;
31
32 import org.apache.derby.iapi.error.StandardException;
33
34 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
35 import org.apache.derby.iapi.sql.conn.StatementContext;
36
37 import org.apache.derby.iapi.sql.depend.Dependency;
38 import org.apache.derby.iapi.sql.depend.DependencyManager;
39
40 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
41
42 import org.apache.derby.iapi.sql.ResultSet;
43 import org.apache.derby.iapi.sql.ParameterValueSet;
44
45 import org.apache.derby.iapi.store.access.TransactionController;
46
47 import org.apache.derby.iapi.services.context.ContextImpl;
48
49 import org.apache.derby.iapi.error.ExceptionSeverity;
50 import org.apache.derby.iapi.reference.SQLState;
51 import java.util.ArrayList JavaDoc;
52 import java.util.Iterator JavaDoc;
53 import java.util.Timer JavaDoc;
54 import java.util.TimerTask JavaDoc;
55 import java.sql.SQLException JavaDoc;
56
57 /**
58  * GenericStatementContext is pushed/popped around a statement prepare and execute
59  * so that any statement specific clean up can be performed.
60  *
61  * @author jerry
62  *
63  */

64 final class GenericStatementContext
65     extends ContextImpl implements StatementContext
66 {
67     private final TransactionController tc;
68
69     private boolean setSavePoint;
70     private String JavaDoc internalSavePointName;
71     private ResultSet topResultSet;
72     private ArrayList JavaDoc dependencies;
73     private NoPutResultSet[] subqueryTrackingArray;
74     private NoPutResultSet[] materializedSubqueries;
75     private final LanguageConnectionContext lcc;
76     private boolean inUse = true;
77
78     // This flag satisfies all the conditions
79
// for using volatile instead of synchronized.
80
// (Source: Doug Lea, Concurrent Programming in Java, Second Edition,
81
// section 2.2.7.4, page 97)
82
// true if statement has been cancelled
83
private volatile boolean cancellationFlag = false;
84
85     // Reference to the TimerTask that will time out this statement.
86
// Needed for stopping the task when execution completes before timeout.
87
private CancelQueryTask cancelTask = null;
88         
89     private boolean parentInTrigger; // whetherparent started with a trigger on stack
90
private boolean isForReadOnly = false;
91     private boolean isAtomic;
92     private boolean isSystemCode;
93     private boolean rollbackParentContext;
94     private String JavaDoc stmtText;
95     private ParameterValueSet pvs;
96
97     /**
98         Set to one of RoutineAliasInfo.{MODIFIES_SQL_DATA, READS_SQL_DATA, CONTAINS_SQL, NO_SQL}
99     */

100     private short sqlAllowed = -1;
101
102     /*
103        constructor
104         @param tc transaction
105     */

106     GenericStatementContext(LanguageConnectionContext lcc, TransactionController tc)
107     {
108         super(lcc.getContextManager(), org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);
109         this.lcc = lcc;
110         this.tc = tc;
111
112         internalSavePointName = "ISSP" + hashCode();
113
114         if (SanityManager.DEBUG)
115         {
116             SanityManager.ASSERT((lcc != null),
117                     "Failed to get language connection context");
118         }
119
120     }
121
122     /**
123      * This is a TimerTask that is responsible for timing out statements,
124      * typically when an application has called Statement.setQueryTimeout().
125      *
126      * When the application invokes execute() on a statement object, or
127      * fetches data on a ResultSet, a StatementContext object is allocated
128      * for the duration of the execution in the engine (until control is
129      * returned to the application).
130      *
131      * When the StatementContext object is assigned with setInUse(),
132      * a CancelQueryTask is scheduled if a timeout > 0 has been set.
133      */

134     private static class CancelQueryTask
135         extends
136             TimerTask JavaDoc
137     {
138         /**
139          * Reference to the StatementContext for the executing statement
140          * which might time out.
141          */

142         private StatementContext statementContext;
143
144         /**
145          * Initializes a new task for timing out a statement's execution.
146          * This does not schedule it for execution, the caller is
147          * responsible for calling Timer.schedule() with this object
148          * as parameter.
149          */

150         public CancelQueryTask(StatementContext ctx)
151         {
152             statementContext = ctx;
153         }
154
155         /**
156          * Invoked by a Timer class to cancel an executing statement.
157          * This method just sets a volatile flag in the associated
158          * StatementContext object by calling StatementContext.cancel();
159          * it is the responsibility of the thread executing the statement
160          * to check this flag regularly.
161          */

162         public void run()
163         {
164             synchronized (this) {
165                 if (statementContext != null) {
166                     statementContext.cancel();
167                 }
168             }
169         }
170
171         /**
172          * Stops this task and prevents it from cancelling a statement.
173          * Guarantees that after this method returns, the associated
174          * StatementContext object will not be tampered with by this task.
175          * Thus, the StatementContext object may safely be allocated to
176          * other executing statements.
177          */

178         public void forgetContext() {
179             boolean mayStillRun = !cancel();
180             if (mayStillRun) {
181                 synchronized (this) {
182                     statementContext = null;
183                 }
184             }
185         }
186     }
187
188     // StatementContext Interface
189

190     public void setInUse
191     (
192         boolean parentInTrigger,
193         boolean isAtomic,
194                 boolean isForReadOnly,
195         String JavaDoc stmtText,
196         ParameterValueSet pvs,
197         long timeoutMillis
198     )
199     {
200         inUse = true;
201
202         this.parentInTrigger = parentInTrigger;
203         this.isForReadOnly = isForReadOnly;
204         this.isAtomic = isAtomic;
205         this.stmtText = stmtText;
206         this.pvs = pvs;
207         rollbackParentContext = false;
208         if (timeoutMillis > 0) {
209             TimerFactory factory = Monitor.getMonitor().getTimerFactory();
210             Timer JavaDoc timer = factory.getCancellationTimer();
211             cancelTask = new CancelQueryTask(this);
212             timer.schedule(cancelTask, timeoutMillis);
213         }
214     }
215
216     public void clearInUse() {
217         /* We must clear out the current top ResultSet to prepare for
218          * reusing a StatementContext.
219          */

220         stuffTopResultSet( null, null );
221         inUse = false;
222
223         parentInTrigger = false;
224         isAtomic = false;
225         isForReadOnly = false;
226         this.stmtText = null;
227         sqlAllowed = -1;
228         isSystemCode = false;
229         rollbackParentContext = false;
230
231         if (cancelTask != null) {
232             cancelTask.forgetContext();
233             cancelTask = null;
234         }
235         cancellationFlag = false;
236     }
237
238     /**
239      * @see StatementContext#setSavePoint
240      * @exception StandardException Thrown on error
241      */

242     public void setSavePoint() throws StandardException {
243         
244         if (SanityManager.DEBUG)
245         {
246             if (SanityManager.DEBUG_ON("traceSavepoints"))
247             {
248                 SanityManager.DEBUG_PRINT(
249                                     "GenericStatementContext.setSavePoint()",
250                                     internalSavePointName);
251             }
252         }
253             
254         pleaseBeOnStack();
255         
256
257         // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller
258
if ( tc != null ) { tc.setSavePoint(internalSavePointName, null); }
259         setSavePoint = true;
260     }
261
262     /**
263      * Resets the savepoint to the current spot if it is
264      * set, otherwise, noop. Used when a commit is
265      * done on a nested connection.
266      *
267      * @see StatementContext#resetSavePoint
268      * @exception StandardException Thrown on error
269      */

270     public void resetSavePoint() throws StandardException {
271         if (SanityManager.DEBUG)
272         {
273             if (SanityManager.DEBUG_ON("traceSavepoints"))
274             {
275                 SanityManager.DEBUG_PRINT(
276                     "GenericStatementContext.resetSavePoint()",
277                     internalSavePointName);
278             }
279         }
280             
281         if (inUse && setSavePoint)
282         {
283             // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller
284
if ( tc != null ) { tc.setSavePoint(internalSavePointName, null); }
285             // stage buffer management
286
}
287     }
288
289     /**
290      * @see StatementContext#clearSavePoint
291      * @exception StandardException Thrown on error
292      */

293     public void clearSavePoint() throws StandardException {
294
295         if (SanityManager.DEBUG)
296         {
297             if (SanityManager.DEBUG_ON("traceSavepoints"))
298             {
299                 SanityManager.DEBUG_PRINT("GenericStatementContext.clearSavePoint()",
300                                           internalSavePointName);
301             }
302         }
303
304         pleaseBeOnStack();
305
306         if (SanityManager.DEBUG)
307         {
308             SanityManager.ASSERT(setSavePoint, "setSavePoint is expected to be true");
309         }
310
311         // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller
312
if ( tc != null ) { tc.releaseSavePoint(internalSavePointName, null); }
313         setSavePoint = false;
314     }
315
316     /**
317      * Set the top ResultSet in the ResultSet tree for close down on
318      * an error.
319      *
320      * @exception StandardException thrown on error.
321      */

322     public void setTopResultSet(ResultSet topResultSet,
323                                 NoPutResultSet[] subqueryTrackingArray)
324          throws StandardException
325     {
326         pleaseBeOnStack();
327
328         /* We have to handle both materialize and non-materialized subqueries.
329          * Materialized subqueries are attached before the top result set is
330          * set. If there are any, then we must copy them into the new
331          * subqueryTrackingArray.
332          */

333         if (materializedSubqueries != null)
334         {
335             // Do the merging into the passed in array.
336
if (subqueryTrackingArray != null)
337             {
338                 if (SanityManager.DEBUG)
339                 {
340                     if (this.materializedSubqueries.length != subqueryTrackingArray.length)
341                     {
342                         SanityManager.THROWASSERT(
343                             "this.ms.length (" + this.materializedSubqueries.length +
344                             ") expected to = sta.length(" + subqueryTrackingArray.length +
345                             ")");
346                     }
347                 }
348                 for (int index = 0; index < subqueryTrackingArray.length; index++)
349                 {
350                     if (this.subqueryTrackingArray[index] != null)
351                     {
352                         subqueryTrackingArray[index] = this.materializedSubqueries[index];
353                     }
354                 }
355             }
356             else
357             {
358                 subqueryTrackingArray = this.materializedSubqueries;
359             }
360             materializedSubqueries = null;
361         }
362
363         stuffTopResultSet( topResultSet, subqueryTrackingArray );
364     }
365
366     /**
367       * Private minion of setTopResultSet() and clearInUse()
368       *
369       * @param topResultSet make this the top result set
370       * @param subqueryTrackingArray where to keep track of subqueries in this statement
371       */

372     private void stuffTopResultSet(ResultSet topResultSet,
373                                       NoPutResultSet[] subqueryTrackingArray)
374     {
375         this.topResultSet = topResultSet;
376         this.subqueryTrackingArray = subqueryTrackingArray;
377         dependencies = null;
378     }
379
380
381     /**
382      * Set the appropriate entry in the subquery tracking array for
383      * the specified subquery.
384      * Useful for closing down open subqueries on an exception.
385      *
386      * @param subqueryNumber The subquery # for this subquery
387      * @param subqueryResultSet The ResultSet at the top of the subquery
388      * @param numSubqueries The total # of subqueries in the entire query
389      *
390      * @exception StandardException thrown on error.
391      */

392     public void setSubqueryResultSet(int subqueryNumber,
393                                      NoPutResultSet subqueryResultSet,
394                                      int numSubqueries)
395         throws StandardException
396     {
397         pleaseBeOnStack();
398         
399         /* NOTE: In degenerate cases, it is possible that there is no top
400          * result set. For example:
401          * call (select 1 from systables).valueOf('111');
402          * In that case, we allocate our own subquery tracking array on
403          * each call. (Gross!)
404          * (Trust me, this is only done in degenerate cases. The tests passed,
405          * except for the degenerate cases, before making this change, so we
406          * know that the top result set and array reuse is working for
407          * the non-degenerate cases.)
408          */

409         if (subqueryTrackingArray == null)
410         {
411             if (topResultSet == null)
412             {
413                 subqueryTrackingArray = new NoPutResultSet[numSubqueries];
414                 materializedSubqueries = new NoPutResultSet[numSubqueries];
415             }
416             else
417             {
418                 subqueryTrackingArray =
419                     topResultSet.getSubqueryTrackingArray(numSubqueries);
420             }
421         }
422         subqueryTrackingArray[subqueryNumber] = subqueryResultSet;
423         if (materializedSubqueries != null)
424         {
425             materializedSubqueries[subqueryNumber] = subqueryResultSet;
426         }
427     }
428
429     /**
430      * Get the subquery tracking array for this query.
431      * (Useful for runtime statistics.)
432      *
433      * @return NoPutResultSet[] The (sparse) array of tops of subquery ResultSet trees
434      * @exception StandardException thrown on error.
435      */

436     public NoPutResultSet[] getSubqueryTrackingArray()
437         throws StandardException
438     {
439         pleaseBeOnStack();
440         
441         return subqueryTrackingArray;
442     }
443
444     /**
445      * Track a Dependency within this StatementContext.
446      * (We need to clear any dependencies added within this
447      * context on an error.
448      *
449      * @param dy The dependency to track.
450      *
451      * @exception StandardException thrown on error.
452      */

453     public void addDependency(Dependency dy)
454         throws StandardException
455     {
456         pleaseBeOnStack();
457         
458         if (dependencies == null)
459         {
460             dependencies = new ArrayList JavaDoc();
461         }
462         dependencies.add(dy);
463     }
464
465     /**
466      * Returns whether we started from within the context of a trigger
467      * or not.
468      *
469      * @return true if we are in a trigger context
470      */

471     public boolean inTrigger()
472     {
473         return parentInTrigger;
474     }
475
476     //
477
// Context interface
478
//
479
/**
480      * Close down the top ResultSet, if relevant, and rollback to the
481      * internal savepoint, if one was set.
482      *
483      * @exception StandardException thrown on error. REVISIT: don't want
484      * cleanupOnError's to throw exceptions.
485      */

486     public void cleanupOnError(Throwable JavaDoc error) throws StandardException
487     {
488
489         if (SanityManager.DEBUG)
490         {
491             if (SanityManager.DEBUG_ON("traceSavepoints"))
492             {
493                 SanityManager.DEBUG_PRINT(
494                         "GenericStatementContext.cleanupOnError()",
495                         String.valueOf( hashCode() ) );
496             }
497         }
498
499         /*
500         ** If it isn't a StandardException, then assume
501         ** session severity. It is probably an unexpected
502         ** java error somewhere in the language.
503         ** Store layer treats JVM error as session severity,
504         ** hence to be consistent and to avoid getting rawstore
505         ** protocol violation errors, we treat java errors here
506         ** to be of session severity.
507         */

508         int severity = (error instanceof StandardException) ?
509             ((StandardException) error).getSeverity() :
510             ExceptionSeverity.SESSION_SEVERITY;
511
512
513         /**
514          * Don't clean up this statement context if it's not in use.
515          * This can happen if you get an error while calling one of
516          * the JDBC getxxxx() methods on a ResultSet, since no statement
517          * context is pushed when those calls occur.
518          */

519         if (! inUse)
520         {
521             return;
522         }
523
524         /* Clean up the ResultSet, if one exists */
525         if (topResultSet != null)
526         {
527             topResultSet.cleanUp();
528         }
529
530         /* Close down any open subqueries */
531         if (subqueryTrackingArray != null)
532         {
533             for (int index = 0; index < subqueryTrackingArray.length; index++)
534             {
535                 /* Remember, the array is sparse, so only check
536                  * non-null entries.
537                  */

538                 if (subqueryTrackingArray[index] != null)
539                 {
540                     subqueryTrackingArray[index].cleanUp();
541                 }
542             }
543         }
544
545         /* Clean up any dependencies */
546         if (dependencies != null)
547         {
548             DependencyManager dmgr = lcc.getDataDictionary().getDependencyManager();
549
550             for (Iterator JavaDoc iterator = dependencies.iterator(); iterator.hasNext(); )
551             {
552                 Dependency dy = (Dependency) iterator.next();
553                 dmgr.clearInMemoryDependency(dy);
554             }
555
556             dependencies = null;
557         }
558
559         if (severity <= ExceptionSeverity.STATEMENT_SEVERITY
560             && setSavePoint)
561         {
562             if (SanityManager.DEBUG)
563             {
564                 if (SanityManager.DEBUG_ON("traceSavepoints"))
565                 {
566                     SanityManager.DEBUG_PRINT(
567                         "GenericStatementContext.cleanupOnError",
568                         "rolling back to: " + internalSavePointName);
569                 }
570             }
571
572             lcc.internalRollbackToSavepoint( internalSavePointName, false, null);
573
574             clearSavePoint();
575         }
576
577         if (severity >= ExceptionSeverity.TRANSACTION_SEVERITY )
578         {
579             // transaction severity errors roll back the transaction.
580

581             /*
582             ** We call clearSavePoint() above only for statement errors.
583             ** We don't call clearSavePoint() for transaction errors because
584             ** the savepoint will be rolled back anyway. So in this case,
585             ** we need to indicate that the savepoint is not set.
586             */

587             setSavePoint = false;
588         }
589
590         /* Pop the context */
591         lcc.popStatementContext(this, error);
592     }
593
594     /**
595      * @see Context#isLastHandler
596      */

597     public boolean isLastHandler(int severity)
598     {
599         // For JVM errors, severity gets mapped to
600
// ExceptionSeverity.NO_APPLICABLE_SEVERITY
601
// in ContextManager.cleanupOnError. It is necessary to
602
// let outer contexts take corrective action for jvm errors, so
603
// return false as this will not be the last handler for such
604
// errors.
605
return inUse && !rollbackParentContext &&
606             ( severity == ExceptionSeverity.STATEMENT_SEVERITY );
607     }
608
609     /**
610       * Reports whether this StatementContext is on the context stack.
611       *
612       * @return true if this StatementContext is on the context stack. false otherwise.
613       */

614     public boolean onStack() { return inUse; }
615
616     /**
617      * Indicates whether the statement needs to be executed atomically
618      * or not, i.e., whether a commit/rollback is permitted by a
619      * connection nested in this statement.
620      *
621      * @return true if needs to be atomic
622      */

623     public boolean isAtomic()
624     {
625         return isAtomic;
626     }
627
628     /**
629      * Return the text of the current statement.
630      * Note that this may be null. It is currently
631      * not set up correctly for ResultSets that aren't
632      * single row result sets (e.g SELECT), replication,
633      * and setXXXX/getXXXX jdbc methods.
634      *
635      * @return the statement text
636      */

637     public String JavaDoc getStatementText()
638     {
639         return stmtText;
640     }
641
642     //
643
// class implementation
644
//
645

646     /**
647       * Raise an exception if this Context is not in use, that is, on the
648       * Context Stack.
649       *
650       * @exception StandardException thrown on error.
651       */

652     private void pleaseBeOnStack() throws StandardException
653     {
654         if ( !inUse ) { throw StandardException.newException(SQLState.LANG_DEAD_STATEMENT); }
655     }
656
657     public boolean inUse()
658     {
659         return inUse;
660     }
661     public boolean isForReadOnly()
662     {
663     return isForReadOnly;
664     }
665         
666     /**
667      * Tests whether the statement which has allocated this StatementContext
668      * object has been cancelled. This method is typically called from the
669      * thread which is executing the statement, to test whether execution
670      * should continue or stop.
671      *
672      * @return whether the statement which has allocated this StatementContext
673      * object has been cancelled.
674      */

675     public boolean isCancelled()
676     {
677         return cancellationFlag;
678     }
679
680     /**
681      * Cancels the statement which has allocated this StatementContext object.
682      * This is done by setting a flag in the StatementContext object. For
683      * this to have any effect, it is the responsibility of the executing
684      * statement to check this flag regularly.
685      */

686     public void cancel()
687     {
688         cancellationFlag = true;
689     }
690
691     public void setSQLAllowed(short allow, boolean force) {
692
693         // cannot override a stricter setting.
694
// -1 is no routine restriction in place
695
// 0 is least restrictive
696
// 4 is most
697
if (force || (allow > sqlAllowed))
698             sqlAllowed = allow;
699
700     }
701     public short getSQLAllowed() {
702         if (!inUse)
703             return org.apache.derby.catalog.types.RoutineAliasInfo.NO_SQL;
704
705         return sqlAllowed;
706     }
707
708     /**
709      * Indicate that, in the event of a statement-level exception,
710      * this context is NOT the last one that needs to be rolled
711      * back--rather, it is nested within some other statement
712      * context, and that other context needs to be rolled back,
713      * too.
714     */

715     public void setParentRollback() {
716         rollbackParentContext = true;
717     }
718
719     /**
720         Set to indicate statement is system code.
721         For example a system procedure, view, function etc.
722     */

723     public void setSystemCode() {
724         isSystemCode = true;
725     }
726
727     /**
728         Return true if this statement is system code.
729     */

730     public boolean getSystemCode() {
731         return isSystemCode;
732     }
733
734     public StringBuffer JavaDoc appendErrorInfo() {
735
736         StringBuffer JavaDoc sb = ((ContextImpl) lcc).appendErrorInfo();
737         if (sb != null) {
738
739             sb.append("Failed Statement is: ");
740
741             sb.append(getStatementText());
742
743             if (lcc.getLogStatementText() && (pvs != null) && pvs.getParameterCount() > 0)
744             {
745                 String JavaDoc pvsString = " with " + pvs.getParameterCount() +
746                         " parameters " + pvs.toString();
747                 sb.append(pvsString);
748             }
749         }
750         return sb;
751
752     }
753 }
754
Popular Tags