--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.depend.BasicDependencyManager\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.depend;\r
+\r
+import java.util.Enumeration;\r
+import java.util.Hashtable;\r
+import java.util.List;\r
+import java.util.ListIterator;\r
+\r
+import org.apache.derby.catalog.DependableFinder;\r
+import org.apache.derby.catalog.UUID;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.Parser;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.conn.StatementContext;\r
+import org.apache.derby.iapi.sql.depend.Dependency;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+import org.apache.derby.iapi.sql.depend.Dependent;\r
+import org.apache.derby.iapi.sql.depend.Provider;\r
+import org.apache.derby.iapi.sql.depend.ProviderInfo;\r
+import org.apache.derby.iapi.sql.depend.ProviderList;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.DependencyDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ViewDescriptor;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.impl.sql.compile.CreateViewNode;\r
+\r
+/**\r
+ The dependency manager tracks needs that dependents have of providers.\r
+ */\r
+\r
+public class BasicDependencyManager implements DependencyManager {\r
+ \r
+ /**\r
+ * DataDictionary for this database.\r
+ */\r
+ private final DataDictionary dd;\r
+ \r
+ /**\r
+ * Map of in-memory dependencies for Dependents.\r
+ * In-memory means that one or both of the Dependent\r
+ * or Provider are non-persistent (isPersistent() returns false).\r
+ * \r
+ * Key is the UUID of the Dependent (from getObjectID()).\r
+ * Value is a List containing Dependency objects, each\r
+ * of whihc links the same Dependent to a Provider.\r
+ * Dependency objects in the List are unique.\r
+ * \r
+ */\r
+ private final Hashtable dependents = new Hashtable();\r
+ \r
+ /**\r
+ * Map of in-memory dependencies for Providers.\r
+ * In-memory means that one or both of the Dependent\r
+ * or Provider are non-persistent (isPersistent() returns false).\r
+ * \r
+ * Key is the UUID of the Provider (from getObjectID()).\r
+ * Value is a List containing Dependency objects, each\r
+ * of which links the same Provider to a Dependent.\r
+ * Dependency objects in the List are unique.\r
+ * \r
+ */ \r
+ private final Hashtable providers = new Hashtable();\r
+\r
+\r
+ //\r
+ // DependencyManager interface\r
+ //\r
+\r
+ /**\r
+ adds a dependency from the dependent on the provider.\r
+ This will be considered to be the default type of\r
+ dependency, when dependency types show up.\r
+ <p>\r
+ Implementations of addDependency should be fast --\r
+ performing alot of extra actions to add a dependency would\r
+ be a detriment.\r
+\r
+ @param d the dependent\r
+ @param p the provider\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ public void addDependency(Dependent d, Provider p, ContextManager cm) \r
+ throws StandardException {\r
+ addDependency(d, p, cm, null);\r
+ }\r
+ \r
+ private void addDependency(Dependent d, Provider p, ContextManager cm, \r
+ TransactionController tc) throws StandardException {\r
+\r
+ //synchronized(this) //Jeff Huang\r
+ {\r
+ Dependency dy = new BasicDependency(d, p);\r
+\r
+ /* Dependencies are either in-memory or stored, but not both */\r
+ if (! d.isPersistent() || ! p.isPersistent())\r
+ {\r
+ /* Duplicate dependencies are not added to the lists.\r
+ * If we find that the dependency we are trying to add in\r
+ * one list is a duplicate, then it should be a duplicate in the\r
+ * other list.\r
+ */\r
+ boolean addedToDeps = false;\r
+ boolean addedToProvs = false;\r
+ \r
+ addedToDeps = addDependencyToTable(dependents, d.getObjectID(), dy);\r
+ if (addedToDeps)\r
+ {\r
+ addedToProvs = addDependencyToTable(providers, p.getObjectID(), dy);\r
+ }\r
+ else if (SanityManager.DEBUG)\r
+ {\r
+ addedToProvs = addDependencyToTable(providers, p.getObjectID(), dy);\r
+ }\r
+\r
+ /* Dependency should have been added to both or neither */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (addedToDeps != addedToProvs)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "addedToDeps (" + addedToDeps + \r
+ ") and addedToProvs (" +\r
+ addedToProvs + ") are expected to agree");\r
+ }\r
+ }\r
+\r
+ /* Add the dependency to the StatementContext, so that\r
+ * it can be cleared on a pre-execution error.\r
+ */\r
+ StatementContext sc = (StatementContext) cm.getContext(org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);\r
+ sc.addDependency(dy);\r
+ }\r
+ else\r
+ {\r
+ /* Add a stored dependency */\r
+ LanguageConnectionContext lcc = getLanguageConnectionContext(cm);\r
+ DependencyDescriptor dependencyDescriptor;\r
+ boolean wait = (tc == null);\r
+ \r
+ dependencyDescriptor = new DependencyDescriptor(d, p);\r
+\r
+ /* We can finally call the DataDictionary to store the dependency */\r
+ dd.addDescriptor(dependencyDescriptor, null,\r
+ DataDictionary.SYSDEPENDS_CATALOG_NUM, true,\r
+ ((wait)?lcc.getTransactionExecute():tc), wait);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ drops a single dependency\r
+\r
+ @param d the dependent\r
+ @param p the provider\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ private void dropDependency(LanguageConnectionContext lcc, Dependent d, Provider p) throws StandardException\r
+ {\r
+ if (SanityManager.DEBUG) {\r
+ // right now, this routine isn't called for in-memory dependencies\r
+ if (! d.isPersistent() || ! p.isPersistent())\r
+ {\r
+ SanityManager.NOTREACHED();\r
+ }\r
+ }\r
+\r
+ DependencyDescriptor dependencyDescriptor = new DependencyDescriptor(d, p);\r
+\r
+ dd.dropStoredDependency( dependencyDescriptor, \r
+ lcc.getTransactionExecute() );\r
+ }\r
+\r
+\r
+ /**\r
+ mark all dependencies on the named provider as invalid.\r
+ When invalidation types show up, this will use the default\r
+ invalidation type. The dependencies will still exist once\r
+ they are marked invalid; clearDependencies should be used\r
+ to remove dependencies that a dependent has or provider gives.\r
+ <p>\r
+ Implementations of this can take a little time, but are not\r
+ really expected to recompile things against any changes\r
+ made to the provider that caused the invalidation. The\r
+ dependency system makes no guarantees about the state of\r
+ the provider -- implementations can call this before or\r
+ after actually changing the provider to its new state.\r
+ <p>\r
+ Implementations should throw StandardException\r
+ if the invalidation should be disallowed.\r
+\r
+ @param p the provider\r
+ @param action The action causing the invalidate\r
+\r
+ @exception StandardException thrown if unable to make it invalid\r
+ */\r
+ public void invalidateFor(Provider p, int action,\r
+ LanguageConnectionContext lcc)\r
+ throws StandardException\r
+ {\r
+ /*\r
+ ** Non-persistent dependencies are stored in memory, and need to\r
+ ** use "synchronized" to ensure their lists don't change while\r
+ ** the invalidation is taking place. Persistent dependencies are\r
+ ** stored in the data dictionary, and we should *not* do anything\r
+ ** transactional (like reading from a system table) from within\r
+ ** a synchronized method, as it could cause deadlock.\r
+ **\r
+ ** Presumably, the transactional locking in the data dictionary\r
+ ** is enough to protect us, so that we don't have to put any\r
+ ** synchronization in the DependencyManager.\r
+ */\r
+ if (p.isPersistent())\r
+ coreInvalidateFor(p, action, lcc);\r
+ else {\r
+ synchronized (this) {\r
+ coreInvalidateFor(p, action, lcc);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * A version of invalidateFor that does not provide synchronization among\r
+ * invalidators. If parameter "forSync" is true, it also provides\r
+ * synchronization on the dependents. Currently, this means synchronizing\r
+ * on the prepared statements, which might be being executed on other\r
+ * threads.\r
+ * @param p provider\r
+ * @param action The action causing the invalidate\r
+ * @param lcc Language connection context\r
+ *\r
+ * @exception StandardException Thrown on error.\r
+ */\r
+ private void coreInvalidateFor(Provider p, int action, LanguageConnectionContext lcc)\r
+ throws StandardException\r
+ {\r
+ List list = getDependents(p);\r
+ if (list == null)\r
+ {\r
+ return;\r
+ }\r
+\r
+\r
+ // affectedCols is passed in from table descriptor provider to indicate\r
+ // which columns it cares; subsetCols is affectedCols' intersection\r
+ // with column bit map found in the provider of SYSDEPENDS line to\r
+ // find out which columns really matter. If SYSDEPENDS line's\r
+ // dependent is view (or maybe others), provider is table, yet it \r
+ // doesn't have column bit map because the view was created in a\r
+ // previous version of server which doesn't support column dependency,\r
+ // and we really want it to have (such as in drop column), in any case\r
+ // if we passed in table descriptor to this function with a bit map,\r
+ // we really need this, we generate the bitmaps on the fly and update\r
+ // SYSDEPENDS\r
+\r
+ FormatableBitSet affectedCols = null, subsetCols = null;\r
+ if (p instanceof TableDescriptor)\r
+ {\r
+ affectedCols = ((TableDescriptor) p).getReferencedColumnMap();\r
+ if (affectedCols != null)\r
+ subsetCols = new FormatableBitSet(affectedCols.getLength());\r
+ }\r
+\r
+ {\r
+ StandardException noInvalidate = null;\r
+ // We cannot use an iterator here as the invalidations can remove\r
+ // entries from this list. \r
+ for (int ei = list.size() - 1; ei >= 0; ei--)\r
+ {\r
+ if (ei >= list.size())\r
+ continue;\r
+ Dependency dependency = (Dependency) list.get(ei);\r
+\r
+ Dependent dep = dependency.getDependent();\r
+\r
+ if (affectedCols != null)\r
+ {\r
+ TableDescriptor td = (TableDescriptor) dependency.getProvider();\r
+ FormatableBitSet providingCols = td.getReferencedColumnMap();\r
+ if (providingCols == null)\r
+ {\r
+ if (dep instanceof ViewDescriptor)\r
+ {\r
+ ViewDescriptor vd = (ViewDescriptor) dep;\r
+ SchemaDescriptor compSchema;\r
+ compSchema = dd.getSchemaDescriptor(vd.getCompSchemaId(), null);\r
+ CompilerContext newCC = lcc.pushCompilerContext(compSchema);\r
+ Parser pa = newCC.getParser();\r
+\r
+ // Since this is always nested inside another SQL\r
+ // statement, so topLevel flag should be false\r
+ CreateViewNode cvn = (CreateViewNode)pa.parseStatement(\r
+ vd.getViewText());\r
+\r
+ // need a current dependent for bind\r
+ newCC.setCurrentDependent(dep);\r
+ cvn.bindStatement();\r
+ ProviderInfo[] providerInfos = cvn.getProviderInfo();\r
+ lcc.popCompilerContext(newCC);\r
+\r
+ boolean interferent = false;\r
+ for (int i = 0; i < providerInfos.length; i++)\r
+ {\r
+ Provider provider = null;\r
+ provider = (Provider) providerInfos[i].\r
+ getDependableFinder().\r
+ getDependable(dd,\r
+ providerInfos[i].getObjectId());\r
+ if (provider instanceof TableDescriptor)\r
+ {\r
+ TableDescriptor tab = (TableDescriptor)provider;\r
+ FormatableBitSet colMap = tab.getReferencedColumnMap();\r
+ if (colMap == null)\r
+ continue;\r
+ // if later on an error is raised such as in\r
+ // case of interference, this dependency line\r
+ // upgrade will not happen due to rollback\r
+ tab.setReferencedColumnMap(null);\r
+ dropDependency(lcc, vd, tab);\r
+ tab.setReferencedColumnMap(colMap);\r
+ addDependency(vd, tab, lcc.getContextManager());\r
+\r
+ if (tab.getObjectID().equals(td.getObjectID()))\r
+ {\r
+ System.arraycopy(affectedCols.getByteArray(), 0, \r
+ subsetCols.getByteArray(), 0, \r
+ affectedCols.getLengthInBytes());\r
+ subsetCols.and(colMap);\r
+ if (subsetCols.anySetBit() != -1)\r
+ {\r
+ interferent = true;\r
+ ((TableDescriptor) p).setReferencedColumnMap(subsetCols);\r
+ }\r
+ }\r
+ } // if provider instanceof TableDescriptor\r
+ } // for providerInfos\r
+ if (! interferent)\r
+ continue;\r
+ } // if dep instanceof ViewDescriptor\r
+ else\r
+ ((TableDescriptor) p).setReferencedColumnMap(null);\r
+ } // if providingCols == null\r
+ else\r
+ {\r
+ System.arraycopy(affectedCols.getByteArray(), 0, subsetCols.getByteArray(), 0, affectedCols.getLengthInBytes());\r
+ subsetCols.and(providingCols);\r
+ if (subsetCols.anySetBit() == -1)\r
+ continue;\r
+ ((TableDescriptor) p).setReferencedColumnMap(subsetCols);\r
+ }\r
+ }\r
+\r
+ // generate a list of invalidations that fail.\r
+ try {\r
+ dep.prepareToInvalidate(p, action, lcc);\r
+ } catch (StandardException sqle) {\r
+\r
+ if (noInvalidate == null) {\r
+ noInvalidate = sqle;\r
+ } else {\r
+ try {\r
+ sqle.initCause(noInvalidate);\r
+ noInvalidate = sqle;\r
+ } catch (IllegalStateException ise) {\r
+ // We weren't able to chain the exceptions. That's\r
+ // OK, since we always have the first exception we\r
+ // caught. Just skip the current exception.\r
+ }\r
+ }\r
+ }\r
+ if (noInvalidate == null) {\r
+\r
+ if (affectedCols != null)\r
+ ((TableDescriptor) p).setReferencedColumnMap(affectedCols);\r
+\r
+ // REVISIT: future impl will want to mark the individual\r
+ // dependency as invalid as well as the dependent...\r
+ dep.makeInvalid(action, lcc);\r
+ }\r
+ }\r
+\r
+ if (noInvalidate != null)\r
+ throw noInvalidate;\r
+ }\r
+ }\r
+\r
+ /**\r
+ Erases all of the dependencies the dependent has, be they\r
+ valid or invalid, of any dependency type. This action is\r
+ usually performed as the first step in revalidating a\r
+ dependent; it first erases all the old dependencies, then\r
+ revalidates itself generating a list of new dependencies,\r
+ and then marks itself valid if all its new dependencies are\r
+ valid.\r
+ <p>\r
+ There might be a future want to clear all dependencies for\r
+ a particular provider, e.g. when destroying the provider.\r
+ However, at present, they are assumed to stick around and\r
+ it is the responsibility of the dependent to erase them when\r
+ revalidating against the new version of the provider.\r
+ <p>\r
+ clearDependencies will delete dependencies if they are\r
+ stored; the delete is finalized at the next commit.\r
+\r
+ @param d the dependent\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ */\r
+ public void clearDependencies(LanguageConnectionContext lcc, Dependent d) throws StandardException {\r
+ clearDependencies(lcc, d, null);\r
+ }\r
+\r
+ /**\r
+ * @inheritDoc\r
+ */\r
+ public void clearDependencies(LanguageConnectionContext lcc, \r
+ Dependent d, TransactionController tc) throws StandardException {\r
+ List deps = (List) dependents.get(d.getObjectID());\r
+\r
+ synchronized(this)\r
+ {\r
+ /* Remove all the stored dependencies */\r
+ if (d.isPersistent())\r
+ {\r
+ boolean wait = (tc == null);\r
+ \r
+ dd.dropDependentsStoredDependencies(d.getObjectID(),\r
+ ((wait)?lcc.getTransactionExecute():tc),\r
+ wait);\r
+ }\r
+\r
+ /* Now remove the in-memory dependencies */\r
+\r
+ if (deps == null) return; // already removed\r
+\r
+ // go through the list notifying providers to remove\r
+ // the dependency from their lists\r
+ for (ListIterator depsIterator = deps.listIterator();\r
+ depsIterator.hasNext(); ) {\r
+\r
+ Dependency dy = (Dependency)depsIterator.next();\r
+ clearProviderDependency(dy.getProviderKey(), dy);\r
+ }\r
+\r
+ dependents.remove(d.getObjectID());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Clear the specified in memory dependency.\r
+ * This is useful for clean-up when an exception occurs.\r
+ * (We clear all in-memory dependencies added in the current\r
+ * StatementContext.)\r
+ */\r
+ public void clearInMemoryDependency(Dependency dy)\r
+ {\r
+ synchronized(this)\r
+ {\r
+ List deps =\r
+ (List) dependents.get(dy.getDependent().getObjectID());\r
+\r
+ // NOTE - this is a NEGATIVE Sanity mode check, in sane mode we continue\r
+ // to ensure the dependency manager is consistent.\r
+ if (!SanityManager.DEBUG) {\r
+ // dependency has already been removed\r
+ if (deps == null)\r
+ return;\r
+ }\r
+\r
+ List provs =\r
+ (List) providers.get(dy.getProvider().getObjectID());\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ // if both are null then everything is OK\r
+ if ((deps != null) || (provs != null)) {\r
+\r
+ // ensure that the Dependency dy is either\r
+ // in both lists or in neither. Even if dy\r
+ // is out of the list we can have non-null\r
+ // deps and provs here because other dependencies\r
+ // with the the same providers or dependents can exist\r
+\r
+ //\r
+ int depCount = 0;\r
+ if (deps != null) {\r
+ for (int ci = 0; ci < deps.size(); ci++) {\r
+ if (dy.equals(deps.get(ci)))\r
+ depCount++;\r
+ }\r
+ }\r
+\r
+ int provCount = 0;\r
+ if (provs != null) {\r
+ for (int ci = 0; ci < provs.size(); ci++) {\r
+ if (dy.equals(provs.get(ci)))\r
+ provCount++;\r
+ }\r
+ }\r
+\r
+ if (depCount != provCount) {\r
+ SanityManager.THROWASSERT("Dependency count mismatch count in deps: " + depCount +\r
+ ", count in provs " + provCount +\r
+ ", dy.getDependent().getObjectID() = " + dy.getDependent().getObjectID() +\r
+ ", dy.getProvider().getObjectID() = " + dy.getProvider().getObjectID());\r
+ }\r
+ }\r
+\r
+ // dependency has already been removed,\r
+ // matches code that is protected by !DEBUG above\r
+ if (deps == null)\r
+ return;\r
+ }\r
+\r
+ // dependency has already been removed\r
+ if (provs == null)\r
+ return;\r
+\r
+\r
+ deps.remove(dy);\r
+ if (deps.size() == 0)\r
+ dependents.remove(dy.getDependent().getObjectID());\r
+ provs.remove(dy);\r
+ if (provs.size() == 0)\r
+ providers.remove(dy.getProvider().getObjectID());\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * @see DependencyManager#getAllProviders\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+// public SList getAllProviders(Dependent dependent)\r
+// throws StandardException\r
+// {\r
+// synchronized(this)\r
+// {\r
+// SList list = getProviders(dependent);\r
+// return list;\r
+// }\r
+// }\r
+\r
+ /**\r
+ * @see DependencyManager#getAllProviderInfos\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+/* public ProviderInfo[] getAllProviderInfos(Dependent dependent)\r
+ throws StandardException\r
+ {\r
+ synchronized(this)\r
+ {\r
+ ProviderInfo[] retval;\r
+ SList list = getProviders(dependent);\r
+ retval = new ProviderInfo[list.size()];\r
+ if (list == null)\r
+ {\r
+ return retval;\r
+ }\r
+ \r
+ int piCtr = 0;\r
+\r
+ Enumeration enum = list.elements();\r
+ while (enum != null && enum.hasMoreElements())\r
+ {\r
+ Dependency dep = (Dependency) enum.nextElement();\r
+\r
+ retval[piCtr++] = new BasicProviderInfo(\r
+ dep.getProvider().getObjectID(),\r
+ dep.getProvider().getDependableFinder(),\r
+ dep.getProvider().getObjectName()\r
+ );\r
+ }\r
+\r
+ return retval;\r
+ }\r
+ }\r
+*/\r
+\r
+ /**\r
+ * @see DependencyManager#getPersistentProviderInfos\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public synchronized ProviderInfo[] getPersistentProviderInfos(Dependent dependent)\r
+ throws StandardException\r
+ {\r
+ List list = getProviders(dependent);\r
+ if (list == null)\r
+ {\r
+ return EMPTY_PROVIDER_INFO;\r
+ }\r
+\r
+ java.util.ArrayList pih = new java.util.ArrayList();\r
+\r
+ for (ListIterator depsIterator = list.listIterator();\r
+ depsIterator.hasNext(); ) \r
+ {\r
+ Dependency dep = (Dependency) depsIterator.next();\r
+\r
+ if (dep.getProvider().isPersistent())\r
+ {\r
+ pih.add(new BasicProviderInfo(\r
+ dep.getProvider().getObjectID(),\r
+ dep.getProvider().getDependableFinder(),\r
+ dep.getProvider().getObjectName()\r
+ ));\r
+ }\r
+ }\r
+\r
+\r
+ return (ProviderInfo[]) pih.toArray(EMPTY_PROVIDER_INFO);\r
+ }\r
+\r
+ private static final ProviderInfo[] EMPTY_PROVIDER_INFO = new ProviderInfo[0];\r
+\r
+ /**\r
+ * @see DependencyManager#getPersistentProviderInfos\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ProviderInfo[] getPersistentProviderInfos(ProviderList pl)\r
+ throws StandardException\r
+ {\r
+ Enumeration e = pl.elements();\r
+ int numProviders = 0;\r
+ ProviderInfo[] retval;\r
+\r
+ /*\r
+ ** We make 2 passes - the first to count the number of persistent\r
+ ** providers and the second to populate the array of ProviderInfos.\r
+ */\r
+ while (e != null && e.hasMoreElements())\r
+ {\r
+ Provider prov = (Provider) e.nextElement();\r
+\r
+ if (prov.isPersistent())\r
+ {\r
+ numProviders++;\r
+ }\r
+ }\r
+\r
+ e = pl.elements();\r
+ retval = new ProviderInfo[numProviders];\r
+ int piCtr = 0;\r
+ while (e != null && e.hasMoreElements())\r
+ {\r
+ Provider prov = (Provider) e.nextElement();\r
+\r
+ if (prov.isPersistent())\r
+ {\r
+ retval[piCtr++] = new BasicProviderInfo(\r
+ prov.getObjectID(),\r
+ prov.getDependableFinder(),\r
+ prov.getObjectName()\r
+ );\r
+ }\r
+ }\r
+\r
+ return retval;\r
+ }\r
+\r
+ /**\r
+ * @see DependencyManager#clearColumnInfoInProviders\r
+ *\r
+ * @param pl provider list\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void clearColumnInfoInProviders(ProviderList pl)\r
+ throws StandardException\r
+ {\r
+ Enumeration e = pl.elements();\r
+ while (e.hasMoreElements())\r
+ {\r
+ Provider pro = (Provider) e.nextElement();\r
+ if (pro instanceof TableDescriptor)\r
+ ((TableDescriptor) pro).setReferencedColumnMap(null);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Copy dependencies from one dependent to another.\r
+ *\r
+ * @param copy_From the dependent to copy from \r
+ * @param copyTo the dependent to copy to\r
+ * @param persistentOnly only copy persistent dependencies\r
+ * @param cm Current ContextManager\r
+ *\r
+ * @exception StandardException Thrown on error.\r
+ */\r
+ public void copyDependencies(Dependent copy_From, \r
+ Dependent copyTo,\r
+ boolean persistentOnly,\r
+ ContextManager cm) throws StandardException\r
+ {\r
+ copyDependencies(copy_From, copyTo, persistentOnly, cm, null);\r
+ }\r
+ \r
+ /**\r
+ * @inheritDoc \r
+ */\r
+ public synchronized void copyDependencies(\r
+ Dependent copy_From, \r
+ Dependent copyTo,\r
+ boolean persistentOnly,\r
+ ContextManager cm,\r
+ TransactionController tc)\r
+ throws StandardException\r
+ {\r
+\r
+ List list = getProviders(copy_From);\r
+ if (list == null)\r
+ return;\r
+\r
+ for (ListIterator depsIterator = list.listIterator(); depsIterator.hasNext(); ) \r
+ {\r
+ Provider provider = ((Dependency) depsIterator.next()).getProvider();\r
+ \r
+ if (!persistentOnly || provider.isPersistent())\r
+ {\r
+ this.addDependency(copyTo, provider, cm, tc);\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
+ /**\r
+ * Returns a string representation of the SQL action, hence no\r
+ * need to internationalize, which is causing the invokation\r
+ * of the Dependency Manager.\r
+ *\r
+ * @param action The action\r
+ *\r
+ * @return String The String representation\r
+ */\r
+ public String getActionString(int action)\r
+ {\r
+ switch (action)\r
+ {\r
+ case ALTER_TABLE:\r
+ return "ALTER TABLE";\r
+\r
+ case RENAME: //for rename table and column\r
+ return "RENAME";\r
+\r
+ case RENAME_INDEX:\r
+ return "RENAME INDEX";\r
+\r
+ case COMPILE_FAILED:\r
+ return "COMPILE FAILED";\r
+\r
+ case DROP_TABLE:\r
+ return "DROP TABLE";\r
+\r
+ case DROP_INDEX:\r
+ return "DROP INDEX";\r
+\r
+ case DROP_VIEW:\r
+ return "DROP VIEW";\r
+\r
+ case CREATE_INDEX:\r
+ return "CREATE INDEX";\r
+\r
+ case ROLLBACK:\r
+ return "ROLLBACK";\r
+\r
+ case CHANGED_CURSOR:\r
+ return "CHANGED CURSOR";\r
+\r
+ case CREATE_CONSTRAINT:\r
+ return "CREATE CONSTRAINT";\r
+\r
+ case DROP_CONSTRAINT:\r
+ return "DROP CONSTRAINT";\r
+\r
+ case DROP_METHOD_ALIAS:\r
+ return "DROP ROUTINE";\r
+\r
+ case PREPARED_STATEMENT_RELEASE:\r
+ return "PREPARED STATEMENT RELEASE";\r
+\r
+ case DROP_SPS:\r
+ return "DROP STORED PREPARED STATEMENT";\r
+\r
+ case USER_RECOMPILE_REQUEST:\r
+ return "USER REQUESTED INVALIDATION";\r
+\r
+ case BULK_INSERT:\r
+ return "BULK INSERT";\r
+\r
+ case CREATE_VIEW:\r
+ return "CREATE_VIEW";\r
+ \r
+ case DROP_JAR:\r
+ return "DROP_JAR";\r
+\r
+ case REPLACE_JAR:\r
+ return "REPLACE_JAR";\r
+\r
+ case SET_CONSTRAINTS_ENABLE:\r
+ return "SET_CONSTRAINTS_ENABLE";\r
+\r
+ case SET_CONSTRAINTS_DISABLE:\r
+ return "SET_CONSTRAINTS_DISABLE";\r
+\r
+ case INTERNAL_RECOMPILE_REQUEST:\r
+ return "INTERNAL RECOMPILE REQUEST";\r
+\r
+ case CREATE_TRIGGER:\r
+ return "CREATE TRIGGER";\r
+\r
+ case DROP_TRIGGER:\r
+ return "DROP TRIGGER";\r
+\r
+ case SET_TRIGGERS_ENABLE:\r
+ return "SET TRIGGERS ENABLED";\r
+\r
+ case SET_TRIGGERS_DISABLE:\r
+ return "SET TRIGGERS DISABLED";\r
+\r
+ case MODIFY_COLUMN_DEFAULT:\r
+ return "MODIFY COLUMN DEFAULT";\r
+\r
+ case COMPRESS_TABLE:\r
+ return "COMPRESS TABLE";\r
+\r
+ case DROP_COLUMN:\r
+ return "DROP COLUMN";\r
+\r
+ case DROP_COLUMN_RESTRICT:\r
+ return "DROP COLUMN RESTRICT";\r
+\r
+ case DROP_STATISTICS:\r
+ return "DROP STATISTICS";\r
+\r
+ case UPDATE_STATISTICS:\r
+ return "UPDATE STATISTICS";\r
+\r
+ case TRUNCATE_TABLE:\r
+ return "TRUNCATE TABLE";\r
+\r
+ case DROP_SYNONYM:\r
+ return "DROP SYNONYM";\r
+\r
+ case REVOKE_PRIVILEGE:\r
+ return "REVOKE PRIVILEGE";\r
+\r
+ case REVOKE_PRIVILEGE_RESTRICT:\r
+ return "REVOKE PRIVILEGE RESTRICT";\r
+ \r
+ default:\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("getActionString() passed an invalid value (" + action + ")");\r
+ }\r
+ // NOTE: This is not internationalized because we should never\r
+ // reach here.\r
+ return "UNKNOWN";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Count the number of active dependencies, both stored and in memory,\r
+ * in the system.\r
+ *\r
+ * @return int The number of active dependencies in the system.\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ public int countDependencies()\r
+ throws StandardException\r
+ {\r
+ synchronized(this)\r
+ {\r
+ int numDependencies = 0;\r
+ Enumeration deps = dependents.elements();\r
+ Enumeration provs = providers.elements();\r
+ List storedDeps = dd.getAllDependencyDescriptorsList();\r
+\r
+ /* Count the in memory dependencies */\r
+ while (deps.hasMoreElements())\r
+ {\r
+ numDependencies += ((List) deps.nextElement()).size();\r
+ }\r
+\r
+ while (provs.hasMoreElements())\r
+ {\r
+ numDependencies += ((List) provs.nextElement()).size();\r
+ }\r
+\r
+ /* Add in the stored dependencies */\r
+ numDependencies += storedDeps.size();\r
+\r
+ return numDependencies;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Dump out debugging info on all of the dependencies currently\r
+ * within the system.\r
+ *\r
+ * @return String Debugging info on the dependencies.\r
+ * (null if SanityManger.DEBUG is false)\r
+\r
+ * @exception StandardException thrown if something goes wrong\r
+ * @exception java.sql.SQLException thrown if something goes wrong\r
+ */\r
+ public String dumpDependencies() throws StandardException, java.sql.SQLException\r
+ {\r
+ synchronized(this)\r
+ {\r
+ boolean foundInMemory = false;\r
+ boolean foundStored = false;\r
+ StringBuffer debugBuf = new StringBuffer();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ Enumeration deps = dependents.keys();\r
+ UUID[] depKeys = new UUID[dependents.size()];\r
+\r
+ /* Record the in memory dependencies */\r
+ for (int i = 0; deps.hasMoreElements(); i++)\r
+ {\r
+ /*\r
+ ** Get all the keys and sort them, so that they will always\r
+ ** be printed in the same order (we have tests that canonize\r
+ ** the order of printing the dependencies, and since the key\r
+ ** is a UUID, the order they are returned from\r
+ ** hasMoreElements() changes from run to run).\r
+ */\r
+ depKeys[i] = (UUID) deps.nextElement();\r
+ }\r
+\r
+ /* Do a bubble sort - there aren't likely to be many elements */\r
+ bubbleSort(depKeys);\r
+\r
+ /* Iterate through the sorted keys */\r
+ for (int i = 0; i < depKeys.length; i++)\r
+ {\r
+ List depsSList = (List) dependents.get(depKeys[i]);\r
+\r
+ for (ListIterator depsIterator = depsSList.listIterator();\r
+ depsIterator.hasNext(); ) \r
+ {\r
+ Dependency dy = (Dependency)depsIterator.next();\r
+\r
+ if (! foundInMemory)\r
+ {\r
+ debugBuf.append("In Memory Dependencies:\n");\r
+ foundInMemory = true;\r
+ }\r
+\r
+ debugBuf.append(dy.getDependent().toString() +\r
+ ", type " + \r
+ dy.getDependent().getClassType() +\r
+ ", " +\r
+ " is dependent on " +\r
+ dy.getProvider().getObjectName() + \r
+ ", type " +\r
+ dy.getProvider().getClassType() +\r
+ "\n");\r
+ }\r
+ }\r
+\r
+ /* Record the in memory dependencies */\r
+ Enumeration provs = providers.keys();\r
+ UUID[] provKeys = new UUID[providers.size()];\r
+ for (int i = 0; provs.hasMoreElements(); i++)\r
+ {\r
+ /*\r
+ ** Get all the keys and sort them, so that they will always\r
+ ** be printed in the same order (we have tests that canonize\r
+ ** the order of printing the dependencies, and since the key\r
+ ** is a UUID, the order they are returned from\r
+ ** hasMoreElements() changes from run to run).\r
+ */\r
+ provKeys[i] = (UUID) provs.nextElement();\r
+ }\r
+\r
+ /* Do a bubble sort - there aren't likely to be many elements */\r
+ bubbleSort(provKeys);\r
+\r
+ /* Iterate through the sorted keys */\r
+ for (int i = 0; i < provKeys.length; i++)\r
+ {\r
+ List depsSList = (List) providers.get(provKeys[i]);\r
+\r
+ for (ListIterator depsIterator = depsSList.listIterator();\r
+ depsIterator.hasNext(); ) \r
+ {\r
+\r
+ Dependency dy = (Dependency)depsIterator.next();\r
+\r
+ if (! foundInMemory)\r
+ {\r
+ debugBuf.append("In Memory Dependencies:\n");\r
+ foundInMemory = true;\r
+ }\r
+\r
+ debugBuf.append( \r
+ dy.getProvider().toString() + \r
+ ", type " +\r
+ dy.getProvider().getClassType() +\r
+ ", provides for " +\r
+ dy.getDependent().getObjectName() +\r
+ ", type " +\r
+ dy.getDependent().getClassType() +\r
+ "\n");\r
+ }\r
+ }\r
+ /* Record the stored dependencies in sorted order to avoid\r
+ ordering problems in canons. Also the dependencyDescriptor.getUUID()\r
+ in this list is not unique, hence the sort on the output string values instead\r
+ */\r
+ List storedDeps = dd.getAllDependencyDescriptorsList();\r
+\r
+ String[] dependStr = new String[storedDeps.size()];\r
+\r
+ int i = 0;\r
+ for (ListIterator depsIterator = storedDeps.listIterator();\r
+ depsIterator.hasNext(); )\r
+ {\r
+ DependencyDescriptor dependDescr =\r
+ (DependencyDescriptor) depsIterator.next();\r
+\r
+ if (! foundStored)\r
+ {\r
+ debugBuf.append("Stored Dependencies:\n");\r
+ foundStored = true;\r
+ }\r
+ \r
+ DependableFinder providerFinder = dependDescr.getProviderFinder();\r
+ DependableFinder dependentFinder = dependDescr.getDependentFinder();\r
+ \r
+\r
+ dependStr[i++] =\r
+ providerFinder.getDependable(dd, dependDescr.getProviderID()).getObjectName() +\r
+ ", type " + providerFinder.getSQLObjectType() +\r
+ ", provides for " +\r
+ dependentFinder.getDependable(dd, dependDescr.getUUID()).getObjectName() +\r
+ ", type " + dependentFinder.getSQLObjectType() +\r
+ "\n";\r
+ }\r
+\r
+ // sort stored dependencies; dependStr\r
+ for (i = 0; i < dependStr.length; i++)\r
+ {\r
+ for (int j = i + 1; j < dependStr.length; j++)\r
+ {\r
+ if (dependStr[i].compareTo(dependStr[j]) > 0)\r
+ {\r
+ String save = dependStr[i];\r
+ dependStr[i] = dependStr[j];\r
+ dependStr[j] = save;\r
+ }\r
+ }\r
+ }\r
+\r
+ for(i=0; i < dependStr.length; i++)\r
+ debugBuf.append(dependStr[i]);\r
+\r
+\r
+ }\r
+\r
+ return debugBuf.toString();\r
+ }\r
+ }\r
+\r
+ //\r
+ // class interface\r
+ //\r
+ public BasicDependencyManager(DataDictionary dd) {\r
+ this.dd = dd;\r
+ }\r
+\r
+ //\r
+ // class implementation\r
+ //\r
+\r
+ /**\r
+ * Add a new dependency to the specified table if it does not\r
+ * already exist in that table.\r
+ *\r
+ * @return boolean Whether or not the dependency get added.\r
+ */\r
+ private boolean addDependencyToTable(Hashtable table, \r
+ Object key, Dependency dy) {\r
+\r
+ List deps = (List) table.get(key);\r
+ if (deps == null) {\r
+ deps = newSList();\r
+ deps.add(dy);\r
+ table.put(key, deps);\r
+ }\r
+ else {\r
+ /* Make sure that we're not adding a duplicate dependency */\r
+ UUID provKey = dy.getProvider().getObjectID();\r
+ UUID depKey = dy.getDependent().getObjectID();\r
+\r
+ for (ListIterator depsIT = deps.listIterator(); depsIT.hasNext(); )\r
+ {\r
+ Dependency curDY = (Dependency)depsIT.next();\r
+ if (curDY.getProvider().getObjectID().equals(provKey) &&\r
+ curDY.getDependent().getObjectID().equals(depKey))\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ deps.add(dy);\r
+ }\r
+\r
+ if (SanityManager.DEBUG) {\r
+\r
+ if (SanityManager.DEBUG_ON("memoryLeakTrace")) {\r
+\r
+ if (table.size() > 100)\r
+ System.out.println("memoryLeakTrace:BasicDependencyManager:table " + table.size());\r
+ if (deps.size() > 50)\r
+ System.out.println("memoryLeakTrace:BasicDependencyManager:deps " + deps.size());\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * removes a dependency for a given provider. assumes\r
+ * that the dependent removal is being dealt with elsewhere.\r
+ * Won't assume that the dependent only appears once in the list.\r
+ */\r
+ protected void clearProviderDependency(UUID p, Dependency d) {\r
+ List deps = (List) providers.get(p);\r
+\r
+ if (deps == null)\r
+ return;\r
+\r
+ deps.remove(d);\r
+\r
+ if (deps.size() == 0)\r
+ providers.remove(p);\r
+ }\r
+\r
+ /**\r
+ * Replace the DependencyDescriptors in an List with Dependencys.\r
+ *\r
+ * @param storedList The List of DependencyDescriptors representing\r
+ * stored dependencies.\r
+ * @param providerForList The provider if this list is being created\r
+ * for a list of dependents. Null otherwise.\r
+ * \r
+ * @return List The converted List\r
+ *\r
+ * @exception StandardException thrown if something goes wrong\r
+ */\r
+ private List getDependencyDescriptorList(List storedList,\r
+ Provider providerForList)\r
+ throws StandardException\r
+ {\r
+ if (storedList.size() != 0)\r
+ {\r
+ /* For each DependencyDescriptor, we need to instantiate\r
+ * object descriptors of the appropriate type for both\r
+ * the dependent and provider, create a Dependency with\r
+ * that Dependent and Provider and substitute the Dependency\r
+ * back into the same place in the List\r
+ * so that the call gets an enumerations of Dependencys.\r
+ */\r
+ for (ListIterator depsIterator = storedList.listIterator();\r
+ depsIterator.hasNext(); ) \r
+ {\r
+ Dependent tempD;\r
+ Provider tempP;\r
+ DependableFinder finder = null;\r
+\r
+ DependencyDescriptor depDesc = (DependencyDescriptor) depsIterator.next();\r
+\r
+ finder = depDesc.getDependentFinder();\r
+ tempD = (Dependent) finder.getDependable(dd, depDesc.getUUID() );\r
+\r
+ if (providerForList != null)\r
+ {\r
+ // Use the provider being passed in.\r
+ tempP = providerForList;\r
+ \r
+ // Sanity check the object identifiers match.\r
+ if (SanityManager.DEBUG) {\r
+ if (!tempP.getObjectID().equals(depDesc.getProviderID()))\r
+ {\r
+ SanityManager.THROWASSERT("mismatch providers");\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ finder = depDesc.getProviderFinder();\r
+ tempP = (Provider) finder.getDependable(dd, depDesc.getProviderID() );\r
+ \r
+ }\r
+\r
+ depsIterator.set(new BasicDependency(tempD, tempP));\r
+ }\r
+ }\r
+\r
+ return storedList;\r
+ }\r
+\r
+ /**\r
+ * Returns the LanguageConnectionContext to use.\r
+ *\r
+ * @param cm Current ContextManager\r
+ *\r
+ * @return LanguageConnectionContext The LanguageConnectionContext to use.\r
+ */\r
+ private LanguageConnectionContext getLanguageConnectionContext(ContextManager cm)\r
+ {\r
+ // find the language context.\r
+ return (LanguageConnectionContext) cm.getContext(LanguageConnectionContext.CONTEXT_ID);\r
+ }\r
+\r
+ /**\r
+ * Do a bubble sort on the given array of UUIDs. This sorts by the\r
+ * String values of the UUIDs. It's slow, but it doesn't matter\r
+ * because this is only for testing and debugging. Sorting by\r
+ * UUID.toString() always gives the same order because, within a\r
+ * single boot of the system, UUIDs are distinguished only by a\r
+ * sequence number.\r
+ *\r
+ * @param uuids The array of UUIDs to sort.\r
+ */\r
+ private void bubbleSort(UUID[] uuids)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ for (int i = 0; i < uuids.length; i++)\r
+ {\r
+ for (int j = i + 1; j < uuids.length; j++)\r
+ {\r
+ if (uuids[i].toString().compareTo(uuids[j].toString()) > 0)\r
+ {\r
+ UUID save = uuids[i];\r
+ uuids[i] = uuids[j];\r
+ uuids[j] = save;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ Returns an enumeration of all dependencies that this\r
+ dependent has with any provider (even\r
+ invalid ones). Includes all dependency types.\r
+\r
+ @param d the dependent\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ private List getProviders (Dependent d) throws StandardException {\r
+\r
+ List deps = (List) dependents.get(d.getObjectID());\r
+\r
+ /* If the Dependent is not persistent, then we only have to\r
+ * worry about in-memory dependencies. Otherwise, we have to\r
+ * integrate the 2.\r
+ */\r
+ if (! d.isPersistent())\r
+ {\r
+ return (deps == null? null : deps);\r
+ }\r
+ else\r
+ {\r
+ if (deps == null)\r
+ {\r
+ deps = newSList();\r
+ }\r
+ else\r
+ {\r
+ deps = newSList(deps);\r
+ }\r
+\r
+ /* Now we need to add any persistent dependencies to the\r
+ * list before returning\r
+ */\r
+ List storedList = getDependencyDescriptorList(\r
+ dd.getDependentsDescriptorList(\r
+ d.getObjectID().toString()\r
+ ),\r
+ (Provider) null\r
+ );\r
+\r
+ if (storedList.size() > 0)\r
+ {\r
+ deps.addAll(0, storedList);\r
+ }\r
+\r
+ return deps;\r
+ }\r
+ }\r
+\r
+ /**\r
+ Returns an enumeration of all dependencies that this\r
+ provider is supporting for any dependent at all (even\r
+ invalid ones). Includes all dependency types.\r
+\r
+ @param p the provider\r
+\r
+ @exception StandardException thrown if something goes wrong\r
+ */\r
+ private List getDependents (Provider p) \r
+ throws StandardException {\r
+\r
+ List deps = (List) providers.get(p.getObjectID());\r
+\r
+ /* If the Provider is not persistent, then we only have to\r
+ * worry about in-memory dependencies. Otherwise, we have to\r
+ * integrate the 2.\r
+ */\r
+ if (! p.isPersistent())\r
+ {\r
+ return deps;\r
+ }\r
+ else\r
+ {\r
+ if (deps == null)\r
+ {\r
+ deps = newSList();\r
+ }\r
+ else\r
+ {\r
+ deps = newSList(deps);\r
+ }\r
+\r
+ /* Now we need to add any persistent dependencies to the\r
+ * list before returning\r
+ */\r
+ List storedList = getDependencyDescriptorList(\r
+ dd.getProvidersDescriptorList(\r
+ p.getObjectID().toString()\r
+ ),\r
+ p\r
+ );\r
+ if (storedList.size() > 0)\r
+ {\r
+ deps.addAll(0, storedList);\r
+ }\r
+\r
+ return deps;\r
+ }\r
+ }\r
+\r
+ private static List newSList() {\r
+ return java.util.Collections.synchronizedList(new java.util.LinkedList());\r
+ }\r
+ private static List newSList(List list) {\r
+ return java.util.Collections.synchronizedList(new java.util.LinkedList(list));\r
+ }\r
+}\r