--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.ExecSPSNode\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.compile;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedClass;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;\r
+\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;\r
+\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.sql.PreparedStatement;\r
+import org.apache.derby.iapi.sql.ResultDescription;\r
+\r
+import org.apache.derby.impl.sql.CursorInfo;\r
+\r
+import org.apache.derby.iapi.util.ByteArray;\r
+\r
+import java.util.Enumeration;\r
+\r
+/**\r
+ * A ExecSPSNode is the root of a QueryTree \r
+ * that represents an EXECUTE STATEMENT\r
+ * statement. It is a tad abnormal. Duringa\r
+ * bind, it locates and retrieves the SPSDescriptor\r
+ * for the particular statement. At generate time,\r
+ * it generates the prepared statement for the \r
+ * stored prepared statement and returns it (i.e.\r
+ * it effectively replaces itself with the appropriate\r
+ * prepared statement).\r
+ *\r
+ */\r
+\r
+public class ExecSPSNode extends StatementNode \r
+{\r
+ private TableName name;\r
+ private SPSDescriptor spsd;\r
+ private ExecPreparedStatement ps;\r
+\r
+ /**\r
+ * Initializer for a ExecSPSNode\r
+ *\r
+ * @param newObjectName The name of the table to be created\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void init(\r
+ Object newObjectName)\r
+ {\r
+ this.name = (TableName) newObjectName;\r
+ }\r
+\r
+ /**\r
+ * Bind this ExecSPSNode. This means doing any static error\r
+ * checking that can be done before actually creating the table.\r
+ * For example, verifying that the ResultColumnList does not\r
+ * contain any duplicate column names.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindStatement() throws StandardException\r
+ {\r
+ /*\r
+ ** Grab the compiler context each time we bind just\r
+ ** to make sure we have the write one (even though\r
+ ** we are caching it).\r
+ */\r
+ DataDictionary dd = getDataDictionary();\r
+\r
+ String schemaName = name.getSchemaName();\r
+ SchemaDescriptor sd = getSchemaDescriptor(name.getSchemaName());\r
+ if (schemaName == null)\r
+ name.setSchemaName(sd.getSchemaName());\r
+\r
+ if (sd.getUUID() != null)\r
+ spsd = dd.getSPSDescriptor(name.getTableName(), sd);\r
+\r
+ if (spsd == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_OBJECT_NOT_FOUND, "STATEMENT", name);\r
+ }\r
+\r
+ if (spsd.getType() == spsd.SPS_TYPE_TRIGGER)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_TRIGGER_SPS_CANNOT_BE_EXECED, name);\r
+ }\r
+ \r
+\r
+ /*\r
+ ** This execute statement is dependent on the\r
+ ** stored prepared statement. If for any reason\r
+ ** the underlying statement is invalidated by\r
+ ** the time we get to execution, the 'execute statement'\r
+ ** will get invalidated when the underlying statement\r
+ ** is invalidated.\r
+ */\r
+ getCompilerContext().createDependency(spsd);\r
+\r
+ }\r
+\r
+ /**\r
+ * SPSes are atomic if its underlying statement is\r
+ * atomic.\r
+ *\r
+ * @return true if the statement is atomic\r
+ */ \r
+ public boolean isAtomic()\r
+ {\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(ps != null, \r
+ "statement expected to be bound before calling isAtomic()");\r
+ }\r
+\r
+ return ps.isAtomic();\r
+ }\r
+\r
+ /**\r
+ * Do code generation for this statement. Overrides\r
+ * the normal generation path in StatementNode.\r
+ *\r
+ * @param ignored - ignored (he he)\r
+ *\r
+ * @return A GeneratedClass for this statement\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public GeneratedClass generate(ByteArray ignored) throws StandardException\r
+ {\r
+ //Bug 4821 - commiting the nested transaction will release any bind time locks\r
+ //This way we won't get lock time out errors while trying to update sysstatement\r
+ //table during stale sps recompilation later in the getPreparedstatement() call.\r
+ if (spsd.isValid() == false) {\r
+ getLanguageConnectionContext().commitNestedTransaction();\r
+ getLanguageConnectionContext().beginNestedTransaction(true);\r
+ } \r
+\r
+ /*\r
+ ** The following does a prepare on the underlying\r
+ ** statement if necessary. The returned statement\r
+ ** is valid and its class is loaded up.\r
+ */\r
+ ps = spsd.getPreparedStatement();\r
+\r
+\r
+ /*\r
+ ** Set the saved constants from the prepared statement.\r
+ ** Put them in the compilation context -- this is where\r
+ ** they are expected.\r
+ */\r
+ getCompilerContext().setSavedObjects(ps.getSavedObjects());\r
+ getCompilerContext().setCursorInfo(ps.getCursorInfo());\r
+ GeneratedClass gc = ps.getActivationClass();\r
+ \r
+ return gc;\r
+ }\r
+ \r
+ /**\r
+ * Make the result description. Really, we are just\r
+ * copying it from the stored prepared statement.\r
+ *\r
+ * @return the description\r
+ */\r
+ public ResultDescription makeResultDescription()\r
+ {\r
+ return ps.getResultDescription();\r
+ }\r
+\r
+ /**\r
+ * Get information about this cursor. For sps,\r
+ * this is info saved off of the original query\r
+ * tree (the one for the underlying query).\r
+ *\r
+ * @return the cursor info\r
+ */\r
+ public Object getCursorInfo()\r
+ {\r
+ return ps.getCursorInfo();\r
+ }\r
+\r
+ /**\r
+ * Return a description of the ? parameters for the statement\r
+ * represented by this query tree. Just return the params\r
+ * stored with the prepared statement.\r
+ *\r
+ * @return An array of DataTypeDescriptors describing the\r
+ * ? parameters for this statement. It returns null\r
+ * if there are no parameters.\r
+ *\r
+ * @exception StandardException on error\r
+ */\r
+ public DataTypeDescriptor[] getParameterTypes() throws StandardException\r
+ {\r
+ return spsd.getParams();\r
+ }\r
+\r
+\r
+ /**\r
+ * Create the Constant information that will drive the guts of Execution.\r
+ * This is assumed to be the first action on this node.\r
+ *\r
+ */\r
+ public ConstantAction makeConstantAction()\r
+ {\r
+ return ps.getConstantAction();\r
+ }\r
+\r
+ /**\r
+ * We need a savepoint if we will do transactional work.\r
+ * We'll ask the underlying statement if it needs\r
+ * a savepoint and pass that back. We have to do this\r
+ * after generation because getting the PS now might\r
+ * cause us to basically do DDL (for a stmt recompilation)\r
+ * which is explicitly banned during binding. So the\r
+ * caller can only call this after generate() has retrieved\r
+ * the target PS. \r
+ *\r
+ * @return boolean always true.\r
+ */\r
+ public boolean needsSavepoint()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(ps != null, \r
+ "statement expected to be bound before calling needsSavepoint()");\r
+ }\r
+\r
+ return ps.needsSavepoint();\r
+ }\r
+\r
+ /** @see StatementNode#executeStatementName */\r
+ public String executeStatementName()\r
+ {\r
+ return name.getTableName();\r
+ }\r
+\r
+ /** @see StatementNode#executeSchemaName */\r
+ public String executeSchemaName()\r
+ {\r
+ return name.getSchemaName();\r
+ }\r
+\r
+ /**\r
+ * Get the name of the SPS that is used\r
+ * to execute this statement. Only relevant\r
+ * for an ExecSPSNode -- otherwise, returns null.\r
+ *\r
+ * @return the name of the underlying sps\r
+ */\r
+ public String getSPSName()\r
+ {\r
+ return spsd.getQualifiedName();\r
+ }\r
+ \r
+ /*\r
+ * Shouldn't be called\r
+ */\r
+ int activationKind()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.THROWASSERT("activationKind not expected "+\r
+ "to be called for a stored prepared statement");\r
+ }\r
+ return StatementNode.NEED_PARAM_ACTIVATION;\r
+ }\r
+ /////////////////////////////////////////////////////////////////////\r
+ //\r
+ // PRIVATE\r
+ //\r
+ /////////////////////////////////////////////////////////////////////\r
+\r
+ \r
+ /////////////////////////////////////////////////////////////////////\r
+ //\r
+ // MISC\r
+ //\r
+ /////////////////////////////////////////////////////////////////////\r
+ public String statementToString()\r
+ {\r
+ return "EXECUTE STATEMENT";\r
+ }\r
+\r
+ // called after bind only\r
+ private final SPSDescriptor getSPSDescriptor()\r
+ {\r
+ return spsd;\r
+ }\r
+}\r