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

com.mckoi.database.JoinedTable 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.JoinedTable  20 Sep 2002
 *
 * 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.util.IntegerVector;
import com.mckoi.util.BlockIntegerList;

/**
 * A Table that represents the result of one or more other tables joined
 * together.  VirtualTable and NaturallyJoinedTable are derived from this
 * class.
 *
 * @author Tobias Downer
 */

public abstract class JoinedTable extends Table {

  /**
   * The list of tables that make up the join.
   */
  protected Table[] reference_list;
  
  /**
   * The schemes to describe the entity relation in the given column.
   */
  protected SelectableScheme[] column_scheme;

  /**
   * These two arrays are lookup tables created in the constructor.  They allow
   * for quick resolution of where a given column should be 'routed' to in
   * the ancestors.
   */
  /**
   * Maps the column number in this table to the reference_list array to route
   * to.
   */
  protected int[] column_table;

  /**
   * Gives a column filter to the given column to route correctly to the
   * ancestor.
   */
  protected int[] column_filter;

  /**
   * The column that we are sorted against.  This is an optimization set by
   * the 'optimisedPostSet' method.
   */
  private int sorted_against_column = -1;

  /**
   * The DataTableDef object that describes the columns and name of this
   * table.
   */
  private DataTableDef vt_table_def;

  /**
   * Incremented when the roots are locked.
   * See the 'lockRoot' and 'unlockRoot' methods.
   * NOTE: This should only ever be 1 or 0.
   */
  private byte roots_locked;

  /**
   * Constructs the JoinedTable with the list of tables in the parent.
   */
  JoinedTable(Table[] tables) {
    super();
    init(tables);
  }

  /**
   * Constructs the JoinedTable with a single table.
   */
  JoinedTable(Table table) {
    super();
    Table[] tables = new Table[1];
    tables[0] = table;
    init(tables);
  }

  /**
   * Protected constructor.
   */
  protected JoinedTable() {
    super();
  }
  
  /**
   * Helper function for initializing the variables in the joined table.
   */
  protected void init(Table[] tables) {
    int table_count = tables.length;
    reference_list = tables;

    final int col_count = getColumnCount();
    column_scheme = new SelectableScheme[col_count];

    vt_table_def = new DataTableDef();

    // Generate look up tables for column_table and column_filter information

    column_table = new int[col_count];
    column_filter = new int[col_count];
    int index = 0;
    for (int i = 0; i < reference_list.length; ++i) {

      Table cur_table = reference_list[i];
      DataTableDef cur_table_def = cur_table.getDataTableDef();
      int ref_col_count = cur_table.getColumnCount();

      // For each column
      for (int n = 0; n < ref_col_count; ++n) {
        column_filter[index] = n;
        column_table[index] = i;
        ++index;
        
        // Add this column to the data table def of this table.
        vt_table_def.addVirtualColumn(
                         new DataTableColumnDef(cur_table_def.columnAt(n)));
      }

    }

    // Final setup the DataTableDef for this virtual table

    vt_table_def.setTableName(new TableName(null, "#VIRTUAL TABLE#"));
    
    vt_table_def.setImmutable();

  }

  /**
   * Returns a row reference list.  This is an IntegerVector that represents a
   * 'reference' to the rows in our virtual table.
   * 

* ISSUE: We should be able to optimise these types of things out. */ private IntegerVector calculateRowReferenceList() { int size = getRowCount(); IntegerVector all_list = new IntegerVector(size); for (int i = 0; i < size; ++i) { all_list.addInt(i); } return all_list; } /** * We simply pick the first table to resolve the Database object. */ public Database getDatabase() { return reference_list[0].getDatabase(); } /** * Returns the number of columns in the table. This simply returns the * column counts in the parent table(s). */ public int getColumnCount() { int column_count_sum = 0; for (int i = 0; i < reference_list.length; ++i) { column_count_sum += reference_list[i].getColumnCount(); } return column_count_sum; } /** * Given a fully qualified variable field name, ie. 'APP.CUSTOMER.CUSTOMERID' * this will return the column number the field is at. Returns -1 if the * field does not exist in the table. */ public int findFieldName(Variable v) { int col_index = 0; for (int i = 0; i < reference_list.length; ++i) { int col = reference_list[i].findFieldName(v); if (col != -1) { return col + col_index; } col_index += reference_list[i].getColumnCount(); } return -1; } /** * Returns a fully qualified Variable object that represents the name of * the column at the given index. For example, * new Variable(new TableName("APP", "CUSTOMER"), "ID") */ public final Variable getResolvedVariable(int column) { Table parent_table = reference_list[column_table[column]]; return parent_table.getResolvedVariable(column_filter[column]); } /** * Returns the list of Table objects that represent this VirtualTable. */ protected final Table[] getReferenceTables() { return reference_list; } /** * This is an optimisation that should only be called _after_ a 'set' method * has been called. Because the 'select' operation returns a set that is * ordered by the given column, we can very easily generate a * SelectableScheme object that can handle this column. * So 'column' is the column in which this virtual table is naturally ordered * by. * NOTE: The internals of this method may be totally commented out and the * database will still operate correctly. However this greatly speeds up * situations when you perform multiple consequtive operations on the same * column. */ void optimisedPostSet(int column) { sorted_against_column = column; } /** * Returns a SelectableScheme for the given column in the given VirtualTable * row domain. This searches down through the tables ancestors until it * comes across a table with a SelectableScheme where the given column is * fully resolved. In most cases, this will be the root DataTable. */ SelectableScheme getSelectableSchemeFor(int column, int original_column, Table table) { // First check if the given SelectableScheme is in the column_scheme array SelectableScheme scheme = column_scheme[column]; if (scheme != null) { if (table == this) { return scheme; } else { return scheme.getSubsetScheme(table, original_column); } } // If it isn't then we need to calculate it SelectableScheme ss; // Optimization: The table may be naturally ordered by a column. If it // is we don't try to generate an ordered set. if (sorted_against_column != -1 && sorted_against_column == column) { InsertSearch isop = new InsertSearch(this, column, calculateRowReferenceList()); isop.RECORD_UID = false; ss = isop; column_scheme[column] = ss; if (table != this) { ss = ss.getSubsetScheme(table, original_column); } } else { // Otherwise we must generate the ordered set from the information in // a parent index. Table parent_table = reference_list[column_table[column]]; ss = parent_table.getSelectableSchemeFor( column_filter[column], original_column, table); if (table == this) { column_scheme[column] = ss; } } return ss; } /** * Given a set, this trickles down through the Table hierarchy resolving * the given row_set to a form that the given ancestor understands. * Say you give the set { 0, 1, 2, 3, 4, 5, 6 }, this function may check * down three levels and return a new 7 element set with the rows fully * resolved to the given ancestors domain. */ void setToRowTableDomain(int column, IntegerVector row_set, TableDataSource ancestor) { if (ancestor == this) { return; } else { int table_num = column_table[column]; Table parent_table = reference_list[table_num]; // Resolve the rows into the parents indices. (MANGLES row_set) resolveAllRowsForTableAt(row_set, table_num); parent_table.setToRowTableDomain(column_filter[column], row_set, ancestor); return; } } /** * Returns an object that contains fully resolved, one level only information * about the DataTable and the row indices of the data in this table. * This information can be used to construct a new VirtualTable. We need * to supply an empty RawTableInformation object. */ RawTableInformation resolveToRawTable(RawTableInformation info, IntegerVector row_set) { if (this instanceof RootTable) { info.add((RootTable) this, calculateRowReferenceList()); } else { for (int i = 0; i < reference_list.length; ++i) { IntegerVector new_row_set = new IntegerVector(row_set); // Resolve the rows into the parents indices. resolveAllRowsForTableAt(new_row_set, i); Table table = reference_list[i]; if (table instanceof RootTable) { info.add((RootTable) table, new_row_set); } else { ((JoinedTable) table).resolveToRawTable(info, new_row_set); } } } return info; } /** * Return the list of DataTable and row sets that make up the raw information * in this table. */ RawTableInformation resolveToRawTable(RawTableInformation info) { IntegerVector all_list = new IntegerVector(); int size = getRowCount(); for (int i = 0; i < size; ++i) { all_list.addInt(i); } return resolveToRawTable(info, all_list); } /** * Returns the DataTableDef object that describes the columns in this * table. For a VirtualTable, this object contains the union of * all the columns in the children in the order set. The name of a * virtual table is the concat of all the parent table names. The * schema is set to null. */ public DataTableDef getDataTableDef() { return vt_table_def; } /** * Returns an object that represents the information in the given cell * in the table. */ public TObject getCellContents(int column, int row) { int table_num = column_table[column]; Table parent_table = reference_list[table_num]; row = resolveRowForTableAt(row, table_num); return parent_table.getCellContents(column_filter[column], row); } /** * Returns an Enumeration of the rows in this table. * The Enumeration is a fast way of retrieving consequtive rows in the table. */ public RowEnumeration rowEnumeration() { return new SimpleRowEnumeration(getRowCount()); } /** * Adds a DataTableListener to the DataTable objects at the root of this * table tree hierarchy. If this table represents the join of a number of * tables then the DataTableListener is added to all the DataTable objects * at the root. *

* A DataTableListener is notified of all modifications to the raw entries * of the table. This listener can be used for detecting changes in VIEWs, * for triggers or for caching of common queries. */ void addDataTableListener(DataTableListener listener) { for (int i = 0; i < reference_list.length; ++i) { reference_list[i].addDataTableListener(listener); } } /** * Removes a DataTableListener from the DataTable objects at the root of * this table tree hierarchy. If this table represents the join of a * number of tables, then the DataTableListener is removed from all the * DataTable objects at the root. */ void removeDataTableListener(DataTableListener listener) { for (int i = 0; i < reference_list.length; ++i) { reference_list[i].removeDataTableListener(listener); } } /** * Locks the root table(s) of this table so that it is impossible to * overwrite the underlying rows that may appear in this table. * This is used when cells in the table need to be accessed 'outside' the * lock. So we may have late access to cells in the table. * 'lock_key' is a given key that will also unlock the root table(s). * NOTE: This is nothing to do with the 'LockingMechanism' object. */ public void lockRoot(int lock_key) { // For each table, recurse. roots_locked++; for (int i = 0; i < reference_list.length; ++i) { reference_list[i].lockRoot(lock_key); } } /** * Unlocks the root tables so that the underlying rows may * once again be used if they are not locked and have been removed. This * should be called some time after the rows have been locked. */ public void unlockRoot(int lock_key) { // For each table, recurse. roots_locked--; for (int i = 0; i < reference_list.length; ++i) { reference_list[i].unlockRoot(lock_key); } } /** * Returns true if the table has its row roots locked (via the lockRoot(int) * method. */ public boolean hasRootsLocked() { return roots_locked != 0; } /** * Prints a graph of the table hierarchy to the stream. */ public void printGraph(java.io.PrintStream out, int indent) { for (int i = 0; i < indent; ++i) { out.print(' '); } out.println("JT[" + getClass()); for (int i = 0; i < reference_list.length; ++i) { reference_list[i].printGraph(out, indent + 2); } for (int i = 0; i < indent; ++i) { out.print(' '); } out.println("]"); } // ---------- Abstract methods ---------- /** * Given a row and a table index (to a parent reference table), this will * return the row index in the given parent table for the given row. */ protected abstract int resolveRowForTableAt(int row_number, int table_num); /** * Given an IntegerVector that represents a list of pointers to rows in this * table, this resolves the rows to row indexes in the given parent table. * This method changes the 'row_set' IntegerVector object. */ protected abstract void resolveAllRowsForTableAt(IntegerVector row_set, int table_num); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy