EMMA Coverage Report (generated Wed Jun 28 22:15:27 PDT 2006)
[all classes][org.apache.derby.impl.store.raw.log]

COVERAGE SUMMARY FOR SOURCE FILE [FileLogger.java]

nameclass, %method, %block, %line, %
FileLogger.java100% (1/1)100% (10/10)75%  (1292/1730)81%  (277.5/343)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class FileLogger100% (1/1)100% (10/10)75%  (1292/1730)81%  (277.5/343)
FileLogger (LogToFile): void 100% (1/1)100% (29/29)100% (7/7)
close (): void 100% (1/1)100% (22/22)100% (8/8)
flush (LogInstant): void 100% (1/1)100% (19/19)100% (4/4)
flushAll (): void 100% (1/1)100% (4/4)100% (2/2)
logAndDo (RawTransaction, Loggable): LogInstant 100% (1/1)87%  (302/346)89%  (58.7/66)
logAndUndo (RawTransaction, Compensation, LogInstant, LimitObjectInput): LogI... 100% (1/1)70%  (133/191)71%  (25/35)
readLogRecord (StreamLogScan, int): Loggable 100% (1/1)100% (21/21)100% (6/6)
redo (RawTransaction, TransactionFactory, StreamLogScan, long, long): long 100% (1/1)80%  (444/557)85%  (88.6/104)
reprepare (RawTransaction, TransactionId, LogInstant, LogInstant): void 100% (1/1)59%  (153/261)61%  (30.3/50)
undo (RawTransaction, TransactionId, LogInstant, LogInstant): void 100% (1/1)59%  (165/280)79%  (47.9/61)

1/*
2 
3   Derby - Class org.apache.derby.impl.store.raw.log.FileLogger
4 
5   Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11      http://www.apache.org/licenses/LICENSE-2.0
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 */
20 
21package org.apache.derby.impl.store.raw.log;
22 
23import org.apache.derby.iapi.reference.SQLState;
24import org.apache.derby.iapi.reference.MessageId;
25 
26import org.apache.derby.impl.store.raw.log.LogCounter;
27import org.apache.derby.impl.store.raw.log.LogRecord;
28import org.apache.derby.impl.store.raw.log.StreamLogScan;
29 
30import org.apache.derby.iapi.store.access.TransactionController;
31 
32import org.apache.derby.iapi.store.raw.RawStoreFactory;
33 
34import org.apache.derby.iapi.store.raw.log.LogInstant;
35import org.apache.derby.iapi.store.raw.log.Logger;
36import org.apache.derby.iapi.store.raw.log.LogScan;
37 
38import org.apache.derby.iapi.store.raw.xact.RawTransaction;
39import org.apache.derby.iapi.store.raw.xact.TransactionFactory;
40import org.apache.derby.iapi.store.raw.xact.TransactionId;
41 
42import org.apache.derby.iapi.store.raw.Compensation;
43import org.apache.derby.iapi.store.raw.ContainerHandle;
44import org.apache.derby.iapi.store.raw.LockingPolicy;
45import org.apache.derby.iapi.store.raw.Loggable;
46import org.apache.derby.iapi.store.raw.Page;
47import org.apache.derby.iapi.store.raw.RePreparable;
48import org.apache.derby.iapi.store.raw.Undoable;
49 
50import org.apache.derby.iapi.services.io.FormatIdOutputStream;
51 
52import org.apache.derby.iapi.services.sanity.SanityManager;
53 
54import org.apache.derby.iapi.error.StandardException;
55import org.apache.derby.iapi.services.i18n.MessageService;
56 
57import org.apache.derby.iapi.services.io.ArrayInputStream;
58import org.apache.derby.iapi.services.io.ArrayOutputStream;
59import org.apache.derby.iapi.util.ByteArray;
60import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
61 
62import org.apache.derby.iapi.services.io.LimitObjectInput;
63import java.io.IOException;
64 
65import org.apache.derby.impl.store.raw.data.InitPageOperation;
66 
67/**
68        Write log records to a log file as a stream
69        (ie. log records added to the end of the file, no concept of pages).
70<P>
71        The format of a log record that is not a compensation operation is
72        <PRE>
73        @format_id        no formatId, format is implied by the log file format and the
74                log record content.
75        @purpose        the log record and optional data
76        @upgrade
77        @disk_layout
78                Log Record
79                        (see org.apache.derby.impl.store.raw.log.LogRecord)
80                length(int)        length of optional data
81                optionalData(byte[length]) optional data written by the log record
82        @end_format
83        </PRE> <HR WIDTH="100%"> 
84 
85        <P>        The form of a log record that is a compensation operation is
86        <PRE>
87        @format_id        no formatId, format is implied by the log file format and the
88        log record content.
89        @purpose        undo a previous log record
90        @upgrade
91        @disk_layout
92                Log Record that contains the compenstation operation
93                        (see org.apache.derby.impl.store.raw.log.LogRecord)
94                undoInstant(long) the log instant of the operation that is to be rolled back
95                        The undo instant is logically part of the LogRecord but is written
96                        by the logger because it is used and controlled by the rollback
97                        code but not by the log operation.
98                There is no optional data in a compensation operation, all data
99                necessary for the rollback must be stored in the operation being
100                undone.
101        @end_format
102        </PRE>
103 
104    <BR>
105 
106        <P>Multithreading considerations:<BR>
107        Logger must be MT-safe.        Each RawTransaction has its own private
108        FileLogger object. Each logger has a logOutputBuffer and a log input
109        buffer which are used to read and write to the log.  Since multiple
110        threads can be in the same transaction, fileLogger must be synchronized.
111 
112        @see LogRecord
113*/
114 
115public class FileLogger implements Logger {
116 
117        private LogRecord                 logRecord;
118 
119        protected byte[] encryptionBuffer;
120        private DynamicByteArrayOutputStream logOutputBuffer;
121        private FormatIdOutputStream logicalOut;
122 
123        private ArrayInputStream logIn;
124 
125        private LogToFile logFactory;        // actually writes the log records.
126 
127        /**
128                Make a new Logger with its own log record buffers
129                MT - not needed for constructor
130        */
131        public FileLogger(LogToFile logFactory) {
132 
133                this.logFactory = logFactory;
134                logOutputBuffer = new DynamicByteArrayOutputStream(1024); // init size 1K
135                logicalOut = new FormatIdOutputStream(logOutputBuffer);
136 
137                // logIn and logOutputBuffer must share the same buffer because they
138                // combined to form an IO stream to access the same log record.
139                // 
140                // Before each use of logIn, you must reset logIn's data to the
141                // byte array you want to read from.
142                //
143                // To log a record, set logIn's data to point to logOutputBuffer's
144                // byte array when you know you have everything you need in the output
145                // buffer, then set limit on logIn and send it to the log operation's
146                // doMe.
147                //
148                // Keep in mind the dynamic nature of the logOutputBuffer which means
149                // it could switch buffer from underneath the logOutputBuffer on every
150                // write.
151                logIn = new ArrayInputStream();
152 
153                logRecord = new LogRecord();
154 
155        }
156 
157        /**
158                Close the logger.
159                MT - caller provide synchronization
160                (RESOLVE: not called by anyone ??)
161        */
162        public void close() throws IOException
163        {
164                if (logOutputBuffer != null)
165                {
166                        logOutputBuffer.close();
167                        logOutputBuffer = null;
168                }
169 
170                logIn = null;
171                logFactory = null;
172 
173                logicalOut = null;
174 
175                logRecord = null;
176        }
177 
178        /*
179        ** Methods of Logger
180        */
181 
182        /**
183                Writes out a log record to the log stream, and call its doMe method to
184                apply the change to the rawStore.
185                <BR>Any optional data the doMe method need is first written to the log
186                stream using operation.writeOptionalData, then whatever is written to
187                the log stream is passed back to the operation for the doMe method.
188 
189                <P>MT - there could be multiple threads running in the same raw
190                transactions and they can be calling the same logger to log different
191                log operations.  This whole method is synchronized to make sure log
192                records are logged one at a time.
193 
194                @param xact the transaction logging the change
195                @param operation the log operation
196                @return the instant in the log that can be used to identify the log
197                record
198 
199                @exception StandardException Cloudscape Standard error policy
200        */
201        public synchronized LogInstant logAndDo(RawTransaction xact, Loggable operation) 
202                 throws StandardException 
203        {
204                boolean isLogPrepared = false;
205 
206                boolean inUserCode = false;
207                byte[] preparedLog;
208 
209                try {
210 
211                        logOutputBuffer.reset();
212 
213                        // always use the short Id, only the BeginXact log record contains
214                        // the XactId (long form)
215                        TransactionId transactionId = xact.getId();
216 
217                        // write out the log header with the operation embedded
218                        // this is by definition not a compensation log record,
219                        // those are called thru the logAndUndo interface
220                        logRecord.setValue(transactionId, operation);
221 
222                        inUserCode = true;
223                        logicalOut.writeObject(logRecord);
224                        inUserCode = false;
225 
226                        int optionalDataLength = 0;
227                        int optionalDataOffset = 0;
228                        int completeLength = 0;
229 
230                        ByteArray preparedLogArray = operation.getPreparedLog();
231                        if (preparedLogArray != null) {
232 
233                                preparedLog = preparedLogArray.getArray();
234                                optionalDataLength = preparedLogArray.getLength();
235                                optionalDataOffset = preparedLogArray.getOffset();
236 
237                                // There is a race condition if the operation is a begin tran in
238                                // that between the time the beginXact log record is written to
239                                // disk and the time the transaction object is updated in the
240                                // beginXact.doMe method, other log records may be written.
241                                // This will render the transaction table in an inconsistent state
242                                // since it may think a later transaction is the earliest
243                                // transaction or it may think that there is no active transactions
244                                // where there is a bunch of them sitting on the log.
245                                //
246                                // Similarly, there is a race condition for endXact, i.e.,
247                                // 1) endXact is written to the log, 
248                                // 2) checkpoint gets that (committed) transaction as the
249                                //                firstUpdateTransaction
250                                // 3) the transaction calls postComplete, nulling out itself
251                                // 4) checkpoint tries to access a closed transaction object
252                                //
253                                // The solution is to sync between the time a begin tran or end
254                                // tran log record is sent to the log stream and its doMe method is
255                                // called to update the transaction table and in memory state
256                                //
257                                // We only need to serialized the begin and end Xact log records
258                                // because once a transaction has been started and in the
259                                // transaction table, its order and transaction state does not
260                                // change.
261                                //
262                                // Use the logFactory as the sync object so that a checkpoint can
263                                // take its snap shot of the undoLWM before or after a transaction
264                                // is started, but not in the middle. (see LogToFile.checkpoint)
265                                //
266 
267                                // now set the input limit to be the optional data.  
268                                // This limits amount of data availiable to logIn that doMe can
269                                // use
270                                logIn.setData(preparedLog);
271                                logIn.setPosition(optionalDataOffset);
272                                logIn.setLimit(optionalDataLength);
273 
274                                if (SanityManager.DEBUG)
275                                {
276                                        if ((optionalDataLength) != logIn.available())
277                                                SanityManager.THROWASSERT(
278                                                        " stream not set correctly " +
279                                                        optionalDataLength + " != " +
280                                                         logIn.available());
281                                }
282 
283                        } else {
284                                preparedLog = null;
285                                optionalDataLength = 0;
286                        }
287 
288                        logicalOut.writeInt(optionalDataLength);
289                        completeLength = logOutputBuffer.getPosition() + optionalDataLength;
290 
291 
292                        LogInstant logInstant = null;
293                        int encryptedLength = 0; // in case of encryption, we need to pad
294 
295                        try
296                        {
297                                if (logFactory.databaseEncrypted())
298                                {
299                                        // we must pad the encryption data to be multiple of block
300                                        // size, which is logFactory.getEncryptionBlockSize()
301                                        encryptedLength = completeLength;
302                                        if ((encryptedLength % logFactory.getEncryptionBlockSize()) != 0)
303                                                encryptedLength = encryptedLength + logFactory.getEncryptionBlockSize() - (encryptedLength % logFactory.getEncryptionBlockSize());
304 
305                                        if (encryptionBuffer == null || 
306                                                encryptionBuffer.length < encryptedLength)
307                                                encryptionBuffer = new byte[encryptedLength];
308 
309                                        System.arraycopy(logOutputBuffer.getByteArray(), 0, 
310                                                                         encryptionBuffer, 0, completeLength-optionalDataLength);
311 
312                                        if (optionalDataLength > 0)
313                                                System.arraycopy(preparedLog, optionalDataOffset, 
314                                                                         encryptionBuffer,
315                                                                         completeLength-optionalDataLength, optionalDataLength);
316 
317                                        // do not bother to clear out the padding area 
318                                        int len = 
319                                                logFactory.encrypt(encryptionBuffer, 0, encryptedLength, 
320                                                                                   encryptionBuffer, 0);
321 
322                                        if (SanityManager.DEBUG)
323                                                SanityManager.ASSERT(len == encryptedLength, 
324                                                        "encrypted log buffer length != log buffer len");
325                                }
326 
327                                if ((operation.group() & (Loggable.FIRST | Loggable.LAST)) != 0)
328                                {
329                                        synchronized (logFactory)
330                                        {
331                                                long instant = 0;
332 
333                                                if (logFactory.databaseEncrypted())
334                                                {
335                                                        // encryption has completely drained both the the
336                                                        // logOuputBuffer array and the preparedLog array
337                                                        instant = logFactory.
338                                                                appendLogRecord(encryptionBuffer, 0, 
339                                                                                                encryptedLength, null, 
340                                                                                                -1, 0);
341                                                }
342                                                else
343                                                {
344                                                        instant = logFactory.
345                                                                appendLogRecord(logOutputBuffer.getByteArray(),
346                                                                                                0, completeLength, preparedLog,
347                                                                                                optionalDataOffset,
348                                                                                                optionalDataLength);
349                                                }
350                                                logInstant = new LogCounter(instant);
351 
352                                                operation.doMe(xact, logInstant, logIn);
353                                        }
354                                }
355                                else
356                                {
357                                        long instant = 0;
358 
359                                        if (logFactory.databaseEncrypted())
360                                        {
361                                                // encryption has completely drained both the the
362                                                // logOuputBuffer array and the preparedLog array
363                                                instant = logFactory.
364                                                        appendLogRecord(encryptionBuffer, 0, 
365                                                                                        encryptedLength, null, -1, 0);
366                                        }
367                                        else
368                                        {
369                                                instant = logFactory.
370                                                        appendLogRecord(logOutputBuffer.getByteArray(), 0,
371                                                                                        completeLength, preparedLog,
372                                                                                        optionalDataOffset,
373                                                                                        optionalDataLength); 
374                                        }
375 
376                                        logInstant = new LogCounter(instant);
377 
378                                        operation.doMe(xact, logInstant, logIn);
379                                }
380 
381                        }
382                        catch (StandardException se)
383                        {
384                                throw logFactory.markCorrupt(
385                        StandardException.newException(
386                            SQLState.LOG_DO_ME_FAIL, se, operation));
387                        }
388                        catch (IOException ioe)
389                        {
390                                throw logFactory.markCorrupt(
391                        StandardException.newException(
392                            SQLState.LOG_DO_ME_FAIL, ioe, operation));
393                        }
394                        finally
395                        {
396                                logIn.clearLimit();
397                        }
398 
399                        if (SanityManager.DEBUG)
400            {
401                if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
402                {
403                    SanityManager.DEBUG(
404                        LogToFile.DBG_FLAG, 
405                        "Write log record: tranId=" + transactionId.toString() +
406                        " instant: " + logInstant.toString() + " length: " +
407                        completeLength + "\n" + operation + "\n");
408                }
409                        }
410                        return logInstant;
411                }
412 
413                catch (IOException ioe) 
414                {
415                        // error writing to the log buffer
416                        if (inUserCode)
417            {
418                                throw StandardException.newException(
419                        SQLState.LOG_WRITE_LOG_RECORD, ioe, operation);
420            }
421                        else
422            {
423                                throw StandardException.newException(
424                        SQLState.LOG_BUFFER_FULL, ioe, operation);
425            }
426                }
427 
428        }
429 
430        /**
431                Writes out a compensation log record to the log stream, and call its
432                doMe method to undo the change of a previous log operation.
433 
434                <P>MT - Not needed. A transaction must be single threaded thru undo, each
435                RawTransaction has its own logger, therefore no need to synchronize.
436                The RawTransaction must handle synchronizing with multiple threads
437                during rollback.
438 
439                @param xact the transaction logging the change
440                @param compensation the compensation log operation
441                @param undoInstant the log instant of the operation that is to be
442                rolled back
443                @param in optional data input for the compenastion doMe method
444 
445                @return the instant in the log that can be used to identify the log
446                record
447 
448                @exception StandardException Cloudscape Standard error policy
449         */
450        public LogInstant logAndUndo(RawTransaction xact, 
451                                                                 Compensation compensation,
452                                                                 LogInstant undoInstant,
453                                                                 LimitObjectInput in)
454                 throws StandardException
455        {
456                boolean inUserCode = false;
457 
458                try {
459                        logOutputBuffer.reset();
460 
461                        TransactionId transactionId = xact.getId();
462 
463                        // write out the log header with the operation embedded
464                        logRecord.setValue(transactionId, compensation);
465 
466                        inUserCode = true;
467                        logicalOut.writeObject(logRecord);
468                        inUserCode = false;
469 
470                        // write out the undoInstant
471                        logicalOut.writeLong(((LogCounter)undoInstant).getValueAsLong());
472 
473                        // in this implemetaion, there is no optional data for the
474                        // compensation operation.  Optional data for the rollback comes
475                        // from the undoable operation - and is passed into this call.
476                        int completeLength = logOutputBuffer.getPosition();
477                        long instant = 0;
478                        
479                        if (logFactory.databaseEncrypted())
480                        {
481                                // we must pad the encryption data to be multiple of block
482                                // size, which is logFactory.getEncryptionBlockSize()
483                                int encryptedLength = completeLength;
484                                if ((encryptedLength % logFactory.getEncryptionBlockSize()) != 0)
485                                        encryptedLength = encryptedLength + logFactory.getEncryptionBlockSize() - (encryptedLength % logFactory.getEncryptionBlockSize());
486 
487                                if (encryptionBuffer == null || 
488                                        encryptionBuffer.length < encryptedLength)
489                                        encryptionBuffer = new byte[encryptedLength];
490 
491                                System.arraycopy(logOutputBuffer.getByteArray(), 0, 
492                                                                 encryptionBuffer, 0, completeLength);
493 
494                                // do not bother to clear out the padding area 
495                                int len = 
496                                        logFactory.encrypt(encryptionBuffer, 0, encryptedLength,
497                                                                           encryptionBuffer, 0);
498 
499                                if (SanityManager.DEBUG)
500                                        SanityManager.ASSERT(len == encryptedLength, 
501                                                "encrypted log buffer length != log buffer len");
502 
503                                instant = logFactory.
504                                        appendLogRecord(encryptionBuffer,
505                                                                        0, encryptedLength, null, 0, 0);
506                        }
507                        else
508                        {
509                                instant = logFactory.
510                                        appendLogRecord(logOutputBuffer.getByteArray(), 
511                                                                        0, completeLength, null, 0, 0);
512                        }
513 
514                        LogInstant logInstant = new LogCounter(instant);
515 
516                        if (SanityManager.DEBUG)
517            {
518                if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
519                {
520                    SanityManager.DEBUG(
521                        LogToFile.DBG_FLAG, 
522                        "Write CLR: Xact: " + transactionId.toString() +
523                        "clrinstant: " + logInstant.toString() + 
524                        " undoinstant " + undoInstant + "\n");
525                }
526                        }
527 
528                        try
529                        {
530                                // in and dataLength contains optional data that was written 
531                                // to the log during a previous call to logAndDo.
532                                compensation.doMe(xact, logInstant, in);
533                        }
534                        catch (StandardException se)
535                        {
536                                throw logFactory.markCorrupt(
537                        StandardException.newException(
538                            SQLState.LOG_DO_ME_FAIL, se, compensation));
539                        }
540                        catch (IOException ioe)
541                        {
542                                throw logFactory.markCorrupt(
543                        StandardException.newException(
544                            SQLState.LOG_DO_ME_FAIL, ioe, compensation));
545                        }
546 
547                        return logInstant;
548 
549                }
550                catch (IOException ioe)
551                {
552                        if (inUserCode)
553            {
554                                throw StandardException.newException(
555                        SQLState.LOG_WRITE_LOG_RECORD, ioe, compensation);
556            }
557                        else
558            {
559                                throw StandardException.newException(
560                        SQLState.LOG_BUFFER_FULL, ioe, compensation);
561            }
562                }
563        }
564 
565        /**
566                Flush the log up to the given log instant.
567 
568                <P>MT - not needed, wrapper method
569 
570                @exception StandardException cannot sync log file
571        */
572        public void flush(LogInstant where) 
573                 throws StandardException
574        {
575                if (SanityManager.DEBUG)
576        {
577            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
578            {
579                SanityManager.DEBUG(
580                    LogToFile.DBG_FLAG, "Flush log to " + where.toString());
581            }
582                }
583 
584                logFactory.flush(where);
585        }
586 
587        /**
588                Flush all outstanding log to disk.
589 
590                <P>MT - not needed, wrapper method
591 
592                @exception StandardException cannot sync log file
593        */
594        public void flushAll () throws StandardException
595        {
596                logFactory.flushAll();
597        }
598 
599    /**
600     * During recovery re-prepare a transaction.
601     * <p>
602     * After redo() and undo(), this routine is called on all outstanding 
603     * in-doubt (prepared) transactions.  This routine re-acquires all 
604     * logical write locks for operations in the xact, and then modifies
605     * the transaction table entry to make the transaction look as if it
606     * had just been prepared following startup after recovery.
607     * <p>
608     *
609     * @param t                 is the transaction performing the re-prepare
610     * @param prepareId         is the transaction ID to be re-prepared
611     * @param prepareStopAt     is where the log instant (inclusive) where the 
612     *                          re-prepare should stop.
613     * @param prepareStartAt    is the log instant (inclusive) where re-prepare 
614     *                          should begin, this is normally the log instant 
615     *                          of the last log record of the transaction that 
616     *                          is to be re-prepare.  If null, then re-prepare 
617     *                          starts from the end of the log.
618     *
619         * @exception  StandardException  Standard exception policy.
620     **/
621        public void reprepare(
622    RawTransaction  t,
623    TransactionId   prepareId,
624    LogInstant      prepareStopAt,
625    LogInstant      prepareStartAt) 
626        throws StandardException
627        {
628 
629                if (SanityManager.DEBUG)
630        {
631            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
632            {
633                if (prepareStartAt != null)
634                {
635                    SanityManager.DEBUG(
636                    LogToFile.DBG_FLAG, 
637                    "----------------------------------------------------\n" +
638                    "\nBegin of RePrepare : " + prepareId.toString() + 
639                        "start at " + prepareStartAt.toString() + 
640                        " stop at " + prepareStopAt.toString()  +
641                    "\n----------------------------------------------------\n");
642                }
643                else
644                {
645                    SanityManager.DEBUG(
646                    LogToFile.DBG_FLAG, 
647                    "----------------------------------------------------\n" +
648                    "\nBegin of Reprepare: " + prepareId.toString() + 
649                        "start at end of log stop at " + 
650                        prepareStopAt.toString() +
651                    "\n----------------------------------------------------\n");
652                }
653            }
654                }
655 
656                // statistics
657                int clrskipped      = 0;
658                int logrecordseen   = 0;
659 
660                RePreparable lop          = null;
661 
662                // stream to read the log record - initial size 4096, scanLog needs
663                // to resize if the log record is larger than that.
664                ArrayInputStream    rawInput    = null;
665 
666                try
667                {
668            StreamLogScan scanLog;
669 
670                        if (prepareStartAt == null)
671            {
672                // don't know where to start, scan from end of log
673                                scanLog = 
674                    (StreamLogScan) logFactory.openBackwardsScan(prepareStopAt);
675            }
676                        else
677                        {
678                                if (prepareStartAt.lessThan(prepareStopAt)) 
679                {
680                    // nothing to prepare!
681                                        return;
682                }
683 
684                                scanLog = (StreamLogScan)
685                                        logFactory.openBackwardsScan(
686                        ((LogCounter) prepareStartAt).getValueAsLong(),
687                        prepareStopAt);
688                        }
689 
690                        if (SanityManager.DEBUG)
691                                SanityManager.ASSERT(
692                    scanLog != null, "cannot open log for prepare");
693 
694                        rawInput    = new ArrayInputStream(new byte[4096]);
695 
696                        LogRecord record;
697 
698                        while ((record = 
699                    scanLog.getNextRecord(rawInput, prepareId, 0)) 
700                       != null) 
701                        { 
702                                if (SanityManager.DEBUG)
703                                {
704                                        SanityManager.ASSERT(
705                        record.getTransactionId().equals(prepareId),
706                                            "getNextRecord return unqualified log rec for prepare");
707                                }
708 
709                                logrecordseen++;
710 
711                                if (record.isCLR())
712                                {
713                                        clrskipped++;
714 
715                    // the loggable is still in the input stream, get rid of it
716                                        record.skipLoggable(); 
717 
718                                        // read the prepareInstant
719                                        long prepareInstant = rawInput.readLong();
720 
721                                        if (SanityManager.DEBUG)
722                    {
723                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
724                        {
725                            SanityManager.DEBUG(
726                                LogToFile.DBG_FLAG, 
727                                    "Skipping over CLRs, reset scan to " + 
728                                    LogCounter.toDebugString(prepareInstant));
729                        }
730                                        }
731 
732                                        scanLog.resetPosition(new LogCounter(prepareInstant));
733                                        // scanLog now positioned at the beginning of the log
734                                        // record that was rolled back by this CLR.
735                                        // The scan is a backward one so getNextRecord will skip
736                                        // over the record that was rolled back and go to the one
737                                        // previous to it
738 
739                                        continue;
740                                }
741 
742                if (record.requiresPrepareLocks())
743                {
744                    lop = record.getRePreparable();
745                }
746                else
747                {
748                    continue;
749                }
750 
751                                if (lop != null)
752                                {
753                    // Reget locks based on log record.  reclaim all locks with
754                    // a serializable locking policy, since we are only 
755                    // reclaiming write locks, isolation level does not matter
756                    // much.
757 
758                    lop.reclaimPrepareLocks(
759                        t,
760                        t.newLockingPolicy(
761                            LockingPolicy.MODE_RECORD, 
762                            TransactionController.ISOLATION_REPEATABLE_READ, 
763                            true));
764 
765                                        if (SanityManager.DEBUG)
766                    {
767                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
768                        {
769                            SanityManager.DEBUG(
770                                LogToFile.DBG_FLAG, 
771                                "Reprepare log record at instant " +
772                                scanLog.getInstant() + " : " + lop);
773                        }
774                    }
775 
776                                }
777                        }
778 
779                }
780                catch (ClassNotFoundException cnfe)
781                {
782                        throw logFactory.markCorrupt(
783                StandardException.newException(SQLState.LOG_CORRUPTED, cnfe));
784                }
785            catch (IOException ioe) 
786                {
787                        throw logFactory.markCorrupt(
788                StandardException.newException(
789                    SQLState.LOG_READ_LOG_FOR_UNDO, ioe));
790                }
791                catch (StandardException se)
792                {
793                        throw 
794                logFactory.markCorrupt(
795                    StandardException.newException(
796                        SQLState.LOG_UNDO_FAILED, se,  
797                        prepareId, lop, (Object) null));
798                }
799                finally
800                {
801                        if (rawInput != null)
802                        {
803                                try
804                                {
805                                        rawInput.close();
806                                }
807                                catch (IOException ioe)
808                                {
809                                        throw logFactory.markCorrupt(
810                        StandardException.newException(
811                            SQLState.LOG_READ_LOG_FOR_UNDO, ioe, prepareId));
812                                }
813                        }
814                }
815 
816                if (SanityManager.DEBUG)
817        {
818            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
819            {
820                SanityManager.DEBUG(LogToFile.DBG_FLAG, "Finish prepare" +
821                                    ", clr skipped = " + clrskipped + 
822                                    ", record seen = " + logrecordseen + "\n");
823            }
824        }
825 
826                if (SanityManager.DEBUG)
827        {
828            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
829            {
830                SanityManager.DEBUG(
831                    LogToFile.DBG_FLAG, 
832                    "----------------------------------------------------\n" +
833                    "End of recovery rePrepare\n" + 
834                    ", clr skipped = " + clrskipped + 
835                    ", record seen = " + logrecordseen + 
836                    "\n----------------------------------------------------\n");
837            }
838        }
839        }
840 
841 
842        /**
843                Undo a part of or the entire transaction.  Begin rolling back the log
844                record at undoStartAt and stopping at (inclusive) the log record at
845                undoStopAt.
846 
847                <P>MT - Not needed. A transaction must be single threaded thru undo, 
848        each RawTransaction has its own logger, therefore no need to 
849        synchronize.  The RawTransaction must handle synchronizing with 
850        multiple threads during rollback.
851 
852                @param t                         the transaction that needs to be rolled back
853                @param undoId                 the transaction ID
854                @param undoStopAt        the last log record that should be rolled back
855                @param undoStartAt        the first log record that should be rolled back
856 
857                @exception StandardException        Standard Cloudscape error policy
858 
859                @see Logger#undo
860          */
861        public void undo(
862    RawTransaction  t, 
863    TransactionId   undoId, 
864    LogInstant      undoStopAt, 
865    LogInstant      undoStartAt)
866                throws StandardException 
867        {
868 
869                if (SanityManager.DEBUG)
870        {
871            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
872            {
873                if (undoStartAt != null)
874                {
875                    SanityManager.DEBUG(
876                        LogToFile.DBG_FLAG, 
877                        "\nUndo transaction: " + undoId.toString() + 
878                        "start at " + undoStartAt.toString() + 
879                        " stop at " + undoStopAt.toString() );
880                }
881                else
882                {
883                    SanityManager.DEBUG(
884                        LogToFile.DBG_FLAG, 
885                        "\nUndo transaction: " + undoId.toString() + 
886                        "start at end of log stop at " + undoStopAt.toString());
887                }
888            }
889        }
890 
891                // statistics
892                int clrgenerated  = 0;
893                int clrskipped    = 0;
894                int logrecordseen = 0;
895 
896                StreamLogScan scanLog;
897                Compensation  compensation = null;
898                Undoable      lop          = null;
899 
900                // stream to read the log record - initial size 4096, scanLog needs
901                // to resize if the log record is larget than that.
902                ArrayInputStream    rawInput   = null;
903 
904                try
905                {
906                        if (undoStartAt == null)        
907            {
908                // don't know where to start, rollback from end of log
909 
910                                scanLog = (StreamLogScan)
911                                        logFactory.openBackwardsScan(undoStopAt);
912            }
913                        else
914                        {
915                                if (undoStartAt.lessThan(undoStopAt))
916                {
917                    // nothing to undo!
918                                        return;
919                }
920 
921                                long undoStartInstant = 
922                    ((LogCounter) undoStartAt).getValueAsLong();
923 
924                                scanLog = (StreamLogScan)
925                                        logFactory.openBackwardsScan(undoStartInstant, undoStopAt);
926                        }
927 
928                        if (SanityManager.DEBUG)
929                                SanityManager.ASSERT(
930                    scanLog != null, "cannot open log for undo");
931 
932                        rawInput   = new ArrayInputStream(new byte[4096]);
933 
934                        LogRecord record;
935 
936                        while ((record = 
937                    scanLog.getNextRecord(rawInput, undoId, 0)) 
938                        != null) 
939                        { 
940                                if (SanityManager.DEBUG)
941                                {
942                                        SanityManager.ASSERT(
943                        record.getTransactionId().equals(undoId),
944                        "getNextRecord return unqualified log record for undo");
945                                }
946 
947                                logrecordseen++;
948 
949                                if (record.isCLR())
950                                {
951                                        clrskipped++;
952 
953                    // the loggable is still in the input stream, get rid of it
954                                        record.skipLoggable(); 
955 
956                                        // read the undoInstant
957                                        long undoInstant = rawInput.readLong();
958 
959                                        if (SanityManager.DEBUG)
960                    {
961                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
962                        {
963                            SanityManager.DEBUG(
964                                LogToFile.DBG_FLAG, 
965                                "Skipping over CLRs, reset scan to " + 
966                                LogCounter.toDebugString(undoInstant));
967                        }
968                    }
969 
970 
971                                        scanLog.resetPosition(new LogCounter(undoInstant));
972 
973                                        // scanLog now positioned at the beginning of the log
974                                        // record that was rolled back by this CLR.
975                                        // The scan is a backward one so getNextRecord will skip
976                                        // over the record that was rolled back and go to the one
977                                        // previous to it
978 
979                                        continue;
980                                }
981 
982                                lop = record.getUndoable();
983 
984                                if (lop != null)
985                                {
986                                        int optionalDataLength = rawInput.readInt();
987                                        int savePosition = rawInput.getPosition();
988                                        rawInput.setLimit(savePosition, optionalDataLength);
989        
990                                        compensation = lop.generateUndo(t, rawInput);
991 
992                                        if (SanityManager.DEBUG)
993                    {
994                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
995                        {
996                            SanityManager.DEBUG(
997                                LogToFile.DBG_FLAG, 
998                                "Rollback log record at instant " +
999                                LogCounter.toDebugString(scanLog.getInstant()) +
1000                                " : " + lop);
1001                        }
1002                    }
1003 
1004                                        clrgenerated++;
1005 
1006                                        if (compensation != null)
1007                                        {
1008                                                // generateUndo may have read stuff off the
1009                                                // stream, reset it for the undo operation.
1010                                                rawInput.setLimit(savePosition, optionalDataLength);
1011 
1012                                                // log the compensation op that rolls back the 
1013                        // operation at this instant 
1014                                                t.logAndUndo(
1015                            compensation, new LogCounter(scanLog.getInstant()),
1016                            rawInput);
1017 
1018                                                compensation.releaseResource(t);
1019                                                compensation = null;
1020                                        }
1021 
1022                                        // if compensation is null, log operation is redo only
1023                                }
1024                                // if this is not an undoable operation, continue with next log
1025                                // record
1026                        }
1027                }
1028                catch (ClassNotFoundException cnfe)
1029                {
1030                        throw logFactory.markCorrupt(
1031                StandardException.newException(SQLState.LOG_CORRUPTED, cnfe));
1032                }
1033            catch (IOException ioe) 
1034                {
1035                        throw logFactory.markCorrupt(
1036                StandardException.newException(
1037                    SQLState.LOG_READ_LOG_FOR_UNDO, ioe));
1038                }
1039                catch (StandardException se)
1040                {
1041            // TODO (4327) - exceptions caught here are nested in the exception
1042            // below but for some reason the nested exceptions are not logged 
1043            // or reported in any way.
1044 
1045                        throw logFactory.markCorrupt(
1046                StandardException.newException(
1047                    SQLState.LOG_UNDO_FAILED, se, undoId, lop, compensation));
1048                }
1049                finally
1050                {
1051                        if (compensation != null) 
1052            {
1053                // errored out
1054                                compensation.releaseResource(t);
1055            }
1056 
1057                        if (rawInput != null)
1058                        {
1059                                try
1060                                {
1061                                        rawInput.close();
1062                                }
1063                                catch (IOException ioe)
1064                                {
1065                                        throw logFactory.markCorrupt(
1066                        StandardException.newException(
1067                            SQLState.LOG_READ_LOG_FOR_UNDO, ioe, undoId));
1068                                }
1069                        }
1070                }
1071 
1072                if (SanityManager.DEBUG)
1073        {
1074            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1075            {
1076                SanityManager.DEBUG(
1077                    LogToFile.DBG_FLAG, 
1078                        "Finish undo" +
1079                        ", clr generated = " + clrgenerated +
1080                        ", clr skipped = " + clrskipped + 
1081                        ", record seen = " + logrecordseen + "\n");
1082            }
1083        }
1084        }
1085 
1086 
1087        /**
1088                Recovery Redo loop.
1089 
1090                <P> The log stream is scanned from the beginning (or
1091                from the undo low water mark of a checkpoint) forward until the end.
1092                The purpose of the redo pass is to repeat history, i.e, to repeat
1093                exactly the same set of changes the rawStore went thru right before it
1094                stopped.   With each log record that is encountered in the redo pass:
1095                <OL>
1096                <LI>if it isFirst(), then the transaction factory is called upon to
1097                    create a new transaction object.
1098                <LI>if it needsRedo(), its doMe() is called (if it is a compensation
1099                    operation, then the undoable operation needs to be created first
1100            before the doMe is called).
1101                <LI>if it isComplete(), then the transaction object is closed.
1102                </OL>
1103 
1104                <P> MT - caller provides synchronization
1105 
1106                @param transFactory     - the transaction factory
1107                @param redoLWM          - if checkpoint seen, starting from this point
1108                                  on, apply redo if necessary
1109 
1110                @return the log instant of the next log record (or the instant just
1111                after the last log record).  This is used to determine where the log
1112                truly ends
1113 
1114                @exception StandardException Standard Cloudscape error policy
1115                @exception IOException error reading log file
1116                @exception ClassNotFoundException log file corrupted
1117 
1118                @see LogToFile#recover
1119         */
1120        protected long redo(
1121    RawTransaction      recoveryTransaction,
1122    TransactionFactory  transFactory,
1123    StreamLogScan       redoScan,
1124    long                redoLWM,
1125    long                ttabInstant)
1126                 throws IOException, StandardException, ClassNotFoundException
1127        {
1128                // begin debug info
1129                if (SanityManager.DEBUG)
1130        {
1131            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1132            {
1133                SanityManager.DEBUG(
1134                    LogToFile.DBG_FLAG, 
1135                    "In recovery redo, redoLWM = " + redoLWM);
1136            }
1137        }
1138 
1139                int scanCount    = 0;
1140        int redoCount    = 0;
1141        int prepareCount = 0; 
1142        int clrCount     = 0;
1143        int btranCount   = 0;
1144        int etranCount   = 0;
1145 
1146                // end debug info
1147 
1148                TransactionId tranId = null;
1149 
1150        // the current log instant
1151                long instant = LogCounter.INVALID_LOG_INSTANT;
1152 
1153                //////////////////////////////////////////////////////////////////////
1154                // During redo time, the byte array in the logOutputBuffer is not used.
1155                // Use it to read the log record - if it is not big enough, scan
1156                // will resize it.  We could create a brand new log input stream that
1157                // has nothing to do with logIn or logOutputBuffer but that seem like
1158                // a waste of memory.
1159                //////////////////////////////////////////////////////////////////////
1160                logIn.setData(logOutputBuffer.getByteArray());
1161 
1162                // use this scan to reconstitute operation to be undone
1163                // when we see a CLR in the redo scan
1164                StreamLogScan undoScan  = null;
1165                Loggable      op        = null;
1166                long          logEnd    = 0;  // we need to determine the log's true end
1167 
1168                try 
1169        {
1170 
1171                        // scan the log forward in redo pass and go to the end
1172                        LogRecord record;
1173                        while((record = 
1174                    redoScan.getNextRecord(logIn, null, 0)) 
1175                        != null)
1176                        {
1177                                scanCount++;
1178                                long undoInstant = 0;
1179 
1180                                // last known good instant
1181                                instant = redoScan.getInstant();
1182 
1183                                // last known good log end
1184                                logEnd = redoScan.getLogRecordEnd();
1185 
1186 
1187                                // NOTE NOTE -- be very careful about the undoInstant, it is
1188                                // read off the input stream in this debug section.
1189                                // if we change the log format we will need to change the way
1190                                // the undo instant is gotten.  Also, once it is read off, it
1191                                // should not be read from the stream any more
1192                                // NOTE NOTE
1193                                if (SanityManager.DEBUG)
1194                {
1195                    if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY) ||
1196                        SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1197 
1198                    {
1199                        if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY))
1200                            SanityManager.DEBUG_SET(LogToFile.DBG_FLAG);
1201 
1202                        op = record.getLoggable();
1203                        tranId = record.getTransactionId();
1204                        if (record.isCLR())        
1205                        {
1206                            // !!!!!!! this moves the file pointer
1207                            undoInstant = logIn.readLong();
1208 
1209                            SanityManager.DEBUG(
1210                                LogToFile.DBG_FLAG, 
1211                                "scanned " + tranId + " : " + op + 
1212                                " instant = " + 
1213                                    LogCounter.toDebugString(instant) + 
1214                                " undoInstant : " + 
1215                                    LogCounter.toDebugString(undoInstant));
1216                        }
1217                        else
1218                        {
1219                            SanityManager.DEBUG(
1220                                LogToFile.DBG_FLAG, 
1221                                "scanned " + tranId + " : " + op + 
1222                                " instant = " + 
1223                                    LogCounter.toDebugString(instant)
1224                                + " logEnd = " + 
1225                                    LogCounter.toDebugString(logEnd) 
1226                                + " logIn at " + logIn.getPosition() 
1227                                + " available " + logIn.available());
1228                        }
1229 
1230                        // we only want to dump the log, don't touch it
1231                        if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY))
1232                            continue;
1233                    }
1234                }
1235 
1236                                // if the redo scan is between the undoLWM and redoLWM, we only
1237                                // need to redo begin and end tran.  Everything else has
1238                                // already been flushed by checkpoint
1239                                if (redoLWM != 
1240                        LogCounter.INVALID_LOG_INSTANT && instant < redoLWM)
1241                                {
1242                                        if (!(record.isFirst()      || 
1243                          record.isComplete()   || 
1244                          record.isPrepare()))
1245                    {
1246                                                continue;
1247                    }
1248                                }
1249 
1250                                // get the transaction
1251                                tranId = record.getTransactionId();
1252 
1253                                // if this transaction is known to the transaction factory, make
1254                // the recoveryTransaction assume its identitiy and properties
1255                // otherwise, make it known to the transaction factory
1256                                if (!transFactory.findTransaction(tranId, recoveryTransaction))
1257                                {
1258                                        // transaction not found
1259 
1260                                        if (redoLWM != LogCounter.INVALID_LOG_INSTANT && 
1261                                                instant < redoLWM &&
1262                        (record.isPrepare() || record.isComplete()))
1263                                        {
1264                                                // What is happening here is that a transaction that
1265                                                // started before the undoLWM has commited by the time
1266                                                // the checkpoint undoLWM was taken.  Hence, we only
1267                                                // see the tail end of its log record and its endXact
1268                        // record. 
1269                                                // 
1270                                                // NOTE:
1271                                                // Since we didn't see its beginXact, we cannot do the
1272                                                // endXact's doMe either.  Also if the endXact, is
1273                        // actually just a prepare, we don't need to do
1274                        // anything as the transaction will commit or abort
1275                        // prior to point we are recovering to.
1276                                                // If it is deemed necessary to do the endXact's doMe, 
1277                        // then we should start the transaction right here. 
1278                        // For now, just completely ignore this transaction
1279                                                // 
1280                                                etranCount++;
1281 
1282                                                continue;
1283                                        }
1284 
1285                                        if ((ttabInstant == LogCounter.INVALID_LOG_INSTANT) && 
1286                        !record.isFirst())
1287                    {
1288                                                throw StandardException.newException(
1289                            SQLState.LOG_UNEXPECTED_RECOVERY_PROBLEM,
1290                            MessageService.getTextMessage(MessageId.LOG_RECORD_NOT_FIRST,tranId));
1291 
1292                    }
1293 
1294                                        if (SanityManager.DEBUG)
1295                                        {
1296                                                // if we dumped the transaction table but see a non 
1297                        // BeginXact record after the transaction table dump 
1298                        // instant, error.
1299                                                if (ttabInstant != LogCounter.INVALID_LOG_INSTANT)
1300                                                {
1301                                                        if (instant > ttabInstant && !record.isFirst())
1302                            {
1303                                                                SanityManager.THROWASSERT(
1304                                                                "log record is Not first but transaction " + 
1305                                "is not in transaction table (2) : " + tranId);
1306                            }
1307 
1308                                                        // If we dump the transaction table and the table
1309                                                        // does not have the transaction, and we see this
1310                                                        // beginXact before the ttab instant, we could have
1311                                                        // igored it because we "know" that we should see
1312                                                        // the endXact before the ttab instant also.
1313                                                        // Leave it in just in case.
1314                                                }
1315                                        }
1316                                        btranCount++;
1317 
1318                                        // the long transaction ID is embedded in the beginXact log
1319                                        // record.  The short ID is stored in the log record.
1320                                        recoveryTransaction.setTransactionId(
1321                        record.getLoggable(), tranId);
1322 
1323                                }
1324                                else                                
1325                                {
1326                    // recoveryTransaction found
1327                    
1328                                        if ((ttabInstant == LogCounter.INVALID_LOG_INSTANT) && 
1329                         record.isFirst())
1330                    {
1331                                                throw StandardException.newException(
1332                            SQLState.LOG_UNEXPECTED_RECOVERY_PROBLEM,
1333                            MessageService.getTextMessage(MessageId.LOG_RECORD_FIRST,
1334                                tranId));
1335 
1336 
1337                    }
1338 
1339                                        if (SanityManager.DEBUG)
1340                                        {
1341                                                if (ttabInstant != LogCounter.INVALID_LOG_INSTANT &&
1342                                                        instant > ttabInstant &&
1343                                                        record.isFirst())
1344                        {
1345                                                        SanityManager.THROWASSERT(
1346                                                                "log record is first but transaction is " + 
1347                                "already in transaction table (3): " + tranId);
1348                        }
1349 
1350                        if (record.isPrepare())
1351                            prepareCount++;
1352                                        }
1353 
1354                                        // if we have a transaction table dumped with the
1355                                        // checkpoint log record, then during the redo scan we may
1356                                        // see the beginXact of a transaction which is already in
1357                    // the transaction table, just ignore it if it is after the
1358                                        // redoLWM but before the transaction table instant.  We
1359                                        // still need to redo any database changes but since the
1360                                        // transaction is already recorded in the transaction
1361                                        // table, ignore it.
1362                                        //
1363                                        if (record.isFirst())
1364                                        {
1365                                                btranCount++;
1366                                                continue;
1367                                        }
1368                                }
1369                                
1370                                op = record.getLoggable();
1371 
1372                                if (SanityManager.DEBUG)
1373                {
1374                    if (!record.isCLR())
1375                    {
1376                        if (logIn.available() < 4)
1377                        {
1378                            SanityManager.THROWASSERT(
1379                              "not enough bytes read in : " + 
1380                                  logIn.available() + 
1381                              " for " + op + " instant " + 
1382                                  LogCounter.toDebugString(instant));
1383                        }
1384                    }
1385                }
1386 
1387                                if (SanityManager.DEBUG)
1388                {
1389                                        SanityManager.ASSERT(
1390                        !recoveryTransaction.handlesPostTerminationWork(),
1391                                                 "recovery transaction handles post termination work");
1392                }
1393 
1394                                if (op.needsRedo(recoveryTransaction))
1395                                {
1396                                        redoCount++;
1397 
1398                                        if (record.isCLR())        
1399                                        {
1400                                                clrCount++;
1401 
1402                                                // the log operation is not complete, the operation to
1403                                                // undo is stashed away at the undoInstant.
1404                                                // Reconstitute that first.
1405 
1406                                                if (SanityManager.DEBUG)
1407                                                        SanityManager.ASSERT(op instanceof Compensation);
1408 
1409 
1410                        // this value may be set by sanity xxxx
1411                                                if (undoInstant == 0) 
1412                                                        undoInstant = logIn.readLong();
1413 
1414                                                if (undoScan == null)
1415                                                {
1416                                                        undoScan = (StreamLogScan)
1417                                                                logFactory.openForwardsScan(
1418                                    undoInstant,(LogInstant)null);
1419                                                }
1420                                                else
1421                                                {
1422                                                        undoScan.resetPosition(new LogCounter(undoInstant));
1423                                                }
1424 
1425                                                // undoScan now positioned at the beginning of the log
1426                                                // record was rolled back by this CLR.  
1427                                                // The scan is a forward one so getNextRecord will get 
1428                        // the log record that needs to be rolled back.
1429 
1430                                                // reuse the buffer in logIn and logIn since CLR 
1431                        // has no optional data and has no use for them anymore 
1432                                                logIn.clearLimit();
1433                                                LogRecord undoRecord =
1434                                                        undoScan.getNextRecord(logIn, null, 0);
1435 
1436                                                Undoable undoOp = undoRecord.getUndoable();
1437 
1438                                                if (SanityManager.DEBUG)
1439                                                {
1440                                                        SanityManager.DEBUG(
1441                                LogToFile.DBG_FLAG, 
1442                                "Redoing CLR: undoInstant = " + 
1443                                    LogCounter.toDebugString(undoInstant) +
1444                                " clrinstant = " + 
1445                                    LogCounter.toDebugString(instant));
1446 
1447                                                        SanityManager.ASSERT(
1448                                undoRecord.getTransactionId().equals(tranId));
1449 
1450                                                        SanityManager.ASSERT(undoOp != null);
1451                                                }
1452 
1453                                                ((Compensation)op).setUndoOp(undoOp);
1454                                        }
1455 
1456                                        // at this point, logIn points to the optional
1457                                        // data of the loggable that is to be redone or to be
1458                                        // rolled back
1459                                        
1460                                        if (SanityManager.DEBUG)
1461                    {
1462                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1463                        {
1464                            SanityManager.DEBUG(
1465                                LogToFile.DBG_FLAG, 
1466                                "redoing " + op + 
1467                                " instant = " + 
1468                                LogCounter.toDebugString(instant));
1469                        }
1470                    }
1471 
1472                                        int dataLength = logIn.readInt();
1473                                        logIn.setLimit(logIn.getPosition(), dataLength);
1474                                                                                
1475                                        // even though the log has already been written, we need to
1476                                        // tie the page to the log stream so that if redo failed
1477                                        // for some reasons, the log factory's corruption will stop
1478                                        // the corrupt page from flushing to disk.
1479 
1480                                        op.doMe(
1481                        recoveryTransaction, 
1482                        new LogCounter(instant), logIn);
1483 
1484                                        op.releaseResource(recoveryTransaction);
1485 
1486                                        op = null;
1487                                }
1488 
1489                                // RESOLVE: to speed up undo, may want to update the 
1490                                // LastLogInstant in the transaction table.  
1491                                // Right now, undo always start from the end of the log.
1492 
1493                                // one last thing, if this is the last log record of the
1494                                // transaction, then commit the transaction and clean up
1495                                //
1496                                // 'commit' even though the transaction maybe a rollback
1497                                // because we already did all the rollback work when redoing
1498                                // the CLRs.  Commit will only flush the log if this session
1499                                // has written any transaction, so in this case, it is a noop.
1500                                if (record.isComplete())
1501                                {
1502                                        etranCount++;
1503 
1504                                        if (SanityManager.DEBUG)
1505                                                SanityManager.ASSERT(
1506                            !recoveryTransaction.handlesPostTerminationWork(),
1507                            "recovery xact handles post termination work");
1508 
1509                                        recoveryTransaction.commit();
1510                                }
1511                        } // while redoScan.getNextRecord() != null
1512 
1513            // If the scan ended in an empty file, update logEnd to reflect that
1514            // in order to avoid to continue logging to an older file
1515            long end = redoScan.getLogRecordEnd(); 
1516            if (end != LogCounter.INVALID_LOG_INSTANT
1517                && (LogCounter.getLogFileNumber(logEnd) 
1518                    < LogCounter.getLogFileNumber(end))) {
1519                logEnd = end;
1520            }
1521                }
1522                catch (StandardException se)
1523                {
1524            throw StandardException.newException(
1525                    SQLState.LOG_REDO_FAILED, se, op);
1526                }
1527                finally
1528                {
1529                        // close all the io streams
1530                        redoScan.close();
1531                        redoScan = null;
1532 
1533                        if (undoScan != null)
1534                        {
1535                                undoScan.close();
1536                                undoScan = null;
1537                        }
1538 
1539                        if (op != null)
1540                                op.releaseResource(recoveryTransaction);
1541 
1542                }
1543 
1544                if (SanityManager.DEBUG)
1545        {
1546            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1547            {
1548                SanityManager.DEBUG(
1549                    LogToFile.DBG_FLAG, 
1550                    "----------------------------------------------------\n" +
1551                    "End of recovery redo\n" + 
1552                    "Scanned = " + scanCount + " log records" +
1553                    ", redid = " + redoCount +
1554                    " ( clr = " + clrCount + " )" +
1555                    " begintran = " + btranCount +
1556                    " endtran = " + etranCount + 
1557                    " preparetran = " + prepareCount + 
1558                    "\n log ends at " + LogCounter.toDebugString(logEnd) +
1559                    "\n----------------------------------------------------\n");
1560            }
1561        }
1562 
1563                if (SanityManager.DEBUG)
1564                {
1565                        // make sure logEnd and instant is consistent
1566                        if (instant != LogCounter.INVALID_LOG_INSTANT)        
1567            {
1568                                SanityManager.ASSERT(
1569                    LogCounter.getLogFileNumber(instant) <
1570                         LogCounter.getLogFileNumber(logEnd) ||
1571                    (LogCounter.getLogFileNumber(instant) ==
1572                         LogCounter.getLogFileNumber(logEnd) &&
1573                     LogCounter.getLogFilePosition(instant) <=
1574                         LogCounter.getLogFilePosition(logEnd)));
1575            }
1576                        else
1577            {
1578                                SanityManager.ASSERT(logEnd == LogCounter.INVALID_LOG_INSTANT);
1579            }
1580                }
1581 
1582        // logEnd is the last good log record position in the log
1583                return logEnd;                        
1584        }
1585 
1586 
1587        /**
1588                Read the next log record from the scan.
1589 
1590                <P>MT - caller must provide synchronization (right now, it is only
1591                called in recovery to find the checkpoint log record.  When this method
1592                is called by a more general audience, MT must be revisited).
1593 
1594                @param scan an opened log scan
1595                @param size estimated size of the log record
1596 
1597                @return the log operation that is the next in the scan, or null if no
1598                more log operation in the log scan
1599 
1600                @exception IOException        Error reading the log file
1601                @exception StandardException Standard Cloudscape error policy
1602                @exception ClassNotFoundException log corrupted
1603         */
1604        protected Loggable readLogRecord(StreamLogScan scan, int size)
1605                 throws IOException, StandardException, ClassNotFoundException
1606        {
1607                Loggable lop = null;
1608 
1609                ArrayInputStream logInputBuffer = new ArrayInputStream(new byte[size]);
1610 
1611                LogRecord record = scan.getNextRecord(logInputBuffer, null, 0);
1612                if (record != null)
1613                        lop = record.getLoggable();
1614                return lop;
1615        }
1616 
1617}

[all classes][org.apache.derby.impl.store.raw.log]
EMMA 2.0.5312 (C) Vladimir Roubtsov