KickJava   Java API By Example, From Geeks To Geeks.

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


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.jdbc.sql.exp.SelectExp;
15 import com.versant.core.jdbc.sql.exp.SqlExp;
16 import com.versant.core.jdbc.sql.SqlDriver;
17 import com.versant.core.jdbc.JdbcStorageManager;
18 import com.versant.core.common.BindingSupportImpl;
19
20 import java.io.PrintStream JavaDoc;
21 import java.sql.Connection JavaDoc;
22 import java.sql.PreparedStatement JavaDoc;
23 import java.sql.ResultSet JavaDoc;
24 import java.sql.SQLException JavaDoc;
25
26 /**
27  * This specifies how each row from a SQL query is processed and helps to
28  * generate the query. A FetchSpec contains a list of FetchOp's, each of
29  * which fetch something from the ResultSet. FetchOp's may provide data
30  * to other subsequent FetchOp's so complex operations can be broken down
31  * into simple operations. A FetchOp may have a nested FetchSpec of its own
32  * to be executed once for each row or in parallel with the 'parent'
33  * FetchSpec.
34  */

35 public class FetchSpec {
36
37     private FetchOptions options = new FetchOptions();
38     private FetchOp[] ops = new FetchOp[2];
39     private int opCount;
40     private FetchOp[] resultOps = new FetchOp[2];
41     private int resultOpCount;
42     private boolean singleObjectRow;
43
44     private SqlDriver sqlDriver;
45     private SelectExp root;
46     private SqlExp pos;
47     private int totColumnCount;
48     private boolean inAddFetchOp;
49
50     private SqlBuffer sqlBuffer;
51
52     public FetchSpec(SelectExp root, SqlDriver sqlDriver) {
53         this.root = root;
54         this.sqlDriver = sqlDriver;
55     }
56
57     /**
58      * Get the topmost SELECT for this spec.
59      */

60     public SelectExp getRoot() {
61         return root;
62     }
63
64     /**
65      * Add a new FetchOp to this plan. If includeInResult is true the the
66      * result of the op is included in the projection returned by the
67      * FetchSpec.
68      */

69     public void addFetchOp(FetchOp op, boolean includeInResult) {
70         if (opCount == ops.length) {
71             FetchOp[] a = new FetchOp[opCount * 2];
72             System.arraycopy(ops, 0, a, 0, opCount);
73             ops = a;
74         }
75         op.setIndex(opCount);
76         ops[opCount++] = op;
77         if (includeInResult) {
78             if (resultOpCount == resultOps.length) {
79                 FetchOp[] a = new FetchOp[resultOpCount * 2];
80                 System.arraycopy(resultOps, 0, a, 0, resultOpCount);
81                 resultOps = a;
82             }
83             resultOps[resultOpCount++] = op;
84         }
85         // Process newly added ops in a loop protected by a flag so that
86
// recursively added ops are processed in add order. This ensures
87
// that the ResultSet columns will be read in ascending order.
88
if (!inAddFetchOp) {
89             try {
90                 inAddFetchOp = true;
91                 for (int i = opCount - 1; i < opCount; i++) {
92                     SqlExp list = ops[i].init(root, totColumnCount + 1);
93                     if (list != null) {
94                         ++totColumnCount;
95                         if (pos == null) {
96                             pos = root.selectList = list;
97                         } else {
98                             pos.next = list;
99                         }
100                         for (; pos.next != null; pos = pos.next) {
101                             ++totColumnCount;
102                         }
103                     }
104                 }
105             } finally {
106                 inAddFetchOp = false;
107             }
108         }
109     }
110
111     /**
112      * Get the number of FetchOp's in this spec.
113      */

114     public int getFetchOpCount() {
115         return opCount;
116     }
117
118     /**
119      * Get the types of the objects in our projection.
120      */

121     public int[] getProjectionTypes() {
122         int[] a = new int[resultOpCount];
123         for (int i = 0; i < resultOpCount; i++) {
124             a[i] = resultOps[i].getResultType();
125         }
126         return a;
127     }
128
129     /**
130      * Get the default FetchOptions for this spec.
131      */

132     public FetchOptions getOptions() {
133         return options;
134     }
135
136     /**
137      * Set the compiled parameter info.
138      */

139     public void setParamList(SqlBuffer.Param paramList) {
140         if (sqlBuffer == null) {
141             generateSQL();
142         }
143         sqlBuffer.setParamList(paramList);
144     }
145
146     /**
147      * Print a user understandable description of this operation.
148      */

149     public void printPlan(PrintStream JavaDoc p, String JavaDoc indent) {
150         for (int i = 0; i < opCount; i++) {
151             ops[i].printPlan(p, indent);
152         }
153     }
154
155     /**
156      * Finish creating this spec and generate the SQL buffer for our query.
157      * This is a NOP if already done.
158      */

159     public synchronized void generateSQL() {
160         if (sqlBuffer != null) {
161             return;
162         }
163         sqlBuffer = new SqlBuffer();
164         int aliasCount = root.createAlias(0);
165         if (aliasCount == 1) {
166             root.alias = null;
167             sqlBuffer.setFirstTableOrAlias(root.table.name);
168         } else {
169             sqlBuffer.setFirstTableOrAlias(root.alias);
170         }
171         root.appendSQL(sqlDriver, sqlBuffer.getSqlbuf(), null);
172         sqlBuffer.setSelectListRange(root.distinct, root.selectListStartIndex,
173                 root.selectListFirstColEndIndex, root.selectListEndIndex);
174         sqlBuffer.setOrderByRange(root.orderByStartIndex, root.orderByEndIndex);
175         // work around bug with replace in CharBuffer class
176
sqlBuffer.getSqlbuf().append(' ');
177
178         for (int i = 0; i < opCount; i++) {
179             ops[i].generateSQL();
180         }
181
182         // clear fields we dont need any more now that we have the SQL
183
root = null;
184         pos = null;
185     }
186
187     /**
188      * Create a FetchResult to execute our query. This will execute the
189      * query as soon as the data is needed.
190      *
191      * @param forUpdate Generate SELECT FOR UPDATE type query
192      * @param forCount Generate a COUNT(*) query to just count the rows
193      * @param fromIncl Index of first row to return
194      * @param toExcl Index of row after last row to return (-1 for all)
195      * @param scrollable Use a scrollable ResultSet
196      *
197      * @see FetchResultImp#execute()
198      */

199     public FetchResult createFetchResult(JdbcStorageManager sm, Connection JavaDoc con,
200             Object JavaDoc[] params, boolean forUpdate, boolean forCount,
201             long fromIncl, long toExcl, int fetchSize, boolean scrollable) {
202
203         if (scrollable && !sqlDriver.isScrollableResultSetSupported()) {
204             throw BindingSupportImpl.getInstance().datastore(
205                     "Scrollable ResultSet's not supported for " +
206                     sqlDriver.getName() + " using JDBC driver " +
207                     sm.getJdbcConnectionSource().getDriverName());
208         }
209
210         if (sqlBuffer == null) {
211             generateSQL();
212         }
213         String JavaDoc sql = sqlBuffer.getSql(sqlDriver, params, forUpdate, forCount,
214                 fromIncl, toExcl);
215         boolean error = true;
216         PreparedStatement JavaDoc ps = null;
217         try {
218             try {
219                 if (scrollable) {
220                     ps = con.prepareStatement(sql,
221                             ResultSet.TYPE_SCROLL_INSENSITIVE,
222                             ResultSet.CONCUR_READ_ONLY);
223                 } else {
224                     ps = con.prepareStatement(sql);
225                 }
226             } catch (Exception JavaDoc e) {
227                 throw BindingSupportImpl.getInstance().datastore(
228                         "Error creating PreparedStatement: " + e + "\nSQL:\n" +
229                         sql);
230             }
231             sqlBuffer.setParamsOnPS(sm.getJmd(), sqlDriver, ps, params, sql);
232             if (fetchSize > 0) {
233                 try {
234                     ps.setFetchSize(fetchSize);
235                 } catch (Exception JavaDoc e) {
236                     throw sqlDriver.mapException(e, e.toString(), true);
237                 }
238             }
239             FetchResultImp ans = new FetchResultImp(this, ps, sql, scrollable);
240             error = false;
241             return ans;
242         } finally {
243             if (error && ps != null) {
244                 try {
245                     ps.close();
246                 } catch (SQLException JavaDoc e) {
247                     // ignore
248
}
249             }
250         }
251     }
252
253     /**
254      * This is invoked by one of our results when it is closed. We call
255      * fetchResultClosed on all of our ops so they have a chance to close
256      * any nested results.
257      */

258     public void fetchResultClosed(FetchResult fetchResult) {
259         for (int i = 0; i < opCount; i++) {
260             ops[i].fetchResultClosed(fetchResult);
261         }
262     }
263
264     public SqlDriver getSqlDriver() {
265         return sqlDriver;
266     }
267
268     public boolean isSingleObjectRow() {
269         return singleObjectRow;
270     }
271
272     /**
273      * If singleObjectRow is true and the projection only has one Object
274      * then this is returned as is and not in an Object[1].
275      */

276     public void setSingleObjectRow(boolean singleObjectRow) {
277         this.singleObjectRow = singleObjectRow;
278     }
279
280     /**
281      * Process the current row in fetchResult's ResultSet and return our
282      * projection.
283      */

284     public Object JavaDoc createRow(FetchResultImp fetchResult) {
285         for (int i = 0; i < opCount; i++) {
286             try {
287                 ops[i].fetch(fetchResult);
288             } catch (Exception JavaDoc e) {
289                 throw sqlDriver.mapException(e, e.toString() + "\nProcessing " +
290                         ops[i].getIndex() + ": " + ops[i].getDescription(),
291                         true);
292             }
293         }
294         if (singleObjectRow && resultOpCount == 1) {
295             return resultOps[0].getResult(fetchResult);
296         } else {
297             Object JavaDoc[] a = new Object JavaDoc[resultOpCount];
298             for (int i = 0; i < resultOpCount; i++) {
299                 a[i] = resultOps[i].getResult(fetchResult);
300             }
301             return a;
302         }
303     }
304
305 }
306
307
Popular Tags