001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.language.markup.xsp;
018:
019: import org.apache.avalon.framework.logger.AbstractLogEnabled;
020:
021: import java.sql.Connection;
022: import java.sql.PreparedStatement;
023: import java.sql.CallableStatement;
024: import java.sql.ResultSet;
025: import java.sql.ResultSetMetaData;
026: import java.sql.SQLException;
027: import java.util.ArrayList;
028:
029: /**
030: * This is base class for all EsqlQueries
031: *
032: * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
033: * @version CVS $Id: AbstractEsqlQuery.java 433543 2006-08-22 06:22:54Z crossley $
034: */
035: public abstract class AbstractEsqlQuery extends AbstractLogEnabled {
036: private int maxRows = -1;
037: private int skipRows = 0;
038: private int rowCount = -1;
039: private int position = -1;
040: private String query = null;
041: private Connection connection = null;
042: private ResultSetMetaData resultSetMetaData = null;
043: private PreparedStatement preparedStatement = null;
044: private ResultSet resultSet = null;
045: private boolean hasResultSet = false;
046: private boolean keepgoing = true;
047:
048: private int queryResultsCount = 0;
049: private int updateResultsCount = 0;
050: private int updateCount = -2;
051:
052: private ArrayList groups = null;
053: private int groupLevel = -1;
054: private int changeLevel = -1;
055:
056: /**
057: * Constructor
058: *
059: * @param connection
060: * @param query - The SQL query string
061: */
062: protected AbstractEsqlQuery(Connection connection, String query) {
063: this .connection = connection;
064: this .query = query;
065: }
066:
067: /**
068: * Only newInstance may use this contructor
069: * @param resultSet
070: */
071: protected AbstractEsqlQuery(final ResultSet resultSet) {
072: this .connection = null;
073: this .query = null;
074: this .resultSet = resultSet;
075: this .hasResultSet = (resultSet != null);
076: }
077:
078: /**
079: * Create a EsqlQuery of the same type
080: * @param resultSet
081: */
082: public abstract AbstractEsqlQuery newInstance(
083: final ResultSet resultSet);
084:
085: /**
086: * Return the query string ("select * from bla")
087: *
088: * NOTE: Might want to be overridden by indiviual EsqlQuery implementations
089: * e.g. for database specific LIMIT features. Be aware that there a two different
090: * limit approaches:
091: * <pre>
092: * retrieve query
093: * time time
094: * +---------+ ...........
095: * |JDBC | : :
096: * |ResultSet| : :
097: * |.........|-+ :_________:_
098: * | | | skip/max+1 |JDBC | | skip/max+1
099: * | | | window |ResultSet| | window
100: * |.........| | |_________| |
101: * | |-+ : :_|
102: * | | : :
103: * +---------+ :.........:
104: * </pre>
105: * With the "retrieve time" limit the JDBC ResultSet includes ALL of the rows of the
106: * query.
107: * With the "query time" limit only a small window of rows are in the actuall JDBC
108: * ResultSet. In order to know whether there are more rows available (without an additional
109: * query) we need to have at least one more row in the JDBC ResultSet. So we ask for getMaxRows()+1
110: *
111: * @throws SQLException
112: */
113: public String getQueryString() throws SQLException {
114: return (query);
115: }
116:
117: /**
118: * NOTE: Might want to be overridden by indiviual EsqlQuery implementations
119: *
120: * @throws SQLException
121: */
122: public PreparedStatement prepareStatement() throws SQLException {
123: preparedStatement = connection
124: .prepareStatement(getQueryString());
125: return (preparedStatement);
126: }
127:
128: /**
129: * NOTE: Might want to be overridden by indiviual EsqlQuery implementations
130: *
131: * @throws SQLException
132: */
133: public CallableStatement prepareCall() throws SQLException {
134: preparedStatement = connection.prepareCall(getQueryString());
135: return ((CallableStatement) preparedStatement);
136: }
137:
138: /**
139: * Gets the total number of rows of a the query WITHOUT the
140: * limits of skip/max rows.
141: *
142: * NOTE: Might want to be overridden by indiviual EsqlQuery implementations
143: *
144: * @return total number of rows
145: * @throws SQLException
146: */
147: public int getRowCount() throws SQLException {
148: if (rowCount < 0) {
149: String lowerQuery = query.toLowerCase();
150: int from = lowerQuery.indexOf(" from ");
151:
152: int groupby = lowerQuery.indexOf(" group by ");
153: int orderby = lowerQuery.indexOf(" order by ");
154:
155: int min = Math.min(groupby, orderby);
156:
157: String countQuery;
158: if (min > -1) {
159: countQuery = "select count(*)"
160: + String.valueOf(query).substring(from, min);
161: } else {
162: countQuery = "select count(*)"
163: + String.valueOf(query).substring(from);
164: }
165:
166: if (getLogger().isDebugEnabled())
167: getLogger().debug(
168: "executing [" + String.valueOf(query) + "]");
169:
170: ResultSet rs = preparedStatement.executeQuery(countQuery);
171: try {
172: if (rs.first()) {
173: rowCount = rs.getInt(1);
174: if (getLogger().isDebugEnabled())
175: getLogger().debug("count = " + rowCount);
176: }
177: } finally {
178: rs.close();
179: }
180: }
181:
182: return (rowCount);
183: }
184:
185: /**
186: * Move to the first row.
187: *
188: * NOTE: Might want to be overridden by indiviual EsqlQuery implementations
189: *
190: * @throws SQLException
191: */
192: public void getResultRows() throws SQLException {
193: if (skipRows > 0) {
194: while (resultSet.next()) {
195: position++;
196: if (position >= skipRows) {
197: break;
198: }
199: }
200: }
201: }
202:
203: /**
204: * Clean up all database resources used by the query. In particular,
205: * close result sets and statements.
206: *
207: */
208: public void cleanUp() {
209: this .resultSetMetaData = null;
210: if (this .resultSet != null) {
211: try {
212: this .resultSet.close();
213: this .resultSet = null;
214: } catch (SQLException e) {
215: // should never happen! (only cause: access error)
216: }
217: }
218: if (this .preparedStatement != null) {
219: try {
220: this .preparedStatement.close();
221: this .preparedStatement = null;
222: } catch (SQLException e) {
223: // should never happen! (only cause: access error)
224: }
225: }
226: }
227:
228: /* ************** FINAL methods *********************** */
229:
230: protected final void setPosition(int p) {
231: position = p;
232: }
233:
234: protected final PreparedStatement setPreparedStatement(
235: final PreparedStatement ps) {
236: preparedStatement = ps;
237: return (preparedStatement);
238: }
239:
240: public final Connection getConnection() {
241: return (connection);
242: }
243:
244: public final int getSkipRows() {
245: return (skipRows);
246: }
247:
248: public final void setSkipRows(int i) {
249: skipRows = i;
250: }
251:
252: public final int getMaxRows() {
253: return (maxRows);
254: }
255:
256: public final void setMaxRows(int i) {
257: maxRows = i;
258: }
259:
260: public final ResultSetMetaData getResultSetMetaData() {
261: return (resultSetMetaData);
262: }
263:
264: public final PreparedStatement getPreparedStatement() {
265: return (preparedStatement);
266: }
267:
268: public final CallableStatement getCallableStatement() {
269: return ((CallableStatement) preparedStatement);
270: }
271:
272: public final ResultSet getResultSet() {
273: return (resultSet);
274: }
275:
276: public final boolean nextRow() throws SQLException {
277: position++;
278: return (resultSet.next());
279: }
280:
281: public final int getCurrentRow() {
282: return (position);
283: }
284:
285: public final boolean execute(int resultSetFromObject)
286: throws SQLException {
287: if (preparedStatement != null) {
288: hasResultSet = preparedStatement.execute();
289: if (hasResultSet) {
290: resultSet = (ResultSet) ((CallableStatement) preparedStatement)
291: .getObject(resultSetFromObject);
292: queryResultsCount++;
293: return (true);
294: } else {
295: updateResultsCount++;
296: updateCount = preparedStatement.getUpdateCount();
297: return (updateCount > -1);
298: }
299: } else {
300: return (false);
301: }
302: }
303:
304: public final boolean execute() throws SQLException {
305: if (preparedStatement != null) {
306: hasResultSet = preparedStatement.execute();
307: if (hasResultSet) {
308: resultSet = preparedStatement.getResultSet();
309: resultSetMetaData = resultSet.getMetaData();
310: queryResultsCount++;
311: return (true);
312: } else {
313: updateResultsCount++;
314: updateCount = preparedStatement.getUpdateCount();
315: return (updateCount > -1);
316: }
317: } else {
318: return (false);
319: }
320: }
321:
322: public final boolean executeQuery() throws SQLException {
323: if (preparedStatement != null) {
324: resultSet = preparedStatement.executeQuery();
325: if (resultSet != null) {
326: resultSetMetaData = resultSet.getMetaData();
327: queryResultsCount++;
328: hasResultSet = true;
329: return (true);
330: } else {
331: return (false);
332: }
333: } else {
334: return (false);
335: }
336: }
337:
338: /**
339: * Try to get the next ResultSet
340: *
341: * @return whether there is one or not
342: * @throws SQLException
343: */
344: public final boolean getMoreResults() throws SQLException {
345: if (preparedStatement != null) {
346: hasResultSet = preparedStatement.getMoreResults();
347: if (hasResultSet) {
348: resultSet = preparedStatement.getResultSet();
349: resultSetMetaData = resultSet.getMetaData();
350: queryResultsCount++;
351: return (true);
352: } else {
353: updateResultsCount++;
354: updateCount = preparedStatement.getUpdateCount();
355: return (updateCount > -1);
356: }
357: } else {
358: return (false);
359: }
360: }
361:
362: public final boolean hasResultSet() {
363: return (hasResultSet);
364: }
365:
366: /**
367: * Returns the how many rows where updated on last update
368: */
369: public final int getUpdateCount() {
370: return (updateCount);
371: }
372:
373: /**
374: * Returns the number of query results
375: */
376: public final int getQueryResultsCount() {
377: return (queryResultsCount);
378: }
379:
380: /**
381: * Returns the number of update results
382: */
383: public final int getUpdateResultsCount() {
384: return (updateResultsCount);
385: }
386:
387: public final boolean keepGoing() {
388: return (keepgoing);
389: }
390:
391: public final void setKeepGoing(boolean still) {
392: keepgoing = still;
393: }
394:
395: /* ************************ GROUPING ************************ */
396:
397: public final void incGroupLevel() {
398: groupLevel++;
399: }
400:
401: public final void decGroupLevel() {
402: groupLevel--;
403: }
404:
405: public final boolean groupLevelExists() {
406: return (groups != null && groups.size() >= groupLevel + 1 && groups
407: .get(groupLevel) != null);
408: }
409:
410: public final void setGroupingVar(String key) throws SQLException {
411: if (groups == null)
412: groups = new ArrayList(groupLevel);
413: groups.ensureCapacity(groupLevel);
414: groups.add(groupLevel, new EsqlGroup(key, getResultSet()
415: .getObject(key)));
416: }
417:
418: public final boolean hasGroupingVarChanged() throws SQLException {
419: if (changeLevel != -1) {
420: if (changeLevel < groupLevel) {
421: return (true);
422: } else {
423: changeLevel = -1;
424: return (true);
425: }
426: } else {
427: boolean result = false;
428: // need to check the complete hierarchy of nested groups for changes
429: for (int i = 0; i <= groupLevel; i++) {
430: Object tmp = getResultSet().getObject(
431: ((EsqlGroup) groups.get(i)).var);
432: if (!tmp.equals(((EsqlGroup) groups.get(i)).value)) {
433: ((EsqlGroup) groups.get(i)).value = tmp;
434: result = true;
435: if (changeLevel == -1 && groupLevel != i)
436: changeLevel = i;
437: }
438: }
439: return (result);
440: }
441: }
442:
443: final static class EsqlGroup {
444: public String var = null;
445: public Object value = null;
446:
447: EsqlGroup(String var, Object value) {
448: this.var = var;
449: this.value = value;
450: }
451: }
452:
453: }
|