001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.StatementNode
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.compile;
023:
024: import org.apache.derby.iapi.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.error.StandardException;
027:
028: import org.apache.derby.iapi.sql.compile.CompilerContext;
029:
030: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
031: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
032: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
033: import org.apache.derby.iapi.store.access.ConglomerateController;
034: import org.apache.derby.iapi.store.access.TransactionController;
035:
036: import org.apache.derby.iapi.services.compiler.MethodBuilder;
037:
038: import org.apache.derby.iapi.reference.SQLState;
039: import org.apache.derby.iapi.reference.ClassName;
040: import org.apache.derby.iapi.services.loader.GeneratedClass;
041:
042: import org.apache.derby.iapi.util.ByteArray;
043: import org.apache.derby.iapi.services.classfile.VMOpcode;
044:
045: import org.apache.derby.iapi.services.sanity.SanityManager;
046:
047: import java.lang.reflect.Modifier;
048:
049: /**
050: * A StatementNode represents a single statement in the language. It is
051: * the top node for any statement.
052: * <p>
053: * StatementNode controls the class generation for query tree nodes.
054: *
055: * @author Jeff Lichtman
056: */
057:
058: /*
059: * History:
060: * 5/8/97 Rick Hilleags Moved node-name-string to child classes.
061: */
062:
063: abstract class StatementNode extends QueryTreeNode {
064:
065: /**
066: * By default, assume StatementNodes are atomic.
067: * The rare statements that aren't atomic (e.g.
068: * CALL method()) override this.
069: *
070: * @return true if the statement is atomic
071: *
072: * @exception StandardException Thrown on error
073: */
074: public boolean isAtomic() throws StandardException {
075: return true;
076: }
077:
078: /**
079: * Convert this object to a String. See comments in QueryTreeNode.java
080: * for how this should be done for tree printing.
081: *
082: * @return This object as a String
083: */
084:
085: public String toString() {
086: if (SanityManager.DEBUG) {
087: return "statementType: " + statementToString() + "\n"
088: + super .toString();
089: } else {
090: return "";
091: }
092: }
093:
094: public abstract String statementToString();
095:
096: /**
097: * create the outer shell class builder for the class we will
098: * be generating, generate the expression to stuff in it,
099: * and turn it into a class.
100: */
101: static final int NEED_DDL_ACTIVATION = 5;
102: static final int NEED_CURSOR_ACTIVATION = 4;
103: static final int NEED_PARAM_ACTIVATION = 2;
104: static final int NEED_ROW_ACTIVATION = 1;
105: static final int NEED_NOTHING_ACTIVATION = 0;
106:
107: abstract int activationKind();
108:
109: /* We need to get some kind of table lock (IX here) at the beginning of
110: * compilation of DMLModStatementNode and DDLStatementNode, to prevent the
111: * interference of insert/update/delete/DDL compilation and DDL execution,
112: * see beetle 3976, 4343, and $WS/language/SolutionsToConcurrencyIssues.txt
113: */
114: protected TableDescriptor lockTableForCompilation(TableDescriptor td)
115: throws StandardException {
116: DataDictionary dd = getDataDictionary();
117:
118: /* we need to lock only if the data dictionary is in DDL cache mode
119: */
120: if (dd.getCacheMode() == DataDictionary.DDL_MODE) {
121: ConglomerateController heapCC;
122: TransactionController tc = getLanguageConnectionContext()
123: .getTransactionCompile();
124:
125: heapCC = tc
126: .openConglomerate(
127: td.getHeapConglomerateId(),
128: false,
129: TransactionController.OPENMODE_FORUPDATE
130: | TransactionController.OPENMODE_FOR_LOCK_ONLY,
131: TransactionController.MODE_RECORD,
132: TransactionController.ISOLATION_SERIALIZABLE);
133: heapCC.close();
134: /*
135: ** Need to get TableDescriptor again after getting the lock, in
136: ** case for example, a concurrent add column thread commits
137: ** while we are binding.
138: */
139: String tableName = td.getName();
140: td = getTableDescriptor(td.getName(),
141: getSchemaDescriptor(td.getSchemaName()));
142: if (td == null) {
143: throw StandardException.newException(
144: SQLState.LANG_TABLE_NOT_FOUND, tableName);
145: }
146: }
147: return td;
148: }
149:
150: /**
151: * Do code generation for this statement.
152: *
153: * @param byteCode the generated byte code for this statement.
154: * if non-null, then the byte code is saved
155: * here.
156: *
157: * @return A GeneratedClass for this statement
158: *
159: * @exception StandardException Thrown on error
160: */
161: public GeneratedClass generate(ByteArray byteCode)
162: throws StandardException {
163: // start the new activation class.
164: // it starts with the Execute method
165: // and the appropriate superclass (based on
166: // statement type, from inspecting the queryTree).
167:
168: int nodeChoice = activationKind();
169:
170: /* RESOLVE: Activation hierarchy was way too complicated
171: * and added no value. Simple thing to do was to simply
172: * leave calling code alone and to handle here and to
173: * eliminate unnecessary classes.
174: */
175: String super Class;
176: switch (nodeChoice) {
177: case NEED_CURSOR_ACTIVATION:
178: super Class = ClassName.CursorActivation;
179: break;
180: case NEED_DDL_ACTIVATION:
181: return getClassFactory()
182: .loadGeneratedClass(
183: "org.apache.derby.impl.sql.execute.ConstantActionActivation",
184: null);
185:
186: case NEED_NOTHING_ACTIVATION:
187: case NEED_ROW_ACTIVATION:
188: case NEED_PARAM_ACTIVATION:
189: super Class = ClassName.BaseActivation;
190: break;
191: default:
192: throw StandardException.newException(
193: SQLState.LANG_UNAVAILABLE_ACTIVATION_NEED, String
194: .valueOf(nodeChoice));
195: }
196:
197: ActivationClassBuilder generatingClass = new ActivationClassBuilder(
198: super Class, getCompilerContext());
199: MethodBuilder executeMethod = generatingClass
200: .getExecuteMethod();
201:
202: /*
203: ** the resultSet variable is cached.
204: **
205: ** resultSet = (resultSet == null) ? ... : resultSet
206: */
207:
208: executeMethod.pushThis();
209: executeMethod.getField(ClassName.BaseActivation, "resultSet",
210: ClassName.ResultSet);
211: executeMethod.conditionalIfNull();
212:
213: /* We should generate the result set here. However, the generated
214: * code size may be too big to fit in a conditional statement for
215: * Java compiler to handle (it has a jump/branch step limit). For
216: * example, a extremely huge insert is issued with many many rows
217: * (beetle 4293). We fork a worker method here to get the
218: * generated result set, pass our parameter to it and call it.
219: */
220: MethodBuilder mbWorker = generatingClass.getClassBuilder()
221: .newMethodBuilder(Modifier.PROTECTED,
222: ClassName.ResultSet, "fillResultSet");
223: mbWorker.addThrownException(ClassName.StandardException);
224:
225: // we expect to get back an expression that will give a resultSet
226: // the nodes use the generatingClass: they add expression functions
227: // to it, and then use those functions in their expressions.
228: generate(generatingClass, mbWorker);
229:
230: mbWorker.methodReturn();
231: mbWorker.complete();
232: executeMethod.pushThis();
233: executeMethod.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null,
234: "fillResultSet", ClassName.ResultSet, 0);
235:
236: executeMethod.startElseCode(); // this is here as the compiler only supports ? :
237: executeMethod.pushThis();
238: executeMethod.getField(ClassName.BaseActivation, "resultSet",
239: ClassName.ResultSet);
240: executeMethod.completeConditional();
241:
242: executeMethod.pushThis();
243: executeMethod.swap();
244: executeMethod.putField(ClassName.BaseActivation, "resultSet",
245: ClassName.ResultSet);
246:
247: executeMethod.endStatement();
248:
249: // wrap up the activation class definition
250: // generate on the tree gave us back the newExpr
251: // for getting a result set on the tree.
252: // we put it in a return statement and stuff
253: // it in the execute method of the activation.
254: // The generated statement is the expression:
255: // the activation class builder takes care of constructing it
256: // for us, given the resultSetExpr to use.
257: // return (this.resultSet = #resultSetExpr);
258: generatingClass.finishExecuteMethod(this instanceof CursorNode);
259:
260: // wrap up the constructor by putting a return at the end of it
261: generatingClass.finishConstructor();
262:
263: try {
264: // cook the completed class into a real class
265: // and stuff it into activationClass
266: GeneratedClass activationClass = generatingClass
267: .getGeneratedClass(byteCode);
268:
269: return activationClass;
270: } catch (StandardException e) {
271:
272: String msgId = e.getMessageId();
273:
274: if (SQLState.GENERATED_CLASS_LIMIT_EXCEEDED.equals(msgId)
275: || SQLState.GENERATED_CLASS_LINKAGE_ERROR
276: .equals(msgId)) {
277: throw StandardException.newException(
278: SQLState.LANG_QUERY_TOO_COMPLEX, e);
279: }
280:
281: throw e;
282: }
283: }
284: }
|