001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.HalfOuterJoinNode
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.context.ContextManager;
025:
026: import org.apache.derby.iapi.services.compiler.MethodBuilder;
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.compile.Optimizable;
033: import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
034: import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
035: import org.apache.derby.iapi.sql.compile.Optimizer;
036: import org.apache.derby.iapi.sql.compile.CostEstimate;
037: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
038:
039: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
040:
041: import org.apache.derby.iapi.sql.Activation;
042: import org.apache.derby.iapi.sql.ResultSet;
043:
044: import org.apache.derby.iapi.error.StandardException;
045:
046: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
047:
048: import org.apache.derby.iapi.util.JBitSet;
049:
050: import java.util.Properties;
051:
052: /**
053: * An HalfOuterJoinNode represents a left or a right outer join result set.
054: * Right outer joins are always transformed into left outer joins during
055: * preprocessing for simplicity.
056: *
057: * @author Jerry Brenner
058: */
059:
060: public class HalfOuterJoinNode extends JoinNode {
061: private boolean rightOuterJoin;
062: private boolean transformed = false;
063:
064: /**
065: * Initializer for a HalfOuterJoinNode.
066: *
067: * @param leftResult The ResultSetNode on the left side of this join
068: * @param rightResult The ResultSetNode on the right side of this join
069: * @param onClause The ON clause
070: * @param usingClause The USING clause
071: * @param rightOuterJoin Whether or not this node represents a user
072: * specified right outer join
073: * @param tableProperties Properties list associated with the table
074: *
075: * @exception StandardException Thrown on error
076: */
077:
078: public void init(Object leftResult, Object rightResult,
079: Object onClause, Object usingClause, Object rightOuterJoin,
080: Object tableProperties) throws StandardException {
081: super .init(leftResult, rightResult, onClause, usingClause,
082: null, tableProperties, null);
083: this .rightOuterJoin = ((Boolean) rightOuterJoin).booleanValue();
084:
085: /* We can only flatten an outer join
086: * using the null intolerant predicate xform.
087: * In that case, we will return an InnerJoin.
088: */
089: flattenableJoin = false;
090: }
091:
092: /*
093: * Optimizable interface
094: */
095:
096: /**
097: * @see Optimizable#pushOptPredicate
098: *
099: * @exception StandardException Thrown on error
100: */
101:
102: public boolean pushOptPredicate(
103: OptimizablePredicate optimizablePredicate)
104: throws StandardException {
105: /* We should never push the predicate to joinPredicates as in JoinNode. joinPredicates
106: * should only be predicates relating the two joining tables. In the case of half join,
107: * it is biased. If the general predicate (not join predicate) contains refernce to right
108: * result set, and if doesn't qualify, we shouldn't return the row for the result to be
109: * correct, but half join will fill right side NULL and return the row. So we can only
110: * push predicate to the left, as we do in "pushExpression". bug 5055
111: */
112: FromTable leftFromTable = (FromTable) leftResultSet;
113: if (leftFromTable.getReferencedTableMap().contains(
114: optimizablePredicate.getReferencedMap()))
115: return leftFromTable.pushOptPredicate(optimizablePredicate);
116: return false;
117: }
118:
119: /**
120: * Convert this object to a String. See comments in QueryTreeNode.java
121: * for how this should be done for tree printing.
122: *
123: * @return This object as a String
124: */
125:
126: public String toString() {
127: if (SanityManager.DEBUG) {
128: return "rightOuterJoin: " + rightOuterJoin + "\n"
129: + "transformed: " + transformed + "\n"
130: + super .toString();
131: } else {
132: return "";
133: }
134: }
135:
136: /**
137: * Put a ProjectRestrictNode on top of each FromTable in the FromList.
138: * ColumnReferences must continue to point to the same ResultColumn, so
139: * that ResultColumn must percolate up to the new PRN. However,
140: * that ResultColumn will point to a new expression, a VirtualColumnNode,
141: * which points to the FromTable and the ResultColumn that is the source for
142: * the ColumnReference.
143: * (The new PRN will have the original of the ResultColumnList and
144: * the ResultColumns from that list. The FromTable will get shallow copies
145: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
146: * will remain at the FromTable, with the PRN getting a new
147: * VirtualColumnNode for each ResultColumn.expression.)
148: * We then project out the non-referenced columns. If there are no referenced
149: * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
150: * whose expression is 1.
151: *
152: * @param numTables Number of tables in the DML Statement
153: * @param gbl The group by list, if any
154: * @param fromList The from list, if any
155: *
156: * @return The generated ProjectRestrictNode atop the original FromTable.
157: *
158: * @exception StandardException Thrown on error
159: */
160: public ResultSetNode preprocess(int numTables, GroupByList gbl,
161: FromList fromList) throws StandardException {
162: ResultSetNode newTreeTop;
163:
164: /* Transform right outer joins to the equivalent left outer join */
165: if (rightOuterJoin) {
166: /* Verify that a user specifed right outer join is transformed into
167: * a left outer join exactly once.
168: */
169: if (SanityManager.DEBUG) {
170: SanityManager
171: .ASSERT(!transformed,
172: "Attempting to transform a right outer join multiple times");
173: }
174:
175: ResultSetNode tmp = leftResultSet;
176:
177: leftResultSet = rightResultSet;
178: rightResultSet = tmp;
179: transformed = true;
180: }
181:
182: newTreeTop = super .preprocess(numTables, gbl, fromList);
183:
184: return newTreeTop;
185: }
186:
187: /**
188: * Push expressions down to the first ResultSetNode which can do expression
189: * evaluation and has the same referenced table map.
190: * RESOLVE - This means only pushing down single table expressions to
191: * DistinctNodes today. Once we have a better understanding of how
192: * the optimizer will work, we can push down join clauses.
193: *
194: * @param outerPredicateList The PredicateList from the outer RS.
195: *
196: * @exception StandardException Thrown on error
197: */
198: public void pushExpressions(PredicateList outerPredicateList)
199: throws StandardException {
200: FromTable leftFromTable = (FromTable) leftResultSet;
201: FromTable rightFromTable = (FromTable) rightResultSet;
202:
203: /* We only try to push single table predicates to the left.
204: * Pushing them to the right would give incorrect semantics.
205: * We use the logic for pushing down single table predicates here.
206: */
207: pushExpressionsToLeft(outerPredicateList);
208:
209: /* Push the pushable outer join predicates to the right. This is done
210: * bottom up, hence at the end of this method, so that outer join
211: * conditions only get pushed down 1 level.
212: * We use the optimizer's logic for pushing down join clause here.
213: */
214: // Walk joinPredicates backwards due to possible deletes
215: for (int index = joinPredicates.size() - 1; index >= 0; index--) {
216: Predicate predicate;
217:
218: predicate = (Predicate) joinPredicates.elementAt(index);
219: if (!predicate.getPushable()) {
220: continue;
221: }
222:
223: getRightPredicateList().addPredicate(predicate);
224:
225: /* Remove the matching predicate from the outer list */
226: joinPredicates.removeElementAt(index);
227: }
228:
229: /* Recurse down both sides of tree */
230: PredicateList noPredicates = (PredicateList) getNodeFactory()
231: .getNode(C_NodeTypes.PREDICATE_LIST,
232: getContextManager());
233: leftFromTable.pushExpressions(getLeftPredicateList());
234: rightFromTable.pushExpressions(noPredicates);
235: }
236:
237: /**
238: * This method determines if (1) the query is a LOJ, and (2) if the LOJ is a candidate for
239: * reordering (i.e., linearization). The condition for LOJ linearization is:
240: * 1. only LOJ in the fromList, i.e., no INNER, no FULL JOINs, no ROJs
241: * 2. ON clause must be equality join between left and right operands and in CNF (i.e., AND is allowed)
242: */
243: public boolean LOJ_reorderable(int numTables)
244: throws StandardException {
245: boolean anyChange = false;
246:
247: ResultSetNode logicalLeftResultSet; // row-preserving side
248: ResultSetNode logicalRightResultSet; // null-producing side
249:
250: // Figure out which is the row-preserving side and which is
251: // null-producing side.
252: if (rightOuterJoin) { // right outer join
253: logicalLeftResultSet = rightResultSet;
254: logicalRightResultSet = leftResultSet;
255: } else {
256: logicalLeftResultSet = leftResultSet;
257: logicalRightResultSet = rightResultSet;
258: }
259:
260: // Redundantly normalize the ON predicate (it will also be called in preprocess()).
261: super .normExpressions();
262:
263: // This is a very simple LOJ of base tables. Do nothing.
264: if (logicalLeftResultSet instanceof FromBaseTable
265: && logicalRightResultSet instanceof FromBaseTable)
266: return anyChange;
267:
268: // Recursively check if we can reordering LOJ, and build the table
269: // references. Note that joins may have been reordered and therefore the
270: // table references need to be recomputed.
271: if (logicalLeftResultSet instanceof HalfOuterJoinNode) {
272: anyChange = ((HalfOuterJoinNode) logicalLeftResultSet)
273: .LOJ_reorderable(numTables)
274: || anyChange;
275: } else if (!(logicalLeftResultSet instanceof FromBaseTable)) {// left operand must be either a base table or another LOJ
276: // In principle, we don't care about the left operand. However, we
277: // need to re-bind the resultColumns. If the left operand is a
278: // view, we may have to re-bind the where clause etc...
279: // We ran into difficulty for the following query:
280: // create view v8 (cv, bv, av) as (select c, b, a from t union select f, e, d from s);
281: // select * from v8 left outer join (s left outer join r on (f = i)) on (e=v8.bv);
282: return anyChange;
283: }
284:
285: if (logicalRightResultSet instanceof HalfOuterJoinNode) {
286: anyChange = ((HalfOuterJoinNode) logicalRightResultSet)
287: .LOJ_reorderable(numTables)
288: || anyChange;
289: } else if (!(logicalRightResultSet instanceof FromBaseTable)) {// right operand must be either a base table or another LOJ
290: return anyChange;
291: }
292:
293: // It is much easier to do LOJ reordering if there is no ROJ.
294: // However, we ran into some problem downstream when we transform an ROJ
295: // into LOJ -- transformOuterJoin() didn't expect ROJ to be transformed
296: // into LOJ alread. So, we skip optimizing ROJ at the moment.
297: if (rightOuterJoin
298: || (logicalRightResultSet instanceof HalfOuterJoinNode && ((HalfOuterJoinNode) logicalRightResultSet).rightOuterJoin)) {
299: return LOJ_bindResultColumns(anyChange);
300: }
301:
302: // Build the data structure for testing/doing LOJ reordering.
303: // Fill in the table references on row-preserving and null-producing sides.
304: // It may be possible that either operand is a complex view.
305: JBitSet NPReferencedTableMap; // Null-producing
306: JBitSet RPReferencedTableMap; // Row-preserving
307:
308: RPReferencedTableMap = logicalLeftResultSet
309: .LOJgetReferencedTables(numTables);
310: NPReferencedTableMap = logicalRightResultSet
311: .LOJgetReferencedTables(numTables);
312:
313: if ((RPReferencedTableMap == null || NPReferencedTableMap == null)
314: && anyChange) {
315: return LOJ_bindResultColumns(anyChange);
316: }
317:
318: // Check if the predicate is equality predicate in CNF (i.e., AND only)
319: // and left/right column references must come from either operand.
320: // That is, we don't allow:
321: // 1. A=A
322: // 2. 1=1
323: // 3. B=C where both B and C are either from left or right operand.
324:
325: // we probably need to make the joinClause "left-deep" so that we can
326: // walk it easier.
327: BinaryRelationalOperatorNode equals;
328: ValueNode leftCol;
329: ValueNode rightCol;
330: AndNode and;
331: ValueNode left;
332: ValueNode vn = joinClause;
333: while (vn instanceof AndNode) {
334: and = (AndNode) vn;
335: left = and.getLeftOperand();
336:
337: // Make sure that this is an equijoin of the form "C = D" where C
338: // and D references tables from both left and right operands.
339: if (left instanceof RelationalOperator
340: && ((ValueNode) left).isBinaryEqualsOperatorNode()) {
341: equals = (BinaryRelationalOperatorNode) left;
342: leftCol = equals.getLeftOperand();
343: rightCol = equals.getRightOperand();
344:
345: if (!(leftCol instanceof ColumnReference && rightCol instanceof ColumnReference))
346: return LOJ_bindResultColumns(anyChange);
347:
348: boolean refCheck = false;
349: boolean leftOperandCheck = false;
350:
351: if (RPReferencedTableMap
352: .get(((ColumnReference) leftCol)
353: .getTableNumber())) {
354: refCheck = true;
355: leftOperandCheck = true;
356: } else if (NPReferencedTableMap
357: .get(((ColumnReference) leftCol)
358: .getTableNumber())) {
359: refCheck = true;
360: }
361:
362: if (refCheck == false)
363: return LOJ_bindResultColumns(anyChange);
364:
365: refCheck = false;
366: if (leftOperandCheck == false
367: && RPReferencedTableMap
368: .get(((ColumnReference) rightCol)
369: .getTableNumber())) {
370: refCheck = true;
371: } else if (leftOperandCheck == true
372: && NPReferencedTableMap
373: .get(((ColumnReference) rightCol)
374: .getTableNumber())) {
375: refCheck = true;
376: }
377:
378: if (refCheck == false)
379: return LOJ_bindResultColumns(anyChange);
380: } else
381: return LOJ_bindResultColumns(anyChange); // get out of here
382:
383: vn = and.getRightOperand();
384: }
385:
386: // Check if the logical right resultset is a composite inner and as such
387: // that this current LOJ can be pushed through it.
388: boolean push = false;
389: // logical right operand is another LOJ... so we may be able to push the
390: // join
391: if (logicalRightResultSet instanceof HalfOuterJoinNode) {
392: // get the Null-producing operand of the child
393: JBitSet logicalNPRefTableMap = ((HalfOuterJoinNode) logicalRightResultSet)
394: .LOJgetNPReferencedTables(numTables);
395:
396: // does the current LOJ join predicate reference
397: // logicalNPRefTableMap? If not, we can push the current
398: // join.
399: vn = joinClause;
400: push = true;
401: while (vn instanceof AndNode) {
402: and = (AndNode) vn;
403: left = and.getLeftOperand();
404: equals = (BinaryRelationalOperatorNode) left;
405: leftCol = equals.getLeftOperand();
406: rightCol = equals.getRightOperand();
407:
408: if (logicalNPRefTableMap
409: .get(((ColumnReference) leftCol)
410: .getTableNumber())
411: || logicalNPRefTableMap
412: .get(((ColumnReference) rightCol)
413: .getTableNumber())) {
414: push = false;
415: break;
416: }
417:
418: vn = and.getRightOperand();
419: }
420: }
421:
422: // Push the current LOJ into the next level
423: if (push) {
424: // For safety, check the JoinNode data members: they should null or
425: // empty list before we proceed.
426: if (super .subqueryList.size() != 0
427: || ((JoinNode) logicalRightResultSet).subqueryList
428: .size() != 0
429: || super .joinPredicates.size() != 0
430: || ((JoinNode) logicalRightResultSet).joinPredicates
431: .size() != 0
432: || super .usingClause != null
433: || ((JoinNode) logicalRightResultSet).usingClause != null)
434: return LOJ_bindResultColumns(anyChange); // get out of here
435:
436: anyChange = true; // we are reordering the LOJs.
437:
438: ResultSetNode tmp = logicalLeftResultSet;
439: ResultSetNode LChild, RChild;
440:
441: // this LOJ
442: // / \
443: // logicalLeftRS LogicalRightRS
444: // / \
445: // LChild RChild
446: // becomes
447: //
448: // this LOJ
449: // / \
450: // LogicalRightRS RChild
451: // / \
452: // logicalLeftRS LChild <<< we need to be careful about this order
453: // as the "LogicalRightRS may be a ROJ
454: //
455:
456: // handle the lower level LOJ node
457: LChild = ((HalfOuterJoinNode) logicalRightResultSet).leftResultSet;
458: RChild = ((HalfOuterJoinNode) logicalRightResultSet).rightResultSet;
459:
460: ((HalfOuterJoinNode) logicalRightResultSet).rightResultSet = LChild;
461: ((HalfOuterJoinNode) logicalRightResultSet).leftResultSet = tmp;
462:
463: // switch the ON clause
464: vn = joinClause;
465: joinClause = ((HalfOuterJoinNode) logicalRightResultSet).joinClause;
466: ((HalfOuterJoinNode) logicalRightResultSet).joinClause = vn;
467:
468: // No need to switch HalfOuterJoinNode data members for now because
469: // we are handling only LOJ.
470: // boolean local_rightOuterJoin = rightOuterJoin;
471: // boolean local_transformed = transformed;
472: // rightOuterJoin = ((HalfOuterJoinNode)logicalRightResultSet).rightOuterJoin;
473: // transformed = ((HalfOuterJoinNode)logicalRightResultSet).transformed;
474: // ((HalfOuterJoinNode)logicalRightResultSet).rightOuterJoin = local_rightOuterJoin;
475: // ((HalfOuterJoinNode)logicalRightResultSet).transformed = local_transformed;
476:
477: FromList localFromList = (FromList) getNodeFactory()
478: .getNode(C_NodeTypes.FROM_LIST,
479: getNodeFactory().doJoinOrderOptimization(),
480: getContextManager());
481:
482: // switch LOJ nodes: by handling the current LOJ node
483: leftResultSet = logicalRightResultSet;
484: rightResultSet = RChild;
485:
486: // rebuild the result columns and re-bind column references
487: ((HalfOuterJoinNode) leftResultSet).resultColumns = null;
488: ((JoinNode) leftResultSet).bindResultColumns(localFromList); // localFromList is empty
489:
490: // left operand must be another LOJ, try again until a fixpoint
491: boolean localChange = ((HalfOuterJoinNode) leftResultSet)
492: .LOJ_reorderable(numTables);
493:
494: // rebuild the result columns and re-bind column references for 'this'
495: return LOJ_bindResultColumns(anyChange);
496: }
497:
498: return LOJ_bindResultColumns(anyChange);
499: }
500:
501: // This method re-binds the result columns which may be referenced in the ON
502: // clause in this node.
503: public boolean LOJ_bindResultColumns(boolean anyChange)
504: throws StandardException {
505: if (anyChange) {
506: this .resultColumns = null;
507: FromList localFromList = (FromList) getNodeFactory()
508: .getNode(C_NodeTypes.FROM_LIST,
509: getNodeFactory().doJoinOrderOptimization(),
510: getContextManager());
511: ((JoinNode) this ).bindResultColumns(localFromList);
512: }
513: return anyChange;
514: }
515:
516: /**
517: * Transform any Outer Join into an Inner Join where applicable.
518: * (Based on the existence of a null intolerant
519: * predicate on the inner table.)
520: *
521: * @param predicateTree The predicate tree for the query block
522: *
523: * @return The new tree top (OuterJoin or InnerJoin).
524: *
525: * @exception StandardException Thrown on error
526: */
527: public FromTable transformOuterJoins(ValueNode predicateTree,
528: int numTables) throws StandardException {
529: ResultSetNode innerRS;
530:
531: if (predicateTree == null) {
532: /* We can't transform this node, so tell both sides of the
533: * outer join that they can't get flattened into outer query block.
534: */
535: leftResultSet.notFlattenableJoin();
536: rightResultSet.notFlattenableJoin();
537: return this ;
538: }
539:
540: super .transformOuterJoins(predicateTree, numTables);
541:
542: JBitSet innerMap = new JBitSet(numTables);
543: if (rightOuterJoin) {
544: if (SanityManager.DEBUG) {
545: SanityManager
546: .ASSERT(!transformed,
547: "right OJ not expected to be transformed into left OJ yet");
548: }
549: innerRS = leftResultSet;
550: } else {
551: innerRS = rightResultSet;
552: }
553:
554: innerRS.fillInReferencedTableMap(innerMap);
555:
556: /* Walk predicates looking for
557: * a null intolerant predicate on the inner table.
558: */
559: ValueNode vn = predicateTree;
560: while (vn instanceof AndNode) {
561: AndNode and = (AndNode) vn;
562: ValueNode left = and.getLeftOperand();
563:
564: /* Skip IS NULL predicates as they are not null intolerant */
565: if (left.isInstanceOf(C_NodeTypes.IS_NULL_NODE)) {
566: vn = and.getRightOperand();
567: continue;
568: }
569:
570: /* Only consider predicates that are relops */
571: if (left instanceof RelationalOperator) {
572: JBitSet refMap = new JBitSet(numTables);
573: /* Do not consider method calls,
574: * conditionals, field references, etc. */
575: if (!(left.categorize(refMap, true))) {
576: vn = and.getRightOperand();
577: continue;
578: }
579:
580: /* If the predicate is a null intolerant predicate
581: * on the right side then we can flatten to an
582: * inner join. We do the xform here, flattening
583: * will happen later.
584: */
585: for (int bit = 0; bit < numTables; bit++) {
586: if (refMap.get(bit) && innerMap.get(bit)) {
587: // OJ -> IJ
588: JoinNode ij = (JoinNode) getNodeFactory()
589: .getNode(C_NodeTypes.JOIN_NODE,
590: leftResultSet, rightResultSet,
591: joinClause, null,
592: resultColumns, null, null,
593: getContextManager());
594: ij.setTableNumber(tableNumber);
595: ij.setSubqueryList(subqueryList);
596: ij.setAggregateVector(aggregateVector);
597: return ij;
598: }
599: }
600: }
601:
602: vn = and.getRightOperand();
603: }
604:
605: /* We can't transform this node, so tell both sides of the
606: * outer join that they can't get flattened into outer query block.
607: */
608: leftResultSet.notFlattenableJoin();
609: rightResultSet.notFlattenableJoin();
610:
611: return this ;
612: }
613:
614: /** @see JoinNode#adjustNumberOfRowsReturned */
615: protected void adjustNumberOfRowsReturned(CostEstimate costEstimate) {
616: /*
617: ** An outer join returns at least as many rows as in the outer
618: ** table. Even if this started as a right outer join, it will
619: ** have been transformed to a left outer join by this point.
620: */
621: CostEstimate outerCost = getLeftResultSet().getCostEstimate();
622:
623: if (costEstimate.rowCount() < outerCost.rowCount()) {
624: costEstimate.setCost(costEstimate.getEstimatedCost(),
625: outerCost.rowCount(), outerCost.rowCount());
626: }
627: }
628:
629: /**
630: * Generate the code for an inner join node.
631: *
632: * @exception StandardException Thrown on error
633: */
634: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
635: throws StandardException {
636: /* Verify that a user specifed right outer join is transformed into
637: * a left outer join exactly once.
638: */
639: if (SanityManager.DEBUG) {
640: SanityManager.ASSERT(rightOuterJoin == transformed,
641: "rightOuterJoin (" + rightOuterJoin
642: + ") is expected to equal transformed ("
643: + transformed + ")");
644: }
645: super .generateCore(acb, mb, LEFTOUTERJOIN);
646: }
647:
648: /**
649: * Generate and add any arguments specifict to outer joins.
650: * Generate the methods (and add them as parameters) for
651: * returning an empty row from 1 or more sides of an outer join,
652: * if required. Pass whether or not this was originally a
653: * right outer join.
654: *
655: * @param acb The ActivationClassBuilder
656: * @param mb the method the generate code is to go into
657: *
658: * return The args that have been added
659: *
660: * @exception StandardException Thrown on error
661: */
662: protected int addOuterJoinArguments(ActivationClassBuilder acb,
663: MethodBuilder mb) throws StandardException {
664: /* Nulls always generated from the right */
665: rightResultSet.getResultColumns().generateNulls(acb, mb);
666:
667: /* Was this originally a right outer join? */
668: mb.push(rightOuterJoin);
669:
670: return 2;
671: }
672:
673: /**
674: * Return the number of arguments to the join result set.
675: */
676: protected int getNumJoinArguments() {
677: /* We add two more arguments than the superclass does */
678: return super .getNumJoinArguments() + 2;
679: }
680:
681: protected void oneRowRightSide(ActivationClassBuilder acb,
682: MethodBuilder mb) {
683: // always return false for now
684: mb.push(false);
685: mb.push(false); //isNotExists?
686: }
687:
688: /**
689: * Return the logical left result set for this qualified
690: * join node.
691: * (For RIGHT OUTER JOIN, the left is the right
692: * and the right is the left and the JOIN is the NIOJ).
693: */
694: ResultSetNode getLogicalLeftResultSet() {
695: if (rightOuterJoin) {
696: return rightResultSet;
697: } else {
698: return leftResultSet;
699: }
700: }
701:
702: /**
703: * Return the logical right result set for this qualified
704: * join node.
705: * (For RIGHT OUTER JOIN, the left is the right
706: * and the right is the left and the JOIN is the NIOJ).
707: */
708: ResultSetNode getLogicalRightResultSet() {
709: if (rightOuterJoin) {
710: return leftResultSet;
711: } else {
712: return rightResultSet;
713: }
714: }
715:
716: /**
717: * Return true if right outer join or false if left outer join
718: * Used to set Nullability correctly in JoinNode
719: */
720: public boolean isRightOuterJoin() {
721: return rightOuterJoin;
722: }
723:
724: // return the Null-producing table references
725: public JBitSet LOJgetNPReferencedTables(int numTables)
726: throws StandardException {
727: if (rightOuterJoin && !transformed)
728: return (JBitSet) leftResultSet
729: .LOJgetReferencedTables(numTables);
730: else
731: return (JBitSet) rightResultSet
732: .LOJgetReferencedTables(numTables);
733: }
734: }
|