1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.sql.conn.GenericStatementContext |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.sql.conn; |
22 | |
23 | import org.apache.derby.iapi.services.context.Context; |
24 | |
25 | import org.apache.derby.iapi.services.sanity.SanityManager; |
26 | |
27 | import org.apache.derby.iapi.services.monitor.Monitor; |
28 | |
29 | import org.apache.derby.iapi.services.timer.TimerFactory; |
30 | |
31 | import org.apache.derby.iapi.error.StandardException; |
32 | |
33 | import org.apache.derby.iapi.sql.conn.LanguageConnectionContext; |
34 | import org.apache.derby.iapi.sql.conn.StatementContext; |
35 | |
36 | import org.apache.derby.iapi.sql.depend.Dependency; |
37 | import org.apache.derby.iapi.sql.depend.DependencyManager; |
38 | |
39 | import org.apache.derby.iapi.sql.execute.NoPutResultSet; |
40 | |
41 | import org.apache.derby.iapi.sql.ResultSet; |
42 | import org.apache.derby.iapi.sql.ParameterValueSet; |
43 | |
44 | import org.apache.derby.iapi.store.access.TransactionController; |
45 | |
46 | import org.apache.derby.iapi.services.context.ContextImpl; |
47 | |
48 | import org.apache.derby.iapi.error.ExceptionSeverity; |
49 | import org.apache.derby.iapi.reference.SQLState; |
50 | import java.util.ArrayList; |
51 | import java.util.Iterator; |
52 | import java.util.Timer; |
53 | import java.util.TimerTask; |
54 | import java.sql.SQLException; |
55 | |
56 | /** |
57 | * GenericStatementContext is pushed/popped around a statement prepare and execute |
58 | * so that any statement specific clean up can be performed. |
59 | * |
60 | * @author jerry |
61 | * |
62 | */ |
63 | final class GenericStatementContext |
64 | extends ContextImpl implements StatementContext |
65 | { |
66 | private final TransactionController tc; |
67 | |
68 | private boolean setSavePoint; |
69 | private String internalSavePointName; |
70 | private ResultSet topResultSet; |
71 | private ArrayList dependencies; |
72 | private NoPutResultSet[] subqueryTrackingArray; |
73 | private NoPutResultSet[] materializedSubqueries; |
74 | private final LanguageConnectionContext lcc; |
75 | private boolean inUse = true; |
76 | |
77 | // This flag satisfies all the conditions |
78 | // for using volatile instead of synchronized. |
79 | // (Source: Doug Lea, Concurrent Programming in Java, Second Edition, |
80 | // section 2.2.7.4, page 97) |
81 | // true if statement has been cancelled |
82 | private volatile boolean cancellationFlag = false; |
83 | |
84 | // Reference to the TimerTask that will time out this statement. |
85 | // Needed for stopping the task when execution completes before timeout. |
86 | private CancelQueryTask cancelTask = null; |
87 | |
88 | private boolean parentInTrigger; // whetherparent started with a trigger on stack |
89 | private boolean isForReadOnly = false; |
90 | private boolean isAtomic; |
91 | private boolean isSystemCode; |
92 | private boolean rollbackParentContext; |
93 | private String stmtText; |
94 | private ParameterValueSet pvs; |
95 | |
96 | /** |
97 | Set to one of RoutineAliasInfo.{MODIFIES_SQL_DATA, READS_SQL_DATA, CONTAINS_SQL, NO_SQL} |
98 | */ |
99 | private short sqlAllowed = -1; |
100 | |
101 | /* |
102 | constructor |
103 | @param tc transaction |
104 | */ |
105 | GenericStatementContext(LanguageConnectionContext lcc, TransactionController tc) |
106 | { |
107 | super(lcc.getContextManager(), org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT); |
108 | this.lcc = lcc; |
109 | this.tc = tc; |
110 | |
111 | internalSavePointName = "ISSP" + hashCode(); |
112 | |
113 | if (SanityManager.DEBUG) |
114 | { |
115 | SanityManager.ASSERT((lcc != null), |
116 | "Failed to get language connection context"); |
117 | } |
118 | |
119 | } |
120 | |
121 | /** |
122 | * This is a TimerTask that is responsible for timing out statements, |
123 | * typically when an application has called Statement.setQueryTimeout(). |
124 | * |
125 | * When the application invokes execute() on a statement object, or |
126 | * fetches data on a ResultSet, a StatementContext object is allocated |
127 | * for the duration of the execution in the engine (until control is |
128 | * returned to the application). |
129 | * |
130 | * When the StatementContext object is assigned with setInUse(), |
131 | * a CancelQueryTask is scheduled if a timeout > 0 has been set. |
132 | */ |
133 | private static class CancelQueryTask |
134 | extends |
135 | TimerTask |
136 | { |
137 | /** |
138 | * Reference to the StatementContext for the executing statement |
139 | * which might time out. |
140 | */ |
141 | private StatementContext statementContext; |
142 | |
143 | /** |
144 | * Initializes a new task for timing out a statement's execution. |
145 | * This does not schedule it for execution, the caller is |
146 | * responsible for calling Timer.schedule() with this object |
147 | * as parameter. |
148 | */ |
149 | public CancelQueryTask(StatementContext ctx) |
150 | { |
151 | statementContext = ctx; |
152 | } |
153 | |
154 | /** |
155 | * Invoked by a Timer class to cancel an executing statement. |
156 | * This method just sets a volatile flag in the associated |
157 | * StatementContext object by calling StatementContext.cancel(); |
158 | * it is the responsibility of the thread executing the statement |
159 | * to check this flag regularly. |
160 | */ |
161 | public void run() |
162 | { |
163 | synchronized (this) { |
164 | if (statementContext != null) { |
165 | statementContext.cancel(); |
166 | } |
167 | } |
168 | } |
169 | |
170 | /** |
171 | * Stops this task and prevents it from cancelling a statement. |
172 | * Guarantees that after this method returns, the associated |
173 | * StatementContext object will not be tampered with by this task. |
174 | * Thus, the StatementContext object may safely be allocated to |
175 | * other executing statements. |
176 | */ |
177 | public void forgetContext() { |
178 | boolean mayStillRun = !cancel(); |
179 | if (mayStillRun) { |
180 | synchronized (this) { |
181 | statementContext = null; |
182 | } |
183 | } |
184 | } |
185 | } |
186 | |
187 | // StatementContext Interface |
188 | |
189 | public void setInUse |
190 | ( |
191 | boolean parentInTrigger, |
192 | boolean isAtomic, |
193 | boolean isForReadOnly, |
194 | String stmtText, |
195 | ParameterValueSet pvs, |
196 | long timeoutMillis |
197 | ) |
198 | { |
199 | inUse = true; |
200 | |
201 | this.parentInTrigger = parentInTrigger; |
202 | this.isForReadOnly = isForReadOnly; |
203 | this.isAtomic = isAtomic; |
204 | this.stmtText = stmtText; |
205 | this.pvs = pvs; |
206 | rollbackParentContext = false; |
207 | if (timeoutMillis > 0) { |
208 | TimerFactory factory = Monitor.getMonitor().getTimerFactory(); |
209 | Timer timer = factory.getCancellationTimer(); |
210 | cancelTask = new CancelQueryTask(this); |
211 | timer.schedule(cancelTask, timeoutMillis); |
212 | } |
213 | } |
214 | |
215 | public void clearInUse() { |
216 | /* We must clear out the current top ResultSet to prepare for |
217 | * reusing a StatementContext. |
218 | */ |
219 | stuffTopResultSet( null, null ); |
220 | inUse = false; |
221 | |
222 | parentInTrigger = false; |
223 | isAtomic = false; |
224 | isForReadOnly = false; |
225 | this.stmtText = null; |
226 | sqlAllowed = -1; |
227 | isSystemCode = false; |
228 | rollbackParentContext = false; |
229 | |
230 | if (cancelTask != null) { |
231 | cancelTask.forgetContext(); |
232 | cancelTask = null; |
233 | } |
234 | cancellationFlag = false; |
235 | } |
236 | |
237 | /** |
238 | * @see StatementContext#setSavePoint |
239 | * @exception StandardException Thrown on error |
240 | */ |
241 | public void setSavePoint() throws StandardException { |
242 | |
243 | if (SanityManager.DEBUG) |
244 | { |
245 | if (SanityManager.DEBUG_ON("traceSavepoints")) |
246 | { |
247 | SanityManager.DEBUG_PRINT( |
248 | "GenericStatementContext.setSavePoint()", |
249 | internalSavePointName); |
250 | } |
251 | } |
252 | |
253 | pleaseBeOnStack(); |
254 | |
255 | |
256 | // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller |
257 | if ( tc != null ) { tc.setSavePoint(internalSavePointName, null); } |
258 | setSavePoint = true; |
259 | } |
260 | |
261 | /** |
262 | * Resets the savepoint to the current spot if it is |
263 | * set, otherwise, noop. Used when a commit is |
264 | * done on a nested connection. |
265 | * |
266 | * @see StatementContext#resetSavePoint |
267 | * @exception StandardException Thrown on error |
268 | */ |
269 | public void resetSavePoint() throws StandardException { |
270 | if (SanityManager.DEBUG) |
271 | { |
272 | if (SanityManager.DEBUG_ON("traceSavepoints")) |
273 | { |
274 | SanityManager.DEBUG_PRINT( |
275 | "GenericStatementContext.resetSavePoint()", |
276 | internalSavePointName); |
277 | } |
278 | } |
279 | |
280 | if (inUse && setSavePoint) |
281 | { |
282 | // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller |
283 | if ( tc != null ) { tc.setSavePoint(internalSavePointName, null); } |
284 | // stage buffer management |
285 | } |
286 | } |
287 | |
288 | /** |
289 | * @see StatementContext#clearSavePoint |
290 | * @exception StandardException Thrown on error |
291 | */ |
292 | public void clearSavePoint() throws StandardException { |
293 | |
294 | if (SanityManager.DEBUG) |
295 | { |
296 | if (SanityManager.DEBUG_ON("traceSavepoints")) |
297 | { |
298 | SanityManager.DEBUG_PRINT("GenericStatementContext.clearSavePoint()", |
299 | internalSavePointName); |
300 | } |
301 | } |
302 | |
303 | pleaseBeOnStack(); |
304 | |
305 | if (SanityManager.DEBUG) |
306 | { |
307 | SanityManager.ASSERT(setSavePoint, "setSavePoint is expected to be true"); |
308 | } |
309 | |
310 | // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller |
311 | if ( tc != null ) { tc.releaseSavePoint(internalSavePointName, null); } |
312 | setSavePoint = false; |
313 | } |
314 | |
315 | /** |
316 | * Set the top ResultSet in the ResultSet tree for close down on |
317 | * an error. |
318 | * |
319 | * @exception StandardException thrown on error. |
320 | */ |
321 | public void setTopResultSet(ResultSet topResultSet, |
322 | NoPutResultSet[] subqueryTrackingArray) |
323 | throws StandardException |
324 | { |
325 | pleaseBeOnStack(); |
326 | |
327 | /* We have to handle both materialize and non-materialized subqueries. |
328 | * Materialized subqueries are attached before the top result set is |
329 | * set. If there are any, then we must copy them into the new |
330 | * subqueryTrackingArray. |
331 | */ |
332 | if (materializedSubqueries != null) |
333 | { |
334 | // Do the merging into the passed in array. |
335 | if (subqueryTrackingArray != null) |
336 | { |
337 | if (SanityManager.DEBUG) |
338 | { |
339 | if (this.materializedSubqueries.length != subqueryTrackingArray.length) |
340 | { |
341 | SanityManager.THROWASSERT( |
342 | "this.ms.length (" + this.materializedSubqueries.length + |
343 | ") expected to = sta.length(" + subqueryTrackingArray.length + |
344 | ")"); |
345 | } |
346 | } |
347 | for (int index = 0; index < subqueryTrackingArray.length; index++) |
348 | { |
349 | if (this.subqueryTrackingArray[index] != null) |
350 | { |
351 | subqueryTrackingArray[index] = this.materializedSubqueries[index]; |
352 | } |
353 | } |
354 | } |
355 | else |
356 | { |
357 | subqueryTrackingArray = this.materializedSubqueries; |
358 | } |
359 | materializedSubqueries = null; |
360 | } |
361 | |
362 | stuffTopResultSet( topResultSet, subqueryTrackingArray ); |
363 | } |
364 | |
365 | /** |
366 | * Private minion of setTopResultSet() and clearInUse() |
367 | * |
368 | * @param topResultSet make this the top result set |
369 | * @param subqueryTrackingArray where to keep track of subqueries in this statement |
370 | */ |
371 | private void stuffTopResultSet(ResultSet topResultSet, |
372 | NoPutResultSet[] subqueryTrackingArray) |
373 | { |
374 | this.topResultSet = topResultSet; |
375 | this.subqueryTrackingArray = subqueryTrackingArray; |
376 | dependencies = null; |
377 | } |
378 | |
379 | |
380 | /** |
381 | * Set the appropriate entry in the subquery tracking array for |
382 | * the specified subquery. |
383 | * Useful for closing down open subqueries on an exception. |
384 | * |
385 | * @param subqueryNumber The subquery # for this subquery |
386 | * @param subqueryResultSet The ResultSet at the top of the subquery |
387 | * @param numSubqueries The total # of subqueries in the entire query |
388 | * |
389 | * @exception StandardException thrown on error. |
390 | */ |
391 | public void setSubqueryResultSet(int subqueryNumber, |
392 | NoPutResultSet subqueryResultSet, |
393 | int numSubqueries) |
394 | throws StandardException |
395 | { |
396 | pleaseBeOnStack(); |
397 | |
398 | /* NOTE: In degenerate cases, it is possible that there is no top |
399 | * result set. For example: |
400 | * call (select 1 from systables).valueOf('111'); |
401 | * In that case, we allocate our own subquery tracking array on |
402 | * each call. (Gross!) |
403 | * (Trust me, this is only done in degenerate cases. The tests passed, |
404 | * except for the degenerate cases, before making this change, so we |
405 | * know that the top result set and array reuse is working for |
406 | * the non-degenerate cases.) |
407 | */ |
408 | if (subqueryTrackingArray == null) |
409 | { |
410 | if (topResultSet == null) |
411 | { |
412 | subqueryTrackingArray = new NoPutResultSet[numSubqueries]; |
413 | materializedSubqueries = new NoPutResultSet[numSubqueries]; |
414 | } |
415 | else |
416 | { |
417 | subqueryTrackingArray = |
418 | topResultSet.getSubqueryTrackingArray(numSubqueries); |
419 | } |
420 | } |
421 | subqueryTrackingArray[subqueryNumber] = subqueryResultSet; |
422 | if (materializedSubqueries != null) |
423 | { |
424 | materializedSubqueries[subqueryNumber] = subqueryResultSet; |
425 | } |
426 | } |
427 | |
428 | /** |
429 | * Get the subquery tracking array for this query. |
430 | * (Useful for runtime statistics.) |
431 | * |
432 | * @return NoPutResultSet[] The (sparse) array of tops of subquery ResultSet trees |
433 | * @exception StandardException thrown on error. |
434 | */ |
435 | public NoPutResultSet[] getSubqueryTrackingArray() |
436 | throws StandardException |
437 | { |
438 | pleaseBeOnStack(); |
439 | |
440 | return subqueryTrackingArray; |
441 | } |
442 | |
443 | /** |
444 | * Track a Dependency within this StatementContext. |
445 | * (We need to clear any dependencies added within this |
446 | * context on an error. |
447 | * |
448 | * @param dy The dependency to track. |
449 | * |
450 | * @exception StandardException thrown on error. |
451 | */ |
452 | public void addDependency(Dependency dy) |
453 | throws StandardException |
454 | { |
455 | pleaseBeOnStack(); |
456 | |
457 | if (dependencies == null) |
458 | { |
459 | dependencies = new ArrayList(); |
460 | } |
461 | dependencies.add(dy); |
462 | } |
463 | |
464 | /** |
465 | * Returns whether we started from within the context of a trigger |
466 | * or not. |
467 | * |
468 | * @return true if we are in a trigger context |
469 | */ |
470 | public boolean inTrigger() |
471 | { |
472 | return parentInTrigger; |
473 | } |
474 | |
475 | // |
476 | // Context interface |
477 | // |
478 | /** |
479 | * Close down the top ResultSet, if relevant, and rollback to the |
480 | * internal savepoint, if one was set. |
481 | * |
482 | * @exception StandardException thrown on error. REVISIT: don't want |
483 | * cleanupOnError's to throw exceptions. |
484 | */ |
485 | public void cleanupOnError(Throwable error) throws StandardException |
486 | { |
487 | |
488 | if (SanityManager.DEBUG) |
489 | { |
490 | if (SanityManager.DEBUG_ON("traceSavepoints")) |
491 | { |
492 | SanityManager.DEBUG_PRINT( |
493 | "GenericStatementContext.cleanupOnError()", |
494 | String.valueOf( hashCode() ) ); |
495 | } |
496 | } |
497 | |
498 | /* |
499 | ** If it isn't a StandardException, then assume |
500 | ** xact severity. It is probably an unexpected |
501 | ** java error somewhere in the language. |
502 | */ |
503 | int severity = (error instanceof StandardException) ? |
504 | ((StandardException) error).getSeverity() : |
505 | ExceptionSeverity.STATEMENT_SEVERITY; |
506 | |
507 | |
508 | /** |
509 | * Don't clean up this statement context if it's not in use. |
510 | * This can happen if you get an error while calling one of |
511 | * the JDBC getxxxx() methods on a ResultSet, since no statement |
512 | * context is pushed when those calls occur. |
513 | */ |
514 | if (! inUse) |
515 | { |
516 | return; |
517 | } |
518 | |
519 | /* Clean up the ResultSet, if one exists */ |
520 | if (topResultSet != null) |
521 | { |
522 | topResultSet.cleanUp(); |
523 | } |
524 | |
525 | /* Close down any open subqueries */ |
526 | if (subqueryTrackingArray != null) |
527 | { |
528 | for (int index = 0; index < subqueryTrackingArray.length; index++) |
529 | { |
530 | /* Remember, the array is sparse, so only check |
531 | * non-null entries. |
532 | */ |
533 | if (subqueryTrackingArray[index] != null) |
534 | { |
535 | subqueryTrackingArray[index].cleanUp(); |
536 | } |
537 | } |
538 | } |
539 | |
540 | /* Clean up any dependencies */ |
541 | if (dependencies != null) |
542 | { |
543 | DependencyManager dmgr = lcc.getDataDictionary().getDependencyManager(); |
544 | |
545 | for (Iterator iterator = dependencies.iterator(); iterator.hasNext(); ) |
546 | { |
547 | Dependency dy = (Dependency) iterator.next(); |
548 | dmgr.clearInMemoryDependency(dy); |
549 | } |
550 | |
551 | dependencies = null; |
552 | } |
553 | |
554 | if (severity <= ExceptionSeverity.STATEMENT_SEVERITY |
555 | && setSavePoint) |
556 | { |
557 | if (SanityManager.DEBUG) |
558 | { |
559 | if (SanityManager.DEBUG_ON("traceSavepoints")) |
560 | { |
561 | SanityManager.DEBUG_PRINT( |
562 | "GenericStatementContext.cleanupOnError", |
563 | "rolling back to: " + internalSavePointName); |
564 | } |
565 | } |
566 | |
567 | lcc.internalRollbackToSavepoint( internalSavePointName, false, null); |
568 | |
569 | clearSavePoint(); |
570 | } |
571 | |
572 | if (severity >= ExceptionSeverity.TRANSACTION_SEVERITY ) |
573 | { |
574 | // transaction severity errors roll back the transaction. |
575 | |
576 | /* |
577 | ** We call clearSavePoint() above only for statement errors. |
578 | ** We don't call clearSavePoint() for transaction errors because |
579 | ** the savepoint will be rolled back anyway. So in this case, |
580 | ** we need to indicate that the savepoint is not set. |
581 | */ |
582 | setSavePoint = false; |
583 | } |
584 | |
585 | /* Pop the context */ |
586 | lcc.popStatementContext(this, error); |
587 | } |
588 | |
589 | /** |
590 | * @see Context#isLastHandler |
591 | */ |
592 | public boolean isLastHandler(int severity) |
593 | { |
594 | return inUse && !rollbackParentContext && ((severity == ExceptionSeverity.STATEMENT_SEVERITY) || |
595 | (severity == ExceptionSeverity.NO_APPLICABLE_SEVERITY)); |
596 | } |
597 | |
598 | /** |
599 | * Reports whether this StatementContext is on the context stack. |
600 | * |
601 | * @return true if this StatementContext is on the context stack. false otherwise. |
602 | */ |
603 | public boolean onStack() { return inUse; } |
604 | |
605 | /** |
606 | * Indicates whether the statement needs to be executed atomically |
607 | * or not, i.e., whether a commit/rollback is permitted by a |
608 | * connection nested in this statement. |
609 | * |
610 | * @return true if needs to be atomic |
611 | */ |
612 | public boolean isAtomic() |
613 | { |
614 | return isAtomic; |
615 | } |
616 | |
617 | /** |
618 | * Return the text of the current statement. |
619 | * Note that this may be null. It is currently |
620 | * not set up correctly for ResultSets that aren't |
621 | * single row result sets (e.g SELECT), replication, |
622 | * and setXXXX/getXXXX jdbc methods. |
623 | * |
624 | * @return the statement text |
625 | */ |
626 | public String getStatementText() |
627 | { |
628 | return stmtText; |
629 | } |
630 | |
631 | // |
632 | // class implementation |
633 | // |
634 | |
635 | /** |
636 | * Raise an exception if this Context is not in use, that is, on the |
637 | * Context Stack. |
638 | * |
639 | * @exception StandardException thrown on error. |
640 | */ |
641 | private void pleaseBeOnStack() throws StandardException |
642 | { |
643 | if ( !inUse ) { throw StandardException.newException(SQLState.LANG_DEAD_STATEMENT); } |
644 | } |
645 | |
646 | public boolean inUse() |
647 | { |
648 | return inUse; |
649 | } |
650 | public boolean isForReadOnly() |
651 | { |
652 | return isForReadOnly; |
653 | } |
654 | |
655 | /** |
656 | * Tests whether the statement which has allocated this StatementContext |
657 | * object has been cancelled. This method is typically called from the |
658 | * thread which is executing the statement, to test whether execution |
659 | * should continue or stop. |
660 | * |
661 | * @return whether the statement which has allocated this StatementContext |
662 | * object has been cancelled. |
663 | */ |
664 | public boolean isCancelled() |
665 | { |
666 | return cancellationFlag; |
667 | } |
668 | |
669 | /** |
670 | * Cancels the statement which has allocated this StatementContext object. |
671 | * This is done by setting a flag in the StatementContext object. For |
672 | * this to have any effect, it is the responsibility of the executing |
673 | * statement to check this flag regularly. |
674 | */ |
675 | public void cancel() |
676 | { |
677 | cancellationFlag = true; |
678 | } |
679 | |
680 | public void setSQLAllowed(short allow, boolean force) { |
681 | |
682 | // cannot override a stricter setting. |
683 | // -1 is no routine restriction in place |
684 | // 0 is least restrictive |
685 | // 4 is most |
686 | if (force || (allow > sqlAllowed)) |
687 | sqlAllowed = allow; |
688 | |
689 | } |
690 | public short getSQLAllowed() { |
691 | if (!inUse) |
692 | return org.apache.derby.catalog.types.RoutineAliasInfo.NO_SQL; |
693 | |
694 | return sqlAllowed; |
695 | } |
696 | |
697 | /** |
698 | * Indicate that, in the event of a statement-level exception, |
699 | * this context is NOT the last one that needs to be rolled |
700 | * back--rather, it is nested within some other statement |
701 | * context, and that other context needs to be rolled back, |
702 | * too. |
703 | */ |
704 | public void setParentRollback() { |
705 | rollbackParentContext = true; |
706 | } |
707 | |
708 | /** |
709 | Set to indicate statement is system code. |
710 | For example a system procedure, view, function etc. |
711 | */ |
712 | public void setSystemCode() { |
713 | isSystemCode = true; |
714 | } |
715 | |
716 | /** |
717 | Return true if this statement is system code. |
718 | */ |
719 | public boolean getSystemCode() { |
720 | return isSystemCode; |
721 | } |
722 | |
723 | public StringBuffer appendErrorInfo() { |
724 | |
725 | StringBuffer sb = ((ContextImpl) lcc).appendErrorInfo(); |
726 | if (sb != null) { |
727 | |
728 | sb.append("Failed Statement is: "); |
729 | |
730 | sb.append(getStatementText()); |
731 | |
732 | if (lcc.getLogStatementText() && (pvs != null) && pvs.getParameterCount() > 0) |
733 | { |
734 | String pvsString = " with " + pvs.getParameterCount() + |
735 | " parameters " + pvs.toString(); |
736 | sb.append(pvsString); |
737 | } |
738 | } |
739 | return sb; |
740 | |
741 | } |
742 | } |