0001: /*
0002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
0003: * (http://h2database.com/html/license.html).
0004: * Initial Developer: H2 Group
0005: */
0006: package org.h2.tools;
0007:
0008: import java.io.BufferedInputStream;
0009: import java.io.BufferedReader;
0010: import java.io.BufferedWriter;
0011: import java.io.ByteArrayInputStream;
0012: import java.io.DataInputStream;
0013: import java.io.FileInputStream;
0014: import java.io.IOException;
0015: import java.io.InputStream;
0016: import java.io.InputStreamReader;
0017: import java.io.OutputStream;
0018: import java.io.PrintWriter;
0019: import java.io.Reader;
0020: import java.sql.SQLException;
0021: import java.util.ArrayList;
0022: import java.util.HashMap;
0023: import java.util.HashSet;
0024: import java.util.Iterator;
0025: import java.util.Map;
0026: import java.util.Map.Entry;
0027:
0028: import org.h2.command.Parser;
0029: import org.h2.engine.Constants;
0030: import org.h2.engine.Database;
0031: import org.h2.engine.DbObject;
0032: import org.h2.engine.MetaRecord;
0033: import org.h2.log.LogFile;
0034: import org.h2.message.Message;
0035: import org.h2.result.SimpleRow;
0036: import org.h2.security.SHA256;
0037: import org.h2.store.DataHandler;
0038: import org.h2.store.DataPage;
0039: import org.h2.store.DiskFile;
0040: import org.h2.store.FileLister;
0041: import org.h2.store.FileStore;
0042: import org.h2.store.FileStoreInputStream;
0043: import org.h2.util.ByteUtils;
0044: import org.h2.util.FileUtils;
0045: import org.h2.util.IOUtils;
0046: import org.h2.util.ObjectArray;
0047: import org.h2.util.ObjectUtils;
0048: import org.h2.util.RandomUtils;
0049: import org.h2.util.SmallLRUCache;
0050: import org.h2.value.Value;
0051: import org.h2.value.ValueLob;
0052:
0053: /**
0054: * Dumps the contents of a database file to a human readable text file. This
0055: * text file can be used to recover most of the data. This tool does not open
0056: * the database and can be used even if the database files are corrupted. A
0057: * database can get corrupted if there is a bug in the database engine or file
0058: * system software, or if an application writes into the database file that
0059: * doesn't understand the the file format, or if there is a hardware problem.
0060: */
0061: public class Recover implements DataHandler {
0062:
0063: private String databaseName;
0064: private boolean textStorage;
0065: private int block;
0066: private int blockCount;
0067: private int storageId;
0068: private int recordLength;
0069: private int valueId;
0070: private boolean log;
0071: private boolean lobFilesInDirectories;
0072:
0073: private void showUsage() {
0074: System.out.println("java " + getClass().getName()
0075: + " [-dir <dir>] [-db <database>] [-log true]");
0076: System.out
0077: .println("See also http://h2database.com/javadoc/org/h2/tools/Recover.html");
0078: }
0079:
0080: /**
0081: * The command line interface for this tool.
0082: * The options must be split into strings like this: "-db", "test",...
0083: * Options are case sensitive. The following options are supported:
0084: * <ul>
0085: * <li>-help or -? (print the list of options)
0086: * </li><li>-dir database directory (the default is the current directory)
0087: * </li><li>-db database name (all databases if no name is specified)
0088: * </li><li>-log {true|false} (log additional messages)
0089: * </li></ul>
0090: *
0091: * @param args the command line arguments
0092: * @throws SQLException
0093: */
0094: public static void main(String[] args) throws SQLException {
0095: new Recover().run(args);
0096: }
0097:
0098: private void run(String[] args) throws SQLException {
0099: String dir = ".";
0100: String db = null;
0101: boolean removePassword = false;
0102: for (int i = 0; args != null && i < args.length; i++) {
0103: if ("-dir".equals(args[i])) {
0104: dir = args[++i];
0105: } else if ("-db".equals(args[i])) {
0106: db = args[++i];
0107: } else if ("-removePassword".equals(args[i])) {
0108: removePassword = true;
0109: log = true;
0110: } else if ("-log".equals(args[i])) {
0111: log = Boolean.valueOf(args[++i]).booleanValue();
0112: } else {
0113: showUsage();
0114: return;
0115: }
0116: }
0117: if (removePassword) {
0118: removePassword(dir, db);
0119: } else {
0120: process(dir, db);
0121: }
0122: }
0123:
0124: /**
0125: * INTERNAL
0126: */
0127: public static Reader readClob(String fileName) throws IOException {
0128: return new BufferedReader(new InputStreamReader(
0129: readBlob(fileName)));
0130: }
0131:
0132: /**
0133: * INTERNAL
0134: */
0135: public static InputStream readBlob(String fileName)
0136: throws IOException {
0137: return new BufferedInputStream(new FileInputStream(fileName));
0138: }
0139:
0140: private void removePassword(String dir, String db)
0141: throws SQLException {
0142: ArrayList list = FileLister.getDatabaseFiles(dir, db, true);
0143: for (int i = 0; i < list.size(); i++) {
0144: String fileName = (String) list.get(i);
0145: if (fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
0146: removePassword(fileName);
0147: }
0148: }
0149: }
0150:
0151: private void log(String message) {
0152: if (log) {
0153: System.out.println(message);
0154: }
0155: }
0156:
0157: private void logError(String message, Throwable t) {
0158: System.out.println(message + ": " + t.toString());
0159: if (log) {
0160: t.printStackTrace();
0161: }
0162: }
0163:
0164: private void removePassword(String fileName) throws SQLException {
0165: setDatabaseName(fileName.substring(fileName.length()
0166: - Constants.SUFFIX_DATA_FILE.length()));
0167: textStorage = Database.isTextStorage(fileName, false);
0168: byte[] magic = Database.getMagic(textStorage);
0169: FileStore store = FileStore.open(null, fileName, "rw", magic);
0170: long length = store.length();
0171: int offset = FileStore.HEADER_LENGTH;
0172: int blockSize = DiskFile.BLOCK_SIZE;
0173: int blocks = (int) (length / blockSize);
0174: blockCount = 1;
0175: for (int block = 0; block < blocks; block += blockCount) {
0176: store.seek(offset + (long) block * blockSize);
0177: byte[] bytes = new byte[blockSize];
0178: DataPage s = DataPage.create(this , bytes);
0179: long start = store.getFilePointer();
0180: store.readFully(bytes, 0, blockSize);
0181: blockCount = s.readInt();
0182: storageId = -1;
0183: recordLength = -1;
0184: valueId = -1;
0185: if (blockCount == 0) {
0186: // free block
0187: blockCount = 1;
0188: continue;
0189: } else if (blockCount < 0) {
0190: blockCount = 1;
0191: continue;
0192: }
0193: try {
0194: s.checkCapacity(blockCount * blockSize);
0195: } catch (OutOfMemoryError e) {
0196: blockCount = 1;
0197: continue;
0198: }
0199: if (blockCount > 1) {
0200: store.readFully(s.getBytes(), blockSize, blockCount
0201: * blockSize - blockSize);
0202: }
0203: try {
0204: s.check(blockCount * blockSize);
0205: } catch (SQLException e) {
0206: blockCount = 1;
0207: continue;
0208: }
0209: storageId = s.readInt();
0210: if (storageId != 0) {
0211: continue;
0212: }
0213: recordLength = s.readInt();
0214: if (recordLength <= 0) {
0215: continue;
0216: }
0217: Value[] data;
0218: try {
0219: data = new Value[recordLength];
0220: } catch (Throwable e) {
0221: continue;
0222: }
0223: for (valueId = 0; valueId < recordLength; valueId++) {
0224: try {
0225: data[valueId] = s.readValue();
0226: } catch (Throwable e) {
0227: continue;
0228: }
0229: }
0230: if (storageId == 0) {
0231: try {
0232: String sql = data[3].getString();
0233: if (!sql.startsWith("CREATE USER ")) {
0234: continue;
0235: }
0236: int idx = sql.indexOf("SALT");
0237: if (idx < 0) {
0238: continue;
0239: }
0240: String userName = sql.substring("CREATE USER "
0241: .length(), idx - 1);
0242: if (userName.startsWith("\"")) {
0243: // TODO doesn't work for all cases ("" inside user name)
0244: userName = userName.substring(1, userName
0245: .length() - 1);
0246: }
0247: SHA256 sha = new SHA256();
0248: byte[] userPasswordHash = sha.getKeyPasswordHash(
0249: userName, "".toCharArray());
0250: byte[] salt = RandomUtils
0251: .getSecureBytes(Constants.SALT_LEN);
0252: byte[] passwordHash = sha.getHashWithSalt(
0253: userPasswordHash, salt);
0254: boolean admin = sql.indexOf("ADMIN") >= 0;
0255: StringBuffer buff = new StringBuffer();
0256: buff.append("CREATE USER ");
0257: buff.append(Parser.quoteIdentifier(userName));
0258: buff.append(" SALT '");
0259: buff.append(ByteUtils.convertBytesToString(salt));
0260: buff.append("' HASH '");
0261: buff.append(ByteUtils
0262: .convertBytesToString(passwordHash));
0263: buff.append("'");
0264: if (admin) {
0265: buff.append(" ADMIN");
0266: }
0267: byte[] replacement = buff.toString().getBytes();
0268: int at = ByteUtils.indexOf(s.getBytes(),
0269: "CREATE USER ".getBytes(), 0);
0270: System.arraycopy(replacement, 0, s.getBytes(), at,
0271: replacement.length);
0272: s.fill(blockCount * blockSize);
0273: s.updateChecksum();
0274: store.seek(start);
0275: store.write(s.getBytes(), 0, s.length());
0276: if (log) {
0277: System.out.println("User: " + userName);
0278: }
0279: break;
0280: } catch (Throwable e) {
0281: e.printStackTrace();
0282: }
0283: }
0284: }
0285: closeSilently(store);
0286: }
0287:
0288: /**
0289: * Dumps the database.
0290: *
0291: * @param dir the directory
0292: * @param db the database name (null for all databases)
0293: * @throws SQLException
0294: */
0295: public static void execute(String dir, String db)
0296: throws SQLException {
0297: new Recover().process(dir, db);
0298: }
0299:
0300: private void process(String dir, String db) throws SQLException {
0301: ArrayList list = FileLister.getDatabaseFiles(dir, db, true);
0302: for (int i = 0; i < list.size(); i++) {
0303: String fileName = (String) list.get(i);
0304: if (fileName.endsWith(Constants.SUFFIX_DATA_FILE)) {
0305: dumpData(fileName);
0306: } else if (fileName.endsWith(Constants.SUFFIX_INDEX_FILE)) {
0307: dumpIndex(fileName);
0308: } else if (fileName.endsWith(Constants.SUFFIX_LOG_FILE)) {
0309: dumpLog(fileName);
0310: } else if (fileName.endsWith(Constants.SUFFIX_LOB_FILE)) {
0311: dumpLob(fileName, true);
0312: dumpLob(fileName, false);
0313: }
0314: }
0315: }
0316:
0317: private PrintWriter getWriter(String fileName, String suffix)
0318: throws IOException, SQLException {
0319: fileName = fileName.substring(0, fileName.length() - 3);
0320: String outputFile = fileName + suffix;
0321: log("Created file: " + outputFile);
0322: return new PrintWriter(new BufferedWriter(FileUtils
0323: .openFileWriter(outputFile, false)));
0324: }
0325:
0326: private void writeDataError(PrintWriter writer, String error,
0327: byte[] data, int dumpBlocks) throws IOException {
0328: writer.println("-- ERROR:" + error + " block:" + block
0329: + " blockCount:" + blockCount + " storageId:"
0330: + storageId + " recordLength: " + recordLength
0331: + " valueId:" + valueId);
0332: StringBuffer sb = new StringBuffer();
0333: for (int i = 0; i < dumpBlocks * DiskFile.BLOCK_SIZE; i++) {
0334: int x = (data[i] & 0xff);
0335: if (x >= ' ' && x < 128) {
0336: sb.append((char) x);
0337: } else {
0338: sb.append('?');
0339: }
0340: }
0341: writer.println("-- dump: " + sb.toString());
0342: sb = new StringBuffer();
0343: for (int i = 0; i < dumpBlocks * DiskFile.BLOCK_SIZE; i++) {
0344: int x = (data[i] & 0xff);
0345: sb.append(' ');
0346: if (x < 16) {
0347: sb.append('0');
0348: }
0349: sb.append(Integer.toHexString(x));
0350: }
0351: writer.println("-- dump: " + sb.toString());
0352: }
0353:
0354: private void dumpLob(String fileName, boolean lobCompression) {
0355: OutputStream out = null;
0356: FileStore store = null;
0357: int size = 0;
0358: String n = fileName + (lobCompression ? ".comp" : "") + ".txt";
0359: InputStream in = null;
0360: try {
0361: out = FileUtils.openFileOutputStream(n, false);
0362: textStorage = Database.isTextStorage(fileName, false);
0363: byte[] magic = Database.getMagic(textStorage);
0364: store = FileStore.open(null, fileName, "r", magic);
0365: store.init();
0366: in = new BufferedInputStream(new FileStoreInputStream(
0367: store, this , lobCompression, false));
0368: byte[] buffer = new byte[Constants.IO_BUFFER_SIZE];
0369: while (true) {
0370: int l = in.read(buffer);
0371: if (l < 0) {
0372: break;
0373: }
0374: out.write(buffer, 0, l);
0375: size += l;
0376: }
0377: out.close();
0378: } catch (Throwable e) {
0379: // this is usually not a problem, because we try both compressed and
0380: // uncompressed
0381: if (log) {
0382: logError(fileName, e);
0383: }
0384: } finally {
0385: IOUtils.closeSilently(out);
0386: IOUtils.closeSilently(in);
0387: closeSilently(store);
0388: }
0389: if (size == 0) {
0390: try {
0391: FileUtils.delete(n);
0392: } catch (SQLException e) {
0393: logError(n, e);
0394: }
0395: }
0396: }
0397:
0398: private void writeLogRecord(PrintWriter writer, DataPage s) {
0399: try {
0400: recordLength = s.readInt();
0401: if (recordLength <= 0) {
0402: writeDataError(writer, "recordLength<0", s.getBytes(),
0403: blockCount);
0404: return;
0405: }
0406: Value[] data;
0407: try {
0408: data = new Value[recordLength];
0409: } catch (OutOfMemoryError e) {
0410: writeDataError(writer, "out of memory", s.getBytes(),
0411: blockCount);
0412: return;
0413: }
0414: StringBuffer sb = new StringBuffer();
0415: sb.append("// data: ");
0416: for (valueId = 0; valueId < recordLength; valueId++) {
0417: try {
0418: Value v = s.readValue();
0419: data[valueId] = v;
0420: if (valueId > 0) {
0421: sb.append(", ");
0422: }
0423: sb.append(getSQL(v));
0424: } catch (Exception e) {
0425: if (log) {
0426: logError("log data", e);
0427: }
0428: writeDataError(writer, "exception " + e, s
0429: .getBytes(), blockCount);
0430: continue;
0431: } catch (OutOfMemoryError e) {
0432: writeDataError(writer, "out of memory", s
0433: .getBytes(), blockCount);
0434: continue;
0435: }
0436: }
0437: writer.println(sb.toString());
0438: writer.flush();
0439: } catch (IOException e) {
0440: try {
0441: writeDataError(writer, "error: " + e.toString(), s
0442: .getBytes(), blockCount);
0443: } catch (IOException e2) {
0444: writeError(writer, e);
0445: }
0446: }
0447: }
0448:
0449: private String getSQL(Value v) {
0450: if (v instanceof ValueLob) {
0451: ValueLob lob = (ValueLob) v;
0452: byte[] small = lob.getSmall();
0453: if (small == null) {
0454: String file = lob.getFileName();
0455: if (lob.getType() == Value.BLOB) {
0456: return "READ_BLOB('" + file + ".txt')";
0457: } else {
0458: return "READ_CLOB('" + file + ".txt')";
0459: }
0460: }
0461: }
0462: return v.getSQL();
0463: }
0464:
0465: private void setDatabaseName(String name) {
0466: databaseName = name;
0467: lobFilesInDirectories = FileUtils.exists(databaseName
0468: + Constants.SUFFIX_LOBS_DIRECTORY);
0469: }
0470:
0471: private void dumpLog(String fileName) throws SQLException {
0472: PrintWriter writer = null;
0473: FileStore store = null;
0474: try {
0475: setDatabaseName(fileName.substring(fileName.length()
0476: - Constants.SUFFIX_LOG_FILE.length()));
0477: writer = getWriter(fileName, ".txt");
0478: textStorage = Database.isTextStorage(fileName, false);
0479: byte[] magic = Database.getMagic(textStorage);
0480: store = FileStore.open(null, fileName, "r", magic);
0481: long length = store.length();
0482: writer.println("// length: " + length);
0483: int offset = FileStore.HEADER_LENGTH;
0484: int blockSize = LogFile.BLOCK_SIZE;
0485: int blocks = (int) (length / blockSize);
0486: byte[] buff = new byte[blockSize];
0487: DataPage s = DataPage.create(this , buff);
0488: s.fill(3 * blockSize);
0489: int len = s.length();
0490: s.reset();
0491: if (length < FileStore.HEADER_LENGTH + len) {
0492: // this is an empty file
0493: writer.println("// empty file");
0494: return;
0495: }
0496: store.seek(offset);
0497: store.readFully(s.getBytes(), 0, len);
0498: int id = s.readInt();
0499: int firstUncommittedPos = s.readInt();
0500: int firstUnwrittenPos = s.readInt();
0501: writer.println("// id:" + id);
0502: writer.println("// firstUncommittedPos:"
0503: + firstUncommittedPos);
0504: writer.println("// firstUnwrittenPos:" + firstUnwrittenPos);
0505: int max = (int) (length / blockSize);
0506: writer.println("// max:" + max);
0507: while (true) {
0508: int pos = (int) (store.getFilePointer() / blockSize);
0509: if ((long) pos * blockSize >= length) {
0510: break;
0511: }
0512: buff = new byte[blockSize];
0513: store.readFully(buff, 0, blockSize);
0514: s = DataPage.create(this , buff);
0515: blocks = Math.abs(s.readInt());
0516: if (blocks > 1) {
0517: byte[] b2 = new byte[blocks * blockSize];
0518: System.arraycopy(buff, 0, b2, 0, blockSize);
0519: buff = b2;
0520: try {
0521: store.readFully(buff, blockSize, blocks
0522: * blockSize - blockSize);
0523: } catch (SQLException e) {
0524: break;
0525: }
0526: s = DataPage.create(this , buff);
0527: s.check(blocks * blockSize);
0528: }
0529: s.reset();
0530: blocks = Math.abs(s.readInt());
0531: if (blocks == 0) {
0532: writer.println("// [" + pos + "] blocks: " + blocks
0533: + " (end)");
0534: break;
0535: } else {
0536: char type = (char) s.readByte();
0537: int sessionId = s.readInt();
0538: if (type == 'P') {
0539: String transaction = s.readString();
0540: writer.println("// prepared session:"
0541: + sessionId + " tx:" + transaction);
0542: } else if (type == 'C') {
0543: writer.println("// commit session:"
0544: + sessionId);
0545: } else {
0546: int storageId = s.readInt();
0547: int recId = s.readInt();
0548: int blockCount = s.readInt();
0549: if (type != 'T') {
0550: s.readDataPageNoSize();
0551: }
0552: switch (type) {
0553: case 'S': {
0554: char fileType = (char) s.readByte();
0555: int sumLength = s.readInt();
0556: byte[] summary = new byte[sumLength];
0557: if (sumLength > 0) {
0558: s.read(summary, 0, sumLength);
0559: }
0560: writer.println("// summary session:"
0561: + sessionId + " fileType:"
0562: + fileType + " sumLength:"
0563: + sumLength);
0564: dumpSummary(writer, summary);
0565: break;
0566: }
0567: case 'T':
0568: writer.println("// truncate session:"
0569: + sessionId + " storage:"
0570: + storageId + " pos:" + recId
0571: + " blockCount:" + blockCount);
0572: break;
0573: case 'I':
0574: writer.println("// insert session:"
0575: + sessionId + " storage:"
0576: + storageId + " pos:" + recId
0577: + " blockCount:" + blockCount);
0578: writeLogRecord(writer, s);
0579: break;
0580: case 'D':
0581: writer.println("// delete session:"
0582: + sessionId + " storage:"
0583: + storageId + " pos:" + recId
0584: + " blockCount:" + blockCount);
0585: writeLogRecord(writer, s);
0586: break;
0587: default:
0588: writer.println("// type?:" + type
0589: + " session:" + sessionId
0590: + " storage:" + storageId + " pos:"
0591: + recId + " blockCount:"
0592: + blockCount);
0593: break;
0594: }
0595: }
0596: }
0597: }
0598: writer.close();
0599: } catch (Throwable e) {
0600: writeError(writer, e);
0601: } finally {
0602: IOUtils.closeSilently(writer);
0603: closeSilently(store);
0604: }
0605: }
0606:
0607: private void dumpSummary(PrintWriter writer, byte[] summary)
0608: throws SQLException {
0609: if (summary == null || summary.length == 0) {
0610: writer.println("// summary is empty");
0611: return;
0612: }
0613: try {
0614: DataInputStream in = new DataInputStream(
0615: new ByteArrayInputStream(summary));
0616: int b2 = in.readInt();
0617: for (int i = 0; i < b2 / 8; i++) {
0618: int x = in.read();
0619: if ((i % 8) == 0) {
0620: writer.print("// ");
0621: }
0622: writer.print(" " + Long.toString(i * 8) + ":");
0623: for (int j = 0; j < 8; j++) {
0624: writer.print(((x & 1) == 1) ? "1" : "0");
0625: x >>>= 1;
0626: }
0627: if ((i % 8) == 7) {
0628: writer.println("");
0629: }
0630: }
0631: writer.println("//");
0632: int len = in.readInt();
0633: for (int i = 0; i < len; i++) {
0634: int storageId = in.readInt();
0635: if (storageId != -1) {
0636: writer.println("// pos:"
0637: + (i * DiskFile.BLOCKS_PER_PAGE)
0638: + " storage:" + storageId);
0639: }
0640: }
0641: while (true) {
0642: int s = in.readInt();
0643: if (s < 0) {
0644: break;
0645: }
0646: int recordCount = in.readInt();
0647: writer.println("// storage:" + s + " recordCount:"
0648: + recordCount);
0649: }
0650: } catch (Throwable e) {
0651: writeError(writer, e);
0652: }
0653: }
0654:
0655: private void dumpIndex(String fileName) throws SQLException {
0656: PrintWriter writer = null;
0657: FileStore store = null;
0658: try {
0659: setDatabaseName(fileName.substring(fileName.length()
0660: - Constants.SUFFIX_INDEX_FILE.length()));
0661: writer = getWriter(fileName, ".txt");
0662: textStorage = Database.isTextStorage(fileName, false);
0663: byte[] magic = Database.getMagic(textStorage);
0664: store = FileStore.open(null, fileName, "r", magic);
0665: long length = store.length();
0666: int offset = FileStore.HEADER_LENGTH;
0667: int blockSize = DiskFile.BLOCK_SIZE;
0668: int blocks = (int) (length / blockSize);
0669: blockCount = 1;
0670: int[] pageOwners = new int[blocks
0671: / DiskFile.BLOCKS_PER_PAGE];
0672: for (int block = 0; block < blocks; block += blockCount) {
0673: store.seek(offset + (long) block * blockSize);
0674: byte[] buff = new byte[blockSize];
0675: DataPage s = DataPage.create(this , buff);
0676: store.readFully(buff, 0, blockSize);
0677: blockCount = s.readInt();
0678: storageId = -1;
0679: recordLength = -1;
0680: valueId = -1;
0681: if (blockCount == 0) {
0682: // free block
0683: blockCount = 1;
0684: continue;
0685: } else if (blockCount < 0) {
0686: writeDataError(writer, "blockCount<0",
0687: s.getBytes(), 1);
0688: blockCount = 1;
0689: continue;
0690: } else if ((blockCount * blockSize) >= Integer.MAX_VALUE / 4) {
0691: writeDataError(writer, "blockCount=" + blockCount,
0692: s.getBytes(), 1);
0693: blockCount = 1;
0694: continue;
0695: }
0696: try {
0697: s.checkCapacity(blockCount * blockSize);
0698: } catch (OutOfMemoryError e) {
0699: writeDataError(writer, "out of memory", s
0700: .getBytes(), 1);
0701: blockCount = 1;
0702: continue;
0703: }
0704: if (blockCount > 1) {
0705: store.readFully(s.getBytes(), blockSize, blockCount
0706: * blockSize - blockSize);
0707: }
0708: try {
0709: s.check(blockCount * blockSize);
0710: } catch (SQLException e) {
0711: writeDataError(writer, "wrong checksum", s
0712: .getBytes(), 1);
0713: blockCount = 1;
0714: continue;
0715: }
0716: storageId = s.readInt();
0717: if (storageId < 0) {
0718: writeDataError(writer, "storageId<0", s.getBytes(),
0719: blockCount);
0720: continue;
0721: }
0722: int page = block / DiskFile.BLOCKS_PER_PAGE;
0723: if (pageOwners[page] != 0
0724: && pageOwners[page] != storageId) {
0725: writeDataError(writer,
0726: "double allocation, previous="
0727: + pageOwners[page] + " now="
0728: + storageId, s.getBytes(),
0729: blockCount);
0730: } else {
0731: pageOwners[page] = storageId;
0732: }
0733: writer.println("// [" + block + "] page:" + page
0734: + " blocks:" + blockCount + " storage:"
0735: + storageId);
0736: }
0737: writer.close();
0738: } catch (Throwable e) {
0739: writeError(writer, e);
0740: } finally {
0741: IOUtils.closeSilently(writer);
0742: closeSilently(store);
0743: }
0744: }
0745:
0746: private void dumpData(String fileName) throws SQLException {
0747: PrintWriter writer = null;
0748: FileStore store = null;
0749: try {
0750: setDatabaseName(fileName.substring(0, fileName.length()
0751: - Constants.SUFFIX_DATA_FILE.length()));
0752: writer = getWriter(fileName, ".sql");
0753: writer
0754: .println("CREATE ALIAS IF NOT EXISTS READ_CLOB FOR \""
0755: + this .getClass().getName()
0756: + ".readClob\";");
0757: writer
0758: .println("CREATE ALIAS IF NOT EXISTS READ_BLOB FOR \""
0759: + this .getClass().getName()
0760: + ".readBlob\";");
0761: ObjectArray schema = new ObjectArray();
0762: HashSet objectIdSet = new HashSet();
0763: HashMap tableMap = new HashMap();
0764: textStorage = Database.isTextStorage(fileName, false);
0765: byte[] magic = Database.getMagic(textStorage);
0766: store = FileStore.open(null, fileName, "r", magic);
0767: long length = store.length();
0768: int offset = FileStore.HEADER_LENGTH;
0769: int blockSize = DiskFile.BLOCK_SIZE;
0770: int blocks = (int) (length / blockSize);
0771: blockCount = 1;
0772: int[] pageOwners = new int[blocks
0773: / DiskFile.BLOCKS_PER_PAGE];
0774: for (int block = 0; block < blocks; block += blockCount) {
0775: store.seek(offset + (long) block * blockSize);
0776: byte[] buff = new byte[blockSize];
0777: DataPage s = DataPage.create(this , buff);
0778: store.readFully(buff, 0, blockSize);
0779: blockCount = s.readInt();
0780: storageId = -1;
0781: recordLength = -1;
0782: valueId = -1;
0783: if (blockCount == 0) {
0784: // free block
0785: blockCount = 1;
0786: continue;
0787: } else if (blockCount < 0) {
0788: writeDataError(writer, "blockCount<0",
0789: s.getBytes(), 1);
0790: blockCount = 1;
0791: continue;
0792: } else if ((blockCount * blockSize) >= Integer.MAX_VALUE / 4) {
0793: writeDataError(writer, "blockCount=" + blockCount,
0794: s.getBytes(), 1);
0795: blockCount = 1;
0796: continue;
0797: }
0798: writer.println("-- block " + block + " - "
0799: + (block + blockCount - 1));
0800: try {
0801: s.checkCapacity(blockCount * blockSize);
0802: } catch (OutOfMemoryError e) {
0803: writeDataError(writer, "out of memory", s
0804: .getBytes(), 1);
0805: blockCount = 1;
0806: continue;
0807: }
0808: if (blockCount > 1) {
0809: if ((blockCount * blockSize) < 0) {
0810: writeDataError(writer, "wrong blockCount", s
0811: .getBytes(), 1);
0812: blockCount = 1;
0813: } else {
0814: store.readFully(s.getBytes(), blockSize,
0815: blockCount * blockSize - blockSize);
0816: }
0817: }
0818: try {
0819: s.check(blockCount * blockSize);
0820: } catch (SQLException e) {
0821: writeDataError(writer, "wrong checksum", s
0822: .getBytes(), 1);
0823: blockCount = 1;
0824: continue;
0825: }
0826: storageId = s.readInt();
0827: if (storageId < 0) {
0828: writeDataError(writer, "storageId<0", s.getBytes(),
0829: blockCount);
0830: continue;
0831: }
0832: int page = block / DiskFile.BLOCKS_PER_PAGE;
0833: if (pageOwners[page] != 0
0834: && pageOwners[page] != storageId) {
0835: writeDataError(writer,
0836: "double allocation, previous="
0837: + pageOwners[page] + " now="
0838: + storageId, s.getBytes(),
0839: blockCount);
0840: } else {
0841: pageOwners[page] = storageId;
0842: }
0843: recordLength = s.readInt();
0844: if (recordLength <= 0) {
0845: writeDataError(writer, "recordLength<0", s
0846: .getBytes(), blockCount);
0847: continue;
0848: }
0849: Value[] data;
0850: try {
0851: data = new Value[recordLength];
0852: } catch (OutOfMemoryError e) {
0853: writeDataError(writer, "out of memory", s
0854: .getBytes(), blockCount);
0855: continue;
0856: }
0857: if (!objectIdSet.contains(ObjectUtils
0858: .getInteger(storageId))) {
0859: objectIdSet.add(ObjectUtils.getInteger(storageId));
0860: StringBuffer sb = new StringBuffer();
0861: sb.append("CREATE TABLE O_" + storageId + "(");
0862: for (int i = 0; i < recordLength; i++) {
0863: if (i > 0) {
0864: sb.append(", ");
0865: }
0866: sb.append("C");
0867: sb.append(i);
0868: sb.append(" VARCHAR");
0869: }
0870: sb.append(");");
0871: writer.println(sb.toString());
0872: writer.flush();
0873: }
0874: StringBuffer sb = new StringBuffer();
0875: sb.append("INSERT INTO O_" + storageId + " VALUES(");
0876: for (valueId = 0; valueId < recordLength; valueId++) {
0877: try {
0878: Value v = s.readValue();
0879: data[valueId] = v;
0880: if (valueId > 0) {
0881: sb.append(", ");
0882: }
0883: sb.append(getSQL(v));
0884: } catch (Exception e) {
0885: writeDataError(writer, "exception " + e, s
0886: .getBytes(), blockCount);
0887: continue;
0888: } catch (OutOfMemoryError e) {
0889: writeDataError(writer, "out of memory", s
0890: .getBytes(), blockCount);
0891: continue;
0892: }
0893: }
0894: sb.append(");");
0895: writer.println(sb.toString());
0896: writer.flush();
0897: if (storageId == 0) {
0898: try {
0899: SimpleRow r = new SimpleRow(data);
0900: MetaRecord meta = new MetaRecord(r);
0901: schema.add(meta);
0902: if (meta.getObjectType() == DbObject.TABLE_OR_VIEW) {
0903: String sql = data[3].getString();
0904: int end = sql.indexOf('(');
0905: if (end >= 0) {
0906: int start = sql.lastIndexOf(' ', end);
0907: String name = sql.substring(start, end)
0908: .trim();
0909: tableMap
0910: .put(ObjectUtils
0911: .getInteger(meta
0912: .getId()), name);
0913: }
0914: }
0915: } catch (Throwable t) {
0916: writeError(writer, t);
0917: }
0918: }
0919: }
0920: MetaRecord.sort(schema);
0921: for (int i = 0; i < schema.size(); i++) {
0922: MetaRecord m = (MetaRecord) schema.get(i);
0923: writer.println(m.getSQL() + ";");
0924: }
0925: for (Iterator it = tableMap.entrySet().iterator(); it
0926: .hasNext();) {
0927: Map.Entry entry = (Entry) it.next();
0928: Integer objectId = (Integer) entry.getKey();
0929: String name = (String) entry.getValue();
0930: writer.println("INSERT INTO " + name
0931: + " SELECT * FROM O_" + objectId + ";");
0932: }
0933: for (Iterator it = objectIdSet.iterator(); it.hasNext();) {
0934: Integer objectId = (Integer) it.next();
0935: writer.println("DROP TABLE O_" + objectId + ";");
0936: }
0937: writer.println("DROP ALIAS READ_CLOB;");
0938: writer.println("DROP ALIAS READ_BLOB;");
0939: writer.close();
0940: } catch (Throwable e) {
0941: writeError(writer, e);
0942: } finally {
0943: IOUtils.closeSilently(writer);
0944: closeSilently(store);
0945: }
0946: }
0947:
0948: private void closeSilently(FileStore store) {
0949: if (store != null) {
0950: store.closeSilently();
0951: store = null;
0952: }
0953: }
0954:
0955: private void writeError(PrintWriter writer, Throwable e) {
0956: if (writer != null) {
0957: writer.println("// error: " + e);
0958: }
0959: logError("Error", e);
0960: }
0961:
0962: /**
0963: * INTERNAL
0964: */
0965: public boolean getTextStorage() {
0966: return textStorage;
0967: }
0968:
0969: /**
0970: * INTERNAL
0971: */
0972: public String getDatabasePath() {
0973: return databaseName;
0974: }
0975:
0976: /**
0977: * INTERNAL
0978: */
0979: public FileStore openFile(String name, String mode,
0980: boolean mustExist) throws SQLException {
0981: return FileStore.open(this , name, "rw",
0982: Constants.MAGIC_FILE_HEADER.getBytes());
0983: }
0984:
0985: /**
0986: * INTERNAL
0987: */
0988: public int getChecksum(byte[] data, int start, int end) {
0989: int x = 0;
0990: while (start < end) {
0991: x += data[start++];
0992: }
0993: return x;
0994: }
0995:
0996: /**
0997: * INTERNAL
0998: */
0999: public void checkPowerOff() throws SQLException {
1000: }
1001:
1002: /**
1003: * INTERNAL
1004: */
1005: public void checkWritingAllowed() throws SQLException {
1006: }
1007:
1008: /**
1009: * INTERNAL
1010: */
1011: public void freeUpDiskSpace() throws SQLException {
1012: }
1013:
1014: /**
1015: * INTERNAL
1016: */
1017: public void handleInvalidChecksum() throws SQLException {
1018: throw new SQLException("Invalid Checksum");
1019: }
1020:
1021: /**
1022: * INTERNAL
1023: */
1024: public int compareTypeSave(Value a, Value b) throws SQLException {
1025: throw Message.getInternalError();
1026: }
1027:
1028: /**
1029: * INTERNAL
1030: */
1031: public int getMaxLengthInplaceLob() {
1032: throw Message.getInternalError();
1033: }
1034:
1035: /**
1036: * INTERNAL
1037: */
1038: public int allocateObjectId(boolean b, boolean c) {
1039: throw Message.getInternalError();
1040: }
1041:
1042: /**
1043: * INTERNAL
1044: */
1045: public String createTempFile() throws SQLException {
1046: throw Message.getInternalError();
1047: }
1048:
1049: /**
1050: * INTERNAL
1051: */
1052: public String getLobCompressionAlgorithm(int type) {
1053: return null;
1054: }
1055:
1056: /**
1057: * INTERNAL
1058: */
1059: public Object getLobSyncObject() {
1060: return this ;
1061: }
1062:
1063: /**
1064: * INTERNAL
1065: */
1066: public boolean getLobFilesInDirectories() {
1067: return lobFilesInDirectories;
1068: }
1069:
1070: /**
1071: * INTERNAL
1072: */
1073: public SmallLRUCache getLobFileListCache() {
1074: return null;
1075: }
1076:
1077: }
|