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 current = new Session(sysSession, id); 953 954 session.set(id, current); 955 dDatabase.registerSession(current); 956 } 957 958 s = s.substring(s.indexOf('/', 1) + 1); 959 } 960 961 if (s.length() != 0) { 962 Result result = dDatabase.execute(s, current); 963 964 if ((result != null) && (result.iMode == Result.ERROR)) { 965 throw (Trace.getError(result.errorCode, 966 result.sError)); 967 } 968 } 969 970 if (s.equals("DISCONNECT")) { 971 int id = current.getId(); 972 973 current = new Session(sysSession, id); 974 975 session.set(id, current); 976 } 977 } 978 979 r.close(); 980 981 for (int i = 0; i < session.size(); i++) { 982 current = (Session) session.get(i); 983 984 if (current != null) { 985 current.rollback(); 986 } 987 } 988 989 time = System.currentTimeMillis() - time; 990 991 if (Trace.TRACE) { 992 Trace.trace(time); 993 } 994 } catch (IOException e) { 995 throw Trace.error(Trace.FILE_IO_ERROR, sFileScript + " " + e); 996 } 997 998 dDatabase.setReferentialIntegrity(true); 999 1000 bRestoring = false; 1001 } 1002 1003 1004 1005 void initializeDatabaseFromBuffer(byte[] buf) throws SQLException { 1006 ByteArrayInputStream bis; 1007 InputStreamReader in; 1008 1009 if (Trace.TRACE) { 1010 Trace.trace(); 1011 } 1012 1013 if(buf == null) 1014 throw Trace.error(Trace.FILE_IO_ERROR, "Log.initializeDatabaseFromBuffer(): buffer is null"); 1015 1016 bRestoring = true; 1017 1018 dDatabase.setReferentialIntegrity(false); 1019 1020 HsqlArrayList session = new HsqlArrayList(); 1021 1022 session.add(sysSession); 1023 1024 Session current = sysSession; 1025 int size = 1; 1026 1027 try { 1028 long time = System.currentTimeMillis(); 1029 LineNumberReader r = null; 1030 1031 bis=new ByteArrayInputStream (buf); 1032 in=new InputStreamReader (bis); 1033 r=new LineNumberReader (in); 1034 1035 while (true) { 1036 String s = readLine(r); 1037 1038 if (s == null) { 1039 break; 1040 } 1041 1042 if (s.startsWith("/*C")) { 1043 int id = Integer.parseInt(s.substring(3, s.indexOf('*', 1044 4))); 1045 1046 if (id >= size) { 1047 session.setSize(id + 1); 1048 } 1049 1050 current = (Session) session.get(id); 1051 1052 if (current == null) { 1053 current = new Session(sysSession, id); 1054 1055 session.set(id, current); 1056 dDatabase.registerSession(current); 1057 } 1058 1059 s = s.substring(s.indexOf('/', 1) + 1); 1060 } 1061 1062 if (s.length() != 0) { 1063 Result result = dDatabase.execute(s, current); 1064 1065 if ((result != null) && (result.iMode == Result.ERROR)) { 1066 throw (Trace.getError(result.errorCode, 1067 result.sError)); 1068 } 1069 } 1070 1071 if (s.equals("DISCONNECT")) { 1072 int id = current.getId(); 1073 1074 current = new Session(sysSession, id); 1075 1076 session.set(id, current); 1077 } 1078 } 1079 1080 r.close(); 1081 1082 for (int i = 0; i < size; i++) { 1083 current = (Session) session.get(i); 1084 1085 if (current != null) { 1086 current.rollback(); 1087 } 1088 } 1089 1090 time = System.currentTimeMillis() - time; 1091 1092 if (Trace.TRACE) { 1093 Trace.trace(time); 1094 } 1095 } catch (IOException e) { 1096 throw Trace.error(Trace.FILE_IO_ERROR, sFileScript + " " + e); 1097 } 1098 1099 dDatabase.setReferentialIntegrity(true); 1100 1101 bRestoring = false; 1102 } 1103 1104 1105 1106 1107 1108 1114 private void writeScript(boolean full) throws SQLException { 1115 1116 if (Trace.TRACE) { 1117 Trace.trace(); 1118 1119 } 1121 1122 (new File (sFileScript + ".new")).delete(); 1123 1124 scriptToFile(dDatabase, sFileScript + ".new", full, sysSession); 1126 } 1127 1128 1135 1136 private static final String lineSep = System.getProperty("line.separator", 1138 "\n"); 1139 1140 private static void writeLine(Writer w, String s) throws IOException { 1141 w.write(StringConverter.unicodeToAscii(s).append(lineSep).toString()); 1142 } 1143 1144 1151 private static String readLine(LineNumberReader r) throws IOException { 1152 1153 String s = r.readLine(); 1154 1155 return StringConverter.asciiToUnicode(s); 1156 } 1157 1158 1163 HsqlDatabaseProperties getProperties() { 1164 return pProperties; 1165 } 1166 1167 private HsqlHashMap textCacheList = new HsqlHashMap(); 1169 1170 Cache openTextCache(String table, String source, boolean readOnlyData, 1171 boolean reversed) throws SQLException { 1172 1173 closeTextCache(table); 1174 1175 if (pProperties.getProperty("textdb.allow_full_path", 1176 "false").equals("false")) { 1177 if (source.indexOf("..") != -1) { 1178 throw (Trace.error(Trace.ACCESS_IS_DENIED, source)); 1179 } 1180 1181 String path = 1182 new File (new File (sName).getAbsolutePath()).getParent(); 1183 1184 if (path != null) { 1185 source = path + File.separator + source; 1186 } 1187 } 1188 1189 String prefix = "textdb." + table.toLowerCase() + "."; 1190 TextCache c; 1191 1192 if (reversed) { 1193 c = new ReverseTextCache(source, prefix, pProperties); 1194 } else { 1195 c = new TextCache(source, prefix, pProperties); 1196 } 1197 1198 c.open(readOnlyData || bReadOnly); 1199 textCacheList.put(table, c); 1200 1201 return (c); 1202 } 1203 1204 void closeTextCache(String table) throws SQLException { 1205 1206 TextCache c = (TextCache) textCacheList.remove(table); 1207 1208 if (c != null) { 1209 c.flush(); 1210 } 1211 } 1212 1213 void closeAllTextCaches(boolean compact) throws SQLException { 1214 1215 for (Enumeration e = 1216 textCacheList.elements(); e.hasMoreElements(); ) { 1217 if (compact) { 1218 ((TextCache) e.nextElement()).purge(); 1219 } else { 1220 ((TextCache) e.nextElement()).flush(); 1221 } 1222 } 1223 } 1224 1225 void reopenAllTextCaches() throws SQLException { 1226 1227 for (Enumeration e = 1228 textCacheList.elements(); e.hasMoreElements(); ) { 1229 ((TextCache) e.nextElement()).reopen(); 1230 } 1231 } 1232 1233 void shutdownAllTextCaches() throws SQLException { 1234 1235 for (Enumeration e = 1236 textCacheList.elements(); e.hasMoreElements(); ) { 1237 ((TextCache) e.nextElement()).shutdown(); 1238 } 1239 1240 textCacheList = new HsqlHashMap(); 1241 } 1242} 1243 | Popular Tags |