0001: /* Copyrights and Licenses
0002: *
0003: * This product includes Hypersonic SQL.
0004: * Originally developed by Thomas Mueller and the Hypersonic SQL Group.
0005: *
0006: * Copyright (c) 1995-2000 by the Hypersonic SQL Group. All rights reserved.
0007: * Redistribution and use in source and binary forms, with or without modification, are permitted
0008: * provided that the following conditions are met:
0009: * - Redistributions of source code must retain the above copyright notice, this list of conditions
0010: * and the following disclaimer.
0011: * - Redistributions in binary form must reproduce the above copyright notice, this list of
0012: * conditions and the following disclaimer in the documentation and/or other materials
0013: * provided with the distribution.
0014: * - All advertising materials mentioning features or use of this software must display the
0015: * following acknowledgment: "This product includes Hypersonic SQL."
0016: * - Products derived from this software may not be called "Hypersonic SQL" nor may
0017: * "Hypersonic SQL" appear in their names without prior written permission of the
0018: * Hypersonic SQL Group.
0019: * - Redistributions of any form whatsoever must retain the following acknowledgment: "This
0020: * product includes Hypersonic SQL."
0021: * This software is provided "as is" and any expressed or implied warranties, including, but
0022: * not limited to, the implied warranties of merchantability and fitness for a particular purpose are
0023: * disclaimed. In no event shall the Hypersonic SQL Group or its contributors be liable for any
0024: * direct, indirect, incidental, special, exemplary, or consequential damages (including, but
0025: * not limited to, procurement of substitute goods or services; loss of use, data, or profits;
0026: * or business interruption). However caused any on any theory of liability, whether in contract,
0027: * strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this
0028: * software, even if advised of the possibility of such damage.
0029: * This software consists of voluntary contributions made by many individuals on behalf of the
0030: * Hypersonic SQL Group.
0031: *
0032: *
0033: * For work added by the HSQL Development Group:
0034: *
0035: * Copyright (c) 2001-2002, The HSQL Development Group
0036: * All rights reserved.
0037: *
0038: * Redistribution and use in source and binary forms, with or without
0039: * modification, are permitted provided that the following conditions are met:
0040: *
0041: * Redistributions of source code must retain the above copyright notice, this
0042: * list of conditions and the following disclaimer, including earlier
0043: * license statements (above) and comply with all above license conditions.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution, including earlier
0048: * license statements (above) and comply with all above license conditions.
0049: *
0050: * Neither the name of the HSQL Development Group nor the names of its
0051: * contributors may be used to endorse or promote products derived from this
0052: * software without specific prior written permission.
0053: *
0054: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0055: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0056: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0057: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0058: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0059: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0060: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0061: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0062: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0063: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0064: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0065: */
0066:
0067: package org.hsqldb;
0068:
0069: import org.hsqldb.lib.HsqlArrayList;
0070: import org.hsqldb.lib.HsqlHashMap;
0071: import org.hsqldb.lib.HsqlStringBuffer;
0072: import java.io.IOException;
0073: import java.io.BufferedWriter;
0074: import java.io.ByteArrayInputStream;
0075: import java.io.ByteArrayOutputStream;
0076: import java.io.File;
0077: import java.io.FileInputStream;
0078: import java.io.FileOutputStream;
0079: import java.io.FileReader;
0080: import java.io.FileWriter;
0081: import java.io.InputStreamReader;
0082: import java.io.LineNumberReader;
0083: import java.io.OutputStreamWriter;
0084: import java.io.Writer;
0085: import java.sql.SQLException;
0086: import java.util.Enumeration;
0087:
0088: //import java.util.zip.
0089: import java.util.zip.Deflater;
0090: import java.util.zip.DeflaterOutputStream;
0091: import java.util.zip.Inflater;
0092: import java.util.zip.InflaterInputStream;
0093:
0094: // fredt@users 20020215 - patch 1.7.0 by fredt
0095: // to move operations on the database.properties files to new
0096: // class HsqlDatabaseProperties
0097: // fredt@users 20020220 - patch 488200 by xclay@users - throw exception
0098: // throw addded to all methods relying on file io
0099: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0100: // fredt@users 20020405 - patch 1.7.0 by fredt - no change in db location
0101: // because important information about the database is now stored in the
0102: // *.properties file, all database files should be in the same folder as the
0103: // *.properties file
0104: // tony_lai@users 20020820 - export hsqldb.log_size to .properties file
0105: // tony_lai@users 20020820 - Modification to shutdown compact process to save memory usage
0106:
0107: /**
0108: * This class is responsible for most file handling. A HSQL database
0109: * consists of a .properties file, a .script file (contains a SQL script),
0110: * a .data file (contains data of cached tables) and a .backup file
0111: * (contains the compressed .data file) <P>
0112: *
0113: * This is an example of the .properties file. The version and the modified
0114: * properties are automatically created by the database and should not be
0115: * changed manually: <pre>
0116: * modified=no
0117: * version=1.43
0118: * </pre> The following lines are optional, this means they are not created
0119: * automatically by the database, but they are interpreted if they exist in
0120: * the .script file. They have to be created manually if required. If they
0121: * don't exist the default is used. This are the defaults of the database
0122: * 'test': <pre>
0123: * readonly=false
0124: * </pre>
0125: *
0126: * @version 1.7.0
0127: */
0128: class Log implements Runnable {
0129:
0130: // block size for copying data
0131: private static final int COPY_BLOCK_SIZE = 1 << 16;
0132: private HsqlDatabaseProperties pProperties;
0133: private String sName;
0134: private Database dDatabase;
0135: private Session sysSession;
0136: private Writer wScript;
0137: private String sFileScript;
0138: private String sFileCache;
0139: private String sFileBackup;
0140: private boolean bRestoring;
0141: private boolean bReadOnly;
0142: private int iLogSize;
0143: private int iLogCount;
0144: private Thread tRunner;
0145: private volatile boolean bNeedFlush;
0146: private volatile boolean bWriteDelay;
0147: private int mLastId;
0148: private Cache cCache;
0149:
0150: // boucherb@users - comment - FIXME
0151: // boolean stopped;
0152:
0153: /**
0154: * Constructor declaration
0155: *
0156: * @param db
0157: * @param system
0158: * @param name
0159: * @exception SQLException Description of the Exception
0160: */
0161: Log(Database db, Session system, String name) throws SQLException {
0162:
0163: dDatabase = db;
0164: sysSession = system;
0165: sName = name;
0166: pProperties = db.getProperties();
0167: tRunner = new Thread(this );
0168:
0169: // boucherb@users - FIXME:
0170: // standard VM behaviour is to shut down only after all
0171: // non-daemon threads exit. Therefor, tRunner shuld be
0172: // daemon. Consider the case of:
0173: /*
0174: public void main(String[] args) {
0175: ...
0176: try {
0177: // fails due to bad user/password...must then connect with valid combo
0178: // again to *really* shutdown database, or explicitly call System.exit(...)
0179: DriverManager.getConnection("jdbc:hsqldb:filespec,"user","password");
0180: ...
0181: } catch (...) {
0182: }
0183: ...
0184: }
0185: */
0186:
0187: // the VM will not exit, since tRunner is still running and
0188: // no shutdown is issued to close the database.
0189: //
0190: // - setDaemon(false) may require flush in finalization
0191: // CB
0192: // tRunner.setDaemon(false);
0193: tRunner.start();
0194: }
0195:
0196: /**
0197: * Method declaration
0198: */
0199: public void run() {
0200:
0201: // boucherb@users - FIXME
0202: // while (!stopped) {
0203: while (tRunner != null) {
0204: try {
0205: tRunner.sleep(1000);
0206:
0207: if (bNeedFlush) {
0208: wScript.flush();
0209:
0210: bNeedFlush = false;
0211: }
0212:
0213: // todo: try to do Cache.cleanUp() here, too
0214: } catch (Exception e) {
0215:
0216: // ignore exceptions; may be InterruptedException or IOException
0217: }
0218: }
0219: }
0220:
0221: /**
0222: * Method declaration
0223: *
0224: * @param delay
0225: */
0226: void setWriteDelay(boolean delay) {
0227: bWriteDelay = delay;
0228: }
0229:
0230: /**
0231: * When opening a database, the hsqldb.compatible_version property is
0232: * used to determine if this version of the engine is equal to or greater
0233: * than the earliest version of the engine capable of opening that
0234: * database.<p>
0235: *
0236: * @return
0237: * @throws SQLException
0238: */
0239: boolean open() throws SQLException {
0240:
0241: if (Trace.TRACE) {
0242: Trace.trace();
0243: }
0244:
0245: if (!pProperties.checkFileExists()) {
0246: create();
0247: open();
0248:
0249: // this is a new database
0250: return true;
0251: }
0252:
0253: // todo: some parts are not necessary for ready-only access
0254: pProperties.load();
0255:
0256: sFileScript = sName + ".script";
0257: sFileCache = sName + ".data";
0258: sFileBackup = sName + ".backup";
0259:
0260: // tony_lai@users 20020820
0261: // Allows the user to modify log size from the properties file.
0262: iLogSize = pProperties.getIntegerProperty("hsqldb.log_size",
0263: iLogSize);
0264:
0265: String version = pProperties
0266: .getProperty("hsqldb.compatible_version");
0267:
0268: // fredt@users 20020428 - patch 1.7.0 by fredt
0269: int check = version.substring(0, 5).compareTo(
0270: jdbcDriver.VERSION);
0271:
0272: Trace.check(check <= 0, Trace.WRONG_DATABASE_FILE_VERSION);
0273:
0274: // save the current version
0275: pProperties.setProperty("hsqldb.version", jdbcDriver.VERSION);
0276:
0277: if (pProperties.isPropertyTrue("readonly")) {
0278: bReadOnly = true;
0279:
0280: dDatabase.setReadOnly();
0281:
0282: if (cCache != null) {
0283: cCache.open(true);
0284: }
0285:
0286: reopenAllTextCaches();
0287: runScript();
0288:
0289: return false;
0290: }
0291:
0292: boolean needbackup = false;
0293: String state = pProperties.getProperty("modified");
0294:
0295: if (state.equals("yes-new-files")) {
0296: renameNewToCurrent(sFileScript);
0297: renameNewToCurrent(sFileBackup);
0298: } else if (state.equals("yes")) {
0299: if (isAlreadyOpen()) {
0300: throw Trace.error(Trace.DATABASE_ALREADY_IN_USE);
0301: }
0302:
0303: // recovering after a crash (or forgot to close correctly)
0304: restoreBackup();
0305:
0306: needbackup = true;
0307: }
0308:
0309: pProperties.setProperty("modified", "yes");
0310: pProperties.save();
0311:
0312: if (cCache != null) {
0313: cCache.open(false);
0314: }
0315:
0316: reopenAllTextCaches();
0317: runScript();
0318:
0319: if (needbackup) {
0320: close(false);
0321: pProperties.setProperty("modified", "yes");
0322: pProperties.save();
0323:
0324: if (cCache != null) {
0325: cCache.open(false);
0326: }
0327:
0328: reopenAllTextCaches();
0329: }
0330:
0331: openScript();
0332:
0333: // this is an existing database
0334: return false;
0335: }
0336:
0337: Cache getCache() throws SQLException {
0338:
0339: if (cCache == null) {
0340: cCache = new Cache(sFileCache, pProperties);
0341:
0342: cCache.open(bReadOnly);
0343: }
0344:
0345: return (cCache);
0346: }
0347:
0348: /**
0349: * Method declaration
0350: */
0351: void /* synchronized */stop() {
0352:
0353: //boucherb@users - FIXME:
0354: /*
0355: if (!stopped)
0356: stopped = true;
0357: tRunner.interrupt();
0358: tRunner = null;
0359: }
0360: */
0361: tRunner = null;
0362: }
0363:
0364: /**
0365: * Method declaration
0366: *
0367: * @param compact
0368: * @throws SQLException
0369: */
0370: void close(boolean compact) throws SQLException {
0371:
0372: if (Trace.TRACE) {
0373: Trace.trace();
0374: }
0375:
0376: if (bReadOnly) {
0377: return;
0378: }
0379:
0380: // no more scripting
0381: closeScript();
0382:
0383: // create '.script.new' (for this the cache may be still required)
0384: writeScript(compact);
0385:
0386: // flush the cache (important: after writing the script)
0387: if (cCache != null) {
0388: cCache.flush();
0389: }
0390:
0391: closeAllTextCaches(compact);
0392:
0393: // create '.backup.new' using the '.data'
0394: backup();
0395:
0396: // we have the new files
0397: pProperties.setProperty("modified", "yes-new-files");
0398: pProperties.save();
0399:
0400: // old files can be removed and new files renamed
0401: renameNewToCurrent(sFileScript);
0402: renameNewToCurrent(sFileBackup);
0403:
0404: // now its done completely
0405: pProperties.setProperty("modified", "no");
0406: pProperties.setProperty("version", jdbcDriver.VERSION);
0407: pProperties.setProperty("hsqldb.compatible_version", "1.7.0");
0408: pProperties.save();
0409: pProperties.close();
0410:
0411: if (compact) {
0412:
0413: // stop the runner thread of this process (just for security)
0414: stop();
0415:
0416: // delete the .data so then a new file is created
0417: (new File(sFileCache)).delete();
0418: (new File(sFileBackup)).delete();
0419:
0420: // tony_lai@users 20020820
0421: // The database re-open and close has been moved to
0422: // Database#close(int closemode) for saving memory usage.
0423: }
0424: }
0425:
0426: /**
0427: * Method declaration
0428: *
0429: * @throws SQLException
0430: */
0431: void checkpoint() throws SQLException {
0432:
0433: close(false);
0434: pProperties.setProperty("modified", "yes");
0435: pProperties.save();
0436:
0437: if (cCache != null) {
0438: cCache.open(false);
0439: }
0440:
0441: reopenAllTextCaches();
0442: openScript();
0443: }
0444:
0445: /**
0446: * Method declaration
0447: *
0448: * @param mb
0449: */
0450: void setLogSize(int mb) {
0451:
0452: iLogSize = mb;
0453:
0454: pProperties.setProperty("hsqldb.log_size", iLogSize);
0455: }
0456:
0457: /**
0458: * Method declaration
0459: *
0460: * @param c
0461: * @param s
0462: * @throws SQLException
0463: */
0464: void write(Session c, String s) throws SQLException {
0465:
0466: if (bRestoring || s == null || s.length() == 0) {
0467: return;
0468: }
0469:
0470: if (!bReadOnly) {
0471: int id = 0;
0472:
0473: if (c != null) {
0474: id = c.getId();
0475: }
0476:
0477: if (id != mLastId) {
0478: s = "/*C" + id + "*/" + s;
0479: mLastId = id;
0480: }
0481:
0482: try {
0483: writeLine(wScript, s);
0484:
0485: if (bWriteDelay) {
0486: bNeedFlush = true;
0487: } else {
0488: wScript.flush();
0489: }
0490: } catch (IOException e) {
0491: throw Trace.error(Trace.FILE_IO_ERROR, sFileScript);
0492: }
0493:
0494: // fredt@users - todo - eliminate new File() calls
0495: if (iLogSize > 0 && iLogCount++ > 100) {
0496: iLogCount = 0;
0497:
0498: if ((new File(sFileScript)).length() > iLogSize * 1024 * 1024) {
0499: checkpoint();
0500: }
0501: }
0502: }
0503: }
0504:
0505: /**
0506: * Method declaration
0507: *
0508: * @throws SQLException
0509: */
0510: void shutdown() throws SQLException {
0511:
0512: tRunner = null;
0513:
0514: if (cCache != null) {
0515: cCache.shutdown();
0516:
0517: cCache = null;
0518: }
0519:
0520: shutdownAllTextCaches();
0521: closeScript();
0522: pProperties.close();
0523: }
0524:
0525: /**
0526: * Method declaration
0527: *
0528: * @param db
0529: * @param file
0530: * @param full
0531: * @param session
0532: * @throws SQLException
0533: */
0534: static void scriptToFile(Database db, String file, boolean full,
0535: Session session) throws SQLException {
0536:
0537: if ((new File(file)).exists()) {
0538:
0539: // there must be no such file; overwriting not allowed for security
0540: throw Trace.error(Trace.FILE_IO_ERROR, file);
0541: }
0542:
0543: try {
0544: long time = System.currentTimeMillis();
0545:
0546: // only ddl commands; needs not so much memory
0547: Result r;
0548:
0549: if (full) {
0550:
0551: // no drop, no insert, and no positions for cached tables
0552: r = db.getScript(false, false, false, session);
0553: } else {
0554:
0555: // no drop, no insert, but positions for cached tables
0556: r = db.getScript(false, false, true, session);
0557: }
0558:
0559: Record n = r.rRoot;
0560: FileWriter w = new FileWriter(file);
0561:
0562: while (n != null) {
0563: writeLine(w, (String) n.data[0]);
0564:
0565: n = n.next;
0566: }
0567:
0568: // inserts are done separetely to save memory
0569: HsqlArrayList tables = db.getTables();
0570:
0571: for (int i = 0; i < tables.size(); i++) {
0572: Table t = (Table) tables.get(i);
0573:
0574: // cached tables have the index roots set in the ddl script
0575: if ((full || !t.isCached()) && !t.isTemp()
0576: && !t.isView()
0577: && (!t.isText() || !t.isDataReadOnly())) {
0578: Index primary = t.getPrimaryIndex();
0579: Node x = primary.first();
0580:
0581: while (x != null) {
0582: writeLine(w, t.getInsertStatement(x.getData()));
0583:
0584: x = primary.next(x);
0585: }
0586: }
0587:
0588: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0589: if (t.isDataReadOnly() && !t.isTemp() && !t.isText()) {
0590: HsqlStringBuffer a = new HsqlStringBuffer(
0591: "SET TABLE ");
0592:
0593: a.append(t.getName().statementName);
0594: a.append(" READONLY TRUE");
0595: writeLine(w, a.toString());
0596: }
0597: }
0598:
0599: w.close();
0600:
0601: time = System.currentTimeMillis() - time;
0602:
0603: if (Trace.TRACE) {
0604: Trace.trace(time);
0605: }
0606: } catch (IOException e) {
0607: throw Trace.error(Trace.FILE_IO_ERROR, file + " " + e);
0608: }
0609: }
0610:
0611: /**
0612: * Called internally by replication to generate initial state to be sent to new replica
0613: * Returns null if the generation fails, or throws an exception
0614: */
0615: static byte[] scriptToBuffer(Database db, boolean full,
0616: Session session) throws SQLException {
0617:
0618: ByteArrayOutputStream bos = null;
0619: OutputStreamWriter w = null;
0620:
0621: try {
0622: long time = System.currentTimeMillis();
0623:
0624: // only ddl commands; needs not so much memory
0625: Result r;
0626:
0627: if (full) {
0628:
0629: // no drop, no insert, and no positions for cached tables
0630: r = db.getScript(false, false, false, session);
0631: } else {
0632:
0633: // no drop, no insert, but positions for cached tables
0634: r = db.getScript(false, false, true, session);
0635: }
0636:
0637: Record n = r.rRoot;
0638: // FileWriter w = new FileWriter(file);
0639: bos = new ByteArrayOutputStream(0xff);
0640: w = new OutputStreamWriter(bos);
0641:
0642: while (n != null) {
0643: writeLine(w, (String) n.data[0]);
0644:
0645: n = n.next;
0646: }
0647:
0648: // inserts are done separetely to save memory
0649: HsqlArrayList tables = db.getTables();
0650:
0651: for (int i = 0; i < tables.size(); i++) {
0652: Table t = (Table) tables.get(i);
0653:
0654: // cached tables have the index roots set in the ddl script
0655: if ((full || !t.isCached()) && !t.isTemp()
0656: && !t.isView()
0657: && (!t.isText() || !t.isDataReadOnly())) {
0658: Index primary = t.getPrimaryIndex();
0659: Node x = primary.first();
0660:
0661: while (x != null) {
0662: writeLine(w, t.getInsertStatement(x.getData()));
0663:
0664: x = primary.next(x);
0665: }
0666: }
0667:
0668: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0669: if (t.isDataReadOnly() && !t.isTemp() && !t.isText()) {
0670: HsqlStringBuffer a = new HsqlStringBuffer(
0671: "SET TABLE ");
0672:
0673: a.append(t.getName().statementName);
0674: a.append(" READONLY TRUE");
0675: writeLine(w, a.toString());
0676: }
0677: }
0678:
0679: w.close(); // flushes as well
0680:
0681: time = System.currentTimeMillis() - time;
0682:
0683: if (Trace.TRACE) {
0684: Trace.trace(time);
0685: }
0686:
0687: return bos != null ? bos.toByteArray() : null;
0688:
0689: } catch (IOException e) {
0690: throw Trace.error(Trace.FILE_IO_ERROR, e.toString());
0691: }
0692: }
0693:
0694: /**
0695: * Method declaration
0696: *
0697: * @param file
0698: */
0699: private void renameNewToCurrent(String file) {
0700:
0701: // even if it crashes here, recovering is no problem
0702: if ((new File(file + ".new")).exists()) {
0703:
0704: // if we have a new file
0705: // delete the old (maybe already deleted)
0706: (new File(file)).delete();
0707:
0708: // rename the new to the current
0709: new File(file + ".new").renameTo(new File(file));
0710: }
0711: }
0712:
0713: private void create() throws SQLException {
0714:
0715: if (Trace.TRACE) {
0716: Trace.trace(sName);
0717: }
0718:
0719: pProperties.setProperty("version", jdbcDriver.VERSION);
0720: pProperties.save();
0721: }
0722:
0723: /**
0724: * Method declaration
0725: *
0726: * @return
0727: * @throws SQLException
0728: */
0729: private boolean isAlreadyOpen() throws SQLException {
0730:
0731: // reading the last modified, wait 3 seconds, read again.
0732: // if the same information was read the file was not changed
0733: // and is probably, except the other process is blocked
0734: if (Trace.TRACE) {
0735: Trace.trace();
0736: }
0737:
0738: File f = new File(sName + ".lock");
0739: long l1 = f.lastModified();
0740:
0741: try {
0742: Thread.sleep(3000);
0743: } catch (Exception e) {
0744: }
0745:
0746: long l2 = f.lastModified();
0747:
0748: if (l1 != l2) {
0749: return true;
0750: }
0751:
0752: return pProperties.isFileOpen();
0753: }
0754:
0755: /**
0756: * Method declaration
0757: *
0758: * @throws SQLException
0759: */
0760: private void backup() throws SQLException {
0761:
0762: if (Trace.TRACE) {
0763: Trace.trace();
0764:
0765: // if there is no cache file then backup is not necessary
0766: }
0767:
0768: if (!(new File(sFileCache)).exists()) {
0769: return;
0770: }
0771:
0772: try {
0773: long time = System.currentTimeMillis();
0774:
0775: // create a '.new' file; rename later
0776: DeflaterOutputStream f = new DeflaterOutputStream(
0777: new FileOutputStream(sFileBackup + ".new"),
0778: new Deflater(Deflater.BEST_SPEED), COPY_BLOCK_SIZE);
0779: byte b[] = new byte[COPY_BLOCK_SIZE];
0780: FileInputStream in = new FileInputStream(sFileCache);
0781:
0782: while (true) {
0783: int l = in.read(b, 0, COPY_BLOCK_SIZE);
0784:
0785: if (l == -1) {
0786: break;
0787: }
0788:
0789: f.write(b, 0, l);
0790: }
0791:
0792: f.close();
0793: in.close();
0794:
0795: time = System.currentTimeMillis() - time;
0796:
0797: if (Trace.TRACE) {
0798: Trace.trace(time);
0799: }
0800: } catch (Exception e) {
0801: throw Trace.error(Trace.FILE_IO_ERROR, sFileBackup);
0802: }
0803: }
0804:
0805: /**
0806: * Method declaration
0807: *
0808: * @throws SQLException
0809: */
0810: private void restoreBackup() throws SQLException {
0811:
0812: if (Trace.TRACE) {
0813: Trace.trace("not closed last time!");
0814: }
0815:
0816: if (!(new File(sFileBackup)).exists()) {
0817:
0818: // the backup don't exists because it was never made or is empty
0819: // the cache file must be deleted in this case
0820: (new File(sFileCache)).delete();
0821:
0822: return;
0823: }
0824:
0825: try {
0826: long time = System.currentTimeMillis();
0827: InflaterInputStream f = new InflaterInputStream(
0828: new FileInputStream(sFileBackup), new Inflater());
0829: FileOutputStream cache = new FileOutputStream(sFileCache);
0830: byte b[] = new byte[COPY_BLOCK_SIZE];
0831:
0832: while (true) {
0833: int l = f.read(b, 0, COPY_BLOCK_SIZE);
0834:
0835: if (l == -1) {
0836: break;
0837: }
0838:
0839: cache.write(b, 0, l);
0840: }
0841:
0842: cache.close();
0843: f.close();
0844:
0845: time = System.currentTimeMillis() - time;
0846:
0847: if (Trace.TRACE) {
0848: Trace.trace(time);
0849: }
0850: } catch (Exception e) {
0851: throw Trace.error(Trace.FILE_IO_ERROR, sFileBackup);
0852: }
0853: }
0854:
0855: /**
0856: * Method declaration
0857: *
0858: * @throws SQLException
0859: */
0860: private void openScript() throws SQLException {
0861:
0862: if (Trace.TRACE) {
0863: Trace.trace();
0864: }
0865:
0866: try {
0867:
0868: // todo: use a compressed stream
0869: wScript = new BufferedWriter(new FileWriter(sFileScript,
0870: true), 4096);
0871: } catch (Exception e) {
0872: throw Trace.error(Trace.FILE_IO_ERROR, sFileScript);
0873: }
0874: }
0875:
0876: /**
0877: * Method declaration
0878: *
0879: * @throws SQLException
0880: */
0881: private void closeScript() throws SQLException {
0882:
0883: if (Trace.TRACE) {
0884: Trace.trace();
0885: }
0886:
0887: try {
0888: if (wScript != null) {
0889: wScript.close();
0890:
0891: wScript = null;
0892: }
0893: } catch (Exception e) {
0894: throw Trace.error(Trace.FILE_IO_ERROR, sFileScript);
0895: }
0896: }
0897:
0898: /**
0899: * Method declaration
0900: *
0901: * @throws SQLException
0902: */
0903: private void runScript() throws SQLException {
0904:
0905: if (Trace.TRACE) {
0906: Trace.trace();
0907: }
0908:
0909: if (!(new File(sFileScript)).exists()) {
0910: return;
0911: }
0912:
0913: bRestoring = true;
0914:
0915: dDatabase.setReferentialIntegrity(false);
0916:
0917: HsqlArrayList session = new HsqlArrayList();
0918:
0919: session.add(sysSession);
0920:
0921: Session current = sysSession;
0922:
0923: try {
0924: long time = System.currentTimeMillis();
0925: LineNumberReader r = new LineNumberReader(new FileReader(
0926: sFileScript));
0927:
0928: while (true) {
0929: String s = readLine(r);
0930:
0931: if (s == null) {
0932: break;
0933: }
0934:
0935: if (s.startsWith("/*C")) {
0936: int id = Integer.parseInt(s.substring(3, s.indexOf(
0937: '*', 4)));
0938:
0939: if (id >= session.size()) {
0940: session.setSize(id + 1);
0941: }
0942:
0943: current = (Session) session.get(id);
0944:
0945: if (current == null) {
0946: current = new Session(sysSession, id);
0947:
0948: session.set(id, current);
0949: dDatabase.registerSession(current);
0950: }
0951:
0952: s = s.substring(s.indexOf('/', 1) + 1);
0953: }
0954:
0955: if (s.length() != 0) {
0956: Result result = dDatabase.execute(s, current);
0957:
0958: if ((result != null)
0959: && (result.iMode == Result.ERROR)) {
0960: throw (Trace.getError(result.errorCode,
0961: result.sError));
0962: }
0963: }
0964:
0965: if (s.equals("DISCONNECT")) {
0966: int id = current.getId();
0967:
0968: current = new Session(sysSession, id);
0969:
0970: session.set(id, current);
0971: }
0972: }
0973:
0974: r.close();
0975:
0976: for (int i = 0; i < session.size(); i++) {
0977: current = (Session) session.get(i);
0978:
0979: if (current != null) {
0980: current.rollback();
0981: }
0982: }
0983:
0984: time = System.currentTimeMillis() - time;
0985:
0986: if (Trace.TRACE) {
0987: Trace.trace(time);
0988: }
0989: } catch (IOException e) {
0990: throw Trace.error(Trace.FILE_IO_ERROR, sFileScript + " "
0991: + e);
0992: }
0993:
0994: dDatabase.setReferentialIntegrity(true);
0995:
0996: bRestoring = false;
0997: }
0998:
0999: void initializeDatabaseFromBuffer(byte[] buf) throws SQLException {
1000: ByteArrayInputStream bis;
1001: InputStreamReader in;
1002:
1003: if (Trace.TRACE) {
1004: Trace.trace();
1005: }
1006:
1007: if (buf == null)
1008: throw Trace
1009: .error(Trace.FILE_IO_ERROR,
1010: "Log.initializeDatabaseFromBuffer(): buffer is null");
1011:
1012: bRestoring = true;
1013:
1014: dDatabase.setReferentialIntegrity(false);
1015:
1016: HsqlArrayList session = new HsqlArrayList();
1017:
1018: session.add(sysSession);
1019:
1020: Session current = sysSession;
1021: int size = 1;
1022:
1023: try {
1024: long time = System.currentTimeMillis();
1025: LineNumberReader r = null;
1026:
1027: bis = new ByteArrayInputStream(buf);
1028: in = new InputStreamReader(bis);
1029: r = new LineNumberReader(in);
1030:
1031: while (true) {
1032: String s = readLine(r);
1033:
1034: if (s == null) {
1035: break;
1036: }
1037:
1038: if (s.startsWith("/*C")) {
1039: int id = Integer.parseInt(s.substring(3, s.indexOf(
1040: '*', 4)));
1041:
1042: if (id >= size) {
1043: session.setSize(id + 1);
1044: }
1045:
1046: current = (Session) session.get(id);
1047:
1048: if (current == null) {
1049: current = new Session(sysSession, id);
1050:
1051: session.set(id, current);
1052: dDatabase.registerSession(current);
1053: }
1054:
1055: s = s.substring(s.indexOf('/', 1) + 1);
1056: }
1057:
1058: if (s.length() != 0) {
1059: Result result = dDatabase.execute(s, current);
1060:
1061: if ((result != null)
1062: && (result.iMode == Result.ERROR)) {
1063: throw (Trace.getError(result.errorCode,
1064: result.sError));
1065: }
1066: }
1067:
1068: if (s.equals("DISCONNECT")) {
1069: int id = current.getId();
1070:
1071: current = new Session(sysSession, id);
1072:
1073: session.set(id, current);
1074: }
1075: }
1076:
1077: r.close();
1078:
1079: for (int i = 0; i < size; i++) {
1080: current = (Session) session.get(i);
1081:
1082: if (current != null) {
1083: current.rollback();
1084: }
1085: }
1086:
1087: time = System.currentTimeMillis() - time;
1088:
1089: if (Trace.TRACE) {
1090: Trace.trace(time);
1091: }
1092: } catch (IOException e) {
1093: throw Trace.error(Trace.FILE_IO_ERROR, sFileScript + " "
1094: + e);
1095: }
1096:
1097: dDatabase.setReferentialIntegrity(true);
1098:
1099: bRestoring = false;
1100: }
1101:
1102: /**
1103: * Method declaration
1104: *
1105: * @param full
1106: * @throws SQLException
1107: */
1108: private void writeScript(boolean full) throws SQLException {
1109:
1110: if (Trace.TRACE) {
1111: Trace.trace();
1112:
1113: // create script in '.new' file
1114: }
1115:
1116: (new File(sFileScript + ".new")).delete();
1117:
1118: // script; but only positions of cached tables, not full
1119: scriptToFile(dDatabase, sFileScript + ".new", full, sysSession);
1120: }
1121:
1122: /**
1123: * Method declaration
1124: *
1125: * @param w
1126: * @param s
1127: * @throws IOException
1128: */
1129:
1130: // fredt@users 20011120 - patch 450455 by kibu@users - optimised
1131: private static final String lineSep = System.getProperty(
1132: "line.separator", "\n");
1133:
1134: private static void writeLine(Writer w, String s)
1135: throws IOException {
1136: w.write(StringConverter.unicodeToAscii(s).append(lineSep)
1137: .toString());
1138: }
1139:
1140: /**
1141: * Method declaration
1142: *
1143: * @param r
1144: * @return
1145: * @throws IOException
1146: */
1147: private static String readLine(LineNumberReader r)
1148: throws IOException {
1149:
1150: String s = r.readLine();
1151:
1152: return StringConverter.asciiToUnicode(s);
1153: }
1154:
1155: /**
1156: * Method declaration
1157: *
1158: * @return
1159: */
1160: HsqlDatabaseProperties getProperties() {
1161: return pProperties;
1162: }
1163:
1164: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) - text tables
1165: private HsqlHashMap textCacheList = new HsqlHashMap();
1166:
1167: Cache openTextCache(String table, String source,
1168: boolean readOnlyData, boolean reversed) throws SQLException {
1169:
1170: closeTextCache(table);
1171:
1172: if (pProperties.getProperty("textdb.allow_full_path", "false")
1173: .equals("false")) {
1174: if (source.indexOf("..") != -1) {
1175: throw (Trace.error(Trace.ACCESS_IS_DENIED, source));
1176: }
1177:
1178: String path = new File(new File(sName).getAbsolutePath())
1179: .getParent();
1180:
1181: if (path != null) {
1182: source = path + File.separator + source;
1183: }
1184: }
1185:
1186: String prefix = "textdb." + table.toLowerCase() + ".";
1187: TextCache c;
1188:
1189: if (reversed) {
1190: c = new ReverseTextCache(source, prefix, pProperties);
1191: } else {
1192: c = new TextCache(source, prefix, pProperties);
1193: }
1194:
1195: c.open(readOnlyData || bReadOnly);
1196: textCacheList.put(table, c);
1197:
1198: return (c);
1199: }
1200:
1201: void closeTextCache(String table) throws SQLException {
1202:
1203: TextCache c = (TextCache) textCacheList.remove(table);
1204:
1205: if (c != null) {
1206: c.flush();
1207: }
1208: }
1209:
1210: void closeAllTextCaches(boolean compact) throws SQLException {
1211:
1212: for (Enumeration e = textCacheList.elements(); e
1213: .hasMoreElements();) {
1214: if (compact) {
1215: ((TextCache) e.nextElement()).purge();
1216: } else {
1217: ((TextCache) e.nextElement()).flush();
1218: }
1219: }
1220: }
1221:
1222: void reopenAllTextCaches() throws SQLException {
1223:
1224: for (Enumeration e = textCacheList.elements(); e
1225: .hasMoreElements();) {
1226: ((TextCache) e.nextElement()).reopen();
1227: }
1228: }
1229:
1230: void shutdownAllTextCaches() throws SQLException {
1231:
1232: for (Enumeration e = textCacheList.elements(); e
1233: .hasMoreElements();) {
1234: ((TextCache) e.nextElement()).shutdown();
1235: }
1236:
1237: textCacheList = new HsqlHashMap();
1238: }
1239: }
|