001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.BinaryLogicalOperatorNode
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.sql.dictionary.DataDictionary;
025:
026: import org.apache.derby.iapi.types.BooleanDataValue;
027: import org.apache.derby.iapi.types.TypeId;
028:
029: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
030:
031: import org.apache.derby.iapi.error.StandardException;
032: import org.apache.derby.iapi.services.compiler.MethodBuilder;
033: import org.apache.derby.iapi.services.compiler.LocalField;
034:
035: import org.apache.derby.iapi.services.sanity.SanityManager;
036: import org.apache.derby.iapi.reference.ClassName;
037: import org.apache.derby.iapi.reference.SQLState;
038: import org.apache.derby.iapi.types.DataTypeDescriptor;
039:
040: import java.lang.reflect.Modifier;
041: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
042: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
043: import org.apache.derby.iapi.services.classfile.VMOpcode;
044:
045: import java.util.Vector;
046:
047: abstract class BinaryLogicalOperatorNode extends BinaryOperatorNode {
048: boolean shortCircuitValue;
049:
050: /**
051: * Initializer for a BinaryLogicalOperatorNode
052: *
053: * @param leftOperand The left operand of the comparison
054: * @param rightOperand The right operand of the comparison
055: * @param methodName The name of the method to call in the generated
056: * class. In this case, it's actually an operator
057: * name.
058: */
059:
060: public void init(Object leftOperand, Object rightOperand,
061: Object methodName) {
062: /* For logical operators, the operator and method names are the same */
063: super .init(leftOperand, rightOperand, methodName, methodName,
064: ClassName.BooleanDataValue, ClassName.BooleanDataValue);
065: }
066:
067: /**
068: * Bind this logical operator. All that has to be done for binding
069: * a logical operator is to bind the operands, check that both operands
070: * are BooleanDataValue, and set the result type to BooleanDataValue.
071: *
072: * @param fromList The query's FROM list
073: * @param subqueryList The subquery list being built as we find SubqueryNodes
074: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
075: *
076: * @return The new top of the expression tree.
077: *
078: * @exception StandardException Thrown on error
079: */
080:
081: public ValueNode bindExpression(FromList fromList,
082: SubqueryList subqueryList, Vector aggregateVector)
083: throws StandardException {
084: //following is to check if we have something like "? AND 1=1" or "2>1 OR ?"
085: if (leftOperand.isParameterNode()
086: || rightOperand.isParameterNode())
087: throw StandardException
088: .newException(
089: SQLState.LANG_NON_BOOLEAN_WHERE_CLAUSE,
090: "PARAMETER");
091: //following 2 ifs are to check if we have something like "+? AND 1=1" or "2>1 OR -?" ie -?/+? by themselves
092: if ((leftOperand instanceof UnaryOperatorNode)
093: && ((UnaryOperatorNode) leftOperand)
094: .isUnaryMinusOrPlusWithParameter())
095: throw StandardException
096: .newException(
097: SQLState.LANG_NON_BOOLEAN_WHERE_CLAUSE,
098: "PARAMETER");
099: if ((rightOperand instanceof UnaryOperatorNode)
100: && ((UnaryOperatorNode) rightOperand)
101: .isUnaryMinusOrPlusWithParameter())
102: throw StandardException
103: .newException(
104: SQLState.LANG_NON_BOOLEAN_WHERE_CLAUSE,
105: "PARAMETER");
106:
107: super .bindExpression(fromList, subqueryList, aggregateVector);
108:
109: return this ;
110: }
111:
112: /**
113: * Verify that eliminateNots() did its job correctly. Verify that
114: * there are no NotNodes above the top level comparison operators
115: * and boolean expressions.
116: *
117: * @return Boolean which reflects validity of the tree.
118: */
119: boolean verifyEliminateNots() {
120: if (SanityManager.ASSERT) {
121: return (leftOperand.verifyEliminateNots() && rightOperand
122: .verifyEliminateNots());
123: } else {
124: return true;
125: }
126: }
127:
128: /**
129: * Do code generation for this logical binary operator.
130: * This is used for AND and OR. the IsNode extends this class but
131: * overrides generateExpression.
132: *
133: * @param acb The ExpressionClassBuilder for the class we're generating
134: * @param mb The method the code to place the code
135: *
136: *
137: * @exception StandardException Thrown on error
138: */
139:
140: public void generateExpression(ExpressionClassBuilder acb,
141: MethodBuilder mb) throws StandardException {
142: /*
143: ** This generates the following code:
144: **
145: ** (<leftOperand>.equals(shortCircuitValue) ?
146: ** <leftOperand> :
147: ** <leftOperand>.<and/or>(<rightOperand>)
148: **
149: ** The ?: operator accomplishes the short-circuiting. We save the
150: ** value of the left operand on the stack so we don't have to evaluate
151: ** it twice.
152: **
153: ** The BooleanDataValue.{and,or} methods return an immutable BooleanDataValue
154: ** and an immutable BooleanDataValue is returned by this generated code in
155: ** the short circuit case.
156: */
157:
158: /*
159: ** See whether the left operand equals the short-circuit value.
160: ** Generated code is:
161: ** .equals(shortCircuitValue)
162: */
163:
164: leftOperand.generateExpression(acb, mb);
165: // stack - left
166:
167: // put an extra left of the stack for potential
168: // use in the else clause.
169: mb.dup();
170: // stack - left, left
171: mb.push(shortCircuitValue);
172: // stack - left, left, shortcircuit
173: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
174: "equals", "boolean", 1);
175: // stack left, result
176:
177: /*
178: ** Generate the if expression. This is what accomplishes
179: ** short-circuiting.
180: **
181: ** Generated code is:
182: **
183: ** <test for short circuiting> ?
184: ** <call to BooleanDataValue.getImmutable> : <call to operator method>
185: **
186: ** For AND short circuiting shortcircuit value will be false, so that
187: ** if left is false, no need to evaluate the right and the result will be false.
188: **
189: ** For OR short circuiting shortcircuit value will be true, so that
190: ** if left is true, no need to to evaluate the right and the result will be true.
191: **
192: ** In both cases the result is the same as the left operand.
193: **
194: ** TODO: Could short circuit when the left value is NULL as well. Then
195: ** the result would be NULL in either case and still equal to the left value.
196: ** This would require a different check on the conditional.
197: */
198:
199: mb.conditionalIf();
200:
201: // stack: left
202: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
203: "getImmutable", ClassName.BooleanDataValue, 0);
204:
205: // stack: result (matching left)
206:
207: mb.startElseCode();
208:
209: /*
210: ** Generate the return value if the left operand does not equal the
211: ** short-circuit value. This is the call to "and" or "or".
212: **
213: ** Generated code is:
214: **
215: ** <fieldx>.<methodName>(<rightOperand>)
216: */
217:
218: // stack: left
219: rightOperand.generateExpression(acb, mb);
220:
221: // stack: left, right
222: mb.upCast(ClassName.BooleanDataValue);
223:
224: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
225: methodName, ClassName.BooleanDataValue, 1);
226: // stack: result(left op right)
227:
228: mb.completeConditional();
229: // stack: result
230:
231: }
232:
233: DataTypeDescriptor resolveLogicalBinaryOperator(
234: DataTypeDescriptor leftType, DataTypeDescriptor rightType)
235: throws StandardException {
236: if ((!(leftType.getTypeId().isBooleanTypeId()))
237: || (!(rightType.getTypeId().isBooleanTypeId()))) {
238: throw StandardException
239: .newException(SQLState.LANG_BINARY_LOGICAL_NON_BOOLEAN);
240: }
241:
242: return new DataTypeDescriptor(leftType, leftType.isNullable()
243: || rightType.isNullable());
244: }
245: }
|