KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jdo > spi > persistence > support > sqlstore > sql > generator > SelectStatement


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 in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
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 Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 /*
25  * SelectStatement.java
26  *
27  * Created on October 3, 2001
28  *
29  */

30
31 package com.sun.jdo.spi.persistence.support.sqlstore.sql.generator;
32
33 import org.netbeans.modules.dbschema.ColumnElement;
34
35 import com.sun.jdo.api.persistence.support.JDOFatalDataStoreException;
36 import com.sun.jdo.api.persistence.support.JDOFatalInternalException;
37 import com.sun.jdo.spi.persistence.support.sqlstore.ActionDesc;
38 import com.sun.jdo.spi.persistence.support.sqlstore.ValueFetcher;
39 import com.sun.jdo.spi.persistence.support.sqlstore.database.DBVendorType;
40 import com.sun.jdo.spi.persistence.support.sqlstore.model.TableDesc;
41 import com.sun.jdo.spi.persistence.support.sqlstore.sql.RetrieveDescImpl;
42 import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.ConstraintField;
43 import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.ConstraintFieldDesc;
44 import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.ConstraintNode;
45 import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.ConstraintOperation;
46 import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.ConstraintJoin;
47 import com.sun.jdo.spi.persistence.utility.I18NHelper;
48
49 import java.util.ArrayList JavaDoc;
50 import java.util.List JavaDoc;
51 import java.sql.SQLException JavaDoc;
52
53
54 /**
55  * This class generates select statements.
56  */

57 public class SelectStatement extends Statement {
58
59     /** Flag indicating if this statement has been joined. */
60     private boolean isJoined;
61
62     private StringBuffer JavaDoc orderClause = new StringBuffer JavaDoc();
63
64     /** SelectQueryplan */
65     SelectQueryPlan plan;
66
67     public SelectStatement(DBVendorType vendorType, SelectQueryPlan plan) {
68         super(vendorType);
69         this.plan = plan;
70         constraint = plan.getConstraint();
71     }
72
73     public boolean isJoined() {
74         return isJoined;
75     }
76
77     public void markJoined() {
78         isJoined = true;
79     }
80
81     public ColumnRef addColumn(ColumnElement columnElement,
82                           QueryTable queryTable) {
83
84         ColumnRef columnRef = null;
85
86         if ((columnRef = getColumnRef(columnElement)) == null) {
87             columnRef = new ColumnRef(columnElement, queryTable);
88             addColumnRef(columnRef);
89         }
90
91         return columnRef;
92     }
93
94     public void copyColumns(SelectStatement sourceStatement) {
95         ArrayList JavaDoc columnRefs = sourceStatement.getColumnRefs();
96
97         int index = columns.size() + 1;
98
99         for (int i = 0; i < columnRefs.size(); i++) {
100             //addColumnRef((ColumnRef) columnRefs.get(i));
101
ColumnRef cref = (ColumnRef) columnRefs.get(i);
102             cref.setIndex(index + i);
103             columns.add(cref);
104         }
105     }
106
107     protected boolean isUpdateLockRequired(QueryTable table) {
108         return (plan.options & RetrieveDescImpl.OPT_AGGREGATE) == 0
109                 && table.getTableDesc().isUpdateLockRequired();
110     }
111
112     public void appendTableText(StringBuffer JavaDoc text, QueryTable table) {
113         super.appendTableText(text, table);
114
115         if (isUpdateLockRequired(table)) {
116             //Append eqivalent of "with (updlock)" to the table text
117
text.append(vendorType.getHoldlock() );
118
119             //For efficiency, the test whether a database is capable of supporting
120
//holdlock would be made in method getText() where we generate
121
//text corresponding to vendorType.getForUpdate()
122
}
123     }
124
125     /**
126       * Determines if Column Type definition is needed for this statement.
127       * Column Type definition is a performance optimization that allows defining
128       * Column Type for the resultset.
129       * If the query to be executed is counting pc instances, the column used
130       * inside COUNT() is one of the pk columns. If the pk column happens to be
131       * not convertable to an int (for example timestamp), database will throw an
132       * exception. To prevent this situation, column type definition should not
133       * be performed on such queries.
134       *
135       * @return true if column type definition is needed, false otherwise.
136       */

137      public boolean isColumnTypeDefinitionNeeded() {
138          return (plan.options & RetrieveDescImpl.OPT_COUNT_PC ) == 0;
139      }
140
141     /** @inheritDoc */
142     public QueryPlan getQueryPlan() {
143         return plan;
144     }
145
146     /**
147      * @inheritDoc
148      */

149     protected void generateStatementText() {
150         // Because join conditions for ANSI outer joins end up in the
151
// from clause, the constraint stack has to be processed before we
152
// generate the from clause.
153
StringBuffer JavaDoc constraints = processConstraints();
154         StringBuffer JavaDoc outerJoinText = processOuterJoinConstraints();
155
156         if (outerJoinText != null && outerJoinText.length() > 0) {
157             if (constraints.length() > 0 ) {
158                 constraints.append(" and ");
159             }
160             constraints.append(outerJoinText);
161         }
162
163         StringBuffer JavaDoc whereClause = new StringBuffer JavaDoc();
164
165         if (constraints.length() > 0 ) {
166             whereClause.append(" where ").append(constraints);
167         }
168
169         if ((plan.options & RetrieveDescImpl.OPT_COUNT_PC) == 0) {
170             generateRegularStatementText(whereClause);
171         } else {
172             generateCountStatementText(whereClause);
173         }
174     }
175
176     /**
177      * Generates the statement text for a "regular" select query.
178      * Count queries have to get special attention in case of objects
179      * with composite primary key.
180      *
181      * @param whereClause Query's where clause.
182      * @see #generateCountStatementText
183      */

184     private void generateRegularStatementText(StringBuffer JavaDoc whereClause) {
185
186         statementText = new StringBuffer JavaDoc();
187
188         StringBuffer JavaDoc columnText = generateColumnText();
189         String JavaDoc tableListText = generateTableListText();
190         String JavaDoc aggregateText = getAggregateText();
191         String JavaDoc aggregateEnd = (aggregateText.length() > 0) ? ")" : ""; // NOI18N
192

193         if (orderClause.length() > 0) {
194             orderClause.insert(0, " order by ");
195         }
196
197         final boolean updateLockRequired = isUpdateLockRequired();
198         StringBuffer JavaDoc forUpdateClause = generateForUpdateClause(updateLockRequired);
199         String JavaDoc distinctText = getDistinctText(updateLockRequired);
200
201         // Create the query filling in the column list, table name, etc.
202
statementText.append("select "). // NOI18N
203
append(aggregateText).append(distinctText).append(columnText).append(aggregateEnd).
204                 append(" from ").append(tableListText). // NOI18N
205
append(whereClause).append(orderClause).append(forUpdateClause);
206     }
207
208     /**
209      * Generates the statement text for a count query. Count queries
210      * on persistence capable objects have been mapped to selecting the
211      * primary key columns in
212      * {@link SelectQueryPlan#addFetchGroups(int, ArrayList, ArrayList)}.
213      * Queries w/o a distinct restriction can be relaxed to select only
214      * one of the primary key columns. Distinct queries on objects with
215      * composite primary key have to be treated special to avoid duplicates.
216      *
217      * @param whereClause Query's where clause.
218      * @see #generateCorrelatedExistsText
219      */

220     private void generateCountStatementText(StringBuffer JavaDoc whereClause) {
221
222         final int selectedColumns = columns.size();
223
224         if (selectedColumns == 1) {
225             // Single PK. Call regular statement generation.
226
generateRegularStatementText(whereClause);
227         } else {
228             boolean oneTable = tableList.size() == 1;
229
230             if ((plan.options & RetrieveDescImpl.OPT_DISTINCT) == 0
231                     || oneTable) {
232                 // Without DISTINCT or when querying just one table, we can
233
// select only one of the pk columns and get the correct result.
234
// Remove the rest.
235
for (int i = selectedColumns; i > 1; ) { columns.remove(--i); }
236                 if (oneTable) {
237                     // When selecting only one table, remove the DISTINCT
238
// contraint from the query options to get all rows.
239
plan.options &= ~RetrieveDescImpl.OPT_DISTINCT;
240                 }
241                 // Now call regular statement generation.
242
generateRegularStatementText(whereClause);
243             } else {
244                 // This is a distinct count on objects having a composite pk and the query
245
// includes join constraints. We map this to a correlated exists query.
246
// Note: columns can only be cleared after this call.
247
generateCorrelatedExistsText(whereClause);
248
249                 // Since we're not selecting any columns in this query, remove all column
250
// information. Oracle's special DB operation code gets confused otherwise.
251
// See OracleSpecialDBOperation#defineColumnTypeForResult(Statement, List)
252
columns.clear();
253             }
254         }
255     }
256
257     /**
258      * Generates the text for a correlated exists query. Count distinct queries on
259      * objects with composite primary key are mapped to a correlated exists query.
260      * The "outer" select on the primary table to is correlated
261      * to the "inner" select by the already generated where clause.
262      *
263      * @param whereClause Query's where clause.
264      */

265     private void generateCorrelatedExistsText(StringBuffer JavaDoc whereClause) {
266
267         statementText = new StringBuffer JavaDoc();
268
269         // TODO: Use correlated exists subquery in SelectQueryPlan?
270
// - Do we handle secondary tables correctly?
271
// - Do we need order by and for update clauses?
272

273         // Generate for update clause while we still have all tables in tableList
274
boolean updateLockRequired = isUpdateLockRequired();
275         StringBuffer JavaDoc forUpdateClause = generateForUpdateClause(updateLockRequired);
276
277         StringBuffer JavaDoc primaryTableText = new StringBuffer JavaDoc();
278         QueryTable primaryTable = generatePrimaryTableText(primaryTableText);
279
280         // Prepare the generation of the correlated "inner" select clause by
281
// removing the primary table from the table list.
282
// As count queries are never generated internally, we're preparing
283
// a user query here. User queries never have outer joins. It's safe
284
// remove the table from tableList.
285
tableList.remove(primaryTable);
286         String JavaDoc tableListText = generateTableListText();
287
288         // Create the query with the previous generated parts.
289
statementText.append("select count(*) from "). // NOI18N
290
append(primaryTableText).
291                 append(" where exists (select * from "). // NOI18N
292
append(tableListText).append(whereClause).append(")"). //NOI18N
293
append(forUpdateClause);
294     }
295
296     /**
297      * Generates the table text for the first column of the column list.
298      *
299      * @param primaryTableText Takes the resulting statement text.
300      * @return The table from the first selected column.
301      */

302     private QueryTable generatePrimaryTableText(StringBuffer JavaDoc primaryTableText) {
303         // Get the primary table from the first selected column.
304
// TODO: Is the first column always mapped to the primary table?
305
QueryTable primaryTable = ((ColumnRef)columns.get(0)).getQueryTable();
306
307         // Generate the table text.
308
appendTableText(primaryTableText, primaryTable);
309
310         return primaryTable;
311     }
312
313     protected StringBuffer JavaDoc generateColumnText() {
314         StringBuffer JavaDoc columnText = new StringBuffer JavaDoc();
315
316         for (int i = 0; i < columns.size(); i++) {
317             ColumnRef cr = (ColumnRef) columns.get(i);
318
319             columnText.append("t").append(cr.getQueryTable().getTableIndex()).append("."); // NOI18N
320
appendQuotedText(columnText, cr.getName());
321             columnText.append(", "); // NOI18N
322
}
323         columnText.delete(columnText.length() - 2, columnText.length());
324         return columnText;
325     }
326
327     private String JavaDoc getAggregateText() {
328         int aggregateOption = plan.options & RetrieveDescImpl.OPT_AGGREGATE;
329
330         switch (aggregateOption) {
331             case RetrieveDescImpl.OPT_AVG:
332                 return "AVG( "; // NOI18N
333
case RetrieveDescImpl.OPT_MIN:
334                 return "MIN("; // NOI18N
335
case RetrieveDescImpl.OPT_MAX:
336                 return "MAX("; // NOI18N
337
case RetrieveDescImpl.OPT_SUM:
338                 return "SUM("; // NOI18N
339
case RetrieveDescImpl.OPT_COUNT:
340             case RetrieveDescImpl.OPT_COUNT_PC:
341                 return "COUNT("; // NOI18N
342
default:
343                 return ""; // NOI18N
344
}
345     }
346
347     private StringBuffer JavaDoc generateForUpdateClause(boolean updateLockRequired) {
348         StringBuffer JavaDoc forUpdateClause = new StringBuffer JavaDoc();
349
350         if (updateLockRequired) {
351             // Check if vendor actually supports updatelock
352
if (!vendorType.isUpdateLockSupported() ) {
353                 // Throw an exception user wanted to have update lock
354
// But vendor is not supporting it. Do not allow user to proceed
355
throw new JDOFatalDataStoreException(I18NHelper.getMessage(messages,
356                         "sqlstore.selectstatement.noupdatelocksupport"));// NOI18N
357
}
358
359             // generating the ForUpdate Clause
360
String JavaDoc vendorForUpdate = vendorType.getForUpdate().trim();
361             boolean vendorHasForUpdateClause = (vendorForUpdate.length() != 0);
362
363             if (vendorHasForUpdateClause) {
364                 forUpdateClause.append(" ").append(vendorForUpdate).append(" ");
365
366                 if (vendorType.isLockColumnListSupported()) {
367                     for (int i = 0; i < tableList.size(); i++) {
368                         QueryTable queryTable = (QueryTable) tableList.get(i);
369                         if (isUpdateLockRequired(queryTable)) {
370                             TableDesc tableDesc = queryTable.getTableDesc();
371                             //Get the first column of primary key
372
ColumnElement ce = (ColumnElement) tableDesc.getKey().getColumns().get(0);
373                             forUpdateClause.append("t").append(i).append("."); // NOI18N
374
appendQuotedText(forUpdateClause, ce.getName().getName());
375                             forUpdateClause.append(", "); // NOI18N
376
}
377                     }
378                     // Remove trailing ", "
379
forUpdateClause.delete(forUpdateClause.length() - 2, forUpdateClause.length());
380                 }
381             }
382         }
383
384         return forUpdateClause;
385     }
386
387     private String JavaDoc getDistinctText(boolean updateLockRequired) {
388         String JavaDoc distinctText = ""; // NOI18N
389

390         if ((plan.options & RetrieveDescImpl.OPT_DISTINCT) > 0) {
391             if( !updateLockRequired || vendorType.isDistinctSupportedWithUpdateLock()) {
392                 //Include DISTINCT only if update lock is not required
393
//If update lock is required, include DISTINCT only if vendor supports update lock with DISTINCT
394
//(e.g. ORACLE throws ORA-01786 if DISTINCT and FOR UPDATE are used in same query
395

396                 //For the case where update lock is required and vendor does not support DISTINCT/
397
//with update lock, we would do DISTINCT in our code after retrieving the data
398
//see SQLStoreManger::retrieve()
399
distinctText = "distinct "; // NOI18N
400
}
401         }
402         return distinctText;
403     }
404
405     /**
406      * Determines if an update lock is required while executing this query.
407      *
408      * @return True if any of the tables invloved in this query requires
409      * update lock
410      */

411     private boolean isUpdateLockRequired() {
412         boolean updateLockRequired = false;
413
414         // TODO: We can optimize this by storing the value in a member variable
415
for (int i = 0; i < tableList.size() && !updateLockRequired; i++) {
416             QueryTable queryTable = (QueryTable) tableList.get(i);
417             updateLockRequired = isUpdateLockRequired(queryTable);
418         }
419
420         return updateLockRequired;
421     }
422
423     /**
424      * Processes Order By constraints and calls the super class
425      * method for all other constrains.
426      */

427     protected void processRootConstraint(ConstraintOperation opNode,
428                                          List JavaDoc stack,
429                                          StringBuffer JavaDoc whereText) {
430         int op = opNode.operation;
431         int opInfo = operationFormat(op);
432
433         if ((opInfo & OP_ORDERBY_MASK) > 0) {
434             stack.remove(stack.size() - 1);
435             ConstraintNode node = (ConstraintNode) stack.get(stack.size() - 1);
436
437             if (!(node instanceof ConstraintField)) {
438                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
439                         "core.constraint.needfieldnode")); // NOI18N
440
} else {
441                 processOrderByField((ConstraintFieldDesc) node, op);
442                 stack.remove(stack.size() - 1);
443             }
444         } else {
445             super.processRootConstraint(opNode, stack, whereText);
446         }
447     }
448
449     protected void processIrregularOperation(ConstraintOperation opNode,
450                                              int opCode,
451                                              List JavaDoc stack,
452                                              StringBuffer JavaDoc result) {
453         switch (opCode) {
454             case ActionDesc.OP_EQUIJOIN:
455                 processJoinOperation((ConstraintJoin)opNode, result);
456                 break;
457             default:
458                 super.processIrregularOperation(opNode, opCode, stack, result);
459         }
460     }
461
462     /**
463      * Process outer join constraints for this statement.
464      *
465      * @return A string buffer representing outer join conditions for this
466      * statement. Please note that the returned string buffer will have text
467      * only for Oracle
468      */

469     private StringBuffer JavaDoc processOuterJoinConstraints() {
470         StringBuffer JavaDoc joinCondition = null;
471         final List JavaDoc joinStack = constraint.getOuterJoinConstraints();
472         final int joinStackSize = joinStack.size();
473
474         if (joinStackSize > 0) {
475             joinCondition = new StringBuffer JavaDoc();
476             for (int i = 0; i < joinStackSize; i++) {
477                 ConstraintJoin joinNode = (ConstraintJoin) joinStack.get(i);
478                 processJoinOperation(joinNode, joinCondition);
479             }
480         }
481         return joinCondition;
482     }
483
484     /**
485      * Generates a join condition for specified jnode. Equi joins and native
486      * outer joins end up in the where clause. Ansi compliant outer join
487      * conditions end up in the from clause.
488      *
489      * @param jnode Join constraint.
490      * @param whereText String buffer taking the join condition. Generated
491      * join condition is appended to this string buffer.
492      */

493     private void processJoinOperation(ConstraintJoin jnode,
494                                       StringBuffer JavaDoc whereText) {
495         int opCode = jnode.operation;
496         // Generate ANSI outer joins if doAnsiJoin == true,
497
// i.e. the vendor has no "native" outer join semantics.
498
boolean doAnsiJoin = opCode != ActionDesc.OP_EQUIJOIN
499                 && !vendorType.isNativeOuterJoin();
500
501         if (doAnsiJoin) {
502             generateAnsiJoin(jnode, opCode);
503         } else {
504             generateJoin(jnode, whereText, opCode);
505         }
506     }
507
508     /**
509      * Generate a "regular" join. The columns for the join condition
510      * end up in the where clause. The corresponding tables are added
511      * directly to the member variable tableList.
512      * Note that this method is normally called to process EQUI- joins
513      * only. For databases that are marked for native join sematics,
514      * this method will be called to process both EQUI- and OUTER- joins.
515      *
516      * @param jnode Join constraint.
517      * @param whereText String buffer taking the join condition. Generated
518      * join condition is appended to this string buffer.
519      * @param opCode Join operation.
520      */

521     private void generateJoin(ConstraintJoin jnode,
522                               StringBuffer JavaDoc whereText,
523                               int opCode) {
524
525         for (int i = 0; i < jnode.fromColumns.size(); i++) {
526             ColumnElement fromColumn = (ColumnElement)jnode.fromColumns.get(i);
527             ColumnElement toColumn = (ColumnElement)jnode.toColumns.get(i);
528             QueryTable fromTable = findQueryTable(jnode.fromPlan, fromColumn);
529             QueryTable toTable = findQueryTable(jnode.toPlan, toColumn);
530
531             addQueryTable(fromTable);
532             addQueryTable(toTable);
533             toTable.prevTable = null;
534
535             appendJoinCondition(whereText,
536                     fromTable, toTable, fromColumn, toColumn,
537                     getJoinOperator(opCode));
538
539             if (opCode == ActionDesc.OP_LEFTJOIN ) {
540                 // Append oracle style (+) or similar.
541
whereText.append(vendorType.getLeftJoinPost());
542             }
543         }
544     }
545
546     /**
547      * Generates an ANSI compliant join. The columns for the join
548      * condition end up in the from clause. The joined tables
549      * are added indirectly to the member variable tableList by
550      * being added to {@link QueryTable#nextTable} associated with
551      * the "from-" table.
552      *
553      * @param jnode Join constraint.
554      * @param opCode Join operation.
555      * @see #processFromClause
556      */

557     private void generateAnsiJoin(ConstraintJoin jnode, int opCode) {
558
559         for (int i = 0; i < jnode.fromColumns.size(); i++) {
560             ColumnElement fromColumn = (ColumnElement)jnode.fromColumns.get(i);
561             ColumnElement toColumn = (ColumnElement)jnode.toColumns.get(i);
562             QueryTable fromTable = findQueryTable(jnode.fromPlan, fromColumn);
563             QueryTable toTable = findQueryTable(jnode.toPlan, toColumn);
564
565             // Process the from clause
566
processFromClause(fromTable, toTable);
567
568             // Process the on clause.
569
if (toTable.onClause == null) {
570                 toTable.onClause = new StringBuffer JavaDoc();
571             }
572
573             appendJoinCondition(toTable.onClause, fromTable, toTable, fromColumn, toColumn, "="); //NOI18N
574

575             fromTable.joinOp = opCode;
576         }
577     }
578
579     /**
580      * Processes specified fromTable and toTable to generate appropriate from
581      * clause when table text is generated.
582      * toTable is added to fromTable.nextTable if not already present. See
583      * {@link #appendAnsiJoinTableText(StringBuffer,QueryTable)} for
584      * details on how this is used to generate table text.
585      *
586      * @param fromTable The from table
587      * @param toTable The to table.
588      */

589     private static void processFromClause(QueryTable fromTable, QueryTable toTable) {
590
591         if (toTable.prevTable != null && toTable.prevTable != fromTable) {
592             // TODO
593
}
594
595         // TODO:
596
// Check that these tables aren't already participating in
597
// a join (except to each other). If either of them are
598
// then we will have to make a new table alias for that
599
// table and put in an addtional equijoin between the old
600
// alias and the new based, of course, on the key columns.
601

602         if (fromTable.nextTable == null) {
603             fromTable.nextTable = new ArrayList JavaDoc();
604             fromTable.nextTable.add(toTable);
605             toTable.prevTable = fromTable;
606         } else {
607             // Make sure we don't add the same table twice.
608
if (!fromTable.nextTable.contains(toTable)) {
609                 fromTable.nextTable.add(toTable);
610                 toTable.prevTable = fromTable;
611             }
612         }
613     }
614
615     /**
616      * Appends join condition corresponding to specified fromTable, toTable,
617      * fromColumn, toColumn, joinOp to result. If result is not empty " and "
618      * will be appended to it before appending the join condition.
619      *
620      * @param result The string buffer to which the condition will be appended.
621      * @param fromTable The from table.
622      * @param toTable The to table.
623      * @param fromColumn The from column.
624      * @param toColumn The to column.
625      * @param joinOp Join operation.
626      */

627     private void appendJoinCondition(StringBuffer JavaDoc result,
628                                      QueryTable fromTable, QueryTable toTable,
629                                      ColumnElement fromColumn, ColumnElement toColumn,
630                                      String JavaDoc joinOp) {
631         if (result.length() > 0) {
632             // Composite fk.
633
result.append(" and ");
634         }
635
636         result.append("t").append(fromTable.getTableIndex()).append("."); // NOI18N
637
appendQuotedText(result, fromColumn.getName().getName());
638         result.append(" ").append(joinOp). // NOI18N
639
append(" t").append(toTable.getTableIndex()).append("."); // NOI18N
640
appendQuotedText(result, toColumn.getName().getName());
641     }
642
643     /**
644      * Returns join operator for specified operation.
645      *
646      * @param operation The join operation. Should be one of
647      * ActionDesc.OP_EQUIJOIN, ActionDesc.OP_LEFTJOIN or ActionDesc.OP_RIGHTJOIN.
648      * @return join operator for specified operation.
649      */

650     protected String JavaDoc getJoinOperator(int operation) {
651         String JavaDoc result = null;
652
653         switch (operation) {
654             case ActionDesc.OP_EQUIJOIN:
655                 result = " = "; // NOI18N
656
break;
657             case ActionDesc.OP_LEFTJOIN:
658                 result = vendorType.getLeftJoin();
659                 break;
660             case ActionDesc.OP_RIGHTJOIN:
661                 result = vendorType.getRightJoin();
662                 break;
663             default:
664                 throw new JDOFatalInternalException(
665                         I18NHelper.getMessage(messages,
666                         "core.constraint.illegalop", operation)); // NOI18N
667
}
668         return result;
669     }
670
671     private static QueryTable findQueryTable(QueryPlan plan, ColumnElement ce) {
672         QueryTable table = plan.findQueryTable(ce.getDeclaringTable());
673
674         if (table == null) {
675             // TODO: throw exception
676
}
677
678         return table;
679     }
680
681     private String JavaDoc generateTableListText() {
682         StringBuffer JavaDoc str = new StringBuffer JavaDoc();
683
684         for (int i = 0; i < tableList.size(); i++) {
685             QueryTable t = (QueryTable) tableList.get(i);
686
687             if (t.prevTable == null && t.nextTable == null) {
688                 appendTableText(str, t);
689                 str.append(", "); // NOI18N
690
} else {
691                 // Table is part of an outer join list.
692

693                 if (t.prevTable == null) {
694                     // Beginning of the list.
695

696                     appendAnsiJoinText(str, t);
697                 } else {
698                     // The table is in the "middle" of the list.
699

700                     while (t.prevTable != null) {
701                         t = t.prevTable;
702                     }
703
704                     if (!tableList.contains(t)) {
705                         // Outer join list starts with a join table.
706
// Because join tables aren't in the table list,
707
// they wouldn't be included in the table text.
708

709                         appendAnsiJoinText(str, t);
710                     }
711                 }
712             }
713         }
714
715         str.delete(str.length() - 2, str.length());
716
717         return str.toString();
718     }
719
720     private void appendAnsiJoinText(StringBuffer JavaDoc str, QueryTable t) {
721         // TODO: getTableListStart() and getTableListEnd() returns ""
722
// for all the databases. Do we need it ?
723
str.append(vendorType.getTableListStart());
724         appendAnsiJoinTableText(str, t);
725         str.append(vendorType.getTableListEnd());
726         str.append(", "); // NOI18N
727
}
728
729     /**
730      * Appends sql text corresponding to specified <code>table</code> to specified text.
731      * The linked list starting with table.nextTable is walked recursively
732      * to generate join text.
733      *
734      * @param text The string buffer receiving sql text.
735      * @param table Table to be joined.
736      */

737     private void appendAnsiJoinTableText(StringBuffer JavaDoc text, QueryTable table) {
738
739         if (table.joinOp == ActionDesc.OP_RIGHTJOIN) {
740             text.append(vendorType.getRightJoinPre());
741         }
742
743         if (table.prevTable == null) {
744             appendTableText(text, table);
745         }
746
747         for (int i = 0; i < table.nextTable.size(); i++) {
748             QueryTable toTable = (QueryTable) table.nextTable.get(i);
749             text.append(getJoinOperator(table.joinOp)).append(" "); // NOI18N
750

751             appendTableText(text, toTable);
752
753             if (toTable.onClause != null) {
754                 text.append(" on "); // NOI18N
755
text.append(toTable.onClause);
756             }
757
758             if (toTable.nextTable != null) {
759                 appendAnsiJoinTableText(text, toTable);
760             }
761
762             // Note: Since this method is called only for ANSI joins,
763
// and only oracle has getLeftJoinPost() defined, we will never
764
// append any text through following code.
765
if (table.joinOp == ActionDesc.OP_LEFTJOIN) {
766                 text.append(vendorType.getLeftJoinPost());
767             }
768         }
769     }
770
771     /**
772      * Adds a column corresponding to field <code>fieldNode</code> and
773      * order operation <code>op</code> to the order constraint.
774      *
775      * @param fieldNode Constraint on a field name of a persistence capable class.
776      * @param op Order operation.
777      */

778     private void processOrderByField(ConstraintFieldDesc fieldNode, int op) {
779         QueryPlan thePlan = getOriginalPlan(fieldNode);
780         StringBuffer JavaDoc orderText = new StringBuffer JavaDoc();
781
782         generateColumnText(fieldNode.desc, thePlan, orderText);
783
784         if (op == ActionDesc.OP_ORDERBY_DESC) {
785             orderText.append(" desc"); // NOI18N
786
}
787
788         if (orderClause.length() > 0) {
789             orderText.append(", "); // NOI18N
790
orderText.append(orderClause);
791         }
792         orderClause = orderText;
793     }
794
795     /**
796      * Binds input valus corrsponding to this <code>Statement</code> object to
797      * database statement s.
798      * @param s The database statement.
799      * @param parameters If an InputValue to be bound is a parameter, the actual
800      * value is contained in this.
801      * @throws SQLException
802      */

803     public void bindInputValues(DBStatement s, ValueFetcher parameters)
804             throws SQLException JavaDoc {
805         for (int i = 0, size = inputDesc.values.size(); i < size; i++) {
806             InputValue inputVal = (InputValue) inputDesc.values.get(i);
807             s.bindInputColumn(i + 1, getInputValue(inputVal, parameters),
808                     inputVal.getColumnElement(), vendorType);
809         }
810     }
811
812     /**
813      * Get Input values to be bound to this statement.
814      * @param parameters If an InputValue to be bound is a parameter, the actual
815      * value is contained in this.
816      * @return An Object array containing input values to be bound to this statement.
817      */

818     private Object JavaDoc[] getInputValues(ValueFetcher parameters) {
819         final int size = inputDesc.values.size();
820         Object JavaDoc[] inputValues = new Object JavaDoc[size];
821         for (int i = 0; i < size; i++) {
822             InputValue inputValue = (InputValue) inputDesc.values.get(i);
823             inputValues[i] = getInputValue(inputValue, parameters);
824         }
825         return inputValues;
826     }
827
828     /**
829      * Gets formatted sql text corrsponding to this statement object. The text
830      * also contains values for input to the statement.
831      * @param parameters The input paramters to this statement.
832      * @return formatted sql text corrsponding to this statement object.
833      */

834     public String JavaDoc getFormattedSQLText(ValueFetcher parameters) {
835         return formatSqlText(getText(), getInputValues(parameters)) ;
836     }
837
838     /**
839      * Gets actual value corresponding to <code>inputVal</code>. If
840      * <code>inputVal</code> is an instanceof InputValue then value contained
841      * in inputVal is returned. If <code>inputVal</code> is an instanceof
842      * InputParamValue then value returned is obtained from <code>parameters
843      * </code>.
844      * @param inputVal The input value.
845      * @param parameters The parameters.
846      * @return Appropriate value as described above.
847      */

848     private static Object JavaDoc getInputValue(InputValue inputVal,
849             ValueFetcher parameters) {
850         Object JavaDoc val;
851         if (inputVal instanceof InputParamValue) {
852             int paramIndex = ((InputParamValue) inputVal).getParamIndex().intValue();
853             val = parameters.getValue(paramIndex);
854         }
855         else {
856             val = inputVal.getValue();
857         }
858         return val;
859     }
860
861 }
862
Popular Tags