| 1 66 67 68 package org.hsqldb; 69 70 import org.hsqldb.lib.HsqlArrayList; 71 import org.hsqldb.lib.HsqlHashMap; 72 import org.hsqldb.lib.HsqlStringBuffer; 73 import java.io.IOException ; 74 import java.io.BufferedWriter ; 75 import java.io.ByteArrayInputStream ; 76 import java.io.ByteArrayOutputStream ; 77 import java.io.File ; 78 import java.io.FileInputStream ; 79 import java.io.FileOutputStream ; 80 import java.io.FileReader ; 81 import java.io.FileWriter ; 82 import java.io.InputStreamReader ; 83 import java.io.LineNumberReader ; 84 import java.io.OutputStreamWriter ; 85 import java.io.Writer ; 86 import java.sql.SQLException ; 87 import java.util.Enumeration ; 88 89 import java.util.zip.Deflater ; 91 import java.util.zip.DeflaterOutputStream ; 92 import java.util.zip.Inflater ; 93 import java.util.zip.InflaterInputStream ; 94 95 108 129 class Log implements Runnable { 130 131 private static final int COPY_BLOCK_SIZE = 1 << 16; 133 private HsqlDatabaseProperties pProperties; 134 private String sName; 135 private Database dDatabase; 136 private Session sysSession; 137 private Writer wScript; 138 private String sFileScript; 139 private String sFileCache; 140 private String sFileBackup; 141 private boolean bRestoring; 142 private boolean bReadOnly; 143 private int iLogSize; 144 private int iLogCount; 145 private Thread tRunner; 146 private volatile boolean bNeedFlush; 147 private volatile boolean bWriteDelay; 148 private int mLastId; 149 private Cache cCache; 150 151 154 162 Log(Database db, Session system, String name) throws SQLException { 163 164 dDatabase = db; 165 sysSession = system; 166 sName = name; 167 pProperties = db.getProperties(); 168 tRunner = new Thread (this); 169 170 187 188 tRunner.start(); 195 } 196 197 200 public void run() { 201 202 while (tRunner != null) { 205 try { 206 tRunner.sleep(1000); 207 208 if (bNeedFlush) { 209 wScript.flush(); 210 211 bNeedFlush = false; 212 } 213 214 } catch (Exception e) { 216 217 } 219 } 220 } 221 222 227 void setWriteDelay(boolean delay) { 228 bWriteDelay = delay; 229 } 230 231 240 boolean open() throws SQLException { 241 242 if (Trace.TRACE) { 243 Trace.trace(); 244 } 245 246 if (!pProperties.checkFileExists()) { 247 create(); 248 open(); 249 250 return true; 252 } 253 254 pProperties.load(); 256 257 sFileScript = sName + ".script"; 258 sFileCache = sName + ".data"; 259 sFileBackup = sName + ".backup"; 260 261 iLogSize = pProperties.getIntegerProperty("hsqldb.log_size", 264 iLogSize); 265 266 String version = pProperties.getProperty("hsqldb.compatible_version"); 267 268 int check = version.substring(0, 5).compareTo(jdbcDriver.VERSION); 270 271 Trace.check(check <= 0, Trace.WRONG_DATABASE_FILE_VERSION); 272 273 pProperties.setProperty("hsqldb.version", jdbcDriver.VERSION); 275 276 if (pProperties.isPropertyTrue("readonly")) { 277 bReadOnly = true; 278 279 dDatabase.setReadOnly(); 280 281 if (cCache != null) { 282 cCache.open(true); 283 } 284 285 reopenAllTextCaches(); 286 runScript(); 287 288 return false; 289 } 290 291 boolean needbackup = false; 292 String state = pProperties.getProperty("modified"); 293 294 if (state.equals("yes-new-files")) { 295 renameNewToCurrent(sFileScript); 296 renameNewToCurrent(sFileBackup); 297 } else if (state.equals("yes")) { 298 if (isAlreadyOpen()) { 299 throw Trace.error(Trace.DATABASE_ALREADY_IN_USE); 300 } 301 302 restoreBackup(); 304 305 needbackup = true; 306 } 307 308 pProperties.setProperty("modified", "yes"); 309 pProperties.save(); 310 311 if (cCache != null) { 312 cCache.open(false); 313 } 314 315 reopenAllTextCaches(); 316 runScript(); 317 318 if (needbackup) { 319 close(false); 320 pProperties.setProperty("modified", "yes"); 321 pProperties.save(); 322 323 if (cCache != null) { 324 cCache.open(false); 325 } 326 327 reopenAllTextCaches(); 328 } 329 330 openScript(); 331 332 return false; 334 } 335 336 Cache getCache() throws SQLException { 337 338 if (cCache == null) { 339 cCache = new Cache(sFileCache, pProperties); 340 341 cCache.open(bReadOnly); 342 } 343 344 return (cCache); 345 } 346 347 350 void stop() { 351 352 360 tRunner = null; 361 } 362 363 369 void close(boolean compact) throws SQLException { 370 371 if (Trace.TRACE) { 372 Trace.trace(); 373 } 374 375 if (bReadOnly) { 376 return; 377 } 378 379 closeScript(); 381 382 writeScript(compact); 384 385 if (cCache != null) { 387 cCache.flush(); 388 } 389 390 closeAllTextCaches(compact); 391 392 backup(); 394 395 pProperties.setProperty("modified", "yes-new-files"); 397 pProperties.save(); 398 399 renameNewToCurrent(sFileScript); 401 renameNewToCurrent(sFileBackup); 402 403 pProperties.setProperty("modified", "no"); 405 pProperties.setProperty("version", jdbcDriver.VERSION); 406 pProperties.setProperty("hsqldb.compatible_version", "1.7.0"); 407 pProperties.save(); 408 pProperties.close(); 409 410 if (compact) { 411 412 stop(); 414 415 (new File (sFileCache)).delete(); 417 (new File (sFileBackup)).delete(); 418 419 } 423 } 424 425 430 void checkpoint() throws SQLException { 431 432 close(false); 433 pProperties.setProperty("modified", "yes"); 434 pProperties.save(); 435 436 if (cCache != null) { 437 cCache.open(false); 438 } 439 440 reopenAllTextCaches(); 441 openScript(); 442 } 443 444 449 void setLogSize(int mb) { 450 451 iLogSize = mb; 452 453 pProperties.setProperty("hsqldb.log_size", iLogSize); 454 } 455 456 463 void write(Session c, String s) throws SQLException { 464 465 if (bRestoring || s == null || s.length() == 0) { 466 return; 467 } 468 469 if (!bReadOnly) { 470 int id = 0; 471 472 if (c != null) { 473 id = c.getId(); 474 } 475 476 if (id != mLastId) { 477 s = "/*C" + id + "*/" + s; 478 mLastId = id; 479 } 480 481 try { 482 writeLine(wScript, s); 483 484 if (bWriteDelay) { 485 bNeedFlush = true; 486 } else { 487 wScript.flush(); 488 } 489 } catch (IOException e) { 490 throw Trace.error(Trace.FILE_IO_ERROR, sFileScript); 491 } 492 493 if (iLogSize > 0 && iLogCount++ > 100) { 495 iLogCount = 0; 496 497 if ((new File (sFileScript)).length() 498 > iLogSize * 1024 * 1024) { 499 checkpoint(); 500 } 501 } 502 } 503 } 504 505 510 void shutdown() throws SQLException { 511 512 tRunner = null; 513 514 if (cCache != null) { 515 cCache.shutdown(); 516 517 cCache = null; 518 } 519 520 shutdownAllTextCaches(); 521 closeScript(); 522 pProperties.close(); 523 } 524 525 534 static void scriptToFile(Database db, String file, boolean full, 535 Session session) throws SQLException { 536 537 if ((new File (file)).exists()) { 538 539 throw Trace.error(Trace.FILE_IO_ERROR, file); 541 } 542 543 try { 544 long time = System.currentTimeMillis(); 545 546 Result r; 548 549 if (full) { 550 551 r = db.getScript(false, false, false, session); 553 } else { 554 555 r = db.getScript(false, false, true, session); 557 } 558 559 Record n = r.rRoot; 560 FileWriter w = new FileWriter (file); 561 562 while (n != null) { 563 writeLine(w, (String ) n.data[0]); 564 565 n = n.next; 566 } 567 568 HsqlArrayList tables = db.getTables(); 570 571 for (int i = 0; i < tables.size(); i++) { 572 Table t = (Table) tables.get(i); 573 574 if ((full ||!t.isCached()) &&!t.isTemp() &&!t.isView() 576 && (!t.isText() ||!t.isDataReadOnly())) { 577 Index primary = t.getPrimaryIndex(); 578 Node x = primary.first(); 579 580 while (x != null) { 581 writeLine(w, t.getInsertStatement(x.getData())); 582 583 x = primary.next(x); 584 } 585 } 586 587 if (t.isDataReadOnly() &&!t.isTemp() &&!t.isText()) { 589 HsqlStringBuffer a = new HsqlStringBuffer("SET TABLE "); 590 591 a.append(t.getName().statementName); 592 a.append(" READONLY TRUE"); 593 writeLine(w, a.toString()); 594 } 595 } 596 597 w.close(); 598 599 time = System.currentTimeMillis() - time; 600 601 if (Trace.TRACE) { 602 Trace.trace(time); 603 } 604 } catch (IOException e) { 605 throw Trace.error(Trace.FILE_IO_ERROR, file + " " + e); 606 } 607 } 608 609 610 611 615 static byte[] scriptToBuffer(Database db, boolean full, 616 Session session) throws SQLException { 617 618 ByteArrayOutputStream bos=null; 619 OutputStreamWriter w=null; 620 621 try { 622 long time = System.currentTimeMillis(); 623 624 Result r; 626 627 if (full) { 628 629 r = db.getScript(false, false, false, session); 631 } else { 632 633 r = db.getScript(false, false, true, session); 635 } 636 637 Record n = r.rRoot; 638 bos=new ByteArrayOutputStream (0xff); 640 w=new OutputStreamWriter (bos); 641 642 while (n != null) { 643 writeLine(w, (String ) n.data[0]); 644 645 n = n.next; 646 } 647 648 HsqlArrayList tables = db.getTables(); 650 651 for (int i = 0; i < tables.size(); i++) { 652 Table t = (Table) tables.get(i); 653 654 if ((full ||!t.isCached()) &&!t.isTemp() &&!t.isView() 656 && (!t.isText() ||!t.isDataReadOnly())) { 657 Index primary = t.getPrimaryIndex(); 658 Node x = primary.first(); 659 660 while (x != null) { 661 writeLine(w, t.getInsertStatement(x.getData())); 662 663 x = primary.next(x); 664 } 665 } 666 667 if (t.isDataReadOnly() &&!t.isTemp() &&!t.isText()) { 669 HsqlStringBuffer a = new HsqlStringBuffer("SET TABLE "); 670 671 a.append(t.getName().statementName); 672 a.append(" READONLY TRUE"); 673 writeLine(w, a.toString()); 674 } 675 } 676 677 w.close(); 679 time = System.currentTimeMillis() - time; 680 681 if (Trace.TRACE) { 682 Trace.trace(time); 683 } 684 685 return bos != null? bos.toByteArray() : null; 686 687 } catch (IOException e) { 688 throw Trace.error(Trace.FILE_IO_ERROR, e.toString()); 689 } 690 } 691 692 693 694 695 696 697 698 699 700 705 private void renameNewToCurrent(String file) { 706 707 if ((new File (file + ".new")).exists()) { 709 710 (new File (file)).delete(); 713 714 new File (file + ".new").renameTo(new File (file)); 716 } 717 } 718 719 private void create() throws SQLException { 720 721 if (Trace.TRACE) { 722 Trace.trace(sName); 723 } 724 725 pProperties.setProperty("version", jdbcDriver.VERSION); 726 pProperties.save(); 727 } 728 729 735 private boolean isAlreadyOpen() throws SQLException { 736 737 if (Trace.TRACE) { 741 Trace.trace(); 742 } 743 744 File f = new File (sName + ".lock"); 745 long l1 = f.lastModified(); 746 747 try { 748 Thread.sleep(3000); 749 } catch (Exception e) {} 750 751 long l2 = f.lastModified(); 752 753 if (l1 != l2) { 754 return true; 755 } 756 757 return pProperties.isFileOpen(); 758 } 759 760 765 private void backup() throws SQLException { 766 767 if (Trace.TRACE) { 768 Trace.trace(); 769 770 } 772 773 if (!(new File (sFileCache)).exists()) { 774 return; 775 } 776 777 try { 778 long time = System.currentTimeMillis(); 779 780 DeflaterOutputStream f = new DeflaterOutputStream ( 782 new FileOutputStream (sFileBackup + ".new"), 783 new Deflater (Deflater.BEST_SPEED), COPY_BLOCK_SIZE); 784 byte b[] = new byte[COPY_BLOCK_SIZE]; 785 FileInputStream in = new FileInputStream (sFileCache); 786 787 while (true) { 788 int l = in.read(b, 0, COPY_BLOCK_SIZE); 789 790 if (l == -1) { 791 break; 792 } 793 794 f.write(b, 0, l); 795 } 796 797 f.close(); 798 in.close(); 799 800 time = System.currentTimeMillis() - time; 801 802 if (Trace.TRACE) { 803 Trace.trace(time); 804 } 805 } catch (Exception e) { 806 throw Trace.error(Trace.FILE_IO_ERROR, sFileBackup); 807 } 808 } 809 810 815 private void restoreBackup() throws SQLException { 816 817 if (Trace.TRACE) { 818 Trace.trace("not closed last time!"); 819 } 820 821 if (!(new File (sFileBackup)).exists()) { 822 823 (new File (sFileCache)).delete(); 826 827 return; 828 } 829 830 try { 831 long time = System.currentTimeMillis(); 832 InflaterInputStream f = 833 new InflaterInputStream (new FileInputStream (sFileBackup), 834 new Inflater ()); 835 FileOutputStream cache = new FileOutputStream (sFileCache); 836 byte b[] = new byte[COPY_BLOCK_SIZE]; 837 838 while (true) { 839 int l = f.read(b, 0, COPY_BLOCK_SIZE); 840 841 if (l == -1) { 842 break; 843 } 844 845 cache.write(b, 0, l); 846 } 847 848 cache.close(); 849 f.close(); 850 851 time = System.currentTimeMillis() - time; 852 853 if (Trace.TRACE) { 854 Trace.trace(time); 855 } 856 } catch (Exception e) { 857 throw Trace.error(Trace.FILE_IO_ERROR, sFileBackup); 858 } 859 } 860 861 866 private void openScript() throws SQLException { 867 868 if (Trace.TRACE) { 869 Trace.trace(); 870 } 871 872 try { 873 874 wScript = new BufferedWriter (new FileWriter (sFileScript, true), 876 4096); 877 } catch (Exception e) { 878 throw Trace.error(Trace.FILE_IO_ERROR, sFileScript); 879 } 880 } 881 882 887 private void closeScript() throws SQLException { 888 889 if (Trace.TRACE) { 890 Trace.trace(); 891 } 892 893 try { 894 if (wScript != null) { 895 wScript.close(); 896 897 wScript = null; 898 } 899 } catch (Exception e) { 900 throw Trace.error(Trace.FILE_IO_ERROR, sFileScript); 901 } 902 } 903 904 909 private void runScript() throws SQLException { 910 911 if (Trace.TRACE) { 912 Trace.trace(); 913 } 914 915 if (!(new File (sFileScript)).exists()) { 916 return; 917 } 918 919 bRestoring = true; 920 921 dDatabase.setReferentialIntegrity(false); 922 923 HsqlArrayList session = new HsqlArrayList(); 924 925 session.add(sysSession); 926 927 Session current = sysSession; 928 929 try { 930 long time = System.currentTimeMillis(); 931 LineNumberReader r = 932 new LineNumberReader (new FileReader (sFileScript)); 933 934 while (true) { 935 String s = readLine(r); 936 937 if (s == null) { 938 break; 939 } 940 941 if (s.startsWith("/*C")) { 942 int id = Integer.parseInt(s.substring(3, s.indexOf('*', 943 4))); 944 945 if (id >= session.size()) { 946 session.setSize(id + 1); 947 } 948 949 current = (Session) session.get(id); 950 951 if (current == null) { 952
|