KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > store > DiskFile


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.store;
6
7 import java.io.ByteArrayInputStream JavaDoc;
8 import java.io.ByteArrayOutputStream JavaDoc;
9 import java.io.DataInputStream JavaDoc;
10 import java.io.DataOutputStream JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.sql.SQLException JavaDoc;
13 import java.util.Comparator JavaDoc;
14
15 import org.h2.api.DatabaseEventListener;
16 import org.h2.engine.Constants;
17 import org.h2.engine.Database;
18 import org.h2.engine.Session;
19 import org.h2.message.Message;
20 import org.h2.message.Trace;
21 import org.h2.util.BitField;
22 import org.h2.util.Cache;
23 import org.h2.util.Cache2Q;
24 import org.h2.util.CacheLRU;
25 import org.h2.util.CacheObject;
26 import org.h2.util.CacheWriter;
27 import org.h2.util.FileUtils;
28 import org.h2.util.IntArray;
29 import org.h2.util.MathUtils;
30 import org.h2.util.ObjectArray;
31
32 /**
33  * @author Thomas
34  */

35 public class DiskFile implements CacheWriter {
36
37     public static final int BLOCK_SIZE = 128;
38     // each page contains blocks from the same storage
39
static final int BLOCK_PAGE_PAGE_SHIFT = 6;
40     public static final int BLOCKS_PER_PAGE = 1 << BLOCK_PAGE_PAGE_SHIFT;
41     public static final int OFFSET = FileStore.HEADER_LENGTH;
42     // TODO storage: header should probably be 4 KB or so (to match block size of operating system)
43
private Database database;
44     private String JavaDoc fileName;
45     private FileStore file;
46     private BitField used;
47     private BitField deleted;
48     private int fileBlockCount;
49     private IntArray pageOwners;
50     private Cache cache;
51     private LogSystem log;
52     private DataPage rowBuff;
53     private DataPage freeBlock;
54     private boolean dataFile;
55     private boolean logChanges;
56     private int recordOverhead;
57     private boolean init, initAlreadyTried;
58     private ObjectArray redoBuffer;
59     private int redoBufferSize;
60     private int readCount, writeCount;
61
62     public DiskFile(Database database, String JavaDoc fileName, boolean dataFile, boolean logChanges, int cacheSize) throws SQLException JavaDoc {
63         reset();
64         this.database = database;
65         this.log = database.getLog();
66         this.fileName = fileName;
67         this.dataFile = dataFile;
68         this.logChanges = logChanges;
69         String JavaDoc cacheType = database.getCacheType();
70         if(Cache2Q.TYPE_NAME.equals(cacheType)) {
71             this.cache = new Cache2Q(this, cacheSize);
72         } else {
73             this.cache = new CacheLRU(this, cacheSize);
74         }
75         rowBuff = DataPage.create(database, BLOCK_SIZE);
76         // TODO: the overhead is larger in the log file, so this value is too high :-(
77
recordOverhead = 4 * rowBuff.getIntLen() + 1 + rowBuff.getFillerLength();
78         freeBlock = DataPage.create(database, BLOCK_SIZE);
79         freeBlock.fill(BLOCK_SIZE);
80         freeBlock.updateChecksum();
81         try {
82             if(FileUtils.exists(fileName)) {
83                 file = database.openFile(fileName, true);
84                 long length = file.length();
85                 database.notifyFileSize(length);
86                 int blocks = (int)((length - OFFSET) / BLOCK_SIZE);
87                 setBlockCount(blocks);
88             } else {
89                 create();
90             }
91         } catch(SQLException JavaDoc e) {
92             close();
93             throw e;
94         }
95     }
96     
97     private void reset() {
98         used = new BitField();
99         deleted = new BitField();
100         pageOwners = new IntArray();
101         // init pageOwners
102
setBlockCount(fileBlockCount);
103         redoBuffer = new ObjectArray();
104     }
105
106     private void setBlockCount(int count) {
107         fileBlockCount = count;
108         int pages = getPage(count);
109         while(pages >= pageOwners.size()) {
110             pageOwners.add(-1);
111         }
112     }
113
114     int getBlockCount() {
115         return fileBlockCount;
116     }
117
118     private void create() throws SQLException JavaDoc {
119         try {
120             file = database.openFile(fileName, false);
121             DataPage header = DataPage.create(database, OFFSET);
122             file.seek(FileStore.HEADER_LENGTH);
123             header.fill(OFFSET);
124             header.updateChecksum();
125             file.write(header.getBytes(), 0, OFFSET);
126         } catch (Exception JavaDoc e) {
127             throw Message.convert(e);
128         }
129     }
130
131     private void freeUnusedPages() throws SQLException JavaDoc {
132         for(int i=0; i<pageOwners.size(); i++) {
133             if(pageOwners.get(i) != -1 && isPageFree(i)) {
134                 setPageOwner(i, -1);
135             }
136         }
137     }
138
139     public synchronized byte[] getSummary() throws SQLException JavaDoc {
140         try {
141             ByteArrayOutputStream JavaDoc buff = new ByteArrayOutputStream JavaDoc();
142             DataOutputStream JavaDoc out = new DataOutputStream JavaDoc(buff);
143             int blocks = (int)((file.length() - OFFSET) / BLOCK_SIZE);
144             out.writeInt(blocks);
145             for(int i=0, x = 0; i<blocks / 8; i++) {
146                 int mask = 0;
147                 for(int j=0; j<8; j++) {
148                     if(used.get(x)) {
149                         mask |= 1 << j ;
150                     }
151                     x++;
152                 }
153                 out.write(mask);
154             }
155             out.writeInt(pageOwners.size());
156             ObjectArray storages = new ObjectArray();
157             for(int i=0; i<pageOwners.size(); i++) {
158                 int s = pageOwners.get(i);
159                 out.writeInt(s);
160                 if(s >= 0 && (s >= storages.size() || storages.get(s) == null)) {
161                     Storage storage = database.getStorage(s, this);
162                     while(storages.size() <= s) {
163                         storages.add(null);
164                     }
165                     storages.set(s, storage);
166                 }
167             }
168             for(int i=0; i<storages.size(); i++) {
169                 Storage storage = (Storage) storages.get(i);
170                 if(storage != null) {
171                     out.writeInt(i);
172                     out.writeInt(storage.getRecordCount());
173                 }
174             }
175             out.writeInt(-1);
176             out.close();
177             byte[] b2 = buff.toByteArray();
178             return b2;
179         } catch(IOException JavaDoc e) {
180             // will probably never happend, because only in-memory strutures are used
181
return null;
182         }
183     }
184
185     private boolean isPageFree(int page) {
186         for(int i=page * BLOCKS_PER_PAGE; i < (page+1) * BLOCKS_PER_PAGE; i++) {
187             if(used.get(i)) {
188                 return false;
189             }
190         }
191         return true;
192     }
193
194     public synchronized void initFromSummary(byte[] summary) {
195         if(summary == null || summary.length==0) {
196             ObjectArray list = database.getAllStorages();
197             for(int i=0; i<list.size(); i++) {
198                 Storage s = (Storage)list.get(i);
199                 if(s != null && s.getDiskFile() == this) {
200                     database.removeStorage(s.getId(), this);
201                 }
202             }
203             reset();
204             initAlreadyTried = false;
205             init = false;
206             return;
207         }
208         if(database.getRecovery() || initAlreadyTried) {
209             return;
210         }
211         initAlreadyTried = true;
212         int stage = 0;
213         try {
214             DataInputStream JavaDoc in = new DataInputStream JavaDoc(new ByteArrayInputStream JavaDoc(summary));
215             int b2 = in.readInt();
216             stage++;
217             for(int i=0, x = 0; i<b2 / 8; i++) {
218                 int mask = in.read();
219                 for(int j=0; j<8; j++) {
220                     if((mask & (1 << j)) != 0) {
221                         used.set(x);
222                     }
223                     x++;
224                 }
225             }
226             stage++;
227             int len = in.readInt();
228             ObjectArray storages = new ObjectArray();
229             for(int i=0; i<len; i++) {
230                 int s = in.readInt();
231                 if(s>=0) {
232                     Storage storage = database.getStorage(s, this);
233                     while(storages.size() <= s) {
234                         storages.add(null);
235                     }
236                     storages.set(s, storage);
237                     storage.addPage(i);
238                 }
239                 setPageOwner(i, s);
240             }
241             stage++;
242             while(true) {
243                 int s = in.readInt();
244                 if(s < 0) {
245                     break;
246                 }
247                 int recordCount = in.readInt();
248                 Storage storage = (Storage) storages.get(s);
249                 storage.setRecordCount(recordCount);
250             }
251             stage++;
252             freeUnusedPages();
253             init = true;
254         } catch (Exception JavaDoc e) {
255             database.getTrace(Trace.DATABASE).error("error initializing summary for " +fileName+" size:" +summary.length + " stage:"+stage, e);
256             // ignore - init is still false in this case
257
}
258     }
259
260     public synchronized void init() throws SQLException JavaDoc {
261         if(init) {
262             return;
263         }
264         ObjectArray storages = database.getAllStorages();
265         for(int i=0; i<storages.size(); i++) {
266             Storage s = (Storage) storages.get(i);
267             if(s != null && s.getDiskFile() == this) {
268                 s.setRecordCount(0);
269             }
270         }
271         try {
272             int blockHeaderLen = Math.max(Constants.FILE_BLOCK_SIZE, 2 * rowBuff.getIntLen());
273             byte[] buff = new byte[blockHeaderLen];
274             DataPage s = DataPage.create(database, buff);
275             long time = 0;
276             for (int i = 0; i < fileBlockCount;) {
277                 long t2 = System.currentTimeMillis();
278                 if(t2 > time + 10) {
279                     time = t2;
280                     database.setProgress(DatabaseEventListener.STATE_SCAN_FILE, this.fileName, i, fileBlockCount);
281                 }
282                 go(i);
283                 file.readFully(buff, 0, blockHeaderLen);
284                 s.reset();
285                 int blockCount = s.readInt();
286                 if(Constants.CHECK && blockCount < 0) {
287                     throw Message.getInternalError();
288                 }
289                 if(blockCount == 0) {
290                     setUnused(i, 1);
291                     i++;
292                 } else {
293                     int id = s.readInt();
294                     if(Constants.CHECK && id < 0) {
295                         throw Message.getInternalError();
296                     }
297                     Storage storage = database.getStorage(id, this);
298                     setBlockOwner(storage, i, blockCount, true);
299                     storage.incrementRecordCount();
300                     i += blockCount;
301                 }
302             }
303             database.setProgress(DatabaseEventListener.STATE_SCAN_FILE, this.fileName, fileBlockCount, fileBlockCount);
304             init = true;
305         } catch (Exception JavaDoc e) {
306             throw Message.convert(e);
307         }
308     }
309
310     synchronized void flush() throws SQLException JavaDoc {
311         database.checkPowerOff();
312         ObjectArray list = cache.getAllChanged();
313         CacheObject.sort(list);
314         for(int i=0; i<list.size(); i++) {
315             Record rec = (Record) list.get(i);
316             writeBack(rec);
317         }
318         // TODO flush performance: maybe it would be faster to write records in the same loop
319
for(int i=0; i<fileBlockCount; i++) {
320             i = deleted.nextSetBit(i);
321             if(i < 0) {
322                 break;
323             }
324             if(deleted.get(i)) {
325                 writeDirectDeleted(i, 1);
326                 deleted.clear(i);
327             }
328         }
329     }
330
331     public synchronized void close() throws SQLException JavaDoc {
332         SQLException JavaDoc closeException = null;
333         if(!database.getReadOnly()) {
334             try {
335                 flush();
336             } catch (SQLException JavaDoc e) {
337                 closeException = e;
338             }
339         }
340         cache.clear();
341         // continue with close even if flush was not possible (file storage problem)
342
if(file != null) {
343             file.closeSilently();
344             file = null;
345         }
346         if(closeException != null) {
347             throw closeException;
348         }
349         readCount = writeCount = 0;
350     }
351
352     private void go(int block) throws SQLException JavaDoc {
353         file.seek(getFilePos(block));
354     }
355     
356     private long getFilePos(int block) {
357         return ((long)block * BLOCK_SIZE) + OFFSET;
358     }
359
360     synchronized Record getRecordIfStored(int pos, RecordReader reader, int storageId) throws SQLException JavaDoc {
361         try {
362             int owner = getPageOwner(getPage(pos));
363             if(owner != storageId) {
364                 return null;
365             }
366             go(pos);
367             rowBuff.reset();
368             byte[] buff = rowBuff.getBytes();
369             file.readFully(buff, 0, BLOCK_SIZE);
370             DataPage s = DataPage.create(database, buff);
371             s.readInt(); // blockCount
372
int id = s.readInt();
373             if(id != storageId) {
374                 return null;
375             }
376         } catch (Exception JavaDoc e) {
377             return null;
378         }
379         return getRecord(pos, reader, storageId);
380     }
381     
382     synchronized Record getRecord(int pos, RecordReader reader, int storageId) throws SQLException JavaDoc {
383         if(file == null) {
384             throw Message.getSQLException(Message.SIMULATED_POWER_OFF);
385         }
386         synchronized(this) {
387             Record record = (Record) cache.get(pos);
388             if(record != null) {
389                 return record;
390             }
391             readCount++;
392             go(pos);
393             rowBuff.reset();
394             byte[] buff = rowBuff.getBytes();
395             file.readFully(buff, 0, BLOCK_SIZE);
396             DataPage s = DataPage.create(database, buff);
397             int blockCount = s.readInt();
398             int id = s.readInt();
399             if(Constants.CHECK && storageId != id) {
400                 throw Message.getInternalError("File ID mismatch got="+id+" expected="+storageId+" pos="+pos+" "+logChanges+" "+this +" blockCount:"+blockCount);
401             }
402             if(Constants.CHECK && blockCount == 0) {
403                 throw Message.getInternalError("0 blocks to read pos="+pos);
404             }
405             if(blockCount > 1) {
406                 byte[] b2 = new byte[blockCount * BLOCK_SIZE];
407                 System.arraycopy(buff, 0, b2, 0, BLOCK_SIZE);
408                 buff = b2;
409                 file.readFully(buff, BLOCK_SIZE, blockCount * BLOCK_SIZE - BLOCK_SIZE);
410                 s = DataPage.create(database, buff);
411                 s.readInt();
412                 s.readInt();
413             }
414             s.check(blockCount*BLOCK_SIZE);
415             Record r = reader.read(s);
416             r.setStorageId(storageId);
417             r.setPos(pos);
418             r.setBlockCount(blockCount);
419             cache.put(r);
420             return r;
421         }
422     }
423
424     synchronized int allocate(Storage storage, int blockCount) throws SQLException JavaDoc {
425         if(file == null) {
426             throw Message.getSQLException(Message.SIMULATED_POWER_OFF);
427         }
428         blockCount = getPage(blockCount+BLOCKS_PER_PAGE-1)*BLOCKS_PER_PAGE;
429         int lastPage = getPage(getBlockCount());
430         int pageCount = getPage(blockCount);
431         int pos = -1;
432         boolean found = false;
433         for (int i = 0; i < lastPage; i++) {
434             found = true;
435             for(int j = i; j < i + pageCount; j++) {
436                 if(j >= lastPage || getPageOwner(j) != -1) {
437                     found = false;
438                     break;
439                 }
440             }
441             if(found) {
442                 pos = i * BLOCKS_PER_PAGE;
443                 break;
444             }
445         }
446         if(!found) {
447             int max = getBlockCount();
448             pos = MathUtils.roundUp(max, BLOCKS_PER_PAGE);
449             if(rowBuff instanceof DataPageText) {
450                 if(pos > max) {
451                     writeDirectDeleted(max, pos-max);
452                 }
453                 writeDirectDeleted(pos, blockCount);
454             } else {
455                 long min = ((long)pos + blockCount) * BLOCK_SIZE ;
456                 min = MathUtils.scaleUp50Percent(128 * 1024, min, 8 * 1024) + OFFSET;
457                 if(min > file.length()) {
458                     file.setLength(min);
459                     database.notifyFileSize(min);
460                 }
461             }
462         }
463         setBlockOwner(storage, pos, blockCount, false);
464         for(int i=0; i<blockCount; i++) {
465             storage.free(i + pos, 1);
466         }
467         return pos;
468     }
469
470     private void setBlockOwner(Storage storage, int pos, int blockCount, boolean inUse) throws SQLException JavaDoc {
471         if (pos + blockCount > fileBlockCount) {
472             setBlockCount(pos + blockCount);
473         }
474         if(!inUse) {
475             setUnused(pos, blockCount);
476         }
477         for(int i = getPage(pos); i <= getPage(pos+blockCount-1); i++) {
478             setPageOwner(i, storage.getId());
479         }
480         if(inUse) {
481             used.setRange(pos, blockCount, true);
482             deleted.setRange(pos, blockCount, false);
483         }
484     }
485
486     private void setUnused(int pos, int blockCount) throws SQLException JavaDoc {
487         if (pos + blockCount > fileBlockCount) {
488             setBlockCount(pos + blockCount);
489         }
490         for (int i = pos; i < pos + blockCount; i++) {
491             used.clear(i);
492             if((i % BLOCKS_PER_PAGE == 0) && (pos + blockCount >= i + BLOCKS_PER_PAGE)) {
493                 // if this is the first page of a block and if the whole page is free
494
setPageOwner(getPage(i), -1);
495             }
496         }
497     }
498
499     int getPage(int pos) {
500         return pos >>> BLOCK_PAGE_PAGE_SHIFT;
501     }
502
503     int getPageOwner(int page) {
504         if(page * BLOCKS_PER_PAGE > fileBlockCount || page >= pageOwners.size()) {
505             return -1;
506         }
507         return pageOwners.get(page);
508     }
509
510     void setPageOwner(int page, int storageId) throws SQLException JavaDoc {
511         int old = pageOwners.get(page);
512         if(old == storageId) {
513             return;
514         }
515         if(Constants.CHECK && old >= 0 && storageId >= 0 && old != storageId) {
516             for(int i=0; i<BLOCKS_PER_PAGE; i++) {
517                 if(used.get(i + page*BLOCKS_PER_PAGE)) {
518                     throw Message.getInternalError("double allocation");
519                 }
520             }
521         }
522         if(old >= 0) {
523             database.getStorage(old, this).removePage(page);
524             if(!logChanges) {
525                 // need to clean the page, otherwise it may never get cleaned and can become corrupted
526
writeDirectDeleted(page * BLOCKS_PER_PAGE, BLOCKS_PER_PAGE);
527             }
528         }
529         if(storageId >= 0) {
530             database.getStorage(storageId, this).addPage(page);
531         }
532         pageOwners.set(page, storageId);
533     }
534
535     synchronized void setUsed(int pos, int blockCount) {
536         if (pos + blockCount > fileBlockCount) {
537             setBlockCount(pos + blockCount);
538         }
539         used.setRange(pos, blockCount, true);
540         deleted.setRange(pos, blockCount, false);
541     }
542
543 // public void finalize() {
544
// if (!Database.RUN_FINALIZERS) {
545
// return;
546
// }
547
// if (file != null) {
548
// try {
549
// file.close();
550
// } catch (Exception e) {
551
// // ignore
552
// }
553
// }
554
// }
555

556     public synchronized void delete() throws SQLException JavaDoc {
557         try {
558             cache.clear();
559             file.close();
560             FileUtils.delete(fileName);
561         } catch (Exception JavaDoc e) {
562             throw Message.convert(e);
563         } finally {
564             file = null;
565             fileName = null;
566         }
567     }
568
569     // private int allocateBest(int start, int blocks) {
570
// while (true) {
571
// int p = getLastUsedPlusOne(start, blocks);
572
// if (p == start) {
573
// start = p;
574
// break;
575
// }
576
// start = p;
577
// }
578
// allocate(start, blocks);
579
// return start;
580
// }
581

582     public synchronized void writeBack(CacheObject obj) throws SQLException JavaDoc {
583         writeCount++;
584         Record record = (Record) obj;
585         synchronized(this) {
586             try {
587                 int blockCount = record.getBlockCount();
588                 record.prepareWrite();
589                 go(record.getPos());
590                 DataPage buff = rowBuff;
591                 buff.reset();
592                 buff.checkCapacity(blockCount * BLOCK_SIZE);
593                 buff.writeInt(blockCount);
594                 buff.writeInt(record.getStorageId());
595                 record.write(buff);
596                 buff.fill(blockCount * BLOCK_SIZE);
597                 buff.updateChecksum();
598                 file.write(buff.getBytes(), 0, buff.length());
599             } catch (Exception JavaDoc e) {
600                 throw Message.convert(e);
601             }
602         }
603         record.setChanged(false);
604     }
605
606     /*
607      * Must be synchronized externally
608      */

609     BitField getUsed() {
610         return used;
611     }
612
613     synchronized void updateRecord(Session session, Record record) throws SQLException JavaDoc {
614         try {
615             record.setChanged(true);
616             int pos = record.getPos();
617             Record old = (Record) cache.update(pos, record);
618             if(Constants.CHECK) {
619                 if(old != null) {
620                     if(old!=record) {
621                         database.checkPowerOff();
622                         throw Message.getInternalError("old != record old="+old+" new="+record);
623                     }
624                     int blockCount = record.getBlockCount();
625                     for(int i=0; i<blockCount; i++) {
626                         if(deleted.get(i + pos)) {
627                             throw Message.getInternalError("update marked as deleted: " + (i+pos));
628                         }
629                     }
630                 }
631             }
632             if(logChanges) {
633                 log.add(session, this, record);
634             }
635         } catch (Exception JavaDoc e) {
636             throw Message.convert(e);
637         }
638     }
639
640     synchronized void writeDirectDeleted(int recordId, int blockCount) throws SQLException JavaDoc {
641         synchronized(this) {
642             try {
643                 go(recordId);
644                 for(int i=0; i<blockCount; i++) {
645                     file.write(freeBlock.getBytes(), 0, freeBlock.length());
646                 }
647                 free(recordId, blockCount);
648             } catch (Exception JavaDoc e) {
649                 throw Message.convert(e);
650             }
651         }
652     }
653
654     synchronized void writeDirect(Storage storage, int pos, byte[] data, int offset) throws SQLException JavaDoc {
655         synchronized(this) {
656             try {
657                 go(pos);
658                 file.write(data, offset, BLOCK_SIZE);
659                 setBlockOwner(storage, pos, 1, true);
660             } catch (Exception JavaDoc e) {
661                 throw Message.convert(e);
662             }
663         }
664     }
665
666     synchronized void removeRecord(Session session, int pos, Record record, int blockCount) throws SQLException JavaDoc {
667         if(logChanges) {
668             log.add(session, this, record);
669         }
670         cache.remove(pos);
671         deleted.setRange(pos, blockCount, true);
672         setUnused(pos, blockCount);
673     }
674
675     synchronized void addRecord(Session session, Record record) throws SQLException JavaDoc {
676         if(logChanges) {
677             log.add(session, this, record);
678         }
679         cache.put(record);
680     }
681
682     /*
683      * Must be synchronized externally
684      */

685     public Cache getCache() {
686         return cache;
687     }
688
689     synchronized void free(int pos, int blockCount) {
690         used.setRange(pos, blockCount, false);
691     }
692
693     public int getRecordOverhead() {
694         return recordOverhead;
695     }
696
697     public synchronized void truncateStorage(Session session, Storage storage, IntArray pages) throws SQLException JavaDoc {
698         int storageId = storage.getId();
699         // make sure the cache records of this storage are not flushed to disk afterwards
700
ObjectArray list = cache.getAllChanged();
701         for(int i=0; i<list.size(); i++) {
702             Record r = (Record) list.get(i);
703             if(r.getStorageId() == storageId) {
704                 r.setChanged(false);
705             }
706         }
707         int[] pagesCopy = new int[pages.size()];
708         // can not use pages directly, because setUnused removes rows from there
709
pages.toArray(pagesCopy);
710         for(int i=0; i<pagesCopy.length; i++) {
711             int page = pagesCopy[i];
712             if(logChanges) {
713                 log.addTruncate(session, this, storageId, page * BLOCKS_PER_PAGE, BLOCKS_PER_PAGE);
714             }
715             for(int j=0; j<BLOCKS_PER_PAGE; j++) {
716                 Record r = (Record) cache.find(page * BLOCKS_PER_PAGE + j);
717                 if(r != null) {
718                     cache.remove(r.getPos());
719                 }
720             }
721             deleted.setRange(page * BLOCKS_PER_PAGE, BLOCKS_PER_PAGE, true);
722             setUnused(page * BLOCKS_PER_PAGE, BLOCKS_PER_PAGE);
723         }
724     }
725
726     public synchronized void sync() {
727         if(file != null) {
728             file.sync();
729         }
730     }
731
732     public boolean isDataFile() {
733         return dataFile;
734     }
735
736     public synchronized void setLogChanges(boolean b) {
737         this.logChanges = b;
738     }
739
740     synchronized void addRedoLog(Storage storage, int recordId, int blockCount, DataPage rec) throws SQLException JavaDoc {
741         byte[] data = null;
742         if(rec != null) {
743             DataPage all = rowBuff;
744             all.reset();
745             all.writeInt(blockCount);
746             all.writeInt(storage.getId());
747             all.writeDataPageNoSize(rec);
748             // the buffer may have some additional fillers - just ignore them
749
all.fill(blockCount * BLOCK_SIZE);
750             all.updateChecksum();
751             if(Constants.CHECK && all.length() != BLOCK_SIZE * blockCount) {
752                 throw Message.getInternalError("blockCount:" + blockCount + " length: " + all.length()*BLOCK_SIZE);
753             }
754             data = new byte[all.length()];
755             System.arraycopy(all.getBytes(), 0, data, 0, all.length());
756         }
757         for(int i=0; i<blockCount; i++) {
758             RedoLogRecord log = new RedoLogRecord();
759             log.recordId = recordId + i;
760             log.offset = i * BLOCK_SIZE;
761             log.storage = storage;
762             log.data= data;
763             log.sequenceId = redoBuffer.size();
764             redoBuffer.add(log);
765             redoBufferSize += log.getSize();
766         }
767         if (redoBufferSize > Constants.REDO_BUFFER_SIZE) {
768             flushRedoLog();
769         }
770     }
771
772     synchronized void flushRedoLog() throws SQLException JavaDoc {
773         if(redoBuffer.size() == 0) {
774             return;
775         }
776         redoBuffer.sort(new Comparator JavaDoc() {
777             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
778                 RedoLogRecord e1 = (RedoLogRecord) o1;
779                 RedoLogRecord e2 = (RedoLogRecord) o2;
780                 int comp = e1.recordId - e2.recordId;
781                 if(comp == 0) {
782                     comp = e1.sequenceId - e2.sequenceId;
783                 }
784                 return comp;
785             }
786         });
787         RedoLogRecord last = null;
788         for(int i=0; i<redoBuffer.size(); i++) {
789             RedoLogRecord entry = (RedoLogRecord) redoBuffer.get(i);
790             if(last != null && entry.recordId != last.recordId) {
791                 writeRedoLog(last);
792             }
793             last = entry;
794         }
795         if(last != null) {
796             writeRedoLog(last);
797         }
798         redoBuffer.clear();
799         redoBufferSize = 0;
800     }
801
802     private void writeRedoLog(RedoLogRecord entry) throws SQLException JavaDoc {
803         if(entry.data == null) {
804             writeDirectDeleted(entry.recordId, 1);
805         } else {
806             writeDirect(entry.storage, entry.recordId, entry.data, entry.offset);
807         }
808     }
809     
810     public int getWriteCount() {
811         return writeCount;
812     }
813     
814     public int getReadCount() {
815         return readCount;
816     }
817
818 }
819
Popular Tags