1 5 package org.h2.store; 6 7 import java.io.ByteArrayInputStream ; 8 import java.io.ByteArrayOutputStream ; 9 import java.io.DataInputStream ; 10 import java.io.DataOutputStream ; 11 import java.io.IOException ; 12 import java.sql.SQLException ; 13 import java.util.Comparator ; 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 35 public class DiskFile implements CacheWriter { 36 37 public static final int BLOCK_SIZE = 128; 38 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 private Database database; 44 private String 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 fileName, boolean dataFile, boolean logChanges, int cacheSize) throws SQLException { 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 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 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 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 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 { 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 e) { 127 throw Message.convert(e); 128 } 129 } 130 131 private void freeUnusedPages() throws SQLException { 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 { 140 try { 141 ByteArrayOutputStream buff = new ByteArrayOutputStream (); 142 DataOutputStream out = new DataOutputStream (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 e) { 180 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 in = new DataInputStream (new ByteArrayInputStream (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 e) { 255 database.getTrace(Trace.DATABASE).error("error initializing summary for " +fileName+" size:" +summary.length + " stage:"+stage, e); 256 } 258 } 259 260 public synchronized void init() throws SQLException { 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 e) { 306 throw Message.convert(e); 307 } 308 } 309 310 synchronized void flush() throws SQLException { 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 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 { 332 SQLException closeException = null; 333 if(!database.getReadOnly()) { 334 try { 335 flush(); 336 } catch (SQLException e) { 337 closeException = e; 338 } 339 } 340 cache.clear(); 341 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 { 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 { 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(); int id = s.readInt(); 373 if(id != storageId) { 374 return null; 375 } 376 } catch (Exception 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 { 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 { 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 { 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 { 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 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 { 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 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 556 public synchronized void delete() throws SQLException { 557 try { 558 cache.clear(); 559 file.close(); 560 FileUtils.delete(fileName); 561 } catch (Exception e) { 562 throw Message.convert(e); 563 } finally { 564 file = null; 565 fileName = null; 566 } 567 } 568 569 582 public synchronized void writeBack(CacheObject obj) throws SQLException { 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 e) { 600 throw Message.convert(e); 601 } 602 } 603 record.setChanged(false); 604 } 605 606 609 BitField getUsed() { 610 return used; 611 } 612 613 synchronized void updateRecord(Session session, Record record) throws SQLException { 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 e) { 636 throw Message.convert(e); 637 } 638 } 639 640 synchronized void writeDirectDeleted(int recordId, int blockCount) throws SQLException { 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 e) { 649 throw Message.convert(e); 650 } 651 } 652 } 653 654 synchronized void writeDirect(Storage storage, int pos, byte[] data, int offset) throws SQLException { 655 synchronized(this) { 656 try { 657 go(pos); 658 file.write(data, offset, BLOCK_SIZE); 659 setBlockOwner(storage, pos, 1, true); 660 } catch (Exception e) { 661 throw Message.convert(e); 662 } 663 } 664 } 665 666 synchronized void removeRecord(Session session, int pos, Record record, int blockCount) throws SQLException { 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 { 676 if(logChanges) { 677 log.add(session, this, record); 678 } 679 cache.put(record); 680 } 681 682 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 { 698 int storageId = storage.getId(); 699 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 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 { 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 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 { 773 if(redoBuffer.size() == 0) { 774 return; 775 } 776 redoBuffer.sort(new Comparator () { 777 public int compare(Object o1, Object 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 { 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 |