001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.ConditionalNode
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:
026: import org.apache.derby.iapi.services.monitor.Monitor;
027:
028: import org.apache.derby.iapi.services.sanity.SanityManager;
029:
030: import org.apache.derby.iapi.error.StandardException;
031:
032: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
033:
034: import org.apache.derby.iapi.types.TypeId;
035:
036: import org.apache.derby.iapi.types.BooleanDataValue;
037: import org.apache.derby.iapi.types.DataTypeDescriptor;
038: import org.apache.derby.iapi.types.DataValueFactory;
039:
040: import org.apache.derby.iapi.reference.SQLState;
041:
042: import org.apache.derby.iapi.types.DataValueDescriptor;
043: import org.apache.derby.iapi.types.TypeId;
044:
045: import org.apache.derby.iapi.services.loader.ClassInspector;
046:
047: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
048:
049: import org.apache.derby.iapi.sql.compile.Visitable;
050: import org.apache.derby.iapi.sql.compile.Visitor;
051: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
052: import org.apache.derby.iapi.reference.ClassName;
053:
054: import org.apache.derby.iapi.util.JBitSet;
055: import org.apache.derby.iapi.services.classfile.VMOpcode;
056:
057: import java.util.Vector;
058:
059: /**
060: * A ConditionalNode represents an if/then/else operator with a single
061: * boolean expression on the "left" of the operator and a list of expressions on
062: * the "right". This is used to represent the java conditional (aka immediate if).
063: *
064: * @author Jerry Brenner
065: */
066:
067: public class ConditionalNode extends ValueNode {
068: ValueNode testCondition;
069: ValueNodeList thenElseList;
070: //true means we are here for NULLIF(V1,V2), false means we are here for following
071: //CASE WHEN BooleanExpression THEN thenExpression ELSE elseExpression END
072: boolean this IsNullIfNode;
073:
074: /**
075: * Initializer for a ConditionalNode
076: *
077: * @param testCondition The boolean test condition
078: * @param thenElseList ValueNodeList with then and else expressions
079: */
080:
081: public void init(Object testCondition, Object thenElseList,
082: Object this IsNullIfNode) {
083: this .testCondition = (ValueNode) testCondition;
084: this .thenElseList = (ValueNodeList) thenElseList;
085: this .this IsNullIfNode = ((Boolean) this IsNullIfNode)
086: .booleanValue();
087: }
088:
089: /**
090: * Prints the sub-nodes of this object. See QueryTreeNode.java for
091: * how tree printing is supposed to work.
092: *
093: * @param depth The depth of this node in the tree
094: */
095:
096: public void printSubNodes(int depth) {
097: if (SanityManager.DEBUG) {
098: super .printSubNodes(depth);
099:
100: if (testCondition != null) {
101: printLabel(depth, "testCondition: ");
102: testCondition.treePrint(depth + 1);
103: }
104:
105: if (thenElseList != null) {
106: printLabel(depth, "thenElseList: ");
107: thenElseList.treePrint(depth + 1);
108: }
109: }
110: }
111:
112: /**
113: * Set the clause that this node appears in.
114: *
115: * @param clause The clause that this node appears in.
116: */
117: public void setClause(int clause) {
118: super .setClause(clause);
119: testCondition.setClause(clause);
120: thenElseList.setClause(clause);
121: }
122:
123: /**
124: * Bind this expression. This means binding the sub-expressions,
125: * as well as figuring out what the return type is for this expression.
126: *
127: * @param fromList The FROM list for the query this
128: * expression is in, for binding columns.
129: * @param subqueryList The subquery list being built as we find SubqueryNodes
130: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
131: *
132: * @return The new top of the expression tree.
133: *
134: * @exception StandardException Thrown on error
135: */
136:
137: public ValueNode bindExpression(FromList fromList,
138: SubqueryList subqueryList, Vector aggregateVector)
139: throws StandardException {
140: testCondition = testCondition.bindExpression(fromList,
141: subqueryList, aggregateVector);
142:
143: if (this IsNullIfNode) {
144: //for NULLIF(V1,V2), parser binds thenElseList.elementAt(0) to untyped NULL
145: //At bind phase, we should bind it to the type of V1 since now we know the
146: //type of V1
147: BinaryComparisonOperatorNode bcon = (BinaryComparisonOperatorNode) testCondition;
148:
149: /*
150: * NULLIF(V1,V2) is equivalent to:
151: *
152: * CASE WHEN V1=V2 THEN NULL ELSE V1 END
153: *
154: * The untyped NULL should have a data type descriptor
155: * that allows its value to be nullable.
156: */
157: QueryTreeNode cast = getNodeFactory().getNode(
158: C_NodeTypes.CAST_NODE,
159: thenElseList.elementAt(0),
160: new DataTypeDescriptor(bcon.getLeftOperand()
161: .getTypeServices(), true),
162: getContextManager());
163: thenElseList.setElementAt(cast, 0);
164: }
165: thenElseList.bindExpression(fromList, subqueryList,
166: aggregateVector);
167:
168: // Can't get the then and else expressions until after they've been bound
169: ValueNode thenExpression = (ValueNode) thenElseList
170: .elementAt(0);
171: ValueNode elseExpression = (ValueNode) thenElseList
172: .elementAt(1);
173:
174: /* testCondition must be a boolean expression.
175: * If it is a ? parameter on the left, then set type to boolean,
176: * otherwise verify that the result type is boolean.
177: */
178: if (testCondition.requiresTypeFromContext()) {
179: testCondition.setType(new DataTypeDescriptor(
180: TypeId.BOOLEAN_ID, true));
181: } else {
182: if (!testCondition.getTypeServices().getTypeId().equals(
183: TypeId.BOOLEAN_ID)) {
184: throw StandardException
185: .newException(SQLState.LANG_CONDITIONAL_NON_BOOLEAN);
186: }
187: }
188:
189: /* We can't determine the type for the result expression if
190: * all result expressions are ?s.
191: */
192: if (thenElseList.containsAllParameterNodes()) {
193: throw StandardException.newException(
194: SQLState.LANG_ALL_RESULT_EXPRESSIONS_PARAMS,
195: "conditional");
196: } else if (thenElseList.containsParameterNode()) {
197: /* Set the parameter's type to be the same as the other element in
198: * the list
199: */
200:
201: DataTypeDescriptor dts;
202: ValueNode typeExpression;
203:
204: if (thenExpression.requiresTypeFromContext()) {
205: dts = elseExpression.getTypeServices();
206: } else {
207: dts = thenExpression.getTypeServices();
208: }
209:
210: thenElseList.setParameterDescriptor(dts);
211: }
212:
213: /* The then and else expressions must be type compatible */
214: ClassInspector cu = getClassFactory().getClassInspector();
215:
216: /*
217: ** If it is comparable, then we are ok. Note that we
218: ** could in fact allow any expressions that are convertible()
219: ** since we are going to generate a cast node, but that might
220: ** be confusing to users...
221: */
222:
223: // RESOLVE DJDOI - this looks wrong, why should the then expression
224: // be comparable to the then expression ??
225: if (!thenExpression.getTypeCompiler().comparable(
226: elseExpression.getTypeId(), false, getClassFactory())
227: && !cu.assignableTo(thenExpression.getTypeId()
228: .getCorrespondingJavaTypeName(), elseExpression
229: .getTypeId().getCorrespondingJavaTypeName())
230: && !cu.assignableTo(elseExpression.getTypeId()
231: .getCorrespondingJavaTypeName(), thenExpression
232: .getTypeId().getCorrespondingJavaTypeName())) {
233: throw StandardException.newException(
234: SQLState.LANG_NOT_TYPE_COMPATIBLE, thenExpression
235: .getTypeId().getSQLTypeName(),
236: elseExpression.getTypeId().getSQLTypeName());
237: }
238:
239: /*
240: ** Set the result type of this conditional to be the dominant type
241: ** of the result expressions.
242: */
243: setType(thenElseList.getDominantTypeServices());
244:
245: /*
246: ** Generate a CastNode if necessary and
247: ** stick it over the original expression
248: */
249: TypeId condTypeId = getTypeId();
250: TypeId thenTypeId = ((ValueNode) thenElseList.elementAt(0))
251: .getTypeId();
252: TypeId elseTypeId = ((ValueNode) thenElseList.elementAt(1))
253: .getTypeId();
254:
255: /* Need to generate conversion if thenExpr or elseExpr is not of
256: * dominant type. (At least 1 of them must be of the dominant type.)
257: */
258: if (thenTypeId.typePrecedence() != condTypeId.typePrecedence()) {
259: ValueNode cast = (ValueNode) getNodeFactory().getNode(
260: C_NodeTypes.CAST_NODE, thenElseList.elementAt(0),
261: dataTypeServices, // cast to dominant type
262: getContextManager());
263: cast = cast.bindExpression(fromList, subqueryList,
264: aggregateVector);
265:
266: thenElseList.setElementAt(cast, 0);
267: }
268:
269: else if (elseTypeId.typePrecedence() != condTypeId
270: .typePrecedence()) {
271: ValueNode cast = (ValueNode) getNodeFactory().getNode(
272: C_NodeTypes.CAST_NODE, thenElseList.elementAt(1),
273: dataTypeServices, // cast to dominant type
274: getContextManager());
275: cast = cast.bindExpression(fromList, subqueryList,
276: aggregateVector);
277:
278: thenElseList.setElementAt(cast, 1);
279: }
280:
281: return this ;
282: }
283:
284: /**
285: * Preprocess an expression tree. We do a number of transformations
286: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
287: * subquery flattening.
288: * NOTE: This is done before the outer ResultSetNode is preprocessed.
289: *
290: * @param numTables Number of tables in the DML Statement
291: * @param outerFromList FromList from outer query block
292: * @param outerSubqueryList SubqueryList from outer query block
293: * @param outerPredicateList PredicateList from outer query block
294: *
295: * @return The modified expression
296: *
297: * @exception StandardException Thrown on error
298: */
299: public ValueNode preprocess(int numTables, FromList outerFromList,
300: SubqueryList outerSubqueryList,
301: PredicateList outerPredicateList) throws StandardException {
302: testCondition = testCondition.preprocess(numTables,
303: outerFromList, outerSubqueryList, outerPredicateList);
304: thenElseList.preprocess(numTables, outerFromList,
305: outerSubqueryList, outerPredicateList);
306: return this ;
307: }
308:
309: /**
310: * Categorize this predicate. Initially, this means
311: * building a bit map of the referenced tables for each predicate.
312: * If the source of this ColumnReference (at the next underlying level)
313: * is not a ColumnReference or a VirtualColumnNode then this predicate
314: * will not be pushed down.
315: *
316: * For example, in:
317: * select * from (select 1 from s) a (x) where x = 1
318: * we will not push down x = 1.
319: * NOTE: It would be easy to handle the case of a constant, but if the
320: * inner SELECT returns an arbitrary expression, then we would have to copy
321: * that tree into the pushed predicate, and that tree could contain
322: * subqueries and method calls.
323: * RESOLVE - revisit this issue once we have views.
324: *
325: * @param referencedTabs JBitSet with bit map of referenced FromTables
326: * @param simplePredsOnly Whether or not to consider method
327: * calls, field references and conditional nodes
328: * when building bit map
329: *
330: * @return boolean Whether or not source.expression is a ColumnReference
331: * or a VirtualColumnNode.
332: * @exception StandardException Thrown on error
333: */
334: public boolean categorize(JBitSet referencedTabs,
335: boolean simplePredsOnly) throws StandardException {
336: /* We stop here when only considering simple predicates
337: * as we don't consider conditional operators when looking
338: * for null invariant predicates.
339: */
340: if (simplePredsOnly) {
341: return false;
342: }
343:
344: boolean pushable;
345:
346: pushable = testCondition.categorize(referencedTabs,
347: simplePredsOnly);
348: pushable = (thenElseList.categorize(referencedTabs,
349: simplePredsOnly) && pushable);
350: return pushable;
351: }
352:
353: /**
354: * Remap all ColumnReferences in this tree to be clones of the
355: * underlying expression.
356: *
357: * @return ValueNode The remapped expression tree.
358: *
359: * @exception StandardException Thrown on error
360: */
361: public ValueNode remapColumnReferencesToExpressions()
362: throws StandardException {
363: testCondition = testCondition
364: .remapColumnReferencesToExpressions();
365: thenElseList = thenElseList
366: .remapColumnReferencesToExpressions();
367: return this ;
368: }
369:
370: /**
371: * Return whether or not this expression tree represents a constant expression.
372: *
373: * @return Whether or not this expression tree represents a constant expression.
374: */
375: public boolean isConstantExpression() {
376: return (testCondition.isConstantExpression() && thenElseList
377: .isConstantExpression());
378: }
379:
380: /** @see ValueNode#constantExpression */
381: public boolean constantExpression(PredicateList whereClause) {
382: return (testCondition.constantExpression(whereClause) && thenElseList
383: .constantExpression(whereClause));
384: }
385:
386: /**
387: * Eliminate NotNodes in the current query block. We traverse the tree,
388: * inverting ANDs and ORs and eliminating NOTs as we go. We stop at
389: * ComparisonOperators and boolean expressions. We invert
390: * ComparisonOperators and replace boolean expressions with
391: * boolean expression = false.
392: * NOTE: Since we do not recurse under ComparisonOperators, there
393: * still could be NotNodes left in the tree.
394: *
395: * @param underNotNode Whether or not we are under a NotNode.
396: *
397: *
398: * @return The modified expression
399: *
400: * @exception StandardException Thrown on error
401: */
402: ValueNode eliminateNots(boolean underNotNode)
403: throws StandardException {
404: ValueNode thenExpression;
405: ValueNode elseExpression;
406:
407: if (!underNotNode) {
408: return this ;
409: }
410:
411: /* Simply swap the then and else expressions */
412: thenExpression = (ValueNode) thenElseList.elementAt(0);
413: elseExpression = (ValueNode) thenElseList.elementAt(1);
414: thenElseList.setElementAt(elseExpression, 0);
415: thenElseList.setElementAt(thenExpression, 1);
416:
417: return this ;
418: }
419:
420: /**
421: * Do code generation for this conditional expression.
422: *
423: * @param acb The ExpressionClassBuilder for the class we're generating
424: * @param mb The method the expression will go into
425: *
426: * @exception StandardException Thrown on error
427: */
428:
429: public void generateExpression(ExpressionClassBuilder acb,
430: MethodBuilder mb) throws StandardException {
431: testCondition.generateExpression(acb, mb);
432: mb.cast(ClassName.BooleanDataValue);
433: mb.push(true);
434: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
435: "equals", "boolean", 1);
436:
437: mb.conditionalIf();
438: ((ValueNode) thenElseList.elementAt(0)).generateExpression(acb,
439: mb);
440: mb.startElseCode();
441: ((ValueNode) thenElseList.elementAt(1)).generateExpression(acb,
442: mb);
443: mb.completeConditional();
444: }
445:
446: /**
447: * Accept a visitor, and call v.visit()
448: * on child nodes as necessary.
449: *
450: * @param v the visitor
451: *
452: * @exception StandardException on error
453: */
454: public Visitable accept(Visitor v) throws StandardException {
455: Visitable returnNode = v.visit(this );
456:
457: if (v.skipChildren(this )) {
458: return returnNode;
459: }
460:
461: if (testCondition != null && !v.stopTraversal()) {
462: testCondition = (ValueNode) testCondition.accept(v);
463: }
464:
465: if (thenElseList != null && !v.stopTraversal()) {
466: thenElseList = (ValueNodeList) thenElseList.accept(v);
467: }
468:
469: return returnNode;
470: }
471:
472: /**
473: * {@inheritDoc}
474: */
475: protected boolean isEquivalent(ValueNode o)
476: throws StandardException {
477: if (isSameNodeType(o)) {
478: ConditionalNode other = (ConditionalNode) o;
479: if (thenElseList.size() == other.thenElseList.size()
480: && (testCondition.isEquivalent(other.testCondition))) {
481: int sz = thenElseList.size();
482: for (int i = 0; i < sz; i++) {
483: ValueNode v1 = (ValueNode) thenElseList
484: .elementAt(i);
485: ValueNode v2 = (ValueNode) other.thenElseList
486: .elementAt(i);
487: if (!v1.isEquivalent(v2)) {
488: return false;
489: }
490:
491: }
492: return true;
493: }
494: }
495: return false;
496: }
497: }
|