001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.GenericStatement
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql;
023:
024: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
025:
026: import org.apache.derby.iapi.reference.JDBC20Translation;
027: import org.apache.derby.iapi.reference.JDBC30Translation;
028: import org.apache.derby.iapi.reference.SQLState;
029:
030: import org.apache.derby.iapi.sql.Activation;
031: import org.apache.derby.iapi.types.DataTypeDescriptor;
032: import org.apache.derby.iapi.sql.ResultSet;
033: import org.apache.derby.iapi.sql.Statement;
034: import org.apache.derby.iapi.sql.PreparedStatement;
035: import org.apache.derby.iapi.sql.execute.ConstantAction;
036: import org.apache.derby.iapi.sql.execute.ExecutionContext;
037: import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
038: import org.apache.derby.iapi.sql.ParameterValueSet;
039:
040: import org.apache.derby.iapi.sql.conn.LanguageConnectionFactory;
041: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
042: import org.apache.derby.iapi.sql.conn.StatementContext;
043:
044: import org.apache.derby.iapi.sql.depend.Dependent;
045:
046: import org.apache.derby.iapi.sql.compile.CompilerContext;
047: import org.apache.derby.iapi.sql.compile.NodeFactory;
048: import org.apache.derby.iapi.sql.compile.Parser;
049:
050: import org.apache.derby.impl.sql.compile.QueryTreeNode;
051: import org.apache.derby.impl.sql.conn.GenericLanguageConnectionContext;
052:
053: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
054: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
055: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
056:
057: import org.apache.derby.iapi.services.compiler.JavaFactory;
058: import org.apache.derby.iapi.services.uuid.UUIDFactory;
059: import org.apache.derby.iapi.util.ByteArray;
060:
061: import org.apache.derby.iapi.error.StandardException;
062:
063: import org.apache.derby.iapi.services.monitor.Monitor;
064:
065: import org.apache.derby.iapi.services.context.Context;
066: import org.apache.derby.iapi.services.context.ContextService;
067: import org.apache.derby.iapi.services.context.ContextManager;
068: import org.apache.derby.iapi.services.sanity.SanityManager;
069:
070: import org.apache.derby.iapi.services.loader.GeneratedClass;
071:
072: import java.sql.Timestamp;
073: import java.sql.SQLWarning;
074:
075: public class GenericStatement implements Statement {
076:
077: // these fields define the identity of the statement
078: private final SchemaDescriptor compilationSchema;
079: private final String statementText;
080: private final boolean isForReadOnly;
081: private int prepareIsolationLevel;
082: private GenericPreparedStatement preparedStmt;
083:
084: /**
085: * Constructor for a Statement given the text of the statement in a String
086: * @param compilationSchema schema
087: * @param statementText The text of the statement
088: * @param isForReadOnly if the statement is opened with level CONCUR_READ_ONLY
089: */
090:
091: public GenericStatement(SchemaDescriptor compilationSchema,
092: String statementText, boolean isForReadOnly) {
093: this .compilationSchema = compilationSchema;
094: this .statementText = statementText;
095: this .isForReadOnly = isForReadOnly;
096: }
097:
098: /*
099: * Statement interface
100: */
101:
102: /* RESOLVE: may need error checking, debugging code here */
103: public PreparedStatement prepare(LanguageConnectionContext lcc)
104: throws StandardException {
105: /*
106: ** Note: don't reset state since this might be
107: ** a recompilation of an already prepared statement.
108: */
109: return prepMinion(lcc, true, (Object[]) null,
110: (SchemaDescriptor) null, false);
111: }
112:
113: public PreparedStatement prepare(LanguageConnectionContext lcc,
114: boolean forMetaData) throws StandardException {
115: /*
116: ** Note: don't reset state since this might be
117: ** a recompilation of an already prepared statement.
118: */
119: return prepMinion(lcc, true, (Object[]) null,
120: (SchemaDescriptor) null, forMetaData);
121: }
122:
123: private PreparedStatement prepMinion(LanguageConnectionContext lcc,
124: boolean cacheMe, Object[] paramDefaults,
125: SchemaDescriptor spsSchema, boolean internalSQL)
126: throws StandardException {
127:
128: long beginTime = 0;
129: long parseTime = 0;
130: long bindTime = 0;
131: long optimizeTime = 0;
132: long generateTime = 0;
133: Timestamp beginTimestamp = null;
134: Timestamp endTimestamp = null;
135: StatementContext statementContext = null;
136:
137: // verify it isn't already prepared...
138: // if it is, and is valid, simply return that tree.
139: // if it is invalid, we will recompile now.
140: if (preparedStmt != null) {
141: if (preparedStmt.upToDate())
142: return preparedStmt;
143: }
144:
145: // Clear the optimizer trace from the last statement
146: if (lcc.getOptimizerTrace())
147: lcc.setOptimizerTraceOutput(getSource() + "\n");
148:
149: beginTime = getCurrentTimeMillis(lcc);
150: /* beginTimestamp only meaningful if beginTime is meaningful.
151: * beginTime is meaningful if STATISTICS TIMING is ON.
152: */
153: if (beginTime != 0) {
154: beginTimestamp = new Timestamp(beginTime);
155: }
156:
157: /** set the prepare Isolaton from the LanguageConnectionContext now as
158: * we need to consider it in caching decisions
159: */
160: prepareIsolationLevel = lcc.getPrepareIsolationLevel();
161:
162: /* a note on statement caching:
163: *
164: * A GenericPreparedStatement (GPS) is only added it to the cache if the
165: * parameter cacheMe is set to TRUE when the GPS is created.
166: *
167: * Earlier only CacheStatement (CS) looked in the statement cache for a
168: * prepared statement when prepare was called. Now the functionality
169: * of CS has been folded into GenericStatement (GS). So we search the
170: * cache for an existing PreparedStatement only when cacheMe is TRUE.
171: * i.e if the user calls prepare with cacheMe set to TRUE:
172: * then we
173: * a) look for the prepared statement in the cache.
174: * b) add the prepared statement to the cache.
175: *
176: * In cases where the statement cache has been disabled (by setting the
177: * relevant cloudscape property) then the value of cacheMe is irrelevant.
178: */
179: boolean foundInCache = false;
180: if (preparedStmt == null) {
181: if (cacheMe)
182: preparedStmt = (GenericPreparedStatement) ((GenericLanguageConnectionContext) lcc)
183: .lookupStatement(this );
184:
185: if (preparedStmt == null) {
186: preparedStmt = new GenericPreparedStatement(this );
187: } else {
188: foundInCache = true;
189: }
190: }
191:
192: // if anyone else also has this prepared statement,
193: // we don't want them trying to compile with it while
194: // we are. So, we synchronize on it and re-check
195: // its validity first.
196: // this is a no-op if and until there is a central
197: // cache of prepared statement objects...
198: synchronized (preparedStmt) {
199:
200: for (;;) {
201:
202: if (foundInCache) {
203: if (preparedStmt.referencesSessionSchema()) {
204: // cannot use this state since it is private to a connection.
205: // switch to a new statement.
206: foundInCache = false;
207: preparedStmt = new GenericPreparedStatement(
208: this );
209: break;
210: }
211: }
212:
213: // did it get updated while we waited for the lock on it?
214: if (preparedStmt.upToDate()) {
215: return preparedStmt;
216: }
217:
218: if (!preparedStmt.compilingStatement) {
219: break;
220: }
221:
222: try {
223: preparedStmt.wait();
224: } catch (InterruptedException ie) {
225: throw StandardException.interrupt(ie);
226: }
227: }
228:
229: preparedStmt.compilingStatement = true;
230: preparedStmt.setActivationClass(null);
231: }
232:
233: try {
234:
235: HeaderPrintWriter istream = lcc.getLogStatementText() ? Monitor
236: .getStream()
237: : null;
238:
239: /*
240: ** For stored prepared statements, we want all
241: ** errors, etc in the context of the underlying
242: ** EXECUTE STATEMENT statement, so don't push/pop
243: ** another statement context unless we don't have
244: ** one. We won't have one if it is an internal
245: ** SPS (e.g. jdbcmetadata).
246: */
247: if (!preparedStmt.isStorable()
248: || lcc.getStatementDepth() == 0) {
249: // since this is for compilation only, set atomic
250: // param to true and timeout param to 0
251: statementContext = lcc.pushStatementContext(true,
252: isForReadOnly, getSource(), null, false, 0L);
253: }
254:
255: /*
256: ** RESOLVE: we may ultimately wish to pass in
257: ** whether we are a jdbc metadata query or not to
258: ** get the CompilerContext to make the createDependency()
259: ** call a noop.
260: */
261: CompilerContext cc = lcc
262: .pushCompilerContext(compilationSchema);
263:
264: if (prepareIsolationLevel != ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL) {
265: cc.setScanIsolationLevel(prepareIsolationLevel);
266: }
267:
268: // Look for stored statements that are in a system schema
269: // and with a match compilation schema. If so, allow them
270: // to compile using internal SQL constructs.
271:
272: if (internalSQL || (spsSchema != null)
273: && (spsSchema.isSystemSchema())
274: && (spsSchema.equals(compilationSchema))) {
275: cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
276: }
277:
278: try {
279: // Statement logging if lcc.getLogStatementText() is true
280: if (istream != null) {
281: String xactId = lcc.getTransactionExecute()
282: .getActiveStateTxIdString();
283: istream
284: .printlnWithHeader(LanguageConnectionContext.xidStr
285: + xactId
286: + "), "
287: + LanguageConnectionContext.lccStr
288: + lcc.getInstanceNumber()
289: + "), "
290: + LanguageConnectionContext.dbnameStr
291: + lcc.getDbname()
292: + "), "
293: + LanguageConnectionContext.drdaStr
294: + lcc.getDrdaID()
295: + "), Begin compiling prepared statement: "
296: + getSource()
297: + " :End prepared statement");
298: }
299:
300: Parser p = cc.getParser();
301:
302: cc.setCurrentDependent(preparedStmt);
303:
304: //Only top level statements go through here, nested statement
305: //will invoke this method from other places
306: QueryTreeNode qt = p.parseStatement(statementText,
307: paramDefaults);
308:
309: parseTime = getCurrentTimeMillis(lcc);
310:
311: if (SanityManager.DEBUG) {
312: if (SanityManager.DEBUG_ON("DumpParseTree")) {
313: qt.treePrint();
314: }
315:
316: if (SanityManager.DEBUG_ON("StopAfterParsing")) {
317: throw StandardException
318: .newException(SQLState.LANG_STOP_AFTER_PARSING);
319: }
320: }
321:
322: /*
323: ** Tell the data dictionary that we are about to do
324: ** a bunch of "get" operations that must be consistent with
325: ** each other.
326: */
327:
328: DataDictionary dataDictionary = lcc.getDataDictionary();
329:
330: int ddMode = dataDictionary == null ? 0
331: : dataDictionary.startReading(lcc);
332:
333: try {
334: // start a nested transaction -- all locks acquired by bind
335: // and optimize will be released when we end the nested
336: // transaction.
337: lcc.beginNestedTransaction(true);
338:
339: qt = qt.bind();
340: bindTime = getCurrentTimeMillis(lcc);
341:
342: if (SanityManager.DEBUG) {
343: if (SanityManager.DEBUG_ON("DumpBindTree")) {
344: qt.treePrint();
345: }
346:
347: if (SanityManager.DEBUG_ON("StopAfterBinding")) {
348: throw StandardException
349: .newException(SQLState.LANG_STOP_AFTER_BINDING);
350: }
351: }
352:
353: //Derby424 - In order to avoid caching select statements referencing
354: // any SESSION schema objects (including statements referencing views
355: // in SESSION schema), we need to do the SESSION schema object check
356: // here.
357: //a specific eg for statement referencing a view in SESSION schema
358: //CREATE TABLE t28A (c28 int)
359: //INSERT INTO t28A VALUES (280),(281)
360: //CREATE VIEW SESSION.t28v1 as select * from t28A
361: //SELECT * from SESSION.t28v1 should show contents of view and we
362: // should not cache this statement because a user can later define
363: // a global temporary table with the same name as the view name.
364: //Following demonstrates that
365: //DECLARE GLOBAL TEMPORARY TABLE SESSION.t28v1(c21 int, c22 int) not
366: // logged
367: //INSERT INTO SESSION.t28v1 VALUES (280,1),(281,2)
368: //SELECT * from SESSION.t28v1 should show contents of global temporary
369: //table and not the view. Since this select statement was not cached
370: // earlier, it will be compiled again and will go to global temporary
371: // table to fetch data. This plan will not be cached either because
372: // select statement is using SESSION schema object.
373: //
374: //Following if statement makes sure that if the statement is
375: // referencing SESSION schema objects, then we do not want to cache it.
376: // We will remove the entry that was made into the cache for
377: //this statement at the beginning of the compile phase.
378: //The reason we do this check here rather than later in the compile
379: // phase is because for a view, later on, we loose the information that
380: // it was referencing SESSION schema because the reference
381: //view gets replaced with the actual view definition. Right after
382: // binding, we still have the information on the view and that is why
383: // we do the check here.
384: if (preparedStmt.referencesSessionSchema(qt)) {
385: if (foundInCache)
386: ((GenericLanguageConnectionContext) lcc)
387: .removeStatement(this );
388: }
389:
390: qt = qt.optimize();
391:
392: optimizeTime = getCurrentTimeMillis(lcc);
393:
394: // Statement logging if lcc.getLogStatementText() is true
395: if (istream != null) {
396: String xactId = lcc.getTransactionExecute()
397: .getActiveStateTxIdString();
398: istream
399: .printlnWithHeader(LanguageConnectionContext.xidStr
400: + xactId
401: + "), "
402: + LanguageConnectionContext.lccStr
403: + lcc.getInstanceNumber()
404: + "), "
405: + LanguageConnectionContext.dbnameStr
406: + lcc.getDbname()
407: + "), "
408: + LanguageConnectionContext.drdaStr
409: + lcc.getDrdaID()
410: + "), End compiling prepared statement: "
411: + getSource()
412: + " :End prepared statement");
413: }
414: }
415:
416: catch (StandardException se) {
417: lcc.commitNestedTransaction();
418:
419: // Statement logging if lcc.getLogStatementText() is true
420: if (istream != null) {
421: String xactId = lcc.getTransactionExecute()
422: .getActiveStateTxIdString();
423: istream
424: .printlnWithHeader(LanguageConnectionContext.xidStr
425: + xactId
426: + "), "
427: + LanguageConnectionContext.lccStr
428: + lcc.getInstanceNumber()
429: + "), "
430: + LanguageConnectionContext.dbnameStr
431: + lcc.getDbname()
432: + "), "
433: + LanguageConnectionContext.drdaStr
434: + lcc.getDrdaID()
435: + "), Error compiling prepared statement: "
436: + getSource()
437: + " :End prepared statement");
438: }
439: throw se;
440: }
441:
442: finally {
443: /* Tell the data dictionary that we are done reading */
444: if (dataDictionary != null)
445: dataDictionary.doneReading(ddMode, lcc);
446: }
447:
448: /* we need to move the commit of nested sub-transaction
449: * after we mark PS valid, during compilation, we might need
450: * to get some lock to synchronize with another thread's DDL
451: * execution, in particular, the compilation of insert/update/
452: * delete vs. create index/constraint (see Beetle 3976). We
453: * can't release such lock until after we mark the PS valid.
454: * Otherwise we would just erase the DDL's invalidation when
455: * we mark it valid.
456: */
457: try // put in try block, commit sub-transaction if bad
458: {
459: if (SanityManager.DEBUG) {
460: if (SanityManager.DEBUG_ON("DumpOptimizedTree")) {
461: qt.treePrint();
462: }
463:
464: if (SanityManager
465: .DEBUG_ON("StopAfterOptimizing")) {
466: throw StandardException
467: .newException(SQLState.LANG_STOP_AFTER_OPTIMIZING);
468: }
469: }
470:
471: GeneratedClass ac = qt.generate(preparedStmt
472: .getByteCodeSaver());
473:
474: generateTime = getCurrentTimeMillis(lcc);
475: /* endTimestamp only meaningful if generateTime is meaningful.
476: * generateTime is meaningful if STATISTICS TIMING is ON.
477: */
478: if (generateTime != 0) {
479: endTimestamp = new Timestamp(generateTime);
480: }
481:
482: if (SanityManager.DEBUG) {
483: if (SanityManager
484: .DEBUG_ON("StopAfterGenerating")) {
485: throw StandardException
486: .newException(SQLState.LANG_STOP_AFTER_GENERATING);
487: }
488: }
489:
490: /*
491: copy over the compile-time created objects
492: to the prepared statement. This always happens
493: at the end of a compile, so there is no need
494: to erase the previous entries on a re-compile --
495: this erases as it replaces. Set the activation
496: class in case it came from a StorablePreparedStatement
497: */
498: preparedStmt.setConstantAction(qt
499: .makeConstantAction());
500: preparedStmt.setSavedObjects(cc.getSavedObjects());
501: preparedStmt.setRequiredPermissionsList(cc
502: .getRequiredPermissionsList());
503: preparedStmt.setActivationClass(ac);
504: preparedStmt.setNeedsSavepoint(qt.needsSavepoint());
505: preparedStmt.setCursorInfo((CursorInfo) cc
506: .getCursorInfo());
507: preparedStmt.setIsAtomic(qt.isAtomic());
508: preparedStmt.setExecuteStatementNameAndSchema(qt
509: .executeStatementName(), qt
510: .executeSchemaName());
511: preparedStmt.setSPSName(qt.getSPSName());
512: preparedStmt.completeCompile(qt);
513: preparedStmt.setCompileTimeWarnings(cc
514: .getWarnings());
515: } catch (StandardException e) // hold it, throw it
516: {
517: lcc.commitNestedTransaction();
518: throw e;
519: }
520:
521: if (lcc.getRunTimeStatisticsMode()) {
522: preparedStmt.setCompileTimeMillis(
523: parseTime - beginTime, //parse time
524: bindTime - parseTime, //bind time
525: optimizeTime - bindTime, //optimize time
526: generateTime - optimizeTime, //generate time
527: getElapsedTimeMillis(beginTime),
528: beginTimestamp, endTimestamp);
529: }
530:
531: } finally // for block introduced by pushCompilerContext()
532: {
533: lcc.popCompilerContext(cc);
534: }
535: } catch (StandardException se) {
536: if (foundInCache)
537: ((GenericLanguageConnectionContext) lcc)
538: .removeStatement(this );
539: throw se;
540: } finally {
541: synchronized (preparedStmt) {
542: preparedStmt.compilingStatement = false;
543: preparedStmt.notifyAll();
544: }
545: }
546:
547: lcc.commitNestedTransaction();
548:
549: if (statementContext != null)
550: lcc.popStatementContext(statementContext, null);
551:
552: return preparedStmt;
553: }
554:
555: /**
556: * Generates an execution plan given a set of named parameters.
557: * Does so for a storable prepared statement.
558: *
559: * @param paramDefaults Parameter defaults
560: *
561: * @return A PreparedStatement that allows execution of the execution
562: * plan.
563: * @exception StandardException Thrown if this is an
564: * execution-only version of the module (the prepare() method
565: * relies on compilation).
566: */
567: public PreparedStatement prepareStorable(
568: LanguageConnectionContext lcc, PreparedStatement ps,
569: Object[] paramDefaults, SchemaDescriptor spsSchema,
570: boolean internalSQL) throws StandardException {
571: if (ps == null)
572: ps = new GenericStorablePreparedStatement(this );
573: else
574: ((GenericPreparedStatement) ps).statement = this ;
575:
576: this .preparedStmt = (GenericPreparedStatement) ps;
577: return prepMinion(lcc, false, paramDefaults, spsSchema,
578: internalSQL);
579: }
580:
581: public String getSource() {
582: return statementText;
583: }
584:
585: public String getCompilationSchema() {
586: return compilationSchema.getDescriptorName();
587: }
588:
589: private static long getCurrentTimeMillis(
590: LanguageConnectionContext lcc) {
591: if (lcc.getStatisticsTiming()) {
592: return System.currentTimeMillis();
593: } else {
594: return 0;
595: }
596: }
597:
598: private static long getElapsedTimeMillis(long beginTime) {
599: if (beginTime != 0) {
600: return System.currentTimeMillis() - beginTime;
601: } else {
602: return 0;
603: }
604: }
605:
606: /*
607: ** Identity
608: */
609:
610: public boolean equals(Object other) {
611:
612: if (other instanceof GenericStatement) {
613:
614: GenericStatement os = (GenericStatement) other;
615:
616: return statementText.equals(os.statementText)
617: && isForReadOnly == os.isForReadOnly
618: && compilationSchema.equals(os.compilationSchema)
619: && (prepareIsolationLevel == os.prepareIsolationLevel);
620: }
621:
622: return false;
623: }
624:
625: public int hashCode() {
626:
627: return statementText.hashCode();
628: }
629: }
|