KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > expressions > RelationExpression


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.internal.expressions;
23
24 import java.util.*;
25 import oracle.toplink.essentials.exceptions.*;
26 import oracle.toplink.essentials.mappings.*;
27 import oracle.toplink.essentials.queryframework.*;
28 import oracle.toplink.essentials.internal.helper.*;
29 import oracle.toplink.essentials.expressions.*;
30 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
31 import oracle.toplink.essentials.internal.sessions.AbstractSession;
32 import oracle.toplink.essentials.descriptors.ClassDescriptor;
33
34 /**
35  * <p><b>Purpose</b>:Used for all relation operators except for between.
36  */

37 public class RelationExpression extends CompoundExpression {
38
39     /**
40      * RelationExpression constructor comment.
41      */

42     public RelationExpression() {
43         super();
44     }
45
46     /**
47      * Test that both of our children are field nodes
48      */

49     protected boolean allChildrenAreFields() {
50         return (getFirstChild().getFields().size() == 1) && (getSecondChild().getFields().size() == 1);
51
52     }
53
54     /**
55      * INTERNAL:
56      * Modify this individual expression node to use outer joins wherever there are
57      * equality operations between two field nodes.
58      */

59     protected void convertNodeToUseOuterJoin() {
60         if ((getOperator().getSelector() == ExpressionOperator.Equal) && allChildrenAreFields()) {
61             setOperator(getOperator(ExpressionOperator.EqualOuterJoin));
62         }
63     }
64
65     /**
66      * INTERNAL:
67      * Used for debug printing.
68      */

69     public String JavaDoc descriptionOfNodeType() {
70         return "Relation";
71     }
72
73     /**
74      * INTERNAL:
75      * Check if the object conforms to the expression in memory.
76      * This is used for in-memory querying.
77      * If the expression in not able to determine if the object conform throw a not supported exception.
78      */

79     public boolean doesConform(Object JavaDoc object, AbstractSession session, AbstractRecord translationRow, InMemoryQueryIndirectionPolicy valueHolderPolicy, boolean isObjectUnregistered) {
80         // Extract the value from the right side.
81
//CR 3677 integration of valueHolderPolicy
82
Object JavaDoc rightValue = getSecondChild().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);
83
84         // Extract the value from the object.
85
//CR 3677 integration of valueHolderPolicy
86
Object JavaDoc leftValue = getFirstChild().valueFromObject(object, session, translationRow, valueHolderPolicy, isObjectUnregistered);
87
88         // The right value may be a vector of values from an anyof, or an in.
89
if (rightValue instanceof Vector) {
90             //right vector means an anyof on right, so must check each value.
91
for (Enumeration rightEnum = (Enumeration)((Vector)rightValue).elements();
92                      rightEnum.hasMoreElements();) {
93                 Object JavaDoc tempRight = rightEnum.nextElement();
94
95                 // Left may also be an anyof some must check each left with each right.
96
if (leftValue instanceof Vector) {
97                     // If anyof the left match return true, otherwise keep checking.
98
if (doesAnyOfLeftValuesConform((Vector)leftValue, tempRight, session)) {
99                         return true;
100                     }
101                 }
102                 if (doValuesConform(leftValue, tempRight, session)) {
103                     return true;
104                 }
105             }
106
107             // None of the value conform.
108
return false;
109         }
110
111         // Otherwise the left may also be a vector of values from an anyof.
112
if (leftValue instanceof Vector) {
113             return doesAnyOfLeftValuesConform((Vector)leftValue, rightValue, session);
114         }
115
116         // Otherwise it is a simple value to value comparison, or simple object to object comparison.
117
return doValuesConform(leftValue, rightValue, session);
118     }
119
120     /**
121      * Conform in-memory the collection of left values with the right value for this expression.
122      * This is used for anyOf support when the left side is a collection of values.
123      */

124     protected boolean doesAnyOfLeftValuesConform(Vector leftValues, Object JavaDoc rightValue, AbstractSession session) {
125         // Check each left value with the right value.
126
for (int index = 0; index < leftValues.size(); index++) {
127             Object JavaDoc leftValue = leftValues.get(index);
128             if (doValuesConform(leftValue, rightValue, session)) {
129                 // Return true if any value matches.
130
return true;
131             }
132         }
133
134         // Return false only if none of the values match.
135
return false;
136     }
137
138     /**
139      * Conform in-memory the two values.
140      */

141     protected boolean doValuesConform(Object JavaDoc leftValue, Object JavaDoc rightValue, AbstractSession session) {
142         // Check for object comparison.
143
if (isObjectComparison()) {
144             return doesObjectConform(leftValue, rightValue, session);
145         } else {
146             return getOperator().doesRelationConform(leftValue, rightValue);
147         }
148     }
149
150     /**
151      * INTERNAL:
152      * Check if the object conforms to the expression in memory.
153      * This is used for in-memory querying across object relationships.
154      */

155     public boolean doesObjectConform(Object JavaDoc leftValue, Object JavaDoc rightValue, AbstractSession session) {
156         if ((leftValue == null) && (rightValue == null)) {
157             return performSelector(true);
158         }
159         if ((leftValue == null) || (rightValue == null)) {
160             //both are not null.
161
return performSelector(false);
162         }
163
164         Class JavaDoc javaClass = leftValue.getClass();
165         Vector leftPrimaryKey;
166         Vector rightPrimaryKey;
167         ClassDescriptor descriptor;
168         oracle.toplink.essentials.internal.identitymaps.CacheKey rightCacheKey;
169         oracle.toplink.essentials.internal.identitymaps.CacheKey leftCacheKey;
170
171         if (javaClass != rightValue.getClass()) {
172             return performSelector(false);
173         }
174
175         descriptor = session.getDescriptor(javaClass);
176         // Currently cannot conform aggregate comparisons in-memory.
177
if (descriptor.isAggregateDescriptor()) {
178             throw QueryException.cannotConformExpression();
179         }
180         leftPrimaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(leftValue, session);
181         rightPrimaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(rightValue, session);
182
183         rightCacheKey = new oracle.toplink.essentials.internal.identitymaps.CacheKey(rightPrimaryKey);
184         leftCacheKey = new oracle.toplink.essentials.internal.identitymaps.CacheKey(leftPrimaryKey);
185
186         return performSelector(rightCacheKey.equals(leftCacheKey));
187
188     }
189
190     /**
191      * INTERNAL:
192      * Extract the primary key from the expression into the row.
193      * Ensure that the query is quering the exact primary key.
194      * @param requireExactMatch refers to the primary key extracted gaurenteeing the result,
195      * if not exact it is a hueristic and the cache hit will be conformed to the expression after the lookup
196      * Return false if not on the primary key.
197      */

198     public boolean extractPrimaryKeyValues(boolean requireExactMatch, ClassDescriptor descriptor, AbstractRecord primaryKeyRow, AbstractRecord translationRow) {
199         // If an exact match is required then the operator must be equality.
200
if (requireExactMatch && (!(getOperator().getSelector() == ExpressionOperator.Equal))) {
201             return false;
202         }
203
204         // If not an exact match only =, <, <=, >=, >,... are allowed but not IN which has a different type
205
if ((!requireExactMatch) && (getOperator().getSelector() == ExpressionOperator.In)) {
206             return false;
207         }
208
209         DatabaseField field = null;
210
211         Object JavaDoc value = null;
212         if (getSecondChild().isConstantExpression()) {
213             value = ((ConstantExpression)getSecondChild()).getValue();
214         } else if (getSecondChild().isParameterExpression()) {
215             value = translationRow.get(((ParameterExpression)getSecondChild()).getField());
216         } else if (getFirstChild().isConstantExpression()) {
217             value = ((ConstantExpression)getFirstChild()).getValue();
218         } else if (getFirstChild().isParameterExpression()) {
219             value = translationRow.get(((ParameterExpression)getFirstChild()).getField());
220         }
221         if (value == null) {
222             return false;
223         }
224
225         // Ensure that the primary key is being queried on.
226
if (getFirstChild().isFieldExpression()) {
227             // Only get field for the source object.
228
if (getFirstChild().getBuilder() != ((FieldExpression)getFirstChild()).getBaseExpression()) {
229                 return false;
230             }
231             field = ((FieldExpression)getFirstChild()).getField();
232         } else if (getFirstChild().isQueryKeyExpression()) {
233             DatabaseMapping mapping = descriptor.getMappingForAttributeName(((QueryKeyExpression)getFirstChild()).getName());
234             if (getFirstChild().getBuilder() != ((QueryKeyExpression) getFirstChild()).getBaseExpression()){
235                 return false;
236              }
237
238             // Only support referencing limited number of relationship types.
239
if (mapping != null && (!mapping.isPrimaryKeyMapping()) ){
240                 return false;
241             }
242
243             if (mapping != null && (mapping.isObjectReferenceMapping() || mapping.isAggregateObjectMapping())){
244                 mapping.writeFromAttributeIntoRow(value, primaryKeyRow, getSession());
245                 return true;
246             }
247
248             if (mapping != null && !mapping.isDirectToFieldMapping()){
249                 return false;
250              }
251             
252             // Only get field for the source object.
253
field = descriptor.getObjectBuilder().getFieldForQueryKeyName(getFirstChild().getName());
254         } else if (getSecondChild().isFieldExpression()) {// For parameterized queries it may also be on the right side.
255
// Only get field for the source object.
256
if (getFirstChild().getBuilder() != ((FieldExpression)getSecondChild()).getBaseExpression()) {
257                 return false;
258             }
259             field = ((FieldExpression)getSecondChild()).getField();
260         } else if (getSecondChild().isQueryKeyExpression()) {
261             DatabaseMapping mapping = descriptor.getMappingForAttributeName(((QueryKeyExpression)getSecondChild()).getName());
262             if (getSecondChild().getBuilder() != ((QueryKeyExpression) getSecondChild()).getBaseExpression() ){
263                 return false;
264              }
265             
266             // Only support referencing limited number of relationship types.
267
if (mapping != null && (!mapping.isPrimaryKeyMapping()) ){
268                 return false;
269             }
270
271             if (mapping != null && (mapping.isObjectReferenceMapping() || mapping.isAggregateObjectMapping())){
272                 mapping.writeFromAttributeIntoRow(value, primaryKeyRow, getSession());
273                 return true;
274             }
275
276             if (mapping != null && !mapping.isDirectToFieldMapping()){
277                 return false;
278              }
279             
280             field = descriptor.getObjectBuilder().getFieldForQueryKeyName(getSecondChild().getName());
281         } else {
282             return false;
283         }
284         if ((field == null) || (!descriptor.getPrimaryKeyFields().contains(field))) {
285             return false;
286         }
287
288         primaryKeyRow.put(field, value);
289
290         return true;
291     }
292
293     /**
294      * Check if the expression is an equal null expression, these must be handle in a special way in SQL.
295      */

296     public boolean isEqualNull(ExpressionSQLPrinter printer) {
297         if (isObjectComparison()) {
298             return false;
299         }
300
301         if (getOperator().getSelector() != ExpressionOperator.Equal) {
302             return false;
303         }
304
305         if (getSecondChild().isConstantExpression() && (((ConstantExpression)getSecondChild()).getValue() == null)) {
306             return true;
307         }
308
309         if (getSecondChild().isParameterExpression() && (printer.getTranslationRow() != null) && (((ParameterExpression)getSecondChild()).getValue(printer.getTranslationRow(), printer.getSession()) == null)) {
310             return true;
311         }
312
313         return false;
314     }
315
316     /**
317      * Check if the expression is an equal null expression, these must be handle in a special way in SQL.
318      */

319     public boolean isNotEqualNull(ExpressionSQLPrinter printer) {
320         if (isObjectComparison()) {
321             return false;
322         }
323
324         if (getOperator().getSelector() != ExpressionOperator.NotEqual) {
325             return false;
326         }
327
328         if (getSecondChild().isConstantExpression() && (((ConstantExpression)getSecondChild()).getValue() == null)) {
329             return true;
330         }
331
332         if (getSecondChild().isParameterExpression() && (printer.getTranslationRow() != null) && (((ParameterExpression)getSecondChild()).getValue(printer.getTranslationRow(), printer.getSession()) == null)) {
333             return true;
334         }
335
336         return false;
337     }
338
339     /**
340      * INTERNAL:
341      * Return if the represents an object comparison.
342      */

343     protected boolean isObjectComparison() {
344         if ((!getFirstChild().isObjectExpression()) || ((ObjectExpression)getFirstChild()).isAttribute()) {
345             return false;
346         }
347
348         DatabaseMapping mapping = ((ObjectExpression)getFirstChild()).getMapping();
349         if ((mapping != null) && (mapping.isDirectCollectionMapping())) {
350             return false;
351         }
352
353         return getSecondChild().isObjectExpression() || (getSecondChild().isValueExpression() || (getSecondChild().isFunctionExpression() && ((FunctionExpression)getSecondChild()).getOperator().isAnyOrAll()));
354     }
355
356     /**
357      * INTERNAL:
358      */

359     public boolean isRelationExpression() {
360         return true;
361     }
362
363     /**
364      * INTERNAL:
365      * Check for object comparison as this requires for the expression to be replaced by the object comparison.
366      */

367     public Expression normalize(ExpressionNormalizer normalizer) {
368         if (!isObjectComparison()) {
369             return super.normalize(normalizer);
370         } else {
371             //bug # 2956674
372
//validation is moved into normalize to ensure that expressions are valid before we attempt to work with them
373
// super.normalize will call validateNode as well.
374
validateNode();
375         }
376         if ((getOperator().getSelector() != ExpressionOperator.Equal) && (getOperator().getSelector() != ExpressionOperator.NotEqual)) {
377             throw QueryException.invalidOperatorForObjectComparison(this);
378         }
379
380         if(getSecondChild().isFunctionExpression()) {
381             FunctionExpression funcExp = (FunctionExpression)getSecondChild();
382             if(funcExp.getOperator().isAnyOrAll()) {
383                 SubSelectExpression subSelectExp = (SubSelectExpression)funcExp.getChildren().elementAt(1);
384                 ReportQuery subQuery = subSelectExp.getSubQuery();
385                 
386                 // some db (derby) require that in EXIST(SELECT...) subquery returns a single column
387
subQuery.getItems().clear();
388                 subQuery.addItem("one", new ConstantExpression(new Integer JavaDoc(1), subQuery.getExpressionBuilder()));
389                 
390                 Expression subSelectCriteria = subQuery.getSelectionCriteria();
391                 ExpressionBuilder subBuilder = subQuery.getExpressionBuilder();
392
393                 ExpressionBuilder builder = getFirstChild().getBuilder();
394
395                 Expression newExp;
396                 if(funcExp.getOperator().isAny()) {
397                     // Any or Some
398
if(getOperator().getSelector() == ExpressionOperator.Equal) {
399                         subSelectCriteria = subBuilder.equal(getFirstChild()).and(subSelectCriteria);
400                     } else {
401                         subSelectCriteria = subBuilder.notEqual(getFirstChild()).and(subSelectCriteria);
402                     }
403                     subQuery.setSelectionCriteria(subSelectCriteria);
404                     newExp = builder.exists(subQuery);
405                 } else {
406                     // All
407
if(getOperator().getSelector() == ExpressionOperator.Equal) {
408                         subSelectCriteria = subBuilder.notEqual(getFirstChild()).and(subSelectCriteria);
409                     } else {
410                         subSelectCriteria = subBuilder.equal(getFirstChild()).and(subSelectCriteria);
411                     }
412                     subQuery.setSelectionCriteria(subSelectCriteria);
413                     newExp = builder.notExists(subQuery);
414                 }
415                 return newExp.normalize(normalizer);
416             }
417         }
418         
419         // This can either be comparison to another object, null or another expression reference.
420
// Object comparisons can be done on other object builders, 1:1 or 1:m m:m mappings,
421
// 1:m/m:m must twist the primary key expression,
422
// 1:1 must not join into the target but become the foreign key expression.
423
// The value may be a constant or another expression.
424
Expression foreignKeyJoin = null;
425         ObjectExpression first = (ObjectExpression)getFirstChild();
426
427         // OPTIMIZATION 1: IDENTITY for CR#2456 / bug 2778339
428
// Most exists subqueries have something like projBuilder.equal(empBuilder.anyOf("projects"))
429
// to correlate the subquery to the enclosing query.
430
// TopLink can produce SQL with one less join by not mapping projBuilder and
431
// anyOf("projects") to separate tables and equating them, but by treating
432
// them as one and the same thing: as identical.
433
// This trick can be pulled off by giving both the same TableAliasLookup,
434
// but needs to be done very conservatively.
435
// the equal() will be replaced directly with the mappingCriteria() of the anyOf("projects")
436
// Example. emp.equal(emp.get("manager")) will now produce this SQL:
437
// SELECT ... FROM EMPLOYEE t0 WHERE (t0.EMP_ID = t0.MANAGER_ID) not:
438
// SELECT ... FROM EMPLOYEE t0, EMPLOYEE t1 WHERE ((t0.EMP_ID = t1.EMP_ID)
439
// AND (t0.MANAGER_ID = t1.EMP_ID))
440
if (first.isExpressionBuilder() &&// If setting two query keys to equal the user probably intends a proper join.
441
getSecondChild().isQueryKeyExpression() &&//.equal(anyOf() or get())
442
!((QueryKeyExpression)getSecondChild()).hasDerivedExpressions()) {//The right side is not used for anything else.
443
first = (ExpressionBuilder)first.normalize(normalizer);
444
445             // If FK joins go in the WHERE clause, want to get hold of it and
446
// not put it in normalizer.additionalExpressions.
447
Vector foreignKeyJoinPointer = new Vector(1);
448             QueryKeyExpression second = (QueryKeyExpression)getSecondChild();
449
450             // If inside an OR the foreign key join must be on both sides.
451
if (second.hasBeenNormalized()) {
452                 second.setHasBeenNormalized(false);
453             }
454             second = (QueryKeyExpression)second.normalize(normalizer, foreignKeyJoinPointer);
455             if (!foreignKeyJoinPointer.isEmpty()) {
456                 foreignKeyJoin = (Expression)foreignKeyJoinPointer.firstElement();
457                 // Will make left and right identical in the SQL.
458
if (first.getTableAliases() == null) {
459                     TableAliasLookup tableAliases = new TableAliasLookup();
460                     first.setTableAliases(tableAliases);
461                     second.setTableAliases(tableAliases);
462                 } else {
463                     second.setTableAliases(first.getTableAliases());
464                 }
465             }
466         }
467         // OPTIMIZATION 2: for 1-1 mappings and get(...).equal(null)
468
// Imagine you had addr1 = emp.get("address"); then addr1.equal(addr2);
469
// One could go (addr1.ADDRESS_ID = addr2.ADDRESS_ID) and (emp.ADDR_ID = addr1.ADDRESS_ID) (foreign key join).
470
// The optimization is to drop addr1 and instead have: (emp.ADDR_ID = addr2.ADDRESS_ID).
471
// Since emp can have only 1 address (OneToOne) the addr1.equal(addr2) is
472
// implicit. This way if addr1 is used only for the comparison it can
473
// be optimized out.
474
// Also if addr2 were NULL there must be no join, just (emp.ADDR_ID = NULL)
475
// For bug 3105559 handle AggregateObject case (emp.get("period").equal(period2)
476
// which always falls into this case.
477
else if (!first.isExpressionBuilder() && !((QueryKeyExpression)first).isNormalizationRequired()) {
478             // Normalize firstChild's base only, as firstChild will be optimized out.
479
if (first.getBaseExpression() != null) {
480                 first.setBaseExpression(first.getBaseExpression().normalize(normalizer));
481             }
482
483             if (getSecondChild().isConstantExpression()) {
484                 Object JavaDoc targetObject = ((ConstantExpression)getSecondChild()).getValue();
485                 foreignKeyJoin = first.getMapping().buildObjectJoinExpression(first, targetObject, getSession());
486             } else if (getSecondChild().isObjectExpression() || getSecondChild().isParameterExpression()) {
487                 foreignKeyJoin = first.getMapping().buildObjectJoinExpression(first, getSecondChild(), getSession());
488             } else {
489                 throw QueryException.invalidUseOfToManyQueryKeyInExpression(this);
490             }
491         }
492
493         // DEFAULT: Left and right are separate entities, and the
494
// equal() will be replaced with a comparison by primary key.
495
if (foreignKeyJoin == null) {
496             first = (ObjectExpression)first.normalize(normalizer);
497
498             // A ConstantExpression stores a selection object. Compare the primary
499
// keys of the first expression and the selection object.
500
if (getSecondChild().isConstantExpression()) {
501                 Expression keyExpression = first.getDescriptor().getObjectBuilder().buildPrimaryKeyExpressionFromObject(((ConstantExpression)getSecondChild()).getValue(), getSession());
502                 foreignKeyJoin = first.twist(keyExpression, first);
503
504                 // Each expression will represent a separate table, so compare the primary
505
// keys of the first and second expressions.
506
} else if (getSecondChild().isObjectExpression() || getSecondChild().isParameterExpression()) {
507                 foreignKeyJoin = first.twist(first.getDescriptor().getObjectBuilder().getPrimaryKeyExpression(), getSecondChild());
508             } else {
509                 throw QueryException.invalidUseOfToManyQueryKeyInExpression(this);
510             }
511         }
512         if (getOperator().getSelector() == ExpressionOperator.NotEqual) {
513             foreignKeyJoin = foreignKeyJoin.not();
514         }
515
516         return foreignKeyJoin.normalize(normalizer);
517     }
518
519     /**
520      * INTERNAL:
521      * Check if the object conforms to the expression in memory.
522      * This is used for in-memory querying across object relationships.
523      */

524     public boolean performSelector(boolean areValuesEqual) {
525         if (getOperator().getSelector() == getOperator().Equal) {
526             return areValuesEqual;
527         }
528         if (getOperator().getSelector() == getOperator().NotEqual) {
529             return !areValuesEqual;
530         }
531
532         throw QueryException.cannotConformExpression();
533
534     }
535
536     /**
537      * INTERNAL:
538      * Print SQL
539      */

540     public void printSQL(ExpressionSQLPrinter printer) {
541         if (isEqualNull(printer)) {
542             getFirstChild().isNull().printSQL(printer);
543         } else if (isNotEqualNull(printer)) {
544             getFirstChild().notNull().printSQL(printer);
545         } else {
546             super.printSQL(printer);
547         }
548     }
549
550     /**
551      * INTERNAL:
552      * Print java for project class generation
553      */

554     public void printJava(ExpressionJavaPrinter printer) {
555         ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform());
556         Expression tempFirstChild = getFirstChild();
557         Expression tempSecondChild = getSecondChild();
558         realOperator.printJavaDuo(tempFirstChild, tempSecondChild, printer);
559     }
560
561     /**
562      * INTERNAL:
563      * Check if it is a FunctionExpression and its Operator is ToUpperCase.
564      */

565 /* public boolean isUpperCaseFunctionExpression(Expression expression, ExpressionJavaPrinter printer) {
566         return expression.isFunctionExpression() &&
567                 ((FunctionExpression)expression).getPlatformOperator(printer.getPlatform()).getSelector() == ExpressionOperator.ToUpperCase;
568     }*/

569
570     /**
571      * INTERNAL:
572      * Check if it is a ConstantExpression and its value(String) is all upper case.
573      */

574 /* public boolean isUpperCaseConstantExpression(Expression expression) {
575         return expression.isConstantExpression() && ((ConstantExpression)expression).getValue() instanceof String
576                 && Helper.isUpperCaseString((String)((ConstantExpression)expression).getValue());
577     }*/

578
579     /**
580      * INTERNAL:
581      * Print SQL without adding parentheses (for DB2 outer joins).
582      */

583     public void printSQLNoParens(ExpressionSQLPrinter printer) {
584         ExpressionOperator realOperator = getPlatformOperator(printer.getPlatform());
585         realOperator.printDuo(getFirstChild(), getSecondChild(), printer);
586     }
587
588     /**
589      * Do any required validation for this node. Throw an exception if it's incorrect.
590      */

591     public void validateNode() {
592         if (getFirstChild().isTableExpression()) {
593             throw QueryException.cannotCompareTablesInExpression(((TableExpression)getFirstChild()).getTable());
594         }
595         if (getSecondChild().isTableExpression()) {
596             throw QueryException.cannotCompareTablesInExpression(((TableExpression)getSecondChild()).getTable());
597         }
598     }
599 }
600
Popular Tags