KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > store > raw > log > FileLogger


1 /*
2
3    Derby - Class org.apache.derby.impl.store.raw.log.FileLogger
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.log;
23
24 import org.apache.derby.iapi.reference.SQLState;
25 import org.apache.derby.iapi.reference.MessageId;
26
27 import org.apache.derby.impl.store.raw.log.LogCounter;
28 import org.apache.derby.impl.store.raw.log.LogRecord;
29 import org.apache.derby.impl.store.raw.log.StreamLogScan;
30
31 import org.apache.derby.iapi.store.access.TransactionController;
32
33 import org.apache.derby.iapi.store.raw.RawStoreFactory;
34
35 import org.apache.derby.iapi.store.raw.log.LogInstant;
36 import org.apache.derby.iapi.store.raw.log.Logger;
37 import org.apache.derby.iapi.store.raw.log.LogScan;
38
39 import org.apache.derby.iapi.store.raw.xact.RawTransaction;
40 import org.apache.derby.iapi.store.raw.xact.TransactionFactory;
41 import org.apache.derby.iapi.store.raw.xact.TransactionId;
42
43 import org.apache.derby.iapi.store.raw.Compensation;
44 import org.apache.derby.iapi.store.raw.ContainerHandle;
45 import org.apache.derby.iapi.store.raw.LockingPolicy;
46 import org.apache.derby.iapi.store.raw.Loggable;
47 import org.apache.derby.iapi.store.raw.Page;
48 import org.apache.derby.iapi.store.raw.RePreparable;
49 import org.apache.derby.iapi.store.raw.Undoable;
50
51 import org.apache.derby.iapi.services.io.FormatIdOutputStream;
52
53 import org.apache.derby.iapi.services.sanity.SanityManager;
54
55 import org.apache.derby.iapi.error.StandardException;
56 import org.apache.derby.iapi.services.i18n.MessageService;
57
58 import org.apache.derby.iapi.services.io.ArrayInputStream;
59 import org.apache.derby.iapi.services.io.ArrayOutputStream;
60 import org.apache.derby.iapi.util.ByteArray;
61 import org.apache.derby.iapi.services.io.DynamicByteArrayOutputStream;
62
63 import org.apache.derby.iapi.services.io.LimitObjectInput;
64 import java.io.IOException JavaDoc;
65
66 import org.apache.derby.impl.store.raw.data.InitPageOperation;
67
68 /**
69     Write log records to a log file as a stream
70     (ie. log records added to the end of the file, no concept of pages).
71 <P>
72     The format of a log record that is not a compensation operation is
73     <PRE>
74     @format_id no formatId, format is implied by the log file format and the
75         log record content.
76     @purpose the log record and optional data
77     @upgrade
78     @disk_layout
79         Log Record
80             (see org.apache.derby.impl.store.raw.log.LogRecord)
81         length(int) length of optional data
82         optionalData(byte[length]) optional data written by the log record
83     @end_format
84     </PRE> <HR WIDTH="100%">
85
86     <P> The form of a log record that is a compensation operation is
87     <PRE>
88     @format_id no formatId, format is implied by the log file format and the
89     log record content.
90     @purpose undo a previous log record
91     @upgrade
92     @disk_layout
93         Log Record that contains the compenstation operation
94             (see org.apache.derby.impl.store.raw.log.LogRecord)
95         undoInstant(long) the log instant of the operation that is to be rolled back
96             The undo instant is logically part of the LogRecord but is written
97             by the logger because it is used and controlled by the rollback
98             code but not by the log operation.
99         There is no optional data in a compensation operation, all data
100         necessary for the rollback must be stored in the operation being
101         undone.
102     @end_format
103     </PRE>
104
105     <BR>
106
107     <P>Multithreading considerations:<BR>
108     Logger must be MT-safe. Each RawTransaction has its own private
109     FileLogger object. Each logger has a logOutputBuffer and a log input
110     buffer which are used to read and write to the log. Since multiple
111     threads can be in the same transaction, fileLogger must be synchronized.
112
113     @see LogRecord
114 */

115
116 public class FileLogger implements Logger {
117
118     private LogRecord logRecord;
119
120     protected byte[] encryptionBuffer;
121     private DynamicByteArrayOutputStream logOutputBuffer;
122     private FormatIdOutputStream logicalOut;
123
124     private ArrayInputStream logIn;
125
126     private LogToFile logFactory; // actually writes the log records.
127

128     /**
129         Make a new Logger with its own log record buffers
130         MT - not needed for constructor
131     */

132     public FileLogger(LogToFile logFactory) {
133
134         this.logFactory = logFactory;
135         logOutputBuffer = new DynamicByteArrayOutputStream(1024); // init size 1K
136
logicalOut = new FormatIdOutputStream(logOutputBuffer);
137
138         // logIn and logOutputBuffer must share the same buffer because they
139
// combined to form an IO stream to access the same log record.
140
//
141
// Before each use of logIn, you must reset logIn's data to the
142
// byte array you want to read from.
143
//
144
// To log a record, set logIn's data to point to logOutputBuffer's
145
// byte array when you know you have everything you need in the output
146
// buffer, then set limit on logIn and send it to the log operation's
147
// doMe.
148
//
149
// Keep in mind the dynamic nature of the logOutputBuffer which means
150
// it could switch buffer from underneath the logOutputBuffer on every
151
// write.
152
logIn = new ArrayInputStream();
153  
154         logRecord = new LogRecord();
155
156     }
157
158     /**
159         Close the logger.
160         MT - caller provide synchronization
161         (RESOLVE: not called by anyone ??)
162     */

163     public void close() throws IOException JavaDoc
164     {
165         if (logOutputBuffer != null)
166         {
167             logOutputBuffer.close();
168             logOutputBuffer = null;
169         }
170
171         logIn = null;
172         logFactory = null;
173
174         logicalOut = null;
175
176         logRecord = null;
177     }
178
179     /*
180     ** Methods of Logger
181     */

182
183     /**
184         Writes out a log record to the log stream, and call its doMe method to
185         apply the change to the rawStore.
186         <BR>Any optional data the doMe method need is first written to the log
187         stream using operation.writeOptionalData, then whatever is written to
188         the log stream is passed back to the operation for the doMe method.
189
190         <P>MT - there could be multiple threads running in the same raw
191         transactions and they can be calling the same logger to log different
192         log operations. This whole method is synchronized to make sure log
193         records are logged one at a time.
194
195         @param xact the transaction logging the change
196         @param operation the log operation
197         @return the instant in the log that can be used to identify the log
198         record
199
200         @exception StandardException Cloudscape Standard error policy
201     */

202     public synchronized LogInstant logAndDo(RawTransaction xact, Loggable operation)
203          throws StandardException
204     {
205         boolean isLogPrepared = false;
206
207         boolean inUserCode = false;
208         byte[] preparedLog;
209
210         try {
211
212             logOutputBuffer.reset();
213
214             // always use the short Id, only the BeginXact log record contains
215
// the XactId (long form)
216
TransactionId transactionId = xact.getId();
217
218             // write out the log header with the operation embedded
219
// this is by definition not a compensation log record,
220
// those are called thru the logAndUndo interface
221
logRecord.setValue(transactionId, operation);
222
223             inUserCode = true;
224             logicalOut.writeObject(logRecord);
225             inUserCode = false;
226
227             int optionalDataLength = 0;
228             int optionalDataOffset = 0;
229             int completeLength = 0;
230
231             ByteArray preparedLogArray = operation.getPreparedLog();
232             if (preparedLogArray != null) {
233
234                 preparedLog = preparedLogArray.getArray();
235                 optionalDataLength = preparedLogArray.getLength();
236                 optionalDataOffset = preparedLogArray.getOffset();
237
238                 // There is a race condition if the operation is a begin tran in
239
// that between the time the beginXact log record is written to
240
// disk and the time the transaction object is updated in the
241
// beginXact.doMe method, other log records may be written.
242
// This will render the transaction table in an inconsistent state
243
// since it may think a later transaction is the earliest
244
// transaction or it may think that there is no active transactions
245
// where there is a bunch of them sitting on the log.
246
//
247
// Similarly, there is a race condition for endXact, i.e.,
248
// 1) endXact is written to the log,
249
// 2) checkpoint gets that (committed) transaction as the
250
// firstUpdateTransaction
251
// 3) the transaction calls postComplete, nulling out itself
252
// 4) checkpoint tries to access a closed transaction object
253
//
254
// The solution is to sync between the time a begin tran or end
255
// tran log record is sent to the log stream and its doMe method is
256
// called to update the transaction table and in memory state
257
//
258
// We only need to serialized the begin and end Xact log records
259
// because once a transaction has been started and in the
260
// transaction table, its order and transaction state does not
261
// change.
262
//
263
// Use the logFactory as the sync object so that a checkpoint can
264
// take its snap shot of the undoLWM before or after a transaction
265
// is started, but not in the middle. (see LogToFile.checkpoint)
266
//
267

268                 // now set the input limit to be the optional data.
269
// This limits amount of data availiable to logIn that doMe can
270
// use
271
logIn.setData(preparedLog);
272                 logIn.setPosition(optionalDataOffset);
273                 logIn.setLimit(optionalDataLength);
274
275                 if (SanityManager.DEBUG)
276                 {
277                     if ((optionalDataLength) != logIn.available())
278                         SanityManager.THROWASSERT(
279                             " stream not set correctly " +
280                             optionalDataLength + " != " +
281                              logIn.available());
282                 }
283
284             } else {
285                 preparedLog = null;
286                 optionalDataLength = 0;
287             }
288
289             logicalOut.writeInt(optionalDataLength);
290             completeLength = logOutputBuffer.getPosition() + optionalDataLength;
291
292
293             LogInstant logInstant = null;
294             int encryptedLength = 0; // in case of encryption, we need to pad
295

296             try
297             {
298                 if (logFactory.databaseEncrypted())
299                 {
300                     // we must pad the encryption data to be multiple of block
301
// size, which is logFactory.getEncryptionBlockSize()
302
encryptedLength = completeLength;
303                     if ((encryptedLength % logFactory.getEncryptionBlockSize()) != 0)
304                         encryptedLength = encryptedLength + logFactory.getEncryptionBlockSize() - (encryptedLength % logFactory.getEncryptionBlockSize());
305
306                     if (encryptionBuffer == null ||
307                         encryptionBuffer.length < encryptedLength)
308                         encryptionBuffer = new byte[encryptedLength];
309
310                     System.arraycopy(logOutputBuffer.getByteArray(), 0,
311                                      encryptionBuffer, 0, completeLength-optionalDataLength);
312
313                     if (optionalDataLength > 0)
314                         System.arraycopy(preparedLog, optionalDataOffset,
315                                      encryptionBuffer,
316                                      completeLength-optionalDataLength, optionalDataLength);
317
318                     // do not bother to clear out the padding area
319
int len =
320                         logFactory.encrypt(encryptionBuffer, 0, encryptedLength,
321                                            encryptionBuffer, 0);
322
323                     if (SanityManager.DEBUG)
324                         SanityManager.ASSERT(len == encryptedLength,
325                             "encrypted log buffer length != log buffer len");
326                 }
327
328                 if ((operation.group() & (Loggable.FIRST | Loggable.LAST)) != 0)
329                 {
330                     synchronized (logFactory)
331                     {
332                         long instant = 0;
333
334                         if (logFactory.databaseEncrypted())
335                         {
336                             // encryption has completely drained both the the
337
// logOuputBuffer array and the preparedLog array
338
instant = logFactory.
339                                 appendLogRecord(encryptionBuffer, 0,
340                                                 encryptedLength, null,
341                                                 -1, 0);
342                         }
343                         else
344                         {
345                             instant = logFactory.
346                                 appendLogRecord(logOutputBuffer.getByteArray(),
347                                                 0, completeLength, preparedLog,
348                                                 optionalDataOffset,
349                                                 optionalDataLength);
350                         }
351                         logInstant = new LogCounter(instant);
352
353                         operation.doMe(xact, logInstant, logIn);
354                     }
355                 }
356                 else
357                 {
358                     long instant = 0;
359
360                     if (logFactory.databaseEncrypted())
361                     {
362                         // encryption has completely drained both the the
363
// logOuputBuffer array and the preparedLog array
364
instant = logFactory.
365                             appendLogRecord(encryptionBuffer, 0,
366                                             encryptedLength, null, -1, 0);
367                     }
368                     else
369                     {
370                         instant = logFactory.
371                             appendLogRecord(logOutputBuffer.getByteArray(), 0,
372                                             completeLength, preparedLog,
373                                             optionalDataOffset,
374                                             optionalDataLength);
375                     }
376
377                     logInstant = new LogCounter(instant);
378
379                     operation.doMe(xact, logInstant, logIn);
380                 }
381
382             }
383             catch (StandardException se)
384             {
385                 throw logFactory.markCorrupt(
386                         StandardException.newException(
387                             SQLState.LOG_DO_ME_FAIL, se, operation));
388             }
389             catch (IOException JavaDoc ioe)
390             {
391                 throw logFactory.markCorrupt(
392                         StandardException.newException(
393                             SQLState.LOG_DO_ME_FAIL, ioe, operation));
394             }
395             finally
396             {
397                 logIn.clearLimit();
398             }
399
400             if (SanityManager.DEBUG)
401             {
402                 if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
403                 {
404                     SanityManager.DEBUG(
405                         LogToFile.DBG_FLAG,
406                         "Write log record: tranId=" + transactionId.toString() +
407                         " instant: " + logInstant.toString() + " length: " +
408                         completeLength + "\n" + operation + "\n");
409                 }
410             }
411             return logInstant;
412         }
413
414         catch (IOException JavaDoc ioe)
415         {
416             // error writing to the log buffer
417
if (inUserCode)
418             {
419                 throw StandardException.newException(
420                         SQLState.LOG_WRITE_LOG_RECORD, ioe, operation);
421             }
422             else
423             {
424                 throw StandardException.newException(
425                         SQLState.LOG_BUFFER_FULL, ioe, operation);
426             }
427         }
428
429     }
430
431     /**
432         Writes out a compensation log record to the log stream, and call its
433         doMe method to undo the change of a previous log operation.
434
435         <P>MT - Not needed. A transaction must be single threaded thru undo, each
436         RawTransaction has its own logger, therefore no need to synchronize.
437         The RawTransaction must handle synchronizing with multiple threads
438         during rollback.
439
440         @param xact the transaction logging the change
441         @param compensation the compensation log operation
442         @param undoInstant the log instant of the operation that is to be
443         rolled back
444         @param in optional data input for the compenastion doMe method
445
446         @return the instant in the log that can be used to identify the log
447         record
448
449         @exception StandardException Cloudscape Standard error policy
450      */

451     public LogInstant logAndUndo(RawTransaction xact,
452                                  Compensation compensation,
453                                  LogInstant undoInstant,
454                                  LimitObjectInput in)
455          throws StandardException
456     {
457         boolean inUserCode = false;
458
459         try {
460             logOutputBuffer.reset();
461
462             TransactionId transactionId = xact.getId();
463
464             // write out the log header with the operation embedded
465
logRecord.setValue(transactionId, compensation);
466
467             inUserCode = true;
468             logicalOut.writeObject(logRecord);
469             inUserCode = false;
470
471             // write out the undoInstant
472
logicalOut.writeLong(((LogCounter)undoInstant).getValueAsLong());
473
474             // in this implemetaion, there is no optional data for the
475
// compensation operation. Optional data for the rollback comes
476
// from the undoable operation - and is passed into this call.
477
int completeLength = logOutputBuffer.getPosition();
478             long instant = 0;
479             
480             if (logFactory.databaseEncrypted())
481             {
482                 // we must pad the encryption data to be multiple of block
483
// size, which is logFactory.getEncryptionBlockSize()
484
int encryptedLength = completeLength;
485                 if ((encryptedLength % logFactory.getEncryptionBlockSize()) != 0)
486                     encryptedLength = encryptedLength + logFactory.getEncryptionBlockSize() - (encryptedLength % logFactory.getEncryptionBlockSize());
487
488                 if (encryptionBuffer == null ||
489                     encryptionBuffer.length < encryptedLength)
490                     encryptionBuffer = new byte[encryptedLength];
491
492                 System.arraycopy(logOutputBuffer.getByteArray(), 0,
493                                  encryptionBuffer, 0, completeLength);
494
495                 // do not bother to clear out the padding area
496
int len =
497                     logFactory.encrypt(encryptionBuffer, 0, encryptedLength,
498                                        encryptionBuffer, 0);
499
500                 if (SanityManager.DEBUG)
501                     SanityManager.ASSERT(len == encryptedLength,
502                         "encrypted log buffer length != log buffer len");
503
504                 instant = logFactory.
505                     appendLogRecord(encryptionBuffer,
506                                     0, encryptedLength, null, 0, 0);
507             }
508             else
509             {
510                 instant = logFactory.
511                     appendLogRecord(logOutputBuffer.getByteArray(),
512                                     0, completeLength, null, 0, 0);
513             }
514
515             LogInstant logInstant = new LogCounter(instant);
516
517             if (SanityManager.DEBUG)
518             {
519                 if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
520                 {
521                     SanityManager.DEBUG(
522                         LogToFile.DBG_FLAG,
523                         "Write CLR: Xact: " + transactionId.toString() +
524                         "clrinstant: " + logInstant.toString() +
525                         " undoinstant " + undoInstant + "\n");
526                 }
527             }
528
529             try
530             {
531                 // in and dataLength contains optional data that was written
532
// to the log during a previous call to logAndDo.
533
compensation.doMe(xact, logInstant, in);
534             }
535             catch (StandardException se)
536             {
537                 throw logFactory.markCorrupt(
538                         StandardException.newException(
539                             SQLState.LOG_DO_ME_FAIL, se, compensation));
540             }
541             catch (IOException JavaDoc ioe)
542             {
543                 throw logFactory.markCorrupt(
544                         StandardException.newException(
545                             SQLState.LOG_DO_ME_FAIL, ioe, compensation));
546             }
547
548             return logInstant;
549
550         }
551         catch (IOException JavaDoc ioe)
552         {
553             if (inUserCode)
554             {
555                 throw StandardException.newException(
556                         SQLState.LOG_WRITE_LOG_RECORD, ioe, compensation);
557             }
558             else
559             {
560                 throw StandardException.newException(
561                         SQLState.LOG_BUFFER_FULL, ioe, compensation);
562             }
563         }
564     }
565
566     /**
567         Flush the log up to the given log instant.
568
569         <P>MT - not needed, wrapper method
570
571         @exception StandardException cannot sync log file
572     */

573     public void flush(LogInstant where)
574          throws StandardException
575     {
576         if (SanityManager.DEBUG)
577         {
578             if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
579             {
580                 SanityManager.DEBUG(
581                     LogToFile.DBG_FLAG, "Flush log to " + where.toString());
582             }
583         }
584
585         logFactory.flush(where);
586     }
587
588     /**
589         Flush all outstanding log to disk.
590
591         <P>MT - not needed, wrapper method
592
593         @exception StandardException cannot sync log file
594     */

595     public void flushAll () throws StandardException
596     {
597         logFactory.flushAll();
598     }
599
600     /**
601      * During recovery re-prepare a transaction.
602      * <p>
603      * After redo() and undo(), this routine is called on all outstanding
604      * in-doubt (prepared) transactions. This routine re-acquires all
605      * logical write locks for operations in the xact, and then modifies
606      * the transaction table entry to make the transaction look as if it
607      * had just been prepared following startup after recovery.
608      * <p>
609      *
610      * @param t is the transaction performing the re-prepare
611      * @param prepareId is the transaction ID to be re-prepared
612      * @param prepareStopAt is where the log instant (inclusive) where the
613      * re-prepare should stop.
614      * @param prepareStartAt is the log instant (inclusive) where re-prepare
615      * should begin, this is normally the log instant
616      * of the last log record of the transaction that
617      * is to be re-prepare. If null, then re-prepare
618      * starts from the end of the log.
619      *
620      * @exception StandardException Standard exception policy.
621      **/

622     public void reprepare(
623     RawTransaction t,
624     TransactionId prepareId,
625     LogInstant prepareStopAt,
626     LogInstant prepareStartAt)
627         throws StandardException
628     {
629
630         if (SanityManager.DEBUG)
631         {
632             if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
633             {
634                 if (prepareStartAt != null)
635                 {
636                     SanityManager.DEBUG(
637                     LogToFile.DBG_FLAG,
638                     "----------------------------------------------------\n" +
639                     "\nBegin of RePrepare : " + prepareId.toString() +
640                         "start at " + prepareStartAt.toString() +
641                         " stop at " + prepareStopAt.toString() +
642                     "\n----------------------------------------------------\n");
643                 }
644                 else
645                 {
646                     SanityManager.DEBUG(
647                     LogToFile.DBG_FLAG,
648                     "----------------------------------------------------\n" +
649                     "\nBegin of Reprepare: " + prepareId.toString() +
650                         "start at end of log stop at " +
651                         prepareStopAt.toString() +
652                     "\n----------------------------------------------------\n");
653                 }
654             }
655         }
656
657         // statistics
658
int clrskipped = 0;
659         int logrecordseen = 0;
660
661         RePreparable lop = null;
662
663         // stream to read the log record - initial size 4096, scanLog needs
664
// to resize if the log record is larger than that.
665
ArrayInputStream rawInput = null;
666
667         try
668         {
669             StreamLogScan scanLog;
670
671             if (prepareStartAt == null)
672             {
673                 // don't know where to start, scan from end of log
674
scanLog =
675                     (StreamLogScan) logFactory.openBackwardsScan(prepareStopAt);
676             }
677             else
678             {
679                 if (prepareStartAt.lessThan(prepareStopAt))
680                 {
681                     // nothing to prepare!
682
return;
683                 }
684
685                 scanLog = (StreamLogScan)
686                     logFactory.openBackwardsScan(
687                         ((LogCounter) prepareStartAt).getValueAsLong(),
688                         prepareStopAt);
689             }
690
691             if (SanityManager.DEBUG)
692                 SanityManager.ASSERT(
693                     scanLog != null, "cannot open log for prepare");
694
695             rawInput = new ArrayInputStream(new byte[4096]);
696
697             LogRecord record;
698
699             while ((record =
700                     scanLog.getNextRecord(rawInput, prepareId, 0))
701                        != null)
702             {
703                 if (SanityManager.DEBUG)
704                 {
705                     SanityManager.ASSERT(
706                         record.getTransactionId().equals(prepareId),
707                         "getNextRecord return unqualified log rec for prepare");
708                 }
709
710                 logrecordseen++;
711
712                 if (record.isCLR())
713                 {
714                     clrskipped++;
715
716                     // the loggable is still in the input stream, get rid of it
717
record.skipLoggable();
718
719                     // read the prepareInstant
720
long prepareInstant = rawInput.readLong();
721
722                     if (SanityManager.DEBUG)
723                     {
724                         if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
725                         {
726                             SanityManager.DEBUG(
727                                 LogToFile.DBG_FLAG,
728                                     "Skipping over CLRs, reset scan to " +
729                                     LogCounter.toDebugString(prepareInstant));
730                         }
731                     }
732
733                     scanLog.resetPosition(new LogCounter(prepareInstant));
734                     // scanLog now positioned at the beginning of the log
735
// record that was rolled back by this CLR.
736
// The scan is a backward one so getNextRecord will skip
737
// over the record that was rolled back and go to the one
738
// previous to it
739

740                     continue;
741                 }
742
743                 if (record.requiresPrepareLocks())
744                 {
745                     lop = record.getRePreparable();
746                 }
747                 else
748                 {
749                     continue;
750                 }
751
752                 if (lop != null)
753                 {
754                     // Reget locks based on log record. reclaim all locks with
755
// a serializable locking policy, since we are only
756
// reclaiming write locks, isolation level does not matter
757
// much.
758

759                     lop.reclaimPrepareLocks(
760                         t,
761                         t.newLockingPolicy(
762                             LockingPolicy.MODE_RECORD,
763                             TransactionController.ISOLATION_REPEATABLE_READ,
764                             true));
765
766                     if (SanityManager.DEBUG)
767                     {
768                         if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
769                         {
770                             SanityManager.DEBUG(
771                                 LogToFile.DBG_FLAG,
772                                 "Reprepare log record at instant " +
773                                 scanLog.getInstant() + " : " + lop);
774                         }
775                     }
776
777                 }
778             }
779
780         }
781         catch (ClassNotFoundException JavaDoc cnfe)
782         {
783             throw logFactory.markCorrupt(
784                 StandardException.newException(SQLState.LOG_CORRUPTED, cnfe));
785         }
786         catch (IOException JavaDoc ioe)
787         {
788             throw logFactory.markCorrupt(
789                 StandardException.newException(
790                     SQLState.LOG_READ_LOG_FOR_UNDO, ioe));
791         }
792         catch (StandardException se)
793         {
794             throw
795                 logFactory.markCorrupt(
796                     StandardException.newException(
797                         SQLState.LOG_UNDO_FAILED, se,
798                         prepareId, lop, (Object JavaDoc) null));
799         }
800         finally
801         {
802             if (rawInput != null)
803             {
804                 try
805                 {
806                     rawInput.close();
807                 }
808                 catch (IOException JavaDoc ioe)
809                 {
810                     throw logFactory.markCorrupt(
811                         StandardException.newException(
812                             SQLState.LOG_READ_LOG_FOR_UNDO, ioe, prepareId));
813                 }
814             }
815         }
816
817         if (SanityManager.DEBUG)
818         {
819             if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
820             {
821                 SanityManager.DEBUG(LogToFile.DBG_FLAG, "Finish prepare" +
822                                     ", clr skipped = " + clrskipped +
823                                     ", record seen = " + logrecordseen + "\n");
824             }
825         }
826
827         if (SanityManager.DEBUG)
828         {
829             if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
830             {
831                 SanityManager.DEBUG(
832                     LogToFile.DBG_FLAG,
833                     "----------------------------------------------------\n" +
834                     "End of recovery rePrepare\n" +
835                     ", clr skipped = " + clrskipped +
836                     ", record seen = " + logrecordseen +
837                     "\n----------------------------------------------------\n");
838             }
839         }
840     }
841
842
843     /**
844         Undo a part of or the entire transaction. Begin rolling back the log
845         record at undoStartAt and stopping at (inclusive) the log record at
846         undoStopAt.
847
848         <P>MT - Not needed. A transaction must be single threaded thru undo,
849         each RawTransaction has its own logger, therefore no need to
850         synchronize. The RawTransaction must handle synchronizing with
851         multiple threads during rollback.
852
853         @param t the transaction that needs to be rolled back
854         @param undoId the transaction ID
855         @param undoStopAt the last log record that should be rolled back
856         @param undoStartAt the first log record that should be rolled back
857
858         @exception StandardException Standard Cloudscape error policy
859
860         @see Logger#undo
861       */

862     public void undo(
863     RawTransaction t,
864     TransactionId undoId,
865     LogInstant undoStopAt,
866     LogInstant undoStartAt)
867         throws StandardException
868     {
869
870         if (SanityManager.DEBUG)
871         {
872             if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
873             {
874                 if (undoStartAt != null)
875                 {
876                     SanityManager.DEBUG(
877                         LogToFile.DBG_FLAG,
878                         "\nUndo transaction: " + undoId.toString() +
879                         "start at " + undoStartAt.toString() +
880                         " stop at " + undoStopAt.toString() );
881                 }
882                 else
883                 {
884                     SanityManager.DEBUG(
885                         LogToFile.DBG_FLAG,
886                         "\nUndo transaction: " + undoId.toString() +
887                         "start at end of log stop at " + undoStopAt.toString());
888                 }
889             }
890         }
891
892         // statistics
893
int clrgenerated = 0;
894         int clrskipped = 0;
895         int logrecordseen = 0;
896
897         StreamLogScan scanLog;
898         Compensation compensation = null;
899         Undoable lop = null;
900
901         // stream to read the log record - initial size 4096, scanLog needs
902
// to resize if the log record is larget than that.
903
ArrayInputStream rawInput = null;
904
905         try
906         {
907             if (undoStartAt == null)
908             {
909                 // don't know where to start, rollback from end of log
910

911                 scanLog = (StreamLogScan)
912                     logFactory.openBackwardsScan(undoStopAt);
913             }
914             else
915             {
916                 if (undoStartAt.lessThan(undoStopAt))
917                 {
918                     // nothing to undo!
919
return;
920                 }
921
922                 long undoStartInstant =
923                     ((LogCounter) undoStartAt).getValueAsLong();
924
925                 scanLog = (StreamLogScan)
926                     logFactory.openBackwardsScan(undoStartInstant, undoStopAt);
927             }
928
929             if (SanityManager.DEBUG)
930                 SanityManager.ASSERT(
931                     scanLog != null, "cannot open log for undo");
932
933             rawInput = new ArrayInputStream(new byte[4096]);
934
935             LogRecord record;
936
937             while ((record =
938                     scanLog.getNextRecord(rawInput, undoId, 0))
939                         != null)
940             {
941                 if (SanityManager.DEBUG)
942                 {
943                     SanityManager.ASSERT(
944                         record.getTransactionId().equals(undoId),
945                         "getNextRecord return unqualified log record for undo");
946                 }
947
948                 logrecordseen++;
949
950                 if (record.isCLR())
951                 {
952                     clrskipped++;
953
954                     // the loggable is still in the input stream, get rid of it
955
record.skipLoggable();
956
957                     // read the undoInstant
958
long undoInstant = rawInput.readLong();
959
960                     if (SanityManager.DEBUG)
961                     {
962                         if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
963                         {
964                             SanityManager.DEBUG(
965                                 LogToFile.DBG_FLAG,
966                                 "Skipping over CLRs, reset scan to " +
967                                 LogCounter.toDebugString(undoInstant));
968                         }
969                     }
970
971
972                     scanLog.resetPosition(new LogCounter(undoInstant));
973
974                     // scanLog now positioned at the beginning of the log
975
// record that was rolled back by this CLR.
976
// The scan is a backward one so getNextRecord will skip
977
// over the record that was rolled back and go to the one
978
// previous to it
979

980                     continue;
981                 }
982
983                 lop = record.getUndoable();
984
985                 if (lop != null)
986                 {
987                     int optionalDataLength = rawInput.readInt();
988                     int savePosition = rawInput.getPosition();
989                     rawInput.setLimit(savePosition, optionalDataLength);
990     
991                     compensation = lop.generateUndo(t, rawInput);
992
993                     if (SanityManager.DEBUG)
994                     {
995                         if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
996                         {
997                             SanityManager.DEBUG(
998                                 LogToFile.DBG_FLAG,
999                                 "Rollback log record at instant " +
1000                                LogCounter.toDebugString(scanLog.getInstant()) +
1001                                " : " + lop);
1002                        }
1003                    }
1004
1005                    clrgenerated++;
1006
1007                    if (compensation != null)
1008                    {
1009                        // generateUndo may have read stuff off the
1010
// stream, reset it for the undo operation.
1011
rawInput.setLimit(savePosition, optionalDataLength);
1012
1013                        // log the compensation op that rolls back the
1014
// operation at this instant
1015
t.logAndUndo(
1016                            compensation, new LogCounter(scanLog.getInstant()),
1017                            rawInput);
1018
1019                        compensation.releaseResource(t);
1020                        compensation = null;
1021                    }
1022
1023                    // if compensation is null, log operation is redo only
1024
}
1025                // if this is not an undoable operation, continue with next log
1026
// record
1027
}
1028        }
1029        catch (ClassNotFoundException JavaDoc cnfe)
1030        {
1031            throw logFactory.markCorrupt(
1032                StandardException.newException(SQLState.LOG_CORRUPTED, cnfe));
1033        }
1034        catch (IOException JavaDoc ioe)
1035        {
1036            throw logFactory.markCorrupt(
1037                StandardException.newException(
1038                    SQLState.LOG_READ_LOG_FOR_UNDO, ioe));
1039        }
1040        catch (StandardException se)
1041        {
1042            // TODO (4327) - exceptions caught here are nested in the exception
1043
// below but for some reason the nested exceptions are not logged
1044
// or reported in any way.
1045

1046            throw logFactory.markCorrupt(
1047                StandardException.newException(
1048                    SQLState.LOG_UNDO_FAILED, se, undoId, lop, compensation));
1049        }
1050        finally
1051        {
1052            if (compensation != null)
1053            {
1054                // errored out
1055
compensation.releaseResource(t);
1056            }
1057
1058            if (rawInput != null)
1059            {
1060                try
1061                {
1062                    rawInput.close();
1063                }
1064                catch (IOException JavaDoc ioe)
1065                {
1066                    throw logFactory.markCorrupt(
1067                        StandardException.newException(
1068                            SQLState.LOG_READ_LOG_FOR_UNDO, ioe, undoId));
1069                }
1070            }
1071        }
1072
1073        if (SanityManager.DEBUG)
1074        {
1075            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1076            {
1077                SanityManager.DEBUG(
1078                    LogToFile.DBG_FLAG,
1079                        "Finish undo" +
1080                        ", clr generated = " + clrgenerated +
1081                        ", clr skipped = " + clrskipped +
1082                        ", record seen = " + logrecordseen + "\n");
1083            }
1084        }
1085    }
1086
1087
1088    /**
1089        Recovery Redo loop.
1090
1091        <P> The log stream is scanned from the beginning (or
1092        from the undo low water mark of a checkpoint) forward until the end.
1093        The purpose of the redo pass is to repeat history, i.e, to repeat
1094        exactly the same set of changes the rawStore went thru right before it
1095        stopped. With each log record that is encountered in the redo pass:
1096        <OL>
1097        <LI>if it isFirst(), then the transaction factory is called upon to
1098            create a new transaction object.
1099        <LI>if it needsRedo(), its doMe() is called (if it is a compensation
1100            operation, then the undoable operation needs to be created first
1101            before the doMe is called).
1102        <LI>if it isComplete(), then the transaction object is closed.
1103        </OL>
1104
1105        <P> MT - caller provides synchronization
1106
1107        @param transFactory - the transaction factory
1108        @param redoLWM - if checkpoint seen, starting from this point
1109                                  on, apply redo if necessary
1110
1111        @return the log instant of the next log record (or the instant just
1112        after the last log record). This is used to determine where the log
1113        truly ends
1114
1115        @exception StandardException Standard Cloudscape error policy
1116        @exception IOException error reading log file
1117        @exception ClassNotFoundException log file corrupted
1118
1119        @see LogToFile#recover
1120     */

1121    protected long redo(
1122    RawTransaction recoveryTransaction,
1123    TransactionFactory transFactory,
1124    StreamLogScan redoScan,
1125    long redoLWM,
1126    long ttabInstant)
1127         throws IOException JavaDoc, StandardException, ClassNotFoundException JavaDoc
1128    {
1129        // begin debug info
1130
if (SanityManager.DEBUG)
1131        {
1132            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1133            {
1134                SanityManager.DEBUG(
1135                    LogToFile.DBG_FLAG,
1136                    "In recovery redo, redoLWM = " + redoLWM);
1137            }
1138        }
1139
1140        int scanCount = 0;
1141        int redoCount = 0;
1142        int prepareCount = 0;
1143        int clrCount = 0;
1144        int btranCount = 0;
1145        int etranCount = 0;
1146
1147        // end debug info
1148

1149        TransactionId tranId = null;
1150
1151        // the current log instant
1152
long instant = LogCounter.INVALID_LOG_INSTANT;
1153
1154        //////////////////////////////////////////////////////////////////////
1155
// During redo time, the byte array in the logOutputBuffer is not used.
1156
// Use it to read the log record - if it is not big enough, scan
1157
// will resize it. We could create a brand new log input stream that
1158
// has nothing to do with logIn or logOutputBuffer but that seem like
1159
// a waste of memory.
1160
//////////////////////////////////////////////////////////////////////
1161
logIn.setData(logOutputBuffer.getByteArray());
1162
1163        // use this scan to reconstitute operation to be undone
1164
// when we see a CLR in the redo scan
1165
StreamLogScan undoScan = null;
1166        Loggable op = null;
1167        long logEnd = 0; // we need to determine the log's true end
1168

1169        try
1170        {
1171
1172            // scan the log forward in redo pass and go to the end
1173
LogRecord record;
1174            while((record =
1175                    redoScan.getNextRecord(logIn, null, 0))
1176                        != null)
1177            {
1178                scanCount++;
1179                long undoInstant = 0;
1180
1181                // last known good instant
1182
instant = redoScan.getInstant();
1183
1184                // last known good log end
1185
logEnd = redoScan.getLogRecordEnd();
1186
1187
1188                // NOTE NOTE -- be very careful about the undoInstant, it is
1189
// read off the input stream in this debug section.
1190
// if we change the log format we will need to change the way
1191
// the undo instant is gotten. Also, once it is read off, it
1192
// should not be read from the stream any more
1193
// NOTE NOTE
1194
if (SanityManager.DEBUG)
1195                {
1196                    if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY) ||
1197                        SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1198
1199                    {
1200                        if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY))
1201                            SanityManager.DEBUG_SET(LogToFile.DBG_FLAG);
1202
1203                        op = record.getLoggable();
1204                        tranId = record.getTransactionId();
1205                        if (record.isCLR())
1206                        {
1207                            // !!!!!!! this moves the file pointer
1208
undoInstant = logIn.readLong();
1209
1210                            SanityManager.DEBUG(
1211                                LogToFile.DBG_FLAG,
1212                                "scanned " + tranId + " : " + op +
1213                                " instant = " +
1214                                    LogCounter.toDebugString(instant) +
1215                                " undoInstant : " +
1216                                    LogCounter.toDebugString(undoInstant));
1217                        }
1218                        else
1219                        {
1220                            SanityManager.DEBUG(
1221                                LogToFile.DBG_FLAG,
1222                                "scanned " + tranId + " : " + op +
1223                                " instant = " +
1224                                    LogCounter.toDebugString(instant)
1225                                + " logEnd = " +
1226                                    LogCounter.toDebugString(logEnd)
1227                                + " logIn at " + logIn.getPosition()
1228                                + " available " + logIn.available());
1229                        }
1230
1231                        // we only want to dump the log, don't touch it
1232
if (SanityManager.DEBUG_ON(LogToFile.DUMP_LOG_ONLY))
1233                            continue;
1234                    }
1235                }
1236
1237                // if the redo scan is between the undoLWM and redoLWM, we only
1238
// need to redo begin and end tran. Everything else has
1239
// already been flushed by checkpoint
1240
if (redoLWM !=
1241                        LogCounter.INVALID_LOG_INSTANT && instant < redoLWM)
1242                {
1243                    if (!(record.isFirst() ||
1244                          record.isComplete() ||
1245                          record.isPrepare()))
1246                    {
1247                        continue;
1248                    }
1249                }
1250
1251                // get the transaction
1252
tranId = record.getTransactionId();
1253
1254                // if this transaction is known to the transaction factory, make
1255
// the recoveryTransaction assume its identitiy and properties
1256
// otherwise, make it known to the transaction factory
1257
if (!transFactory.findTransaction(tranId, recoveryTransaction))
1258                {
1259                    // transaction not found
1260

1261                    if (redoLWM != LogCounter.INVALID_LOG_INSTANT &&
1262                        instant < redoLWM &&
1263                        (record.isPrepare() || record.isComplete()))
1264                    {
1265                        // What is happening here is that a transaction that
1266
// started before the undoLWM has commited by the time
1267
// the checkpoint undoLWM was taken. Hence, we only
1268
// see the tail end of its log record and its endXact
1269
// record.
1270
//
1271
// NOTE:
1272
// Since we didn't see its beginXact, we cannot do the
1273
// endXact's doMe either. Also if the endXact, is
1274
// actually just a prepare, we don't need to do
1275
// anything as the transaction will commit or abort
1276
// prior to point we are recovering to.
1277
// If it is deemed necessary to do the endXact's doMe,
1278
// then we should start the transaction right here.
1279
// For now, just completely ignore this transaction
1280
//
1281
etranCount++;
1282
1283                        continue;
1284                    }
1285
1286                    if ((ttabInstant == LogCounter.INVALID_LOG_INSTANT) &&
1287                        !record.isFirst())
1288                    {
1289                        throw StandardException.newException(
1290                            SQLState.LOG_UNEXPECTED_RECOVERY_PROBLEM,
1291                            MessageService.getTextMessage(MessageId.LOG_RECORD_NOT_FIRST,tranId));
1292
1293                    }
1294
1295                    if (SanityManager.DEBUG)
1296                    {
1297                        // if we dumped the transaction table but see a non
1298
// BeginXact record after the transaction table dump
1299
// instant, error.
1300
if (ttabInstant != LogCounter.INVALID_LOG_INSTANT)
1301                        {
1302                            if (instant > ttabInstant && !record.isFirst())
1303                            {
1304                                SanityManager.THROWASSERT(
1305                                "log record is Not first but transaction " +
1306                                "is not in transaction table (2) : " + tranId);
1307                            }
1308
1309                            // If we dump the transaction table and the table
1310
// does not have the transaction, and we see this
1311
// beginXact before the ttab instant, we could have
1312
// igored it because we "know" that we should see
1313
// the endXact before the ttab instant also.
1314
// Leave it in just in case.
1315
}
1316                    }
1317                    btranCount++;
1318
1319                    // the long transaction ID is embedded in the beginXact log
1320
// record. The short ID is stored in the log record.
1321
recoveryTransaction.setTransactionId(
1322                        record.getLoggable(), tranId);
1323
1324                }
1325                else
1326                {
1327                    // recoveryTransaction found
1328

1329                    if ((ttabInstant == LogCounter.INVALID_LOG_INSTANT) &&
1330                         record.isFirst())
1331                    {
1332                        throw StandardException.newException(
1333                            SQLState.LOG_UNEXPECTED_RECOVERY_PROBLEM,
1334                            MessageService.getTextMessage(MessageId.LOG_RECORD_FIRST,
1335                                tranId));
1336
1337 
1338                    }
1339
1340                    if (SanityManager.DEBUG)
1341                    {
1342                        if (ttabInstant != LogCounter.INVALID_LOG_INSTANT &&
1343                            instant > ttabInstant &&
1344                            record.isFirst())
1345                        {
1346                            SanityManager.THROWASSERT(
1347                                "log record is first but transaction is " +
1348                                "already in transaction table (3): " + tranId);
1349                        }
1350
1351                        if (record.isPrepare())
1352                            prepareCount++;
1353                    }
1354
1355                    // if we have a transaction table dumped with the
1356
// checkpoint log record, then during the redo scan we may
1357
// see the beginXact of a transaction which is already in
1358
// the transaction table, just ignore it if it is after the
1359
// redoLWM but before the transaction table instant. We
1360
// still need to redo any database changes but since the
1361
// transaction is already recorded in the transaction
1362
// table, ignore it.
1363
//
1364
if (record.isFirst())
1365                    {
1366                        btranCount++;
1367                        continue;
1368                    }
1369                }
1370                
1371                op = record.getLoggable();
1372
1373                if (SanityManager.DEBUG)
1374                {
1375                    if (!record.isCLR())
1376                    {
1377                        if (logIn.available() < 4)
1378                        {
1379                            SanityManager.THROWASSERT(
1380                              "not enough bytes read in : " +
1381                                  logIn.available() +
1382                              " for " + op + " instant " +
1383                                  LogCounter.toDebugString(instant));
1384                        }
1385                    }
1386                }
1387
1388                if (SanityManager.DEBUG)
1389                {
1390                    SanityManager.ASSERT(
1391                        !recoveryTransaction.handlesPostTerminationWork(),
1392                        "recovery transaction handles post termination work");
1393                }
1394
1395                if (op.needsRedo(recoveryTransaction))
1396                {
1397                    redoCount++;
1398
1399                    if (record.isCLR())
1400                    {
1401                        clrCount++;
1402
1403                        // the log operation is not complete, the operation to
1404
// undo is stashed away at the undoInstant.
1405
// Reconstitute that first.
1406

1407                        if (SanityManager.DEBUG)
1408                            SanityManager.ASSERT(op instanceof Compensation);
1409
1410
1411                        // this value may be set by sanity xxxx
1412
if (undoInstant == 0)
1413                            undoInstant = logIn.readLong();
1414
1415                        if (undoScan == null)
1416                        {
1417                            undoScan = (StreamLogScan)
1418                                logFactory.openForwardsScan(
1419                                    undoInstant,(LogInstant)null);
1420                        }
1421                        else
1422                        {
1423                            undoScan.resetPosition(new LogCounter(undoInstant));
1424                        }
1425
1426                        // undoScan now positioned at the beginning of the log
1427
// record was rolled back by this CLR.
1428
// The scan is a forward one so getNextRecord will get
1429
// the log record that needs to be rolled back.
1430

1431                        // reuse the buffer in logIn and logIn since CLR
1432
// has no optional data and has no use for them anymore
1433
logIn.clearLimit();
1434                        LogRecord undoRecord =
1435                            undoScan.getNextRecord(logIn, null, 0);
1436
1437                        Undoable undoOp = undoRecord.getUndoable();
1438
1439                        if (SanityManager.DEBUG)
1440                        {
1441                            SanityManager.DEBUG(
1442                                LogToFile.DBG_FLAG,
1443                                "Redoing CLR: undoInstant = " +
1444                                    LogCounter.toDebugString(undoInstant) +
1445                                " clrinstant = " +
1446                                    LogCounter.toDebugString(instant));
1447
1448                            SanityManager.ASSERT(
1449                                undoRecord.getTransactionId().equals(tranId));
1450
1451                            SanityManager.ASSERT(undoOp != null);
1452                        }
1453
1454                        ((Compensation)op).setUndoOp(undoOp);
1455                    }
1456
1457                    // at this point, logIn points to the optional
1458
// data of the loggable that is to be redone or to be
1459
// rolled back
1460

1461                    if (SanityManager.DEBUG)
1462                    {
1463                        if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1464                        {
1465                            SanityManager.DEBUG(
1466                                LogToFile.DBG_FLAG,
1467                                "redoing " + op +
1468                                " instant = " +
1469                                LogCounter.toDebugString(instant));
1470                        }
1471                    }
1472
1473                    int dataLength = logIn.readInt();
1474                    logIn.setLimit(logIn.getPosition(), dataLength);
1475                                        
1476                    // even though the log has already been written, we need to
1477
// tie the page to the log stream so that if redo failed
1478
// for some reasons, the log factory's corruption will stop
1479
// the corrupt page from flushing to disk.
1480

1481                    op.doMe(
1482                        recoveryTransaction,
1483                        new LogCounter(instant), logIn);
1484
1485                    op.releaseResource(recoveryTransaction);
1486
1487                    op = null;
1488                }
1489
1490                // RESOLVE: to speed up undo, may want to update the
1491
// LastLogInstant in the transaction table.
1492
// Right now, undo always start from the end of the log.
1493

1494                // one last thing, if this is the last log record of the
1495
// transaction, then commit the transaction and clean up
1496
//
1497
// 'commit' even though the transaction maybe a rollback
1498
// because we already did all the rollback work when redoing
1499
// the CLRs. Commit will only flush the log if this session
1500
// has written any transaction, so in this case, it is a noop.
1501
if (record.isComplete())
1502                {
1503                    etranCount++;
1504
1505                    if (SanityManager.DEBUG)
1506                        SanityManager.ASSERT(
1507                            !recoveryTransaction.handlesPostTerminationWork(),
1508                            "recovery xact handles post termination work");
1509
1510                    recoveryTransaction.commit();
1511                }
1512            } // while redoScan.getNextRecord() != null
1513

1514            // If the scan ended in an empty file, update logEnd to reflect that
1515
// in order to avoid to continue logging to an older file
1516
long end = redoScan.getLogRecordEnd();
1517            if (end != LogCounter.INVALID_LOG_INSTANT
1518                && (LogCounter.getLogFileNumber(logEnd)
1519                    < LogCounter.getLogFileNumber(end))) {
1520                logEnd = end;
1521            }
1522        }
1523        catch (StandardException se)
1524        {
1525            throw StandardException.newException(
1526                    SQLState.LOG_REDO_FAILED, se, op);
1527        }
1528        finally
1529        {
1530            // close all the io streams
1531
redoScan.close();
1532            redoScan = null;
1533
1534            if (undoScan != null)
1535            {
1536                undoScan.close();
1537                undoScan = null;
1538            }
1539
1540            if (op != null)
1541                op.releaseResource(recoveryTransaction);
1542
1543        }
1544
1545        if (SanityManager.DEBUG)
1546        {
1547            if (SanityManager.DEBUG_ON(LogToFile.DBG_FLAG))
1548            {
1549                SanityManager.DEBUG(
1550                    LogToFile.DBG_FLAG,
1551                    "----------------------------------------------------\n" +
1552                    "End of recovery redo\n" +
1553                    "Scanned = " + scanCount + " log records" +
1554                    ", redid = " + redoCount +
1555                    " ( clr = " + clrCount + " )" +
1556                    " begintran = " + btranCount +
1557                    " endtran = " + etranCount +
1558                    " preparetran = " + prepareCount +
1559                    "\n log ends at " + LogCounter.toDebugString(logEnd) +
1560                    "\n----------------------------------------------------\n");
1561            }
1562        }
1563
1564        if (SanityManager.DEBUG)
1565        {
1566            // make sure logEnd and instant is consistent
1567
if (instant != LogCounter.INVALID_LOG_INSTANT)
1568            {
1569                SanityManager.ASSERT(
1570                    LogCounter.getLogFileNumber(instant) <
1571                         LogCounter.getLogFileNumber(logEnd) ||
1572                    (LogCounter.getLogFileNumber(instant) ==
1573                         LogCounter.getLogFileNumber(logEnd) &&
1574                     LogCounter.getLogFilePosition(instant) <=
1575                         LogCounter.getLogFilePosition(logEnd)));
1576            }
1577            else
1578            {
1579                SanityManager.ASSERT(logEnd == LogCounter.INVALID_LOG_INSTANT);
1580            }
1581        }
1582
1583        // logEnd is the last good log record position in the log
1584
return logEnd;
1585    }
1586
1587
1588    /**
1589        Read the next log record from the scan.
1590
1591        <P>MT - caller must provide synchronization (right now, it is only
1592        called in recovery to find the checkpoint log record. When this method
1593        is called by a more general audience, MT must be revisited).
1594
1595        @param scan an opened log scan
1596        @param size estimated size of the log record
1597
1598        @return the log operation that is the next in the scan, or null if no
1599        more log operation in the log scan
1600
1601        @exception IOException Error reading the log file
1602        @exception StandardException Standard Cloudscape error policy
1603        @exception ClassNotFoundException log corrupted
1604     */

1605    protected Loggable readLogRecord(StreamLogScan scan, int size)
1606         throws IOException JavaDoc, StandardException, ClassNotFoundException JavaDoc
1607    {
1608        Loggable lop = null;
1609
1610        ArrayInputStream logInputBuffer = new ArrayInputStream(new byte[size]);
1611
1612        LogRecord record = scan.getNextRecord(logInputBuffer, null, 0);
1613        if (record != null)
1614            lop = record.getLoggable();
1615        return lop;
1616    }
1617
1618}
1619
Popular Tags