protected Map<String, Serializable> fetchCurrent() throws SQLException {
   Map<String, Serializable> map = q.selectInfo.mapMaker.makeMap(rs);
   if (logger.isLogEnabled()) {
     logger.logMap(map);
   }
   return map;
 }
 protected Map<String, Serializable> fetchNext() throws StorageException, SQLException {
   if (rs == null) {
     return null;
   }
   if (!rs.next()) {
     if (logger.isLogEnabled()) {
       logger.log("  -> END");
     }
     return null;
   }
   return fetchCurrent();
 }
 @Override
 protected void finalize() {
   if (rs != null) {
     logger.warn("Closing an IterableQueryResult for you. Please close them yourself.");
   }
   close();
 }
 public ResultSetQueryResult(
     QueryMaker queryMaker,
     String query,
     QueryFilter queryFilter,
     PathResolver pathResolver,
     JDBCMapper mapper,
     Object... params)
     throws StorageException, SQLException {
   logger = mapper.logger;
   q =
       queryMaker.buildQuery(
           mapper.sqlInfo, mapper.model, pathResolver, query, queryFilter, params);
   if (q == null) {
     logger.log("Query cannot return anything due to conflicting clauses");
     ps = null;
     rs = null;
     eof = true;
     return;
   } else {
     eof = false;
   }
   if (logger.isLogEnabled()) {
     logger.logSQL(q.selectInfo.sql, q.selectParams);
   }
   ps =
       mapper.connection.prepareStatement(
           q.selectInfo.sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
   int i = 1;
   for (Object object : q.selectParams) {
     if (object instanceof Calendar) {
       Calendar cal = (Calendar) object;
       Timestamp ts = new Timestamp(cal.getTimeInMillis());
       ps.setTimestamp(i++, ts, cal); // cal passed for timezone
     } else if (object instanceof String[]) {
       Array array =
           mapper.sqlInfo.dialect.createArrayOf(
               Types.VARCHAR, (Object[]) object, mapper.connection);
       ps.setArray(i++, array);
     } else {
       ps.setObject(i++, object);
     }
   }
   rs = ps.executeQuery();
   // rs.setFetchDirection(ResultSet.FETCH_UNKNOWN); fails in H2
 }
 @Override
 public boolean hasNext() {
   if (next != null) {
     return true;
   }
   if (eof) {
     return false;
   }
   try {
     next = fetchNext();
   } catch (Exception e) {
     logger.error("Error fetching next: " + e.getMessage(), e);
   }
   eof = next == null;
   return !eof;
 }
 @Override
 public void close() {
   if (rs != null) {
     try {
       rs.close();
       closePreparedStatement(ps);
     } catch (SQLException e) {
       logger.error("Error closing statement: " + e.getMessage(), e);
     } finally {
       pos = -1;
       rs = null;
       ps = null;
       q = null;
     }
   }
 }
 @Override
 public void skipTo(long pos) {
   if (rs == null || pos < 0) {
     this.pos = -1;
     return;
   }
   try {
     boolean available = rs.absolute((int) pos + 1);
     if (available) {
       next = fetchCurrent();
       eof = false;
       this.pos = pos;
     } else {
       // after last row
       next = null;
       eof = true;
       this.pos = -1; // XXX
     }
   } catch (SQLException e) {
     logger.error("Error skipping to: " + pos + ": " + e.getMessage(), e);
   }
 }