001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.SQLToJavaValueNode
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.compiler.MethodBuilder;
025: import org.apache.derby.iapi.services.compiler.LocalField;
026:
027: import org.apache.derby.iapi.services.sanity.SanityManager;
028:
029: import org.apache.derby.iapi.types.JSQLType;
030:
031: import org.apache.derby.iapi.types.DataValueDescriptor;
032: import org.apache.derby.iapi.types.DataTypeDescriptor;
033:
034: import org.apache.derby.iapi.sql.compile.TypeCompiler;
035:
036: import org.apache.derby.iapi.sql.Activation;
037:
038: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
039: import org.apache.derby.iapi.error.StandardException;
040: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
041: import org.apache.derby.iapi.sql.compile.Visitable;
042: import org.apache.derby.iapi.sql.compile.Visitor;
043:
044: import org.apache.derby.iapi.reference.ClassName;
045:
046: import org.apache.derby.iapi.util.JBitSet;
047: import org.apache.derby.iapi.services.classfile.VMOpcode;
048:
049: import java.lang.reflect.Modifier;
050:
051: import java.util.Vector;
052:
053: /**
054: * This node type converts a value in the SQL domain to a value in the Java
055: * domain.
056: */
057:
058: public class SQLToJavaValueNode extends JavaValueNode {
059: ValueNode value;
060:
061: LocalField returnsNullOnNullState;
062:
063: /**
064: * Constructor for a SQLToJavaValueNode
065: *
066: * @param value A ValueNode representing a SQL value to convert to
067: * the Java domain.
068: */
069:
070: public void init(Object value) {
071: this .value = (ValueNode) value;
072: }
073:
074: /**
075: * Prints the sub-nodes of this object. See QueryTreeNode.java for
076: * how tree printing is supposed to work.
077: *
078: * @param depth The depth of this node in the tree
079: */
080:
081: public void printSubNodes(int depth) {
082: if (SanityManager.DEBUG) {
083: int parm;
084:
085: super .printSubNodes(depth);
086: if (value != null) {
087: printLabel(depth, "value: ");
088: value.treePrint(depth + 1);
089: }
090: }
091: }
092:
093: /**
094: * Returns the name of the java class type that this node coerces to.
095: *
096: * @return name of java class type
097: *
098: */
099: public String getJavaTypeName() throws StandardException {
100: JSQLType myType = getJSQLType();
101:
102: if (myType == null) {
103: return "";
104: } else {
105: return mapToTypeID(myType).getCorrespondingJavaTypeName();
106: }
107: }
108:
109: /**
110: * Returns the name of the java primitive type that this node coerces to.
111: *
112: * @return name of java primitive type
113: *
114: * @exception StandardException Thrown on error
115: */
116: public String getPrimitiveTypeName() throws StandardException {
117: JSQLType myType = getJSQLType();
118:
119: if (myType == null) {
120: return "";
121: } else {
122: return getTypeCompiler(mapToTypeID(myType))
123: .getCorrespondingPrimitiveTypeName();
124: }
125: }
126:
127: /**
128: * Get the JSQLType that corresponds to this node. Could be a SQLTYPE,
129: * a Java primitive, or a Java class.
130: *
131: * Overrides method in JavaValueNode.
132: *
133: * @return the corresponding JSQLType
134: *
135: */
136: public JSQLType getJSQLType() throws StandardException {
137: if (jsqlType == null) {
138: if (value.requiresTypeFromContext()) {
139: ParameterNode pn;
140: if (value instanceof UnaryOperatorNode)
141: pn = ((UnaryOperatorNode) value)
142: .getParameterOperand();
143: else
144: pn = (ParameterNode) (value);
145: jsqlType = pn.getJSQLType();
146: } else {
147: DataTypeDescriptor dtd = value.getTypeServices();
148: if (dtd != null)
149: jsqlType = new JSQLType(dtd);
150: }
151: }
152:
153: return jsqlType;
154: }
155:
156: /**
157: * Set the clause that this node appears in.
158: *
159: * @param clause The clause that this node appears in.
160: */
161: public void setClause(int clause) {
162: super .setClause(clause);
163: value.setClause(clause);
164: }
165:
166: /**
167: * Bind this expression. This means binding the sub-expressions,
168: * as well as figuring out what the return type is for this expression.
169: *
170: * @param fromList The FROM list for the query this
171: * expression is in, for binding columns.
172: * @param subqueryList The subquery list being built as we find
173: * SubqueryNodes
174: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
175: *
176: * @return this
177: *
178: * @exception StandardException Thrown on error
179: */
180:
181: public JavaValueNode bindExpression(FromList fromList,
182: SubqueryList subqueryList, Vector aggregateVector)
183: throws StandardException {
184: /* Bind the expression under us */
185: value = value.bindExpression(fromList, subqueryList,
186: aggregateVector);
187:
188: return this ;
189: }
190:
191: /**
192: * Remap all ColumnReferences in this tree to be clones of the
193: * underlying expression.
194: *
195: * @return JavaValueNode The remapped expression tree.
196: *
197: * @exception StandardException Thrown on error
198: */
199: public JavaValueNode remapColumnReferencesToExpressions()
200: throws StandardException {
201: value = value.remapColumnReferencesToExpressions();
202: return this ;
203: }
204:
205: /**
206: * Categorize this predicate. Initially, this means
207: * building a bit map of the referenced tables for each predicate.
208: * If the source of this ColumnReference (at the next underlying level)
209: * is not a ColumnReference or a VirtualColumnNode then this predicate
210: * will not be pushed down.
211: *
212: * For example, in:
213: * select * from (select 1 from s) a (x) where x = 1
214: * we will not push down x = 1.
215: * NOTE: It would be easy to handle the case of a constant, but if the
216: * inner SELECT returns an arbitrary expression, then we would have to copy
217: * that tree into the pushed predicate, and that tree could contain
218: * subqueries and method calls.
219: * RESOLVE - revisit this issue once we have views.
220: *
221: * @param referencedTabs JBitSet with bit map of referenced FromTables
222: * @param simplePredsOnly Whether or not to consider method
223: * calls, field references and conditional nodes
224: * when building bit map
225: *
226: * @return boolean Whether or not source.expression is a ColumnReference
227: * or a VirtualColumnNode.
228: *
229: * @exception StandardException Thrown on error
230: */
231: public boolean categorize(JBitSet referencedTabs,
232: boolean simplePredsOnly) throws StandardException {
233: return value.categorize(referencedTabs, simplePredsOnly);
234: }
235:
236: /**
237: * Preprocess an expression tree. We do a number of transformations
238: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
239: * subquery flattening.
240: * NOTE: This is done before the outer ResultSetNode is preprocessed.
241: *
242: * @param numTables Number of tables in the DML Statement
243: * @param outerFromList FromList from outer query block
244: * @param outerSubqueryList SubqueryList from outer query block
245: * @param outerPredicateList PredicateList from outer query block
246: *
247: * @exception StandardException Thrown on error
248: */
249: public void preprocess(int numTables, FromList outerFromList,
250: SubqueryList outerSubqueryList,
251: PredicateList outerPredicateList) throws StandardException {
252: value.preprocess(numTables, outerFromList, outerSubqueryList,
253: outerPredicateList);
254: }
255:
256: /**
257: * Return the variant type for the underlying expression.
258: * The variant type can be:
259: * VARIANT - variant within a scan
260: * (method calls and non-static field access)
261: * SCAN_INVARIANT - invariant within a scan
262: * (column references from outer tables)
263: * QUERY_INVARIANT - invariant within the life of a query
264: * (constant expressions)
265: *
266: * @return The variant type for the underlying expression.
267: * @exception StandardException thrown on error
268: */
269: protected int getOrderableVariantType() throws StandardException {
270: return value.getOrderableVariantType();
271: }
272:
273: ///////////////////////////////////////////////////////////////////////
274: //
275: // CODE GENERATION METHODS
276: //
277: ///////////////////////////////////////////////////////////////////////
278:
279: /**
280: * Generate code to get the Java value out of a SQL value.
281: *
282: * Every SQL type has a corresponding Java type. The getObject() method
283: * on the SQL type gets the right Java type.
284: *
285: * The generated code will be:
286: *
287: * (<Java type name>) ((DataValueDescriptor)
288: * <generated value>.getObject())
289: *
290: * where <Java type name> comes from the getCorrespondingJavaTypeName()
291: * method of the value's TypeId.
292: *
293: * @param acb The ExpressionClassBuilder for the class being built
294: * @param mb The method the expression will go into
295: *
296: *
297: * @exception StandardException Thrown on error
298: */
299:
300: public void generateExpression(ExpressionClassBuilder acb,
301: MethodBuilder mb) throws StandardException {
302: /* Compile the expression under us */
303: generateSQLValue(acb, mb);
304:
305: /* now cast the SQLValue to a Java value */
306: generateJavaValue(acb, mb);
307: }
308:
309: /**
310: * Generate the SQLvalue that this node wraps.
311: *
312: * @param acb The ExpressionClassBuilder for the class being built
313: * @param mb The method the expression will go into
314: *
315: *
316: * @exception StandardException Thrown on error
317: */
318:
319: public void generateSQLValue(ExpressionClassBuilder acb,
320: MethodBuilder mb) throws StandardException {
321: value.generateExpression(acb, mb);
322: }
323:
324: /**
325: * Generate code to cast the SQLValue to a Java value.
326: *
327: *
328: * @param acb The ExpressionClassBuilder for the class being built
329: * @param mbex The method the expression will go into
330: *
331: *
332: * @exception StandardException Thrown on error
333: */
334:
335: public void generateJavaValue(ExpressionClassBuilder acb,
336: MethodBuilder mbex) throws StandardException {
337: /* If this is a conversion to a primitive type, then call the
338: * appropriate method for getting the primitive value and
339: * cast it to the primitive type.
340: * NOTE: We first call Activation.nullToPrimitiveTest(),
341: * which will throw a StandardException if the value is null
342: */
343: if (isPrimitiveType() || mustCastToPrimitive()) {
344: String primitiveTN = value.getTypeCompiler()
345: .getCorrespondingPrimitiveTypeName();
346:
347: /* Put the code to check if the object is null and to
348: * get the primitive value in a method call. This is
349: * necessary because we are generating an expression here and
350: * cannot have multiple statements.
351: * The method call will take SQLValue as a parameter.
352: */
353: String[] pd = new String[1];
354: pd[0] = getSQLValueInterfaceName(); // parameter "param1"
355:
356: MethodBuilder mb = acb.newGeneratedFun(primitiveTN,
357: Modifier.PRIVATE, pd);
358:
359: mb.getParameter(0);
360:
361: if (returnsNullOnNullState != null) {
362: generateReturnsNullOnNullCheck(mb);
363: } else {
364: mb.dup();
365: mb.upCast(ClassName.DataValueDescriptor);
366: mb.push(primitiveTN);
367: mb.callMethod(VMOpcode.INVOKESTATIC,
368: ClassName.BaseActivation,
369: "nullToPrimitiveTest", "void", 2);
370: }
371:
372: // stack is dvd
373:
374: /* Generate the code to get the primitive value */
375: mb.callMethod(VMOpcode.INVOKEINTERFACE,
376: ClassName.DataValueDescriptor,
377: value.getTypeCompiler().getPrimitiveMethodName(),
378: primitiveTN, 0);
379:
380: mb.methodReturn();
381: mb.complete();
382:
383: /* Generate the call to the new method, with the parameter */
384:
385: mbex.pushThis();
386: mbex.swap(); // caller pushed out parameter
387: mbex.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mb
388: .getName(), primitiveTN, 1);
389: } else {
390: if (returnsNullOnNullState != null)
391: generateReturnsNullOnNullCheck(mbex);
392:
393: /* Call getObject() to get the right type of Java value */
394: mbex.callMethod(VMOpcode.INVOKEINTERFACE,
395: ClassName.DataValueDescriptor, "getObject",
396: "java.lang.Object", 0);
397:
398: mbex.cast(value.getTypeId().getCorrespondingJavaTypeName());
399: }
400: }
401:
402: /**
403: Generate the code for the returns Null on Null input check..
404: Stack must contain the DataDescriptorValue.
405: */
406:
407: private void generateReturnsNullOnNullCheck(MethodBuilder mb) {
408: mb.dup();
409: mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Storable,
410: "isNull", "boolean", 0);
411:
412: mb.conditionalIf();
413: mb.push(true);
414: mb.startElseCode();
415: mb.getField(returnsNullOnNullState);
416: mb.completeConditional();
417:
418: mb.setField(returnsNullOnNullState);
419: }
420:
421: /**
422: * Get the type name of the SQLValue we generate.
423: *
424: * @return name of interface corresponding to SQLValue
425: *
426: *
427: * @exception StandardException Thrown on error
428: */
429: public String getSQLValueInterfaceName() throws StandardException {
430: return value.getTypeCompiler().interfaceName();
431: }
432:
433: ///////////////////////////////////////////////////////////////////////
434: //
435: // OTHER VALUE NODE METHODS
436: //
437: ///////////////////////////////////////////////////////////////////////
438:
439: /**
440: * Get the SQL ValueNode that is being converted to a JavaValueNode
441: *
442: * @return The underlying SQL ValueNode
443: */
444: ValueNode getSQLValueNode() {
445: return value;
446: }
447:
448: /** @see ValueNode#getConstantValueAsObject
449: *
450: * @exception StandardException Thrown on error
451: */
452: Object getConstantValueAsObject() throws StandardException {
453: return value.getConstantValueAsObject();
454: }
455:
456: /**
457: * Accept a visitor, and call v.visit()
458: * on child nodes as necessary.
459: *
460: * @param v the visitor
461: *
462: * @exception StandardException on error
463: */
464: public Visitable accept(Visitor v) throws StandardException {
465: Visitable returnNode = v.visit(this );
466:
467: if (v.skipChildren(this )) {
468: return returnNode;
469: }
470:
471: if (value != null && !v.stopTraversal()) {
472: value = (ValueNode) value.accept(v);
473: }
474:
475: return returnNode;
476: }
477: }
|