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.engine;
0007:
0008: import java.io.IOException;
0009: import java.io.InputStream;
0010: import java.sql.SQLException;
0011: import java.util.Collections;
0012: import java.util.HashMap;
0013: import java.util.HashSet;
0014: import java.util.Iterator;
0015: import java.util.Set;
0016: import java.util.StringTokenizer;
0017:
0018: import org.h2.api.DatabaseEventListener;
0019: import org.h2.command.dml.SetTypes;
0020: import org.h2.constant.ErrorCode;
0021: import org.h2.constant.SysProperties;
0022: import org.h2.index.Cursor;
0023: import org.h2.index.Index;
0024: import org.h2.index.IndexType;
0025: import org.h2.log.LogSystem;
0026: import org.h2.log.UndoLogRecord;
0027: import org.h2.message.Message;
0028: import org.h2.message.Trace;
0029: import org.h2.message.TraceSystem;
0030: import org.h2.result.Row;
0031: import org.h2.result.SearchRow;
0032: import org.h2.schema.Schema;
0033: import org.h2.schema.SchemaObject;
0034: import org.h2.schema.Sequence;
0035: import org.h2.store.DataHandler;
0036: import org.h2.store.DataPage;
0037: import org.h2.store.DiskFile;
0038: import org.h2.store.FileLock;
0039: import org.h2.store.FileStore;
0040: import org.h2.store.RecordReader;
0041: import org.h2.store.Storage;
0042: import org.h2.store.WriterThread;
0043: import org.h2.store.fs.FileSystem;
0044: import org.h2.table.Column;
0045: import org.h2.table.IndexColumn;
0046: import org.h2.table.MetaTable;
0047: import org.h2.table.Table;
0048: import org.h2.table.TableData;
0049: import org.h2.table.TableView;
0050: import org.h2.tools.DeleteDbFiles;
0051: import org.h2.util.BitField;
0052: import org.h2.util.ByteUtils;
0053: import org.h2.util.CacheLRU;
0054: import org.h2.util.ClassUtils;
0055: import org.h2.util.FileUtils;
0056: import org.h2.util.IOUtils;
0057: import org.h2.util.IntHashMap;
0058: import org.h2.util.ObjectArray;
0059: import org.h2.util.SmallLRUCache;
0060: import org.h2.util.StringUtils;
0061: import org.h2.value.CompareMode;
0062: import org.h2.value.Value;
0063: import org.h2.value.ValueInt;
0064: import org.h2.value.ValueLob;
0065:
0066: /**
0067: * There is one database object per open database.
0068: *
0069: * The format of the meta data table is:
0070: * id int, headPos int (for indexes), objectType int, sql varchar
0071: *
0072: * @since 2004-04-15 22:49
0073: */
0074: public class Database implements DataHandler {
0075:
0076: private final boolean persistent;
0077: private final String databaseName;
0078: private final String databaseShortName;
0079: private final String databaseURL;
0080: private final String cipher;
0081: private final byte[] filePasswordHash;
0082:
0083: private final HashMap roles = new HashMap();
0084: private final HashMap users = new HashMap();
0085: private final HashMap settings = new HashMap();
0086: private final HashMap schemas = new HashMap();
0087: private final HashMap rights = new HashMap();
0088: private final HashMap functionAliases = new HashMap();
0089: private final HashMap userDataTypes = new HashMap();
0090: private final HashMap aggregates = new HashMap();
0091: private final HashMap comments = new HashMap();
0092: private final Set sessions = Collections
0093: .synchronizedSet(new HashSet());
0094: private Session exclusiveSession;
0095: private final BitField objectIds = new BitField();
0096: private final Object lobSyncObject = new Object();
0097:
0098: private boolean textStorage;
0099: private Schema mainSchema;
0100: private Schema infoSchema;
0101: private int nextSessionId;
0102: private User systemUser;
0103: private Session systemSession;
0104: private TableData meta;
0105: private Index metaIdIndex;
0106: private FileLock lock;
0107: private LogSystem log;
0108: private WriterThread writer;
0109: private IntHashMap storageMap = new IntHashMap();
0110: private boolean starting;
0111: private DiskFile fileData, fileIndex;
0112: private TraceSystem traceSystem;
0113: private DataPage dummy;
0114: private int fileLockMethod;
0115: private Role publicRole;
0116: private long modificationDataId;
0117: private long modificationMetaId;
0118: private CompareMode compareMode;
0119: private String cluster = Constants.CLUSTERING_DISABLED;
0120: private boolean readOnly;
0121: private boolean noDiskSpace;
0122: private int writeDelay = Constants.DEFAULT_WRITE_DELAY;
0123: private DatabaseEventListener eventListener;
0124: private FileStore emergencyReserve;
0125: private int maxMemoryRows = Constants.DEFAULT_MAX_MEMORY_ROWS;
0126: private int maxMemoryUndo = SysProperties.DEFAULT_MAX_MEMORY_UNDO;
0127: private int lockMode = SysProperties.DEFAULT_LOCK_MODE;
0128: private boolean logIndexChanges;
0129: private int logLevel = 1;
0130: private int cacheSize;
0131: private int maxLengthInplaceLob = Constants.DEFAULT_MAX_LENGTH_INPLACE_LOB;
0132: private long biggestFileSize;
0133: private int allowLiterals = Constants.DEFAULT_ALLOW_LITERALS;
0134:
0135: private static int initialPowerOffCount;
0136: private int powerOffCount = initialPowerOffCount;
0137: private int closeDelay;
0138: private DatabaseCloser delayedCloser;
0139: private boolean recovery;
0140: private volatile boolean closing;
0141: private boolean ignoreCase;
0142: private boolean deleteFilesOnDisconnect;
0143: private String lobCompressionAlgorithm;
0144: private boolean optimizeReuseResults = true;
0145: private String cacheType;
0146: private boolean indexSummaryValid = true;
0147: private String accessModeLog, accessModeData;
0148: private boolean referentialIntegrity = true;
0149: private boolean multiVersion;
0150: private DatabaseCloser closeOnExit;
0151: private Mode mode = Mode.getInstance(Mode.REGULAR);
0152: // TODO change in version 1.1
0153: private boolean multiThreaded;
0154: private int maxOperationMemory = SysProperties.DEFAULT_MAX_OPERATION_MEMORY;
0155: private boolean lobFilesInDirectories = SysProperties.LOB_FILES_IN_DIRECTORIES;
0156: private SmallLRUCache lobFileListCache = new SmallLRUCache(128);
0157:
0158: public Database(String name, ConnectionInfo ci, String cipher)
0159: throws SQLException {
0160: this .compareMode = new CompareMode(null, null, 0);
0161: this .persistent = ci.isPersistent();
0162: this .filePasswordHash = ci.getFilePasswordHash();
0163: this .databaseName = name;
0164: this .databaseShortName = parseDatabaseShortName();
0165: this .cipher = cipher;
0166: String lockMethodName = ci.removeProperty("FILE_LOCK", null);
0167: this .accessModeLog = ci.removeProperty("ACCESS_MODE_LOG", "rw")
0168: .toLowerCase();
0169: this .accessModeData = ci.removeProperty("ACCESS_MODE_DATA",
0170: "rw").toLowerCase();
0171: if ("r".equals(accessModeData)) {
0172: readOnly = true;
0173: accessModeLog = "r";
0174: }
0175: this .fileLockMethod = FileLock
0176: .getFileLockMethod(lockMethodName);
0177: this .textStorage = ci.getTextStorage();
0178: this .databaseURL = ci.getURL();
0179: this .eventListener = ci.removeDatabaseEventListenerObject();
0180: if (eventListener == null) {
0181: String listener = ci.removeProperty(
0182: "DATABASE_EVENT_LISTENER", null);
0183: if (listener != null) {
0184: if (listener.startsWith("'")) {
0185: listener = listener.substring(1);
0186: }
0187: if (listener.endsWith("'")) {
0188: listener = listener.substring(0,
0189: listener.length() - 1);
0190: }
0191: setEventListener(listener);
0192: }
0193: }
0194: String log = ci.getProperty(SetTypes.LOG, null);
0195: if (log != null) {
0196: this .logIndexChanges = "2".equals(log);
0197: }
0198: String ignoreSummary = ci.getProperty("RECOVER", null);
0199: if (ignoreSummary != null) {
0200: this .recovery = true;
0201: }
0202: this .multiVersion = ci.removeProperty("MVCC", false);
0203: boolean closeAtVmShutdown = ci.removeProperty(
0204: "DB_CLOSE_ON_EXIT", true);
0205: int traceLevelFile = ci.getIntProperty(
0206: SetTypes.TRACE_LEVEL_FILE,
0207: TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
0208: int traceLevelSystemOut = ci.getIntProperty(
0209: SetTypes.TRACE_LEVEL_SYSTEM_OUT,
0210: TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
0211: this .cacheType = StringUtils.toUpperEnglish(ci.removeProperty(
0212: "CACHE_TYPE", CacheLRU.TYPE_NAME));
0213: try {
0214: open(traceLevelFile, traceLevelSystemOut);
0215: if (closeAtVmShutdown) {
0216: closeOnExit = new DatabaseCloser(this , 0, true);
0217: try {
0218: Runtime.getRuntime().addShutdownHook(closeOnExit);
0219: } catch (IllegalStateException e) {
0220: // shutdown in progress - just don't register the handler
0221: // (maybe an application wants to write something into a
0222: // database at shutdown time)
0223: } catch (SecurityException e) {
0224: // applets may not do that - ignore
0225: }
0226: }
0227: } catch (Throwable e) {
0228: if (traceSystem != null) {
0229: traceSystem.getTrace(Trace.DATABASE).error(
0230: "opening " + databaseName, e);
0231: traceSystem.close();
0232: }
0233: closeOpenFilesAndUnlock();
0234: throw Message.convert(e);
0235: }
0236: }
0237:
0238: public static void setInitialPowerOffCount(int count) {
0239: initialPowerOffCount = count;
0240: }
0241:
0242: public void setPowerOffCount(int count) {
0243: if (powerOffCount == -1) {
0244: return;
0245: }
0246: powerOffCount = count;
0247: }
0248:
0249: public boolean getTextStorage() {
0250: return textStorage;
0251: }
0252:
0253: public static boolean isTextStorage(String fileName,
0254: boolean defaultValue) throws SQLException {
0255: byte[] magicText = Constants.MAGIC_FILE_HEADER_TEXT.getBytes();
0256: byte[] magicBinary = Constants.MAGIC_FILE_HEADER.getBytes();
0257: try {
0258: InputStream fin = FileUtils.openFileInputStream(fileName);
0259: byte[] magic = IOUtils.readBytesAndClose(fin,
0260: magicBinary.length);
0261: if (ByteUtils.compareNotNull(magic, magicText) == 0) {
0262: return true;
0263: } else if (ByteUtils.compareNotNull(magic, magicBinary) == 0) {
0264: return false;
0265: } else if (magic.length < magicText.length) {
0266: // file size is 0 or too small
0267: return defaultValue;
0268: }
0269: throw Message.getSQLException(
0270: ErrorCode.FILE_VERSION_ERROR_1, fileName);
0271: } catch (IOException e) {
0272: throw Message.convertIOException(e, fileName);
0273: }
0274: }
0275:
0276: public static byte[] getMagic(boolean textStorage) {
0277: if (textStorage) {
0278: return Constants.MAGIC_FILE_HEADER_TEXT.getBytes();
0279: } else {
0280: return Constants.MAGIC_FILE_HEADER.getBytes();
0281: }
0282: }
0283:
0284: public byte[] getMagic() {
0285: return getMagic(textStorage);
0286: }
0287:
0288: public boolean areEqual(Value a, Value b) throws SQLException {
0289: // TODO optimization possible
0290: // boolean is = a.compareEqual(b);
0291: // boolean is2 = a.compareTo(b, compareMode) == 0;
0292: // if(is != is2) {
0293: // is = a.compareEqual(b);
0294: // System.out.println("hey!");
0295: // }
0296: // return a.compareEqual(b);
0297: return a.compareTo(b, compareMode) == 0;
0298: }
0299:
0300: public int compare(Value a, Value b) throws SQLException {
0301: return a.compareTo(b, compareMode);
0302: }
0303:
0304: public int compareTypeSave(Value a, Value b) throws SQLException {
0305: return a.compareTypeSave(b, compareMode);
0306: }
0307:
0308: public long getModificationDataId() {
0309: return modificationDataId;
0310: }
0311:
0312: public long getNextModificationDataId() {
0313: return ++modificationDataId;
0314: }
0315:
0316: public long getModificationMetaId() {
0317: return modificationMetaId;
0318: }
0319:
0320: public long getNextModificationMetaId() {
0321: // if the meta data has been modified, the data is modified as well
0322: // (because MetaTable returns modificationDataId)
0323: modificationDataId++;
0324: return modificationMetaId++;
0325: }
0326:
0327: public int getPowerOffCount() {
0328: return powerOffCount;
0329: }
0330:
0331: public void checkPowerOff() throws SQLException {
0332: if (powerOffCount == 0) {
0333: return;
0334: }
0335: if (powerOffCount > 1) {
0336: powerOffCount--;
0337: return;
0338: }
0339: if (powerOffCount != -1) {
0340: try {
0341: powerOffCount = -1;
0342: if (log != null) {
0343: try {
0344: stopWriter();
0345: log.close();
0346: } catch (SQLException e) {
0347: // ignore
0348: }
0349: log = null;
0350: }
0351: if (fileData != null) {
0352: try {
0353: fileData.close();
0354: } catch (SQLException e) {
0355: // ignore
0356: }
0357: fileData = null;
0358: }
0359: if (fileIndex != null) {
0360: try {
0361: fileIndex.close();
0362: } catch (SQLException e) {
0363: // ignore
0364: }
0365: fileIndex = null;
0366: }
0367: if (lock != null) {
0368: lock.unlock();
0369: lock = null;
0370: }
0371: if (emergencyReserve != null) {
0372: emergencyReserve.closeAndDeleteSilently();
0373: emergencyReserve = null;
0374: }
0375: } catch (Exception e) {
0376: TraceSystem.traceThrowable(e);
0377: }
0378: }
0379: Engine.getInstance().close(databaseName);
0380: throw Message.getSQLException(ErrorCode.SIMULATED_POWER_OFF);
0381: }
0382:
0383: public static boolean exists(String name) {
0384: return FileUtils.exists(name + Constants.SUFFIX_DATA_FILE);
0385: }
0386:
0387: public Trace getTrace(String module) {
0388: return traceSystem.getTrace(module);
0389: }
0390:
0391: public FileStore openFile(String name, String mode,
0392: boolean mustExist) throws SQLException {
0393: if (mustExist && !FileUtils.exists(name)) {
0394: throw Message.getSQLException(ErrorCode.FILE_NOT_FOUND_1,
0395: name);
0396: }
0397: FileStore store = FileStore.open(this , name, mode, getMagic(),
0398: cipher, filePasswordHash);
0399: try {
0400: store.init();
0401: } catch (SQLException e) {
0402: store.closeSilently();
0403: throw e;
0404: }
0405: return store;
0406: }
0407:
0408: public void checkFilePasswordHash(String c, byte[] hash)
0409: throws SQLException {
0410: if (!ByteUtils.compareSecure(hash, filePasswordHash)
0411: || !StringUtils.equals(c, cipher)) {
0412: try {
0413: Thread.sleep(Constants.DELAY_WRONG_PASSWORD);
0414: } catch (InterruptedException e) {
0415: // ignore
0416: }
0417: throw Message
0418: .getSQLException(ErrorCode.WRONG_USER_OR_PASSWORD);
0419: }
0420: }
0421:
0422: private void openFileData() throws SQLException {
0423: fileData = new DiskFile(this , databaseName
0424: + Constants.SUFFIX_DATA_FILE, accessModeData, true,
0425: true, SysProperties.CACHE_SIZE_DEFAULT);
0426: }
0427:
0428: private void openFileIndex() throws SQLException {
0429: fileIndex = new DiskFile(this , databaseName
0430: + Constants.SUFFIX_INDEX_FILE, accessModeData, false,
0431: logIndexChanges, SysProperties.CACHE_SIZE_INDEX_DEFAULT);
0432: }
0433:
0434: public DataPage getDataPage() {
0435: return dummy;
0436: }
0437:
0438: private String parseDatabaseShortName() {
0439: String n = databaseName;
0440: if (n.endsWith(":")) {
0441: n = null;
0442: }
0443: if (n != null) {
0444: StringTokenizer tokenizer = new StringTokenizer(n, "/\\:,;");
0445: while (tokenizer.hasMoreTokens()) {
0446: n = tokenizer.nextToken();
0447: }
0448: }
0449: if (n == null || n.length() == 0) {
0450: n = "UNNAMED";
0451: }
0452: return StringUtils.toUpperEnglish(n);
0453: }
0454:
0455: private synchronized void open(int traceLevelFile,
0456: int traceLevelSystemOut) throws SQLException {
0457: if (persistent) {
0458: String dataFileName = databaseName
0459: + Constants.SUFFIX_DATA_FILE;
0460: if (FileUtils.exists(dataFileName)) {
0461: // if it is already read-only because ACCESS_MODE_DATA=r
0462: readOnly = readOnly
0463: | FileUtils.isReadOnly(dataFileName);
0464: textStorage = isTextStorage(dataFileName, textStorage);
0465: lobFilesInDirectories |= FileUtils.exists(databaseName
0466: + Constants.SUFFIX_LOBS_DIRECTORY);
0467: }
0468: }
0469: dummy = DataPage.create(this , 0);
0470: if (persistent) {
0471: if (readOnly) {
0472: traceSystem = new TraceSystem(null, false);
0473: } else {
0474: traceSystem = new TraceSystem(databaseName
0475: + Constants.SUFFIX_TRACE_FILE, true);
0476: }
0477: if (cipher != null) {
0478: traceSystem.setManualEnabling(false);
0479: }
0480: traceSystem.setLevelFile(traceLevelFile);
0481: traceSystem.setLevelSystemOut(traceLevelSystemOut);
0482: traceSystem.getTrace(Trace.DATABASE).info(
0483: "opening " + databaseName + " (build "
0484: + Constants.BUILD_ID + ")");
0485: if (!readOnly && fileLockMethod != FileLock.LOCK_NO) {
0486: lock = new FileLock(traceSystem, Constants.LOCK_SLEEP);
0487: lock.lock(databaseName + Constants.SUFFIX_LOCK_FILE,
0488: fileLockMethod == FileLock.LOCK_SOCKET);
0489: }
0490: deleteOldTempFiles();
0491: log = new LogSystem(this , databaseName, readOnly,
0492: accessModeLog);
0493: openFileData();
0494: log.open();
0495: openFileIndex();
0496: log.recover();
0497: fileData.init();
0498: try {
0499: fileIndex.init();
0500: } catch (Throwable e) {
0501: if (recovery) {
0502: traceSystem.getTrace(Trace.DATABASE).error(
0503: "opening index", e);
0504: fileIndex.close();
0505: fileIndex.delete();
0506: openFileIndex();
0507: } else {
0508: throw Message.convert(e);
0509: }
0510: }
0511: reserveLobFileObjectIds();
0512: writer = WriterThread.create(this , writeDelay);
0513: } else {
0514: traceSystem = new TraceSystem(null, false);
0515: log = new LogSystem(null, null, false, null);
0516: }
0517: systemUser = new User(this , 0, Constants.DBA_NAME, true);
0518: mainSchema = new Schema(this , 0, Constants.SCHEMA_MAIN,
0519: systemUser, true);
0520: infoSchema = new Schema(this , -1, Constants.SCHEMA_INFORMATION,
0521: systemUser, true);
0522: schemas.put(mainSchema.getName(), mainSchema);
0523: schemas.put(infoSchema.getName(), infoSchema);
0524: publicRole = new Role(this , 0, Constants.PUBLIC_ROLE_NAME, true);
0525: roles.put(Constants.PUBLIC_ROLE_NAME, publicRole);
0526: systemUser.setAdmin(true);
0527: systemSession = new Session(this , systemUser, ++nextSessionId);
0528: ObjectArray cols = new ObjectArray();
0529: Column columnId = new Column("ID", Value.INT);
0530: columnId.setNullable(false);
0531: cols.add(columnId);
0532: cols.add(new Column("HEAD", Value.INT));
0533: cols.add(new Column("TYPE", Value.INT));
0534: cols.add(new Column("SQL", Value.STRING));
0535: meta = mainSchema
0536: .createTable("SYS", 0, cols, persistent, false);
0537: IndexColumn[] pkCols = IndexColumn
0538: .wrap(new Column[] { columnId });
0539: metaIdIndex = meta.addIndex(systemSession, "SYS_ID", 0, pkCols,
0540: IndexType.createPrimaryKey(false, false),
0541: Index.EMPTY_HEAD, null);
0542: objectIds.set(0);
0543: // there could be views on system tables, so they must be added first
0544: addMetaData(MetaTable.TABLES);
0545: addMetaData(MetaTable.COLUMNS);
0546: addMetaData(MetaTable.INDEXES);
0547: addMetaData(MetaTable.TABLE_TYPES);
0548: addMetaData(MetaTable.TYPE_INFO);
0549: addMetaData(MetaTable.CATALOGS);
0550: addMetaData(MetaTable.SETTINGS);
0551: addMetaData(MetaTable.HELP);
0552: addMetaData(MetaTable.SEQUENCES);
0553: addMetaData(MetaTable.USERS);
0554: addMetaData(MetaTable.ROLES);
0555: addMetaData(MetaTable.RIGHTS);
0556: addMetaData(MetaTable.FUNCTION_ALIASES);
0557: addMetaData(MetaTable.SCHEMATA);
0558: addMetaData(MetaTable.TABLE_PRIVILEGES);
0559: addMetaData(MetaTable.COLUMN_PRIVILEGES);
0560: addMetaData(MetaTable.COLLATIONS);
0561: addMetaData(MetaTable.VIEWS);
0562: addMetaData(MetaTable.IN_DOUBT);
0563: addMetaData(MetaTable.CROSS_REFERENCES);
0564: addMetaData(MetaTable.CONSTRAINTS);
0565: addMetaData(MetaTable.FUNCTION_COLUMNS);
0566: addMetaData(MetaTable.CONSTANTS);
0567: addMetaData(MetaTable.DOMAINS);
0568: addMetaData(MetaTable.TRIGGERS);
0569: addMetaData(MetaTable.SESSIONS);
0570: addMetaData(MetaTable.LOCKS);
0571: starting = true;
0572: Cursor cursor = metaIdIndex.find(systemSession, null, null);
0573: // first, create all function aliases and sequences because
0574: // they might be used in create table / view / constraints and so on
0575: ObjectArray records = new ObjectArray();
0576: while (cursor.next()) {
0577: MetaRecord rec = new MetaRecord(cursor.get());
0578: objectIds.set(rec.getId());
0579: records.add(rec);
0580: }
0581: MetaRecord.sort(records);
0582: for (int i = 0; i < records.size(); i++) {
0583: MetaRecord rec = (MetaRecord) records.get(i);
0584: rec.execute(this , systemSession, eventListener);
0585: }
0586: // try to recompile the views that are invalid
0587: recompileInvalidViews(systemSession);
0588: starting = false;
0589: addDefaultSetting(systemSession, SetTypes.DEFAULT_LOCK_TIMEOUT,
0590: null, Constants.INITIAL_LOCK_TIMEOUT);
0591: addDefaultSetting(systemSession, SetTypes.DEFAULT_TABLE_TYPE,
0592: null, Constants.DEFAULT_TABLE_TYPE);
0593: addDefaultSetting(systemSession, SetTypes.TRACE_LEVEL_FILE,
0594: null, traceSystem.getLevelFile());
0595: addDefaultSetting(systemSession,
0596: SetTypes.TRACE_LEVEL_SYSTEM_OUT, null, traceSystem
0597: .getLevelSystemOut());
0598: addDefaultSetting(systemSession, SetTypes.CACHE_SIZE, null,
0599: SysProperties.CACHE_SIZE_DEFAULT);
0600: addDefaultSetting(systemSession, SetTypes.CLUSTER,
0601: Constants.CLUSTERING_DISABLED, 0);
0602: addDefaultSetting(systemSession, SetTypes.WRITE_DELAY, null,
0603: Constants.DEFAULT_WRITE_DELAY);
0604: addDefaultSetting(systemSession, SetTypes.CREATE_BUILD, null,
0605: Constants.BUILD_ID);
0606: if (!readOnly) {
0607: removeUnusedStorages(systemSession);
0608: }
0609: systemSession.commit(true);
0610: if (!readOnly && persistent) {
0611: emergencyReserve = openFile(createTempFile(), "rw", false);
0612: emergencyReserve.autoDelete();
0613: emergencyReserve
0614: .setLength(SysProperties.EMERGENCY_SPACE_INITIAL);
0615: }
0616: traceSystem.getTrace(Trace.DATABASE).info(
0617: "opened " + databaseName);
0618: }
0619:
0620: private void recompileInvalidViews(Session session) {
0621: boolean recompileSuccessful;
0622: do {
0623: recompileSuccessful = false;
0624: ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
0625: for (int i = 0; i < list.size(); i++) {
0626: DbObject obj = (DbObject) list.get(i);
0627: if (obj instanceof TableView) {
0628: TableView view = (TableView) obj;
0629: if (view.getInvalid()) {
0630: try {
0631: view.recompile(session);
0632: } catch (Throwable e) {
0633: // ignore
0634: }
0635: if (!view.getInvalid()) {
0636: recompileSuccessful = true;
0637: }
0638: }
0639: }
0640: }
0641: } while (recompileSuccessful);
0642: // when opening a database, views are initialized before indexes,
0643: // so they may not have the optimal plan yet
0644: // this is not a problem, it is just nice to see the newest plan
0645: ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
0646: for (int i = 0; i < list.size(); i++) {
0647: DbObject obj = (DbObject) list.get(i);
0648: if (obj instanceof TableView) {
0649: TableView view = (TableView) obj;
0650: if (!view.getInvalid()) {
0651: try {
0652: view.recompile(systemSession);
0653: } catch (SQLException e) {
0654: // ignore
0655: }
0656: }
0657: }
0658: }
0659: }
0660:
0661: private void removeUnusedStorages(Session session)
0662: throws SQLException {
0663: if (persistent) {
0664: ObjectArray storages = getAllStorages();
0665: for (int i = 0; i < storages.size(); i++) {
0666: Storage storage = (Storage) storages.get(i);
0667: if (storage != null
0668: && storage.getRecordReader() == null) {
0669: storage.delete(session);
0670: }
0671: }
0672: }
0673: }
0674:
0675: private void addDefaultSetting(Session session, int type,
0676: String stringValue, int intValue) throws SQLException {
0677: if (readOnly) {
0678: return;
0679: }
0680: String name = SetTypes.getTypeName(type);
0681: if (settings.get(name) == null) {
0682: Setting setting = new Setting(this , allocateObjectId(false,
0683: true), name);
0684: if (stringValue == null) {
0685: setting.setIntValue(intValue);
0686: } else {
0687: setting.setStringValue(stringValue);
0688: }
0689: addDatabaseObject(session, setting);
0690: }
0691: }
0692:
0693: public void removeStorage(int id, DiskFile file) {
0694: if (SysProperties.CHECK) {
0695: Storage s = (Storage) storageMap.get(id);
0696: if (s == null || s.getDiskFile() != file) {
0697: throw Message.getInternalError();
0698: }
0699: }
0700: storageMap.remove(id);
0701: }
0702:
0703: public Storage getStorage(int id, DiskFile file) {
0704: Storage storage = (Storage) storageMap.get(id);
0705: if (storage != null) {
0706: if (SysProperties.CHECK && storage.getDiskFile() != file) {
0707: throw Message.getInternalError();
0708: }
0709: } else {
0710: storage = new Storage(this , file, null, id);
0711: storageMap.put(id, storage);
0712: }
0713: return storage;
0714: }
0715:
0716: private void addMetaData(int type) throws SQLException {
0717: MetaTable m = new MetaTable(infoSchema, -1 - type, type);
0718: infoSchema.add(m);
0719: }
0720:
0721: private synchronized void addMeta(Session session, DbObject obj)
0722: throws SQLException {
0723: if (obj.getTemporary()) {
0724: return;
0725: }
0726: Row r = meta.getTemplateRow();
0727: MetaRecord rec = new MetaRecord(obj);
0728: rec.setRecord(r);
0729: objectIds.set(obj.getId());
0730: meta.lock(session, true, true);
0731: meta.addRow(session, r);
0732: if (isMultiVersion()) {
0733: // TODO this should work without MVCC, but avoid risks at the moment
0734: session.log(meta, UndoLogRecord.INSERT, r);
0735: }
0736: }
0737:
0738: public synchronized void removeMeta(Session session, int id)
0739: throws SQLException {
0740: SearchRow r = meta.getTemplateSimpleRow(false);
0741: r.setValue(0, ValueInt.get(id));
0742: Cursor cursor = metaIdIndex.find(session, r, r);
0743: if (cursor.next()) {
0744: Row found = cursor.get();
0745: meta.lock(session, true, true);
0746: meta.removeRow(session, found);
0747: if (isMultiVersion()) {
0748: // TODO this should work without MVCC, but avoid risks at the
0749: // moment
0750: session.log(meta, UndoLogRecord.DELETE, found);
0751: }
0752: objectIds.clear(id);
0753: if (SysProperties.CHECK) {
0754: checkMetaFree(session, id);
0755: }
0756: }
0757: }
0758:
0759: private HashMap getMap(int type) {
0760: switch (type) {
0761: case DbObject.USER:
0762: return users;
0763: case DbObject.SETTING:
0764: return settings;
0765: case DbObject.ROLE:
0766: return roles;
0767: case DbObject.RIGHT:
0768: return rights;
0769: case DbObject.FUNCTION_ALIAS:
0770: return functionAliases;
0771: case DbObject.SCHEMA:
0772: return schemas;
0773: case DbObject.USER_DATATYPE:
0774: return userDataTypes;
0775: case DbObject.COMMENT:
0776: return comments;
0777: case DbObject.AGGREGATE:
0778: return aggregates;
0779: default:
0780: throw Message.getInternalError("type=" + type);
0781: }
0782: }
0783:
0784: public synchronized void addSchemaObject(Session session,
0785: SchemaObject obj) throws SQLException {
0786: obj.getSchema().add(obj);
0787: int id = obj.getId();
0788: if (id > 0 && !starting) {
0789: addMeta(session, obj);
0790: }
0791: }
0792:
0793: public synchronized void addDatabaseObject(Session session,
0794: DbObject obj) throws SQLException {
0795: HashMap map = getMap(obj.getType());
0796: if (obj.getType() == DbObject.USER) {
0797: User user = (User) obj;
0798: if (user.getAdmin()
0799: && systemUser.getName().equals(Constants.DBA_NAME)) {
0800: systemUser.rename(user.getName());
0801: }
0802: }
0803: String name = obj.getName();
0804: if (SysProperties.CHECK && map.get(name) != null) {
0805: throw Message.getInternalError("object already exists");
0806: }
0807: int id = obj.getId();
0808: if (id > 0 && !starting) {
0809: addMeta(session, obj);
0810: }
0811: map.put(name, obj);
0812: }
0813:
0814: public Setting findSetting(String name) {
0815: return (Setting) settings.get(name);
0816: }
0817:
0818: public Comment findComment(DbObject object) {
0819: if (object.getType() == DbObject.COMMENT) {
0820: return null;
0821: }
0822: String key = Comment.getKey(object);
0823: return (Comment) comments.get(key);
0824: }
0825:
0826: public User findUser(String name) {
0827: return (User) users.get(name);
0828: }
0829:
0830: public FunctionAlias findFunctionAlias(String name) {
0831: return (FunctionAlias) functionAliases.get(name);
0832: }
0833:
0834: public UserAggregate findAggregate(String name) {
0835: return (UserAggregate) aggregates.get(name);
0836: }
0837:
0838: public UserDataType findUserDataType(String name) {
0839: return (UserDataType) userDataTypes.get(name);
0840: }
0841:
0842: public User getUser(String name, SQLException notFound)
0843: throws SQLException {
0844: User user = (User) users.get(name);
0845: if (user == null) {
0846: try {
0847: Thread.sleep(Constants.DELAY_WRONG_PASSWORD);
0848: } catch (InterruptedException e) {
0849: // ignore
0850: }
0851: throw notFound;
0852: }
0853: return user;
0854: }
0855:
0856: public User getUser(String name) throws SQLException {
0857: return getUser(name, Message.getSQLException(
0858: ErrorCode.USER_NOT_FOUND_1, name));
0859: }
0860:
0861: public synchronized Session createSession(User user)
0862: throws SQLException {
0863: if (exclusiveSession != null) {
0864: throw Message
0865: .getSQLException(ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE);
0866: }
0867: Session session = new Session(this , user, ++nextSessionId);
0868: sessions.add(session);
0869: traceSystem.getTrace(Trace.SESSION).info(
0870: "connecting #" + session.getId() + " to "
0871: + databaseName);
0872: if (delayedCloser != null) {
0873: delayedCloser.reset();
0874: delayedCloser = null;
0875: }
0876: return session;
0877: }
0878:
0879: public synchronized void removeSession(Session session)
0880: throws SQLException {
0881: if (session != null) {
0882: if (exclusiveSession == session) {
0883: exclusiveSession = null;
0884: }
0885: sessions.remove(session);
0886: if (session != systemSession) {
0887: traceSystem.getTrace(Trace.SESSION).info(
0888: "disconnecting #" + session.getId());
0889: }
0890: }
0891: if (sessions.size() == 0 && session != systemSession) {
0892: if (closeDelay == 0) {
0893: close(false);
0894: } else if (closeDelay < 0) {
0895: return;
0896: } else {
0897: delayedCloser = new DatabaseCloser(this ,
0898: closeDelay * 1000, false);
0899: delayedCloser.setName("H2 Close Delay "
0900: + getShortName());
0901: delayedCloser.setDaemon(true);
0902: delayedCloser.start();
0903: }
0904: }
0905: if (session != systemSession && session != null) {
0906: traceSystem.getTrace(Trace.SESSION).info(
0907: "disconnected #" + session.getId());
0908: }
0909: }
0910:
0911: synchronized void close(boolean fromShutdownHook) {
0912: closing = true;
0913: if (sessions.size() > 0) {
0914: if (!fromShutdownHook) {
0915: return;
0916: }
0917: traceSystem.getTrace(Trace.DATABASE).info(
0918: "closing " + databaseName + " from shutdown hook");
0919: Session[] all = new Session[sessions.size()];
0920: sessions.toArray(all);
0921: for (int i = 0; i < all.length; i++) {
0922: Session s = all[i];
0923: try {
0924: s.close();
0925: } catch (SQLException e) {
0926: traceSystem.getTrace(Trace.SESSION).error(
0927: "disconnecting #" + s.getId(), e);
0928: }
0929: }
0930: }
0931: if (log != null) {
0932: log.setDisabled(false);
0933: }
0934: traceSystem.getTrace(Trace.DATABASE).info(
0935: "closing " + databaseName);
0936: if (eventListener != null) {
0937: // allow the event listener to connect to the database
0938: closing = false;
0939: DatabaseEventListener e = eventListener;
0940: // set it to null, to make sure it's called only once
0941: eventListener = null;
0942: e.closingDatabase();
0943: if (sessions.size() > 0) {
0944: // if a connection was opened, we can't close the database
0945: return;
0946: }
0947: closing = true;
0948: }
0949: try {
0950: if (systemSession != null) {
0951: ObjectArray tablesAndViews = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
0952: for (int i = 0; i < tablesAndViews.size(); i++) {
0953: Table table = (Table) tablesAndViews.get(i);
0954: table.close(systemSession);
0955: }
0956: ObjectArray sequences = getAllSchemaObjects(DbObject.SEQUENCE);
0957: for (int i = 0; i < sequences.size(); i++) {
0958: Sequence sequence = (Sequence) sequences.get(i);
0959: sequence.close();
0960: }
0961: meta.close(systemSession);
0962: indexSummaryValid = true;
0963: }
0964: } catch (SQLException e) {
0965: traceSystem.getTrace(Trace.DATABASE).error("close", e);
0966: }
0967: // remove all session variables
0968: if (persistent) {
0969: try {
0970: ValueLob.removeAllForTable(this ,
0971: ValueLob.TABLE_ID_SESSION);
0972: } catch (SQLException e) {
0973: traceSystem.getTrace(Trace.DATABASE).error("close", e);
0974: }
0975: }
0976: try {
0977: closeOpenFilesAndUnlock();
0978: } catch (SQLException e) {
0979: traceSystem.getTrace(Trace.DATABASE).error("close", e);
0980: }
0981: traceSystem.getTrace(Trace.DATABASE).info("closed");
0982: traceSystem.close();
0983: if (closeOnExit != null) {
0984: closeOnExit.reset();
0985: try {
0986: Runtime.getRuntime().removeShutdownHook(closeOnExit);
0987: } catch (IllegalStateException e) {
0988: // ignore
0989: } catch (SecurityException e) {
0990: // applets may not do that - ignore
0991: }
0992: closeOnExit = null;
0993: }
0994: Engine.getInstance().close(databaseName);
0995: if (deleteFilesOnDisconnect && persistent) {
0996: deleteFilesOnDisconnect = false;
0997: try {
0998: String directory = FileUtils.getParent(databaseName);
0999: String name = FileUtils.getFileName(databaseName);
1000: DeleteDbFiles.execute(directory, name, true);
1001: } catch (Exception e) {
1002: // ignore (the trace is closed already)
1003: }
1004: }
1005: }
1006:
1007: private void stopWriter() {
1008: if (writer != null) {
1009: try {
1010: writer.stopThread();
1011: } catch (SQLException e) {
1012: traceSystem.getTrace(Trace.DATABASE).error("close", e);
1013: }
1014: writer = null;
1015: }
1016: }
1017:
1018: private synchronized void closeOpenFilesAndUnlock()
1019: throws SQLException {
1020: if (log != null) {
1021: stopWriter();
1022: log.close();
1023: log = null;
1024: }
1025: closeFiles();
1026: if (persistent && lock == null
1027: && fileLockMethod != FileLock.LOCK_NO) {
1028: // everything already closed (maybe in checkPowerOff)
1029: // don't delete temp files in this case because
1030: // the database could be open now (even from within another process)
1031: return;
1032: }
1033: if (persistent) {
1034: deleteOldTempFiles();
1035: }
1036: if (systemSession != null) {
1037: systemSession.close();
1038: systemSession = null;
1039: }
1040: if (lock != null) {
1041: lock.unlock();
1042: lock = null;
1043: }
1044: }
1045:
1046: private void closeFiles() throws SQLException {
1047: try {
1048: if (fileData != null) {
1049: fileData.close();
1050: fileData = null;
1051: }
1052: if (fileIndex != null) {
1053: fileIndex.close();
1054: fileIndex = null;
1055: }
1056: } catch (SQLException e) {
1057: traceSystem.getTrace(Trace.DATABASE).error("close", e);
1058: }
1059: storageMap.clear();
1060: }
1061:
1062: private void checkMetaFree(Session session, int id)
1063: throws SQLException {
1064: SearchRow r = meta.getTemplateSimpleRow(false);
1065: r.setValue(0, ValueInt.get(id));
1066: Cursor cursor = metaIdIndex.find(session, r, r);
1067: if (cursor.next()) {
1068: throw Message.getInternalError();
1069: }
1070: }
1071:
1072: public synchronized int allocateObjectId(boolean needFresh,
1073: boolean dataFile) {
1074: // TODO refactor: use hash map instead of bit field for object ids
1075: needFresh = true;
1076: int i;
1077: if (needFresh) {
1078: i = objectIds.getLastSetBit() + 1;
1079: if ((i & 1) != (dataFile ? 1 : 0)) {
1080: i++;
1081: }
1082:
1083: while (storageMap.get(i) != null || objectIds.get(i)) {
1084: i++;
1085: if ((i & 1) != (dataFile ? 1 : 0)) {
1086: i++;
1087: }
1088: }
1089: } else {
1090: i = objectIds.nextClearBit(0);
1091: }
1092: if (SysProperties.CHECK && objectIds.get(i)) {
1093: throw Message.getInternalError();
1094: }
1095: objectIds.set(i);
1096: return i;
1097: }
1098:
1099: public ObjectArray getAllSettings() {
1100: return new ObjectArray(settings.values());
1101: }
1102:
1103: public ObjectArray getAllUsers() {
1104: return new ObjectArray(users.values());
1105: }
1106:
1107: public ObjectArray getAllRoles() {
1108: return new ObjectArray(roles.values());
1109: }
1110:
1111: public ObjectArray getAllRights() {
1112: return new ObjectArray(rights.values());
1113: }
1114:
1115: public ObjectArray getAllComments() {
1116: return new ObjectArray(comments.values());
1117: }
1118:
1119: public ObjectArray getAllSchemas() {
1120: return new ObjectArray(schemas.values());
1121: }
1122:
1123: public ObjectArray getAllFunctionAliases() {
1124: return new ObjectArray(functionAliases.values());
1125: }
1126:
1127: public ObjectArray getAllAggregates() {
1128: return new ObjectArray(aggregates.values());
1129: }
1130:
1131: public ObjectArray getAllUserDataTypes() {
1132: return new ObjectArray(userDataTypes.values());
1133: }
1134:
1135: public ObjectArray getAllSchemaObjects(int type) {
1136: ObjectArray list = new ObjectArray();
1137: for (Iterator it = schemas.values().iterator(); it.hasNext();) {
1138: Schema schema = (Schema) it.next();
1139: list.addAll(schema.getAll(type));
1140: }
1141: return list;
1142: }
1143:
1144: public String getShortName() {
1145: return databaseShortName;
1146: }
1147:
1148: public String getName() {
1149: return databaseName;
1150: }
1151:
1152: public LogSystem getLog() {
1153: return log;
1154: }
1155:
1156: public Session[] getSessions() {
1157: Session[] list = new Session[sessions.size()];
1158: sessions.toArray(list);
1159: return list;
1160: }
1161:
1162: public synchronized void update(Session session, DbObject obj)
1163: throws SQLException {
1164: int id = obj.getId();
1165: removeMeta(session, id);
1166: addMeta(session, obj);
1167: }
1168:
1169: /**
1170: * Rename a schema object.
1171: *
1172: * @param session the session
1173: * @param obj the object
1174: * @param newName the new name
1175: */
1176: public synchronized void renameSchemaObject(Session session,
1177: SchemaObject obj, String newName) throws SQLException {
1178: obj.getSchema().rename(obj, newName);
1179: updateWithChildren(session, obj);
1180: }
1181:
1182: private synchronized void updateWithChildren(Session session,
1183: DbObject obj) throws SQLException {
1184: ObjectArray list = obj.getChildren();
1185: Comment comment = findComment(obj);
1186: if (comment != null) {
1187: throw Message.getInternalError();
1188: }
1189: update(session, obj);
1190: // remember that this scans only one level deep!
1191: for (int i = 0; list != null && i < list.size(); i++) {
1192: DbObject o = (DbObject) list.get(i);
1193: if (o.getCreateSQL() != null) {
1194: update(session, o);
1195: }
1196: }
1197: }
1198:
1199: /**
1200: * Rename a database object.
1201: *
1202: * @param session the session
1203: * @param obj the object
1204: * @param newName the new name
1205: */
1206: public synchronized void renameDatabaseObject(Session session,
1207: DbObject obj, String newName) throws SQLException {
1208: int type = obj.getType();
1209: HashMap map = getMap(type);
1210: if (SysProperties.CHECK) {
1211: if (!map.containsKey(obj.getName())) {
1212: throw Message.getInternalError("not found: "
1213: + obj.getName());
1214: }
1215: if (obj.getName().equals(newName)
1216: || map.containsKey(newName)) {
1217: throw Message
1218: .getInternalError("object already exists: "
1219: + newName);
1220: }
1221: }
1222: int id = obj.getId();
1223: removeMeta(session, id);
1224: map.remove(obj.getName());
1225: obj.rename(newName);
1226: map.put(newName, obj);
1227: updateWithChildren(session, obj);
1228: }
1229:
1230: public String createTempFile() throws SQLException {
1231: try {
1232: boolean inTempDir = readOnly;
1233: String name = databaseName;
1234: if (!persistent) {
1235: name = FileSystem.MEMORY_PREFIX + name;
1236: }
1237: return FileUtils.createTempFile(name,
1238: Constants.SUFFIX_TEMP_FILE, true, inTempDir);
1239: } catch (IOException e) {
1240: throw Message.convertIOException(e, databaseName);
1241: }
1242: }
1243:
1244: private void reserveLobFileObjectIds() throws SQLException {
1245: String prefix = FileUtils.normalize(databaseName) + ".";
1246: String path = FileUtils.getParent(databaseName);
1247: String[] list = FileUtils.listFiles(path);
1248: for (int i = 0; i < list.length; i++) {
1249: String name = list[i];
1250: if (name.endsWith(Constants.SUFFIX_LOB_FILE)
1251: && FileUtils.fileStartsWith(name, prefix)) {
1252: name = name.substring(prefix.length());
1253: name = name.substring(0, name.length()
1254: - Constants.SUFFIX_LOB_FILE.length());
1255: int dot = name.indexOf('.');
1256: if (dot >= 0) {
1257: String id = name.substring(dot + 1);
1258: int objectId = Integer.parseInt(id);
1259: objectIds.set(objectId);
1260: }
1261: }
1262: }
1263: }
1264:
1265: private void deleteOldTempFiles() throws SQLException {
1266: if (emergencyReserve != null) {
1267: emergencyReserve.closeAndDeleteSilently();
1268: emergencyReserve = null;
1269: }
1270: String path = FileUtils.getParent(databaseName);
1271: String prefix = FileUtils.normalize(databaseName);
1272: String[] list = FileUtils.listFiles(path);
1273: for (int i = 0; i < list.length; i++) {
1274: String name = list[i];
1275: if (name.endsWith(Constants.SUFFIX_TEMP_FILE)
1276: && FileUtils.fileStartsWith(name, prefix)) {
1277: // can't always delete the files, they may still be open
1278: FileUtils.tryDelete(name);
1279: }
1280: }
1281: }
1282:
1283: /**
1284: * Get or create the specified storage object.
1285: *
1286: * @param reader the record reader
1287: * @param id the object id
1288: * @param dataFile true if the data is in the data file
1289: * @return the storage
1290: */
1291: public Storage getStorage(RecordReader reader, int id,
1292: boolean dataFile) {
1293: DiskFile file;
1294: if (dataFile) {
1295: file = fileData;
1296: } else {
1297: file = fileIndex;
1298: }
1299: Storage storage = getStorage(id, file);
1300: storage.setReader(reader);
1301: return storage;
1302: }
1303:
1304: public Role findRole(String roleName) {
1305: return (Role) roles.get(roleName);
1306: }
1307:
1308: public Schema findSchema(String schemaName) {
1309: return (Schema) schemas.get(schemaName);
1310: }
1311:
1312: public Schema getSchema(String schemaName) throws SQLException {
1313: Schema schema = findSchema(schemaName);
1314: if (schema == null) {
1315: throw Message.getSQLException(ErrorCode.SCHEMA_NOT_FOUND_1,
1316: schemaName);
1317: }
1318: return schema;
1319: }
1320:
1321: public synchronized void removeDatabaseObject(Session session,
1322: DbObject obj) throws SQLException {
1323: String objName = obj.getName();
1324: int type = obj.getType();
1325: HashMap map = getMap(type);
1326: if (SysProperties.CHECK && !map.containsKey(objName)) {
1327: throw Message.getInternalError("not found: " + objName);
1328: }
1329: Comment comment = findComment(obj);
1330: if (comment != null) {
1331: removeDatabaseObject(session, comment);
1332: }
1333: int id = obj.getId();
1334: obj.removeChildrenAndResources(session);
1335: map.remove(objName);
1336: removeMeta(session, id);
1337: }
1338:
1339: private String getDependentObject(SchemaObject obj) {
1340: switch (obj.getType()) {
1341: case DbObject.COMMENT:
1342: case DbObject.CONSTRAINT:
1343: case DbObject.INDEX:
1344: case DbObject.RIGHT:
1345: case DbObject.TRIGGER:
1346: case DbObject.USER:
1347: return null;
1348: }
1349: ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
1350: HashSet set = new HashSet();
1351: for (int i = 0; i < list.size(); i++) {
1352: Table t = (Table) list.get(i);
1353: set.clear();
1354: t.addDependencies(set);
1355: if (set.contains(obj)) {
1356: return t.getSQL();
1357: }
1358: }
1359: return null;
1360: }
1361:
1362: private String getFirstInvalidTable(Session session) {
1363: String conflict = null;
1364: try {
1365: ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
1366: for (int i = 0; i < list.size(); i++) {
1367: Table t = (Table) list.get(i);
1368: conflict = t.getSQL();
1369: session.prepare(t.getCreateSQL());
1370: }
1371: } catch (SQLException e) {
1372: return conflict;
1373: }
1374: return null;
1375: }
1376:
1377: public synchronized void removeSchemaObject(Session session,
1378: SchemaObject obj) throws SQLException {
1379: if (obj.getType() == DbObject.TABLE_OR_VIEW) {
1380: Table table = (Table) obj;
1381: if (table.getTemporary() && !table.getGlobalTemporary()) {
1382: session.removeLocalTempTable(table);
1383: return;
1384: }
1385: }
1386: Comment comment = findComment(obj);
1387: if (comment != null) {
1388: removeDatabaseObject(session, comment);
1389: }
1390: obj.getSchema().remove(session, obj);
1391: String invalid;
1392: if (SysProperties.OPTIMIZE_DROP_DEPENDENCIES) {
1393: invalid = getDependentObject(obj);
1394: } else {
1395: invalid = getFirstInvalidTable(session);
1396: }
1397: if (invalid != null) {
1398: obj.getSchema().add(obj);
1399: throw Message.getSQLException(ErrorCode.CANNOT_DROP_2,
1400: new String[] { obj.getSQL(), invalid });
1401: }
1402: int id = obj.getId();
1403: obj.removeChildrenAndResources(session);
1404: removeMeta(session, id);
1405: }
1406:
1407: public boolean isPersistent() {
1408: return persistent;
1409: }
1410:
1411: public TraceSystem getTraceSystem() {
1412: return traceSystem;
1413: }
1414:
1415: public DiskFile getDataFile() {
1416: return fileData;
1417: }
1418:
1419: public DiskFile getIndexFile() {
1420: return fileIndex;
1421: }
1422:
1423: public synchronized void setCacheSize(int kb) throws SQLException {
1424: if (fileData != null) {
1425: fileData.getCache().setMaxSize(kb);
1426: int valueIndex = kb <= 32 ? kb
1427: : (kb >>> SysProperties.CACHE_SIZE_INDEX_SHIFT);
1428: fileIndex.getCache().setMaxSize(valueIndex);
1429: cacheSize = kb;
1430: }
1431: }
1432:
1433: public synchronized void setMasterUser(User user)
1434: throws SQLException {
1435: addDatabaseObject(systemSession, user);
1436: systemSession.commit(true);
1437: }
1438:
1439: public Role getPublicRole() {
1440: return publicRole;
1441: }
1442:
1443: public String getTempTableName(int sessionId) {
1444: String tempName;
1445: for (int i = 0;; i++) {
1446: tempName = Constants.TEMP_TABLE_PREFIX + sessionId + "_"
1447: + i;
1448: if (mainSchema.findTableOrView(null, tempName) == null) {
1449: break;
1450: }
1451: }
1452: return tempName;
1453: }
1454:
1455: public void setCompareMode(CompareMode compareMode) {
1456: this .compareMode = compareMode;
1457: }
1458:
1459: public CompareMode getCompareMode() {
1460: return compareMode;
1461: }
1462:
1463: public String getCluster() {
1464: return cluster;
1465: }
1466:
1467: public void setCluster(String cluster) {
1468: this .cluster = cluster;
1469: }
1470:
1471: public void checkWritingAllowed() throws SQLException {
1472: if (readOnly) {
1473: throw Message
1474: .getSQLException(ErrorCode.DATABASE_IS_READ_ONLY);
1475: }
1476: if (noDiskSpace) {
1477: throw Message
1478: .getSQLException(ErrorCode.NO_DISK_SPACE_AVAILABLE);
1479: }
1480: }
1481:
1482: public boolean getReadOnly() {
1483: return readOnly;
1484: }
1485:
1486: public void setWriteDelay(int value) {
1487: writeDelay = value;
1488: if (writer != null) {
1489: writer.setWriteDelay(value);
1490: }
1491: }
1492:
1493: public void deleteLogFileLater(String fileName) throws SQLException {
1494: if (writer != null) {
1495: writer.deleteLogFileLater(fileName);
1496: } else {
1497: FileUtils.delete(fileName);
1498: }
1499: }
1500:
1501: public Class loadUserClass(String className) throws SQLException {
1502: try {
1503: return ClassUtils.loadUserClass(className);
1504: } catch (ClassNotFoundException e) {
1505: throw Message.getSQLException(ErrorCode.CLASS_NOT_FOUND_1,
1506: new String[] { className }, e);
1507: } catch (NoClassDefFoundError e) {
1508: throw Message.getSQLException(ErrorCode.CLASS_NOT_FOUND_1,
1509: new String[] { className }, e);
1510: }
1511: }
1512:
1513: public void setEventListener(String className) throws SQLException {
1514: if (className == null || className.length() == 0) {
1515: eventListener = null;
1516: } else {
1517: try {
1518: eventListener = (DatabaseEventListener) loadUserClass(
1519: className).newInstance();
1520: String url = databaseURL;
1521: if (cipher != null) {
1522: url += ";CIPHER=" + cipher;
1523: }
1524: eventListener.init(url);
1525: } catch (Throwable e) {
1526: throw Message
1527: .getSQLException(
1528: ErrorCode.ERROR_SETTING_DATABASE_EVENT_LISTENER_2,
1529: new String[] { className, e.toString() },
1530: e);
1531: }
1532: }
1533: }
1534:
1535: public synchronized void freeUpDiskSpace() throws SQLException {
1536: long sizeAvailable = 0;
1537: if (emergencyReserve != null) {
1538: sizeAvailable = emergencyReserve.length();
1539: long newLength = sizeAvailable / 4;
1540: if (newLength < SysProperties.EMERGENCY_SPACE_MIN) {
1541: newLength = 0;
1542: noDiskSpace = true;
1543: }
1544: emergencyReserve.setLength(newLength);
1545: }
1546: if (eventListener != null) {
1547: eventListener.diskSpaceIsLow(sizeAvailable);
1548: }
1549: }
1550:
1551: /**
1552: * Set the progress of a long running operation.
1553: * This method calls the {@link DatabaseEventListener} if one is registered.
1554: *
1555: * @param state the {@link DatabaseEventListener} state
1556: * @param name the object name
1557: * @param x the current position
1558: * @param max the highest value
1559: */
1560:
1561: public void setProgress(int state, String name, int x, int max) {
1562: if (eventListener != null) {
1563: try {
1564: eventListener.setProgress(state, name, x, max);
1565: } catch (Exception e2) {
1566: // ignore this second (user made) exception
1567: }
1568: }
1569: }
1570:
1571: public void exceptionThrown(SQLException e, String sql) {
1572: if (eventListener != null) {
1573: try {
1574: eventListener.exceptionThrown(e, sql);
1575: } catch (Exception e2) {
1576: // ignore this second (user made) exception
1577: }
1578: }
1579: }
1580:
1581: public void sync() throws SQLException {
1582: if (log != null) {
1583: log.sync();
1584: }
1585: if (fileData != null) {
1586: fileData.sync();
1587: }
1588: if (fileIndex != null) {
1589: fileIndex.sync();
1590: }
1591: }
1592:
1593: public int getMaxMemoryRows() {
1594: return maxMemoryRows;
1595: }
1596:
1597: public void setMaxMemoryRows(int value) {
1598: this .maxMemoryRows = value;
1599: }
1600:
1601: public void setMaxMemoryUndo(int value) {
1602: this .maxMemoryUndo = value;
1603: }
1604:
1605: public int getMaxMemoryUndo() {
1606: return maxMemoryUndo;
1607: }
1608:
1609: public int getChecksum(byte[] data, int start, int end) {
1610: int x = 0;
1611: while (start < end) {
1612: x += data[start++];
1613: }
1614: return x;
1615: }
1616:
1617: public void setLockMode(int lockMode) {
1618: this .lockMode = lockMode;
1619: }
1620:
1621: public int getLockMode() {
1622: return lockMode;
1623: }
1624:
1625: public synchronized void setCloseDelay(int value) {
1626: this .closeDelay = value;
1627: }
1628:
1629: public boolean getLogIndexChanges() {
1630: return logIndexChanges;
1631: }
1632:
1633: public synchronized void setLog(int level) throws SQLException {
1634: if (logLevel == level) {
1635: return;
1636: }
1637: boolean logData;
1638: boolean logIndex;
1639: switch (level) {
1640: case 0:
1641: logData = false;
1642: logIndex = false;
1643: break;
1644: case 1:
1645: logData = true;
1646: logIndex = false;
1647: break;
1648: case 2:
1649: logData = true;
1650: logIndex = true;
1651: break;
1652: default:
1653: throw Message.getInternalError("level=" + level);
1654: }
1655: if (fileIndex != null) {
1656: fileIndex.setLogChanges(logIndex);
1657: }
1658: if (log != null) {
1659: log.setDisabled(!logData);
1660: log.checkpoint();
1661: }
1662: traceSystem.getTrace(Trace.DATABASE).error("SET LOG " + level,
1663: null);
1664: logLevel = level;
1665: }
1666:
1667: public ObjectArray getAllStorages() {
1668: return new ObjectArray(storageMap.values());
1669: }
1670:
1671: public boolean getRecovery() {
1672: return recovery;
1673: }
1674:
1675: public Session getSystemSession() {
1676: return systemSession;
1677: }
1678:
1679: public String getDatabasePath() {
1680: if (persistent) {
1681: return FileUtils.getAbsolutePath(databaseName);
1682: } else {
1683: return null;
1684: }
1685: }
1686:
1687: public void handleInvalidChecksum() throws SQLException {
1688: SQLException e = Message.getSQLException(
1689: ErrorCode.FILE_CORRUPTED_1, "wrong checksum");
1690: if (!recovery) {
1691: throw e;
1692: } else {
1693: traceSystem.getTrace(Trace.DATABASE).error("recover", e);
1694: }
1695: }
1696:
1697: public boolean isClosing() {
1698: return closing;
1699: }
1700:
1701: public int getWriteDelay() {
1702: return writeDelay;
1703: }
1704:
1705: public int getCacheSize() {
1706: return cacheSize;
1707: }
1708:
1709: public void setMaxLengthInplaceLob(int value) {
1710: this .maxLengthInplaceLob = value;
1711: }
1712:
1713: public int getMaxLengthInplaceLob() {
1714: return maxLengthInplaceLob;
1715: }
1716:
1717: public void setIgnoreCase(boolean b) {
1718: ignoreCase = b;
1719: }
1720:
1721: public boolean getIgnoreCase() {
1722: if (starting) {
1723: // tables created at startup must not be converted to ignorecase
1724: return false;
1725: }
1726: return ignoreCase;
1727: }
1728:
1729: public synchronized void setDeleteFilesOnDisconnect(boolean b) {
1730: this .deleteFilesOnDisconnect = b;
1731: }
1732:
1733: public String getLobCompressionAlgorithm(int type) {
1734: return lobCompressionAlgorithm;
1735: }
1736:
1737: public void setLobCompressionAlgorithm(String stringValue) {
1738: this .lobCompressionAlgorithm = stringValue;
1739: }
1740:
1741: public void notifyFileSize(long length) {
1742: if (length > biggestFileSize) {
1743: biggestFileSize = length;
1744: setMaxLogSize(0);
1745: }
1746: }
1747:
1748: public synchronized void setMaxLogSize(long value) {
1749: long minLogSize = biggestFileSize / Constants.LOG_SIZE_DIVIDER;
1750: minLogSize = Math.max(value, minLogSize);
1751: long currentLogSize = getLog().getMaxLogSize();
1752: if (minLogSize > currentLogSize
1753: || (value > 0 && minLogSize > value)) {
1754: // works for currentLogSize <= 0 as well
1755: value = minLogSize;
1756: }
1757: if (value > 0) {
1758: getLog().setMaxLogSize(value);
1759: }
1760: }
1761:
1762: public void setAllowLiterals(int value) {
1763: this .allowLiterals = value;
1764: }
1765:
1766: public int getAllowLiterals() {
1767: if (starting) {
1768: return Constants.ALLOW_LITERALS_ALL;
1769: }
1770: return allowLiterals;
1771: }
1772:
1773: public boolean getOptimizeReuseResults() {
1774: return optimizeReuseResults;
1775: }
1776:
1777: public void setOptimizeReuseResults(boolean b) {
1778: optimizeReuseResults = b;
1779: }
1780:
1781: public String getCacheType() {
1782: return cacheType;
1783: }
1784:
1785: public void invalidateIndexSummary() throws SQLException {
1786: if (indexSummaryValid) {
1787: indexSummaryValid = false;
1788: log.invalidateIndexSummary();
1789: }
1790: }
1791:
1792: public boolean getIndexSummaryValid() {
1793: return indexSummaryValid;
1794: }
1795:
1796: public Object getLobSyncObject() {
1797: return lobSyncObject;
1798: }
1799:
1800: public int getSessionCount() {
1801: return sessions.size();
1802: }
1803:
1804: public void setReferentialIntegrity(boolean b) {
1805: referentialIntegrity = b;
1806: }
1807:
1808: public boolean getReferentialIntegrity() {
1809: return referentialIntegrity;
1810: }
1811:
1812: public boolean isStarting() {
1813: return starting;
1814: }
1815:
1816: public boolean isMultiVersion() {
1817: return multiVersion;
1818: }
1819:
1820: public void opened() throws SQLException {
1821: if (eventListener != null) {
1822: eventListener.opened();
1823: }
1824: }
1825:
1826: public void setMode(Mode mode) {
1827: this .mode = mode;
1828: }
1829:
1830: public Mode getMode() {
1831: return mode;
1832: }
1833:
1834: public boolean getMultiThreaded() {
1835: return multiThreaded;
1836: }
1837:
1838: public void setMultiThreaded(boolean multiThreaded) {
1839: this .multiThreaded = multiThreaded;
1840: }
1841:
1842: public void setMaxOperationMemory(int maxOperationMemory) {
1843: this .maxOperationMemory = maxOperationMemory;
1844: }
1845:
1846: public int getMaxOperationMemory() {
1847: return maxOperationMemory;
1848: }
1849:
1850: public Session getExclusiveSession() {
1851: return exclusiveSession;
1852: }
1853:
1854: public void setExclusiveSession(Session session) {
1855: this .exclusiveSession = session;
1856: }
1857:
1858: public boolean getLobFilesInDirectories() {
1859: return lobFilesInDirectories;
1860: }
1861:
1862: public SmallLRUCache getLobFileListCache() {
1863: return lobFileListCache;
1864: }
1865:
1866: }
|