/* Copyright (c) 2001-2010, 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.lib.HashMappedList; import org.hsqldb.lib.HsqlArrayList; import org.hsqldb.lib.Iterator; import org.hsqldb.lib.MultiValueHashMap; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.lib.StringConverter; import org.hsqldb.lib.WrapperIterator; import org.hsqldb.navigator.RowIterator; import org.hsqldb.rights.Grantee; import org.hsqldb.types.Type; /** * Manages all SCHEMA related database objects * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 1.9.0 * @since 1.8.0 */ public class SchemaManager { Database database; HsqlName defaultSchemaHsqlName; HashMappedList schemaMap = new HashMappedList(); MultiValueHashMap referenceMap = new MultiValueHashMap(); int defaultTableType = TableBase.MEMORY_TABLE; long schemaChangeTimestamp; public SchemaManager(Database database) { this.database = database; defaultSchemaHsqlName = SqlInvariants.INFORMATION_SCHEMA_HSQLNAME; Schema schema = new Schema(SqlInvariants.INFORMATION_SCHEMA_HSQLNAME, SqlInvariants.INFORMATION_SCHEMA_HSQLNAME.owner); schemaMap.put(schema.getName().name, schema); try { schema.typeLookup.add(SqlInvariants.CARDINAL_NUMBER); schema.typeLookup.add(SqlInvariants.YES_OR_NO); schema.typeLookup.add(SqlInvariants.CHARACTER_DATA); schema.typeLookup.add(SqlInvariants.SQL_IDENTIFIER); schema.typeLookup.add(SqlInvariants.TIME_STAMP); schema.charsetLookup.add(SqlInvariants.SQL_TEXT); schema.charsetLookup.add(SqlInvariants.SQL_IDENTIFIER_CHARSET); schema.charsetLookup.add(SqlInvariants.SQL_CHARACTER); } catch (HsqlException e) {} } public void setSchemaChangeTimestamp() { schemaChangeTimestamp = database.txManager.getGlobalChangeTimestamp(); } public long getSchemaChangeTimestamp() { return schemaChangeTimestamp; } // pre-defined public HsqlName getSQLJSchemaHsqlName() { return SqlInvariants.SQLJ_SCHEMA_HSQLNAME; } // SCHEMA management public void createPublicSchema() { HsqlName name = database.nameManager.newHsqlName(null, SqlInvariants.PUBLIC_SCHEMA, SchemaObject.SCHEMA); Schema schema = new Schema(name, database.getGranteeManager().getDBARole()); defaultSchemaHsqlName = schema.getName(); schemaMap.put(schema.getName().name, schema); } /** * Creates a schema belonging to the given grantee. */ public void createSchema(HsqlName name, Grantee owner) { SqlInvariants.checkSchemaNameNotSystem(name.name); Schema schema = new Schema(name, owner); schemaMap.add(name.name, schema); } public void dropSchema(Session session, String name, boolean cascade) { Schema schema = (Schema) schemaMap.get(name); if (schema == null) { throw Error.error(ErrorCode.X_42501, name); } if (SqlInvariants.isLobsSchemaName(name)) { throw Error.error(ErrorCode.X_42503, name); } if (!cascade && !schema.isEmpty()) { throw Error.error(ErrorCode.X_2B000); } OrderedHashSet externalReferences = new OrderedHashSet(); getCascadingSchemaReferences(schema.getName(), externalReferences); removeSchemaObjects(externalReferences); Iterator tableIterator = schema.schemaObjectIterator(SchemaObject.TABLE); while (tableIterator.hasNext()) { Table table = ((Table) tableIterator.next()); Constraint[] list = table.getFKConstraints(); for (int i = 0; i < list.length; i++) { Constraint constraint = list[i]; if (constraint.getMain().getSchemaName() != schema.getName()) { constraint.getMain().removeConstraint( constraint.getMainName().name); } } removeTable(session, table); } Iterator sequenceIterator = schema.schemaObjectIterator(SchemaObject.SEQUENCE); while (sequenceIterator.hasNext()) { NumberSequence sequence = ((NumberSequence) sequenceIterator.next()); database.getGranteeManager().removeDbObject(sequence.getName()); } schema.clearStructures(); schemaMap.remove(name); if (defaultSchemaHsqlName.name.equals(name)) { HsqlName hsqlName = database.nameManager.newHsqlName(name, false, SchemaObject.SCHEMA); schema = new Schema(hsqlName, database.getGranteeManager().getDBARole()); defaultSchemaHsqlName = schema.getName(); schemaMap.put(schema.getName().name, schema); } // these are called last and in this particular order database.getUserManager().removeSchemaReference(name); database.getSessionManager().removeSchemaReference(schema); } public void renameSchema(HsqlName name, HsqlName newName) { Schema schema = (Schema) schemaMap.get(name.name); Schema exists = (Schema) schemaMap.get(newName.name); if (schema == null) { throw Error.error(ErrorCode.X_42501, name.name); } if (exists != null) { throw Error.error(ErrorCode.X_42504, newName.name); } SqlInvariants.checkSchemaNameNotSystem(name.name); SqlInvariants.checkSchemaNameNotSystem(newName.name); int index = schemaMap.getIndex(name.name); schema.getName().rename(newName); schemaMap.set(index, newName.name, schema); } public void clearStructures() { Iterator it = schemaMap.values().iterator(); while (it.hasNext()) { Schema schema = (Schema) it.next(); schema.clearStructures(); } } public Iterator allSchemaNameIterator() { return schemaMap.keySet().iterator(); } public HsqlName getUserSchemaHsqlName(String name) { Schema schema = (Schema) schemaMap.get(name); if (schema == null) { throw Error.error(ErrorCode.X_3F000, name); } if (schema.getName() == SqlInvariants.INFORMATION_SCHEMA_HSQLNAME) { throw Error.error(ErrorCode.X_3F000, name); } return schema.getName(); } public Grantee toSchemaOwner(String name) { // Note that INFORMATION_SCHEMA and DEFINITION_SCHEMA aren't in the // backing map. // This may not be the most elegant solution, but it is the safest // (without doing a code review for implications of adding // them to the map). if (SqlInvariants.INFORMATION_SCHEMA_HSQLNAME.name.equals(name)) { return SqlInvariants.INFORMATION_SCHEMA_HSQLNAME.owner; } Schema schema = (Schema) schemaMap.get(name); return schema == null ? null : schema.getOwner(); } public HsqlName getDefaultSchemaHsqlName() { return defaultSchemaHsqlName; } public void setDefaultSchemaHsqlName(HsqlName name) { defaultSchemaHsqlName = name; } public boolean schemaExists(String name) { return SqlInvariants.INFORMATION_SCHEMA.equals(name) || schemaMap.containsKey(name); } public HsqlName findSchemaHsqlName(String name) { Schema schema = ((Schema) schemaMap.get(name)); if (schema == null) { return null; } return schema.getName(); } /** * If schemaName is null, return the default schema name, else return * the HsqlName object for the schema. If schemaName does not exist, * throw. */ public HsqlName getSchemaHsqlName(String name) { if (name == null) { return defaultSchemaHsqlName; } if (SqlInvariants.INFORMATION_SCHEMA.equals(name)) { return SqlInvariants.INFORMATION_SCHEMA_HSQLNAME; } Schema schema = ((Schema) schemaMap.get(name)); if (schema == null) { throw Error.error(ErrorCode.X_3F000, name); } return schema.getName(); } /** * Same as above, but return string */ public String getSchemaName(String name) { return getSchemaHsqlName(name).name; } public Schema findSchema(String name) { return ((Schema) schemaMap.get(name)); } /** * Iterator includes DEFINITION_SCHEMA */ public Iterator fullSchemaNamesIterator() { return schemaMap.keySet().iterator(); } /** * is a grantee the authorization of any schema */ boolean isSchemaAuthorisation(Grantee grantee) { Iterator schemas = allSchemaNameIterator(); while (schemas.hasNext()) { String schemaName = (String) schemas.next(); if (grantee.equals(toSchemaOwner(schemaName))) { return true; } } return false; } /** * drop all schemas with the given authorisation */ public void dropSchemas(Session session, Grantee grantee, boolean cascade) { HsqlArrayList list = getSchemas(grantee); Iterator it = list.iterator(); while (it.hasNext()) { Schema schema = (Schema) it.next(); dropSchema(session, schema.getName().name, cascade); } } public HsqlArrayList getSchemas(Grantee grantee) { HsqlArrayList list = new HsqlArrayList(); Iterator it = schemaMap.values().iterator(); while (it.hasNext()) { Schema schema = (Schema) it.next(); if (grantee.equals(schema.getOwner())) { list.add(schema); } } return list; } public boolean hasSchemas(Grantee grantee) { Iterator it = schemaMap.values().iterator(); while (it.hasNext()) { Schema schema = (Schema) it.next(); if (grantee.equals(schema.getOwner())) { return true; } } return false; } /** * Returns an HsqlArrayList containing references to all non-system * tables and views. This includes all tables and views registered with * this Database. */ public HsqlArrayList getAllTables() { Iterator schemas = allSchemaNameIterator(); HsqlArrayList alltables = new HsqlArrayList(); while (schemas.hasNext()) { String name = (String) schemas.next(); if (SqlInvariants.isLobsSchemaName(name)) { continue; } if (SqlInvariants.isSystemSchemaName(name)) { continue; } HashMappedList current = getTables(name); alltables.addAll(current.values()); } return alltables; } public HashMappedList getTables(String schema) { Schema temp = (Schema) schemaMap.get(schema); return temp.tableList; } public HsqlName[] getCatalogAndBaseTableNames() { OrderedHashSet names = new OrderedHashSet(); HsqlArrayList tables = getAllTables(); for (int i = 0; i < tables.size(); i++) { Table table = (Table) tables.get(i); if (!table.isTemp()) { names.add(table.getName()); } } names.add(database.getCatalogName()); HsqlName[] array = new HsqlName[names.size()]; names.toArray(array); return array; } private SchemaObjectSet getSchemaObjectSet(Schema schema, int type) { SchemaObjectSet set = null; switch (type) { case SchemaObject.SEQUENCE : set = schema.sequenceLookup; break; case SchemaObject.TABLE : case SchemaObject.VIEW : set = schema.tableLookup; break; case SchemaObject.CHARSET : set = schema.charsetLookup; break; case SchemaObject.COLLATION : set = schema.collationLookup; break; case SchemaObject.PROCEDURE : set = schema.procedureLookup; break; case SchemaObject.FUNCTION : set = schema.functionLookup; break; case SchemaObject.DOMAIN : case SchemaObject.TYPE : set = schema.typeLookup; break; case SchemaObject.INDEX : set = schema.indexLookup; break; case SchemaObject.CONSTRAINT : set = schema.constraintLookup; break; case SchemaObject.TRIGGER : set = schema.triggerLookup; break; case SchemaObject.SPECIFIC_ROUTINE : set = schema.specificRoutineLookup; } return set; } public void checkSchemaObjectNotExists(HsqlName name) { Schema schema = (Schema) schemaMap.get(name.schema.name); SchemaObjectSet set = getSchemaObjectSet(schema, name.type); set.checkAdd(name); } /** * Returns the specified user-defined table or view visible within the * context of the specified Session, or any system table of the given * name. It excludes any temp tables created in other Sessions. * Throws if the table does not exist in the context. */ public Table getTable(Session session, String name, String schema) { Table t = null; if (Tokens.T_MODULE.equals(schema)) { t = findSessionTable(session, name, null); if (t == null) { throw Error.error(ErrorCode.X_42501, name); } } if (schema == null) { t = findSessionTable(session, name, null); } if (t == null) { schema = session.getSchemaName(schema); t = findUserTable(session, name, schema); } if (t == null) { if (SqlInvariants.INFORMATION_SCHEMA.equals(schema) && database.dbInfo != null) { t = database.dbInfo.getSystemTable(session, name); } } if (t == null) { throw Error.error(ErrorCode.X_42501, name); } return t; } public Table getUserTable(Session session, HsqlName name) { return getUserTable(session, name.name, name.schema.name); } /** * Returns the specified user-defined table or view visible within the * context of the specified Session. It excludes system tables and * any temp tables created in different Sessions. * Throws if the table does not exist in the context. */ public Table getUserTable(Session session, String name, String schema) { Table t = findUserTable(session, name, schema); if (t == null) { throw Error.error(ErrorCode.X_42501, name); } return t; } /** * Returns the specified user-defined table or view visible within the * context of the specified schema. It excludes system tables. * Returns null if the table does not exist in the context. */ public Table findUserTable(Session session, String name, String schemaName) { Schema schema = (Schema) schemaMap.get(schemaName); if (schema == null) { return null; } int i = schema.tableList.getIndex(name); if (i == -1) { return null; } return (Table) schema.tableList.get(i); } /** * Returns the specified session context table. * Returns null if the table does not exist in the context. */ public Table findSessionTable(Session session, String name, String schemaName) { return session.findSessionTable(name); } /** * Drops the specified user-defined view or table from this Database object. * * <p> The process of dropping a table or view includes: * <OL> * <LI> checking that the specified Session's currently connected User has * the right to perform this operation and refusing to proceed if not by * throwing. * <LI> checking for referential constraints that conflict with this * operation and refusing to proceed if they exist by throwing.</LI> * <LI> removing the specified Table from this Database object. * <LI> removing any exported foreign keys Constraint objects held by any * tables referenced by the table to be dropped. This is especially * important so that the dropped Table ceases to be referenced, eventually * allowing its full garbage collection. * <LI> * </OL> * * <p> * * @param session the connected context in which to perform this operation * @param table if true and if the Table to drop does not exist, fail * silently, else throw * @param cascade true if the name argument refers to a View */ public void dropTableOrView(Session session, Table table, boolean cascade) { // ft - concurrent session.commit(false); if (table.isView()) { removeSchemaObject(table.getName(), cascade); } else { dropTable(session, table, cascade); } } private void dropTable(Session session, Table table, boolean cascade) { Schema schema = (Schema) schemaMap.get(table.getSchemaName().name); int dropIndex = schema.tableList.getIndex(table.getName().name); OrderedHashSet externalConstraints = table.getDependentExternalConstraints(); OrderedHashSet externalReferences = new OrderedHashSet(); getCascadingReferencingObjectNames(table.getName(), externalReferences); if (!cascade) { for (int i = 0; i < externalConstraints.size(); i++) { Constraint c = (Constraint) externalConstraints.get(i); HsqlName tablename = c.getRef().getName(); HsqlName refname = c.getRefName(); if (c.getConstraintType() == SchemaObject.ConstraintTypes.MAIN) { throw Error.error(ErrorCode.X_42533, refname.schema.name + '.' + tablename.name + '.' + refname.name); } } if (!externalReferences.isEmpty()) { int i = 0; for (; i < externalReferences.size(); i++) { HsqlName name = (HsqlName) externalReferences.get(i); if (name.parent == table.getName()) { continue; } throw Error.error(ErrorCode.X_42502, name.getSchemaQualifiedStatementName()); } } } OrderedHashSet tableSet = new OrderedHashSet(); OrderedHashSet constraintNameSet = new OrderedHashSet(); OrderedHashSet indexNameSet = new OrderedHashSet(); // only columns with refs OrderedHashSet childReferences = table.getReferences(); SchemaObject[] triggers = table.getTriggers(); for (int i = 0; i < triggers.length; i++) { childReferences.add(triggers[i].getName()); } for (int i = 0; i < externalConstraints.size(); i++) { Constraint c = (Constraint) externalConstraints.get(i); Table t = c.getMain(); if (t != table) { tableSet.add(t); } t = c.getRef(); if (t != table) { tableSet.add(t); } constraintNameSet.add(c.getMainName()); constraintNameSet.add(c.getRefName()); indexNameSet.add(c.getRefIndex().getName()); } TableWorks tw = new TableWorks(session, table); tableSet = tw.makeNewTables(tableSet, constraintNameSet, indexNameSet); tw.setNewTablesInSchema(tableSet); tw.updateConstraints(tableSet, constraintNameSet); removeSchemaObjects(externalReferences); removeSchemaObjects(childReferences); removeReferencedObject(table.getName()); removeReferencingObject(table); schema.tableList.remove(dropIndex); schema.indexLookup.removeParent(table.getName()); schema.constraintLookup.removeParent(table.getName()); removeTable(session, table); recompileDependentObjects(tableSet); } private void removeTable(Session session, Table table) { database.getGranteeManager().removeDbObject(table.getName()); table.releaseTriggers(); if (table.hasLobColumn()) { RowIterator it = table.rowIterator(session); while (it.hasNext()) { Row row = it.getNextRow(); Object[] data = row.getData(); session.sessionData.adjustLobUsageCount(table, data, -1); } } database.persistentStoreCollection.releaseStore(table); } public void setTable(int index, Table table) { Schema schema = (Schema) schemaMap.get(table.getSchemaName().name); schema.tableList.set(index, table.getName().name, table); } /** * Returns index of a table or view in the HashMappedList that * contains the table objects for this Database. * * @param table the Table object * @return the index of the specified table or view, or -1 if not found */ public int getTableIndex(Table table) { Schema schema = (Schema) schemaMap.get(table.getSchemaName().name); if (schema == null) { return -1; } HsqlName name = table.getName(); return schema.tableList.getIndex(name.name); } public void recompileDependentObjects(OrderedHashSet tableSet) { OrderedHashSet set = new OrderedHashSet(); for (int i = 0; i < tableSet.size(); i++) { Table table = (Table) tableSet.get(i); set.addAll(getReferencingObjectNames(table.getName())); } Session session = database.sessionManager.getSysSession(); for (int i = 0; i < set.size(); i++) { HsqlName name = (HsqlName) set.get(i); switch (name.type) { case SchemaObject.VIEW : case SchemaObject.CONSTRAINT : case SchemaObject.ASSERTION : case SchemaObject.ROUTINE : case SchemaObject.PROCEDURE : case SchemaObject.FUNCTION : case SchemaObject.SPECIFIC_ROUTINE : SchemaObject object = getSchemaObject(name); object.compile(session, null); break; } } } /** * After addition or removal of columns and indexes all views that * reference the table should be recompiled. */ public void recompileDependentObjects(Table table) { OrderedHashSet set = new OrderedHashSet(); getCascadingReferencingObjectNames(table.getName(), set); Session session = database.sessionManager.getSysSession(); for (int i = 0; i < set.size(); i++) { HsqlName name = (HsqlName) set.get(i); switch (name.type) { case SchemaObject.VIEW : case SchemaObject.CONSTRAINT : case SchemaObject.ASSERTION : case SchemaObject.ROUTINE : case SchemaObject.PROCEDURE : case SchemaObject.FUNCTION : case SchemaObject.SPECIFIC_ROUTINE : SchemaObject object = getSchemaObject(name); object.compile(session, null); break; } } HsqlArrayList list = getAllTables(); for (int i = 0; i < list.size(); i++) { Table t = (Table) list.get(i); t.verifyConstraintsIntegrity(); } } public NumberSequence getSequence(String name, String schemaName, boolean raise) { Schema schema = (Schema) schemaMap.get(schemaName); if (schema != null) { NumberSequence object = (NumberSequence) schema.sequenceList.get(name); if (object != null) { return object; } } if (raise) { throw Error.error(ErrorCode.X_42501, name); } return null; } public Type getUserDefinedType(String name, String schemaName, boolean raise) { Schema schema = (Schema) schemaMap.get(schemaName); if (schema != null) { SchemaObject object = schema.typeLookup.getObject(name); if (object != null) { return (Type) object; } } if (raise) { throw Error.error(ErrorCode.X_42501, name); } return null; } public Type getDomain(String name, String schemaName, boolean raise) { Schema schema = (Schema) schemaMap.get(schemaName); if (schema != null) { SchemaObject object = schema.typeLookup.getObject(name); if (object != null && ((Type) object).isDomainType()) { return (Type) object; } } if (raise) { throw Error.error(ErrorCode.X_42501, name); } return null; } public Type getDistinctType(String name, String schemaName, boolean raise) { Schema schema = (Schema) schemaMap.get(schemaName); if (schema != null) { SchemaObject object = schema.typeLookup.getObject(name); if (object != null && ((Type) object).isDomainType()) { return (Type) object; } } if (raise) { throw Error.error(ErrorCode.X_42501, name); } return null; } public SchemaObject getSchemaObject(String name, String schemaName, int type) { SchemaObject object = findSchemaObject(name, schemaName, type); if (object == null) { throw Error.error(SchemaObjectSet.getGetErrorCode(type), name); } return object; } public SchemaObject findSchemaObject(String name, String schemaName, int type) { Schema schema = (Schema) schemaMap.get(schemaName); if (schema == null) { return null; } SchemaObjectSet set = null; HsqlName objectName; Table table; switch (type) { case SchemaObject.SEQUENCE : return schema.sequenceLookup.getObject(name); case SchemaObject.TABLE : case SchemaObject.VIEW : return schema.tableLookup.getObject(name); case SchemaObject.CHARSET : if (name.equals("SQL_IDENTIFIER")) { return SqlInvariants.SQL_IDENTIFIER_CHARSET; } if (name.equals("SQL_TEXT")) { return SqlInvariants.SQL_TEXT; } if (name.equals("LATIN1")) { return SqlInvariants.LATIN1; } if (name.equals("ASCII_GRAPHIC")) { return SqlInvariants.ASCII_GRAPHIC; } return schema.charsetLookup.getObject(name); case SchemaObject.COLLATION : return schema.collationLookup.getObject(name); case SchemaObject.PROCEDURE : return schema.procedureLookup.getObject(name); case SchemaObject.FUNCTION : return schema.functionLookup.getObject(name); case SchemaObject.ROUTINE : { SchemaObject object = schema.procedureLookup.getObject(name); if (object == null) { object = schema.functionLookup.getObject(name); } return object; } case SchemaObject.SPECIFIC_ROUTINE : return schema.specificRoutineLookup.getObject(name); case SchemaObject.DOMAIN : case SchemaObject.TYPE : return schema.typeLookup.getObject(name); case SchemaObject.INDEX : set = schema.indexLookup; objectName = set.getName(name); if (objectName == null) { return null; } table = (Table) schema.tableList.get(objectName.parent.name); return table.getIndex(name); case SchemaObject.CONSTRAINT : set = schema.constraintLookup; objectName = set.getName(name); if (objectName == null) { return null; } table = (Table) schema.tableList.get(objectName.parent.name); if (table == null) { return null; } return table.getConstraint(name); case SchemaObject.TRIGGER : set = schema.indexLookup; objectName = set.getName(name); if (objectName == null) { return null; } table = (Table) schema.tableList.get(objectName.parent.name); return table.getTrigger(name); default : throw Error.runtimeError(ErrorCode.U_S0500, "SchemaManager"); } } // INDEX management /** * Returns the table that has an index with the given name and schema. */ Table findUserTableForIndex(Session session, String name, String schemaName) { Schema schema = (Schema) schemaMap.get(schemaName); HsqlName indexName = schema.indexLookup.getName(name); if (indexName == null) { return null; } return findUserTable(session, indexName.parent.name, schemaName); } /** * Drops the index with the specified name. */ void dropIndex(Session session, HsqlName name) { Table t = getTable(session, name.parent.name, name.parent.schema.name); TableWorks tw = new TableWorks(session, t); tw.dropIndex(name.name); } /** * Drops the index with the specified name. */ void dropConstraint(Session session, HsqlName name, boolean cascade) { Table t = getTable(session, name.parent.name, name.parent.schema.name); TableWorks tw = new TableWorks(session, t); tw.dropConstraint(name.name, cascade); } void removeDependentObjects(HsqlName name) { Schema schema = (Schema) schemaMap.get(name.schema.name); schema.indexLookup.removeParent(name); schema.constraintLookup.removeParent(name); schema.triggerLookup.removeParent(name); } /** * Removes any foreign key Constraint objects (exported keys) held by any * tables referenced by the specified table. <p> * * This method is called as the last step of a successful call to * dropTable() in order to ensure that the dropped Table ceases to be * referenced when enforcing referential integrity. * * @param toDrop The table to which other tables may be holding keys. * This is a table that is in the process of being dropped. */ void removeExportedKeys(Table toDrop) { // toDrop.schema may be null because it is not registerd Schema schema = (Schema) schemaMap.get(toDrop.getSchemaName().name); for (int i = 0; i < schema.tableList.size(); i++) { Table table = (Table) schema.tableList.get(i); Constraint[] constraints = table.getConstraints(); for (int j = constraints.length - 1; j >= 0; j--) { Table refTable = constraints[j].getRef(); if (toDrop == refTable) { table.removeConstraint(j); } } } } public Iterator databaseObjectIterator(String schemaName, int type) { Schema schema = (Schema) schemaMap.get(schemaName); return schema.schemaObjectIterator(type); } public Iterator databaseObjectIterator(int type) { Iterator it = schemaMap.values().iterator(); Iterator objects = new WrapperIterator(); while (it.hasNext()) { Schema temp = (Schema) it.next(); objects = new WrapperIterator(objects, temp.schemaObjectIterator(type)); } return objects; } // references private void addReferences(SchemaObject object) { OrderedHashSet set = object.getReferences(); if (set == null) { return; } for (int i = 0; i < set.size(); i++) { HsqlName referenced = (HsqlName) set.get(i); HsqlName name = object.getName(); if (object instanceof Routine) { name = ((Routine) object).getSpecificName(); } referenceMap.put(referenced, name); } } private void removeReferencedObject(HsqlName referenced) { referenceMap.remove(referenced); } private void removeReferencingObject(SchemaObject object) { OrderedHashSet set = object.getReferences(); if (set == null) { return; } for (int i = 0; i < set.size(); i++) { HsqlName referenced = (HsqlName) set.get(i); HsqlName name = object.getName(); if (object instanceof Routine) { name = ((Routine) object).getSpecificName(); } referenceMap.remove(referenced, name); if (name.parent != null) { referenceMap.remove(referenced, name.parent); } } } OrderedHashSet getReferencingObjectNames(HsqlName object) { OrderedHashSet set = new OrderedHashSet(); Iterator it = referenceMap.get(object); while (it.hasNext()) { HsqlName name = (HsqlName) it.next(); set.add(name); } return set; } public OrderedHashSet getReferencingObjectNames(HsqlName table, HsqlName column) { OrderedHashSet set = new OrderedHashSet(); Iterator it = referenceMap.get(table); while (it.hasNext()) { HsqlName name = (HsqlName) it.next(); SchemaObject object = getSchemaObject(name); OrderedHashSet references = object.getReferences(); if (references.contains(column)) { set.add(name); } } return set; } private boolean isReferenced(HsqlName object) { return referenceMap.containsKey(object); } // public void getCascadingReferencingObjectNames(HsqlName object, OrderedHashSet set) { OrderedHashSet newSet = new OrderedHashSet(); Iterator it = referenceMap.get(object); while (it.hasNext()) { HsqlName name = (HsqlName) it.next(); boolean added = set.add(name); if (added) { newSet.add(name); } } for (int i = 0; i < newSet.size(); i++) { HsqlName name = (HsqlName) newSet.get(i); getCascadingReferencingObjectNames(name, set); } } // private void getCascadingSchemaReferences(HsqlName schema, OrderedHashSet set) { Iterator mainIterator = referenceMap.keySet().iterator(); while (mainIterator.hasNext()) { HsqlName name = (HsqlName) mainIterator.next(); if (name.schema != schema) { continue; } getCascadingReferencingObjectNames(name, set); } for (int i = 0; i < set.size(); i++) { HsqlName name = (HsqlName) set.get(i); if (name.schema == schema) { set.remove(i); i--; } } } // public HsqlName getSchemaObjectName(HsqlName schemaName, String name, int type, boolean raise) { Schema schema = (Schema) schemaMap.get(schemaName.name); SchemaObjectSet set = null; if (schema == null) { if (raise) { throw Error.error(SchemaObjectSet.getGetErrorCode(type)); } else { return null; } } if (type == SchemaObject.ROUTINE) { set = schema.functionLookup; SchemaObject object = schema.functionLookup.getObject(name); if (object == null) { set = schema.procedureLookup; object = schema.procedureLookup.getObject(name); } } else { set = getSchemaObjectSet(schema, type); } if (raise) { set.checkExists(name); } return set.getName(name); } public SchemaObject getSchemaObject(HsqlName name) { Schema schema = (Schema) schemaMap.get(name.schema.name); if (schema == null) { return null; } switch (name.type) { case SchemaObject.SEQUENCE : return (SchemaObject) schema.sequenceList.get(name.name); case SchemaObject.TABLE : case SchemaObject.VIEW : return (SchemaObject) schema.tableList.get(name.name); case SchemaObject.CHARSET : return schema.charsetLookup.getObject(name.name); case SchemaObject.COLLATION : return schema.collationLookup.getObject(name.name); case SchemaObject.PROCEDURE : return schema.procedureLookup.getObject(name.name); case SchemaObject.FUNCTION : return schema.functionLookup.getObject(name.name); case RoutineSchema.SPECIFIC_ROUTINE : return schema.specificRoutineLookup.getObject(name.name); case RoutineSchema.ROUTINE : SchemaObject object = schema.functionLookup.getObject(name.name); if (object == null) { object = schema.procedureLookup.getObject(name.name); } return object; case SchemaObject.DOMAIN : case SchemaObject.TYPE : return schema.typeLookup.getObject(name.name); case SchemaObject.TRIGGER : { name = schema.triggerLookup.getName(name.name); if (name == null) { return null; } HsqlName tableName = name.parent; Table table = (Table) schema.tableList.get(tableName.name); return table.getTrigger(name.name); } case SchemaObject.CONSTRAINT : { name = schema.constraintLookup.getName(name.name); if (name == null) { return null; } HsqlName tableName = name.parent; Table table = (Table) schema.tableList.get(tableName.name); return table.getConstraint(name.name); } case SchemaObject.ASSERTION : return null; case SchemaObject.INDEX : name = schema.indexLookup.getName(name.name); if (name == null) { return null; } HsqlName tableName = name.parent; Table table = (Table) schema.tableList.get(tableName.name); return table.getIndex(name.name); } return null; } public void checkColumnIsReferenced(HsqlName tableName, HsqlName name) { OrderedHashSet set = getReferencingObjectNames(tableName, name); if (!set.isEmpty()) { HsqlName objectName = (HsqlName) set.get(0); throw Error.error(ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } } public void checkObjectIsReferenced(HsqlName name) { OrderedHashSet set = getReferencingObjectNames(name); HsqlName refName = null; for (int i = 0; i < set.size(); i++) { refName = (HsqlName) set.get(i); // except columns of same table if (refName.parent != name) { break; } refName = null; } if (refName == null) { return; } throw Error.error(ErrorCode.X_42502, refName.getSchemaQualifiedStatementName()); } public void checkSchemaNameCanChange(HsqlName name) { Iterator it = referenceMap.values().iterator(); HsqlName refName = null; mainLoop: while (it.hasNext()) { refName = (HsqlName) it.next(); switch (refName.type) { case SchemaObject.VIEW : case SchemaObject.ROUTINE : case SchemaObject.FUNCTION : case SchemaObject.PROCEDURE : case SchemaObject.TRIGGER : case SchemaObject.SPECIFIC_ROUTINE : if (refName.schema == name) { break mainLoop; } break; default : break; } refName = null; } if (refName == null) { return; } throw Error.error(ErrorCode.X_42502, refName.getSchemaQualifiedStatementName()); } public void addSchemaObject(SchemaObject object) { HsqlName name = object.getName(); Schema schema = (Schema) schemaMap.get(name.schema.name); SchemaObjectSet set = getSchemaObjectSet(schema, name.type); switch (name.type) { case SchemaObject.PROCEDURE : case SchemaObject.FUNCTION : { RoutineSchema routine = (RoutineSchema) set.getObject(name.name); if (routine == null) { routine = new RoutineSchema(name.type, name); routine.addSpecificRoutine(database, (Routine) object); set.checkAdd(name); SchemaObjectSet specificSet = getSchemaObjectSet(schema, SchemaObject.SPECIFIC_ROUTINE); specificSet.checkAdd(((Routine) object).getSpecificName()); set.add(routine); specificSet.add(object); } else { SchemaObjectSet specificSet = getSchemaObjectSet(schema, SchemaObject.SPECIFIC_ROUTINE); HsqlName specificName = ((Routine) object).getSpecificName(); if (specificName != null) { specificSet.checkAdd(specificName); } routine.addSpecificRoutine(database, (Routine) object); specificSet.add(object); } addReferences(object); return; } case SchemaObject.TABLE : { OrderedHashSet refs = object.getReferences(); for (int i = 0; i < refs.size(); i++) { HsqlName ref = (HsqlName) refs.get(i); if (ref.type == SchemaObject.COLUMN) { int index = ((Table) object).findColumn(ref.name); ColumnSchema column = ((Table) object).getColumn(index); addSchemaObject(column); } } break; } case SchemaObject.COLUMN : { if (object.getReferences().isEmpty()) { return; } break; } } if (set != null) { set.add(object); } addReferences(object); } public void removeSchemaObject(HsqlName name, boolean cascade) { OrderedHashSet objectSet = new OrderedHashSet(); switch (name.type) { case SchemaObject.ROUTINE : case SchemaObject.PROCEDURE : case SchemaObject.FUNCTION : { RoutineSchema routine = (RoutineSchema) getSchemaObject(name); if (routine != null) { Routine[] specifics = routine.getSpecificRoutines(); for (int i = 0; i < specifics.length; i++) { getCascadingReferencingObjectNames( specifics[i].getSpecificName(), objectSet); } } } break; case SchemaObject.SEQUENCE : case SchemaObject.TABLE : case SchemaObject.VIEW : case SchemaObject.TYPE : case SchemaObject.CHARSET : case SchemaObject.COLLATION : case SchemaObject.SPECIFIC_ROUTINE : getCascadingReferencingObjectNames(name, objectSet); break; case SchemaObject.DOMAIN : OrderedHashSet set = getReferencingObjectNames(name); Iterator it = set.iterator(); while (it.hasNext()) { HsqlName ref = (HsqlName) it.next(); if (ref.type == SchemaObject.COLUMN) { it.remove(); } } if (!set.isEmpty()) { HsqlName objectName = (HsqlName) set.get(0); throw Error.error( ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } break; } if (objectSet.isEmpty()) { removeSchemaObject(name); return; } if (!cascade) { HsqlName objectName = (HsqlName) objectSet.get(0); throw Error.error(ErrorCode.X_42502, objectName.getSchemaQualifiedStatementName()); } objectSet.add(name); removeSchemaObjects(objectSet); } public void removeSchemaObjects(OrderedHashSet set) { for (int i = 0; i < set.size(); i++) { HsqlName name = (HsqlName) set.get(i); removeSchemaObject(name); } } public void removeSchemaObject(HsqlName name) { Schema schema = (Schema) schemaMap.get(name.schema.name); SchemaObject object = null; SchemaObjectSet set = null; switch (name.type) { case SchemaObject.SEQUENCE : set = schema.sequenceLookup; object = set.getObject(name.name); break; case SchemaObject.TABLE : case SchemaObject.VIEW : { set = schema.tableLookup; object = set.getObject(name.name); break; } case SchemaObject.COLUMN : { Table table = (Table) getSchemaObject(name.parent); if (table != null) { object = table.getColumn(table.getColumnIndex(name.name)); } break; } case SchemaObject.CHARSET : set = schema.charsetLookup; object = set.getObject(name.name); break; case SchemaObject.COLLATION : set = schema.collationLookup; object = set.getObject(name.name); break; case SchemaObject.PROCEDURE : { set = schema.procedureLookup; RoutineSchema routine = (RoutineSchema) set.getObject(name.name); object = routine; Routine[] specifics = routine.getSpecificRoutines(); for (int i = 0; i < specifics.length; i++) { removeSchemaObject(specifics[i].getSpecificName()); } break; } case SchemaObject.FUNCTION : { set = schema.functionLookup; RoutineSchema routine = (RoutineSchema) set.getObject(name.name); object = routine; Routine[] specifics = routine.getSpecificRoutines(); for (int i = 0; i < specifics.length; i++) { removeSchemaObject(specifics[i].getSpecificName()); } break; } case SchemaObject.SPECIFIC_ROUTINE : { set = schema.specificRoutineLookup; Routine routine = (Routine) set.getObject(name.name); object = routine; routine.routineSchema.removeSpecificRoutine(routine); if (routine.routineSchema.getSpecificRoutines().length == 0) { removeSchemaObject(routine.getName()); } break; } case SchemaObject.DOMAIN : case SchemaObject.TYPE : set = schema.typeLookup; object = set.getObject(name.name); break; case SchemaObject.INDEX : set = schema.indexLookup; break; case SchemaObject.CONSTRAINT : { set = schema.constraintLookup; if (name.parent.type == SchemaObject.TABLE) { Table table = (Table) schema.tableList.get(name.parent.name); object = table.getConstraint(name.name); table.removeConstraint(name.name); } else if (name.parent.type == SchemaObject.DOMAIN) { Type type = (Type) schema.typeLookup.getObject(name.parent.name); object = type.userTypeModifier.getConstraint(name.name); type.userTypeModifier.removeConstraint(name.name); } break; } case SchemaObject.TRIGGER : { set = schema.triggerLookup; Table table = (Table) schema.tableList.get(name.parent.name); object = table.getTrigger(name.name); if (object != null) { table.removeTrigger((TriggerDef) object); } break; } default : throw Error.runtimeError(ErrorCode.U_S0500, "SchemaManager"); } if (object != null) { database.getGranteeManager().removeDbObject(name); removeReferencingObject(object); } if (set != null) { set.remove(name.name); } removeReferencedObject(name); } public void renameSchemaObject(HsqlName name, HsqlName newName) { if (name.schema != newName.schema) { throw Error.error(ErrorCode.X_42505, newName.schema.name); } checkObjectIsReferenced(name); Schema schema = (Schema) schemaMap.get(name.schema.name); SchemaObjectSet set = getSchemaObjectSet(schema, name.type); set.rename(name, newName); } public String[] getSQLArray() { OrderedHashSet resolved = new OrderedHashSet(); OrderedHashSet unresolved = new OrderedHashSet(); HsqlArrayList list = new HsqlArrayList(); Iterator schemas = schemaMap.values().iterator(); schemas = schemaMap.values().iterator(); while (schemas.hasNext()) { Schema schema = (Schema) schemas.next(); if (SqlInvariants.isSystemSchemaName(schema.getName().name)) { continue; } if (SqlInvariants.isLobsSchemaName(schema.getName().name)) { continue; } list.add(schema.getSQL()); schema.addSimpleObjects(unresolved); } while (true) { Iterator it = unresolved.iterator(); if (!it.hasNext()) { break; } OrderedHashSet newResolved = new OrderedHashSet(); SchemaObjectSet.addAllSQL(resolved, unresolved, list, it, newResolved); unresolved.removeAll(newResolved); if (newResolved.size() == 0) { break; } } schemas = schemaMap.values().iterator(); while (schemas.hasNext()) { Schema schema = (Schema) schemas.next(); if (SqlInvariants.isLobsSchemaName(schema.getName().name)) { continue; } if (SqlInvariants.isSystemSchemaName(schema.getName().name)) { continue; } list.addAll(schema.getSQLArray(resolved, unresolved)); } while (true) { Iterator it = unresolved.iterator(); if (!it.hasNext()) { break; } OrderedHashSet newResolved = new OrderedHashSet(); SchemaObjectSet.addAllSQL(resolved, unresolved, list, it, newResolved); unresolved.removeAll(newResolved); if (newResolved.size() == 0) { break; } } schemas = schemaMap.values().iterator(); while (schemas.hasNext()) { Schema schema = (Schema) schemas.next(); if (SqlInvariants.isLobsSchemaName(schema.getName().name)) { continue; } if (SqlInvariants.isSystemSchemaName(schema.getName().name)) { continue; } String[] t = schema.getTriggerSQL(); if (t.length > 0) { list.add(Schema.getSetSchemaSQL(schema.getName())); list.addAll(t); } list.addAll(schema.getSequenceRestartSQL()); } schemas = schemaMap.values().iterator(); while (schemas.hasNext()) { Schema schema = (Schema) schemas.next(); list.addAll(schema.getSequenceRestartSQL()); } if (defaultSchemaHsqlName != null) { StringBuffer sb = new StringBuffer(); sb.append(Tokens.T_SET).append(' ').append(Tokens.T_DATABASE); sb.append(' ').append(Tokens.T_DEFAULT).append(' '); sb.append(Tokens.T_INITIAL).append(' ').append(Tokens.T_SCHEMA); sb.append(' ').append(defaultSchemaHsqlName.statementName); list.add(sb.toString()); } String[] array = new String[list.size()]; list.toArray(array); return array; } public String[] getTablePropsSQL(boolean withHeader) { HsqlArrayList tableList = getAllTables(); HsqlArrayList list = new HsqlArrayList(); for (int i = 0; i < tableList.size(); i++) { Table t = (Table) tableList.get(i); if (t.isText()) { String[] ddl = t.getSQLForTextSource(withHeader); list.addAll(ddl); } else { String ddl = t.getSQLForReadOnly(); if (ddl != null) { list.add(ddl); } } } String[] array = new String[list.size()]; list.toArray(array); return array; } public String[] getIndexRootsSQL() { Session sysSession = database.sessionManager.getSysSession(); int[][] rootsArray = getIndexRoots(sysSession); HsqlArrayList tableList = getAllTables(); HsqlArrayList list = new HsqlArrayList(); for (int i = 0; i < rootsArray.length; i++) { Table t = (Table) tableList.get(i); if (rootsArray[i] != null && rootsArray[i].length > 0 && rootsArray[i][0] != -1) { String ddl = ((Table) tableList.get(i)).getIndexRootsSQL(rootsArray[i]); list.add(ddl); } } String[] array = new String[list.size()]; list.toArray(array); return array; } public String[] getCommentsArray() { HsqlArrayList tableList = getAllTables(); HsqlArrayList list = new HsqlArrayList(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < tableList.size(); i++) { Table table = (Table) tableList.get(i); if (table.getTableType() == Table.SYSTEM_TABLE) { continue; } int colCount = table.getColumnCount(); for (int j = 0; j < colCount; j++) { ColumnSchema column = table.getColumn(j); if (column.getName().comment == null) { continue; } sb.setLength(0); sb.append(Tokens.T_COMMENT).append(' ').append(Tokens.T_ON); sb.append(' ').append(Tokens.T_COLUMN).append(' '); sb.append(table.getName().getSchemaQualifiedStatementName()); sb.append('.').append(column.getName().statementName); sb.append(' ').append(Tokens.T_IS).append(' '); sb.append( StringConverter.toQuotedString( column.getName().comment, '\'', true)); list.add(sb.toString()); } if (table.getName().comment == null) { continue; } sb.setLength(0); sb.append(Tokens.T_COMMENT).append(' ').append(Tokens.T_ON); sb.append(' ').append(Tokens.T_TABLE).append(' '); sb.append(table.getName().getSchemaQualifiedStatementName()); sb.append(' ').append(Tokens.T_IS).append(' '); sb.append(StringConverter.toQuotedString(table.getName().comment, '\'', true)); list.add(sb.toString()); } Iterator it = databaseObjectIterator(SchemaObject.ROUTINE); while (it.hasNext()) { SchemaObject object = (SchemaObject) it.next(); if (object.getName().comment == null) { continue; } sb.setLength(0); sb.append(Tokens.T_COMMENT).append(' ').append(Tokens.T_ON); sb.append(' ').append(Tokens.T_ROUTINE).append(' '); sb.append(object.getName().getSchemaQualifiedStatementName()); sb.append(' ').append(Tokens.T_IS).append(' '); sb.append(StringConverter.toQuotedString(object.getName().comment, '\'', true)); list.add(sb.toString()); } String[] array = new String[list.size()]; list.toArray(array); return array; } int[][] tempIndexRoots; public void setTempIndexRoots(int[][] roots) { tempIndexRoots = roots; } public int[][] getIndexRoots(Session session) { if (tempIndexRoots != null) { int[][] roots = tempIndexRoots; tempIndexRoots = null; return roots; } HsqlArrayList allTables = getAllTables(); HsqlArrayList list = new HsqlArrayList(); for (int i = 0, size = allTables.size(); i < size; i++) { Table t = (Table) allTables.get(i); if (t.getTableType() == TableBase.CACHED_TABLE) { int[] roots = t.getIndexRootsArray(); list.add(roots); } else { list.add(null); } } int[][] array = new int[list.size()][]; list.toArray(array); return array; } /** * called after the completion of defrag */ public void setIndexRoots(int[][] roots) { HsqlArrayList allTables = database.schemaManager.getAllTables(); for (int i = 0, size = allTables.size(); i < size; i++) { Table t = (Table) allTables.get(i); if (t.getTableType() == TableBase.CACHED_TABLE) { int[] rootsArray = roots[i]; if (roots != null) { t.setIndexRoots(rootsArray); } } } } public void setDefaultTableType(int type) { defaultTableType = type; } public int getDefaultTableType() { return defaultTableType; } }