Source code: org/apache/derby/impl/sql/compile/ActivationClassBuilder.java
1 /*
2
3 Derby - Class org.apache.derby.impl.sql.compile.ActivationClassBuilder
4
5 Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable.
6
7 Licensed under the Apache License, Version 2.0 (the "License");
8 you may not use this file except in compliance with the License.
9 You may obtain a copy of the License at
10
11 http://www.apache.org/licenses/LICENSE-2.0
12
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18
19 */
20
21 package org.apache.derby.impl.sql.compile;
22
23 import org.apache.derby.iapi.services.compiler.ClassBuilder;
24 import org.apache.derby.iapi.services.compiler.MethodBuilder;
25 import org.apache.derby.iapi.services.compiler.LocalField;
26 import org.apache.derby.iapi.reference.ClassName;
27
28
29 import org.apache.derby.iapi.services.sanity.SanityManager;
30
31 import org.apache.derby.iapi.sql.compile.CompilerContext;
32 import org.apache.derby.iapi.sql.compile.CodeGeneration;
33
34 import org.apache.derby.iapi.sql.execute.CursorResultSet;
35
36 import org.apache.derby.iapi.sql.ResultSet;
37
38 import org.apache.derby.iapi.sql.execute.ExecRow;
39
40 import org.apache.derby.iapi.types.DataTypeDescriptor;
41 import org.apache.derby.iapi.types.TypeId;
42
43 import org.apache.derby.iapi.error.StandardException;
44
45 import org.apache.derby.iapi.services.loader.GeneratedMethod;
46 import org.apache.derby.iapi.services.classfile.VMOpcode;
47
48 import java.lang.reflect.Modifier;
49
50 import java.io.PrintWriter;
51
52 import java.io.File;
53 import java.io.FileOutputStream;
54 import java.io.ByteArrayOutputStream;
55 import java.io.IOException;
56 import java.util.Hashtable;
57
58 /**
59 * ActivationClassBuilder
60 * provides an interface to satisfy generation's
61 * common tasks in building an activation class,
62 * as well as a repository for the JavaFactory used
63 * to generate the basic language constructs for the methods in the class.
64 * Common tasks include the setting of a static field for each
65 * expression function that gets added, the creation
66 * of the execute method that gets expanded as the query tree
67 * is walked, setting the superclass.
68 * <p>
69 * An activation class is defined for each statement. It has
70 * the following basic layout: TBD
71 * See the document
72 * \\Jeeves\Unversioned Repository 1\Internal Technical Documents\Other\GenAndExec.doc
73 * for details.
74 * <p>
75 * We could also verify methods as they are
76 * added, to have 0 parameters, ...
77 *
78 * @author ames
79 */
80 public class ActivationClassBuilder extends ExpressionClassBuilder
81 {
82 ///////////////////////////////////////////////////////////////////////
83 //
84 // CONSTANTS
85 //
86 ///////////////////////////////////////////////////////////////////////
87
88 ///////////////////////////////////////////////////////////////////////
89 //
90 // STATE
91 //
92 ///////////////////////////////////////////////////////////////////////
93
94 private LocalField targetResultSetField;
95 private LocalField cursorResultSetField;
96
97 private MethodBuilder closeActivationMethod;
98
99
100 ///////////////////////////////////////////////////////////////////////
101 //
102 // CONSTRUCTOR
103 //
104 ///////////////////////////////////////////////////////////////////////
105
106 /**
107 * By the time this is done, it has constructed the following class:
108 * <pre>
109 * public class #className extends #superClass {
110 * // public void reset() { return; }
111 * public ResultSet execute() throws StandardException {
112 * throwIfClosed("execute");
113 * // statements must be added here
114 * }
115 * public #className() { super(); }
116 * }
117 * </pre>
118 *
119 * @exception StandardException thrown on failure
120 */
121 public ActivationClassBuilder (String superClass, CompilerContext cc) throws StandardException
122 {
123 super( superClass, (String) null, cc );
124 executeMethod = beginExecuteMethod();
125 }
126
127 ///////////////////////////////////////////////////////////////////////
128 //
129 // ACCESSORS
130 //
131 ///////////////////////////////////////////////////////////////////////
132
133 /**
134 * Get the package name that this generated class lives in
135 *
136 * @return package name
137 */
138 public String getPackageName()
139 { return CodeGeneration.GENERATED_PACKAGE_PREFIX; }
140
141 /**
142 The base class for activations is BaseActivation
143 */
144 public String getBaseClassName() {
145 return ClassName.BaseActivation;
146 }
147
148
149 /**
150 * Get the number of ExecRows to allocate
151 *
152 * @exception StandardException thrown on failure
153 * @return package name
154 */
155 public int getRowCount()
156 throws StandardException
157 {
158 return myCompCtx.getNumResultSets();
159 }
160
161 /**
162 * Generate the assignment for numSubqueries = x
163 *
164 * @param numSubqueries The number of subqueries in the query.
165 *
166 * @return Nothing.
167 * @exception StandardException thrown on failure
168 */
169 public void setNumSubqueries()
170 {
171 int numSubqueries = myCompCtx.getNumSubquerys();
172
173 // If there are no subqueries then
174 // the field is set to the correctly
175 // value (0) by java.
176 if (numSubqueries == 0)
177 return;
178
179 /* Generated code is:
180 * numSubqueries = x;
181 */
182 constructor.pushThis();
183 constructor.push(numSubqueries);
184 constructor.putField(ClassName.BaseActivation, "numSubqueries", "int");
185 constructor.endStatement();
186 }
187
188
189 ///////////////////////////////////////////////////////////////////////
190 //
191 // EXECUTE METHODS
192 //
193 ///////////////////////////////////////////////////////////////////////
194
195 /**
196 * By the time this is done, it has generated the following code
197 * <pre>
198 * public ResultSet execute() throws StandardException {
199 * throwIfClosed("execute");
200 * // statements must be added here
201 * }
202 * }
203 * </pre>
204 *
205 * @exception StandardException thrown on failure
206 */
207 public MethodBuilder beginExecuteMethod()
208 throws StandardException
209 {
210 // create a reset method that does nothing.
211 // REVISIT: this might better belong in the Activation
212 // superclasses ?? not clear yet what it needs to do.
213
214 // don't yet need a reset method here. when we do,
215 // it will need to call super.reset() as well as
216 // whatever it does.
217 // mb = cb.newMethodBuilder(
218 // Modifier.PUBLIC, "void", "reset");
219 // mb.addStatement(javaFac.newStatement(
220 // javaFac.newSpecialMethodCall(
221 // thisExpression(),
222 // BaseActivation.CLASS_NAME,
223 // "reset", "void")));
224 // mb.addStatement(javaFac.newReturnStatement());
225 // mb.complete(); // there is nothing else.
226
227
228 // This method is an implementation of the interface method
229 // Activation - ResultSet execute()
230
231 // create an empty execute method
232 MethodBuilder mb = cb.newMethodBuilder(Modifier.PUBLIC,
233 ClassName.ResultSet, "execute");
234 mb.addThrownException(ClassName.StandardException);
235
236 // put a 'throwIfClosed("execute");' statement into the execute method.
237 mb.pushThis(); // instance
238 mb.push("execute");
239 mb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "throwIfClosed", "void", 1);
240
241 // call this.startExecution(), so the parent class can know an execution
242 // has begun.
243
244 mb.pushThis(); // instance
245 mb.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "startExecution", "void", 0);
246
247 return mb;
248 }
249
250 public MethodBuilder startResetMethod() {
251 MethodBuilder mb = cb.newMethodBuilder(Modifier.PUBLIC,
252 "void", "reset");
253
254 mb.addThrownException(ClassName.StandardException);
255 mb.pushThis();
256 mb.callMethod(VMOpcode.INVOKESPECIAL, ClassName.BaseActivation, "reset", "void", 0);
257
258
259 return mb;
260 }
261
262 /**
263 * An execute method always ends in a return statement, returning
264 * the result set that has been constructed. We want to
265 * do some bookkeeping on that statement, so we generate
266 * the return given the result set.
267
268 Upon entry the only word on the stack is the result set expression
269 */
270 public void finishExecuteMethod(boolean genMarkAsTopNode) {
271
272 executeMethod.pushThis();
273 executeMethod.getField(ClassName.BaseActivation, "resultSet", ClassName.ResultSet);
274
275 /* We only call markAsTopResultSet() for selects.
276 * Non-select DML marks the top NoPutResultSet in the constructor.
277 * Needed for closing down resultSet on an error.
278 */
279 if (genMarkAsTopNode)
280 {
281 // dup the result set to leave one for the return and one for this call
282 executeMethod.dup();
283 executeMethod.cast(ClassName.NoPutResultSet);
284 executeMethod.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "markAsTopResultSet", "void", 0);
285 }
286
287 /* return resultSet */
288 executeMethod.methodReturn();
289 executeMethod.complete();
290
291 getClassBuilder().newFieldWithAccessors("getExecutionCount", "setExecutionCount",
292 Modifier.PROTECTED, true, "int");
293
294 getClassBuilder().newFieldWithAccessors("getRowCountCheckVector", "setRowCountCheckVector",
295 Modifier.PROTECTED, true, "java.util.Vector");
296
297 getClassBuilder().newFieldWithAccessors("getStalePlanCheckInterval", "setStalePlanCheckInterval",
298 Modifier.PROTECTED, true, "int");
299
300 if (closeActivationMethod != null) {
301 closeActivationMethod.methodReturn();
302 closeActivationMethod.complete();
303 }
304 }
305
306 ///////////////////////////////////////////////////////////////////////
307 //
308 // CURSOR SUPPORT
309 //
310 ///////////////////////////////////////////////////////////////////////
311
312 /**
313 * Updatable cursors
314 * need to add a getter method for use in BaseActivation to access
315 * the result set that identifies target rows for a positioned
316 * update or delete.
317 * <p>
318 * The code that is generated is:
319 * <pre><verbatim>
320 * public CursorResultSet getTargetResultSet() {
321 * return targetResultSet;
322 * }
323 *
324 * public CursorResultSet getCursorResultSet() {
325 * return cursorResultSet;
326 * }
327 * </verbatim></pre>
328 *
329 */
330 public void addCursorPositionCode() {
331
332 // the getter
333 // This method is an implementation of the interface method
334 // CursorActivation - CursorResultSet getTargetResultSet()
335 MethodBuilder getter = cb.newMethodBuilder(Modifier.PUBLIC,
336 ClassName.CursorResultSet, "getTargetResultSet");
337
338 getter.getField(targetResultSetField);
339 getter.methodReturn();
340 getter.complete();
341
342 // This method is an implementation of the interface method
343 // CursorActivation - CursorResultSet getCursorResultSet()
344
345 getter = cb.newMethodBuilder(Modifier.PUBLIC,
346 ClassName.CursorResultSet, "getCursorResultSet");
347
348 getter.getField(cursorResultSetField);
349 getter.methodReturn();
350 getter.complete();
351 }
352
353 /**
354 * Updatable cursors
355 * need to add a field and its initialization
356 * for use in BaseActivation to access the result set that
357 * identifies target rows for a positioned update or delete.
358 * <p>
359 * The code that is generated is:
360 * <pre><verbatim>
361 * private CursorResultSet targetResultSet;
362 *
363 * </verbatim></pre>
364 *
365 * The expression that is generated is:
366 * <pre><verbatim>
367 * (ResultSet) (targetResultSet = (CursorResultSet) #expression#)
368 * </verbatim></pre>
369 *
370 */
371 public void rememberCursorTarget(MethodBuilder mb) {
372
373 // the field
374 targetResultSetField = cb.addField(ClassName.CursorResultSet,
375 "targetResultSet",
376 Modifier.PRIVATE);
377
378 mb.cast(ClassName.CursorResultSet);
379 mb.putField(targetResultSetField);
380 mb.cast(ClassName.NoPutResultSet);
381 }
382
383 /**
384 * Updatable cursors
385 * need to add a field and its initialization
386 * for use in BaseActivation to access the result set that
387 * identifies cursor result rows for a positioned update or delete.
388 * <p>
389 * The code that is generated is:
390 * <pre><verbatim>
391 * private CursorResultSet cursorResultSet;
392 *
393 * </verbatim></pre>
394 *
395 * The expression that is generated is:
396 * <pre><verbatim>
397 * (ResultSet) (cursorResultSet = (CursorResultSet) #expression#)
398 * </verbatim></pre>
399
400 The expression must be the top stack word when this method is called.
401 *
402 */
403 public void rememberCursor(MethodBuilder mb) {
404
405 // the field
406 cursorResultSetField = cb.addField(ClassName.CursorResultSet,
407 "cursorResultSet",
408 Modifier.PRIVATE);
409
410 mb.cast(ClassName.CursorResultSet);
411 mb.putField(cursorResultSetField);
412 mb.cast(ClassName.ResultSet);
413 }
414
415 ///////////////////////////////////////////////////////////////////////
416 //
417 // CURRENT DATE/TIME SUPPORT
418 //
419 ///////////////////////////////////////////////////////////////////////
420
421 /*
422 The first time a current datetime is needed, create the class
423 level support for it. The first half of the logic is in our parent
424 class. Then we add logic here to tidy up for ResultSet management.
425 */
426 protected LocalField getCurrentSetup()
427 {
428 if (cdtField != null) return cdtField;
429
430 LocalField lf = super.getCurrentSetup();
431
432 // 3) the execute method gets a statement (prior to the return)
433 // to tell cdt to restart:
434 // cdt.forget();
435
436 executeMethod.getField(lf);
437 executeMethod.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, "forget", "void", 0);
438
439 // 4) a resultSetClosed method is set up to be passed to
440 // the outermost result set, if it is an open/close result set,
441 // so that cdt can be told to forget when a result set closes:
442 // GeneratedMethod rscm; // the name is just a generated name, simpler.
443 // void rscm() {
444 // cdt.forget();
445 // }
446 MethodBuilder mb = newExprFun();
447 mb.getField(lf); // the instance
448 mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, "forget", "void", 0);
449 mb.pushNull("java.lang.Object");
450 mb.methodReturn();
451 mb.complete();
452
453 resultSetClosedMethod = mb;
454
455 return lf;
456 }
457
458 //////////////////////////////////////////////////////////////////////////
459 //
460 // NAMED PARAMETER METHODS
461 //
462 //////////////////////////////////////////////////////////////////////////
463
464 /**
465 * Generates a parameter reference. Only implemented for Filters right now.
466 *
467 * @param name Parameter name.
468 * @param position Parameter id.
469 * @param dataType Parameter datatype.
470 * @param mb The method to put the generated code into
471 *
472 * @return an expression representing the parameter reference.
473 *
474 * @exception StandardException thrown on failure
475 */
476 public void getParameterReference( String name,
477 int position,
478 DataTypeDescriptor dataType,
479 MethodBuilder mb )
480 throws StandardException
481 {
482 if (SanityManager.DEBUG)
483 SanityManager.NOTREACHED();
484 }
485
486
487 public MethodBuilder getCloseActivationMethod() {
488
489 if (closeActivationMethod == null) {
490 closeActivationMethod = cb.newMethodBuilder(Modifier.PUBLIC, "void", "closeActivationAction");
491 closeActivationMethod.addThrownException("java.lang.Exception");
492 }
493 return closeActivationMethod;
494 }
495 }
496