001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.UnaryComparisonOperatorNode
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.Optimizable;
025:
026: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
027: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
028:
029: import org.apache.derby.iapi.types.TypeId;
030: import org.apache.derby.iapi.types.DataTypeDescriptor;
031:
032: import org.apache.derby.iapi.store.access.ScanController;
033:
034: import org.apache.derby.iapi.error.StandardException;
035:
036: import org.apache.derby.iapi.services.loader.GeneratedMethod;
037:
038: import org.apache.derby.iapi.services.compiler.MethodBuilder;
039:
040: import org.apache.derby.iapi.services.sanity.SanityManager;
041:
042: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
043:
044: import org.apache.derby.iapi.util.JBitSet;
045:
046: import java.sql.Types;
047:
048: import java.util.Vector;
049:
050: /**
051: * This node is the superclass for all unary comparison operators, such as is null
052: * and is not null.
053: *
054: * @author Jerry Brenner
055: */
056:
057: public class UnaryComparisonOperatorNode extends UnaryOperatorNode {
058: /**
059: * Bind this comparison operator. All that has to be done for binding
060: * a comparison operator is to bind the operand and set the result type
061: * to SQLBoolean.
062: *
063: * @param fromList The query's FROM list
064: * @param subqueryList The subquery list being built as we find SubqueryNodes
065: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
066: *
067: * @return The new top of the expression tree.
068: *
069: * @exception StandardException Thrown on error
070: */
071:
072: public ValueNode bindExpression(FromList fromList,
073: SubqueryList subqueryList, Vector aggregateVector)
074: throws StandardException {
075: super .bindExpression(fromList, subqueryList, aggregateVector);
076:
077: /* Set type info for this node */
078: bindComparisonOperator();
079:
080: return this ;
081: }
082:
083: /**
084: * Set the type info for this node. This method is useful both during
085: * binding and when we generate nodes within the language module outside
086: * of the parser.
087: *
088: * @exception StandardException Thrown on error
089: */
090: public void bindComparisonOperator() throws StandardException {
091: /*
092: ** Set the result type of this comparison operator based on the
093: ** operand. The result type is always SQLBoolean and always
094: ** non-nullable.
095: */
096: setType(new DataTypeDescriptor(TypeId.BOOLEAN_ID, false));
097: }
098:
099: /**
100: * Eliminate NotNodes in the current query block. We traverse the tree,
101: * inverting ANDs and ORs and eliminating NOTs as we go. We stop at
102: * ComparisonOperators and boolean expressions. We invert
103: * ComparisonOperators and replace boolean expressions with
104: * boolean expression = false.
105: * NOTE: Since we do not recurse under ComparisonOperators, there
106: * still could be NotNodes left in the tree.
107: *
108: * @param underNotNode Whether or not we are under a NotNode.
109: *
110: *
111: * @return The modified expression
112: *
113: * @exception StandardException Thrown on error
114: */
115: ValueNode eliminateNots(boolean underNotNode)
116: throws StandardException {
117: if (!underNotNode) {
118: return this ;
119: }
120:
121: /* Convert the BinaryComparison operator to its negation */
122: return getNegation(operand);
123: }
124:
125: /**
126: * Negate the comparison.
127: *
128: * @param operand The operand of the comparison operator
129: *
130: * @return BinaryOperatorNode The negated expression
131: *
132: * @exception StandardException Thrown on error
133: */
134: UnaryOperatorNode getNegation(ValueNode operand)
135: throws StandardException {
136: /* Keep the compiler happy - this method should never be called.
137: * We should always be calling the method in a sub-class.
138: */
139: if (SanityManager.DEBUG)
140: SanityManager.ASSERT(false,
141: "expected to call getNegation() for subclass "
142: + getClass().toString());
143: return this ;
144: }
145:
146: /* RelationalOperator interface */
147:
148: /** @see RelationalOperator#getColumnOperand */
149: public ColumnReference getColumnOperand(Optimizable optTable,
150: int columnPosition) {
151: FromBaseTable ft;
152:
153: if (SanityManager.DEBUG) {
154: SanityManager.ASSERT(optTable instanceof FromBaseTable);
155: }
156:
157: ft = (FromBaseTable) optTable;
158: ColumnReference cr;
159: if (operand instanceof ColumnReference) {
160: /*
161: ** The operand is a column reference.
162: ** Is it the correct column?
163: */
164: cr = (ColumnReference) operand;
165: if (cr.getTableNumber() == ft.getTableNumber()) {
166: /* The table is correct, how about the column position? */
167: if (cr.getSource().getColumnPosition() == columnPosition) {
168: /* We've found the correct column - return it */
169: return cr;
170: }
171: }
172: }
173:
174: /* Neither side is the column we're looking for */
175: return null;
176: }
177:
178: /** @see RelationalOperator#getColumnOperand */
179: public ColumnReference getColumnOperand(Optimizable optTable) {
180: ColumnReference cr;
181:
182: if (operand instanceof ColumnReference) {
183: /*
184: ** The operand is a column reference.
185: ** Is it the correct column?
186: */
187: cr = (ColumnReference) operand;
188: if (cr.getTableNumber() == optTable.getTableNumber()) {
189: /* We've found the correct column - return it */
190: return cr;
191: }
192: }
193:
194: /* Not the column we're looking for */
195: return null;
196: }
197:
198: /** @see RelationalOperator#getOperand */
199: public ValueNode getOperand(ColumnReference cRef, int refSetSize,
200: boolean otherSide) {
201: if (otherSide)
202: // there is no "other" side for Unary, so just return null.
203: return null;
204:
205: ColumnReference cr;
206: if (operand instanceof ColumnReference) {
207: /*
208: ** The operand is a column reference.
209: ** Is it the correct column?
210: */
211: JBitSet cRefTables = new JBitSet(refSetSize);
212: JBitSet crTables = new JBitSet(refSetSize);
213: BaseTableNumbersVisitor btnVis = new BaseTableNumbersVisitor(
214: crTables);
215:
216: cr = (ColumnReference) operand;
217: try {
218: cr.accept(btnVis);
219: btnVis.setTableMap(cRefTables);
220: cRef.accept(btnVis);
221: } catch (StandardException se) {
222: if (SanityManager.DEBUG) {
223: SanityManager
224: .THROWASSERT("Failed when trying to "
225: + "find base table number for column reference check:\n"
226: + se.getMessage());
227: }
228: }
229: crTables.and(cRefTables);
230: if (crTables.getFirstSetBit() != -1) {
231: /*
232: ** The table is correct, how about the column position?
233: */
234: if (cr.getSource().getColumnPosition() == cRef
235: .getColumnNumber()) {
236: /* We've found the correct column - return it. */
237: return operand;
238: }
239: }
240: }
241:
242: /* Not the column we're looking for */
243: return null;
244: }
245:
246: /** @see RelationalOperator#selfComparison */
247: public boolean selfComparison(ColumnReference cr) {
248: ValueNode otherSide;
249: JBitSet tablesReferenced;
250:
251: if (SanityManager.DEBUG) {
252: SanityManager.ASSERT(cr == operand,
253: "ColumnReference not found in IsNullNode.");
254: }
255:
256: /* An IsNullNode is not a comparison with any other column */
257: return false;
258: }
259:
260: /**
261: * @see RelationalOperator#getExpressionOperand
262: */
263: public ValueNode getExpressionOperand(int tableNumber,
264: int columnNumber, FromTable ft) {
265: return null;
266: }
267:
268: /**
269: * @see RelationalOperator#generateExpressionOperand
270: *
271: * @exception StandardException Thrown on error
272: */
273: public void generateExpressionOperand(Optimizable optTable,
274: int columnPosition, ExpressionClassBuilder acb,
275: MethodBuilder mb) throws StandardException {
276: acb.generateNull(mb, operand.getTypeCompiler());
277: }
278:
279: /** @see RelationalOperator#getStartOperator */
280: public int getStartOperator(Optimizable optTable) {
281: if (SanityManager.DEBUG) {
282: SanityManager
283: .THROWASSERT("getStartOperator not expected to be called for "
284: + this .getClass().getName());
285: }
286:
287: return ScanController.GE;
288: }
289:
290: /** @see RelationalOperator#getStopOperator */
291: public int getStopOperator(Optimizable optTable) {
292: if (SanityManager.DEBUG) {
293: SanityManager
294: .THROWASSERT("getStopOperator not expected to be called for "
295: + this .getClass().getName());
296: }
297:
298: return ScanController.GT;
299: }
300:
301: /** @see RelationalOperator#generateOrderedNulls */
302: public void generateOrderedNulls(MethodBuilder mb) {
303: mb.push(true);
304: }
305:
306: /**
307: * @see RelationalOperator#generateQualMethod
308: *
309: * @exception StandardException Thrown on error
310: */
311: public void generateQualMethod(ExpressionClassBuilder acb,
312: MethodBuilder mb, Optimizable optTable)
313: throws StandardException {
314: MethodBuilder qualMethod = acb.newUserExprFun();
315:
316: /* Generate a method that returns that expression */
317: acb.generateNull(qualMethod, operand.getTypeCompiler());
318: qualMethod.methodReturn();
319: qualMethod.complete();
320:
321: /* Return an expression that evaluates to the GeneratedMethod */
322: acb.pushMethodReference(mb, qualMethod);
323: }
324:
325: /** @see RelationalOperator#generateAbsoluteColumnId */
326: public void generateAbsoluteColumnId(MethodBuilder mb,
327: Optimizable optTable) {
328: // Get the absolute 0-based column position for the column
329: int columnPosition = getAbsoluteColumnPosition(optTable);
330:
331: mb.push(columnPosition);
332: }
333:
334: /** @see RelationalOperator#generateRelativeColumnId */
335: public void generateRelativeColumnId(MethodBuilder mb,
336: Optimizable optTable) {
337: // Get the absolute 0-based column position for the column
338: int columnPosition = getAbsoluteColumnPosition(optTable);
339: // Convert the absolute to the relative 0-based column position
340: columnPosition = optTable
341: .convertAbsoluteToRelativeColumnPosition(columnPosition);
342:
343: mb.push(columnPosition);
344: }
345:
346: /**
347: * Get the absolute 0-based column position of the ColumnReference from
348: * the conglomerate for this Optimizable.
349: *
350: * @param optTable The Optimizable
351: *
352: * @return The absolute 0-based column position of the ColumnReference
353: */
354: private int getAbsoluteColumnPosition(Optimizable optTable) {
355: ColumnReference cr = (ColumnReference) operand;
356: int columnPosition;
357: ConglomerateDescriptor bestCD;
358:
359: /* Column positions are one-based, store is zero-based */
360: columnPosition = cr.getSource().getColumnPosition();
361:
362: bestCD = optTable.getTrulyTheBestAccessPath()
363: .getConglomerateDescriptor();
364:
365: /*
366: ** If it's an index, find the base column position in the index
367: ** and translate it to an index column position.
368: */
369: if (bestCD.isIndex()) {
370: columnPosition = bestCD.getIndexDescriptor()
371: .getKeyColumnPosition(new Integer(columnPosition))
372: .intValue();
373:
374: if (SanityManager.DEBUG) {
375: SanityManager.ASSERT(columnPosition > 0,
376: "Base column not found in index");
377: }
378: }
379:
380: // return the 0-based column position
381: return columnPosition - 1;
382: }
383:
384: /** @see RelationalOperator#orderedNulls */
385: public boolean orderedNulls() {
386: return true;
387: }
388:
389: /** @see RelationalOperator#isQualifier */
390: public boolean isQualifier(Optimizable optTable, boolean forPush) {
391: /*
392: ** It's a Qualifier if the operand is a ColumnReference referring
393: ** to a column in the given Optimizable table.
394: */
395: if (!(operand instanceof ColumnReference))
396: return false;
397:
398: ColumnReference cr = (ColumnReference) operand;
399: FromTable ft = (FromTable) optTable;
400:
401: if (cr.getTableNumber() != ft.getTableNumber())
402: return false;
403:
404: return true;
405: }
406:
407: /**
408: * @see RelationalOperator#getOrderableVariantType
409: *
410: * @exception StandardException thrown on error
411: */
412: public int getOrderableVariantType(Optimizable optTable)
413: throws StandardException {
414: return operand.getOrderableVariantType();
415: }
416: }
|