/* Copyright (c) 1995-2000, The Hypersonic SQL 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 Hypersonic SQL 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 THE HYPERSONIC SQL GROUP, * 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. * * This software consists of voluntary contributions made by many individuals * on behalf of the Hypersonic SQL Group. * * * For work added by the HSQL Development Group: * * Copyright (c) 2001-2009, 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.HSQLInterface.HSQLParseException; import org.hsqldb.HsqlNameManager.HsqlName; import org.hsqldb.index.Index; import org.hsqldb.index.IndexAVL; import org.hsqldb.lib.ArrayUtil; import org.hsqldb.lib.HashMappedList; import org.hsqldb.lib.HsqlArrayList; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.lib.OrderedIntHashSet; import org.hsqldb.lib.Set; import org.hsqldb.lib.StringUtil; import org.hsqldb.navigator.RowIterator; import org.hsqldb.navigator.RowSetNavigator; import org.hsqldb.persist.CachedObject; import org.hsqldb.persist.PersistentStore; import org.hsqldb.result.Result; import org.hsqldb.rights.Grantee; import org.hsqldb.store.ValuePool; import org.hsqldb.types.Type; import org.hsqldb.types.LobData; // fredt@users 20020130 - patch 491987 by jimbag@users - made optional // fredt@users 20020405 - patch 1.7.0 by fredt - quoted identifiers // for sql standard quoted identifiers for column and table names and aliases // applied to different places // fredt@users 20020225 - patch 1.7.0 - restructuring // some methods moved from Database.java, some rewritten // changes to several methods // fredt@users 20020225 - patch 1.7.0 - ON DELETE CASCADE // fredt@users 20020225 - patch 1.7.0 - named constraints // boucherb@users 20020225 - patch 1.7.0 - multi-column primary keys // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP) // tony_lai@users 20020820 - patch 595099 - user defined PK name // tony_lai@users 20020820 - patch 595172 - drop constraint fix // fredt@users 20021210 - patch 1.7.2 - better ADD / DROP INDEX for non-CACHED tables // fredt@users 20030901 - patch 1.7.2 - allow multiple nulls for UNIQUE columns // fredt@users 20030901 - patch 1.7.2 - reworked IDENTITY support // achnettest@users 20040130 - patch 878288 - bug fix for new indexes in memory tables by Arne Christensen // boucherb@users 20040327 - doc 1.7.2 - javadoc updates // boucherb@users 200404xx - patch 1.7.2 - proper uri for getCatalogName // fredt@users 20050000 - 1.8.0 updates in several areas // fredt@users 20050220 - patch 1.8.0 enforcement of DECIMAL precision/scale // fredt@users 1.9.0 referential constraint enforcement moved to CompiledStatementExecutor // fredt@users 1.9.0 base functionality moved to TableBase /** * Holds the data structures and methods for creation of a database table. * * * Extensively rewritten and extended in successive versions of HSQLDB. * * @author Thomas Mueller (Hypersonic SQL Group) * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 1.9.0 * @since Hypersonic SQL */ public class Table extends TableBase implements SchemaObject { public static final Table[] emptyArray = new Table[]{}; // main properties protected HsqlName tableName; // public HashMappedList columnList; // columns in table int identityColumn; // -1 means no such column NumberSequence identitySequence; // next value of identity column // ----------------------------------------------------------------------- Constraint[] constraintList; // constrainst for the table Constraint[] fkPath; // Constraint[] fkConstraints; // Constraint[] fkMainConstraints; Constraint[] checkConstraints; TriggerDef[] triggerList; TriggerDef[][] triggerLists; // array of trigger lists Expression[] colDefaults; // fredt - expressions of DEFAULT values protected int[] defaultColumnMap; // fred - holding 0,1,2,3,... private boolean hasDefaultValues; //fredt - shortcut for above // public Table(Database database, HsqlName name, int type) { this.database = database; tableName = name; persistenceId = database.persistentStoreCollection.getNextId(); switch (type) { case SYSTEM_SUBQUERY : persistenceScope = SCOPE_STATEMENT; isSessionBased = true; break; case SYSTEM_TABLE : persistenceScope = SCOPE_FULL; isSchemaBased = true; break; case CACHED_TABLE : if (DatabaseURL.isFileBasedDatabaseType(database.getType())) { persistenceScope = SCOPE_FULL; isSchemaBased = true; isCached = true; isLogged = !database.isFilesReadOnly(); break; } type = MEMORY_TABLE; // fall through case MEMORY_TABLE : persistenceScope = SCOPE_FULL; isSchemaBased = true; isLogged = !database.isFilesReadOnly(); break; case TEMP_TABLE : persistenceScope = SCOPE_TRANSACTION; isTemp = true; isSchemaBased = true; isSessionBased = true; break; case TEMP_TEXT_TABLE : persistenceScope = SCOPE_SESSION; if (!DatabaseURL.isFileBasedDatabaseType(database.getType())) { throw Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY); } isSchemaBased = true; isSessionBased = true; isTemp = true; isText = true; isReadOnly = true; break; case TEXT_TABLE : persistenceScope = SCOPE_FULL; if (!DatabaseURL.isFileBasedDatabaseType(database.getType())) { throw Error.error(ErrorCode.DATABASE_IS_MEMORY_ONLY); } isSchemaBased = true; isText = true; break; case VIEW_TABLE : persistenceScope = SCOPE_STATEMENT; isSchemaBased = true; isSessionBased = true; isView = true; break; case RESULT_TABLE : persistenceScope = SCOPE_SESSION; isSessionBased = true; break; default : throw Error.runtimeError(ErrorCode.U_S0500, "Table"); } // type may have changed above for CACHED tables tableType = type; primaryKeyCols = null; primaryKeyTypes = null; identityColumn = -1; columnList = new HashMappedList(); indexList = Index.emptyArray; constraintList = Constraint.emptyArray; fkPath = Constraint.emptyArray; fkConstraints = Constraint.emptyArray; fkMainConstraints = Constraint.emptyArray; checkConstraints = Constraint.emptyArray; triggerList = TriggerDef.emptyArray; triggerLists = new TriggerDef[TriggerDef.NUM_TRIGS][]; for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) { triggerLists[i] = TriggerDef.emptyArray; } if (database.isFilesReadOnly() && isFileBased()) { this.isReadOnly = true; } if (!isTemp) { createDefaultStore(); } } public Table(Table table, HsqlName name) { persistenceScope = SCOPE_STATEMENT; name.schema = SqlInvariants.SYSTEM_SCHEMA_HSQLNAME; this.tableName = name; this.database = table.database; this.tableType = RESULT_TABLE; this.columnList = table.columnList; this.columnCount = table.columnCount; this.indexList = Index.emptyArray; this.constraintList = Constraint.emptyArray; createPrimaryKey(); } public void createDefaultStore() { store = database.logger.newStore(null, database.persistentStoreCollection, this, true); } public int getType() { return SchemaObject.TABLE; } /** * Returns the HsqlName object fo the table */ public final HsqlName getName() { return tableName; } /** * Returns the catalog name or null, depending on a database property. */ public HsqlName getCatalogName() { return database.getCatalogName(); } /** * Returns the schema name. */ public HsqlName getSchemaName() { return tableName.schema; } public Grantee getOwner() { return tableName.schema.owner; } public OrderedHashSet getReferences() { OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < colTypes.length; i++) { if (colTypes[i].isDomainType() || colTypes[i].isDistinctType()) { HsqlName name = ((SchemaObject) colTypes[i]).getName(); set.add(name); } } return set; } public OrderedHashSet getComponents() { OrderedHashSet set = new OrderedHashSet(); set.addAll(constraintList); set.addAll(triggerList); for (int i = 0; i < indexList.length; i++) { if (!indexList[i].isConstraint()) { set.add(indexList[i]); } } return set; } public void compile(Session session) {} String[] getSQL(OrderedHashSet resolved, OrderedHashSet unresolved) { for (int i = 0; i < constraintList.length; i++) { Constraint c = constraintList[i]; if (c.isForward) { unresolved.add(c); } else if (c.getConstraintType() == Constraint.UNIQUE || c.getConstraintType() == Constraint.PRIMARY_KEY) { resolved.add(c.getName()); } } HsqlArrayList list = new HsqlArrayList(); list.add(getSQL()); // readonly for TEXT tables only if (isText()) { if (((TextTable) this).isConnected() && isDataReadOnly()) { StringBuffer sb = new StringBuffer(64); sb.append(Tokens.T_SET).append(' ').append( Tokens.T_TABLE).append(' '); sb.append(getName().getSchemaQualifiedStatementName()); sb.append(' ').append(Tokens.T_READONLY).append(' '); sb.append(Tokens.T_TRUE); list.add(sb.toString()); } // data source String dataSource = ((TextTable) this).getDataSourceDDL(); if (dataSource != null) { list.add(dataSource); } // header String header = ((TextTable) this).getDataSourceHeader(); if (header != null) { list.add(header); } } if (!isTemp && hasIdentityColumn()) { list.add(NumberSequence.getRestartSQL(this)); } for (int i = 0; i < indexList.length; i++) { if (!indexList[i].isConstraint()) { list.add(indexList[i].getSQL()); } } String[] array = new String[list.size()]; list.toArray(array); return array; } String[] getTriggerSQL() { String[] array = new String[triggerList.length]; for (int i = 0; i < triggerList.length; i++) { array[i] = triggerList[i].getSQL(); } return array; } public String getSQL() { StringBuffer sb = new StringBuffer(); sb.append(Tokens.T_CREATE).append(' '); if (isTemp()) { sb.append(Tokens.T_GLOBAL).append(' '); sb.append(Tokens.T_TEMPORARY).append(' '); } else if (isText()) { sb.append(Tokens.T_TEXT).append(' '); } else if (isCached()) { sb.append(Tokens.T_CACHED).append(' '); } else { sb.append(Tokens.T_MEMORY).append(' '); } sb.append(Tokens.T_TABLE).append(' '); sb.append(getName().getSchemaQualifiedStatementName()); sb.append('('); int columns = getColumnCount(); int[] pk = getPrimaryKey(); Constraint pkConst = getPrimaryConstraint(); for (int j = 0; j < columns; j++) { ColumnSchema column = getColumn(j); String colname = column.getName().statementName; Type type = column.getDataType(); if (j > 0) { sb.append(','); } sb.append(colname); sb.append(' '); sb.append(type.getTypeDefinition()); String defaultString = column.getDefaultSQL(); if (defaultString != null) { sb.append(' ').append(Tokens.T_DEFAULT).append(' '); sb.append(defaultString); } if (column.isIdentity()) { sb.append(' ').append(column.getIdentitySequence().getSQL()); } if (!column.isNullable()) { Constraint c = getNotNullConstraintForColumn(j); if (c != null && !c.getName().isReservedName()) { sb.append(' ').append(Tokens.T_CONSTRAINT).append( ' ').append(c.getName().statementName); } sb.append(' ').append(Tokens.T_NOT).append(' ').append( Tokens.T_NULL); } if (pk.length == 1 && j == pk[0] && pkConst.getName().isReservedName()) { sb.append(' ').append(Tokens.T_PRIMARY).append(' ').append( Tokens.T_KEY); } } Constraint[] constraintList = getConstraints(); for (int j = 0, vSize = constraintList.length; j < vSize; j++) { Constraint c = constraintList[j]; if (!c.isForward) { String d = c.getSQL(); if (d.length() > 0) { sb.append(','); sb.append(d); } } } sb.append(')'); if (onCommitPreserve()) { sb.append(' ').append(Tokens.T_ON).append(' '); sb.append(Tokens.T_COMMIT).append(' ').append(Tokens.T_PRESERVE); sb.append(' ').append(Tokens.T_ROWS); } return sb.toString(); } public String getIndexRootsSQL() { StringBuffer sb = new StringBuffer(128); sb.append(Tokens.T_SET).append(' ').append(Tokens.T_TABLE).append(' '); sb.append(getName().getSchemaQualifiedStatementName()); sb.append(' ').append(Tokens.T_INDEX).append(' ').append('\''); sb.append(getIndexRoots()); sb.append('\''); return sb.toString(); } /** * Used to create row id's */ public int getId() { return tableName.hashCode(); } public final boolean isText() { return isText; } public final boolean isTemp() { return isTemp; } public final boolean isReadOnly() { return isReadOnly; } public final boolean isView() { return isView; } public boolean isCached() { return isCached; } public boolean isDataReadOnly() { return isReadOnly; } /** * returns false if the table has to be recreated in order to add / drop * indexes. Only CACHED tables return false. */ final boolean isIndexingMutable() { return !isIndexCached(); } /** * Returns true if table is CACHED */ boolean isIndexCached() { return isCached; } /** * Used by INSERT, DELETE, UPDATE operations */ void checkDataReadOnly() { if (isReadOnly) { throw Error.error(ErrorCode.DATA_IS_READONLY); } } // ---------------------------------------------------------------------------- // akede@users - 1.7.2 patch Files readonly public void setDataReadOnly(boolean value) { // Changing the Read-Only mode for the table is only allowed if the // the database can realize it. if (!value && database.isFilesReadOnly() && isFileBased()) { throw Error.error(ErrorCode.DATA_IS_READONLY); } isReadOnly = value; } /** * Text or Cached Tables are normally file based */ public boolean isFileBased() { return isCached || isText; } /** * Adds a constraint. */ public void addConstraint(Constraint c) { int index = c.getConstraintType() == Constraint.PRIMARY_KEY ? 0 : constraintList .length; constraintList = (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, c, index, 1); updateConstraintLists(); } void updateConstraintPath() { if (fkMainConstraints.length == 0) { return; } OrderedHashSet list = new OrderedHashSet(); getConstraintPath(defaultColumnMap, list); if (list.size() == 0) { return; } fkPath = new Constraint[list.size()]; list.toArray(fkPath); for (int i = 0; i < fkPath.length; i++) { Constraint c = fkPath[i]; HsqlName tableName = c.getMain().getName(); if (c.getMain() != database.schemaManager.getUserTable(null, tableName)) { throw Error.runtimeError(ErrorCode.U_S0500, "table constraint"); } tableName = c.getRef().getName(); if (c.getRef() != database.schemaManager.getUserTable(null, tableName)) { throw Error.runtimeError(ErrorCode.U_S0500, "table constraint"); } } } void updateConstraintLists() { int fkCount = 0; int mainCount = 0; int checkCount = 0; for (int i = 0; i < constraintList.length; i++) { switch (constraintList[i].getConstraintType()) { case Constraint.FOREIGN_KEY : fkCount++; break; case Constraint.MAIN : mainCount++; break; case Constraint.CHECK : if (constraintList[i].isNotNull()) { break; } checkCount++; break; } } fkConstraints = fkCount == 0 ? Constraint.emptyArray : new Constraint[fkCount]; fkCount = 0; fkMainConstraints = mainCount == 0 ? Constraint.emptyArray : new Constraint[mainCount]; mainCount = 0; checkConstraints = checkCount == 0 ? Constraint.emptyArray : new Constraint[checkCount]; checkCount = 0; for (int i = 0; i < constraintList.length; i++) { switch (constraintList[i].getConstraintType()) { case Constraint.FOREIGN_KEY : fkConstraints[fkCount] = constraintList[i]; fkCount++; break; case Constraint.MAIN : fkMainConstraints[mainCount] = constraintList[i]; mainCount++; break; case Constraint.CHECK : if (constraintList[i].isNotNull()) { break; } checkConstraints[checkCount] = constraintList[i]; checkCount++; break; } } } /** * Returns the list of constraints. */ public Constraint[] getConstraints() { return constraintList; } /** * Returns the primary constraint. */ public Constraint getPrimaryConstraint() { return primaryKeyCols.length == 0 ? null : constraintList[0]; } void getConstraintPath(int[] columnMap, OrderedHashSet list) { for (int i = 0; i < constraintList.length; i++) { if (constraintList[i].hasTriggeredAction()) { int[] mainColumns = constraintList[i].getMainColumns(); if (ArrayUtil.countCommonElements(columnMap, mainColumns) > 0) { if (list.add(constraintList[i])) { constraintList[i].getRef().getConstraintPath( constraintList[i].getRefColumns(), list); } } } } } Constraint getNotNullConstraintForColumn(int colIndex) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.isNotNull() && c.notNullColumnIndex == colIndex) { return c; } } return null; } /** * Returns the UNIQUE or PK constraint with the given column signature. */ Constraint getUniqueConstraintForColumns(int[] cols) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.isUniqueWithColumns(cols)) { return c; } } return null; } /** * Returns the UNIQUE or PK constraint with the given column signature. * Modifies the composition of refTableCols if necessary. */ Constraint getUniqueConstraintForColumns(int[] mainTableCols, int[] refTableCols) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; int type = c.getConstraintType(); if (type != Constraint.UNIQUE && type != Constraint.PRIMARY_KEY) { continue; } int[] constraintCols = c.getMainColumns(); if (constraintCols.length != mainTableCols.length) { continue; } if (ArrayUtil.areEqual(constraintCols, mainTableCols, mainTableCols.length, true)) { return c; } if (ArrayUtil.areEqualSets(constraintCols, mainTableCols)) { int[] newRefTableCols = new int[mainTableCols.length]; for (int j = 0; j < mainTableCols.length; j++) { int pos = ArrayUtil.find(constraintCols, mainTableCols[j]); newRefTableCols[pos] = refTableCols[j]; } for (int j = 0; j < mainTableCols.length; j++) { refTableCols[j] = newRefTableCols[j]; } return c; } } return null; } /** * Returns any foreign key constraint equivalent to the column sets */ Constraint getFKConstraintForColumns(Table tableMain, int[] mainCols, int[] refCols) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.isEquivalent(tableMain, mainCols, this, refCols)) { return c; } } return null; } /** * Returns any unique Constraint using this index * * @param index */ public Constraint getUniqueOrPKConstraintForIndex(Index index) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.getMainIndex() == index && (c.getConstraintType() == Constraint.UNIQUE || c.getConstraintType() == Constraint.PRIMARY_KEY)) { return c; } } return null; } /** * Returns the next constraint of a given type * * @param from * @param type */ int getNextConstraintIndex(int from, int type) { for (int i = from, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.getConstraintType() == type) { return i; } } return -1; } /** * Performs the table level checks and adds a column to the table at the * DDL level. Only used at table creation, not at alter column. */ public void addColumn(ColumnSchema column) { String name = column.getName().name; if (findColumn(name) >= 0) { throw Error.error(ErrorCode.X_42504, name); } if (column.isIdentity()) { if (identityColumn != -1) { throw Error.error(ErrorCode.X_42525, name); } identityColumn = getColumnCount(); identitySequence = column.getIdentitySequence(); } addColumnNoCheck(column); } public void addColumnNoCheck(ColumnSchema column) { if (primaryKeyCols != null) { throw Error.runtimeError(ErrorCode.U_S0500, "Table"); } columnList.add(column.getName().name, column); columnCount++; if (column.getDataType().isLobType()) { hasLobColumn = true; } } public boolean hasIdentityColumn() { return identityColumn != -1; } public long getNextIdentity() { return identitySequence.peek(); } /** * Match two valid, equal length, columns arrays for type of columns * * @param col column array from this Table * @param other the other Table object * @param othercol column array from the other Table */ void checkColumnsMatch(int[] col, Table other, int[] othercol) { for (int i = 0; i < col.length; i++) { Type type = colTypes[col[i]]; Type otherType = other.colTypes[othercol[i]]; if (type.typeComparisonGroup != otherType.typeComparisonGroup) { throw Error.error(ErrorCode.X_42562); } } } void checkColumnsMatch(ColumnSchema column, int colIndex) { Type type = colTypes[colIndex]; Type otherType = column.getDataType(); if (type.typeComparisonGroup != otherType.typeComparisonGroup) { throw Error.error(ErrorCode.X_42562); } } /** * For removal or addition of columns, constraints and indexes * * Does not work in this form for FK's as Constraint.ConstraintCore * is not transfered to a referencing or referenced table */ Table moveDefinition(Session session, int newType, ColumnSchema column, Constraint constraint, Index index, int colIndex, int adjust, OrderedHashSet dropConstraints, OrderedHashSet dropIndexes) { boolean newPK = false; if (constraint != null && constraint.constType == Constraint.PRIMARY_KEY) { newPK = true; } Table tn = new Table(database, tableName, newType); if (tableType == TEMP_TABLE) { tn.persistenceScope = persistenceScope; } for (int i = 0; i < getColumnCount(); i++) { ColumnSchema col = (ColumnSchema) columnList.get(i); if (i == colIndex) { if (column != null) { tn.addColumn(column); } if (adjust <= 0) { continue; } } tn.addColumn(col); } if (getColumnCount() == colIndex) { tn.addColumn(column); } int[] pkCols = null; if (hasPrimaryKey() && !dropConstraints.contains( getPrimaryConstraint().getName())) { pkCols = primaryKeyCols; pkCols = ArrayUtil.toAdjustedColumnArray(pkCols, colIndex, adjust); } else if (newPK) { pkCols = constraint.getMainColumns(); } tn.createPrimaryKey(getIndex(0).getName(), pkCols, false); for (int i = 1; i < indexList.length; i++) { Index idx = indexList[i]; if (dropIndexes.contains(idx.getName())) { continue; } int[] colarr = ArrayUtil.toAdjustedColumnArray(idx.getColumns(), colIndex, adjust); idx = tn.createIndexStructure(idx.getName(), colarr, idx.getColumnDesc(), null, idx.isUnique(), idx.isConstraint(), idx.isForward()); tn.addIndex(idx); } if (index != null) { tn.addIndex(index); } HsqlArrayList newList = new HsqlArrayList(); if (newPK) { constraint.core.mainIndex = tn.indexList[0]; constraint.core.mainTable = tn; constraint.core.mainTableName = tn.tableName; newList.add(constraint); } for (int i = 0; i < constraintList.length; i++) { Constraint c = constraintList[i]; if (dropConstraints.contains(c.getName())) { continue; } c = c.duplicate(); c.updateTable(session, this, tn, colIndex, adjust); newList.add(c); } if (!newPK && constraint != null) { constraint.updateTable(session, this, tn, -1, 0); newList.add(constraint); } tn.constraintList = new Constraint[newList.size()]; newList.toArray(tn.constraintList); tn.updateConstraintLists(); tn.setBestRowIdentifiers(); tn.triggerList = triggerList; tn.triggerLists = triggerLists; return tn; } /** * Used for drop / retype column. */ void checkColumnInCheckConstraint(int colIndex) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.constType == Constraint.CHECK && !c.isNotNull() && c.hasColumn(colIndex)) { HsqlName name = c.getName(); throw Error.error(ErrorCode.X_42502, name.getSchemaQualifiedStatementName()); } } } /** * Used for retype column. Checks whether column is in an FK or is * referenced by a FK * @param colIndex index */ void checkColumnInFKConstraint(int colIndex) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.hasColumn(colIndex) && (c.getConstraintType() == Constraint.MAIN || c.getConstraintType() == Constraint.FOREIGN_KEY)) { HsqlName name = c.getName(); throw Error.error(ErrorCode.X_42533, name.getSchemaQualifiedStatementName()); } } } /** * Returns list of constraints dependent only on one column */ OrderedHashSet getDependentConstraints(int colIndex) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.hasColumnOnly(colIndex)) { set.add(c); } } return set; } /** * Returns list of constraints dependent on more than one column */ OrderedHashSet getContainingConstraints(int colIndex) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.hasColumnPlus(colIndex)) { set.add(c); } } return set; } OrderedHashSet getContainingIndexNames(int colIndex) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0, size = indexList.length; i < size; i++) { Index index = indexList[i]; if (ArrayUtil.find(index.getColumns(), colIndex) != -1) { set.add(index.getName()); } } return set; } /** * Returns list of MAIN constraints dependent on this PK or UNIQUE constraint */ OrderedHashSet getDependentConstraints(Constraint constraint) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.getConstraintType() == Constraint.MAIN) { if (c.core.uniqueName == constraint.getName()) { set.add(c); } } } return set; } public OrderedHashSet getDependentExternalConstraints() { OrderedHashSet set = new OrderedHashSet(); for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.getConstraintType() == Constraint.MAIN || c.getConstraintType() == Constraint.FOREIGN_KEY) { if (c.core.mainTable != c.core.refTable) { set.add(c); } } } return set; } /** * Used for column defaults and nullability. Checks whether column is in an * FK with a given referential action type. * * @param colIndex index of column * @param actionType referential action of the FK */ void checkColumnInFKConstraint(int colIndex, int actionType) { for (int i = 0, size = constraintList.length; i < size; i++) { Constraint c = constraintList[i]; if (c.getConstraintType() == Constraint.FOREIGN_KEY && c.hasColumn(colIndex) && (actionType == c.getUpdateAction() || actionType == c.getDeleteAction())) { HsqlName name = c.getName(); throw Error.error(ErrorCode.X_42533, name.getSchemaQualifiedStatementName()); } } } /** * Returns the identity column index. */ int getIdentityColumnIndex() { return identityColumn; } /** * Returns the index of given column name or throws if not found */ public int getColumnIndex(String name) { int i = findColumn(name); if (i == -1) { throw Error.error(ErrorCode.X_42501, name); } return i; } /** * Returns the index of given column name or -1 if not found. */ public int findColumn(String name) { int index = columnList.getIndex(name); return index; } /** * Sets the SQL default value for a columm. */ void setDefaultExpression(int columnIndex, Expression def) { ColumnSchema column = getColumn(columnIndex); column.setDefaultExpression(def); setColumnTypeVars(columnIndex); } /** * sets the flag for the presence of any default expression */ void resetDefaultsFlag() { hasDefaultValues = false; for (int i = 0; i < colDefaults.length; i++) { hasDefaultValues = hasDefaultValues || colDefaults[i] != null; } } public int[] getBestRowIdentifiers() { return bestRowIdentifierCols; } public boolean isBestRowIdentifiersStrict() { return bestRowIdentifierStrict; } /** * Finds an existing index for a column */ Index getIndexForColumn(int col) { int i = bestIndexForColumn[col]; return i == -1 ? null : this.indexList[i]; } boolean isIndexed(int colIndex) { return bestIndexForColumn[colIndex] != -1; } int[] getUniqueNotNullColumnGroup(boolean[] usedColumns) { for (int i = 0, count = constraintList.length; i < count; i++) { Constraint constraint = constraintList[i]; if (constraint.constType == Constraint.UNIQUE) { int[] indexCols = constraint.getMainColumns(); if (ArrayUtil.areIntIndexesInBooleanArray( indexCols, colNotNull) && ArrayUtil .areIntIndexesInBooleanArray( indexCols, usedColumns)) { return indexCols; } } else if (constraint.constType == Constraint.PRIMARY_KEY) { int[] indexCols = constraint.getMainColumns(); if (ArrayUtil.areIntIndexesInBooleanArray(indexCols, usedColumns)) { return indexCols; } } } return null; } boolean areColumnsNotNull(int[] indexes) { return ArrayUtil.areIntIndexesInBooleanArray(indexes, colNotNull); } /** * Shortcut for creating system table PK's. */ void createPrimaryKey(int[] cols) { createPrimaryKey(null, cols, false); } /** * Shortcut for creating default PK's. */ public void createPrimaryKey() { createPrimaryKey(null, null, false); } /** * Creates a single or multi-column primary key and index. sets the * colTypes array. Finalises the creation of the table. (fredt@users) */ public void createPrimaryKey(HsqlName indexName, int[] columns, boolean columnsNotNull) { if (primaryKeyCols != null) { throw Error.runtimeError(ErrorCode.U_S0500, "Table"); } if (columns == null) { columns = ValuePool.emptyIntArray; } else { for (int i = 0; i < columns.length; i++) { getColumn(columns[i]).setPrimaryKey(true); } } primaryKeyCols = columns; setColumnStructures(); primaryKeyTypes = new Type[primaryKeyCols.length]; ArrayUtil.projectRow(colTypes, primaryKeyCols, primaryKeyTypes); primaryKeyColsSequence = new int[primaryKeyCols.length]; ArrayUtil.fillSequence(primaryKeyColsSequence); HsqlName name = indexName; if (name == null) { name = database.nameManager.newAutoName("IDX", getSchemaName(), getName(), SchemaObject.INDEX); } createPrimaryIndex(primaryKeyCols, primaryKeyTypes, name); setBestRowIdentifiers(); } void setColumnStructures() { colTypes = new Type[columnCount]; colDefaults = new Expression[columnCount]; colNotNull = new boolean[columnCount]; defaultColumnMap = new int[columnCount]; for (int i = 0; i < columnCount; i++) { setColumnTypeVars(i); } resetDefaultsFlag(); } void setColumnTypeVars(int i) { ColumnSchema column = getColumn(i); colTypes[i] = column.getDataType(); colNotNull[i] = column.isPrimaryKey() || !column.isNullable(); defaultColumnMap[i] = i; if (column.isIdentity()) { identitySequence = column.getIdentitySequence(); identityColumn = i; } else if (identityColumn == i) { identityColumn = -1; } colDefaults[i] = column.getDefaultExpression(); resetDefaultsFlag(); } /** * Returns direct mapping array. */ int[] getColumnMap() { return defaultColumnMap; } /** * Returns empty mapping array. */ int[] getNewColumnMap() { return new int[getColumnCount()]; } boolean[] getColumnCheckList(int[] columnIndexes) { boolean[] columnCheckList = new boolean[getColumnCount()]; for (int i = 0; i < columnIndexes.length; i++) { int index = columnIndexes[i]; if (index > -1) { columnCheckList[index] = true; } } return columnCheckList; } int[] getColumnIndexes(OrderedHashSet set) { int[] cols = new int[set.size()]; for (int i = 0; i < cols.length; i++) { cols[i] = getColumnIndex((String) set.get(i)); } return cols; } int[] getColumnIndexes(HashMappedList list) { int[] cols = new int[list.size()]; for (int i = 0; i < cols.length; i++) { cols[i] = ((Integer) list.get(i)).intValue(); } return cols; } /** * Returns the Column object at the given index */ public ColumnSchema getColumn(int i) { return (ColumnSchema) columnList.get(i); } public OrderedHashSet getColumnNameSet(int[] columnIndexes) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < columnIndexes.length; i++) { set.add(columnList.get(i)); } return set; } public OrderedHashSet getColumnNameSet(boolean[] columnCheckList) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < columnCheckList.length; i++) { if (columnCheckList[i]) { set.add(columnList.get(i)); } } return set; } public void getColumnNames(boolean[] columnCheckList, Set set) { for (int i = 0; i < columnCheckList.length; i++) { if (columnCheckList[i]) { set.add(((ColumnSchema) columnList.get(i)).getName()); } } } public OrderedHashSet getColumnNameSet() { OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < getColumnCount(); i++) { set.add(((ColumnSchema) columnList.get(i)).getName()); } return set; } /** * Returns array for a new row with SQL DEFAULT value for each column n * where exists[n] is false. This provides default values only where * required and avoids evaluating these values where they will be * overwritten. */ Object[] getNewRowData(Session session) { Object[] data = new Object[getColumnCount()]; int i; if (hasDefaultValues) { for (i = 0; i < getColumnCount(); i++) { Expression def = colDefaults[i]; if (def != null) { data[i] = def.getValue(session, colTypes[i]); } } } return data; } boolean hasTrigger(int trigVecIndex) { return triggerLists[trigVecIndex].length != 0; } /** * Adds a trigger. */ void addTrigger(TriggerDef td, HsqlName otherName) { int index = triggerList.length; if (otherName != null) { int pos = getTriggerIndex(otherName.name); if (pos != -1) { index = pos + 1; } } triggerList = (TriggerDef[]) ArrayUtil.toAdjustedArray(triggerList, td, index, 1); TriggerDef[] list = triggerLists[td.vectorIndex]; index = list.length; if (otherName != null) { for (int i = 0; i < list.length; i++) { TriggerDef trigger = list[i]; if (trigger.name.name.equals(otherName.name)) { index = i + 1; break; } } } list = (TriggerDef[]) ArrayUtil.toAdjustedArray(list, td, index, 1); triggerLists[td.vectorIndex] = list; } /** * Returns a trigger. */ TriggerDef getTrigger(String name) { for (int i = triggerList.length - 1; i >= 0; i--) { if (triggerList[i].name.name.equals(name)) { return triggerList[i]; } } return null; } public int getTriggerIndex(String name) { for (int i = 0; i < triggerList.length; i++) { if (triggerList[i].name.name.equals(name)) { return i; } } return -1; } /** * Drops a trigger. */ void removeTrigger(String name) { TriggerDef td = null; for (int i = 0; i < triggerList.length; i++) { td = triggerList[i]; if (td.name.name.equals(name)) { td.terminate(); triggerList = (TriggerDef[]) ArrayUtil.toAdjustedArray(triggerList, null, i, -1); break; } } if (td == null) { return; } int index = td.vectorIndex; // look in each trigger list of each type of trigger for (int j = 0; j < triggerLists[index].length; j++) { td = triggerLists[index][j]; if (td.name.name.equals(name)) { td.terminate(); triggerLists[index] = (TriggerDef[]) ArrayUtil.toAdjustedArray( triggerLists[index], null, j, -1); break; } } } /** * Drops all triggers. */ void releaseTriggers() { // look in each trigger list of each type of trigger for (int i = 0; i < TriggerDef.NUM_TRIGS; i++) { for (int j = 0; j < triggerLists[i].length; j++) { triggerLists[i][j].terminate(); } triggerLists[i] = TriggerDef.emptyArray; } } /** * Returns the index of the Index object of the given name or -1 if not found. */ int getIndexIndex(String indexName) { Index[] indexes = indexList; for (int i = 0; i < indexes.length; i++) { if (indexName.equals(indexes[i].getName().name)) { return i; } } // no such index return -1; } /** * Returns the Index object of the given name or null if not found. */ Index getIndex(String indexName) { Index[] indexes = indexList; int i = getIndexIndex(indexName); return i == -1 ? null : indexes[i]; } /** * Return the position of the constraint within the list */ int getConstraintIndex(String constraintName) { for (int i = 0, size = constraintList.length; i < size; i++) { if (constraintList[i].getName().name.equals(constraintName)) { return i; } } return -1; } /** * return the named constriant */ public Constraint getConstraint(String constraintName) { int i = getConstraintIndex(constraintName); return (i < 0) ? null : constraintList[i]; } /** * remove a named constraint */ void removeConstraint(String name) { int index = getConstraintIndex(name); if (index != -1) { removeConstraint(index); } } void removeConstraint(int index) { constraintList = (Constraint[]) ArrayUtil.toAdjustedArray(constraintList, null, index, -1); updateConstraintLists(); } void renameColumn(ColumnSchema column, String newName, boolean isquoted) { String oldname = column.getName().name; int i = getColumnIndex(oldname); columnList.setKey(i, newName); column.getName().rename(newName, isquoted); } void renameColumn(ColumnSchema column, HsqlName newName) { String oldname = column.getName().name; int i = getColumnIndex(oldname); columnList.setKey(i, newName); column.getName().rename(newName); } public TriggerDef[] getTriggers() { return triggerList; } public boolean isWritable() { return !isReadOnly && !database.databaseReadOnly && !(database.isFilesReadOnly() && (isCached || isText)); } public boolean isInsertable() { return isWritable(); } public boolean isUpdatable() { return isWritable(); } public int[] getUpdatableColumns() { return defaultColumnMap; } public Table getBaseTable() { return this; } public int[] getBaseTableColumnMap() { return defaultColumnMap; } // /** * Used to create an index automatically for system tables. */ Index createIndexForColumns(int[] columns) { HsqlName indexName = database.nameManager.newAutoName("IDX_T", getSchemaName(), getName(), SchemaObject.INDEX); try { Index index = createAndAddIndexStructure(indexName, columns, null, null, false, false, false); return index; } catch (Throwable t) { return null; } } void fireAfterTriggers(Session session, int trigVecIndex, RowSetNavigator rowSet) { if (!database.isReferentialIntegrity()) { return; } TriggerDef[] trigVec = triggerLists[trigVecIndex]; for (int i = 0, size = trigVec.length; i < size; i++) { TriggerDef td = trigVec[i]; boolean sqlTrigger = td instanceof TriggerDefSQL; if (td.hasOldTable()) { // } if (td.isForEachRow()) { while (rowSet.hasNext()) { Object[] oldData = null; Object[] newData = null; switch (td.triggerType) { case Trigger.DELETE_AFTER_ROW : oldData = rowSet.getNext(); if (!sqlTrigger) { oldData = (Object[]) ArrayUtil.duplicateArray( oldData); } break; case Trigger.INSERT_AFTER_ROW : newData = rowSet.getNext(); if (!sqlTrigger) { newData = (Object[]) ArrayUtil.duplicateArray( newData); } break; } td.pushPair(session, oldData, newData); } rowSet.beforeFirst(); } else { td.pushPair(session, null, null); } } } /** * Fires all row-level triggers of the given set (trigger type) * */ void fireBeforeTriggers(Session session, int trigVecIndex, Object[] oldData, Object[] newData, int[] cols) { if (!database.isReferentialIntegrity()) { return; } TriggerDef[] trigVec = triggerLists[trigVecIndex]; for (int i = 0, size = trigVec.length; i < size; i++) { TriggerDef td = trigVec[i]; boolean sqlTrigger = td instanceof TriggerDefSQL; if (cols != null && td.getUpdateColumnIndexes() != null && !ArrayUtil.haveCommonElement( td.getUpdateColumnIndexes(), cols, cols.length)) { continue; } if (td.isForEachRow()) { switch (td.triggerType) { case Trigger.UPDATE_BEFORE_ROW : case Trigger.DELETE_BEFORE_ROW : if (!sqlTrigger) { oldData = (Object[]) ArrayUtil.duplicateArray(oldData); } break; } td.pushPair(session, oldData, newData); } else { td.pushPair(session, null, null); } } } /** * Enforce max field sizes according to SQL column definition. * SQL92 13.8 */ void enforceRowConstraints(Session session, Object[] data) { for (int i = 0; i < defaultColumnMap.length; i++) { Type type = colTypes[i]; data[i] = type.convertToTypeLimits(session, data[i]); if (type.isDomainType()) { Constraint[] constraints = type.userTypeModifier.getConstraints(); for (int j = 0; j < constraints.length; j++) { constraints[j].checkCheckConstraint(session, this, (Object) data[i]); } } if (data[i] == null) { if (colNotNull[i]) { Constraint c = getNotNullConstraintForColumn(i); if (c == null) { if (getColumn(i).isPrimaryKey()) { c = this.getPrimaryConstraint(); } } String[] info = new String[] { c.getName().name, tableName.name }; throw Error.error(ErrorCode.X_23503, ErrorCode.CONSTRAINT, info); } } } } /** * Finds an existing index for a column group */ Index getIndexForColumns(int[] cols) { int i = bestIndexForColumn[cols[0]]; if (i > -1) { return indexList[i]; } switch (tableType) { case TableBase.SYSTEM_SUBQUERY : case TableBase.SYSTEM_TABLE : case TableBase.VIEW_TABLE : case TableBase.TEMP_TABLE : { Index index = createIndexForColumns(cols); return index; } } return null; } /** * Finds an existing index for a column set or create one for temporary * tables */ Index getIndexForColumns(OrderedIntHashSet set) { int maxMatchCount = 0; Index selected = null; if (set.isEmpty()) { return null; } for (int i = 0, count = indexList.length; i < count; i++) { Index currentindex = getIndex(i); int[] indexcols = currentindex.getColumns(); int matchCount = set.getOrderedMatchCount(indexcols); if (matchCount == 0) { continue; } if (matchCount == indexcols.length) { return currentindex; } if (matchCount > maxMatchCount) { maxMatchCount = matchCount; selected = currentindex; } } if (selected != null) { return selected; } switch (tableType) { case TableBase.SYSTEM_SUBQUERY : case TableBase.SYSTEM_TABLE : case TableBase.VIEW_TABLE : case TableBase.TEMP_TABLE : { selected = createIndexForColumns(set.toArray()); } } return selected; } /** * Return the list of file pointers to root nodes for this table's * indexes. */ public final int[] getIndexRootsArray() { PersistentStore store = database.persistentStoreCollection.getStore(this); int[] roots = new int[getIndexCount()]; for (int i = 0; i < getIndexCount(); i++) { CachedObject accessor = store.getAccessor(indexList[i]); roots[i] = accessor == null ? -1 : accessor.getPos(); } return roots; } /** * Returns the string consisting of file pointers to roots of indexes * plus the next identity value (hidden or user defined). This is used * with CACHED tables. */ String getIndexRoots() { String roots = StringUtil.getList(getIndexRootsArray(), " ", ""); StringBuffer s = new StringBuffer(roots); /* s.append(' '); s.append(identitySequence.peek()); */ return s.toString(); } /** * Sets the index roots of a cached/text table to specified file * pointers. If a * file pointer is -1 then the particular index root is null. A null index * root signifies an empty table. Accordingly, all index roots should be * null or all should be a valid file pointer/reference. */ public void setIndexRoots(int[] roots) { if (!isCached) { throw Error.error(ErrorCode.X_42501, tableName.name); } PersistentStore store = database.persistentStoreCollection.getStore(this); for (int i = 0; i < getIndexCount(); i++) { store.setAccessor(indexList[i], roots[i]); } } /** * Sets the index roots and next identity. */ void setIndexRoots(Session session, String s) { if (!isCached) { throw Error.error(ErrorCode.X_42501, tableName.name); } ParserDQL p = new ParserDQL(session, new Scanner(s)); int[] roots = new int[getIndexCount()]; p.read(); for (int i = 0; i < getIndexCount(); i++) { int v = p.readInteger(); roots[i] = v; } setIndexRoots(roots); } /** * 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, String indexname) { // find the array index for indexname and remove int todrop = getIndexIndex(indexname); indexList = (Index[]) ArrayUtil.toAdjustedArray(indexList, null, todrop, -1); for (int i = 0; i < indexList.length; i++) { indexList[i].setPosition(i); } setBestRowIdentifiers(); if (store != null) { store.resetAccessorKeys(indexList); } } /** * Moves the data from table to table. * The colindex argument is the index of the column that was * added or removed. The adjust argument is {-1 | 0 | +1} */ void moveData(Session session, Table from, int colindex, int adjust) { Object colvalue = null; ColumnSchema column = null; if (adjust >= 0 && colindex != -1) { column = getColumn(colindex); colvalue = column.getDefaultValue(session); } PersistentStore store = session.sessionData.getRowStore(this); try { RowIterator it = from.rowIterator(session); while (it.hasNext()) { Row row = it.getNextRow(); Object[] o = row.getData(); Object[] data = getEmptyRowData(); if (adjust == 0 && colindex != -1) { colvalue = column.getDataType().convertToType(session, o[colindex], from.getColumn(colindex).getDataType()); } ArrayUtil.copyAdjustArray(o, data, colvalue, colindex, adjust); systemSetIdentityColumn(session, data); enforceRowConstraints(session, data); // get object without RowAction Row newrow = (Row) store.getNewCachedObject(null, data); if (row.rowAction != null) { newrow.rowAction = row.rowAction.duplicate(newrow.getPos()); } store.indexRow(null, newrow); } } catch (Throwable t) { store.release(); if (t instanceof HsqlException) { throw (HsqlException) t; } throw new HsqlException(t, "", 0); } } // /** * Mid level method for inserting rows. Performs constraint checks and * fires row level triggers. */ void insertRow(Session session, PersistentStore store, Object[] data) { setIdentityColumn(session, data); if (triggerLists[Trigger.INSERT_BEFORE].length != 0) { fireBeforeTriggers(session, Trigger.INSERT_BEFORE, null, data, null); } if (isView) { return; } checkRowDataInsert(session, data); insertNoCheck(session, store, data); } /** * Multi-row insert method. Used for CREATE TABLE AS ... queries. */ void insertIntoTable(Session session, Result result) { PersistentStore store = session.sessionData.getRowStore(this); RowSetNavigator nav = result.initialiseNavigator(); while (nav.hasNext()) { Object[] data = (Object[]) nav.getNext(); Object[] newData = (Object[]) ArrayUtil.resizeArrayIfDifferent(data, getColumnCount()); insertData(store, newData); } } /** * Low level method for row insert. * UNIQUE or PRIMARY constraints are enforced by attempting to * add the row to the indexes. */ private Row insertNoCheck(Session session, PersistentStore store, Object[] data) { Row row = (Row) store.getNewCachedObject(session, data); store.indexRow(session, row); session.addInsertAction(this, row); return row; } /** * */ public void insertNoCheckFromLog(Session session, Object[] data) { systemUpdateIdentityValue(data); PersistentStore store = session.sessionData.getRowStore(this); Row row = (Row) store.getNewCachedObject(session, data); store.indexRow(session, row); session.addInsertAction(this, row); } /** * Used for system table inserts. No checks. No identity * columns. */ public int insertSys(PersistentStore store, Result ins) { RowSetNavigator nav = ins.getNavigator(); int count = 0; while (nav.hasNext()) { insertSys(store, (Object[]) nav.getNext()); count++; } return count; } /** * Used for subquery inserts. No checks. No identity * columns. */ void insertResult(PersistentStore store, Result ins) { RowSetNavigator nav = ins.initialiseNavigator(); while (nav.hasNext()) { Object[] data = (Object[]) nav.getNext(); Object[] newData = (Object[]) ArrayUtil.resizeArrayIfDifferent(data, getColumnCount()); insertData(store, newData); } } /** * Not for general use. * Used by ScriptReader to unconditionally insert a row into * the table when the .script file is read. */ public void insertFromScript(PersistentStore store, Object[] data) { systemUpdateIdentityValue(data); insertData(store, data); } /** * For system operations outside transaction constrol */ public void insertData(PersistentStore store, Object[] data) { Row row = (Row) store.getNewCachedObject(null, data); store.indexRow(null, row); } /** * Used by the system tables only */ public void insertSys(PersistentStore store, Object[] data) { Row row = (Row) store.getNewCachedObject(null, data); store.indexRow(null, row); } /** * If there is an identity column in the table, sets * the value and/or adjusts the identiy value for the table. */ protected void setIdentityColumn(Session session, Object[] data) { if (identityColumn != -1) { Number id = (Number) data[identityColumn]; if (id == null) { id = (Number) identitySequence.getValueObject(); data[identityColumn] = id; } else { identitySequence.userUpdate(id.longValue()); } if (session != null) { session.setLastIdentity(id); } } } protected void systemSetIdentityColumn(Session session, Object[] data) { if (identityColumn != -1) { Number id = (Number) data[identityColumn]; if (id == null) { id = (Number) identitySequence.getValueObject(); data[identityColumn] = id; } else { identitySequence.userUpdate(id.longValue()); } } } /** * If there is an identity column in the table, sets * the max identity value. */ protected void systemUpdateIdentityValue(Object[] data) { if (identityColumn != -1) { Number id = (Number) data[identityColumn]; if (id != null) { identitySequence.systemUpdate(id.longValue()); } } } /** * Delete method for referential triggered actions. */ void deleteRowAsTriggeredAction(Session session, Row row) { deleteNoCheck(session, row); } /** * Mid level row delete method. Fires triggers but no integrity * constraint checks. */ void deleteNoRefCheck(Session session, Row row) { Object[] data = row.getData(); fireBeforeTriggers(session, Trigger.DELETE_BEFORE, data, null, null); if (isView) { return; } deleteNoCheck(session, row); } /** * Low level row delete method. Removes the row from the indexes and * from the Cache. */ private void deleteNoCheck(Session session, Row row) { if (row.isDeleted(session)) { return; } session.addDeleteAction(this, row); } /** * For log statements. Delete a single row. */ public void deleteNoCheckFromLog(Session session, Object[] data) { Row row = null; PersistentStore store = session.sessionData.getRowStore(this); if (hasPrimaryKey()) { RowIterator it = getPrimaryIndex().findFirstRow(session, store, data, primaryKeyColsSequence); row = it.getNextRow(); } else if (bestIndex == null) { RowIterator it = rowIterator(session); while (true) { row = it.getNextRow(); if (row == null) { break; } if (IndexAVL.compareRows( row.getData(), data, defaultColumnMap, colTypes) == 0) { break; } } } else { RowIterator it = bestIndex.findFirstRow(session, store, data); while (true) { row = it.getNextRow(); if (row == null) { break; } Object[] rowdata = row.getData(); // reached end of range if (bestIndex.compareRowNonUnique( data, bestIndex.getColumns(), rowdata) != 0) { row = null; break; } if (IndexAVL.compareRows( rowdata, data, defaultColumnMap, colTypes) == 0) { break; } } } if (row == null) { return; } deleteNoCheck(session, row); } void updateRowSet(Session session, HashMappedList rowSet, int[] cols, boolean isTriggeredSet) { boolean hasLob = false; PersistentStore store = session.sessionData.getRowStore(this); for (int i = 0; i < rowSet.size(); i++) { Row row = (Row) rowSet.getKey(i); if (row.isDeleted(session)) { if (isTriggeredSet) { rowSet.remove(i); i--; continue; } else { throw Error.error(ErrorCode.X_27000); } } } for (int i = 0; i < rowSet.size(); i++) { Row row = (Row) rowSet.getKey(i); Object[] data = (Object[]) rowSet.get(i); checkRowData(session, data, cols); // todo - see if check is necessary ?? deleteNoCheck(session, row); } for (int i = 0; i < rowSet.size(); i++) { Object[] data = (Object[]) rowSet.get(i); insertNoCheck(session, store, data); } } void addLobUsageCount(Session session, Object[] data) { if (!hasLobColumn) { return; } for (int j = 0; j < columnCount; j++) { if (colTypes[j].isLobType()) { Object value = data[j]; if (value == null) { continue; } session.sessionData.addLobUsageCount( ((LobData) value).getId()); } } } void removeLobUsageCount(Session session, Object[] data) { if (!hasLobColumn) { return; } for (int j = 0; j < columnCount; j++) { if (colTypes[j].isLobType()) { Object value = data[j]; if (value == null) { continue; } session.sessionData.removeUsageCount( ((LobData) value).getId()); } } } void checkRowDataInsert(Session session, Object[] data) { enforceRowConstraints(session, data); if (database.isReferentialIntegrity()) { for (int i = 0, size = constraintList.length; i < size; i++) { constraintList[i].checkInsert(session, this, data); } } } void checkRowData(Session session, Object[] data, int[] cols) { enforceRowConstraints(session, data); for (int i = 0; i < checkConstraints.length; i++) { checkConstraints[i].checkCheckConstraint(session, this, data); } } public void clearAllData(Session session) { super.clearAllData(session); if (identitySequence != null) { identitySequence.reset(); } } public void clearAllData(PersistentStore store) { super.clearAllData(store); if (identitySequence != null) { identitySequence.reset(); } } /*************** VOLTDB *********************/ /** * VoltDB added method to get a non-catalog-dependent * representation of this HSQLDB object. * @param session The current Session object may be needed to resolve * some names. * @param indent A string of whitespace to be prepended to every line * in the resulting XML. * @return XML, correctly indented, representing this object. * @throws HSQLParseException */ String voltGetXML(Session session, String indent) throws HSQLParseException { StringBuilder sb = new StringBuilder(); // append open table tag sb.append(indent).append("<table"); // add table metadata sb.append(" name='").append(getName().name).append("'"); sb.append(">\n"); // read all the columns sb.append(indent + " ").append("<columns>\n"); int[] columnIndices = getColumnMap(); for (int i : columnIndices) { ColumnSchema column = getColumn(i); sb.append(column.voltGetXML(session, indent + " ")); } sb.append(indent + " ").append("</columns>\n"); // read all the indexes sb.append(indent + " ").append("<indexes>\n"); for (Index index : getIndexes()) { sb.append(index.voltGetXML(session, indent + " ")); } sb.append(indent + " ").append("</indexes>\n"); // read all the constraints sb.append(indent + " ").append("<constraints>\n"); for (Constraint constraint : getConstraints()) { // giant hack to ignore "CHECK" constraint if (constraint.getConstraintType() != Constraint.CHECK) sb.append(constraint.voltGetXML(session, indent + " ")); } sb.append(indent + " ").append("</constraints>\n"); // close table tag sb.append(indent).append("</table>\n"); return sb.toString(); } }