KickJava   Java API By Example, From Geeks To Geeks.

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


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  * Statement.java
26  *
27  * Created on March 3, 2000
28  *
29  */

30
31 package com.sun.jdo.spi.persistence.support.sqlstore.sql.generator;
32
33 import org.netbeans.modules.dbschema.ColumnElement;
34 import org.netbeans.modules.dbschema.TableElement;
35 import com.sun.jdo.api.persistence.support.JDOFatalInternalException;
36 import com.sun.jdo.spi.persistence.support.sqlstore.ActionDesc;
37 import com.sun.jdo.spi.persistence.support.sqlstore.database.DBVendorType;
38 import com.sun.jdo.spi.persistence.support.sqlstore.model.LocalFieldDesc;
39 import com.sun.jdo.spi.persistence.support.sqlstore.sql.constraint.*;
40 import com.sun.jdo.spi.persistence.utility.I18NHelper;
41
42 import java.sql.SQLException JavaDoc;
43 import java.util.ArrayList JavaDoc;
44 import java.util.Iterator JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.ResourceBundle JavaDoc;
47
48 /**
49  * This class is used to represent a SQL statement.
50  */

51 public abstract class Statement extends Object JavaDoc implements Cloneable JavaDoc {
52
53     private static final Integer JavaDoc ONE = new Integer JavaDoc(1);
54
55     protected static final int OP_PREFIX_MASK = 0x001; // 1
56

57     protected static final int OP_INFIX_MASK = 0x002; // 2
58

59     protected static final int OP_POSTFIX_MASK = 0x004; // 4
60

61     protected static final int OP_PAREN_MASK = 0x008; // 8
62

63     protected static final int OP_ORDERBY_MASK = 0x010; // 16
64

65     protected static final int OP_WHERE_MASK = 0x020; // 32
66

67     protected static final int OP_IRREGULAR_MASK = 0x040; // 64
68

69     protected static final int OP_OTHER_MASK = 0x080; // 128
70

71     protected static final int OP_PARAM_MASK = 0x100; // 256
72

73     protected static final int OP_BINOP_MASK = 2 * OP_PARAM_MASK | OP_WHERE_MASK | OP_INFIX_MASK; // 546
74

75     protected static final int OP_FUNC_MASK = OP_PARAM_MASK | OP_PREFIX_MASK | OP_PAREN_MASK | OP_WHERE_MASK; // 297
76

77     protected static final int OP_PCOUNT_MASK = 3 * OP_PARAM_MASK; // 768
78

79     protected StringBuffer JavaDoc statementText;
80
81     private String JavaDoc quoteCharStart;
82
83     private String JavaDoc quoteCharEnd;
84
85     /** array of ColumnRef */
86     protected ArrayList JavaDoc columns;
87
88     Constraint constraint;
89
90     protected InputDesc inputDesc;
91
92     int action;
93
94     /** array of QueryTable */
95     public ArrayList JavaDoc tableList;
96
97     protected DBVendorType vendorType;
98
99     protected ArrayList JavaDoc secondaryTableStatements;
100
101     /**
102      * I18N message handler
103      */

104     protected final static ResourceBundle JavaDoc messages = I18NHelper.loadBundle(
105            "com.sun.jdo.spi.persistence.support.sqlstore.Bundle", // NOI18N
106
Statement.class.getClassLoader());
107
108
109     public Statement(DBVendorType vendorType) {
110
111         inputDesc = new InputDesc();
112         columns = new ArrayList JavaDoc();
113         constraint = new Constraint();
114         tableList = new ArrayList JavaDoc();
115         this.vendorType = vendorType;
116
117         if (vendorType.getQuoteSpecialOnly() == false) {
118             // DO NOT SUPPORT QUOTING OTHERWISE
119
this.quoteCharStart = vendorType.getQuoteCharStart();
120             this.quoteCharEnd = vendorType.getQuoteCharEnd();
121         }
122     }
123
124     public void addQueryTable(QueryTable table) {
125         //
126
// First check to make sure this is not a duplicate.
127
//
128
if (tableList.indexOf(table) == -1) {
129             tableList.add(table);
130         }
131     }
132
133     protected ColumnRef getColumnRef(ColumnElement columnElement) {
134         //
135
// Check whether this column has already been added.
136
// If so, simply return.
137
//
138
int size = columns.size();
139
140         for (int i = 0; i < size; i++) {
141             ColumnRef cref = (ColumnRef) columns.get(i);
142
143             if (cref.getColumnElement() == columnElement) return cref;
144         }
145
146         return null;
147     }
148
149     protected void addColumnRef(ColumnRef columnRef) {
150         columnRef.setIndex(columns.size()+1);
151         columns.add(columnRef);
152     }
153
154     /**
155      * Adds a comparison on local field <CODE>lf</CODE> and value <CODE>value</CODE>.
156      */

157     public void addConstraint(LocalFieldDesc lf, Object JavaDoc value) {
158         int operation;
159         if (value == null) {
160             operation = ActionDesc.OP_NULL;
161         } else {
162             constraint.addValue(value, lf);
163             if (lf.isPrimitiveMappedToNullableColumn() ||
164                        (vendorType.mapEmptyStringToNull() && lf.getType() == String JavaDoc.class)) {
165                 // Primitive fields mapped to nullable columns might hold
166
// a default value indicting that the actual db column is null.
167
// Databases might treat zero length Strings as null.
168
operation = ActionDesc.OP_MAYBE_NULL;
169             } else {
170                 // Object type fields or primitive fields
171
// mapped to non nullable columns are compared exactly
172
operation = ActionDesc.OP_EQ;
173             }
174         }
175         constraint.addField(lf);
176         constraint.addOperation(operation);
177     }
178
179     public DBVendorType getVendorType() {
180         return vendorType;
181     }
182
183     public void appendTableText(StringBuffer JavaDoc text, QueryTable table) {
184         appendQuotedText(text, table.getTableDesc().getName());
185         text.append(" t"); // NOI18N
186
text.append(table.getTableIndex());
187     }
188
189     /**
190      * Append <code>text</code> surrounded by quote char to <code>buffer</code>
191      * @param buffer The given buffer
192      * @param text The given text
193      */

194     protected void appendQuotedText(StringBuffer JavaDoc buffer, String JavaDoc text) {
195         buffer.append(quoteCharStart);
196         buffer.append(text);
197         buffer.append(quoteCharEnd);
198     }
199
200     /**
201      * Returns the SQL text for the query described by this object.
202      *
203      * @return The text of the SQL query described by this object.
204      */

205     public String JavaDoc getText() {
206
207         if (statementText == null) {
208             generateStatementText();
209         }
210
211
212         return statementText.toString();
213     }
214
215     /**
216      * Generates the SQL text for the query described by this object.
217      */

218     protected abstract void generateStatementText();
219
220     /**
221      * Processes the constraint stack and adds it to the query.
222      * This means turning the constraint stack into SQL text and
223      * adding it to the where clause.
224      *
225      * @return Where clause based on the constraint stack.
226      */

227     public StringBuffer JavaDoc processConstraints() {
228         StringBuffer JavaDoc whereText = new StringBuffer JavaDoc();
229         List JavaDoc stack = constraint.getConstraints();
230
231         while (stack.size() > 0) {
232             ConstraintNode node = (ConstraintNode) stack.get(stack.size() - 1);
233
234             if (!(node instanceof ConstraintOperation)) {
235                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
236                         "core.generic.notinstanceof", // NOI18N
237
node.getClass().getName(), "ConstraintOperation")); // NOI18N
238
}
239
240             processRootConstraint((ConstraintOperation) node, stack, whereText);
241         }
242
243         return whereText;
244     }
245
246     protected void processRootConstraint(ConstraintOperation opNode,
247                                          List JavaDoc stack,
248                                          StringBuffer JavaDoc whereText) {
249         int op = opNode.operation;
250         int opInfo = operationFormat(op);
251
252         if ((opInfo & OP_WHERE_MASK) > 0) {
253             String JavaDoc constraint = getWhereText(stack);
254             if (whereText.length() > 0 && constraint.length() > 0) {
255                 // This is neccessary, if the constraint stack is "un-balanced",
256
// see OrderingTest#ordering006 for an example.
257
whereText.append(" and ");
258             }
259             whereText.append(constraint);
260         } else {
261                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
262                         "sqlstore.sql.generator.statement.unexpectedconstraint", op)); // NOI18N
263
}
264     }
265
266     /**
267      * Get QueryPlan for this statement
268      * @return QueryPlan for this statement
269      */

270     public abstract QueryPlan getQueryPlan();
271
272     /**
273      * Constructs the where clause for the statement from
274      * the constraint stack.
275      *
276      * @param stack
277      * The stack parameter holds the constraint stack to be decoded.
278      *
279      * RESOLVE: We don't support constraints on multiple statements yet.
280      * We would need to sort constraints out by statement and do something
281      * about constraints that span statements (e.g. t1.c1 = t2.c2).
282      */

283     protected String JavaDoc getWhereText(List JavaDoc stack) {
284
285         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
286         ConstraintNode node;
287
288         if (stack.size() == 0) {
289             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
290                     "core.constraint.stackempty")); // NOI18N
291
}
292
293         node = (ConstraintNode) stack.get(stack.size() - 1);
294         stack.remove(stack.size() - 1);
295
296         if (node instanceof ConstraintParamIndex) {
297             processConstraintParamIndex((ConstraintParamIndex) node, result);
298         } else if (node instanceof ConstraintValue) {
299             processConstraintValue((ConstraintValue) node, result);
300         } else if (node instanceof ConstraintField) {
301             processConstraintField((ConstraintField) node, result);
302         } else if (node instanceof ConstraintConstant) {
303             result.append(((ConstraintConstant) node).value.toString());
304         } else if (node instanceof ConstraintOperation) {
305             processConstraintOperation((ConstraintOperation) node, stack, result);
306         } else {
307             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
308                     "core.constraint.illegalnode", // NOI18N
309
node.getClass().getName()));
310         }
311
312         return result.toString();
313     }
314
315     protected void processConstraintParamIndex(ConstraintParamIndex node, StringBuffer JavaDoc result) {
316         // DB2 requires cast for parameter markers involved in numeric expressions.
317
result.append(vendorType.getParameterMarker(node.getType()));
318         Integer JavaDoc index = node.getIndex();
319         inputDesc.values.add(new InputParamValue(index, getColumnElementForValueNode(node) ));
320     }
321
322     protected void processConstraintValue(ConstraintValue node, StringBuffer JavaDoc result) {
323         boolean generateValueInSQLStatement = false;
324         String JavaDoc strToAppend = "?"; // NOI18N
325

326         // DB2 requires cast for parameter markers involved in numeric expressions.
327
// For DB2, do not generate parameter markers, but inline values in generated SQL.
328
// TODO: Inline numerics for all the databases not just DB2.
329

330         if (vendorType.isInlineNumeric()) {
331         // TODO: Ask Michael to pass on type for values also as enums.
332
// Suggestion : Convert FieldTypeEnumeration to a class. Implement methods like
333
// isNumeric(int type) for following if condition
334
Object JavaDoc value = node.getValue();
335             if (value != null && value instanceof Number JavaDoc) {
336                 generateValueInSQLStatement = true;
337                 strToAppend = value.toString();
338             }
339         }
340         result.append(strToAppend);
341
342         if(!generateValueInSQLStatement) {
343             // We have added a "?" to sql. Add value to inputDesc
344
generateInputValueForConstraintValueNode(node);
345         }
346     }
347
348     protected QueryPlan getOriginalPlan(ConstraintField fieldNode) {
349         return (fieldNode.originalPlan != null) ? fieldNode.originalPlan : getQueryPlan();
350     }
351
352     private void processConstraintField(ConstraintField fieldNode, StringBuffer JavaDoc result) {
353         LocalFieldDesc desc = null;
354
355         QueryPlan thePlan = getOriginalPlan(fieldNode);
356
357         if (fieldNode instanceof ConstraintFieldDesc) {
358             desc = ((ConstraintFieldDesc) fieldNode).desc;
359         } else if (fieldNode instanceof ConstraintFieldName) {
360             desc = thePlan.config.getLocalFieldDesc(((ConstraintFieldName) fieldNode).name);
361         } else {
362             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
363                     "core.generic.notinstanceof", // NOI18N
364
fieldNode.getClass().getName(),
365                     "ConstraintFieldDesc/ConstraintFieldName")); // NOI18N
366
}
367         generateColumnText(desc, thePlan, result);
368     }
369
370     /**
371      * Generates the column text for field <code>desc</code>.
372      * The column has to be associated to the corresponding query table
373      * from the list <code>tableList</code>.
374      * For fields mapped to multiple columns choose one column to be included,
375      * as all mapped columns should have the same value.
376      *
377      * @param desc Local field descriptor to be included in the constraint text.
378      * @param thePlan Query plan corresponding to <code>desc</code>.
379      * @param sb String buffer taking the resulting text.
380      */

381     protected void generateColumnText(LocalFieldDesc desc, QueryPlan thePlan,
382                                       StringBuffer JavaDoc sb) {
383         QueryTable table = null;
384         ColumnElement column = null;
385         Iterator JavaDoc iter = desc.getColumnElements();
386
387         while (iter.hasNext() && table == null) {
388             column = (ColumnElement) iter.next();
389
390             // For updates, the member variable tableList is complete
391
// at this point and includes only the table being updated.
392
// For selects, new tables are still added to tableList
393
// when join constraints are processed. Take the table list
394
// from the query plan to find the table matching the column.
395
if (action == QueryPlan.ACT_SELECT) {
396                 table = thePlan.findQueryTable(column.getDeclaringTable());
397             } else {
398                 table = findQueryTable(column.getDeclaringTable());
399             }
400         }
401
402         if (table == null) {
403             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
404                     "core.configuration.fieldnotable", // NOI18N
405
desc.getName()));
406         }
407
408         // Select statements might include columns from several tables.
409
// Qualify the column with the table index.
410
if (action == QueryPlan.ACT_SELECT) {
411             sb.append("t").append(table.getTableIndex()).append("."); // NOI18N
412
}
413
414         appendQuotedText(sb, column.getName().getName());
415     }
416
417     /**
418      * Matches the table element <code>tableElement</code> to the
419      * corresponding query table from the list <code>tableList</code>.
420      *
421      * @param tableElement Table element to be found.
422      * @return Query table object corresponding to table element.
423      * @see QueryPlan#findQueryTable(TableElement)
424      */

425     protected QueryTable findQueryTable(TableElement tableElement) {
426         QueryTable table = null;
427
428         for (Iterator JavaDoc iter = tableList.iterator(); iter.hasNext() && table == null; ) {
429             QueryTable t = (QueryTable) iter.next();
430             if (t.getTableDesc().getTableElement() == tableElement) {
431             // if (t.getTableDesc().getTableElement().equals(tableElement)) {
432
table = t;
433             }
434         }
435
436         return table;
437     }
438
439     private void processConstraintOperation(ConstraintOperation opNode,
440                                             List JavaDoc stack,
441                                             StringBuffer JavaDoc result) {
442         int opCode = opNode.operation;
443         int format = operationFormat(opCode);
444
445         if ((format & OP_IRREGULAR_MASK) == 0) {
446             processFunctionOrBinaryOperation(format, opCode, stack, result);
447         } else {
448             processIrregularOperation(opNode, opCode, stack, result);
449         }
450     }
451
452     private void processFunctionOrBinaryOperation(int format,
453                                                   int opCode,
454                                                   List JavaDoc stack,
455                                                   StringBuffer JavaDoc result) {
456
457         if ((format & OP_PREFIX_MASK) > 0) {
458             result.append(prefixOperator(opCode));
459         }
460
461         if ((format & OP_PCOUNT_MASK) > 0) {
462             if ((format & OP_PAREN_MASK) > 0) {
463                 result.append("("); // NOI18N
464
}
465
466             result.append(getWhereText(stack));
467
468             for (int i = 0; i < ((format & OP_PCOUNT_MASK) / OP_PARAM_MASK) - 1; i++) {
469                 if ((format & OP_INFIX_MASK) > 0) {
470                     // opCode for which OP_BINOP_MASK is set
471
result.append(infixOperator(opCode, i - 1));
472                 } else {
473                     // opCode for which OP_FUNC_MASK is set
474
result.append(", "); // NOI18N
475
}
476
477                 result.append(getWhereText(stack));
478             }
479
480             if ((format & OP_PAREN_MASK) > 0) {
481                 result.append(")"); // NOI18N
482
}
483         }
484
485         if ((format & OP_POSTFIX_MASK) > 0) {
486             result.append(postfixOperator(opCode));
487         }
488     }
489
490     protected void processIrregularOperation(ConstraintOperation opNode,
491                                              int opCode,
492                                              List JavaDoc stack,
493                                              StringBuffer JavaDoc result) {
494         switch (opCode) {
495             case ActionDesc.OP_NULL:
496             case ActionDesc.OP_NOTNULL:
497                 processNullOperation(opCode, stack, result);
498                 break;
499            case ActionDesc.OP_BETWEEN:
500                 if (stack.size() < 3) {
501                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
502                             "core.constraint.stackempty")); // NOI18N
503
}
504
505                 if (!(stack.get(stack.size() - 1) instanceof ConstraintField)) {
506                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
507                             "core.constraint.needfieldnode")); // NOI18N
508
} else {
509                     result.append(getWhereText(stack));
510                     result.append(" between "); // NOI18N
511
}
512                 result.append(getWhereText(stack));
513                 result.append(" and ");
514                 result.append(getWhereText(stack));
515                 break;
516             case ActionDesc.OP_IN:
517             case ActionDesc.OP_NOTIN:
518                 processInOperation(opCode, stack, result);
519                 break;
520             case ActionDesc.OP_NOTEXISTS:
521             case ActionDesc.OP_EXISTS:
522                 if (!(opNode instanceof ConstraintSubquery)) {
523                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
524                             "core.generic.notinstanceof", // NOI18N
525
opNode.getClass().getName(), "ConstraintSubquery")); // NOI18N
526
}
527
528                 ConstraintSubquery sqNode = (ConstraintSubquery) opNode;
529
530                 result.append(prefixOperator(opCode));
531                 result.append("("); // NOI18N
532

533                 Statement sqstmt = (Statement) sqNode.plan.statements.get(0);
534
535                 result.append(sqstmt.getText());
536
537                 result.append(")"); // NOI18N
538
break;
539             case ActionDesc.OP_LIKE_ESCAPE:
540                 if (stack.size() < 3) {
541                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
542                             "core.constraint.stackempty")); // NOI18N
543
}
544
545                 if (vendorType.supportsLikeEscape()) {
546                     if (!(stack.get(stack.size() - 1) instanceof ConstraintField)) {
547                         throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
548                                 "core.constraint.needfieldnode")); // NOI18N
549
} else {
550                         result.append(getWhereText(stack));
551                         result.append(" LIKE "); // NOI18N
552
}
553                     result.append(getWhereText(stack));
554                     result.append(vendorType.getLeftLikeEscape());
555                     result.append(" ESCAPE "); // NOI18N
556
result.append(getWhereText(stack));
557                     result.append(vendorType.getRightLikeEscape());
558                 } else {
559                     throw new JDOFatalInternalException(
560                             I18NHelper.getMessage(messages,
561                                     "sqlstore.sql.generator.statement.likeescapenotsupported")); //NOI18N
562
}
563
564                 break;
565             case ActionDesc.OP_SUBSTRING:
566                 if (stack.size() < 3) {
567                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
568                             "core.constraint.stackempty")); // NOI18N
569
}
570
571                 result.append(vendorType.getSubstring());
572                 result.append("("); // NOI18N
573
result.append(getWhereText(stack));
574                 result.append(vendorType.getSubstringFrom());
575                 result.append(getWhereText(stack));
576                 result.append(vendorType.getSubstringFor());
577                 result.append(getWhereText(stack));
578                 result.append(")"); // NOI18N
579
break;
580             case ActionDesc.OP_POSITION:
581                 if (stack.size() < 2) {
582                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
583                             "core.constraint.stackempty")); // NOI18N
584
}
585
586                 result.append(vendorType.getPosition());
587                 result.append("("); // NOI18N
588
boolean swap = vendorType.isPositionSearchSource();
589                 if (swap) {
590                     ConstraintNode expr =
591                             (ConstraintNode)stack.remove(stack.size() - 1);
592                     ConstraintNode pattern =
593                             (ConstraintNode)stack.remove(stack.size() - 1);
594                     stack.add(expr);
595                     stack.add(pattern);
596                 }
597
598                 result.append(getWhereText(stack));
599                 result.append(vendorType.getPositionSep());
600                 result.append(" ").append(getWhereText(stack)); // NOI18N
601
result.append(")"); // NOI18N
602
break;
603             case ActionDesc.OP_POSITION_START:
604                 if (stack.size() < 3) {
605                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
606                             "core.constraint.stackempty")); // NOI18N
607
}
608
609                 boolean swapArgs = vendorType.isPositionSearchSource();
610                 boolean threeArgs = vendorType.isPositionThreeArgs();
611
612                 if (threeArgs) {
613                     if (swapArgs) {
614                         ConstraintNode expr =
615                                 (ConstraintNode)stack.remove(stack.size() - 1);
616                         ConstraintNode pattern =
617                                 (ConstraintNode)stack.remove(stack.size() - 1);
618                         stack.add(expr);
619                         stack.add(pattern);
620                     }
621                     result.append(vendorType.getPosition());
622                     result.append("("); // NOI18N
623
result.append(getWhereText(stack));
624                     result.append(vendorType.getPositionSep());
625                     result.append(" ").append(getWhereText(stack)); // NOI18N
626
result.append(vendorType.getPositionSep());
627                     result.append(" ").append(getWhereText(stack)); // NOI18N
628
result.append(")"); // NOI18N
629
} else { //twoArgs
630
ConstraintValue valueNode =
631                             (ConstraintValue)stack.remove(stack.size() - 3);
632                     if (valueNode != null && ONE.equals(valueNode.getValue())) {
633                         stack.add(new ConstraintOperation(ActionDesc.OP_POSITION));
634                         result.append(getWhereText(stack));
635                     } else {
636                         throw new JDOFatalInternalException(
637                                 I18NHelper.getMessage(messages,
638                                         "sqlstore.sql.generator.statement.positionthreeargsnotsupported")); // NOI18N
639
}
640                 }
641                 break;
642             case ActionDesc.OP_MAYBE_NULL:
643                 processMaybeNullOperation(stack, result);
644                 break;
645             case ActionDesc.OP_MOD:
646                 if (stack.size() < 2) {
647                     throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
648                             "core.constraint.stackempty")); // NOI18N
649
}
650                 result.append(prefixOperator(opCode));
651                 result.append("("); // NOI18N
652
result.append(getWhereText(stack));
653                 result.append(", "); // NOI18N
654
result.append(getWhereText(stack));
655                 result.append(")"); // NOI18N
656
break;
657             case ActionDesc.OP_CONCAT:
658                 processConcatOperation(opCode, stack, result);
659                 break;
660
661             default:
662                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
663                         "core.constraint.illegalop", // NOI18N
664
"" + opCode)); // NOI18N
665
}
666     }
667
668     private void processConcatOperation(int opCode, List JavaDoc stack,
669             StringBuffer JavaDoc result) {
670
671         if (stack.size() < 2) {
672             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
673                     "core.constraint.stackempty")); // NOI18N
674
}
675         String JavaDoc concatCast = vendorType.getConcatCast();
676         if (concatCast.length() != 0) {
677             result.append(concatCast);
678             //Opening brace for concat cast
679
result.append("( "); // NOI18N
680
}
681         // Concat is a binary infix operator. Process it manually here.
682
// Resolve: Why should CONCAT operation be inside braces.
683
// Opening brace around CONCAT operation
684
result.append("( "); // NOI18N
685
result.append(getWhereText(stack));
686         result.append(infixOperator(opCode, 0));
687         result.append(getWhereText(stack));
688         // Closing brace around CONCAT operation
689
result.append(" ) "); // NOI18N
690

691         if (concatCast.length() != 0) {
692             // Closing brace for concat cast
693
result.append(" ) "); // NOI18N
694
}
695     }
696
697     private void processMaybeNullOperation(List JavaDoc stack, StringBuffer JavaDoc result) {
698         ConstraintValue valueNode = null;
699         ConstraintField fieldNode = null;
700
701         if (stack.size() < 2) {
702             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
703                     "core.constraint.stackempty")); // NOI18N
704
}
705
706         if (!(stack.get(stack.size() - 1) instanceof ConstraintField)) {
707             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
708                     "core.constraint.needfieldnode")); // NOI18N
709
} else {
710             fieldNode = (ConstraintField) stack.get(stack.size() - 1);
711             stack.remove(stack.size() - 1);
712         }
713
714         if (!(stack.get(stack.size() - 1) instanceof ConstraintValue)) {
715             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
716                     "core.constraint.needvalnode")); // NOI18N
717
} else {
718             valueNode = (ConstraintValue) stack.get(stack.size() - 1);
719             stack.remove(stack.size() - 1);
720         }
721
722         Object JavaDoc value = valueNode.getValue();
723         if (value instanceof String JavaDoc) {
724             String JavaDoc v = (String JavaDoc) value;
725
726             if (v.length() == 0) {
727                 stack.add(fieldNode);
728                 stack.add(new ConstraintOperation(ActionDesc.OP_NULL));
729             } else {
730                 stack.add(valueNode);
731                 stack.add(fieldNode);
732                 stack.add(new ConstraintOperation(ActionDesc.OP_EQ));
733             }
734         } else {
735             stack.add(valueNode);
736             stack.add(fieldNode);
737             stack.add(new ConstraintOperation(ActionDesc.OP_EQ));
738
739             //
740
// This takes care of the case where a nullable database column
741
// is mapped to a primitive field.
742
// This check adds a "OR numericColumn IS NULL" to the constraint.
743
// See FieldDesc#setValue(StateManager, Object) for the conversions
744
// done on binding a primitive field from the store.
745
//
746
// RESOLVE: Don't we have to do this in each OP_EQ comparison
747
// involving primitive Fields?
748
//
749
boolean maybeNull = false;
750
751             if ((value instanceof Number JavaDoc) &&
752                     ((Number JavaDoc) value).doubleValue() == 0) {
753                 maybeNull = true;
754             } else if ((value instanceof Boolean JavaDoc) &&
755                     ((Boolean JavaDoc) value).booleanValue() == false) {
756                 maybeNull = true;
757             } else if ((value instanceof Character JavaDoc) &&
758                     ((Character JavaDoc) value).charValue() == '\0') {
759                 maybeNull = true;
760             }
761
762             if (maybeNull) {
763                 stack.add(fieldNode);
764                 stack.add(new ConstraintOperation(ActionDesc.OP_NULL));
765                 stack.add(new ConstraintOperation(ActionDesc.OP_OR));
766             }
767         }
768
769         if (stack.size() > 0) {
770             result.append(getWhereText(stack));
771         }
772     }
773
774     private void processNullOperation(int opCode, List JavaDoc stack, StringBuffer JavaDoc result) {
775         String JavaDoc nullComparisionFunctionName =
776             vendorType.getNullComparisonFunctionName();
777         if( nullComparisionFunctionName.length() != 0) {
778             // Null comparision for LOB type fields is
779
// through function for this DB
780
Object JavaDoc nextNode = stack.get(stack.size() - 1);
781             if( nextNode != null) {
782                 if ( nextNode instanceof ConstraintFieldName) {
783                     ConstraintFieldName fieldNode =
784                             (ConstraintFieldName) nextNode;
785                     QueryPlan originalPlan = getQueryPlan();
786                     if (fieldNode.originalPlan != null) {
787                         originalPlan = fieldNode.originalPlan;
788                     }
789                     LocalFieldDesc desc = (LocalFieldDesc) originalPlan.config.getField(fieldNode.name);
790                     if ( desc.isMappedToLob() ) {
791                         // Add a dummy ConstraintOperation Node corresponding
792
// to null comparision func.
793
stack.add (new ConstraintOperation(
794                                 ActionDesc.OP_NULL_COMPARISION_FUNCTION) );
795                     }
796                 }
797             } else {
798                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
799                     "core.constraint.stackempty")); // NOI18N
800
}
801         }
802         result.append (getWhereText(stack));
803         String JavaDoc str = (opCode == ActionDesc.OP_NULL) ? vendorType.getIsNull() : vendorType.getIsNotNull();
804         result.append(str);
805     }
806
807     private void processInOperation(int opCode, List JavaDoc stack, StringBuffer JavaDoc result) {
808         //We are trying to construct a where clause like following in the quotes here
809
// where "(t0.field1, t0.field2, t0.field3,...) in
810
// ( select t1.fld1, t1.fld2, t2.fld3,...where ....)"
811

812         StringBuffer JavaDoc c = new StringBuffer JavaDoc();
813
814         if (stack.size() < 2) {
815             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
816                     "core.constraint.stackempty")); // NOI18N
817
}
818
819         //Append the first bracket "(" to the result
820
result.append("("); // NOI18N
821

822         if (!(stack.get(stack.size() - 1) instanceof ConstraintField)) {
823             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
824                     "core.constraint.needfieldnode")); // NOI18N
825
} else {
826             //append "t0.field1" to c
827
c.append(getWhereText(stack));
828         }
829
830         //Append ", t0.field2, t0fld3,...."
831
while (stack.size() > 1 && (stack.get(stack.size() - 1) instanceof ConstraintField)) {
832             c.replace(0, 0, ", "); // NOI18N
833
c.replace(0, 0, getWhereText(stack));
834         }
835         result.append(c.toString());
836         result.append(") "); // NOI18N
837
if (opCode == ActionDesc.OP_NOTIN) {
838             result.append("not "); // NOI18N
839
}
840         result.append("in ("); // NOI18N
841

842
843         ConstraintNode currentNode = (ConstraintNode)stack.remove(stack.size() - 1);
844         if ( ! ( currentNode instanceof ConstraintSubquery)) {
845             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
846                     "core.generic.notinstanceof", // NOI18N
847
currentNode.getClass().getName(), "ConstraintSubquery")); // NOI18N
848
} else {
849
850             ConstraintSubquery sqnode = (ConstraintSubquery) currentNode;
851
852             Statement sqstmt = (Statement) sqnode.plan.statements.get(0);
853             //Append the subquery i.e. "select t1.fld1, t1.fld2, t2.fld3,...where ...."
854
result.append(sqstmt.getText());
855
856             //Close the final bracket
857
result.append(")"); // NOI18N
858

859             //Append the Input values to the InputDesc of current statement
860
inputDesc.values.addAll(sqstmt.inputDesc.values);
861         }
862
863         /*
864           //This is old code that takes care of case when we just have a list of values
865           //as the parameter to the IN clause. We might uncomment and enhance this code
866           //when we implement the functionality to have list of values inside IN clause.
867         if (!(stack.get(stack.size() - 1) instanceof ConstraintValue)) {
868             throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
869                     "core.constraint.needvalnode")); // NOI18N
870         } else {
871             result.append(((ConstraintValue) stack.get(stack.size() - 1)).value.toString());
872             stack.remove(stack.size() - 1);
873         }
874
875         result.append(")"); // NOI18N
876         */

877     }
878
879     /**
880      * Gets the column information for the field to which <code>node</code>
881      * is bound.
882      * @param node The input node.
883      * @return Returns null if the node isn't bound to a field. Otherwise
884      * returns ColumnElement for the primary column of the field to which the
885      * node is bound.
886      */

887     private static ColumnElement getColumnElementForValueNode(ConstraintValue node) {
888         ColumnElement columnElement = null;
889         LocalFieldDesc field = node.getLocalField();
890         if(field != null) {
891             //For fields mapped to multiple columns, we assume
892
//that all the columns have the same value in the database.
893
//Hence we only use the primary column in where clause.
894
columnElement = field.getPrimaryColumn();
895         }
896         return columnElement;
897     }
898
899     /**
900      * Generates InputValue for <code>node</code>.
901      * @param node The input node.
902      */

903     protected void generateInputValueForConstraintValueNode(ConstraintValue node) {
904         inputDesc.values.add(new InputValue(node.getValue(), getColumnElementForValueNode(node) ));
905     }
906
907     //only operations end with ")" or nothing can be here, cf. getWhereText
908
protected String JavaDoc infixOperator(int operation, int position) {
909         //
910
// InfixOperator
911
// The InfixOperator method returns the SQL text for the operator specified
912
// by the operation parameter.
913
//
914
// operation
915
// The operation parameter specifies which operation for which to return
916
// SQL text. Legal values are defined by the ConstraintOperation class.
917
//
918
StringBuffer JavaDoc result = new StringBuffer JavaDoc();
919
920         switch (operation) {
921             case ActionDesc.OP_ADD:
922                 result.append(" + "); // NOI18N
923
break;
924             case ActionDesc.OP_AND:
925                 result.append(" and ");
926                 break;
927             case ActionDesc.OP_DIV:
928                 result.append(" / "); // NOI18N
929
break;
930             case ActionDesc.OP_EQ:
931                 result.append(" = "); // NOI18N
932
break;
933             case ActionDesc.OP_GE:
934                 result.append(" >= "); // NOI18N
935
break;
936             case ActionDesc.OP_GT:
937                 result.append(" > "); // NOI18N
938
break;
939             case ActionDesc.OP_LE:
940                 result.append(" <= "); // NOI18N
941
break;
942             case ActionDesc.OP_LT:
943                 result.append(" < "); // NOI18N
944
break;
945             case ActionDesc.OP_NE:
946                 result.append(" != "); // NOI18N
947
break;
948             case ActionDesc.OP_OR:
949                 result.append(" or "); // NOI18N
950
break;
951             case ActionDesc.OP_LIKE:
952                 result.append(" like "); // NOI18N
953
break;
954             case ActionDesc.OP_MUL:
955                 result.append(" * "); // NOI18N
956
break;
957             case ActionDesc.OP_SUB:
958                 result.append(" - "); // NOI1N8
959
break;
960             case ActionDesc.OP_MOD:
961                 result.append(" % "); // NOI1N8
962
break;
963             case ActionDesc.OP_BETWEEN:
964                 if (position == 1) {
965                     result.append(" between "); // NOI18N
966
} else {
967                     result.append(" and ");
968                 }
969                 break;
970             case ActionDesc.OP_CONCAT:
971                 result.append(vendorType.getStringConcat());
972                 break;
973             default:
974                 throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
975                         "core.constraint.illegalop", // NOI18N
976
"" + operation)); // NOI18N
977
}
978
979         return result.toString();
980     }
981
982
983     protected int operationFormat(int operation) {
984         int format = 0;
985         switch (operation) {
986             // Binary operators
987
case ActionDesc.OP_EQ:
988             case ActionDesc.OP_NE:
989             case ActionDesc.OP_GT:
990             case ActionDesc.OP_GE:
991             case ActionDesc.OP_LT:
992             case ActionDesc.OP_LE:
993             case ActionDesc.OP_AND:
994             case ActionDesc.OP_MUL:
995             case ActionDesc.OP_LIKE:
996                 format = OP_BINOP_MASK;
997                 break;
998
999             // Binary operators that require Parenthesis
1000
case ActionDesc.OP_ADD:
1001            case ActionDesc.OP_SUB:
1002            case ActionDesc.OP_DIV:
1003            case ActionDesc.OP_OR:
1004                format = OP_BINOP_MASK | OP_PAREN_MASK;
1005                break;
1006
1007            // Functions
1008
case ActionDesc.OP_ABS:
1009            case ActionDesc.OP_SQRT:
1010            case ActionDesc.OP_LENGTH:
1011            case ActionDesc.OP_LTRIM:
1012            case ActionDesc.OP_NOT:
1013            case ActionDesc.OP_NULL_COMPARISION_FUNCTION:
1014                format = OP_FUNC_MASK;
1015                break;
1016
1017            // Irregular operators
1018
case ActionDesc.OP_BETWEEN:
1019            case ActionDesc.OP_LIKE_ESCAPE:
1020            case ActionDesc.OP_IN:
1021            case ActionDesc.OP_NOTIN:
1022            case ActionDesc.OP_NULL:
1023            case ActionDesc.OP_NOTNULL:
1024            case ActionDesc.OP_MAYBE_NULL:
1025            case ActionDesc.OP_CONCAT:
1026            case ActionDesc.OP_EQUIJOIN:
1027                format = OP_IRREGULAR_MASK | OP_WHERE_MASK;
1028                break;
1029
1030            // Irregular operators that can never be at the root of a where clause.
1031
// Hence they do not have OP_WHERE_MASK set.
1032
case ActionDesc.OP_SUBSTRING:
1033            case ActionDesc.OP_POSITION:
1034            case ActionDesc.OP_POSITION_START:
1035                format = OP_IRREGULAR_MASK;
1036                break;
1037
1038            case ActionDesc.OP_NOTEXISTS:
1039            case ActionDesc.OP_EXISTS:
1040                format = OP_IRREGULAR_MASK | OP_WHERE_MASK | OP_PREFIX_MASK;
1041                break;
1042
1043            case ActionDesc.OP_MOD:
1044                format = vendorType.isModOperationUsingFunction() ?
1045                    OP_IRREGULAR_MASK : OP_BINOP_MASK | OP_PAREN_MASK;
1046                break;
1047
1048            case ActionDesc.OP_DISTINCT:
1049                format = OP_OTHER_MASK;
1050                break;
1051
1052            case ActionDesc.OP_ORDERBY:
1053            case ActionDesc.OP_ORDERBY_DESC:
1054                format = OP_ORDERBY_MASK;
1055                break;
1056
1057            case ActionDesc.OP_RTRIM:
1058            case ActionDesc.OP_RTRIMFIXED:
1059                if (vendorType.isAnsiTrim()) {
1060                    format = OP_WHERE_MASK | OP_PREFIX_MASK | OP_POSTFIX_MASK | OP_PARAM_MASK;
1061                } else {
1062                    format = OP_FUNC_MASK;
1063                }
1064                break;
1065
1066            //TODO: Check how can this masks be optimized
1067
case ActionDesc.OP_LEFTJOIN:
1068                format = OP_IRREGULAR_MASK | OP_WHERE_MASK | OP_INFIX_MASK;
1069                format = format | OP_POSTFIX_MASK;
1070                break;
1071            case ActionDesc.OP_RIGHTJOIN:
1072                format = OP_IRREGULAR_MASK | OP_WHERE_MASK | OP_INFIX_MASK;
1073                format = format | OP_PREFIX_MASK;
1074                break;
1075
1076            default:
1077                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
1078                        "core.constraint.illegalop", // NOI18N
1079
"" + operation)); // NOI18N
1080
}
1081
1082        return format;
1083    }
1084
1085    protected String JavaDoc postfixOperator(int operation) {
1086        //
1087
// PostfixOperator
1088
// The PostfixOperator method returns the SQL text for the operator specified
1089
// by the operation parameter.
1090
//
1091
// operation
1092
// The operation parameter specifies which operation for which to return
1093
// SQL text. Legal values are defined by the ConstraintOperation class.
1094
//
1095
StringBuffer JavaDoc result = new StringBuffer JavaDoc();
1096
1097        switch (operation) {
1098            case ActionDesc.OP_RTRIM:
1099                result.append(vendorType.getRtrimPost());
1100                break;
1101            case ActionDesc.OP_RTRIMFIXED:
1102                result.append(postfixOperator(ActionDesc.OP_RTRIM));
1103                break;
1104            case ActionDesc.OP_LEFTJOIN:
1105                result.append(vendorType.getLeftJoinPost());
1106                break;
1107            default:
1108                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
1109                        "core.constraint.illegalop", // NOI18N
1110
"" + operation)); // NOI18N
1111
}
1112
1113        return result.toString();
1114    }
1115
1116    protected String JavaDoc prefixOperator(int operation) {
1117        //
1118
// PrefixOperator
1119
// The PrefixOperator method returns the SQL text for the operator specified
1120
// by the operation parameter.
1121
//
1122
// operation
1123
// The operation parameter specifies which operation for which to return
1124
// SQL text. Legal values are defined by the ConstraintOperation class.
1125
//
1126
StringBuffer JavaDoc result = new StringBuffer JavaDoc();
1127
1128        switch (operation) {
1129            case ActionDesc.OP_ABS:
1130                result.append(vendorType.getAbs()); // NOI18N
1131
break;
1132            case ActionDesc.OP_LENGTH:
1133                result.append(vendorType.getCharLength()); // NOI18N
1134
break;
1135            case ActionDesc.OP_RTRIM:
1136                result.append(vendorType.getRtrim());
1137                break;
1138            case ActionDesc.OP_RTRIMFIXED:
1139                result.append(prefixOperator(ActionDesc.OP_RTRIM));
1140                break;
1141            case ActionDesc.OP_NOT:
1142                result.append("not "); // NOI18N
1143
break;
1144            case ActionDesc.OP_SQRT:
1145                result.append(vendorType.getSqrt());
1146                break;
1147            case ActionDesc.OP_NOTEXISTS:
1148                result.append("not exists "); // NOI18N
1149
break;
1150            case ActionDesc.OP_EXISTS:
1151                result.append("exists "); // NOI18N
1152
break;
1153            case ActionDesc.OP_NULL_COMPARISION_FUNCTION:
1154                result.append(vendorType.getNullComparisonFunctionName());
1155                break;
1156            case ActionDesc.OP_MOD:
1157                result.append(vendorType.getModFunctionName()); // NOI18N
1158
break;
1159
1160            default:
1161                throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
1162                        "core.constraint.illegalop", "" + operation)); // NOI18N
1163
}
1164
1165        return result.toString();
1166    }
1167
1168    public void addSecondaryTableStatement(Statement s) {
1169        if (s == null) return;
1170
1171        if (secondaryTableStatements == null)
1172            secondaryTableStatements = new ArrayList JavaDoc();
1173
1174        secondaryTableStatements.add(s);
1175    }
1176
1177    public ArrayList JavaDoc getSecondaryTableStatements() {
1178        return secondaryTableStatements;
1179    }
1180
1181    public ArrayList JavaDoc getQueryTables() {
1182        return tableList;
1183    }
1184
1185    public ArrayList JavaDoc getColumnRefs() {
1186        return columns;
1187    }
1188
1189    public void setAction(int action) {
1190        this.action = action;
1191    }
1192
1193    public int getAction() {
1194        return action;
1195    }
1196
1197    public Object JavaDoc clone() {
1198        try {
1199            return super.clone();
1200        } catch (CloneNotSupportedException JavaDoc e) {
1201            //
1202
// shouldn't happen.
1203
//
1204
return null;
1205        }
1206    }
1207
1208    /**
1209     * Binds input valus corrsponding to this <code>Statement</code> object to
1210     * database statement s.
1211     * @param s The database statement
1212     * @throws SQLException
1213     */

1214    public void bindInputValues(DBStatement s) throws SQLException JavaDoc {
1215        for (int i = 0, size = inputDesc.values.size(); i < size; i++) {
1216            InputValue inputVal = (InputValue) inputDesc.values.get(i);
1217            s.bindInputColumn(i + 1, inputVal.getValue(),
1218                    inputVal.getColumnElement(), vendorType);
1219        }
1220    }
1221
1222    /**
1223     * Gets input values corrsponding to this <code>Statement</code>.
1224     * @return An Object array containing input values.
1225     */

1226    private Object JavaDoc[] getInputValues() {
1227        final int size = inputDesc.values.size();
1228        Object JavaDoc[] inputValues = new Object JavaDoc[size];
1229        for (int i = 0; i < size; i++) {
1230            InputValue inputValue = (InputValue) inputDesc.values.get(i);
1231            inputValues[i] = inputValue.getValue();
1232        }
1233        return inputValues;
1234    }
1235
1236    /**
1237     * Gets formatted sql text corrsponding to this statement object. The text
1238     * also contains values for input to the statement.
1239     * @return formatted sql text corrsponding to this statement object.
1240     */

1241    public String JavaDoc getFormattedSQLText() {
1242        return formatSqlText(getText(), getInputValues());
1243    }
1244
1245    /**
1246     * The formatSqlText method returns a string containing the text of
1247     * the SQL statement about to be executed and the input values for the
1248     * placeholders.
1249     * @param sqlText Specifies the text of the SQL statement to be executed.
1250     * @param input Holds the input values used for the SQL statement.
1251     * @return The SQL text and the input values formatted into a printable
1252     * string.
1253     */

1254    static protected String JavaDoc formatSqlText(String JavaDoc sqlText, Object JavaDoc[] input) {
1255        StringBuffer JavaDoc str = new StringBuffer JavaDoc();
1256
1257        str.append(I18NHelper.getMessage(messages,
1258                "sqlstore.sql.generator.statement.sqlStatement") ); //NOI18N
1259
str.append("<").append(sqlText).append("> "); // NOI18N
1260

1261        if (input != null && input.length > 0) {
1262            str.append(I18NHelper.getMessage(messages,
1263            "sqlstore.sql.generator.statement.withinputvalues")); // NOI18N
1264
for (int i = 0; i < input.length; i++) {
1265                if (i > 0) {
1266                    str.append(", "); // NOI18N
1267
}
1268                Object JavaDoc inputValue = input[i];
1269                if (inputValue == null) {
1270                    str.append("<null>"); // NOI18N
1271
} else {
1272                    str.append(inputValue.getClass().getName());
1273                    str.append(":"); // NOI18N
1274
str.append(inputValue.toString());
1275                }
1276            }
1277        } else {
1278            str.append(I18NHelper.getMessage(messages,
1279            "sqlstore.sql.generator.statement.withnoinputvalues")); // NOI18N
1280
}
1281
1282        return str.toString();
1283    }
1284
1285}
1286
Popular Tags