All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mckoi.database.SimpleTransaction Maven / Gradle / Ivy

Go to download

A full SQL database system with JDBC driver that can be embedded in a Java application or operate as a stand-alone server with clients connecting via TCP/IP.

The newest version!
/**
 * com.mckoi.database.SimpleTransaction  09 Mar 2003
 *
 * Mckoi SQL Database ( http://www.mckoi.com/database )
 * Copyright (C) 2000, 2001, 2002  Diehl and Associates, Inc.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * Version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License Version 2 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * Version 2 along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * Change Log:
 * 
 * 
 */

package com.mckoi.database;

import com.mckoi.debug.DebugLogger;
import java.util.ArrayList;
import java.util.HashMap;

/**
 * An simple implementation of Transaction that provides various facilities for
 * implementing a Transaction object on a number of MasterTableDataSource
 * tables.  The Transaction object is designed such that concurrent
 * modification can happen to the database via other transactions without this
 * view of the database being changed.
 * 

* This object does not implement any transaction control mechanisms such as * 'commit' or 'rollback'. This object is most useful for setting up a * short-term minimal transaction for modifying or querying some data in the * database given on some view. * * @author Tobias Downer */ public abstract class SimpleTransaction { /** * The TransactionSystem context. */ private TransactionSystem system; /** * The list of tables that represent this transaction's view of the database. * (MasterTableDataSource). */ private ArrayList visible_tables; /** * An IndexSet for each visible table from the above list. These objects * are used to represent index information for all tables. * (IndexSet) */ private ArrayList table_indices; /** * A queue of MasterTableDataSource and IndexSet objects that are pending to * be cleaned up when this transaction is disposed. */ private ArrayList cleanup_queue; /** * A cache of tables that have been accessed via this transaction. This is * a map of table_name -> MutableTableDataSource. */ private HashMap table_cache; /** * A local cache for sequence values. */ private HashMap sequence_value_cache; /** * The SequenceManager for this abstract transaction. */ private SequenceManager sequence_manager; /** * If true, this is a read-only transaction and does not permit any type of * modification to this vew of the database. */ private boolean read_only; /** * Constructs the AbstractTransaction. SequenceManager may be null in which * case sequence generator operations are not permitted. */ SimpleTransaction(TransactionSystem system, SequenceManager sequence_manager) { this.system = system; this.visible_tables = new ArrayList(); this.table_indices = new ArrayList(); this.table_cache = new HashMap(); this.sequence_value_cache = new HashMap(); this.sequence_manager = sequence_manager; this.read_only = false; } /** * Sets this transaction as read only. A read only transaction does not * allow for the view to be modified in any way. */ public void setReadOnly() { read_only = true; } /** * Returns true if the transaction is read-only, otherwise returns false. */ public boolean isReadOnly() { return read_only; } /** * Returns the TransactionSystem that this Transaction is part of. */ public final TransactionSystem getSystem() { return system; } /** * Returns a list of all visible tables. */ protected final ArrayList getVisibleTables() { return visible_tables; } /** * Returns a DebugLogger object that we use to log debug messages to. */ public final DebugLogger Debug() { return getSystem().Debug(); } /** * Returns the number of visible tables being managed by this transaction. */ protected int getVisibleTableCount() { return visible_tables.size(); } /** * Returns a MasterTableDataSource object representing table 'n' in the set * of tables visible in this transaction. */ protected MasterTableDataSource getVisibleTable(int n) { return (MasterTableDataSource) visible_tables.get(n); } /** * Searches through the list of tables visible within this transaction and * returns the MasterTableDataSource object with the given name. Returns * null if no visible table with the given name could be found. */ protected MasterTableDataSource findVisibleTable(TableName table_name, boolean ignore_case) { int size = visible_tables.size(); for (int i = 0; i < size; ++i) { MasterTableDataSource master = (MasterTableDataSource) visible_tables.get(i); DataTableDef table_def = master.getDataTableDef(); if (ignore_case) { if (table_def.getTableName().equalsIgnoreCase(table_name)) { return master; } } else { // Not ignore case if (table_def.getTableName().equals(table_name)) { return master; } } } return null; } /** * Returns the IndexSet for the given MasterTableDataSource object that * is visible in this transaction. */ final IndexSet getIndexSetForTable(MasterTableDataSource table) { int sz = table_indices.size(); for (int i = 0; i < sz; ++i) { if (visible_tables.get(i) == table) { return (IndexSet) table_indices.get(i); } } throw new RuntimeException( "MasterTableDataSource not found in this transaction."); } /** * Sets the IndexSet for the given MasterTableDataSource object in this * transaction. */ protected final void setIndexSetForTable(MasterTableDataSource table, IndexSet index_set) { int sz = table_indices.size(); for (int i = 0; i < sz; ++i) { if (visible_tables.get(i) == table) { table_indices.set(i, index_set); return; } } throw new RuntimeException( "MasterTableDataSource not found in this transaction."); } /** * Returns true if the given table name is a dynamically generated table and * is not a table that is found in the table list defined in this transaction * object. *

* It is intended this is implemented by derived classes to handle dynamically * generated tables (tables based on some function or from an external data * source) */ protected boolean isDynamicTable(TableName table_name) { // By default, dynamic tables are not implemented. return false; } /** * If this transaction implementation defines dynamic tables (tables whose * content is determined by some function), this should return the * table here as a MutableTableDataSource object. If the table is not * defined an exception is generated. *

* It is intended this is implemented by derived classes to handle dynamically * generated tables (tables based on some function or from an external data * source) */ protected MutableTableDataSource getDynamicTable(TableName table_name) { // By default, dynamic tables are not implemented. throw new StatementException("Table '" + table_name + "' not found."); } /** * Returns the DataTableDef for a dynamic table defined in this transaction. *

* It is intended this is implemented by derived classes to handle dynamically * generated tables (tables based on some function or from an external data * source) */ protected DataTableDef getDynamicDataTableDef(TableName table_name) { // By default, dynamic tables are not implemented. throw new StatementException("Table '" + table_name + "' not found."); } /** * Returns a string type describing the type of the dynamic table. *

* It is intended this is implemented by derived classes to handle dynamically * generated tables (tables based on some function or from an external data * source) */ protected String getDynamicTableType(TableName table_name) { // By default, dynamic tables are not implemented. throw new StatementException("Table '" + table_name + "' not found."); } /** * Returns a list of all dynamic table names. We can assume that the object * returned here is static so the content of this list should not be changed. *

* It is intended this is implemented by derived classes to handle dynamically * generated tables (tables based on some function or from an external data * source) */ protected TableName[] getDynamicTableList() { return new TableName[0]; } // ----- /** * Returns a new MutableTableDataSource for the view of the * MasterTableDataSource at the start of this transaction. Note that this is * called only once per table accessed in this transaction. */ abstract MutableTableDataSource createMutableTableDataSourceAtCommit( MasterTableDataSource master); // ----- /** * Flushes the table cache and purges the cache of the entry for the given * table name. */ protected void flushTableCache(TableName table_name) { table_cache.remove(table_name); } /** * Adds a MasterTableDataSource and IndexSet to this transaction view. */ void addVisibleTable(MasterTableDataSource table, IndexSet index_set) { if (isReadOnly()) { throw new RuntimeException("Transaction is read-only."); } visible_tables.add(table); table_indices.add(index_set); } /** * Removes a MasterTableDataSource (and its IndexSet) from this view and * puts the information on the cleanup queue. */ void removeVisibleTable(MasterTableDataSource table) { if (isReadOnly()) { throw new RuntimeException("Transaction is read-only."); } int i = visible_tables.indexOf(table); if (i != -1) { visible_tables.remove(i); IndexSet index_set = (IndexSet) table_indices.remove(i); if (cleanup_queue == null) { cleanup_queue = new ArrayList(); } cleanup_queue.add(table); cleanup_queue.add(index_set); // Remove from the table cache TableName table_name = table.getTableName(); table_cache.remove(table_name); } } /** * Updates a MastertableDataSource (and its IndexSet) for this view. The * existing IndexSet/MasterTableDataSource for this is put on the clean up * queue. */ void updateVisibleTable(MasterTableDataSource table, IndexSet index_set) { if (isReadOnly()) { throw new RuntimeException("Transaction is read-only."); } removeVisibleTable(table); addVisibleTable(table, index_set); } /** * Disposes of all IndexSet objects currently accessed by this Transaction. * This includes IndexSet objects on tables that have been dropped by * operations on this transaction and are in the 'cleanup_queue' object. * Disposing of the IndexSet is a common cleanup practice and would typically * be used at the end of a transaction. */ protected void disposeAllIndices() { // Dispose all the IndexSet for each table try { for (int i = 0; i < table_indices.size(); ++i) { ((IndexSet) table_indices.get(i)).dispose(); } } catch (Throwable e) { Debug().writeException(e); } // Dispose all tables we dropped (they will be in the cleanup_queue. try { if (cleanup_queue != null) { for (int i = 0; i < cleanup_queue.size(); i += 2) { MasterTableDataSource master = (MasterTableDataSource) cleanup_queue.get(i); IndexSet index_set = (IndexSet) cleanup_queue.get(i + 1); index_set.dispose(); } cleanup_queue = null; } } catch (Throwable e) { Debug().writeException(e); } } // ----- /** * Returns a TableDataSource object that represents the table with the * given name within this transaction. This table is represented by an * immutable interface. */ public TableDataSource getTableDataSource(TableName table_name) { return getTable(table_name); } /** * Returns a MutableTableDataSource object that represents the table with * the given name within this transaction. Any changes made to this table * are only made within the context of this transaction. This means if a * row is added or removed, it is not made perminant until the transaction * is committed. *

* If the table does not exist then an exception is thrown. */ public MutableTableDataSource getTable(TableName table_name) { // If table is in the cache, return it MutableTableDataSource table = (MutableTableDataSource) table_cache.get(table_name); if (table != null) { return table; } // Is it represented as a master table? MasterTableDataSource master = findVisibleTable(table_name, false); // Not a master table, so see if it's a dynamic table instead, if (master == null) { // Is this a dynamic table? if (isDynamicTable(table_name)) { return getDynamicTable(table_name); } } else { // Otherwise make a view of tha master table data source and put it in // the cache. table = createMutableTableDataSourceAtCommit(master); // Put table name in the cache table_cache.put(table_name, table); } return table; } /** * Returns the DataTableDef for the table with the given name that is * visible within this transaction. *

* Returns null if table name doesn't refer to a table that exists. */ public DataTableDef getDataTableDef(TableName table_name) { // If this is a dynamic table then handle specially if (isDynamicTable(table_name)) { return getDynamicDataTableDef(table_name); } else { // Otherwise return from the pool of visible tables int sz = visible_tables.size(); for (int i = 0; i < sz; ++i) { MasterTableDataSource master = (MasterTableDataSource) visible_tables.get(i); DataTableDef table_def = master.getDataTableDef(); if (table_def.getTableName().equals(table_name)) { return table_def; } } return null; } } /** * Returns a list of table names that are visible within this transaction. */ public TableName[] getTableList() { TableName[] internal_tables = getDynamicTableList(); int sz = visible_tables.size(); // The result list TableName[] tables = new TableName[sz + internal_tables.length]; // Add the master tables for (int i = 0; i < sz; ++i) { MasterTableDataSource master = (MasterTableDataSource) visible_tables.get(i); DataTableDef table_def = master.getDataTableDef(); tables[i] = new TableName(table_def.getSchema(), table_def.getName()); } // Add any internal system tables to the list for (int i = 0; i < internal_tables.length; ++i) { tables[sz + i] = internal_tables[i]; } return tables; } /** * Returns true if the database table object with the given name exists * within this transaction. */ public boolean tableExists(TableName table_name) { // // NASTY HACK: This hack is to get around an annoying recursive problem // // when resolving views. We know this table can't possibly be an // // internal table. // boolean is_view_table = (table_name.getName().equals("sUSRView") && // table_name.getSchema().equals("SYS_INFO")); // if (is_view_table) { // return findVisibleTable(table_name, false) != null; // } // return isDynamicTable(table_name) || realTableExists(table_name); } /** * Returns true if the table with the given name exists within this * transaction. This is different from 'tableExists' because it does not try * to resolve against dynamic tables, and is therefore useful for quickly * checking if a system table exists or not. */ final boolean realTableExists(TableName table_name) { return findVisibleTable(table_name, false) != null; } /** * Attempts to resolve the given table name to its correct case assuming * the table name represents a case insensitive version of the name. For * example, "aPP.CuSTOMer" may resolve to "APP.Customer". If the table * name can not resolve to a valid identifier it returns the input table * name, therefore the actual presence of the table should always be * checked by calling 'tableExists' after this method returns. */ public TableName tryResolveCase(TableName table_name) { // Is it a visable table (match case insensitive) MasterTableDataSource table = findVisibleTable(table_name, true); if (table != null) { return table.getTableName(); } // Is it an internal table? String tschema = table_name.getSchema(); String tname = table_name.getName(); TableName[] list = getDynamicTableList(); for (int i = 0; i < list.length; ++i) { TableName ctable = list[i]; if (ctable.getSchema().equalsIgnoreCase(tschema) && ctable.getName().equalsIgnoreCase(tname)) { return ctable; } } // No matches so return the original object. return table_name; } /** * Returns the type of the table object with the given name. If the table * is a base table, this method returns "TABLE". If it is a virtual table, * it returns the type assigned to by the InternalTableInfo interface. */ public String getTableType(TableName table_name) { if (isDynamicTable(table_name)) { return getDynamicTableType(table_name); } else if (findVisibleTable(table_name, false) != null) { return "TABLE"; } // No table found so report the error. throw new RuntimeException("No table '" + table_name + "' to report type for."); } /** * Resolves the given string to a table name, throwing an exception if * the reference is ambiguous. This also generates an exception if the * table object is not found. */ public TableName resolveToTableName(String current_schema, String name, boolean case_insensitive) { TableName table_name = TableName.resolve(current_schema, name); TableName[] tables = getTableList(); TableName found = null; for (int i = 0; i < tables.length; ++i) { boolean match; if (case_insensitive) { match = tables[i].equalsIgnoreCase(table_name); } else { match = tables[i].equals(table_name); } if (match) { if (found != null) { throw new StatementException("Ambiguous reference: " + name); } else { found = tables[i]; } } } if (found == null) { throw new StatementException("Object not found: " + name); } return found; } // ---------- Sequence management ---------- /** * Flushes the sequence cache. This should be used whenever a sequence * is changed. */ void flushSequenceManager(TableName name) { sequence_manager.flushGenerator(name); } /** * Requests of the sequence generator the next value from the sequence. *

* NOTE: This does NOT check that the user owning this connection has the * correct privs to perform this operation. */ public long nextSequenceValue(TableName name) { if (isReadOnly()) { throw new RuntimeException( "Sequence operation not permitted for read only transaction."); } // Check: if null sequence manager then sequence ops not allowed. if (sequence_manager == null) { throw new RuntimeException("Sequence operations are not permitted."); } SequenceManager seq = sequence_manager; long val = seq.nextValue(this, name); // No synchronized because a DatabaseConnection should be single threaded // only. sequence_value_cache.put(name, new Long(val)); return val; } /** * Returns the sequence value for the given sequence generator that * was last returned by a call to 'nextSequenceValue'. If a value was not * last returned by a call to 'nextSequenceValue' then a statement exception * is generated. *

* NOTE: This does NOT check that the user owning this connection has the * correct privs to perform this operation. */ public long lastSequenceValue(TableName name) { // No synchronized because a DatabaseConnection should be single threaded // only. Long v = (Long) sequence_value_cache.get(name); if (v != null) { return v.longValue(); } else { throw new StatementException( "Current value for sequence generator " + name + " is not available."); } } /** * Sets the sequence value for the given sequence generator. If the generator * does not exist or it is not possible to set the value for the generator * then an exception is generated. *

* NOTE: This does NOT check that the user owning this connection has the * correct privs to perform this operation. */ public void setSequenceValue(TableName name, long value) { if (isReadOnly()) { throw new RuntimeException( "Sequence operation not permitted for read only transaction."); } // Check: if null sequence manager then sequence ops not allowed. if (sequence_manager == null) { throw new RuntimeException("Sequence operations are not permitted."); } SequenceManager seq = sequence_manager; seq.setValue(this, name, value); sequence_value_cache.put(name, new Long(value)); } /** * Returns the current unique id for the given table name. Note that this * is NOT a view of the ID, it is the actual ID value at this time regardless * of transaction. */ public long currentUniqueID(TableName table_name) { MasterTableDataSource master = findVisibleTable(table_name, false); if (master == null) { throw new StatementException( "Table with name '" + table_name + "' could not be " + "found to retrieve unique id."); } return master.currentUniqueID(); } /** * Atomically returns a unique id that can be used as a seed for a set of * unique identifiers for a table. Values returned by this method are * guarenteed unique within this table. This is true even across * transactions. *

* NOTE: This change can not be rolled back. */ public long nextUniqueID(TableName table_name) { if (isReadOnly()) { throw new RuntimeException( "Sequence operation not permitted for read only transaction."); } MasterTableDataSource master = findVisibleTable(table_name, false); if (master == null) { throw new StatementException( "Table with name '" + table_name + "' could not be " + "found to retrieve unique id."); } return master.nextUniqueID(); } /** * Sets the unique id for the given table name. This must only be called * under very controlled situations, such as when altering a table or when * we need to fix sequence corruption. */ public void setUniqueID(TableName table_name, long unique_id) { if (isReadOnly()) { throw new RuntimeException( "Sequence operation not permitted for read only transaction."); } MasterTableDataSource master = findVisibleTable(table_name, false); if (master == null) { throw new StatementException( "Table with name '" + table_name + "' could not be " + "found to set unique id."); } master.setUniqueID(unique_id); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy