KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdbc > fetch > SqlBuffer


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdbc.fetch;
13
14 import com.versant.core.util.CharBuf;
15 import com.versant.core.common.Debug;
16 import com.versant.core.common.OID;
17 import com.versant.core.common.Utils;
18 import com.versant.core.metadata.ModelMetaData;
19 import com.versant.core.metadata.ClassMetaData;
20 import com.versant.core.metadata.MDStaticUtils;
21 import com.versant.core.jdbc.sql.SqlDriver;
22 import com.versant.core.jdbc.metadata.JdbcColumn;
23 import com.versant.core.jdbc.metadata.JdbcField;
24 import com.versant.core.jdbc.metadata.JdbcClass;
25 import com.versant.core.jdbc.JdbcUtils;
26 import com.versant.core.jdbc.JdbcOID;
27
28 import java.io.Serializable JavaDoc;
29 import java.sql.PreparedStatement JavaDoc;
30 import java.sql.SQLException JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Iterator JavaDoc;
33
34 import com.versant.core.common.BindingSupportImpl;
35
36 /**
37  * Buffer holding the SQL for a query. This supports modification of the
38  * SQL to convert nullparameter references into IS NULL, toggling SELECT
39  * FOR UPDATE and other things.
40  */

41 public class SqlBuffer {
42
43     private static final char[] COUNT_STAR_PRE = "COUNT(".toCharArray();
44     private static final char[] COUNT_STAR_POST = ")".toCharArray();
45     private static final char[] COUNT_STAR_PRE_DISTINCT = "COUNT(DISTINCT(".toCharArray();
46     private static final char[] COUNT_STAR_POST_DISTINCT = "))".toCharArray();
47     private static final char[] IS_NULL = "IS NULL".toCharArray();
48     private static final char[] IS_NOT_NULL = "IS NOT NULL".toCharArray();
49
50     /**
51      * The SQL to execute the query. It is kept in a CharBuf as
52      * it may need to be modified before each execution of the query depending
53      * on which parameters are null.
54      */

55     private CharBuf sqlbuf;
56     /**
57      * Is the query a 'select distinct'?
58      */

59     private boolean distinct;
60     /**
61      * Index of the first character in the select list.
62      */

63     private int selectListStartIndex;
64     /**
65      * Number of characters in the select list.
66      */

67     private int selectListLength;
68     /**
69      * Index of the first column in the select list.
70      */

71     private int selectFirstColStart;
72     /**
73      * Index of the first column in the select list.
74      */

75     private int selectFirstColLength;
76     /**
77      * Index of the start of the 'order by ..' clause or 0 if none.
78      */

79     private int orderByStartIndex;
80     /**
81      * Number of characters in the 'order by ..' clause.
82      */

83     private int orderByLength;
84     /**
85      * The first table or alias in the from clause for the query. This is
86      * required for some databases if the query needs to be converted into a
87      * 'select for update' query (e.g. postgres).
88      */

89     private String JavaDoc firstTableOrAlias;
90     /**
91      * The sql is cached here in string form.
92      */

93     private transient String JavaDoc sql;
94     /**
95      * Is the current sqlbuf 'select for update'?
96      */

97     private boolean sqlForUpdate;
98     /**
99      * What was the original size of the sqlbuf before any modifications for
100      * 'select for update'?
101      */

102     private int sqlbufNotForUpdateSize;
103     /**
104      * Is the current sqlbuf 'select count(*)'?
105      */

106     private boolean sqlForCount;
107     /**
108      * Store for the select list when query is converted to count(*).
109      */

110     private char[] originalSelectList;
111     /**
112      * Store for the first column of the select list when query is converted to
113      * count(*).
114      */

115     private char[] originalSelectListFirstColumn;
116     /**
117      * Store for the order by clause when query is converted to count(*).
118      */

119     private char[] originalOrderByClause;
120     /**
121      * Params in the order that they appear in the SQL string. Each declared
122      * parameter may have several entries in this array if it was used more
123      * than once in the original query.
124      */

125     private Param paramList;
126     /**
127      * Characters used to convert a param into 'is null'.
128      */

129     private char[] isNullChars;
130     /**
131      * Characters used to convert a param into 'is not null'.
132      */

133     private char[] isNotNullChars;
134     /**
135      * Is this an aggregate query.
136      */

137     private boolean aggregate;
138
139     public SqlBuffer() {
140         sqlbuf = new CharBuf(256);
141     }
142
143     public SqlBuffer getClone() {
144         SqlBuffer clone = new SqlBuffer();
145         clone.sqlbuf = new CharBuf(sqlbuf);
146         clone.distinct = distinct;
147         clone.selectListStartIndex = selectListStartIndex;
148         clone.selectListLength = selectListLength;
149         clone.orderByStartIndex = orderByStartIndex;
150         clone.orderByLength = orderByLength;
151         clone.firstTableOrAlias = firstTableOrAlias;
152         clone.sql = sql;
153         clone.sqlForUpdate = sqlForUpdate;
154         clone.sqlbufNotForUpdateSize = sqlbufNotForUpdateSize;
155         clone.sqlForCount = sqlForCount;
156         clone.originalSelectList = originalSelectList;
157         clone.originalOrderByClause = originalOrderByClause;
158         clone.paramList = (paramList == null ? null : paramList.getClone());
159         if (isNullChars != null) {
160             clone.isNullChars = new char[isNullChars.length];
161             for (int i = 0; i < isNullChars.length; i++) {
162                 clone.isNullChars[i] = isNullChars[i];
163             }
164         }
165         if (isNotNullChars != null) {
166             clone.isNotNullChars = new char[isNotNullChars.length];
167             for (int i = 0; i < isNotNullChars.length; i++) {
168                 clone.isNotNullChars[i] = isNotNullChars[i];
169             }
170         }
171         return clone;
172     }
173
174     public boolean isAggregate() {
175         return aggregate;
176     }
177
178     public void setAggregate(boolean aggregate) {
179         this.aggregate = aggregate;
180     }
181
182     /**
183      * Set the range of characters in our buffer that contain all the columns
184      * in the select list.
185      * @param start Index of the first character in the select list (after
186      * 'SELECT ' or 'SELECT DISTINCT ')
187      * @param firstColEnd Index of the last character in the first column + 1
188      * @param end Index of the last character in the list + 1
189      */

190     public void setSelectListRange(boolean distinct, int start, int firstColEnd,
191             int end) {
192         this.distinct = distinct;
193         if (distinct) {
194             selectListStartIndex = start - 9; // "DISTINCT ".length()
195
} else {
196             selectListStartIndex = start;
197         }
198         selectFirstColStart = start;
199         selectListLength = end - selectListStartIndex;
200         selectFirstColLength = firstColEnd - selectFirstColStart;
201     }
202
203     /**
204      * Set the range of characters in our buffer that contain order by clause
205      * including the 'order by' keywords. The end index is exclusive.
206      */

207     public void setOrderByRange(int start, int end) {
208         orderByStartIndex = start;
209         orderByLength = end - start;
210     }
211
212     public CharBuf getSqlbuf() {
213         return sqlbuf;
214     }
215
216     public boolean isDistinct() {
217         return distinct;
218     }
219
220     public String JavaDoc getFirstTableOrAlias() {
221         return firstTableOrAlias;
222     }
223
224     public void setFirstTableOrAlias(String JavaDoc firstTableOrAlias) {
225         this.firstTableOrAlias = firstTableOrAlias;
226     }
227
228     public boolean isSqlForUpdate() {
229         return sqlForUpdate;
230     }
231
232     public int getSqlbufNotForUpdateSize() {
233         return sqlbufNotForUpdateSize;
234     }
235
236     public boolean isSqlForCount() {
237         return sqlForCount;
238     }
239
240     public char[] getOriginalSelectList() {
241         return originalSelectList;
242     }
243
244     public char[] getOriginalOrderByClause() {
245         return originalOrderByClause;
246     }
247
248     public Param getParamList() {
249         return paramList;
250     }
251
252     public void setParamList(Param paramList) {
253         this.paramList = paramList;
254         if (paramList != null) analyzeCharSpans();
255     }
256
257     private void analyzeCharSpans() {
258         int max = IS_NOT_NULL.length;
259         for (Param p = paramList; p != null; p = p.next) {
260             for (CharSpan s = p.charSpanList; s != null; s = s.next) {
261                 if (s.type == CharSpan.TYPE_REMOVE) continue;
262                 int len = s.lastCharIndex - s.firstCharIndex;
263                 if (len > max) max = len;
264             }
265         }
266         if (max > 0) {
267             isNullChars = new char[max];
268             copyAndPad(IS_NULL, isNullChars, max);
269             isNotNullChars = new char[max];
270             copyAndPad(IS_NOT_NULL, isNotNullChars, max);
271         }
272     }
273
274     /**
275      * This must create space in the charBuf at the index.
276      */

277     public void createSpace(CharBuf charBuf, int index, int amount) {
278         sql = null;
279         for (SqlBuffer.Param p = paramList; p != null; p = p.next) {
280             if (p.firstCharIndex > index) {
281                 //must update
282
p.firstCharIndex += amount;
283                 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) {
284                     cs.firstCharIndex += amount;
285                     cs.lastCharIndex += amount;
286                 }
287             }
288         }
289
290         if (orderByStartIndex > index) {
291             orderByStartIndex += amount;
292         }
293     }
294
295     /**
296      * This must create space in the charBuf at the index.
297      */

298     public void removeSpace(CharBuf charBuf, int index, int amount) {
299         sql = null;
300         for (SqlBuffer.Param p = paramList; p != null; p = p.next) {
301             if (p.firstCharIndex > index) {
302                 //must update
303
p.firstCharIndex -= amount;
304                 for (CharSpan cs = p.charSpanList; cs != null; cs = cs.next) {
305                     cs.firstCharIndex -= amount;
306                     cs.lastCharIndex -= amount;
307                 }
308             }
309         }
310
311         if (orderByStartIndex > index) {
312             orderByStartIndex -= amount;
313         }
314     }
315
316     /**
317      * Update all our Param's for the null/not null state of their parameters
318      * and for 'select for update' or not. Return the updated SQL.
319
320      * @param forUpdate Generate SELECT FOR UPDATE type query
321      * @param forCount Generate a COUNT(*) query to just count the rows
322      * @param fromIncl Index of first row to return
323      * @param toExcl Index of row after last row to return
324      */

325     public synchronized String JavaDoc getSql(SqlDriver driver, Object JavaDoc[] params,
326             boolean forUpdate, boolean forCount, long fromIncl, long toExcl) {
327         boolean changed = sql == null;
328         if (params != null) {
329             int paramIndex = 0;
330             for (SqlBuffer.Param p = paramList; p != null; p = p.next, paramIndex++) {
331                 if (params[p.declaredParamIndex] instanceof Collection JavaDoc) {
332                     Collection JavaDoc col = (Collection JavaDoc) params[p.declaredParamIndex];
333                     int n = col.size();
334                     if (n == 0) {
335                         throw BindingSupportImpl.getInstance().invalidOperation(
336                                 "The supplied collection param at index "
337                                 + paramIndex + " may not be empty");
338                     }
339                     if (p.inListParamCount == 0) {
340                         //this is a readOnly char[]. it may not be modified
341
final char[] charsToInsert = driver.getSqlParamStringChars(p.jdbcType);
342                         int toInsert = (n == 1 ? charsToInsert.length : (n * charsToInsert.length) + (n - 1));
343                         char[] chars = new char[toInsert];
344
345                         int offset = 0;
346                         for (int i = 0; i < n; i++) {
347                             if (offset > 0) {
348                                 chars[offset++] = ',';
349                             }
350                             for (int j = 0; j < charsToInsert.length; j++) {
351                                 chars[j + offset] = charsToInsert[j];
352                             }
353                             offset += charsToInsert.length;
354                         }
355                         createSpace(sqlbuf, p.firstCharIndex, toInsert);
356                         sqlbuf.insert(p.firstCharIndex, chars);
357                         p.charLength = chars.length;
358                         p.inListParamCount = n;
359                         changed = true;
360                     } else if (p.inListParamCount < n) {
361                         //must insert more param's
362
int insertPoint = p.charLength + p.firstCharIndex;
363                         //the diff between the required number and what is already there
364
int paramsToInsert = n - p.inListParamCount;
365                         final char[] charStamp = driver.getSqlParamStringChars(p.jdbcType);
366                         int charsToInsert = (charStamp.length * paramsToInsert) + paramsToInsert;
367                         char[] chars = new char[charsToInsert];
368                         int offset = 0;
369                         for (int i = 0; i < paramsToInsert; i++) {
370                             chars[offset++] = ',';
371                             for (int j = 0; j < charStamp.length; j++) {
372                                 chars[offset++] = charStamp[j];
373                             }
374                         }
375                         if (Debug.DEBUG) {
376                             if (offset != chars.length) {
377                                 throw BindingSupportImpl.getInstance().internal("");
378                             }
379                         }
380                         createSpace(sqlbuf, insertPoint, charsToInsert);
381                         sqlbuf.insert(insertPoint, chars);
382                         p.charLength += chars.length;
383                         p.inListParamCount = n;
384                         changed = true;
385                     } else if (p.inListParamCount > n) {
386                         //must remove some
387
changed = true;
388                         int removeStart = p.firstCharIndex;
389                         int paramToRemove = p.inListParamCount - n;
390
391                         int charsToRemove = driver.getSqlParamStringChars(p.jdbcType).length * paramToRemove + paramToRemove;
392                         int removeTo = charsToRemove + removeStart;
393                         sqlbuf.remove(removeStart, removeTo);
394                         removeSpace(sqlbuf, p.firstCharIndex, removeTo - removeStart);
395
396                         p.charLength -= (removeTo - removeStart);
397                         p.inListParamCount = n;
398                         changed = true;
399                     }
400
401                 }
402             }
403         }
404
405         if (params != null) {
406             for (SqlBuffer.Param p = paramList; p != null; p = p.next) {
407                 if (p.update(this, params[p.declaredParamIndex] == null)) {
408                     changed = true;
409                 }
410             }
411         }
412         if (forUpdate != sqlForUpdate
413                 && (!distinct || driver.isSelectForUpdateWithDistinctOk())
414                 && (!aggregate || driver.isSelectForUpdateWithAggregateOk())) {
415             char[] a = driver.getSelectForUpdate();
416             if (a != null) {
417                 if (forUpdate) {
418                     sqlbufNotForUpdateSize = sqlbuf.size();
419                     sqlbuf.append(a);
420                     if (driver.isSelectForUpdateAppendTable()) {
421                         sqlbuf.append(firstTableOrAlias);
422                     }
423                 } else {
424                     sqlbuf.setSize(sqlbufNotForUpdateSize);
425                 }
426                 sqlForUpdate = forUpdate;
427                 changed = true;
428             }
429         }
430         if (forCount != sqlForCount) {
431             if (forCount) {
432                 if (originalSelectList == null) {
433                     originalSelectList = sqlbuf.toArray(selectListStartIndex,
434                             selectListLength);
435                     originalSelectListFirstColumn = sqlbuf.toArray(
436                             selectFirstColStart, selectFirstColLength);
437                 }
438                 int start;
439                 if (distinct) {
440                     sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE_DISTINCT);
441                     start = selectListStartIndex + COUNT_STAR_PRE_DISTINCT.length;
442                 } else {
443                     sqlbuf.replace(selectListStartIndex, COUNT_STAR_PRE);
444                     start = selectListStartIndex + COUNT_STAR_PRE.length;
445                 }
446                 sqlbuf.replace(start, originalSelectListFirstColumn);
447                 start += originalSelectListFirstColumn.length;
448                 if (distinct) {
449                     sqlbuf.replace(start, COUNT_STAR_POST_DISTINCT);
450                     start += COUNT_STAR_POST_DISTINCT.length;
451                 } else {
452                     sqlbuf.replace(start, COUNT_STAR_POST);
453                     start += COUNT_STAR_POST.length;
454                 }
455                 int n = (selectListStartIndex + selectListLength) - start;
456                 if (n > 0) sqlbuf.replace(start, start + n, ' ');
457                 if (orderByStartIndex > 0) {
458                     if (originalOrderByClause == null) {
459                         originalOrderByClause = sqlbuf.toArray(orderByStartIndex,
460                                 orderByLength);
461                     }
462                     sqlbuf.replace(orderByStartIndex,
463                             orderByStartIndex + orderByLength, ' ');
464                 }
465             } else {
466                 sqlbuf.replace(selectListStartIndex, originalSelectList);
467                 if (orderByStartIndex > 0) {
468                     sqlbuf.replace(orderByStartIndex, originalOrderByClause);
469                 }
470             }
471             sqlForCount = forCount;
472             changed = true;
473         }
474
475         if (changed) {
476             sql = sqlbuf.toString();
477         }
478         return sql;
479     }
480
481     /**
482      * Set all the parameters for this query on ps. This is a NOP if params
483      * is null.
484      */

485     public void setParamsOnPS(ModelMetaData jmd, SqlDriver driver,
486             PreparedStatement JavaDoc ps, Object JavaDoc[] params, String JavaDoc sql) {
487         if (params == null) return;
488         int pos = 1;
489         SqlBuffer.Param p = paramList;
490         Object JavaDoc value = null;
491         try {
492             for (; p != null; p = p.next) {
493                 value = params[p.declaredParamIndex];
494                 switch (p.mod) {
495                     case Param.MOD_NONE:
496                         break;
497                     case Param.MOD_APPEND_PERCENT:
498                         if (value != null) value = value + "%";
499                         break;
500                     case Param.MOD_PREPEND_PERCENT:
501                         if (value != null) value = "%" + value;
502                         break;
503                     default:
504                         throw BindingSupportImpl.getInstance().internal("Invalid mod: "
505                                 + p.mod);
506                 }
507                 if (value == null && p.requiresUpdate()) continue;
508                 int pci = p.classIndex;
509                 if (pci >= 0) {
510                     ClassMetaData pcmd = jmd.classes[pci];
511                     int pfno = p.fieldNo;
512                     if (pfno >= 0) {
513                         JdbcField f = ((JdbcClass)pcmd.storeClass).stateFields[pfno];
514                         if (value instanceof Collection JavaDoc) {
515                             Collection JavaDoc col = (Collection JavaDoc) value;
516                             for (Iterator JavaDoc iterator = col.iterator(); iterator.hasNext();) {
517                                 Object JavaDoc o = iterator.next();
518                                 if (o instanceof OID) {
519                                     pos = ((JdbcOID)o).setParams(ps, pos);
520                                 } else {
521                                     pos = f.setQueryParam(ps, pos, o);
522                                 }
523                             }
524                         } else {
525                             pos = f.setQueryParam(ps, pos, value);
526                         }
527                     } else { // this is an OID param for a link table
528
if (value != null) {
529                             pos = ((JdbcOID)value).setParams(ps, pos);
530                         } else {
531                             JdbcColumn[] pkcols = ((JdbcClass)pcmd.storeClass).table.pkSimpleCols;
532                             int nc = pkcols.length;
533                             for (int i = 0; i < nc; i++) {
534                                 ps.setNull(pos++, pkcols[i].jdbcType);
535                             }
536                         }
537                     }
538                 } else {
539                     if (p.col != null) {
540                         p.col.set(ps, pos++, value);
541                     } else {
542                         int javaTypeCode = p.javaTypeCode;
543                         if (javaTypeCode == 0 && value != null) {
544                             javaTypeCode = MDStaticUtils.toTypeCode(value.getClass());
545                         }
546                         JdbcUtils.set(ps, pos++, value, javaTypeCode, p.jdbcType);
547                     }
548                 }
549             }
550         } catch (Exception JavaDoc e) {
551             throw driver.mapException(e, "Error setting query parameter " +
552                 p.getIdentifier() + " = '" + Utils.toString(value) +
553                 "' at PreparedStatement index " + pos + " in\n" +
554                 JdbcUtils.getPreparedStatementInfo(sql, ps) + "\n" +
555                JdbcUtils.toString(e), false);
556         }
557     }
558
559     private static void copyAndPad(char[] src, char[] dest, int len) {
560         int n = src.length;
561         System.arraycopy(src, 0, dest, 0, n);
562         for (; n < len;) dest[n++] = ' ';
563     }
564
565     public char[] getNullChars() {
566         return isNullChars;
567     }
568
569     public void setNullChars(char[] nullChars) {
570         isNullChars = nullChars;
571     }
572
573     public char[] getNotNullChars() {
574         return isNotNullChars;
575     }
576
577     public void setNotNullChars(char[] notNullChars) {
578         isNotNullChars = notNullChars;
579     }
580
581     /**
582      * A parameter. This tells us the indexes of the first and last characters
583      * of this parameter and its index within the original list of declared
584      * parameters.
585      */

586     public final static class Param implements Serializable JavaDoc {
587
588         public static final int MOD_NONE = 0;
589         public static final int MOD_PREPEND_PERCENT = 1;
590         public static final int MOD_APPEND_PERCENT = 2;
591
592         /**
593          * An identifier for this parameter for error messages.
594          */

595         private String JavaDoc identifier;
596         /**
597          * The next Param in the list.
598          */

599         public Param next;
600         /**
601          * The index of this parameter in the original list of declared
602          * parameters for the query or the index in the array of parameter
603          * values used to exec the query (EJBQL).
604          */

605         public int declaredParamIndex;
606         /**
607          * The index of the first character in sql for this Param.
608          * If charSpanList is not null then this is the same as there. It
609          * is used for sorting.
610          */

611         public transient int firstCharIndex;
612         /**
613          * The current amount of chars that the param occupies when used a in list
614          * for collection params
615          */

616         public transient int charLength;
617         /**
618          * The current amount params that this param can take is used as a collection
619          * param.
620          */

621         public transient int inListParamCount;
622         /**
623          * List of character ranges used by this Param. This info is used
624          * to turn '= ?' into 'is null' and so on. It is null if there is
625          * no need for any replacement (e.g. using Sybase).
626          */

627         public CharSpan charSpanList;
628         /**
629          * The classIndex of the class associated with this parameter or
630          * -1 if there is none (e.g. a parameter used in an expression).
631          * @see #fieldNo
632          */

633         public int classIndex;
634         /**
635          * The field number associated with this parameter or -1 if none.
636          * This is used with cls to locate its column(s). It is not used if
637          * the classIndex is -1.
638          * @see #classIndex
639          */

640         public int fieldNo;
641         /**
642          * The java type code of this parameter. This is only set if
643          * classIndex is -1 i.e. this parameter is not for a field.
644          */

645         public int javaTypeCode;
646         /**
647          * The JDBC type (from java.sql.Types) for this parameter. This is
648          * only set if classIndex is -1.
649          * @see java.sql.Types
650          */

651         public int jdbcType;
652         /**
653          * How must the parameter value be modified before being set? This is
654          * used for startsWith and endsWith for databases that do not allow
655          * expressions on the right hand side of a LIKE (e.g. Informix).
656          * @see #MOD_APPEND_PERCENT
657          */

658         public int mod;
659
660         public transient JdbcColumn col;
661
662         public Param(String JavaDoc identifier) {
663             this.identifier = identifier;
664         }
665
666         public String JavaDoc getIdentifier() {
667             return identifier;
668         }
669
670         /**
671          * Update all our CharSpan's for the null/not null state of the
672          * parameter. This is a NOP if the parameter does not require update.
673          * @return True if changes were made else false
674          * @see #requiresUpdate
675          */

676         public boolean update(SqlBuffer q, boolean newParamIsNull) {
677             boolean ans = false;
678             for (CharSpan cs = charSpanList; cs != null; cs = cs.next) {
679                 if (cs.update(q, newParamIsNull)) ans = true;
680             }
681             return ans;
682         }
683
684         /**
685          * Does this need to be updated for null/not null parameter values?
686          * @see #update
687          */

688         public boolean requiresUpdate() {
689             return charSpanList != null;
690         }
691
692         public Param getClone() {
693             Param clone = new Param(identifier);
694             clone.next = (next == null ? null : next.getClone());
695             clone.declaredParamIndex = declaredParamIndex;
696             clone.firstCharIndex = firstCharIndex;
697             clone.charSpanList = (charSpanList == null ? null : charSpanList.getClone());
698             clone.classIndex = classIndex;
699             clone.fieldNo = fieldNo;
700             clone.javaTypeCode = javaTypeCode;
701             clone.jdbcType = jdbcType;
702             clone.mod = mod;
703             clone.col = col;
704             return clone;
705         }
706     }
707
708     /**
709      * This specifies a range of characters for a Param in our sql buffer. The
710      * lastCharIndex is the index of the character after the last character
711      * in the range.
712      */

713     public final static class CharSpan implements Serializable JavaDoc {
714
715         public static final int TYPE_NULL = 1;
716         public static final int TYPE_NOT_NULL = 2;
717         public static final int TYPE_REMOVE = 3;
718
719         /**
720          * What must be done to this span?
721          */

722         public int type;
723         /**
724          * The index of the first character in sql.
725          */

726         public int firstCharIndex;
727         /**
728          * The index of the character after the last character in the span.
729          */

730         public int lastCharIndex;
731         /**
732          * The next span in the list.
733          */

734         public CharSpan next;
735
736         /**
737          * The current state of the parameter in sql. If this is true then
738          * the parameter has been replaced with an 'is null' or 'is not null'.
739          */

740         private boolean paramIsNull;
741         /**
742          * The original text from the sql query. This is filled the first
743          * time the param is null and is used to restore the query when it
744          * is not null in future.
745          */

746         private char[] originalSql;
747
748         public CharSpan getClone() {
749             CharSpan clone = new CharSpan();
750             clone.type = type;
751             clone.firstCharIndex = firstCharIndex;
752             clone.lastCharIndex = lastCharIndex;
753             clone.next = (next == null ? null : next.getClone());
754             clone.paramIsNull = paramIsNull;
755             if (originalSql != null) {
756                 clone.originalSql = new char[originalSql.length];
757                 for (int i = 0; i < originalSql.length; i++) {
758                     clone.originalSql[i] = originalSql[i];
759                 }
760             }
761             return clone;
762         }
763
764         /**
765          * Update the query and our state if the newParamIsNull value differs
766          * from our paramIsNull field.
767          * @return True if changes were made else false
768          */

769         public boolean update(SqlBuffer q, boolean newParamIsNull) {
770             if (newParamIsNull == paramIsNull) return false;
771             CharBuf sql = q.sqlbuf;
772             if (newParamIsNull) {
773                 if (originalSql == null) {
774                     originalSql = sql.toArray(firstCharIndex,
775                             lastCharIndex - firstCharIndex);
776                 }
777                 if (Debug.DEBUG) {
778                     System.out.println("*** CharSpan.update replacing '" +
779                             new String JavaDoc(originalSql) + "' " + originalSql.length +
780                             " " + q.isNullChars.length);
781                 }
782                 switch (type) {
783                     case TYPE_NULL:
784                         sql.replace(firstCharIndex, q.isNullChars);
785                         break;
786                     case TYPE_NOT_NULL:
787                         sql.replace(firstCharIndex, q.isNotNullChars);
788                         break;
789                     case TYPE_REMOVE:
790                         sql.replace(firstCharIndex, lastCharIndex, ' ');
791                         break;
792                     default:
793                         throw BindingSupportImpl.getInstance().internal(
794                                 "Unknown CharSpan type: " + type);
795                 }
796             } else {
797                 sql.replace(firstCharIndex, originalSql);
798             }
799             paramIsNull = newParamIsNull;
800             return true;
801         }
802     }
803
804 }
805
Popular Tags