--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.GenericStatement\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;\r
+\r
+import java.sql.Timestamp;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.loader.GeneratedClass;\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.stream.HeaderPrintWriter;\r
+import org.apache.derby.iapi.sql.PreparedStatement;\r
+import org.apache.derby.iapi.sql.Statement;\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.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;\r
+import org.apache.derby.iapi.sql.execute.ExecutionContext;\r
+import org.apache.derby.impl.sql.compile.StatementNode;\r
+import org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext;\r
+\r
+public class GenericStatement\r
+ implements Statement {\r
+\r
+ // these fields define the identity of the statement\r
+ private final SchemaDescriptor compilationSchema;\r
+ private final String statementText;\r
+ private final boolean isForReadOnly;\r
+ private int prepareIsolationLevel;\r
+ private GenericPreparedStatement preparedStmt;\r
+\r
+ /**\r
+ * Constructor for a Statement given the text of the statement in a String\r
+ * @param compilationSchema schema\r
+ * @param statementText The text of the statement\r
+ * @param isForReadOnly if the statement is opened with level CONCUR_READ_ONLY\r
+ */\r
+\r
+ public GenericStatement(SchemaDescriptor compilationSchema, String statementText, boolean isForReadOnly)\r
+ {\r
+ this.compilationSchema = compilationSchema;\r
+ this.statementText = statementText;\r
+ this.isForReadOnly = isForReadOnly;\r
+ }\r
+\r
+ /*\r
+ * Statement interface\r
+ */\r
+\r
+ \r
+ /* RESOLVE: may need error checking, debugging code here */\r
+ public PreparedStatement prepare(LanguageConnectionContext lcc) throws StandardException\r
+ {\r
+ /*\r
+ ** Note: don't reset state since this might be\r
+ ** a recompilation of an already prepared statement.\r
+ */ \r
+ return prepMinion(lcc, true, (Object[]) null, (SchemaDescriptor) null, false); \r
+ }\r
+ public PreparedStatement prepare(LanguageConnectionContext lcc, boolean forMetaData) throws StandardException\r
+ {\r
+ /*\r
+ ** Note: don't reset state since this might be\r
+ ** a recompilation of an already prepared statement.\r
+ */ \r
+ return prepMinion(lcc, true, (Object[]) null, (SchemaDescriptor) null, forMetaData); \r
+ }\r
+\r
+ private PreparedStatement prepMinion(LanguageConnectionContext lcc, boolean cacheMe, Object[] paramDefaults,\r
+ SchemaDescriptor spsSchema, boolean internalSQL)\r
+ throws StandardException\r
+ {\r
+ \r
+ long beginTime = 0;\r
+ long parseTime = 0;\r
+ long bindTime = 0;\r
+ long optimizeTime = 0;\r
+ long generateTime = 0;\r
+ Timestamp beginTimestamp = null;\r
+ Timestamp endTimestamp = null;\r
+ StatementContext statementContext = null;\r
+\r
+ // verify it isn't already prepared...\r
+ // if it is, and is valid, simply return that tree.\r
+ // if it is invalid, we will recompile now.\r
+ if (preparedStmt != null) {\r
+ if (preparedStmt.upToDate())\r
+ return preparedStmt;\r
+ }\r
+\r
+ // Clear the optimizer trace from the last statement\r
+ if (lcc.getOptimizerTrace())\r
+ lcc.setOptimizerTraceOutput(getSource() + "\n");\r
+\r
+ beginTime = getCurrentTimeMillis(lcc);\r
+ /* beginTimestamp only meaningful if beginTime is meaningful.\r
+ * beginTime is meaningful if STATISTICS TIMING is ON.\r
+ */\r
+ if (beginTime != 0)\r
+ {\r
+ beginTimestamp = new Timestamp(beginTime);\r
+ }\r
+\r
+ /** set the prepare Isolaton from the LanguageConnectionContext now as \r
+ * we need to consider it in caching decisions\r
+ */\r
+ prepareIsolationLevel = lcc.getPrepareIsolationLevel();\r
+\r
+ /* a note on statement caching:\r
+ * \r
+ * A GenericPreparedStatement (GPS) is only added it to the cache if the\r
+ * parameter cacheMe is set to TRUE when the GPS is created.\r
+ * \r
+ * Earlier only CacheStatement (CS) looked in the statement cache for a\r
+ * prepared statement when prepare was called. Now the functionality \r
+ * of CS has been folded into GenericStatement (GS). So we search the\r
+ * cache for an existing PreparedStatement only when cacheMe is TRUE.\r
+ * i.e if the user calls prepare with cacheMe set to TRUE:\r
+ * then we \r
+ * a) look for the prepared statement in the cache.\r
+ * b) add the prepared statement to the cache.\r
+ *\r
+ * In cases where the statement cache has been disabled (by setting the\r
+ * relevant Derby property) then the value of cacheMe is irrelevant.\r
+ */ \r
+ boolean foundInCache = false;\r
+ if (preparedStmt == null) \r
+ {\r
+ if (cacheMe)\r
+ preparedStmt = (GenericPreparedStatement)((GenericLanguageConnectionContext)lcc).lookupStatement(this);\r
+\r
+ if (preparedStmt == null) \r
+ {\r
+ preparedStmt = new GenericPreparedStatement(this);\r
+ }\r
+ else\r
+ {\r
+ foundInCache = true;\r
+ }\r
+ }\r
+\r
+ // if anyone else also has this prepared statement,\r
+ // we don't want them trying to compile with it while\r
+ // we are. So, we synchronize on it and re-check\r
+ // its validity first.\r
+ // this is a no-op if and until there is a central\r
+ // cache of prepared statement objects...\r
+ synchronized (preparedStmt) \r
+ {\r
+\r
+ for (;;) {\r
+\r
+ if (foundInCache) {\r
+ if (preparedStmt.referencesSessionSchema()) {\r
+ // cannot use this state since it is private to a connection.\r
+ // switch to a new statement.\r
+ foundInCache = false;\r
+ preparedStmt = new GenericPreparedStatement(this);\r
+ break;\r
+ }\r
+ }\r
+\r
+ // did it get updated while we waited for the lock on it?\r
+ if (preparedStmt.upToDate()) {\r
+ return preparedStmt;\r
+ }\r
+\r
+ if (!preparedStmt.compilingStatement) {\r
+ break;\r
+ }\r
+\r
+ try {\r
+ preparedStmt.wait();\r
+ } catch (InterruptedException ie) {\r
+ throw StandardException.interrupt(ie);\r
+ }\r
+ }\r
+\r
+ preparedStmt.compilingStatement = true;\r
+ preparedStmt.setActivationClass(null);\r
+ }\r
+\r
+ try {\r
+\r
+ HeaderPrintWriter istream = lcc.getLogStatementText() ? Monitor.getStream() : null;\r
+\r
+ /*\r
+ ** For stored prepared statements, we want all\r
+ ** errors, etc in the context of the underlying\r
+ ** EXECUTE STATEMENT statement, so don't push/pop\r
+ ** another statement context unless we don't have\r
+ ** one. We won't have one if it is an internal\r
+ ** SPS (e.g. jdbcmetadata).\r
+ */\r
+ if (!preparedStmt.isStorable() || lcc.getStatementDepth() == 0)\r
+ {\r
+ // since this is for compilation only, set atomic\r
+ // param to true and timeout param to 0\r
+ statementContext = lcc.pushStatementContext(true, isForReadOnly, getSource(),\r
+ null, false, 0L);\r
+ }\r
+\r
+\r
+\r
+ /*\r
+ ** RESOLVE: we may ultimately wish to pass in\r
+ ** whether we are a jdbc metadata query or not to\r
+ ** get the CompilerContext to make the createDependency()\r
+ ** call a noop.\r
+ */\r
+ CompilerContext cc = lcc.pushCompilerContext(compilationSchema);\r
+ \r
+ if (prepareIsolationLevel != \r
+ ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL)\r
+ {\r
+ cc.setScanIsolationLevel(prepareIsolationLevel);\r
+ }\r
+\r
+\r
+ // Look for stored statements that are in a system schema\r
+ // and with a match compilation schema. If so, allow them\r
+ // to compile using internal SQL constructs.\r
+\r
+ if (internalSQL ||\r
+ (spsSchema != null) && (spsSchema.isSystemSchema()) &&\r
+ (spsSchema.equals(compilationSchema))) {\r
+ cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);\r
+ }\r
+\r
+ try \r
+ {\r
+ // Statement logging if lcc.getLogStatementText() is true\r
+ if (istream != null)\r
+ {\r
+ String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();\r
+ istream.printlnWithHeader(LanguageConnectionContext.xidStr + \r
+ xactId + \r
+ "), " +\r
+ LanguageConnectionContext.lccStr +\r
+ lcc.getInstanceNumber() +\r
+ "), " +\r
+ LanguageConnectionContext.dbnameStr +\r
+ lcc.getDbname() +\r
+ "), " +\r
+ LanguageConnectionContext.drdaStr +\r
+ lcc.getDrdaID() +\r
+ "), Begin compiling prepared statement: " + \r
+ getSource() +\r
+ " :End prepared statement");\r
+ }\r
+\r
+ Parser p = cc.getParser();\r
+\r
+ cc.setCurrentDependent(preparedStmt);\r
+\r
+ //Only top level statements go through here, nested statement\r
+ //will invoke this method from other places\r
+ StatementNode qt = p.parseStatement(statementText, paramDefaults);\r
+\r
+ parseTime = getCurrentTimeMillis(lcc);\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ if (SanityManager.DEBUG_ON("DumpParseTree")) \r
+ {\r
+ qt.treePrint();\r
+ }\r
+\r
+ if (SanityManager.DEBUG_ON("StopAfterParsing")) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_STOP_AFTER_PARSING);\r
+ }\r
+ }\r
+\r
+ /*\r
+ ** Tell the data dictionary that we are about to do\r
+ ** a bunch of "get" operations that must be consistent with\r
+ ** each other.\r
+ */\r
+ \r
+ DataDictionary dataDictionary = lcc.getDataDictionary();\r
+\r
+ int ddMode = dataDictionary == null ? 0 : dataDictionary.startReading(lcc);\r
+\r
+ try\r
+ {\r
+ // start a nested transaction -- all locks acquired by bind\r
+ // and optimize will be released when we end the nested\r
+ // transaction.\r
+ lcc.beginNestedTransaction(true);\r
+\r
+ qt.bindStatement();\r
+ bindTime = getCurrentTimeMillis(lcc);\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ if (SanityManager.DEBUG_ON("DumpBindTree")) \r
+ {\r
+ qt.treePrint();\r
+ }\r
+\r
+ if (SanityManager.DEBUG_ON("StopAfterBinding")) {\r
+ throw StandardException.newException(SQLState.LANG_STOP_AFTER_BINDING);\r
+ }\r
+ }\r
+\r
+ //Derby424 - In order to avoid caching select statements referencing\r
+ // any SESSION schema objects (including statements referencing views\r
+ // in SESSION schema), we need to do the SESSION schema object check\r
+ // here. \r
+ //a specific eg for statement referencing a view in SESSION schema \r
+ //CREATE TABLE t28A (c28 int)\r
+ //INSERT INTO t28A VALUES (280),(281)\r
+ //CREATE VIEW SESSION.t28v1 as select * from t28A\r
+ //SELECT * from SESSION.t28v1 should show contents of view and we\r
+ // should not cache this statement because a user can later define\r
+ // a global temporary table with the same name as the view name.\r
+ //Following demonstrates that\r
+ //DECLARE GLOBAL TEMPORARY TABLE SESSION.t28v1(c21 int, c22 int) not\r
+ // logged\r
+ //INSERT INTO SESSION.t28v1 VALUES (280,1),(281,2)\r
+ //SELECT * from SESSION.t28v1 should show contents of global temporary\r
+ //table and not the view. Since this select statement was not cached\r
+ // earlier, it will be compiled again and will go to global temporary\r
+ // table to fetch data. This plan will not be cached either because\r
+ // select statement is using SESSION schema object.\r
+ //\r
+ //Following if statement makes sure that if the statement is\r
+ // referencing SESSION schema objects, then we do not want to cache it.\r
+ // We will remove the entry that was made into the cache for \r
+ //this statement at the beginning of the compile phase.\r
+ //The reason we do this check here rather than later in the compile\r
+ // phase is because for a view, later on, we loose the information that\r
+ // it was referencing SESSION schema because the reference\r
+ //view gets replaced with the actual view definition. Right after\r
+ // binding, we still have the information on the view and that is why\r
+ // we do the check here.\r
+ if (preparedStmt.referencesSessionSchema(qt)) {\r
+ if (foundInCache)\r
+ ((GenericLanguageConnectionContext)lcc).removeStatement(this);\r
+ }\r
+ \r
+ qt.optimizeStatement();\r
+\r
+ optimizeTime = getCurrentTimeMillis(lcc);\r
+\r
+ // Statement logging if lcc.getLogStatementText() is true\r
+ if (istream != null)\r
+ {\r
+ String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();\r
+ istream.printlnWithHeader(LanguageConnectionContext.xidStr + \r
+ xactId + \r
+ "), " +\r
+ LanguageConnectionContext.lccStr +\r
+ lcc.getInstanceNumber() +\r
+ "), " +\r
+ LanguageConnectionContext.dbnameStr +\r
+ lcc.getDbname() +\r
+ "), " +\r
+ LanguageConnectionContext.drdaStr +\r
+ lcc.getDrdaID() +\r
+ "), End compiling prepared statement: " + \r
+ getSource() +\r
+ " :End prepared statement");\r
+ }\r
+ }\r
+\r
+ catch (StandardException se)\r
+ {\r
+ lcc.commitNestedTransaction();\r
+\r
+ // Statement logging if lcc.getLogStatementText() is true\r
+ if (istream != null)\r
+ {\r
+ String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();\r
+ istream.printlnWithHeader(LanguageConnectionContext.xidStr + \r
+ xactId + \r
+ "), " +\r
+ LanguageConnectionContext.lccStr +\r
+ lcc.getInstanceNumber() +\r
+ "), " +\r
+ LanguageConnectionContext.dbnameStr +\r
+ lcc.getDbname() +\r
+ "), " +\r
+ LanguageConnectionContext.drdaStr +\r
+ lcc.getDrdaID() +\r
+ "), Error compiling prepared statement: " + \r
+ getSource() +\r
+ " :End prepared statement");\r
+ }\r
+ throw se;\r
+ }\r
+\r
+ finally\r
+ {\r
+ /* Tell the data dictionary that we are done reading */\r
+ if (dataDictionary != null)\r
+ dataDictionary.doneReading(ddMode, lcc);\r
+ }\r
+\r
+ /* we need to move the commit of nested sub-transaction\r
+ * after we mark PS valid, during compilation, we might need\r
+ * to get some lock to synchronize with another thread's DDL\r
+ * execution, in particular, the compilation of insert/update/\r
+ * delete vs. create index/constraint (see Beetle 3976). We\r
+ * can't release such lock until after we mark the PS valid.\r
+ * Otherwise we would just erase the DDL's invalidation when\r
+ * we mark it valid.\r
+ */\r
+ try // put in try block, commit sub-transaction if bad\r
+ {\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ if (SanityManager.DEBUG_ON("DumpOptimizedTree")) \r
+ {\r
+ qt.treePrint();\r
+ }\r
+\r
+ if (SanityManager.DEBUG_ON("StopAfterOptimizing")) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_STOP_AFTER_OPTIMIZING);\r
+ }\r
+ }\r
+\r
+ GeneratedClass ac = qt.generate(preparedStmt.getByteCodeSaver());\r
+\r
+ generateTime = getCurrentTimeMillis(lcc);\r
+ /* endTimestamp only meaningful if generateTime is meaningful.\r
+ * generateTime is meaningful if STATISTICS TIMING is ON.\r
+ */\r
+ if (generateTime != 0)\r
+ {\r
+ endTimestamp = new Timestamp(generateTime);\r
+ }\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ if (SanityManager.DEBUG_ON("StopAfterGenerating")) \r
+ {\r
+ throw StandardException.newException(SQLState.LANG_STOP_AFTER_GENERATING);\r
+ }\r
+ }\r
+\r
+ /*\r
+ copy over the compile-time created objects\r
+ to the prepared statement. This always happens\r
+ at the end of a compile, so there is no need\r
+ to erase the previous entries on a re-compile --\r
+ this erases as it replaces. Set the activation\r
+ class in case it came from a StorablePreparedStatement\r
+ */\r
+ preparedStmt.setConstantAction( qt.makeConstantAction() );\r
+ preparedStmt.setSavedObjects( cc.getSavedObjects() );\r
+ preparedStmt.setRequiredPermissionsList(cc.getRequiredPermissionsList());\r
+ preparedStmt.setActivationClass(ac);\r
+ preparedStmt.setNeedsSavepoint(qt.needsSavepoint());\r
+ preparedStmt.setCursorInfo((CursorInfo)cc.getCursorInfo());\r
+ preparedStmt.setIsAtomic(qt.isAtomic());\r
+ preparedStmt.setExecuteStatementNameAndSchema(\r
+ qt.executeStatementName(),\r
+ qt.executeSchemaName()\r
+ );\r
+ preparedStmt.setSPSName(qt.getSPSName());\r
+ preparedStmt.completeCompile(qt);\r
+ preparedStmt.setCompileTimeWarnings(cc.getWarnings());\r
+ }\r
+ catch (StandardException e) // hold it, throw it\r
+ {\r
+ lcc.commitNestedTransaction();\r
+ throw e;\r
+ }\r
+\r
+ if (lcc.getRunTimeStatisticsMode())\r
+ {\r
+ preparedStmt.setCompileTimeMillis(\r
+ parseTime - beginTime, //parse time\r
+ bindTime - parseTime, //bind time\r
+ optimizeTime - bindTime, //optimize time\r
+ generateTime - optimizeTime, //generate time\r
+ getElapsedTimeMillis(beginTime),\r
+ beginTimestamp,\r
+ endTimestamp);\r
+ }\r
+\r
+ }\r
+ finally // for block introduced by pushCompilerContext()\r
+ {\r
+ lcc.popCompilerContext( cc );\r
+ }\r
+ }\r
+ catch (StandardException se)\r
+ {\r
+ if (foundInCache)\r
+ ((GenericLanguageConnectionContext)lcc).removeStatement(this);\r
+ throw se;\r
+ }\r
+ finally\r
+ {\r
+ synchronized (preparedStmt) {\r
+ preparedStmt.compilingStatement = false;\r
+ preparedStmt.notifyAll();\r
+ }\r
+ }\r
+\r
+ lcc.commitNestedTransaction();\r
+\r
+ if (statementContext != null)\r
+ lcc.popStatementContext(statementContext, null);\r
+\r
+ return preparedStmt;\r
+ }\r
+\r
+ /**\r
+ * Generates an execution plan given a set of named parameters.\r
+ * Does so for a storable prepared statement.\r
+ *\r
+ * @param paramDefaults Parameter defaults\r
+ *\r
+ * @return A PreparedStatement that allows execution of the execution\r
+ * plan.\r
+ * @exception StandardException Thrown if this is an\r
+ * execution-only version of the module (the prepare() method\r
+ * relies on compilation).\r
+ */\r
+ public PreparedStatement prepareStorable(\r
+ LanguageConnectionContext lcc,\r
+ PreparedStatement ps,\r
+ Object[] paramDefaults,\r
+ SchemaDescriptor spsSchema,\r
+ boolean internalSQL)\r
+ throws StandardException\r
+ {\r
+ if (ps == null)\r
+ ps = new GenericStorablePreparedStatement(this);\r
+ else\r
+ ((GenericPreparedStatement) ps).statement = this;\r
+\r
+ this.preparedStmt = (GenericPreparedStatement) ps;\r
+ return prepMinion(lcc, false, paramDefaults, spsSchema, internalSQL);\r
+ }\r
+\r
+ public String getSource() {\r
+ return statementText;\r
+ }\r
+\r
+ public String getCompilationSchema() {\r
+ return compilationSchema.getDescriptorName();\r
+ }\r
+\r
+ private static long getCurrentTimeMillis(LanguageConnectionContext lcc)\r
+ {\r
+ if (lcc.getStatisticsTiming())\r
+ {\r
+ return System.currentTimeMillis();\r
+ }\r
+ else\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ private static long getElapsedTimeMillis(long beginTime)\r
+ {\r
+ if (beginTime != 0)\r
+ {\r
+ return System.currentTimeMillis() - beginTime;\r
+ }\r
+ else\r
+ {\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ /*\r
+ ** Identity\r
+ */\r
+\r
+ public boolean equals(Object other) {\r
+\r
+ if (other instanceof GenericStatement) {\r
+\r
+ GenericStatement os = (GenericStatement) other;\r
+\r
+ return statementText.equals(os.statementText) && isForReadOnly==os.isForReadOnly\r
+ && compilationSchema.equals(os.compilationSchema) &&\r
+ (prepareIsolationLevel == os.prepareIsolationLevel);\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ public int hashCode() {\r
+\r
+ return statementText.hashCode();\r
+ }\r
+}\r