KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > raw > xact > Xact


1 /*
2
3    Derby - Class org.apache.derby.impl.store.raw.xact.Xact
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.store.raw.xact;
23
24 import org.apache.derby.iapi.reference.SQLState;
25
26 import org.apache.derby.iapi.store.raw.ContainerKey;
27
28 import org.apache.derby.iapi.services.context.ContextManager;
29 import org.apache.derby.iapi.services.daemon.Serviceable;
30 import org.apache.derby.iapi.services.locks.LockFactory;
31 import org.apache.derby.iapi.services.locks.Limit;
32
33 import org.apache.derby.iapi.store.raw.ContainerHandle;
34 import org.apache.derby.iapi.store.raw.Compensation;
35 import org.apache.derby.iapi.store.raw.GlobalTransactionId;
36 import org.apache.derby.iapi.store.raw.LockingPolicy;
37 import org.apache.derby.iapi.store.raw.Loggable;
38 import org.apache.derby.iapi.store.raw.RecordHandle;
39 import org.apache.derby.iapi.store.raw.StreamContainerHandle;
40 import org.apache.derby.iapi.store.raw.Transaction;
41
42 import org.apache.derby.iapi.store.raw.data.DataFactory;
43 import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
44
45 import org.apache.derby.iapi.store.raw.xact.RawTransaction;
46 import org.apache.derby.iapi.store.raw.xact.TransactionId;
47
48 import org.apache.derby.iapi.store.raw.log.LogFactory;
49 import org.apache.derby.iapi.store.raw.log.LogInstant;
50 import org.apache.derby.iapi.store.raw.log.Logger;
51
52 import org.apache.derby.iapi.store.access.FileResource;
53 import org.apache.derby.iapi.store.access.RowSource;
54 import org.apache.derby.iapi.store.access.TransactionController;
55 import org.apache.derby.iapi.error.ExceptionSeverity;
56
57 import org.apache.derby.iapi.services.property.PersistentSet;
58
59 import org.apache.derby.catalog.UUID;
60
61 import java.util.Stack JavaDoc;
62 import java.util.Enumeration JavaDoc;
63 import java.util.Properties JavaDoc;
64 import java.util.ArrayList JavaDoc;
65 import java.util.List JavaDoc;
66 import java.util.Dictionary JavaDoc;
67
68 import org.apache.derby.iapi.error.StandardException;
69
70 import org.apache.derby.iapi.services.sanity.SanityManager;
71 import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
72 import org.apache.derby.iapi.util.ByteArray;
73 import org.apache.derby.iapi.services.property.PropertyUtil;
74 import org.apache.derby.iapi.reference.Property;
75
76 import org.apache.derby.impl.store.raw.log.LogToFile;
77
78 import org.apache.derby.iapi.services.io.LimitObjectInput;
79
80 import org.apache.derby.iapi.services.context.ContextService;
81
82 /**
83
84   A transaction has five states
85   <OL>
86   <LI> CLOSED - cannot be used
87   <LI> IDLE - no reads have been performed by the transaction.
88   <LI> ACTIVE - at least one read has been attempted by the transaction
89   <LI> UPDATE - at least one update has been attempted by the transaction
90   <LI> PREPARED - the transaction is ready to commit (FUTURE).
91   </OL>
92   <BR>Transaction identifiers are re-used for transactions that do not enter the
93   UPDATE state during their lifetime.
94
95     @see Transaction
96
97 */

98 public class Xact extends RawTransaction implements Limit {
99
100     /*
101     ** Static Fields
102     */

103
104     protected static final int CLOSED = 0;
105     protected static final int IDLE = 1;
106     protected static final int ACTIVE = 2;
107     protected static final int UPDATE = 3;
108     protected static final int PREPARED = 4;
109
110
111     /*
112     ** Transaction status stored in the beginXact and endXact
113     */

114
115     public static final int END_ABORTED = 0x00000001;
116     public static final int END_PREPARED = 0x00000002;
117     public static final int END_COMMITTED = 0x00000004;
118
119     public static final int RECOVERY_ROLLBACK_FIRST = 0x00000010;
120     public static final int INTERNAL_TRANSACTION = 0x00000020;
121     public static final int NESTED_TOP_TRANSACTION = 0x00000040;
122
123
124     /**
125       private static -
126
127       make sure these bits don't overwrite bits in Transaction.commit commitflag
128     */

129     private static final int COMMIT_SYNC = 0x00010000;
130     private static final int COMMIT_NO_SYNC = 0x00020000;
131     private static final int COMMIT_PREPARE = 0x00040000;
132
133
134     /*
135     ** Fields
136     */

137
138     //
139
// set during recovery if this is the recovery transaction.
140
//
141
private int savedEndStatus;
142
143     //
144
// if this transaction object was committed without syncing, then in a
145
// subsequent commit with sync, the log must be flushed even if the last
146
// transaction is read only
147
private boolean needSync;
148
149     //
150
// When the xact is first created, it is in an IDLE state. Since the
151
// transaction table needs an XactId, one will be made for it. When this
152
// transaction commits, it goes back to the IDLE state. If we then create
153
// a new XactId for it, we will waste one XactId per transaction object
154
// because it must first go thru the IDLE state before it gets closed.
155
// Therefore, the first XactId is assigned in the constructor and
156
// subsequent XactId is assigned in the setActiveState. However, the first
157
// time it goes into setActiveState, we don't want it to create a new
158
// XactId when the one that was assigned to it in the constructore is good
159
// enough, so we use this justCreate field to indicate to setActiveState
160
// whether it needs to make a new XactId (for the next transaction) for
161
// not.
162
private boolean justCreated = true;
163
164
165     protected XactContext xc; // my context - set by XactContext
166

167     // these fields remain fixed for the lifetime of this object
168
protected final XactFactory xactFactory;
169     protected final DataFactory dataFactory;
170     protected final LogFactory logFactory;
171     protected final Object JavaDoc compatibilitySpace;
172
173     // these fields remain fixedfor the lifetime
174
private LockingPolicy defaultLocking;
175
176     // Global id, unique among all rawstores and all eternity
177
private GlobalTransactionId myGlobalId;
178
179     // id that is valid locally in this raw store.
180
private volatile TransactionId myId;
181
182     protected Logger logger; // the object we use to access the log.
183

184
185     protected volatile int state; // we access this without synchronization sometimes
186
private Integer JavaDoc inComplete = null; // set between preComplete() and postComplete()
187

188     private boolean seenUpdates; // true if this session has written a log
189
// record to disk. Note this is per
190
// session and not per transaction, namely
191
// during recovery, a transaction may have
192
// updates but it may not have any updates
193
// during recovery. In that case,
194
// seenUpdates is false even though state
195
// is UPDATE.
196
// This value is used to decide whether
197
// the log needs to get flushed at commit
198
// time.
199

200     private boolean inPostCommitProcessing; // true if we are processing post
201
// commit work in the same context the
202
// work was queued. This is used to stop
203
// recursion only. We don't want a post
204
// commit task to queue other post commit
205
// task, ad infinitum. PostCommitWork
206
// requested while processing
207
// postCommitWork will be processed
208
// by the daemon, which may itself
209
// recurse once.
210

211     private LogInstant logStart; // If this is a read only transaction (has
212
// never written a log record to disk),
213
// then null. Otherwise, set to the log
214
// instant of the first log record.
215

216     private LogInstant logLast; // the last log record written by this
217
// transaction
218

219     private Stack JavaDoc savePoints; // stack of SavePoint objects.
220

221     protected List JavaDoc postCommitWorks; // a list of post commit work
222
protected List JavaDoc postTerminationWorks; // work to be done after
223
// transaction terminates,
224
// commit or abort
225
private boolean recoveryTransaction; // this transaction is being
226
// used by recovery
227

228     DynamicByteArrayOutputStream logBuffer;
229
230     private boolean postCompleteMode; // perform most preComplete work in postComplete
231

232     // Use this flag to catch the case where a global transaction was closed.
233
// Normally a closed transaction should not be aborted again, but we need
234
// to allow abort of a closed global transaction for error handling. Use
235
// this flag to make sure people are not abusing this loop hole.
236
// RESOLVE: sku to remove before GA
237
private boolean sanityCheck_xaclosed;
238
239     // Indicates the name of the transaction, and if it is set, it is displayed
240
// by the transactiontable VTI
241
private String JavaDoc transName;
242
243     // The transaction is only allowed read operations, no log writes.
244
private boolean readOnly;
245
246     // true, if the transaction executed some operations(like unlogged
247
// operations) that block the online backup to prevent inconsistent
248
// backup copy.
249
private boolean backupBlocked;
250
251
252     /*
253     ** Constructor
254     */

255
256     protected Xact(
257     XactFactory xactFactory,
258     LogFactory logFactory,
259     DataFactory dataFactory,
260     boolean readOnly,
261     Object JavaDoc compatibilitySpace)
262     {
263
264         super();
265
266         this.xactFactory = xactFactory;
267         this.logFactory = logFactory;
268         this.dataFactory = dataFactory;
269         this.readOnly = readOnly;
270
271         this.compatibilitySpace =
272             (compatibilitySpace == null ? this : compatibilitySpace);
273
274         if (SanityManager.DEBUG)
275         {
276             SanityManager.ASSERT(dataFactory != null, "datafactory is null");
277             SanityManager.ASSERT(xactFactory != null, "xactfactory is null");
278             SanityManager.ASSERT(logFactory != null, "logfactory is null");
279         }
280
281
282         resetDefaultLocking();
283
284         // TransactionTable needs this
285
xactFactory.setNewTransactionId((XactId)null, this);
286
287         setIdleState();
288
289         backupBlocked = false;
290
291         /*
292         System.out.println("Xact.constructor: readonly = " + this.readOnly +
293                 ";this = " + this);
294                 */

295     }
296
297
298     /*
299     ** Methods of RawTransaction
300     */

301
302     /**
303     */

304     public final LockFactory getLockFactory() {
305         return xactFactory.getLockFactory();
306     }
307
308     public final DataFactory getDataFactory() {
309         return dataFactory;
310     }
311
312     /**
313         Get cache statistics for the specified cache
314     */

315     public long[] getCacheStats(String JavaDoc cacheName) {
316         return getDataFactory().getCacheStats(cacheName);
317     }
318
319     /**
320         Reset the cache statistics for the specified cache
321     */

322     public void resetCacheStats(String JavaDoc cacheName) {
323         getDataFactory().resetCacheStats(cacheName);
324     }
325
326     /**
327         Return true if any transaction is currently blocked, even if not by
328         this transaction.
329
330      */

331     public boolean anyoneBlocked() {
332         return getLockFactory().anyoneBlocked();
333     }
334
335     public DynamicByteArrayOutputStream getLogBuffer() {
336
337         if (logBuffer == null) {
338             logBuffer = new DynamicByteArrayOutputStream(1024);
339         } else {
340             logBuffer.reset();
341         }
342
343         return logBuffer;
344     }
345     
346     /** Log and apply a compensation operation.
347         Only need to write out the compensation op itself, the optional data has already
348         been written by the rollforward operation this is attempting to undo.
349
350         @see RawTransaction#logAndDo
351         
352         @exception StandardException Standard cloudscape exception policy
353     */

354     public void logAndUndo(Compensation compensation, LogInstant undoInstant, LimitObjectInput in)
355          throws StandardException
356     {
357         if (SanityManager.DEBUG) {
358             SanityManager.ASSERT(logStart != null);
359         }
360
361         setActiveState();
362
363         if (state == ACTIVE)
364             setUpdateState();
365
366         seenUpdates = true;
367         
368         LogInstant clrInstant = logger.logAndUndo(this, compensation, undoInstant, in);
369
370         setLastLogInstant(clrInstant);
371
372         // set the top savepoint to rollback to this record if it doesn't yet have a point saved
373
if ((savePoints != null) && !savePoints.empty()) {
374
375             SavePoint sp = (SavePoint) savePoints.peek();
376             if (sp.getSavePoint() == null)
377                 sp.setSavePoint(clrInstant);
378         }
379     }
380
381     /**
382         Add this to the xactFactory list of update transaction.
383     */

384     public void addUpdateTransaction(int transactionStatus)
385     {
386         // during runtime, rollbackFirst == recoveryRolblackFirst(), but during
387
// recovery redo, we only use a regular transaction, so we need to get
388
// the rollbackFirst status from the log record
389
//
390
// If my Id is null, I have no identity, makes no sense to add it to a
391
// transaction table where my identity will be saved and restored
392
if (myId != null)
393             xactFactory.addUpdateTransaction(myId, this, transactionStatus);
394
395     }
396
397     /** Remove this from the xactFactory list of update transaction. */
398     public void removeUpdateTransaction()
399     {
400         if (myId != null)
401             xactFactory.removeUpdateTransaction(myId);
402
403         // If my Id is null, I have no identity, makes no sense to remove it
404
// from transaction table
405
}
406
407     /** Remove this from the xactFactory list of update transaction. */
408     public void prepareTransaction()
409     {
410         // RESOLVE - should I be changing the state to PREPARE?
411

412         if (myId != null)
413         {
414             // If my Id is null, I have no identity, makes no sense to set
415
// my state in the transaction table.
416

417             xactFactory.prepareTransaction(myId);
418         }
419     }
420
421     /**
422         Set the log instant for the first log record written by this transaction.
423     */

424     public void setFirstLogInstant(LogInstant instant)
425     {
426         if (SanityManager.DEBUG) {
427             SanityManager.ASSERT(instant != null);
428             SanityManager.ASSERT(logStart == null);
429         }
430
431         logStart = instant;
432     }
433
434     /**
435         Get the log instant for the first log record written by this transaction.
436     */

437     public LogInstant getFirstLogInstant()
438     {
439         return logStart;
440     }
441
442     /**
443         Set the log instant for the last log record written by this transaction.
444     */

445     public void setLastLogInstant(LogInstant instant)
446     {
447         if (SanityManager.DEBUG) {
448             SanityManager.ASSERT(instant != null);
449         }
450
451         logLast = instant;
452     }
453
454     /**
455         Get the log instant for the last log record written by this transaction.
456     */

457     public LogInstant getLastLogInstant()
458     {
459         return logLast;
460     }
461
462     /**
463         Set my transaction identifier.
464     */

465     public void setTransactionId(GlobalTransactionId extid, TransactionId localid) {
466
467         if (SanityManager.DEBUG) {
468
469             //SanityManager.ASSERT(myGlobalId == null, "my globalId is not null");
470
if (!(state == IDLE || state == Xact.ACTIVE ||
471                   (state== CLOSED && justCreated)))
472             {
473                 SanityManager.THROWASSERT(
474                     "my state is not idle nor active " + state);
475             }
476         }
477
478         myGlobalId = extid;
479         myId = localid;
480
481         if (SanityManager.DEBUG)
482         {
483             if (SanityManager.DEBUG_ON("XATrace") && extid != null)
484             {
485                 SanityManager.DEBUG(
486                     "XATrace","setting xid: " + myId + " " + myGlobalId
487                                + " state " + state + " " + this);
488
489                 SanityManager.showTrace(new Throwable JavaDoc());
490                 // Thread.dumpStack();
491
}
492         }
493
494     }
495
496     public void setTransactionId(Loggable beginXact, TransactionId localId)
497     {
498         if (SanityManager.DEBUG) {
499             // SanityManager.ASSERT(myId == null);
500
SanityManager.ASSERT((state == IDLE) || (state == ACTIVE));
501             SanityManager.ASSERT(beginXact instanceof BeginXact);
502         }
503
504         myId = localId;
505         myGlobalId = ((BeginXact)beginXact).getGlobalId();
506     }
507
508     /*
509     ** Methods of Transaction
510     */

511
512     /**
513         The default value for LOCKS_ESCALATION_THRESHOLD
514         @exception StandardException Standard cloudscape exception policy
515      */

516     public void setup(PersistentSet set)
517         throws StandardException {
518
519         int escalationThreshold = PropertyUtil.getServiceInt(set,
520             Property.LOCKS_ESCALATION_THRESHOLD,
521             Property.MIN_LOCKS_ESCALATION_THRESHOLD,
522             Integer.MAX_VALUE,
523             Property.DEFAULT_LOCKS_ESCALATION_THRESHOLD);
524
525
526         getLockFactory().setLimit(this, this, escalationThreshold, this);
527
528     }
529
530     /**
531         get the Global (external to raw store) transaction id that is unique
532         across all raw stores
533     */

534     public final GlobalTransactionId getGlobalId()
535     {
536
537         return myGlobalId;
538     }
539
540     public final ContextManager getContextManager()
541     {
542         return(xc.getContextManager());
543     }
544
545     /**
546      * Get the compatibility space of the transaction.
547      * <p>
548      * Returns an object that can be used with the lock manager to provide
549      * the compatibility space of a transaction. 2 transactions with the
550      * same compatibility space will not conflict in locks. The usual case
551      * is that each transaction has it's own unique compatibility space.
552      * <p>
553      *
554      * @return The compatibility space of the transaction.
555      **/

556     public Object JavaDoc getCompatibilitySpace()
557     {
558         if (SanityManager.DEBUG)
559         {
560             SanityManager.ASSERT(
561                 compatibilitySpace != null,
562                 "cannot have a null compatibilitySpace.");
563         }
564
565         return(this.compatibilitySpace);
566     }
567
568
569     /**
570         get the short (internal to raw store) transaction id that is unique
571         only for this raw store
572     */

573     public final TransactionId getId() {
574
575         if (SanityManager.DEBUG)
576             SanityManager.ASSERT(
577                 myId != null, "cannot have a transaction with null id");
578
579         return myId;
580     }
581
582     /**
583         Get the transaction id without sanity check, this should only be called
584         by a cloned TransactionTableEntry
585      */

586     protected final TransactionId getIdNoCheck()
587     {
588         return myId;
589     }
590
591     /**
592         Get my transaction context Id
593     */

594     public final String JavaDoc getContextId()
595     {
596         return (xc == null) ? null : xc.getIdName();
597     }
598
599
600     /**
601         Get the current default locking policy for all operations within this
602         transaction. The transaction is initially started with a default
603         locking policy equivalent to
604         <PRE>
605              newLockingPolicy(
606               LockingPolicy.MODE_RECORD, TransactionController.ISOLATION_SERIALIZABLE, true);
607         </PRE>
608         This default can be changed by subsequent calls to
609         setDefaultLockingPolicy(LockingPolicy policy).
610
611         @see Transaction#getDefaultLockingPolicy
612
613
614         @return The current default locking policy in this transaction.
615     */

616
617     public LockingPolicy getDefaultLockingPolicy()
618     {
619         return(defaultLocking);
620     }
621
622
623     /** @see Transaction#newLockingPolicy */
624     public final LockingPolicy newLockingPolicy(int mode, int isolation, boolean stricterOk) {
625
626         return xactFactory.getLockingPolicy(mode, isolation, stricterOk);
627
628     }
629
630     /** @see Transaction#setDefaultLockingPolicy */
631     public final void setDefaultLockingPolicy(LockingPolicy policy) {
632
633         if (policy == null)
634             policy = xactFactory.getLockingPolicy(LockingPolicy.MODE_NONE, TransactionController.ISOLATION_NOLOCK, false);
635         defaultLocking = policy;
636     }
637     
638     /**
639       @exception StandardException Standard cloudscape exception policy
640     */

641     public LogInstant commit() throws StandardException
642     {
643         return commit(COMMIT_SYNC);
644     }
645
646     /**
647       @exception StandardException Standard cloudscape exception policy
648     */

649     public LogInstant commitNoSync(int commitflag) throws StandardException
650     {
651         if (SanityManager.DEBUG)
652         {
653             int checkflag = Transaction.RELEASE_LOCKS|Transaction.KEEP_LOCKS;
654
655             SanityManager.ASSERT((commitflag & checkflag) != 0,
656               "commitNoSync must specify whether to keep or release locks");
657
658             SanityManager.ASSERT((commitflag & checkflag) != checkflag,
659               "cannot set both RELEASE and KEEP LOCKS flag");
660
661             if ((commitflag &
662                  TransactionController.READONLY_TRANSACTION_INITIALIZATION)
663                     != 0)
664             {
665                 SanityManager.ASSERT((state == IDLE) || (state == ACTIVE));
666             }
667         }
668
669         // Short circuit commit no sync if we are still initializing the
670
// transaction. Before a new transaction object is returned to the
671
// user, it is "commit'ed" many times using commitNoSync with
672
// TransactionController.READONLY_TRANSACTION_INITIALIZATION flag to
673
// release read locks and reset the transaction state back to Idle.
674
// If nothing has actually happened to the transaction object, return
675
// right away and avoid the cost of going thru the commit logic.
676
//
677
if (state == IDLE && savePoints == null &&
678             ((commitflag & TransactionController.READONLY_TRANSACTION_INITIALIZATION) != 0))
679             return null;
680
681         return commit(COMMIT_NO_SYNC | commitflag);
682     }
683
684     /**
685       @exception StandardException Standard cloudscape exception policy
686       @see Transaction#commit
687     */

688
689     /**
690      * Do work of commit that is common to xa_prepare and commit.
691      * <p>
692      * Do all the work necessary as part of a commit up to and including
693      * writing the commit log record. This routine is used by both prepare
694      * and commit. The work post commit is done by completeCommit().
695      * <p>
696      *
697      * @param commitflag various flavors of commit.
698      *
699      * @exception StandardException Standard exception policy.
700      * @see Transaction#commit
701      **/

702     private LogInstant prepareCommit(int commitflag)
703         throws StandardException
704     {
705         LogInstant flushTo = null;
706
707         if (state == CLOSED)
708         {
709             throw StandardException.newException(
710                     SQLState.XACT_PROTOCOL_VIOLATION);
711         }
712
713         if (SanityManager.DEBUG)
714         {
715             if ((commitflag & Transaction.KEEP_LOCKS) != 0)
716             {
717                 // RESOLVE (mikem) - prepare actually want's to keep locks
718
// during a prepare.
719
SanityManager.ASSERT(
720                     (((commitflag & COMMIT_NO_SYNC) != 0) ||
721                      ((commitflag & COMMIT_PREPARE) != 0)),
722                     "can keep locks around only in commitNoSync or prepare");
723
724                 SanityManager.ASSERT(
725                     isUserTransaction(),
726                     "KEEP_LOCKS can only be set on user transaction commits");
727             }
728         }
729
730
731         try {
732
733             preComplete(COMMIT);
734
735             // flush the log.
736

737             if (seenUpdates) {
738
739                 EndXact ex =
740                     new EndXact(
741                         getGlobalId(),
742                         ((commitflag & COMMIT_PREPARE) == 0 ?
743                              END_COMMITTED : END_PREPARED)
744                                 | statusForEndXactLog());
745
746                 flushTo = logger.logAndDo(this, ex);
747
748                 if (xactFactory.flushLogOnCommit(xc.getIdName()))
749                 {
750                     if ((commitflag & COMMIT_SYNC) == 0)
751                     {
752                         // not flushing the log right now, subsequent commit
753
// will need to flush the log
754
needSync = true;
755                     }
756                     else
757                     {
758                         logger.flush(flushTo);
759                         needSync = false;
760                     }
761                 }
762             }
763             else if (needSync && (commitflag & COMMIT_SYNC) != 0)
764             {
765                 // this transaction object was used to lazily commit some
766
// previous transaction without syncing. Now that we commit
767
// for real, make sure any outstanding log is flushed.
768
logger.flushAll();
769                 needSync = false;
770             }
771         }
772         catch (StandardException se)
773         {
774
775             // This catches any exceptions that have Transaction severity
776
// or less (e.g. Statement exception). If we received any lesser
777
// error then we abort the transaction anyway.
778

779             if (se.getSeverity() < ExceptionSeverity.TRANSACTION_SEVERITY)
780             {
781                 throw StandardException.newException(
782                         SQLState.XACT_COMMIT_EXCEPTION, se);
783             }
784
785             throw se;
786
787         }
788         return flushTo;
789     }
790
791     /**
792      * Do work to complete a commit which is not just a prepare.
793      * <p>
794      * Releases locks, does post commit work, and moves the state of the
795      * transaction to IDLE.
796      * <p>
797      *
798      * @param commitflag various flavors of commit.
799      *
800      * @exception StandardException Standard exception policy.
801      **/

802     private void completeCommit(int commitflag)
803         throws StandardException
804     {
805         // this releases our logical locks if commitflag don't have KEEP_LOCKS.
806
postComplete(commitflag, COMMIT);
807
808         // this transfer postCommitWorks to PostCommit queue
809
if ((commitflag & Transaction.KEEP_LOCKS) == 0)
810         {
811             // if locks are released, start post commit processing
812
postTermination();
813         }
814         else
815         {
816             // RESOLVE: actually, this transaction may not have outstanding
817
// locks. It didn't release them, but that doesn't mean it has got
818
// them. This is mostly harmless.
819

820             if (SanityManager.DEBUG)
821                 SanityManager.ASSERT(myGlobalId == null,
822                  "calling commit with KEEP_LOCKS on a global transaction");
823
824             // we have unreleased locks, the transaction has resource and
825
// therefore is "active"
826
setActiveState();
827         }
828
829         myGlobalId = null;
830         return;
831     }
832
833     /**
834       @exception StandardException Standard cloudscape exception policy
835       @see Transaction#commit
836     */

837     private LogInstant commit(int commitflag)
838         throws StandardException
839     {
840         if (SanityManager.DEBUG)
841         {
842             if (SanityManager.DEBUG_ON("XATrace"))
843                 SanityManager.DEBUG("XATrace","commiting ");
844         }
845
846         LogInstant flushTo = prepareCommit(commitflag);
847
848         completeCommit(commitflag);
849
850         return(flushTo);
851     }
852
853
854     /**
855         @exception StandardException Standard cloudscape exception policy
856         @see Transaction#abort
857     */

858     public void abort() throws StandardException {
859
860         if (SanityManager.DEBUG)
861         {
862             if (SanityManager.DEBUG_ON("XATrace"))
863                 SanityManager.DEBUG("XATrace","aborting ");
864         }
865
866         if (state == CLOSED)
867         {
868             // I would have leave this in but close() nulls out myGlobalId
869
// if (myGlobalId == null)
870
// {
871
// throw StandardException.newException(
872
// SQLState.XACT_PROTOCOL_VIOLATION);
873
// }
874

875             if (SanityManager.DEBUG)
876             {
877                 // Only global transaction is allowed to abort a closed
878
// transaction.
879
if (!sanityCheck_xaclosed)
880                 {
881                     throw StandardException.newException(
882                             SQLState.XACT_PROTOCOL_VIOLATION);
883                 }
884             }
885
886             // In global transaction, the xact object is closed automatically
887
// on a transaction level rollback. This cause error handling to
888
// fail because when upper level contexts in the context manager
889
// unwinds, it calls abort again, which would have caused a
890
// protocol violation.
891
return;
892         }
893
894         /* This routine is never called by recovery redo, only by runtime and
895            recovery undo. During recovery undo, even though no log record has
896            been written by this session, it still need to rollback the
897            incomplete transaction.
898
899            The way to tell if this trasanction has ever written a log record is
900            by FirstLogInstant.
901         */

902
903         try {
904             preComplete(ABORT);
905
906             // rollback the log - if logger is null, nothing I can do, crash.
907
if (getFirstLogInstant() != null) {
908                 if (logger == null)
909                 {
910                     throw StandardException.newException(SQLState.XACT_CANNOT_ABORT_NULL_LOGGER);
911                 }
912
913                 logger.undo(
914                     this, getId(), getFirstLogInstant(), getLastLogInstant());
915
916                 EndXact ex = new EndXact(getGlobalId(),
917                                          END_ABORTED | statusForEndXactLog());
918
919                 logger.flush(logger.logAndDo(this, ex));
920             }
921             else if (needSync)
922             {
923                 // this transaction object was used to lazily commit some
924
// previous transaction without syncing. Now that we abort
925
// for real, make sure any outstanding log is flushed.
926
logger.flushAll();
927             }
928
929             needSync = false;
930             
931         } catch (StandardException se) {
932
933             // This catches any exceptions that have System severity
934
// or less (e.g. Statement exception).
935
//
936
// If we have any error during an undo we just shut the system
937
// down, this is a bit drastic but it does ensure that the database
938
// will not become corrupted by changes that see half committed
939
// changes.
940
//
941
// Note that we do not release our locks if we come thorugh this
942
// path, if we did then another transaction could complete before
943
// the system shuts down and make changes based upon the changes
944
// that we couldn't back out.
945

946             if (se.getSeverity() < ExceptionSeverity.SYSTEM_SEVERITY)
947             {
948                 throw logFactory.markCorrupt(
949                     StandardException.newException(
950                         SQLState.XACT_ABORT_EXCEPTION, se));
951             }
952
953             throw se;
954
955         }
956
957         // this releases our locks.
958
postComplete(0, ABORT);
959
960         // get rid of all post commit work - we aborted, therefore no post
961
// commit work
962
if (postCommitWorks != null && !postCommitWorks.isEmpty())
963         {
964             postCommitWorks.clear();
965         }
966
967         // Now do post termination work - must do this after the rollback is
968
// complete because the rollback itself may generate postTermination
969
// work.
970
postTermination();
971
972         myGlobalId = null;
973     }
974
975     /**
976      * During recovery re-prepare a transaction.
977      * <p>
978      * After redo() and undo(), this routine is called on all outstanding
979      * in-doubt (prepared) transactions. This routine re-acquires all
980      * logical write locks for operations in the xact, and then modifies
981      * the transaction table entry to make the transaction look as if it
982      * had just been prepared following startup after recovery.
983      * <p>
984      * This routine is only called during Recovery.
985      *
986      * @exception StandardException Standard exception policy.
987      **/

988     public void reprepare()
989         throws StandardException
990     {
991         if (state == CLOSED)
992         {
993             throw StandardException.newException(
994                     SQLState.XACT_PROTOCOL_VIOLATION);
995         }
996
997         // Should only be called during recovery on global transactions,
998
// after redo and undo.
999
if (SanityManager.DEBUG)
1000        {
1001            SanityManager.ASSERT(myGlobalId != null);
1002            SanityManager.ASSERT(state == PREPARED);
1003        }
1004
1005        try
1006        {
1007            if (logger == null)
1008            {
1009                throw StandardException.newException(
1010                        SQLState.XACT_CANNOT_ABORT_NULL_LOGGER);
1011            }
1012
1013            // temporarily set state back to UPDATE, so that the reprepare()
1014
// call can do operations on the xact that would "normally" be
1015
// disallowed on a prepared xact - like opening a container to
1016
// lock it.
1017

1018            state = UPDATE;
1019
1020            // re-prepare the transaction.
1021
logger.reprepare(
1022                this, getId(), getFirstLogInstant(), getLastLogInstant());
1023
1024            // make sure the xact is prepare state when we are done.
1025
state = PREPARED;
1026
1027            seenUpdates = true;
1028
1029        } catch (StandardException se) {
1030
1031            // This catches any exceptions that have System severity
1032
// or less (e.g. Statement exception).
1033
//
1034
// If we have any error during an reprepare we just shut the system
1035
// down, this is a bit drastic but it does ensure that the database
1036
// will not become corrupted by changes that see data that is part
1037
// of a prepared transaction.
1038
//
1039
// Note that we do not release our locks if we come thorugh this
1040
// path, if we did then another transaction could complete before
1041
// the system shuts down and make changes based upon the changes
1042
// that we couldn't back out.
1043

1044            if (se.getSeverity() < ExceptionSeverity.SYSTEM_SEVERITY)
1045            {
1046                throw logFactory.markCorrupt(
1047                    StandardException.newException(
1048                        SQLState.XACT_ABORT_EXCEPTION, se));
1049            }
1050
1051            throw se;
1052        }
1053
1054        // RESOLVE - something needs to change the state of the XACT so that
1055
// it is not recovery state anymore?
1056
}
1057
1058    /**
1059        If this transaction is not idle, abort it. After this call close().
1060
1061        @exception StandardException Standard Cloudscape error policy
1062        Thrown if the transaction is not idle.
1063
1064        
1065    */

1066    public void destroy() throws StandardException
1067    {
1068        if (state != CLOSED)
1069            abort();
1070
1071        close();
1072    }
1073
1074    /**
1075        @exception StandardException Standard cloudscape exception policy
1076        @exception StandardException Thrown if the transaction is not idle, the
1077        transaction remains open.
1078        @see Transaction#close
1079
1080        @exception StandardException Standard cloudscape policy
1081    */

1082    public void close() throws StandardException {
1083
1084
1085        /*
1086
1087        if (((LogToFile) logFactory).inRedo)
1088        {
1089            SanityManager.showTrace(new Throwable());
1090            SanityManager.THROWASSERT("in Redo while in close");
1091        }
1092        */

1093
1094        switch (state) {
1095        case CLOSED:
1096            return;
1097        case IDLE:
1098            break;
1099        default:
1100            throw StandardException.newException(
1101                SQLState.XACT_TRANSACTION_NOT_IDLE);
1102        }
1103
1104        if (SanityManager.DEBUG) {
1105
1106            SanityManager.ASSERT(xc.getTransaction() == this);
1107
1108            SanityManager.ASSERT(
1109                (postCommitWorks == null || postCommitWorks.isEmpty()),
1110                "cannot close a transaction with post commit work pending");
1111
1112            // use this for sanity checking
1113
if (myGlobalId != null)
1114                sanityCheck_xaclosed = true;
1115        }
1116
1117        getLockFactory().clearLimit(this, this);
1118
1119        if (SanityManager.DEBUG)
1120        {
1121            if (SanityManager.DEBUG_ON("XATrace"))
1122                SanityManager.DEBUG("XATrace","closing " + myId + " " + myGlobalId);
1123            // Thread.dumpStack();
1124
}
1125
1126        // if we just finished recovery myId could be null
1127
if (myId != null)
1128            xactFactory.remove((XactId)myId);
1129
1130        xc.popMe();
1131        xc = null;
1132
1133        myGlobalId = null;
1134        myId = null;
1135        logStart = null;
1136        logLast = null;
1137
1138
1139
1140        /* MT - no need to synchronize it, the state is current IDLE which will
1141         * return the same result to isActive() as if it is CLOSED
1142         */

1143        state = CLOSED;
1144
1145    }
1146
1147    /**
1148        Log the operation and do it.
1149
1150        If this transaction has not generated any log records prior to this,
1151        then log a beginXact log record.
1152
1153        If the passed in operation is null, then do nothing (after logging the
1154        beginXact if needed).
1155
1156        @exception StandardException Standard cloudscape exception policy
1157        @see Transaction#logAndDo
1158    */

1159    public void logAndDo(Loggable operation) throws StandardException {
1160
1161        LogInstant instant = null;
1162
1163        if (logger == null)
1164            getLogger();
1165
1166        if (logger == null)
1167        {
1168            throw StandardException.newException(
1169                    SQLState.XACT_CANNOT_LOG_CHANGE);
1170        }
1171
1172        setActiveState();
1173
1174        if (state == ACTIVE)
1175        {
1176            instant =
1177                logger.logAndDo(
1178                    this,
1179                    new BeginXact(getGlobalId(), statusForBeginXactLog()));
1180
1181            setUpdateState();
1182        }
1183        seenUpdates = true;
1184
1185        if (operation != null)
1186        {
1187            instant = logger.logAndDo(this, operation);
1188            if (instant != null) {
1189                setLastLogInstant(instant);
1190
1191                if ((savePoints != null) && !savePoints.empty()) {
1192                    for (int i = savePoints.size() - 1; i >= 0; i--) {
1193                        // set the top savepoint to rollback to this record if
1194
// it doesn't yet have a point saved
1195

1196                        SavePoint sp = (SavePoint) savePoints.elementAt(i);
1197                        if (sp.getSavePoint() == null) {
1198                            sp.setSavePoint(instant);
1199                        } else
1200                            break;
1201                    }
1202                }
1203            }
1204
1205        }
1206        else
1207        {
1208            if (instant != null)
1209                setLastLogInstant(instant);
1210        }
1211
1212    }
1213
1214    public void addPostCommitWork(Serviceable work)
1215    {
1216        if (recoveryTransaction)
1217            return;
1218
1219        if (postCommitWorks == null)
1220            postCommitWorks = new ArrayList JavaDoc(1);
1221        postCommitWorks.add(work);
1222    }
1223
1224    public void addPostTerminationWork(Serviceable work)
1225    {
1226        if (recoveryTransaction)
1227            return;
1228
1229        if (postTerminationWorks == null)
1230            postTerminationWorks = new ArrayList JavaDoc(2);
1231        postTerminationWorks.add(work);
1232    }
1233
1234
1235    /**
1236        Return a record handle that is initialized to the given page number and
1237        record id.
1238
1239        @exception StandardException Standard cloudscape exception policy.
1240
1241        @param segmentId segment where the RecordHandle belongs.
1242        @param containerId container where the RecordHandle belongs.
1243        @param pageNumber the page number of the RecordHandle.
1244        @param recordId the record id of the RecordHandle.
1245
1246        @see RecordHandle
1247    */

1248// public RecordHandle makeRecordHandle(long segmentId, long containerId, long pageNumber, int recordId)
1249
// throws StandardException
1250
// {
1251
// return(this.dataFactory.makeRecordHandle(
1252
// segmentId, containerId, pageNumber, recordId));
1253
// }
1254

1255    /**
1256        @exception StandardException Standard cloudscape exception policy
1257        @see Transaction#openContainer
1258    */

1259    public ContainerHandle openContainer(ContainerKey containerId, int mode)
1260        throws StandardException {
1261
1262        return openContainer(containerId, defaultLockingPolicy(), mode);
1263    }
1264
1265    /**
1266        @exception StandardException Standard cloudscape exception policy
1267        @see Transaction#openContainer
1268    */

1269    public ContainerHandle openContainer(ContainerKey containerId, LockingPolicy locking, int mode)
1270        throws StandardException {
1271
1272        setActiveState();
1273
1274        if (locking == null)
1275            locking = xactFactory.getLockingPolicy(LockingPolicy.MODE_NONE, TransactionController.ISOLATION_NOLOCK, false);
1276
1277        return dataFactory.openContainer(this, containerId, locking, mode);
1278    }
1279
1280    /**
1281        Open a container that may already have been dropped.
1282
1283        @exception StandardException Standard cloudscape exception policy
1284        @see RawTransaction#openDroppedContainer
1285    */

1286    public RawContainerHandle openDroppedContainer(ContainerKey containerId, LockingPolicy locking)
1287         throws StandardException
1288    {
1289        setActiveState();
1290
1291        if (locking == null)
1292            locking = xactFactory.getLockingPolicy(LockingPolicy.MODE_NONE, TransactionController.ISOLATION_NOLOCK, false);
1293
1294        RawContainerHandle hdl = null;
1295
1296        // first try to open it for update, if that fail, open it for read
1297
try
1298        {
1299            hdl = dataFactory.openDroppedContainer(this, containerId, locking,
1300                                            ContainerHandle.MODE_FORUPDATE);
1301        }
1302        catch (StandardException se)
1303        {
1304            // if this also fail, throw exception
1305
hdl = dataFactory.openDroppedContainer(this, containerId,
1306                                            locking,
1307                                            ContainerHandle.MODE_READONLY);
1308        }
1309
1310        return hdl;
1311    }
1312
1313
1314    /**
1315        @exception StandardException Standard cloudscape exception policy
1316        @see Transaction#addContainer
1317    */

1318    public long addContainer(long segmentId, long containerid, int mode, Properties JavaDoc tableProperties, int temporaryFlag)
1319        throws StandardException {
1320
1321        setActiveState();
1322
1323        return dataFactory.addContainer(this, segmentId, containerid, mode, tableProperties, temporaryFlag);
1324    }
1325
1326    /**
1327        @exception StandardException Standard cloudscape exception policy
1328        @see Transaction#addAndLoadStreamContainer
1329    */

1330    public long addAndLoadStreamContainer(long segmentId, Properties JavaDoc tableProperties, RowSource rowSource)
1331        throws StandardException {
1332
1333        setActiveState();
1334
1335        return dataFactory.addAndLoadStreamContainer(this, segmentId, tableProperties, rowSource);
1336
1337    }
1338
1339    /**
1340        @exception StandardException Standard cloudscape exception policy
1341        @see Transaction#openStreamContainer
1342    */

1343    public StreamContainerHandle openStreamContainer(
1344    long segmentId,
1345    long containerId,
1346    boolean hold)
1347        throws StandardException
1348    {
1349        setActiveState();
1350
1351        return(
1352            dataFactory.openStreamContainer(
1353                this, segmentId, containerId, hold));
1354    }
1355
1356    /**
1357        @see Transaction#dropStreamContainer
1358        @exception StandardException Standard Cloudscape error policy
1359    */

1360    public void dropStreamContainer(long segmentId, long containerId)
1361        throws StandardException {
1362
1363        setActiveState();
1364
1365        dataFactory.dropStreamContainer(this, segmentId, containerId);
1366    }
1367
1368    /**
1369        Recreate a container during redo recovery.
1370
1371        Used only during redo recovery while processing log records which
1372        are trying to create a container, and no valid container is found
1373        in the database.
1374
1375        @exception StandardException Standard cloudscape exception policy
1376        @see RawTransaction#reCreateContainerForRedoRecovery
1377     */

1378    public void reCreateContainerForRedoRecovery
1379        (long segmentId, long containerId, ByteArray containerInfo)
1380        throws StandardException
1381    {
1382        setActiveState();
1383
1384        dataFactory.reCreateContainerForRedoRecovery(
1385            this, segmentId, containerId, containerInfo);
1386    }
1387
1388    /**
1389        @see Transaction#dropContainer
1390        @exception StandardException Standard Cloudscape error policy
1391    */

1392    public void dropContainer(ContainerKey containerId)
1393        throws StandardException {
1394
1395        setActiveState();
1396
1397        dataFactory.dropContainer(this, containerId);
1398    }
1399
1400    /**
1401        @exception StandardException Standard cloudscape exception policy
1402        @see Transaction#setSavePoint
1403    */

1404    public int setSavePoint(String JavaDoc name, Object JavaDoc kindOfSavepoint)
1405        throws StandardException
1406    {
1407
1408        if (kindOfSavepoint != null && kindOfSavepoint instanceof String JavaDoc)
1409        {
1410            //that means we are trying to set a SQL savepoint
1411

1412            //error if this SQL savepoint is getting nested into other user
1413
// defined savepoints
1414
throwExceptionIfSQLSavepointNotAllowed(kindOfSavepoint);
1415        }
1416
1417        // while setting a savepoint, we just want to see if there is a
1418
// savepoint with the passed name already in the system.
1419
if (getSavePointPosition(name, kindOfSavepoint, false) != -1)
1420        {
1421            throw StandardException.newException(
1422                    SQLState.XACT_SAVEPOINT_EXISTS);
1423        }
1424
1425        if (savePoints == null)
1426            savePoints = new Stack JavaDoc();
1427
1428        savePoints.push(new SavePoint(name, kindOfSavepoint));
1429
1430        if (SanityManager.DEBUG) {
1431
1432            if (SanityManager.DEBUG_ON("memoryLeakTrace")) {
1433
1434                if (savePoints.size() > 20)
1435                    System.out.println("memoryLeakTrace:Xact:savepoints " + savePoints.size());
1436            }
1437        }
1438        return savePoints.size();
1439    }
1440
1441    // SQL savepoint can't be nested inside other user defined savepoints. To
1442
// enforce this, we check if there are already user savepoint(SQL/JDBC)
1443
// defined in the transaction. If yes, then throw an exception
1444

1445    /**
1446        @exception StandardException Standard cloudscape exception policy
1447        @see Transaction#setSavePoint
1448    */

1449    private void throwExceptionIfSQLSavepointNotAllowed(Object JavaDoc kindOfSavepoint)
1450        throws StandardException
1451    {
1452
1453        boolean foundUserSavepoint = false;
1454
1455        if ((savePoints != null) && !savePoints.empty()) {
1456            for (int i = savePoints.size() - 1; i >= 0; i--) {
1457                SavePoint sp = (SavePoint) savePoints.elementAt(i);
1458                if (sp.isThisUserDefinedsavepoint())
1459                {
1460                    //found a user defined savepoint
1461

1462                    foundUserSavepoint = true;
1463                    break;
1464                }
1465            }
1466        }
1467
1468        if (foundUserSavepoint)
1469            throw StandardException.newException(
1470                    SQLState.XACT_MAX_SAVEPOINT_LEVEL_REACHED);
1471    }
1472
1473    /**
1474        @exception StandardException Standard cloudscape exception policy
1475        @see Transaction#releaseSavePoint
1476    */

1477    public int releaseSavePoint(String JavaDoc name, Object JavaDoc kindOfSavepoint)
1478        throws StandardException
1479    {
1480        int position = getSavePointPosition(name, kindOfSavepoint, true);
1481
1482        if (position == -1)
1483        {
1484            // following means this is a JDBC savepoint.
1485
// We prepend i./e. to JDBC savepoint names in JDBC layer.
1486
// Need to trim that here before giving error
1487

1488            if (kindOfSavepoint != null && !(kindOfSavepoint instanceof String JavaDoc))
1489            {
1490                // this means this is a JDBC savepoint.
1491
// We append "i."/"e." to JDBC savepoint names.
1492
// Trimming that here before giving error
1493

1494                name = name.substring(2);
1495            }
1496            throw StandardException.newException(
1497                        SQLState.XACT_SAVEPOINT_NOT_FOUND, name);
1498        }
1499
1500        popSavePoints(position, true);
1501        return savePoints.size();
1502    }
1503
1504    /**
1505        @exception StandardException Standard cloudscape exception policy
1506        @see Transaction#rollbackToSavePoint
1507    */

1508    public int rollbackToSavePoint(String JavaDoc name, Object JavaDoc kindOfSavepoint)
1509        throws StandardException
1510    {
1511        int position = getSavePointPosition(name, kindOfSavepoint, true);
1512
1513        if (position == -1)
1514        {
1515            // following means this is a JDBC savepoint.
1516
// We append i./e. to JDBC savepoint names in JDBC layer.
1517
// Need to trim that here before giving error
1518
if (kindOfSavepoint != null && !(kindOfSavepoint instanceof String JavaDoc))
1519                name = name.substring(2);
1520            throw StandardException.newException(
1521                        SQLState.XACT_SAVEPOINT_NOT_FOUND, name);
1522        }
1523
1524        notifyObservers(SAVEPOINT_ROLLBACK);
1525
1526        popSavePoints(position, false);
1527        return savePoints.size();
1528    }
1529
1530
1531    /*
1532    ** Implementation specific methods
1533    */

1534
1535    /**
1536        Get the Logger object used to write log records to the transaction log.
1537    */

1538    private void getLogger() {
1539
1540        logger = logFactory.getLogger();
1541    }
1542
1543
1544    /**
1545        Transform this identity to the one stored in transaction table entry.
1546        Used by recovery only!
1547    */

1548    protected void assumeIdentity(TransactionTableEntry ent)
1549    {
1550        if (ent != null)
1551        {
1552            if (SanityManager.DEBUG)
1553            {
1554                SanityManager.ASSERT(ent.getXid() != null, "TTE.xid is null");
1555
1556                SanityManager.ASSERT(
1557                    ent.getFirstLog() != null, "TTE.firstLog is null");
1558            }
1559
1560            // I am the transaction that is using this TransactionTableEntry
1561
ent.setXact(this);
1562
1563            myId = ent.getXid();
1564            logStart = ent.getFirstLog();
1565            logLast = ent.getLastLog();
1566
1567
1568            // This routine is only used by recovery to assume the identity
1569
// of the transaction for each log record during redo and undo.
1570
// For this purpose the transaction should act like a local
1571
// transaction, and ignore the fact that it may or may not be
1572
// an XA global transaction - this is necessary as global
1573
// transactions can only be committed or aborted once, but
1574
// recovery needs to reuse the same xact over and over again.
1575
// For this purpose set myGlobalId to null so it is treated as
1576
// a local xact - the entry in the transaction table will
1577
// track that in reality it is a global id and remember it's
1578
// value.
1579
myGlobalId = null;
1580
1581            // I am very active
1582
if (state == IDLE)
1583                state = ACTIVE;
1584
1585            if (SanityManager.DEBUG)
1586            {
1587                if (state != ACTIVE && state != UPDATE && state != PREPARED)
1588                    SanityManager.THROWASSERT(
1589                        "recovery transaction have illegal state " + state +
1590                        "xact = " + this);
1591            }
1592
1593
1594
1595            if (logger == null)
1596                getLogger();
1597
1598            savedEndStatus = 0;
1599        }
1600        else
1601        {
1602            myGlobalId = null;
1603            myId = null;
1604            logStart = null;
1605            logLast = null;
1606            state = IDLE;
1607        }
1608    }
1609
1610    /**
1611     * Assume complete identity of the given Transaction Table Entry.
1612     * <p>
1613     * Used by the final phase of the recovery to create new real transactions
1614     * to take on the identity of in-doubt prepared transactions found during
1615     * redo. Need to assume the globalId.
1616     *
1617     * @param ent The original entry we are assuming the identity of.
1618     *
1619     **/

1620    protected void assumeGlobalXactIdentity(
1621    TransactionTableEntry ent)
1622    {
1623        if (SanityManager.DEBUG)
1624        {
1625            SanityManager.ASSERT(ent != null);
1626            SanityManager.ASSERT(ent.getXid() != null, "TTE.xid is null");
1627            SanityManager.ASSERT(
1628                ent.getFirstLog() != null, "TTE.firstLog is null");
1629            SanityManager.ASSERT(ent.isPrepared());
1630        }
1631
1632        myId = ent.getXid();
1633        myGlobalId = ent.getGid();
1634        logStart = ent.getFirstLog();
1635        logLast = ent.getLastLog();
1636
1637        // I am very active
1638
if (state == IDLE)
1639        {
1640            if (SanityManager.DEBUG)
1641            {
1642                if (SanityManager.DEBUG_ON("XATrace"))
1643                    SanityManager.DEBUG("XATrace","set active state in assume Global XactIdentity");
1644            }
1645
1646            state = ACTIVE;
1647        }
1648
1649        if (ent.isPrepared())
1650            state = PREPARED;
1651
1652        // I am the transaction that is using this TransactionTableEntry
1653
ent.setXact(this);
1654
1655        if (SanityManager.DEBUG)
1656        {
1657            if (state != ACTIVE && state != UPDATE && state != PREPARED)
1658                SanityManager.THROWASSERT(
1659                    "recovery transaction have illegal state " + state +
1660                    "xact = " + this);
1661        }
1662
1663        if (logger == null)
1664            getLogger();
1665
1666        savedEndStatus = 0;
1667
1668        if (SanityManager.DEBUG)
1669        {
1670            SanityManager.ASSERT(myGlobalId != null);
1671
1672            // at least right now only prepared xact call this during recovery.
1673
SanityManager.ASSERT(state == PREPARED);
1674        }
1675    }
1676
1677
1678    /**
1679        Move the transaction into the update state.
1680        @exception StandardException problem setting a transaction id
1681    */

1682    private final void setUpdateState() throws StandardException {
1683
1684        /*
1685        System.out.println("calling setUpdateState() - readOnly = " + readOnly +
1686                ";this = " + this);
1687        System.out.println("calling setUpdateState():");
1688                (new Throwable()).printStackTrace();
1689        */

1690
1691        if (SanityManager.DEBUG)
1692        {
1693            SanityManager.ASSERT(
1694                state == ACTIVE,
1695                "setting update state without first going thru ACTIVE state");
1696
1697            SanityManager.ASSERT(
1698                myId != null,
1699                "setting update state to a trasnaction with Null ID");
1700        }
1701
1702        if (SanityManager.DEBUG)
1703        {
1704            if (SanityManager.DEBUG_ON("XATrace"))
1705            {
1706                SanityManager.DEBUG("XATrace","set update state");
1707                SanityManager.showTrace(new Throwable JavaDoc());
1708            }
1709        }
1710
1711        if (readOnly)
1712        {
1713            throw StandardException.newException(
1714                SQLState.XACT_PROTOCOL_VIOLATION);
1715        }
1716
1717        state = UPDATE;
1718    }
1719
1720    protected void setIdleState() {
1721
1722        if (SanityManager.DEBUG)
1723            SanityManager.ASSERT(myId != null, "setIdleState got null ID");
1724
1725        if (SanityManager.DEBUG)
1726        {
1727            if (SanityManager.DEBUG_ON("TranTrace"))
1728            {
1729                SanityManager.DEBUG(
1730                    "TranTrace", "transaction going idle " + myId);
1731
1732                SanityManager.showTrace(new Throwable JavaDoc("TranTrace"));
1733            }
1734        }
1735
1736
1737        /* MT - single thread throught synchronizing this. Even though no other
1738         * thread can call this, they may call isActive which needs to be
1739         * synchronized with state transaction into or out of the idle state
1740         */

1741        // synchronized(this) -- int access, implicit synchronization
1742
// due to atomic action
1743
{
1744            state = IDLE;
1745        }
1746
1747        
1748
1749        seenUpdates = false;
1750
1751        // these fields will NOT be accessed by the checkpoint thread at the
1752
// same time because the doMe method of EndXact removed this
1753
// transaction from the "update transaction" list, therefore when the
1754
// checkpoint writes out the transaction table, this transaction will
1755
// be skipped. OK to just change it without synchronization here.
1756

1757        logStart = null;
1758        logLast = null;
1759
1760        if (SanityManager.DEBUG)
1761        {
1762            if (SanityManager.DEBUG_ON("XATrace"))
1763                SanityManager.DEBUG("XATrace","set idle state : " + state + " " + this);
1764        }
1765
1766    }
1767
1768    protected final void setActiveState() throws StandardException {
1769
1770        if ((state == CLOSED) || (!inAbort() && (state == PREPARED)))
1771        {
1772            // This is where we catch attempted activity on a prepared xact.
1773
throw StandardException.newException(
1774                    SQLState.XACT_PROTOCOL_VIOLATION);
1775        }
1776
1777        if (SanityManager.DEBUG)
1778            SanityManager.ASSERT(myId != null, "setActiveState got null ID");
1779
1780        if (state == IDLE)
1781        {
1782            synchronized(this)
1783            {
1784                state = ACTIVE;
1785            }
1786
1787            if (SanityManager.DEBUG)
1788            {
1789                if (SanityManager.DEBUG_ON("XATrace"))
1790                {
1791                    SanityManager.DEBUG("XATrace","set active state " + this);
1792                    SanityManager.showTrace(new Throwable JavaDoc("XATrace"));
1793                }
1794            }
1795
1796
1797            if (!justCreated)
1798                xactFactory.setNewTransactionId(myId, this);
1799
1800            justCreated = false;
1801
1802            if (SanityManager.DEBUG)
1803            {
1804                if (SanityManager.DEBUG_ON("TranTrace"))
1805                {
1806                    SanityManager.DEBUG(
1807                        "TranTrace", "transaction going active " + myId);
1808
1809                    SanityManager.showTrace(new Throwable JavaDoc("TranTrace"));
1810                }
1811            }
1812
1813        }
1814    }
1815
1816    /**
1817     * Move the state of the transaction from UPDATE to PREPARE.
1818     * <p>
1819     * The state transition should only be from UPDATE to PREPARE. Read-only
1820     * transactions (IDLE and ACTIVE) will never be prepared, they will be
1821     * commited when the prepare is requested. Only Update transactions will
1822     * be allowed to go to prepared state.
1823     * <p>
1824     *
1825     * @exception StandardException Standard exception policy.
1826     **/

1827    protected final void setPrepareState()
1828        throws StandardException
1829    {
1830        if (state == PREPARED || state == CLOSED)
1831        {
1832            throw StandardException.newException(
1833                    SQLState.XACT_PROTOCOL_VIOLATION);
1834        }
1835
1836        if (SanityManager.DEBUG)
1837        {
1838            SanityManager.ASSERT(
1839                state == UPDATE,
1840                "setting PREPARED state without first going thru UPDATE state");
1841            SanityManager.ASSERT(
1842                myId != null,
1843                "setting PREPARED state to a transaction with Null ID");
1844        }
1845
1846        state = PREPARED;
1847    }
1848
1849
1850    public final LockingPolicy defaultLockingPolicy() {
1851        return defaultLocking;
1852    }
1853
1854
1855    private final void releaseAllLocks() {
1856
1857        getLockFactory().unlockGroup(getCompatibilitySpace(), this);
1858    }
1859
1860    void resetDefaultLocking() {
1861
1862        setDefaultLockingPolicy(
1863            newLockingPolicy(LockingPolicy.MODE_RECORD, TransactionController.ISOLATION_SERIALIZABLE, true));
1864
1865        if (SanityManager.DEBUG) {
1866            SanityManager.ASSERT(defaultLocking != null);
1867        }
1868
1869    }
1870    
1871    protected void preComplete(Integer JavaDoc commitOrAbort) throws StandardException {
1872        
1873        /* If a transaction is in COMMIT/ABORT at this point, most probably
1874         * some thing went wrong in earlier attempt to commit or abort,
1875         * so we don't know wther the log records got written in previous
1876         * attempt. It's is better to bring down the system than make recovery
1877         * fail with a duplicate log records of COMMIT/ABORT for the same Transaction.
1878         */

1879        if (inComplete != null)
1880            if (commitOrAbort.equals(COMMIT))
1881                throw logFactory.markCorrupt(
1882                         StandardException.newException(SQLState.XACT_COMMIT_EXCEPTION));
1883            else
1884                throw logFactory.markCorrupt(
1885                         StandardException.newException(SQLState.XACT_ABORT_EXCEPTION));
1886        
1887        inComplete = commitOrAbort;
1888        if (!postCompleteMode)
1889            doComplete(commitOrAbort);
1890
1891    }
1892
1893    protected void postComplete(int commitflag, Integer JavaDoc commitOrAbort) throws StandardException {
1894
1895        if (postCompleteMode)
1896            doComplete(commitOrAbort);
1897
1898        // if we are want to commitNoSync with KEEP_LOCKS flag set, don't
1899
// release any locks
1900
if ((commitflag & Transaction.KEEP_LOCKS) == 0)
1901        {
1902            releaseAllLocks();
1903        }
1904        else
1905        {
1906            if (SanityManager.DEBUG)
1907            {
1908                SanityManager.ASSERT(commitOrAbort.equals(COMMIT),
1909                 "cannot keep locks around after an ABORT");
1910            }
1911        }
1912
1913        setIdleState();
1914
1915        inComplete = null;
1916    }
1917
1918    protected void doComplete(Integer JavaDoc commitOrAbort) throws StandardException {
1919
1920        // throw away all our savepoints
1921
if (savePoints != null)
1922            savePoints.removeAllElements();
1923
1924        // notify any of our observers that we are completing.
1925
notifyObservers(commitOrAbort);
1926
1927        checkObserverException();
1928
1929        if (SanityManager.DEBUG)
1930        {
1931            if (countObservers() != 0)
1932            {
1933                System.out.println(
1934                    "There should be 0 observers, but we still have "
1935                    + countObservers() + " observers.");
1936                notifyObservers(null);
1937            }
1938        }
1939    }
1940
1941    private void checkObserverException() throws StandardException {
1942        if (observerException != null) {
1943            StandardException se = observerException;
1944            observerException = null;
1945            throw se;
1946        }
1947    }
1948
1949    /**
1950      If this is a user transaction (not an internal or nested top
1951      transaction), and this is not already taking care of post
1952      commit work, and not an XA transaction, then take care of hi prioirty
1953      work right now using this thread and this context manager.
1954      Otherwise, leave it to the post commit daemon.
1955      */

1956    protected boolean doPostCommitWorkInTran()
1957    {
1958        return (!inPostCommitProcessing &&
1959                !recoveryTransaction &&
1960                isUserTransaction() &&
1961                (myGlobalId == null));
1962    }
1963
1964    public boolean handlesPostTerminationWork()
1965    {
1966        // recovery transaction cannot handle post termination work
1967
return (recoveryTransaction == false);
1968    }
1969
1970    public void recoveryTransaction()
1971    {
1972        recoveryTransaction = true;
1973
1974        // remove myself from the transaction table because I am really a
1975
// "fake" transaction. All interaction I have with the transaction
1976
// table should happen after I have assumed the identity of one of the
1977
// recovery transaction that has its state frozen in the transaction
1978
// table.
1979
xactFactory.remove(myId);
1980
1981    }
1982
1983
1984    private final void postTermination() throws StandardException
1985    {
1986        // move all the postTermination work to the postCommit queue
1987
int count = (postTerminationWorks == null) ?
1988            0 : postTerminationWorks.size();
1989
1990        for (int i = 0; i < count; i++)
1991            addPostCommitWork((Serviceable)postTerminationWorks.get(i));
1992
1993        if (count > 0)
1994            postTerminationWorks.clear();
1995
1996
1997        // if there are post commit work to be done, transfer them to the
1998
// daemon. The log is flushed, all locks released and the
1999
// transaction has ended at this point.
2000
if (postCommitWorks != null && !postCommitWorks.isEmpty())
2001        {
2002            int pcsize = postCommitWorks.size();
2003            
2004            // do we want to do post commit work with this transaction object?
2005
if (doPostCommitWorkInTran())
2006            {
2007                try
2008                {
2009                    inPostCommitProcessing = true;
2010
2011                    // to avoid confusion, copy the post commit work to an array if this
2012
// is going to do some work now
2013
Serviceable[] work = new Serviceable[pcsize];
2014                    work = (Serviceable[])postCommitWorks.toArray(work);
2015
2016                    // clear this for post commit processing to queue its own post
2017
// commit works - when it commits, it will send all its post
2018
// commit request to the daemon instead of dealing with it here.
2019
postCommitWorks.clear();
2020
2021                    //All the post commit work that is part of the database creation
2022
//should be done on this thread immediately.
2023
boolean doWorkInThisThread = xactFactory.inDatabaseCreation();
2024
2025                    for (int i = 0; i < pcsize; i++)
2026                    {
2027
2028                        //process work that should be done immediately or
2029
//when we are in still in database creattion.
2030
//All the other work should be submitted
2031
//to the post commit thread to be processed asynchronously
2032
if (doWorkInThisThread || work[i].serviceImmediately())
2033                        {
2034                            try
2035                            {
2036                                // this may cause other post commit work to be
2037
// added. when that transaction commits, those
2038
// work will be transfered to the daemon
2039
if (work[i].performWork(xc.getContextManager()) == Serviceable.DONE)
2040                                    work[i] = null;
2041
2042                                // if REQUEUE, leave it on for the postcommit
2043
// daemon to handle
2044
}
2045                            catch (StandardException se)
2046                            {
2047                                // don't try to service this again
2048
work[i] = null;
2049
2050                                // try to handle it here. If we fail, then let the error percolate.
2051
xc.cleanupOnError(se);
2052                            }
2053                        }
2054
2055                        // either it need not be serviedASAP or it needs
2056
// requeueing, send it off. Note that this is one case
2057
// where a REQUEUE ends up in the high priority queue.
2058
// Unfortunately, there is no easy way to tell. If the
2059
// Servicable is well mannered, it can change itself from
2060
// serviceASAP to not serviceASAP if it returns REQUEUE.
2061
if (work[i] != null)
2062                        {
2063                            boolean needHelp = xactFactory.submitPostCommitWork(work[i]);
2064                            work[i] = null;
2065                            if (needHelp)
2066                                doWorkInThisThread = true;
2067                        }
2068                    }
2069                }
2070                finally
2071                {
2072                    inPostCommitProcessing = false;
2073
2074                    // if something untoward happends, clear the queue.
2075
if (postCommitWorks != null)
2076                        postCommitWorks.clear();
2077                }
2078
2079            }
2080            else
2081            {
2082                // this is for non-user transaction or post commit work that is
2083
// submitted in PostCommitProcessing. (i.e., a post commit
2084
// work submitting other post commit work)
2085
for (int i = 0; i < pcsize; i++)
2086                {
2087                    // SanityManager.DEBUG_PRINT("PostTermination",postCommitWorks.elementAt((i)).toString());
2088
xactFactory.submitPostCommitWork((Serviceable)postCommitWorks.get((i)));
2089                }
2090            }
2091
2092            postCommitWorks.clear();
2093
2094        }
2095
2096        // any backup blocking operations (like unlogged ops) in this
2097
// transaction are done with post commit/abort work that needs be
2098
// done in the same trasaction, unblock the backup.
2099
unblockBackup();
2100    }
2101
2102    /**
2103        Does a save point exist in the stack with the given name.
2104        Returns the position of the savepoint in the array
2105    */

2106    private int getSavePointPosition(
2107    String JavaDoc name,
2108    Object JavaDoc kindOfSavepoint,
2109    boolean forRollbackOrRelease)
2110    {
2111        if ((savePoints == null) || (savePoints.empty()))
2112            return -1;
2113
2114        for (int i = savePoints.size() - 1; i >= 0; i--)
2115        {
2116            SavePoint savepoint = (SavePoint)savePoints.elementAt(i);
2117
2118            if (savepoint.getName().equals(name))
2119            {
2120                if (forRollbackOrRelease &&
2121                    savepoint.getKindOfSavepoint() != null)
2122                {
2123                    if (savepoint.getKindOfSavepoint().equals(kindOfSavepoint))
2124                        return(i);
2125                }
2126                else
2127                {
2128                    return(i);
2129                }
2130            }
2131        }
2132        return -1;
2133    }
2134
2135    /**
2136        Pop all savepoints upto the one with the given name and rollback
2137        all changes made since this savepoint was pushed.
2138        If release is true then this savepoint is popped as well,
2139        otherwise it is left in the stack (at the top).
2140
2141        @return true if any work is rolled back, false if no work is rolled back
2142        @exception StandardException Standard cloudscape policy
2143        @exception StandardException Thrown if a error of severity less than TransactionException#SEVERITY
2144        is encountered during the rollback of this savepoint.
2145    */

2146    protected boolean popSavePoints(int position, boolean release) throws StandardException {
2147
2148        if (release) {
2149            savePoints.setSize(position);
2150            return false;
2151        }
2152
2153        LogInstant rollbackTo = null;
2154
2155        int size = savePoints.size();
2156        for (int i = position; i < size; i++) {
2157            SavePoint rollbackSavePoint = (SavePoint) savePoints.elementAt(i);
2158
2159            LogInstant li = rollbackSavePoint.getSavePoint();
2160            if (li != null) {
2161                rollbackTo = li;
2162                break;
2163            }
2164        }
2165
2166        savePoints.setSize(position + 1);
2167
2168        if (rollbackTo == null)
2169            return false;
2170
2171        // now perform the rollback
2172
try {
2173
2174            logger.undo(this, getId(), rollbackTo, getLastLogInstant());
2175
2176        } catch (StandardException se) {
2177
2178            // This catches any exceptions that have Transaction severity
2179
// or less (e.g. Statement exception). If we received any lesser
2180
// error then we abort the transaction anyway.
2181

2182            if (se.getSeverity() < ExceptionSeverity.TRANSACTION_SEVERITY)
2183            {
2184                throw StandardException.newException(
2185                        SQLState.XACT_ROLLBACK_EXCEPTION, se);
2186            }
2187
2188            throw se;
2189        }
2190
2191        return true;
2192    }
2193
2194    /**
2195        @exception StandardException Cloudscape Standard error policy
2196     */

2197    public RawTransaction startNestedTopTransaction() throws StandardException {
2198
2199        return xactFactory.startNestedTopTransaction(xc.getFactory(), xc.getContextManager());
2200    }
2201
2202    /**
2203     * see if this transaction is a user transaction.
2204     *
2205     * @return true if this transaction is a user transaction
2206     */

2207    private boolean isUserTransaction()
2208    {
2209        String JavaDoc context_id = getContextId();
2210
2211        return(
2212            (context_id == XactFactory.USER_CONTEXT_ID ||
2213             context_id.equals(XactFactory.USER_CONTEXT_ID)));
2214    }
2215
2216    /**
2217     * see if this transaction has ever done anything.
2218     *
2219     * MT - single thread through synchronizing this. This method may be
2220     * called by other thread to test the state of this transaction. That's
2221     * why we need to synchronize with all methods which enters or exits the
2222     * Idle state.
2223     *
2224     * Local method which read the state need not be synchronized because
2225     * the other thread may look at the state but it may not change it.
2226     *
2227     * @return true if this transaction is not in idle or closed state
2228     */

2229    public final boolean isActive()
2230    {
2231        // synchronized(this) -- int access, implicit synchronization
2232
// due to atomic action
2233
int localState = state;
2234
2235
2236        return (localState != CLOSED && localState != IDLE);
2237    }
2238
2239    /**
2240     * see if this transaction is in PREPARED state.
2241     *
2242     * MT - single thread through synchronizing this. This method may be
2243     * called by other thread to test the state of this transaction.
2244     *
2245     * @return true if this transaction is in PREPARED state.
2246     */

2247    public final boolean isPrepared()
2248    {
2249        // synchronized(this) -- int access, implicit synchronization
2250
// due to atomic action
2251
return(state == PREPARED);
2252    }
2253
2254    /**
2255        See if this transaction is in the idle state, called by other thread to
2256        test the state of this transaction. That's why we need to synchronzied
2257        with all methods whcih enters or exits the idle state
2258
2259        @return true if it is idle, otherwise false
2260    */

2261    public boolean isIdle()
2262    {
2263        // synchronized(this) -- int access, implicit synchronization
2264
// due to atomic action
2265
if (SanityManager.DEBUG)
2266        {
2267            if (SanityManager.DEBUG_ON("XATrace"))
2268                SanityManager.DEBUG("XATrace","RawTran, isIdle, state = " + state);
2269        }
2270
2271        return (state == IDLE);
2272    }
2273
2274    /**
2275        see if this transaction is in a pristine state.
2276
2277        <BR>MT - called only by the same thread that owns the xact, no need to synchronize.
2278
2279        @return true if it hasn't done any updates, otherwise false
2280    */

2281    public boolean isPristine()
2282    {
2283        return (state == IDLE || state == ACTIVE);
2284    }
2285
2286    public boolean inAbort() {
2287        return ABORT.equals(inComplete);
2288    }
2289
2290    public FileResource getFileHandler() {
2291        return dataFactory.getFileHandler();
2292    }
2293
2294 
2295    /**
2296     * put this into the beginXact log record to help recovery
2297     * if we needs to rolled back first, put that in
2298     */

2299    protected int statusForBeginXactLog()
2300    {
2301        return recoveryRollbackFirst() ? RECOVERY_ROLLBACK_FIRST : 0;
2302    }
2303
2304    /**
2305     * put this into the endXact log record to help recovery,
2306     * nothing to add
2307     */

2308    protected int statusForEndXactLog()
2309    {
2310        // during recovery, the beginXact may be logged by a non-standard
2311
// transaction and hence the end xact it log
2312
// must also contain whatever a non-standard Transaction will output.
2313
return savedEndStatus;
2314    }
2315
2316    /**
2317        Set the transaction to issue pre complete work at postComplete
2318        time, instead of preComplete time. This means that latches
2319        and containers will be held open until after a commit or an abort.
2320    */

2321    void setPostComplete() {
2322        postCompleteMode = true;
2323    }
2324
2325
2326    /*
2327     * Make the transaction block the online backup.
2328     *
2329     * @param wait if <tt>true</tt>, waits until the transaction
2330     * can block the backup.
2331     * @return <tt>true</tt> if the transaction blocked the
2332     * backup. <tt>false</tt> otherwise.
2333     * @exception StandardException if interrupted while waiting
2334     * for the backup in progress to complete.
2335     */

2336    public boolean blockBackup(boolean wait)
2337        throws StandardException
2338    {
2339        if (!backupBlocked) {
2340            backupBlocked = xactFactory.blockBackup(wait);
2341        }
2342
2343        return backupBlocked;
2344    }
2345    
2346    /*
2347     * Unblock the backup, if it was blocked by some operation in
2348     * this transaction. Unblocking is done at commit/abort of this
2349     * transaction.
2350     */

2351    private void unblockBackup() {
2352        if (backupBlocked)
2353            xactFactory.unblockBackup();
2354        backupBlocked = false;
2355    }
2356
2357
2358    /**
2359     * Check if the transaction is blocking the backup ?
2360     * @return <tt> true </tt> if this transaction is
2361     * blocking the backup, otherwise <tt> false </tt>
2362     */

2363    public boolean isBlockingBackup() {
2364        return backupBlocked;
2365    }
2366
2367    /*
2368    ** Lock escalation related
2369    */

2370
2371    /*
2372    ** Methods of Limit
2373    */

2374
2375    public void reached(Object JavaDoc compatabilitySpace, Object JavaDoc group, int limit,
2376        Enumeration JavaDoc lockList, int lockCount)
2377        throws StandardException {
2378
2379        // Count row locks by table
2380
Dictionary JavaDoc containers = new java.util.Hashtable JavaDoc();
2381
2382        for (; lockList.hasMoreElements(); ) {
2383
2384            Object JavaDoc plainLock = lockList.nextElement();
2385            if (!(plainLock instanceof RecordHandle)) {
2386                // only interested in rows locks
2387
continue;
2388            }
2389
2390            ContainerKey ckey = ((RecordHandle) plainLock).getContainerId();
2391            
2392            LockCount lc = (LockCount) containers.get(ckey);
2393            if (lc == null) {
2394                lc = new LockCount();
2395                containers.put(ckey, lc);
2396            }
2397            lc.count++;
2398        }
2399
2400        // Determine the threshold for lock escalation
2401
// based upon our own limit, not the current count
2402
int threshold = limit / (containers.size() + 1);
2403        if (threshold < (limit / 4))
2404            threshold = limit / 4;
2405
2406        // try to table lock all tables that are above
2407
// this threshold
2408

2409        boolean didEscalate = false;
2410        for (Enumeration JavaDoc e = containers.keys(); e.hasMoreElements(); ) {
2411            ContainerKey ckey = (ContainerKey) e.nextElement();
2412
2413            LockCount lc = (LockCount) containers.get(ckey);
2414
2415            if (lc.count < threshold) {
2416                continue;
2417            }
2418
2419            try
2420            {
2421                if (openContainer(ckey,
2422                    new RowLocking3Escalate(getLockFactory()),
2423                    ContainerHandle.MODE_OPEN_FOR_LOCK_ONLY |
2424                    ContainerHandle.MODE_FORUPDATE |
2425                    ContainerHandle.MODE_LOCK_NOWAIT) != null)
2426                {
2427
2428                    didEscalate = true;
2429                }
2430            }
2431            catch (StandardException se)
2432            {
2433                if (!se.getMessageId().equals(SQLState.LOCK_TIMEOUT))
2434                {
2435                    // if it is a timeout then escalate did not happen and
2436
// just fall through.
2437
throw se;
2438                }
2439            }
2440        }
2441
2442        // Now notify all open containers that an escalation
2443
// event happened. This will cause all open row locked
2444
// containers to re-get their container intent locks,
2445
// those that are now covered by a container lock due
2446
// to the above escalation will move into no locking
2447
// mode. The open containers that were not escalated
2448
// will simply bump the lock count in the lock manager
2449
// and will not have to wait for the lock they already have.
2450
//
2451
// It would be possible to pass in the notifyObservers
2452
// some indication of which tables were escalated
2453
// to reduce the extra lock call for the un-escalated
2454
// containers. This would involve passing the Hashtable
2455
// of escalated containers and having the update method
2456
// of BaseContainerHandle look for its ContainerKey within it.
2457
if (didEscalate) {
2458            notifyObservers(LOCK_ESCALATE);
2459            checkObserverException();
2460        }
2461    }
2462
2463    /**
2464     * Convert a local transaction to a global transaction.
2465     * <p>
2466     * Must only be called a previous local transaction was created and exists
2467     * in the context. Can only be called if the current transaction is in
2468     * the idle state, and no current global id.
2469     * <p>
2470     * Simply call setTransactionId() which takes care of error checking.
2471     *
2472     * @param format_id the format id part of the Xid - ie. Xid.getFormatId().
2473     * @param global_id the global transaction identifier part of XID - ie.
2474     * Xid.getGlobalTransactionId().
2475     * @param branch_id The branch qualifier of the Xid - ie.
2476     * Xid.getBranchQaulifier()
2477     *
2478     * @exception StandardException Standard exception policy.
2479     **/

2480    public void createXATransactionFromLocalTransaction(
2481    int format_id,
2482    byte[] global_id,
2483    byte[] branch_id)
2484        throws StandardException
2485    {
2486        GlobalXactId gid = new GlobalXactId(format_id, global_id, branch_id);
2487
2488        if (((TransactionTable) xactFactory.getTransactionTable()).
2489                findTransactionContextByGlobalId(gid) != null)
2490        {
2491            throw StandardException.newException(SQLState.STORE_XA_XAER_DUPID);
2492        }
2493
2494        setTransactionId(gid, this.getId());
2495
2496        if (SanityManager.DEBUG)
2497            SanityManager.ASSERT(myGlobalId != null);
2498    }
2499
2500    /**
2501     * This method is called to commit the current XA global transaction.
2502     * <p>
2503     * RESOLVE - how do we map to the "right" XAExceptions.
2504     * <p>
2505     *
2506     * @param onePhase If true, the resource manager should use a one-phase
2507     * commit protocol to commit the work done on behalf of
2508     * current xid.
2509     *
2510     * @exception StandardException Standard exception policy.
2511     **/

2512    public void xa_commit(
2513    boolean onePhase)
2514        throws StandardException
2515    {
2516        if (SanityManager.DEBUG)
2517            SanityManager.ASSERT(state != CLOSED);
2518
2519        if (onePhase)
2520        {
2521            if (state == PREPARED)
2522            {
2523                throw StandardException.newException(
2524                        SQLState.XACT_PROTOCOL_VIOLATION);
2525            }
2526
2527            prepareCommit(COMMIT_SYNC);
2528
2529            completeCommit(COMMIT_SYNC);
2530        }
2531        else
2532        {
2533            if (state != PREPARED)
2534            {
2535                throw StandardException.newException(
2536                        SQLState.XACT_PROTOCOL_VIOLATION);
2537            }
2538
2539            prepareCommit(COMMIT_SYNC);
2540
2541            completeCommit(COMMIT_SYNC);
2542        }
2543
2544
2545        return;
2546    }
2547
2548    /**
2549     * This method is called to ask the resource manager to prepare for
2550     * a transaction commit of the transaction specified in xid.
2551     * <p>
2552     *
2553     * @return A value indicating the resource manager's vote on the
2554     * the outcome of the transaction. The possible values
2555     * are: XA_RDONLY or XA_OK. If the resource manager wants
2556     * to roll back the transaction, it should do so by
2557     * throwing an appropriate XAException in the prepare
2558     * method.
2559     *
2560     * @exception StandardException Standard exception policy.
2561     **/

2562    public int xa_prepare()
2563        throws StandardException
2564    {
2565        if (SanityManager.DEBUG)
2566        {
2567            if (state == CLOSED)
2568            {
2569                SanityManager.THROWASSERT(
2570                    "state = " + state + ";myGlobalId = " + myGlobalId);
2571            }
2572
2573            if (SanityManager.DEBUG_ON("XATrace"))
2574                SanityManager.DEBUG("XATrace","in xa_prepare, state is " + state);
2575        }
2576
2577        if ((state == IDLE) || (state == ACTIVE))
2578        {
2579            abort();
2580            return(Transaction.XA_RDONLY);
2581        }
2582        else
2583        {
2584            prepareCommit(
2585                COMMIT_SYNC | COMMIT_PREPARE | Transaction.KEEP_LOCKS);
2586            //we set the following variable during prepareCommit
2587
// to what we are doing, so we unset here.
2588
inComplete = null;
2589
2590            setPrepareState();
2591
2592            return(Transaction.XA_OK);
2593        }
2594    }
2595
2596    /**
2597     * rollback the current global transaction.
2598     * <p>
2599     * The given transaction is roll'ed back and it's history is not
2600     * maintained in the transaction table or long term log.
2601     * <p>
2602     *
2603     * @exception StandardException Standard exception policy.
2604     **/

2605    public void xa_rollback()
2606        throws StandardException
2607    {
2608        if (SanityManager.DEBUG)
2609            SanityManager.ASSERT(state != CLOSED);
2610
2611        abort();
2612
2613        return;
2614    }
2615
2616    /**
2617     * Return the xid as a string.
2618     * <p>
2619     * The virtual lock table depends on this routine returning just the
2620     * local transaction id as a string, even if it is a global transaction.
2621     * Joins between the lock table and the transaction table will not work
2622     * if this routine returns anything other than myId.toString().
2623     * <p>
2624     *
2625     * @return The xid as a string.
2626     *
2627     **/

2628    public String JavaDoc toString()
2629    {
2630        // needed for virtual lock table
2631
try
2632        {
2633            return(myId.toString());
2634        }
2635        catch (Throwable JavaDoc t)
2636        {
2637            // using try/catch rather than if (myId != null) because on
2638
// multiple processor sometimes myId was going null after the
2639
// test but before the use.
2640
return("null");
2641        }
2642    }
2643
2644    
2645    /*
2646     * Get string id of the transaction that would be when the Transaction
2647     * is IN active state.
2648     *
2649     *This transaction "name" will be the same id which is returned in
2650     * the TransactionInfo information if Tx is already in Active State.
2651     * If the Transaction is in IDLE state, Transaction ID is
2652     * incremented when getActiveStateTxIdString() on raw transaction is called,
2653     * instead of the Tx ID being incremented when Transaction gets into
2654     * active state. The reason for incrementing the Tx ID earlier than when Tx
2655     * is actually goes into active state is some debug statement cases like
2656     * log statement text. SQL statements are wriited to log before they are
2657     * actully executed; In such cases we would like to display the actual TX ID on which
2658     * locks are acquired when the statement is executed.
2659     * @return The a string which identifies the transaction.
2660     */

2661    public String JavaDoc getActiveStateTxIdString()
2662    {
2663        if(!justCreated && state == IDLE)
2664        {
2665            // TransactionTable needs this
2666
xactFactory.setNewTransactionId(myId, this);
2667            //mark as if this tx is just created , so that setActiveState()
2668
//does not increment the transaction id number.
2669
justCreated = true;
2670        }
2671        
2672        return toString();
2673    }
2674
2675
2676    /* package */
2677    String JavaDoc getState()
2678    {
2679        int localState ;
2680
2681        // synchronized(this) -- int assignment, implicit synchronization
2682
// due to atomic action
2683
{
2684            localState = state;
2685        }
2686
2687        switch (localState)
2688        {
2689        case CLOSED:
2690                    return "CLOSED";
2691        case IDLE:
2692                    return "IDLE";
2693        case ACTIVE:
2694        case UPDATE:
2695                    return "ACTIVE";
2696
2697        case PREPARED:
2698                    return "PREPARED";
2699        }
2700        return null;
2701    }
2702
2703    public String JavaDoc getTransName()
2704    {
2705        return transName;
2706    }
2707
2708    public void setTransName(String JavaDoc name)
2709    {
2710        transName = name;
2711    }
2712
2713
2714
2715    /**
2716        Is the transaction in rollforward recovery
2717    */

2718    public boolean inRollForwardRecovery()
2719    {
2720        return logFactory.inRFR();
2721    }
2722
2723
2724    /**
2725        perform a checkpoint during rollforward recovery
2726    */

2727    public void checkpointInRollForwardRecovery(LogInstant cinstant,
2728                                                long redoLWM)
2729        throws StandardException
2730    {
2731        logFactory.checkpointInRFR(cinstant, redoLWM, dataFactory);
2732    }
2733
2734}
2735
2736class LockCount {
2737    int count;
2738}
2739
2740
2741
Popular Tags