0001: /**
0002: * com.mckoi.database.TransactionSystem 24 Mar 2002
0003: *
0004: * Mckoi SQL Database ( http://www.mckoi.com/database )
0005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License
0009: * Version 2 as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License Version 2 for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * Version 2 along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0019: *
0020: * Change Log:
0021: *
0022: *
0023: */package com.mckoi.database;
0024:
0025: import com.mckoi.store.LoggingBufferManager;
0026: import com.mckoi.util.Stats;
0027: import com.mckoi.util.StringUtil;
0028: import com.mckoi.util.LogWriter;
0029: import com.mckoi.debug.*;
0030: import com.mckoi.database.control.DBConfig;
0031: import java.io.File;
0032: import java.io.IOException;
0033: import java.io.PrintWriter;
0034: import java.io.Writer;
0035: import java.util.Date;
0036: import java.util.List;
0037: import java.util.ArrayList;
0038: import java.util.Properties;
0039:
0040: /**
0041: * A class that provides information and global functions for the transaction
0042: * layer in the engine. Shared information includes configuration details,
0043: * logging, etc.
0044: *
0045: * @author Tobias Downer
0046: */
0047:
0048: public class TransactionSystem {
0049:
0050: /**
0051: * The stats object that keeps track of database statistics.
0052: */
0053: private final Stats stats = new Stats();
0054:
0055: /**
0056: * A logger to output any debugging messages.
0057: * NOTE: This MUST be final, because other objects may retain a reference
0058: * to the object. If it is not final, then different objects will be
0059: * logging to different places if this reference is changed.
0060: */
0061: private final DefaultDebugLogger logger;
0062:
0063: /**
0064: * The ResourceBundle that contains properties of the entire database
0065: * system.
0066: */
0067: private DBConfig config = null;
0068:
0069: /**
0070: * The path in the file system for the database files. Note that this will
0071: * be null if the database does not exist in a local file system. For this
0072: * reason it's best not to write code that relies on the use of this value.
0073: */
0074: private File db_path;
0075:
0076: /**
0077: * Set to true if lookup comparison lists are enabled.
0078: */
0079: private boolean lookup_comparison_list_enabled = false;
0080:
0081: /**
0082: * Set to true if the database is in read only mode. This is set from the
0083: * configuration file.
0084: */
0085: private boolean read_only_access = false;
0086:
0087: /**
0088: * Set to true if locking checks should be performed each time a table is
0089: * accessed.
0090: */
0091: private boolean table_lock_check = false;
0092:
0093: /**
0094: * Set to false if there is conservative index memory storage. If true,
0095: * all root selectable schemes are stored behind a soft reference that will
0096: * be garbage collected.
0097: */
0098: private boolean soft_index_storage = false;
0099:
0100: /**
0101: * If this is set to true, during boot up the engine will reindex all the
0102: * tables that weren't closed. If false, the engine will only reindex the
0103: * tables that have unchecked in modifications.
0104: */
0105: private boolean always_reindex_dirty_tables = false;
0106:
0107: /**
0108: * Set to true if the file handles should NOT be synchronized with the
0109: * system file IO when the indices are written. If this is true, then the
0110: * database is not as fail safe, however File IO performance is improved.
0111: */
0112: private boolean dont_synch_filesystem = false;
0113:
0114: /**
0115: * Set to true if the parser should ignore case when searching for a schema,
0116: * table or column using an identifier.
0117: */
0118: private boolean ignore_case_for_identifiers = false;
0119:
0120: /**
0121: * Transaction option, if this is true then a transaction error is generated
0122: * during commit if a transaction selects data from a table that has
0123: * committed changes to it during commit time.
0124: * <p>
0125: * True by default.
0126: */
0127: private boolean transaction_error_on_dirty_select = true;
0128:
0129: /**
0130: * The DataCellCache that is a shared resource between on database's.
0131: */
0132: private DataCellCache data_cell_cache = null;
0133:
0134: /**
0135: * The list of FunctionFactory objects that handle different functions from
0136: * SQL.
0137: */
0138: private ArrayList function_factory_list;
0139:
0140: /**
0141: * The FunctionLookup object that can resolve a FunctionDef object to a
0142: * Function object.
0143: */
0144: private DSFunctionLookup function_lookup;
0145:
0146: /**
0147: * The regular expression library bridge for the library we are configured
0148: * to use.
0149: */
0150: private RegexLibrary regex_library;
0151:
0152: /**
0153: * The log directory.
0154: */
0155: private File log_directory;
0156:
0157: /**
0158: * A LoggingBufferManager object used to manage pages of ScatteringFileStore
0159: * objects in the file system. We can configure the maximum pages and page
0160: * size via this object, so we have control over how much memory from the
0161: * heap is used for buffering.
0162: */
0163: private LoggingBufferManager buffer_manager;
0164:
0165: /**
0166: * The underlying StoreSystem implementation that encapsulates the behaviour
0167: * for storing data persistantly.
0168: */
0169: private StoreSystem store_system;
0170:
0171: // ---------- Low level row listeners ----------
0172:
0173: /**
0174: * A list of table names and listeners that are notified of add and remove
0175: * events in a table.
0176: */
0177: private ArrayList table_listeners;
0178:
0179: /**
0180: * Constructor.
0181: */
0182: public TransactionSystem() {
0183: // Setup generate properties from the JVM.
0184: logger = new DefaultDebugLogger();
0185: Properties p = System.getProperties();
0186: stats.set(0, "Runtime.java.version: "
0187: + p.getProperty("java.version"));
0188: stats.set(0, "Runtime.java.vendor: "
0189: + p.getProperty("java.vendor"));
0190: stats.set(0, "Runtime.java.vm.name: "
0191: + p.getProperty("java.vm.name"));
0192: stats.set(0, "Runtime.os.name: " + p.getProperty("os.name"));
0193: stats.set(0, "Runtime.os.arch: " + p.getProperty("os.arch"));
0194: stats.set(0, "Runtime.os.version: "
0195: + p.getProperty("os.version"));
0196: table_listeners = new ArrayList();
0197: }
0198:
0199: /**
0200: * Parses a file string to an absolute position in the file system. We must
0201: * provide the path to the root directory (eg. the directory where the
0202: * config bundle is located).
0203: */
0204: private static File parseFileString(File root_path,
0205: String root_info, String path_string) {
0206: File path = new File(path_string);
0207: File res;
0208: // If the path is absolute then return the absoluate reference
0209: if (path.isAbsolute()) {
0210: res = path;
0211: } else {
0212: // If the root path source is the jvm then just return the path.
0213: if (root_info != null && root_info.equals("jvm")) {
0214: return path;
0215: }
0216: // If the root path source is the configuration file then
0217: // concat the configuration path with the path string and return it.
0218: else {
0219: res = new File(root_path, path_string);
0220: }
0221: }
0222: return res;
0223: }
0224:
0225: /**
0226: * Sets up the log file from the config information.
0227: */
0228: private void setupLog(DBConfig config) {
0229: String log_path_string = config.getValue("log_path");
0230: String root_path_var = config.getValue("root_path");
0231: String read_only_access = config.getValue("read_only");
0232: String debug_logs = config.getValue("debug_logs");
0233: boolean read_only_bool = false;
0234: if (read_only_access != null) {
0235: read_only_bool = read_only_access
0236: .equalsIgnoreCase("enabled");
0237: }
0238: boolean debug_logs_bool = true;
0239: if (debug_logs != null) {
0240: debug_logs_bool = debug_logs.equalsIgnoreCase("enabled");
0241: }
0242:
0243: // Conditions for not initializing a log directory;
0244: // 1. read only access is enabled
0245: // 2. log_path is empty or not set
0246:
0247: if (debug_logs_bool && !read_only_bool
0248: && log_path_string != null
0249: && !log_path_string.equals("")) {
0250: // First set up the debug information in this VM for the 'Debug' class.
0251: File log_path = parseFileString(config.currentPath(),
0252: root_path_var, log_path_string);
0253: // If the path doesn't exist the make it.
0254: if (!log_path.exists()) {
0255: log_path.mkdirs();
0256: }
0257: // Set the log directory in the DatabaseSystem
0258: setLogDirectory(log_path);
0259:
0260: LogWriter f_writer;
0261: File debug_log_file;
0262: String dlog_file_name = "";
0263: try {
0264: dlog_file_name = config.getValue("debug_log_file");
0265: debug_log_file = new File(log_path.getCanonicalPath(),
0266: dlog_file_name);
0267:
0268: // Allow log size to grow to 512k and allow 12 archives of the log
0269: f_writer = new LogWriter(debug_log_file, 512 * 1024, 12);
0270: f_writer.write("**** Debug log started: "
0271: + new Date(System.currentTimeMillis())
0272: + " ****\n");
0273: f_writer.flush();
0274: } catch (IOException e) {
0275: throw new RuntimeException(
0276: "Unable to open debug file '" + dlog_file_name
0277: + "' in path '" + log_path + "'");
0278: }
0279: setDebugOutput(f_writer);
0280: }
0281:
0282: // If 'debug_logs=disabled', don't write out any debug logs
0283: if (!debug_logs_bool) {
0284: // Otherwise set it up so the output from the logs goes to a PrintWriter
0285: // that doesn't do anything. Basically - this means all log information
0286: // will get sent into a black hole.
0287: setDebugOutput(new PrintWriter(new Writer() {
0288: public void write(int c) throws IOException {
0289: }
0290:
0291: public void write(char cbuf[], int off, int len)
0292: throws IOException {
0293: }
0294:
0295: public void write(String str, int off, int len)
0296: throws IOException {
0297: }
0298:
0299: public void flush() throws IOException {
0300: }
0301:
0302: public void close() throws IOException {
0303: }
0304: }));
0305: }
0306:
0307: int debug_level = Integer.parseInt(config
0308: .getValue("debug_level"));
0309: if (debug_level == -1) {
0310: setDebugLevel(255);
0311: } else {
0312: setDebugLevel(debug_level);
0313: }
0314: }
0315:
0316: /**
0317: * Returns a configuration value, or the default if it's not found.
0318: */
0319: public final String getConfigString(String property,
0320: String default_val) {
0321: String v = config.getValue(property);
0322: if (v == null) {
0323: return default_val;
0324: }
0325: return v.trim();
0326: }
0327:
0328: /**
0329: * Returns a configuration value, or the default if it's not found.
0330: */
0331: public final int getConfigInt(String property, int default_val) {
0332: String v = config.getValue(property);
0333: if (v == null) {
0334: return default_val;
0335: }
0336: return Integer.parseInt(v);
0337: }
0338:
0339: /**
0340: * Returns a configuration value, or the default if it's not found.
0341: */
0342: public final boolean getConfigBoolean(String property,
0343: boolean default_val) {
0344: String v = config.getValue(property);
0345: if (v == null) {
0346: return default_val;
0347: }
0348: return v.trim().equalsIgnoreCase("enabled");
0349: }
0350:
0351: /**
0352: * Given a regular expression string representing a particular library, this
0353: * will return the name of the class to use as a bridge between the library
0354: * and Mckoi. Returns null if the library name is invalid.
0355: */
0356: private static String regexStringToClass(String lib) {
0357: if (lib.equals("java.util.regexp")) {
0358: return "com.mckoi.database.regexbridge.JavaRegex";
0359: } else if (lib.equals("org.apache.regexp")) {
0360: return "com.mckoi.database.regexbridge.ApacheRegex";
0361: } else if (lib.equals("gnu.regexp")) {
0362: return "com.mckoi.database.regexbridge.GNURegex";
0363: } else {
0364: return null;
0365: }
0366: }
0367:
0368: /**
0369: * Inits the TransactionSystem with the configuration properties of the
0370: * system.
0371: * This can only be called once, and should be called at database boot time.
0372: */
0373: public void init(DBConfig config) {
0374:
0375: function_factory_list = new ArrayList();
0376: function_lookup = new DSFunctionLookup();
0377:
0378: if (config != null) {
0379: this .config = config;
0380:
0381: // Set the read_only property
0382: read_only_access = getConfigBoolean("read_only", false);
0383:
0384: // Setup the log
0385: setupLog(config);
0386:
0387: // The storage encapsulation that has been configured.
0388: String storage_system = getConfigString("storage_system",
0389: "v1file");
0390:
0391: boolean is_file_store_mode;
0392:
0393: // Construct the system store.
0394: if (storage_system.equalsIgnoreCase("v1file")) {
0395: Debug().write(Lvl.MESSAGE, this ,
0396: "Storage System: v1 file storage mode.");
0397:
0398: // The path where the database data files are stored.
0399: String database_path = getConfigString("database_path",
0400: "./data");
0401: // The root path variable
0402: String root_path_var = getConfigString("root_path",
0403: "jvm");
0404:
0405: // Set the absolute database path
0406: db_path = parseFileString(config.currentPath(),
0407: root_path_var, database_path);
0408:
0409: store_system = new V1FileStoreSystem(this , db_path,
0410: read_only_access);
0411: is_file_store_mode = true;
0412: }
0413:
0414: else if (storage_system.equalsIgnoreCase("v1javaheap")) {
0415: Debug().write(Lvl.MESSAGE, this ,
0416: "Storage System: v1 Java heap storage mode.");
0417: store_system = new V1HeapStoreSystem();
0418: is_file_store_mode = false;
0419: }
0420:
0421: else {
0422: String error_msg = "Unknown storage_system property: "
0423: + storage_system;
0424: Debug().write(Lvl.ERROR, this , error_msg);
0425: throw new RuntimeException(error_msg);
0426: }
0427:
0428: // Register the internal function factory,
0429: addFunctionFactory(new InternalFunctionFactory());
0430:
0431: String status;
0432:
0433: // Set up the DataCellCache from the values in the configuration
0434: int max_cache_size = 0, max_cache_entry_size = 0;
0435:
0436: max_cache_size = getConfigInt("data_cache_size", 0);
0437: max_cache_entry_size = getConfigInt("max_cache_entry_size",
0438: 0);
0439:
0440: if (max_cache_size >= 4096 && max_cache_entry_size >= 16
0441: && max_cache_entry_size < (max_cache_size / 2)) {
0442:
0443: Debug().write(
0444: Lvl.MESSAGE,
0445: this ,
0446: "Internal Data Cache size: "
0447: + max_cache_size);
0448: Debug().write(
0449: Lvl.MESSAGE,
0450: this ,
0451: "Internal Data Cache max cell size: "
0452: + max_cache_entry_size);
0453:
0454: // Find a prime hash size depending on the size of the cache.
0455: int hash_size = DataCellCache
0456: .closestPrime(max_cache_size / 55);
0457:
0458: // Set up the data_cell_cache
0459: data_cell_cache = new DataCellCache(this ,
0460: max_cache_size, max_cache_entry_size, hash_size);
0461:
0462: } else {
0463: Debug().write(Lvl.MESSAGE, this ,
0464: "Internal Data Cache disabled.");
0465: }
0466:
0467: // Are lookup comparison lists enabled?
0468: // lookup_comparison_list_enabled =
0469: // getConfigBoolean("lookup_comparison_list", false);
0470: lookup_comparison_list_enabled = false;
0471: Debug().write(
0472: Lvl.MESSAGE,
0473: this ,
0474: "lookup_comparison_list = "
0475: + lookup_comparison_list_enabled);
0476:
0477: // Should we open the database in read only mode?
0478: Debug().write(Lvl.MESSAGE, this ,
0479: "read_only = " + read_only_access);
0480: if (read_only_access)
0481: stats.set(1, "DatabaseSystem.read_only");
0482:
0483: // // Hard Sync file system whenever we update index files?
0484: // if (is_file_store_mode) {
0485: // dont_synch_filesystem = getConfigBoolean("dont_synch_filesystem", false);
0486: // Debug().write(Lvl.MESSAGE, this,
0487: // "dont_synch_filesystem = " + dont_synch_filesystem);
0488: // }
0489:
0490: // Generate transaction error if dirty selects are detected?
0491: transaction_error_on_dirty_select = getConfigBoolean(
0492: "transaction_error_on_dirty_select", true);
0493: Debug().write(
0494: Lvl.MESSAGE,
0495: this ,
0496: "transaction_error_on_dirty_select = "
0497: + transaction_error_on_dirty_select);
0498:
0499: // Case insensitive identifiers?
0500: ignore_case_for_identifiers = getConfigBoolean(
0501: "ignore_case_for_identifiers", false);
0502: Debug().write(
0503: Lvl.MESSAGE,
0504: this ,
0505: "ignore_case_for_identifiers = "
0506: + ignore_case_for_identifiers);
0507:
0508: // ---- Store system setup ----
0509:
0510: // See if this JVM supports the java.nio interface
0511: // (first introduced in 1.4)
0512: if (is_file_store_mode) {
0513: boolean nio_interface_available;
0514: try {
0515: Class.forName("java.nio.channels.FileChannel");
0516: nio_interface_available = true;
0517: Debug().write(Lvl.MESSAGE, this ,
0518: "Java NIO API is available.");
0519: } catch (ClassNotFoundException e) {
0520: nio_interface_available = false;
0521: Debug().write(Lvl.MESSAGE, this ,
0522: "Java NIO API is not available.");
0523: }
0524:
0525: // Bug workaround - there are problems with memory mapped NIO under 95/98
0526: // which we workaround by disabling NIO support on 95/98.
0527: boolean nio_bugged_os;
0528: String os_name = System.getProperties().getProperty(
0529: "os.name");
0530: nio_bugged_os = (os_name.equalsIgnoreCase("Windows 95") || os_name
0531: .equalsIgnoreCase("Windows 98"));
0532:
0533: // Get the safety level of the file system where 10 is the most safe
0534: // and 1 is the least safe.
0535: int io_safety_level = getConfigInt("io_safety_level",
0536: 10);
0537: if (io_safety_level < 1 || io_safety_level > 10) {
0538: Debug()
0539: .write(Lvl.MESSAGE, this ,
0540: "Invalid io_safety_level value. Setting to the most safe level.");
0541: io_safety_level = 10;
0542: }
0543: Debug().write(Lvl.MESSAGE, this ,
0544: "io_safety_level = " + io_safety_level);
0545:
0546: // Logging is disabled when safety level is less or equal to 2
0547: boolean enable_logging = true;
0548: if (io_safety_level <= 2) {
0549: Debug().write(Lvl.MESSAGE, this ,
0550: "Disabling journaling and file sync.");
0551: enable_logging = false;
0552: }
0553:
0554: // If the configuration property 'use_nio_if_available' is enabled then
0555: // we setup a LoggingBufferManager that uses NIO (default to 'false')
0556: boolean use_nio_if_available = getConfigBoolean(
0557: "use_nio_if_available", false);
0558: boolean force_use_nio = getConfigBoolean(
0559: "force_use_nio", false);
0560:
0561: String api_to_use;
0562: int page_size;
0563: int max_pages;
0564:
0565: final boolean disable_nio = true;
0566:
0567: // If NIO interface available and configuration tells us to use NIO and
0568: // we are not running on an OS where NIO is buggy, we set the NIO options
0569: // here.
0570: if (!disable_nio
0571: && (force_use_nio || (nio_interface_available
0572: && use_nio_if_available && !nio_bugged_os))) {
0573: Debug()
0574: .write(Lvl.MESSAGE, this ,
0575: "Using NIO API for OS memory mapped file access.");
0576: page_size = getConfigInt("buffered_nio_page_size",
0577: 1024 * 1024);
0578: max_pages = getConfigInt("buffered_nio_max_pages",
0579: 64);
0580: api_to_use = "Java NIO";
0581: } else {
0582: Debug()
0583: .write(Lvl.MESSAGE, this ,
0584: "Using stardard IO API for heap buffered file access.");
0585: page_size = getConfigInt("buffered_io_page_size",
0586: 8192);
0587: max_pages = getConfigInt("buffered_io_max_pages",
0588: 256);
0589: api_to_use = "Java IO";
0590: }
0591:
0592: // Output this information to the log
0593: Debug().write(Lvl.MESSAGE, this ,
0594: "[Buffer Manager] Using IO API: " + api_to_use);
0595: Debug().write(Lvl.MESSAGE, this ,
0596: "[Buffer Manager] Page Size: " + page_size);
0597: Debug().write(Lvl.MESSAGE, this ,
0598: "[Buffer Manager] Max pages: " + max_pages);
0599:
0600: // Journal path is currently always the same as database path.
0601: final File journal_path = db_path;
0602: // Max slice size is 1 GB for file scattering class
0603: final long max_slice_size = 16384 * 65536;
0604: // First file extention is 'koi'
0605: final String first_file_ext = "koi";
0606:
0607: // Set up the BufferManager
0608: buffer_manager = new LoggingBufferManager(db_path,
0609: journal_path, read_only_access, max_pages,
0610: page_size, first_file_ext, max_slice_size,
0611: Debug(), enable_logging);
0612: // ^ This is a big constructor. It sets up the logging manager and
0613: // sets a resource store data accessor converter to a scattering
0614: // implementation with a max slice size of 1 GB
0615:
0616: // Start the buffer manager.
0617: try {
0618: buffer_manager.start();
0619: } catch (IOException e) {
0620: Debug().write(Lvl.ERROR, this ,
0621: "Error starting buffer manager");
0622: Debug().writeException(Lvl.ERROR, e);
0623: throw new Error("IO Error: " + e.getMessage());
0624: }
0625:
0626: }
0627:
0628: // What regular expression library are we using?
0629: // If we want the engine to support other regular expression libraries
0630: // then include the additional entries here.
0631:
0632: // Test to see if the regex API exists
0633: boolean regex_api_exists;
0634: try {
0635: Class.forName("java.util.regex.Pattern");
0636: regex_api_exists = true;
0637: } catch (ClassNotFoundException e) {
0638: // Internal API doesn't exist
0639: regex_api_exists = false;
0640: Debug().write(Lvl.MESSAGE, this ,
0641: "Java regex API not available.");
0642: }
0643:
0644: String regex_bridge;
0645: String lib_used;
0646:
0647: String force_lib = getConfigString("force_regex_library",
0648: null);
0649:
0650: // Are we forcing a particular regular expression library?
0651: if (force_lib != null) {
0652: lib_used = force_lib;
0653: // Convert the library string to a class name
0654: regex_bridge = regexStringToClass(force_lib);
0655: } else {
0656: String lib = getConfigString("regex_library", null);
0657: lib_used = lib;
0658: // Use the standard Java 1.4 regular expression library if it is found.
0659: if (regex_api_exists) {
0660: regex_bridge = "com.mckoi.database.regexbridge.JavaRegex";
0661: } else if (lib != null) {
0662: // Convert the library string to a class name
0663: regex_bridge = regexStringToClass(lib);
0664: } else {
0665: regex_bridge = null;
0666: }
0667: }
0668:
0669: if (regex_bridge != null) {
0670: try {
0671: Class c = Class.forName(regex_bridge);
0672: regex_library = (RegexLibrary) c.newInstance();
0673: Debug().write(Lvl.MESSAGE, this ,
0674: "Using regex bridge: " + lib_used);
0675: } catch (Throwable e) {
0676: Debug().write(
0677: Lvl.ERROR,
0678: this ,
0679: "Unable to load regex bridge: "
0680: + regex_bridge);
0681: Debug().writeException(Lvl.WARNING, e);
0682: }
0683: } else {
0684: if (lib_used != null) {
0685: Debug().write(Lvl.ERROR, this ,
0686: "Regex library not known: " + lib_used);
0687: }
0688: Debug().write(Lvl.MESSAGE, this ,
0689: "Regex features disabled.");
0690: }
0691:
0692: // ---------- Plug ins ---------
0693:
0694: try {
0695: // The 'function_factories' property.
0696: String function_factories = getConfigString(
0697: "function_factories", null);
0698: if (function_factories != null) {
0699: List factories = StringUtil.explode(
0700: function_factories, ";");
0701: for (int i = 0; i < factories.size(); ++i) {
0702: String factory_class = factories.get(i)
0703: .toString();
0704: Class c = Class.forName(factory_class);
0705: FunctionFactory fun_factory = (FunctionFactory) c
0706: .newInstance();
0707: addFunctionFactory(fun_factory);
0708: Debug().write(
0709: Lvl.MESSAGE,
0710: this ,
0711: "Successfully added function factory: "
0712: + factory_class);
0713: }
0714: } else {
0715: Debug()
0716: .write(Lvl.MESSAGE, this ,
0717: "No 'function_factories' config property found.");
0718: // If resource missing, do nothing...
0719: }
0720: } catch (Throwable e) {
0721: Debug()
0722: .write(Lvl.ERROR, this ,
0723: "Error parsing 'function_factories' configuration property.");
0724: Debug().writeException(e);
0725: }
0726:
0727: // Flush the contents of the function lookup object.
0728: flushCachedFunctionLookup();
0729:
0730: }
0731:
0732: }
0733:
0734: /**
0735: * Hack - set up the DataCellCache in DatabaseSystem so we can use the
0736: * MasterTableDataSource object without having to boot a new DatabaseSystem.
0737: */
0738: public void setupRowCache(int max_cache_size,
0739: int max_cache_entry_size) {
0740: // Set up the data_cell_cache
0741: data_cell_cache = new DataCellCache(this , max_cache_size,
0742: max_cache_entry_size);
0743: }
0744:
0745: /**
0746: * Returns true if the database is in read only mode. In read only mode,
0747: * any 'write' operations are not permitted.
0748: */
0749: public boolean readOnlyAccess() {
0750: return read_only_access;
0751: }
0752:
0753: /**
0754: * Returns the path of the database in the local file system if the database
0755: * exists within the local file system. If the database is not within the
0756: * local file system then null is returned. It is recommended this method
0757: * is not used unless for legacy or compatability purposes.
0758: */
0759: public File getDatabasePath() {
0760: return db_path;
0761: }
0762:
0763: /**
0764: * Returns true if the database should perform checking of table locks.
0765: */
0766: public boolean tableLockingEnabled() {
0767: return table_lock_check;
0768: }
0769:
0770: /**
0771: * Returns true if we should generate lookup caches in InsertSearch otherwise
0772: * returns false.
0773: */
0774: public boolean lookupComparisonListEnabled() {
0775: return lookup_comparison_list_enabled;
0776: }
0777:
0778: /**
0779: * Returns true if all table indices are kept behind a soft reference that
0780: * can be garbage collected.
0781: */
0782: public boolean softIndexStorage() {
0783: return soft_index_storage;
0784: }
0785:
0786: /**
0787: * Returns the status of the 'always_reindex_dirty_tables' property.
0788: */
0789: public boolean alwaysReindexDirtyTables() {
0790: return always_reindex_dirty_tables;
0791: }
0792:
0793: /**
0794: * Returns true if we shouldn't synchronize with the file system when
0795: * important indexing information is flushed to the disk.
0796: */
0797: public boolean dontSynchFileSystem() {
0798: return dont_synch_filesystem;
0799: }
0800:
0801: /**
0802: * Returns true if during commit the engine should look for any selects
0803: * on a modified table and fail if they are detected.
0804: */
0805: public boolean transactionErrorOnDirtySelect() {
0806: return transaction_error_on_dirty_select;
0807: }
0808:
0809: /**
0810: * Returns true if the parser should ignore case when searching for
0811: * schema/table/column identifiers.
0812: */
0813: public boolean ignoreIdentifierCase() {
0814: return ignore_case_for_identifiers;
0815: }
0816:
0817: /**
0818: * Returns the LoggingBufferManager object enabling us to create no file
0819: * stores in the file system. This provides access to the buffer scheme that
0820: * has been configured.
0821: */
0822: public LoggingBufferManager getBufferManager() {
0823: return buffer_manager;
0824: }
0825:
0826: /**
0827: * Returns the regular expression library from the configuration file.
0828: */
0829: public RegexLibrary getRegexLibrary() {
0830: if (regex_library != null) {
0831: return regex_library;
0832: }
0833: throw new Error(
0834: "No regular expression library found in classpath "
0835: + "and/or in configuration file.");
0836: }
0837:
0838: // ---------- Store System encapsulation ----------
0839:
0840: /**
0841: * Returns the StoreSystem encapsulation being used in this database.
0842: */
0843: public final StoreSystem storeSystem() {
0844: return store_system;
0845: }
0846:
0847: // ---------- Debug logger methods ----------
0848:
0849: /**
0850: * Sets the Writer output for the debug logger.
0851: */
0852: public final void setDebugOutput(java.io.Writer writer) {
0853: // System.out.println("**** Setting debug log output ****" + writer);
0854: // System.out.println(logger);
0855: logger.setOutput(writer);
0856: }
0857:
0858: /**
0859: * Sets the debug minimum level that is output to the logger.
0860: */
0861: public final void setDebugLevel(int level) {
0862: logger.setDebugLevel(level);
0863: }
0864:
0865: /**
0866: * Returns the DebugLogger object that is used to log debug message. This
0867: * method must always return a debug logger that we can log to.
0868: */
0869: public final DebugLogger Debug() {
0870: return logger;
0871: }
0872:
0873: // ---------- Function factories ----------
0874:
0875: /**
0876: * Registers a new FunctionFactory with the database system. The function
0877: * factories are used to resolve a function name into a Function object.
0878: * Function factories are checked in the order they are added to the database
0879: * system.
0880: */
0881: public void addFunctionFactory(FunctionFactory factory) {
0882: synchronized (function_factory_list) {
0883: function_factory_list.add(factory);
0884: }
0885: factory.init();
0886: }
0887:
0888: /**
0889: * Flushes the 'FunctionLookup' object returned by the getFunctionLookup
0890: * method. This should be called if the function factory list has been
0891: * modified in some way.
0892: */
0893: public void flushCachedFunctionLookup() {
0894: FunctionFactory[] factories;
0895: synchronized (function_factory_list) {
0896: factories = (FunctionFactory[]) function_factory_list
0897: .toArray(new FunctionFactory[function_factory_list
0898: .size()]);
0899: }
0900: function_lookup.flushContents(factories);
0901: }
0902:
0903: /**
0904: * Returns a FunctionLookup object that will search through the function
0905: * factories in this database system and find and resolve a function. The
0906: * returned object may throw an exception from the 'generateFunction' method
0907: * if the FunctionDef is invalid. For example, if the number of parameters
0908: * is incorrect or the name can not be found.
0909: */
0910: public FunctionLookup getFunctionLookup() {
0911: return function_lookup;
0912: }
0913:
0914: // ---------- System preparers ----------
0915:
0916: /**
0917: * Given a Transaction.CheckExpression, this will prepare the expression and
0918: * return a new prepared CheckExpression. The default implementation of this
0919: * is to do nothing. However, a sub-class of the system choose to prepare
0920: * the expression, such as resolving the functions via the function lookup,
0921: * and resolving the sub-queries, etc.
0922: */
0923: public Transaction.CheckExpression prepareTransactionCheckConstraint(
0924: DataTableDef table_def, Transaction.CheckExpression check) {
0925:
0926: // ExpressionPreparer expression_preparer = getFunctionExpressionPreparer();
0927: // Resolve the expression to this table and row and evaluate the
0928: // check constraint.
0929: Expression exp = check.expression;
0930: table_def.resolveColumns(ignoreIdentifierCase(), exp);
0931: // try {
0932: // // Prepare the functions
0933: // exp.prepare(expression_preparer);
0934: // }
0935: // catch (Exception e) {
0936: // Debug().writeException(e);
0937: // throw new RuntimeException(e.getMessage());
0938: // }
0939:
0940: return check;
0941: }
0942:
0943: // ---------- Database System Statistics Methods ----------
0944:
0945: /**
0946: * Returns a com.mckoi.util.Stats object that can be used to keep track
0947: * of database statistics for this VM.
0948: */
0949: public final Stats stats() {
0950: return stats;
0951: }
0952:
0953: // ---------- Log directory management ----------
0954:
0955: /**
0956: * Sets the log directory. This should preferably be called during
0957: * initialization. If the log directory is not set or is set to 'null' then
0958: * no logging to files occurs.
0959: */
0960: public final void setLogDirectory(File log_path) {
0961: this .log_directory = log_path;
0962: }
0963:
0964: /**
0965: * Returns the current log directory or null if no logging should occur.
0966: */
0967: public final File getLogDirectory() {
0968: return log_directory;
0969: }
0970:
0971: // ---------- Cache Methods ----------
0972:
0973: /**
0974: * Returns a DataCellCache object that is a shared resource between all
0975: * database's running on this VM. If this returns 'null' then the internal
0976: * cache is disabled.
0977: */
0978: DataCellCache getDataCellCache() {
0979: return data_cell_cache;
0980: }
0981:
0982: // ---------- Dispatch methods ----------
0983:
0984: /**
0985: * The dispatcher.
0986: */
0987: private DatabaseDispatcher dispatcher;
0988:
0989: /**
0990: * Returns the DatabaseDispatcher object.
0991: */
0992: private DatabaseDispatcher getDispatcher() {
0993: synchronized (this ) {
0994: if (dispatcher == null) {
0995: dispatcher = new DatabaseDispatcher(this );
0996: }
0997: return dispatcher;
0998: }
0999: }
1000:
1001: /**
1002: * Creates an event object that is passed into 'postEvent' method
1003: * to run the given Runnable method after the time has passed.
1004: * <p>
1005: * The event created here can be safely posted on the event queue as many
1006: * times as you like. It's useful to create an event as a persistant object
1007: * to service some event. Just post it on the dispatcher when you want
1008: * it run!
1009: */
1010: Object createEvent(Runnable runnable) {
1011: return getDispatcher().createEvent(runnable);
1012: }
1013:
1014: /**
1015: * Adds a new event to be dispatched on the queue after 'time_to_wait'
1016: * milliseconds has passed.
1017: * <p>
1018: * 'event' must be an event object returned via 'createEvent'.
1019: */
1020: void postEvent(int time_to_wait, Object event) {
1021: getDispatcher().postEvent(time_to_wait, event);
1022: }
1023:
1024: /**
1025: * Disposes this object.
1026: */
1027: public void dispose() {
1028: if (buffer_manager != null) {
1029: try {
1030: // Set a check point
1031: store_system.setCheckPoint();
1032: // Stop the buffer manager
1033: buffer_manager.stop();
1034: } catch (IOException e) {
1035: System.out.println("Error stopping buffer manager.");
1036: e.printStackTrace();
1037: }
1038: }
1039: buffer_manager = null;
1040: regex_library = null;
1041: data_cell_cache = null;
1042: config = null;
1043: log_directory = null;
1044: function_factory_list = null;
1045: store_system = null;
1046: if (dispatcher != null) {
1047: dispatcher.finish();
1048: }
1049: // trigger_manager = null;
1050: dispatcher = null;
1051: }
1052:
1053: // ---------- Inner classes ----------
1054:
1055: /**
1056: * A FunctionLookup implementation that will look up a function from a
1057: * list of FunctionFactory objects provided with.
1058: */
1059: private static class DSFunctionLookup implements FunctionLookup {
1060:
1061: private FunctionFactory[] factories;
1062:
1063: public synchronized Function generateFunction(
1064: FunctionDef function_def) {
1065: for (int i = 0; i < factories.length; ++i) {
1066: Function f = factories[i]
1067: .generateFunction(function_def);
1068: if (f != null) {
1069: return f;
1070: }
1071: }
1072: return null;
1073: }
1074:
1075: public synchronized boolean isAggregate(FunctionDef function_def) {
1076: for (int i = 0; i < factories.length; ++i) {
1077: FunctionInfo f_info = factories[i]
1078: .getFunctionInfo(function_def.getName());
1079: if (f_info != null) {
1080: return f_info.getType() == FunctionInfo.AGGREGATE;
1081: }
1082: }
1083: return false;
1084: }
1085:
1086: public synchronized void flushContents(
1087: FunctionFactory[] factories) {
1088: this.factories = factories;
1089: }
1090:
1091: }
1092:
1093: }
|