/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.persist.DataSpaceManager;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.types.Type;

The base of all HSQLDB table implementations.
Author:Fred Toussi (fredt@users dot sourceforge.net)
Version:2.5.0
Since:1.7.2
/** * The base of all HSQLDB table implementations. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.5.0 * @since 1.7.2 */
public class TableBase implements Cloneable { // types of table public static final int INFO_SCHEMA_TABLE = 1; public static final int SYSTEM_SUBQUERY = 2; public static final int TEMP_TABLE = 3; public static final int MEMORY_TABLE = 4; public static final int CACHED_TABLE = 5; public static final int TEMP_TEXT_TABLE = 6; public static final int TEXT_TABLE = 7; public static final int VIEW_TABLE = 8; public static final int RESULT_TABLE = 9; public static final int TRANSITION_TABLE = 10; public static final int FUNCTION_TABLE = 11; public static final int SYSTEM_TABLE = 12; public static final int CHANGE_SET_TABLE = 13; public static final int MODULE_TABLE = 14; // public static final int SCOPE_ROUTINE = 20; public static final int SCOPE_STATEMENT = 21; public static final int SCOPE_TRANSACTION = 22; public static final int SCOPE_SESSION = 23; public static final int SCOPE_FULL = 24; // public PersistentStore store; public int persistenceScope; public long persistenceId; int tableSpace = DataSpaceManager.tableIdDefault; // Index[] indexList; // first index is the primary key index public Database database; int[] bestRowIdentifierCols; // column set for best index boolean bestRowIdentifierStrict; // true if it has no nullable column int[] bestIndexForColumn; // index of the 'best' index for each column Index bestIndex; // the best index overall - null if there is no user-defined index Index fullIndex; // index on all columns boolean[] colNotNull; // nullability Type[] colTypes; // types of columns protected int columnCount; boolean[] emptyColumnCheckList; // int tableType; protected boolean isReadOnly; protected boolean isTemp; protected boolean isCached; protected boolean isText; boolean isView; protected boolean isWithDataSource; public boolean isSessionBased; protected boolean isSchemaBased; protected boolean isLogged; public boolean isSystemVersioned; boolean hasLobColumn; // TableBase() {} public TableBase duplicate() { TableBase copy; try { copy = (TableBase) super.clone(); } catch (CloneNotSupportedException ex) { throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } copy.persistenceId = database.persistentStoreCollection.getNextId(); return copy; } public final int getTableType() { return tableType; } public long getPersistenceId() { return persistenceId; } public int getSpaceID() { return tableSpace; } public void setSpaceID(int id) { tableSpace = id; } int getId() { return 0; } public final boolean onCommitPreserve() { return persistenceScope == TableBase.SCOPE_SESSION; } public final RowIterator rowIterator(Session session) { PersistentStore store = getRowStore(session); return getPrimaryIndex().firstRow(session, store, null, 0, null); } public final RowIterator rowIterator(PersistentStore store) { return getPrimaryIndex().firstRow(store); } public final int getIndexCount() { return indexList.length; } public final Index getPrimaryIndex() { return indexList.length > 0 ? indexList[0] : null; } public final Type[] getPrimaryKeyTypes() { return indexList[0].getColumnTypes(); } public final boolean hasPrimaryKey() { return indexList[0].getColumnCount() > 0; } public final int[] getPrimaryKey() { return indexList[0].getColumns(); }
Returns an array of Type indicating the SQL type of the columns
/** * Returns an array of Type indicating the SQL type of the columns */
public final Type[] getColumnTypes() { return colTypes; }
Returns the Index object at the given index
/** * Returns the Index object at the given index */
public final Index getIndex(int i) { return indexList[i]; }
Returns the indexes
/** * Returns the indexes */
public final Index[] getIndexList() { return indexList; }
Returns empty boolean array.
/** * Returns empty boolean array. */
public final boolean[] getNewColumnCheckList() { return new boolean[getColumnCount()]; } public final boolean[] getEmptyColumnCheckList() { return emptyColumnCheckList; }
Returns the count of all visible columns.
/** * Returns the count of all visible columns. */
public int getColumnCount() { return columnCount; }
Returns the count of all columns.
/** * Returns the count of all columns. */
public final int getDataColumnCount() { return colTypes.length; } public boolean isSystemVersioned() { return isSystemVersioned; }
This method is called whenever there is a change to table structure and serves two purposes: (a) to reset the best set of columns that identify the rows of the table (b) to reset the best index that can be used to find rows of the table given a column value. (a) gives most weight to a primary key index, followed by a unique address with the lowest count of nullable columns. Otherwise there is no best row identifier. (b) finds for each column an index with a corresponding first column. It uses any type of visible index and accepts the one with the largest column count. bestIndex is the user defined, primary key, the first unique index, or the first non-unique index. NULL if there is no user-defined index.
/** * This method is called whenever there is a change to table structure and * serves two purposes: (a) to reset the best set of columns that identify * the rows of the table (b) to reset the best index that can be used * to find rows of the table given a column value. * * (a) gives most weight to a primary key index, followed by a unique * address with the lowest count of nullable columns. Otherwise there is * no best row identifier. * * (b) finds for each column an index with a corresponding first column. * It uses any type of visible index and accepts the one with the largest * column count. * * bestIndex is the user defined, primary key, the first unique index, or * the first non-unique index. NULL if there is no user-defined index. * */
public final void setBestRowIdentifiers() { int[] briCols = null; int briColsCount = 0; boolean isStrict = false; int nNullCount = 0; // ignore if called prior to completion of primary key construction if (colNotNull == null) { return; } bestIndex = null; bestIndexForColumn = new int[colTypes.length]; ArrayUtil.fillArray(bestIndexForColumn, -1); for (int i = 0; i < indexList.length; i++) { Index index = indexList[i]; int[] cols = index.getColumns(); int colsCount = index.getColumnCount(); if (colsCount == 0) { continue; } if (i == 0) { isStrict = true; } if (bestIndexForColumn[cols[0]] == -1) { bestIndexForColumn[cols[0]] = i; } else { Index existing = indexList[bestIndexForColumn[cols[0]]]; if (colsCount > existing.getColumns().length) { bestIndexForColumn[cols[0]] = i; } } if (!index.isUnique()) { if (bestIndex == null) { bestIndex = index; } continue; } int nnullc = 0; for (int j = 0; j < colsCount; j++) { if (colNotNull[cols[j]]) { nnullc++; } } if (bestIndex != null) { bestIndex = index; } if (nnullc == colsCount) { if (briCols == null || briColsCount != nNullCount || colsCount < briColsCount) { // nothing found before || // found but has null columns || // found but has more columns than this index briCols = cols; briColsCount = colsCount; nNullCount = colsCount; isStrict = true; } continue; } else if (isStrict) { continue; } else if (briCols == null || colsCount < briColsCount || nnullc > nNullCount) { // nothing found before || // found but has more columns than this index|| // found but has fewer not null columns than this index briCols = cols; briColsCount = colsCount; nNullCount = nnullc; } } if (briCols == null || briColsCount == briCols.length) { bestRowIdentifierCols = briCols; } else { bestRowIdentifierCols = ArrayUtil.arraySlice(briCols, 0, briColsCount); } bestRowIdentifierStrict = isStrict; if (indexList[0].getColumnCount() > 0) { bestIndex = indexList[0]; } } public boolean[] getColumnNotNull() { return this.colNotNull; } public final void createPrimaryIndex(int[] pkcols, Type[] pktypes, HsqlName name) { Index newIndex = getNewPrimaryIndex(pkcols, pktypes, name); addIndexStructure(newIndex); } Index getNewPrimaryIndex(int[] pkcols, Type[] pktypes, HsqlName name) { long id = database.persistentStoreCollection.getNextId(); return database.logger.newIndex(name, id, this, pkcols, null, null, pktypes, true, pkcols.length > 0, pkcols.length > 0, false); } public final Index createAndAddIndexStructure(Session session, HsqlName name, int[] columns, boolean[] descending, boolean[] nullsLast, boolean unique, boolean constraint, boolean forward) { Index newindex = createIndexStructure(name, columns, descending, nullsLast, false, unique, constraint, forward); addIndex(session, newindex); return newindex; } public final Index createIndexStructure(HsqlName name, int[] columns, boolean[] descending, boolean[] nullsLast, boolean primaryKey, boolean unique, boolean constraint, boolean forward) { int s = columns.length; int[] cols = new int[s]; Type[] types = new Type[s]; for (int j = 0; j < s; j++) { cols[j] = columns[j]; types[j] = colTypes[cols[j]]; } long id = database.persistentStoreCollection.getNextId(); Index newIndex = database.logger.newIndex(name, id, this, cols, descending, nullsLast, types, primaryKey, unique, constraint, forward); return newIndex; }
Performs Table structure modification and changes to the index nodes to remove a given index from a MEMORY or TEXT table. Not for PK index.
/** * Performs Table structure modification and changes to the index nodes * to remove a given index from a MEMORY or TEXT table. Not for PK index. * */
public void dropIndex(Session session, int todrop) { Index[] list = (Index[]) ArrayUtil.toAdjustedArray(indexList, null, todrop, -1); for (int i = 0; i < list.length; i++) { list[i].setPosition(i); } resetAccessorKeys(session, list); indexList = list; setBestRowIdentifiers(); } final void addIndexStructure(Index index) { indexList = getNewIndexArray(index, indexList); setBestRowIdentifiers(); } static Index[] getNewIndexArray(Index index, Index[] list) { int i = 0; for (; i < list.length; i++) { Index current = list[i]; int order = index.getIndexOrderValue() - current.getIndexOrderValue(); if (order < 0) { break; } } boolean replacePK = index.isPrimaryKey() && list.length > 0 && list[0].isPrimaryKey(); if (replacePK) { list = (Index[]) ArrayUtil.duplicateArray(list); list[0] = index; } else { list = (Index[]) ArrayUtil.toAdjustedArray(list, index, i, 1); } for (i = 0; i < list.length; i++) { list[i].setPosition(i); } return list; } final void addIndex(Session session, Index index) { Index[] list = getNewIndexArray(index, indexList); try { resetAccessorKeys(session, list); } catch (HsqlException e) { for (int i = 0; i < indexList.length; i++) { indexList[i].setPosition(i); } throw e; } indexList = list; setBestRowIdentifiers(); } private void resetAccessorKeys(Session session, Index[] indexes) { if (store != null) { store.resetAccessorKeys(session, indexes); return; } } public final void setIndexes(Index[] indexes) { this.indexList = indexes; } public final Object[] getEmptyRowData() { return new Object[getDataColumnCount()]; }
Create new memory-resident index. For MEMORY and TEXT tables.
/** * Create new memory-resident index. For MEMORY and TEXT tables. */
public final Index createIndex(Session session, HsqlName name, int[] columns, boolean[] descending, boolean[] nullsLast, boolean unique, boolean constraint, boolean forward) { Index newIndex = createAndAddIndexStructure(session, name, columns, descending, nullsLast, unique, constraint, forward); return newIndex; } /** * @todo - this is not for general use, as it returns true when table has no * rows, but not where it has rows that are not visible by session. * current usage is fine. */
Returns true if the table has any rows at all.
/** * Returns true if the table has any rows at all. */
public final boolean isEmpty(Session session) { if (getIndexCount() == 0) { return true; } PersistentStore store = getRowStore(session); return getIndex(0).isEmpty(store); } public PersistentStore getRowStore(Session session) { return store == null ? session.sessionData.persistentStoreCollection.getStore(this) : store; } }