001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.BinaryComparisonOperatorNode
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.compile.C_NodeTypes;
025: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
026:
027: import org.apache.derby.iapi.sql.compile.TypeCompiler;
028: import org.apache.derby.iapi.types.DataValueDescriptor;
029: import org.apache.derby.iapi.types.TypeId;
030: import org.apache.derby.iapi.types.DataTypeDescriptor;
031:
032: import org.apache.derby.iapi.reference.SQLState;
033: import org.apache.derby.iapi.reference.ClassName;
034: import org.apache.derby.iapi.error.StandardException;
035:
036: import org.apache.derby.iapi.services.sanity.SanityManager;
037:
038: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
039:
040: import java.util.Vector;
041: import java.sql.Types;
042:
043: /**
044: * This node is the superclass for all binary comparison operators, such as =,
045: * <>, <, etc.
046: *
047: * @author Jeff Lichtman
048: */
049:
050: public abstract class BinaryComparisonOperatorNode extends
051: BinaryOperatorNode {
052: // Use between selectivity?
053: private boolean forQueryRewrite;
054: private boolean betweenSelectivity;
055:
056: /**
057: * Initializer for a BinaryComparisonOperatorNode
058: *
059: * @param leftOperand The left operand of the comparison
060: * @param rightOperand The right operand of the comparison
061: * @param operator The name of the operator
062: * @param methodName The name of the method to call in the generated
063: * class
064: */
065:
066: public void init(Object leftOperand, Object rightOperand,
067: Object operator, Object methodName) {
068: super .init(leftOperand, rightOperand, operator, methodName,
069: ClassName.DataValueDescriptor,
070: ClassName.DataValueDescriptor);
071: }
072:
073: /**
074: * This node was generated as part of a query rewrite. Bypass the
075: * normal comparability checks.
076: * @param val true if this was for a query rewrite
077: */
078: public void setForQueryRewrite(boolean val) {
079: forQueryRewrite = val;
080: }
081:
082: /**
083: * Was this node generated in a query rewrite?
084: *
085: * @return true if it was generated in a query rewrite.
086: */
087: public boolean getForQueryRewrite() {
088: return forQueryRewrite;
089: }
090:
091: /**
092: * Use between selectivity when calculating the selectivity.
093: */
094: void setBetweenSelectivity() {
095: betweenSelectivity = true;
096: }
097:
098: /**
099: * Return whether or not to use the between selectivity for this node.
100: *
101: * @return Whether or not to use the between selectivity for this node.
102: */
103: boolean getBetweenSelectivity() {
104: return betweenSelectivity;
105: }
106:
107: /**
108: * Bind this comparison operator. All that has to be done for binding
109: * a comparison operator is to bind the operands, check the compatibility
110: * of the types, and set the result type to SQLBoolean.
111: *
112: * @param fromList The query's FROM list
113: * @param subqueryList The subquery list being built as we find SubqueryNodes
114: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
115: *
116: * @return The new top of the expression tree.
117: *
118: * @exception StandardException Thrown on error
119: */
120:
121: public ValueNode bindExpression(FromList fromList,
122: SubqueryList subqueryList, Vector aggregateVector)
123: throws StandardException {
124: super .bindExpression(fromList, subqueryList, aggregateVector);
125:
126: //RESOLVELOCALIZE - convert constants to national constants
127: TypeCompiler leftTC = leftOperand.getTypeCompiler();
128: TypeCompiler rightTC = rightOperand.getTypeCompiler();
129: TypeId leftTypeId = leftOperand.getTypeId();
130: TypeId rightTypeId = rightOperand.getTypeId();
131:
132: /*
133: * If we are comparing a non-string with a string type, then we
134: * must prevent the non-string value from being used to probe into
135: * an index on a string column. This is because the string types
136: * are all of low precedence, so the comparison rules of the non-string
137: * value are used, so it may not find values in a string index because
138: * it will be in the wrong order. So, cast the string value to its
139: * own type. This is easier than casting it to the non-string type,
140: * because we would have to figure out the right length to cast it to.
141: */
142: if (!leftTypeId.isStringTypeId()
143: && rightTypeId.isStringTypeId()) {
144: DataTypeDescriptor rightTypeServices = rightOperand
145: .getTypeServices();
146:
147: rightOperand = (ValueNode) getNodeFactory().getNode(
148: C_NodeTypes.CAST_NODE,
149: rightOperand,
150: new DataTypeDescriptor(rightTypeId, true,
151: rightTypeServices.getMaximumWidth()),
152: getContextManager());
153: ((CastNode) rightOperand).bindCastNodeOnly();
154: } else if (!rightTypeId.isStringTypeId()
155: && leftTypeId.isStringTypeId()) {
156: DataTypeDescriptor leftTypeServices = leftOperand
157: .getTypeServices();
158:
159: leftOperand = (ValueNode) getNodeFactory().getNode(
160: C_NodeTypes.CAST_NODE,
161: leftOperand,
162: new DataTypeDescriptor(leftTypeId, true,
163: leftTypeServices.getMaximumWidth()),
164: getContextManager());
165: ((CastNode) leftOperand).bindCastNodeOnly();
166: }
167: /* If we are comparing a char with a national char then
168: * we need to generate a cast to the appropriate national
169: * char above the char operand.
170: */
171: else if (!leftTypeId.isNationalStringTypeId()
172: && rightTypeId.isNationalStringTypeId()) {
173: leftOperand = (ValueNode) getNodeFactory().getNode(
174: C_NodeTypes.CAST_NODE,
175: leftOperand,
176: DataTypeDescriptor.getBuiltInDataTypeDescriptor(
177: leftTC.getMatchingNationalCharTypeName(),
178: leftTC.getCastToCharWidth(leftOperand
179: .getTypeServices())),
180: getContextManager());
181: ((CastNode) leftOperand).bindCastNodeOnly();
182: } else if (!rightTypeId.isNationalStringTypeId()
183: && leftTypeId.isNationalStringTypeId()) {
184: rightOperand = (ValueNode) getNodeFactory().getNode(
185: C_NodeTypes.CAST_NODE,
186: rightOperand,
187: DataTypeDescriptor.getBuiltInDataTypeDescriptor(
188: rightTC.getMatchingNationalCharTypeName(),
189: rightTC.getCastToCharWidth(rightOperand
190: .getTypeServices())),
191: getContextManager());
192: ((CastNode) rightOperand).bindCastNodeOnly();
193: }
194:
195: /* Test type compatability and set type info for this node */
196: bindComparisonOperator();
197:
198: return this ;
199: }
200:
201: /**
202: * Test the type compatability of the operands and set the type info
203: * for this node. This method is useful both during binding and
204: * when we generate nodes within the language module outside of the parser.
205: *
206: * @exception StandardException Thrown on error
207: */
208: public void bindComparisonOperator() throws StandardException {
209: TypeId leftType;
210: TypeId rightType;
211: boolean nullableResult;
212:
213: leftType = leftOperand.getTypeId();
214: rightType = rightOperand.getTypeId();
215:
216: /*
217: ** Can the types be compared to each other? If not, throw an
218: ** exception.
219: */
220: boolean forEquals = operator.equals("=")
221: || operator.equals("<>");
222:
223: boolean cmp = leftOperand.getTypeCompiler().comparable(
224: rightType, forEquals, getClassFactory());
225: // Bypass the comparable check if this is a rewrite from the
226: // optimizer. We will assume Mr. Optimizer knows what he is doing.
227: if (!cmp && !forQueryRewrite) {
228: throw StandardException.newException(
229: SQLState.LANG_NOT_COMPARABLE, leftType
230: .getSQLTypeName(), rightType
231: .getSQLTypeName());
232: }
233:
234: /*
235: ** Set the result type of this comparison operator based on the
236: ** operands. The result type is always SQLBoolean - the only question
237: ** is whether it is nullable or not. If either of the operands is
238: ** nullable, the result of the comparison must be nullable, too, so
239: ** we can represent the unknown truth value.
240: */
241: nullableResult = leftOperand.getTypeServices().isNullable()
242: || rightOperand.getTypeServices().isNullable();
243: setType(new DataTypeDescriptor(TypeId.BOOLEAN_ID,
244: nullableResult));
245:
246: }
247:
248: /**
249: * Preprocess an expression tree. We do a number of transformations
250: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
251: * subquery flattening.
252: * NOTE: This is done before the outer ResultSetNode is preprocessed.
253: *
254: * @param numTables Number of tables in the DML Statement
255: * @param outerFromList FromList from outer query block
256: * @param outerSubqueryList SubqueryList from outer query block
257: * @param outerPredicateList PredicateList from outer query block
258: *
259: * @return The modified expression
260: *
261: * @exception StandardException Thrown on error
262: */
263: public ValueNode preprocess(int numTables, FromList outerFromList,
264: SubqueryList outerSubqueryList,
265: PredicateList outerPredicateList) throws StandardException {
266: leftOperand = leftOperand.preprocess(numTables, outerFromList,
267: outerSubqueryList, outerPredicateList);
268:
269: /* This is where we start to consider flattening expression subqueries based
270: * on a uniqueness condition. If the right child is a SubqueryNode then
271: * it is a potentially flattenable expression subquery. If we flatten the
272: * subquery then we at least need to change the right operand of this
273: * comparison. However, we may want to push the comparison into the subquery
274: * itself and replace this outer comparison with TRUE in the tree. Thus we
275: * return rightOperand.preprocess() if the rightOperand is a SubqueryNode.
276: * NOTE: SubqueryNode.preprocess() is smart enough to return this node
277: * if it is not flattenable.
278: * NOTE: We only do this if the subquery has not yet been preprocessed.
279: * (A subquery can get preprocessed multiple times if it is a child node
280: * in an expression that gets transformed, like BETWEEN. The subquery
281: * remembers whether or not it has been preprocessed and simply returns if
282: * it has already been preprocessed. The return returns the SubqueryNode,
283: * so an invalid tree is returned if we set the parent comparison operator
284: * when the subquery has already been preprocessed.)
285: */
286: if ((rightOperand instanceof SubqueryNode)
287: && !((SubqueryNode) rightOperand).getPreprocessed()) {
288: ((SubqueryNode) rightOperand)
289: .setParentComparisonOperator(this );
290: return rightOperand.preprocess(numTables, outerFromList,
291: outerSubqueryList, outerPredicateList);
292: } else {
293: rightOperand = rightOperand.preprocess(numTables,
294: outerFromList, outerSubqueryList,
295: outerPredicateList);
296: return this ;
297: }
298: }
299:
300: /**
301: * Eliminate NotNodes in the current query block. We traverse the tree,
302: * inverting ANDs and ORs and eliminating NOTs as we go. We stop at
303: * ComparisonOperators and boolean expressions. We invert
304: * ComparisonOperators and replace boolean expressions with
305: * boolean expression = false.
306: * NOTE: Since we do not recurse under ComparisonOperators, there
307: * still could be NotNodes left in the tree.
308: *
309: * @param underNotNode Whether or not we are under a NotNode.
310: *
311: *
312: * @return The modified expression
313: *
314: * @exception StandardException Thrown on error
315: */
316: ValueNode eliminateNots(boolean underNotNode)
317: throws StandardException {
318: if (!underNotNode) {
319: return this ;
320: }
321:
322: /* Convert the BinaryComparison operator to its negation */
323: return getNegation(leftOperand, rightOperand);
324: }
325:
326: /**
327: * Negate the comparison.
328: *
329: * @param leftOperand The left operand of the comparison operator
330: * @param rightOperand The right operand of the comparison operator
331: *
332: * @return BinaryOperatorNode The negated expression
333: *
334: * @exception StandardException Thrown on error
335: */
336: BinaryOperatorNode getNegation(ValueNode leftOperand,
337: ValueNode rightOperand) throws StandardException {
338: /* Keep the compiler happy - this method should never be called.
339: * We should always be calling the method in a sub-class.
340: */
341: if (SanityManager.DEBUG)
342: SanityManager.ASSERT(false,
343: "expected to call getNegation() for subclass "
344: + getClass().toString());
345: return this ;
346: }
347:
348: /**
349: * Finish putting an expression into conjunctive normal
350: * form. An expression tree in conjunctive normal form meets
351: * the following criteria:
352: * o If the expression tree is not null,
353: * the top level will be a chain of AndNodes terminating
354: * in a true BooleanConstantNode.
355: * o The left child of an AndNode will never be an AndNode.
356: * o Any right-linked chain that includes an AndNode will
357: * be entirely composed of AndNodes terminated by a true BooleanConstantNode.
358: * o The left child of an OrNode will never be an OrNode.
359: * o Any right-linked chain that includes an OrNode will
360: * be entirely composed of OrNodes terminated by a false BooleanConstantNode.
361: * o ValueNodes other than AndNodes and OrNodes are considered
362: * leaf nodes for purposes of expression normalization.
363: * In other words, we won't do any normalization under
364: * those nodes.
365: *
366: * In addition, we track whether or not we are under a top level AndNode.
367: * SubqueryNodes need to know this for subquery flattening.
368: *
369: * @param underTopAndNode Whether or not we are under a top level AndNode.
370: *
371: *
372: * @return The modified expression
373: *
374: * @exception StandardException Thrown on error
375: */
376: public ValueNode changeToCNF(boolean underTopAndNode)
377: throws StandardException {
378: /* If our right child is a subquery and we are under a top and node
379: * then we want to mark the subquery as under a top and node.
380: * That will allow us to consider flattening it.
381: */
382: if (underTopAndNode && (rightOperand instanceof SubqueryNode)) {
383: rightOperand = rightOperand.changeToCNF(underTopAndNode);
384: }
385:
386: return this ;
387: }
388:
389: /** @see BinaryOperatorNode#genSQLJavaSQLTree */
390: public ValueNode genSQLJavaSQLTree() throws StandardException {
391: TypeId leftTypeId = leftOperand.getTypeId();
392:
393: /* If I have Java types, I need only add java->sql->java if the types
394: * are not comparable
395: */
396: if (leftTypeId.userType()) {
397: if (leftOperand.getTypeCompiler().comparable(leftTypeId,
398: false, getClassFactory()))
399: return this ;
400:
401: leftOperand = leftOperand.genSQLJavaSQLTree();
402: }
403:
404: TypeId rightTypeId = rightOperand.getTypeId();
405:
406: if (rightTypeId.userType()) {
407: if (rightOperand.getTypeCompiler().comparable(rightTypeId,
408: false, getClassFactory()))
409: return this;
410:
411: rightOperand = rightOperand.genSQLJavaSQLTree();
412: }
413:
414: return this;
415: }
416: }
|