/** * */ package edu.washington.escience.myria.accessmethod; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.almworks.sqlite4java.SQLiteConnection; import com.almworks.sqlite4java.SQLiteException; import com.almworks.sqlite4java.SQLiteStatement; import edu.washington.escience.myria.Schema; import edu.washington.escience.myria.column.Column; import edu.washington.escience.myria.column.builder.ColumnBuilder; import edu.washington.escience.myria.column.builder.ColumnFactory; import edu.washington.escience.myria.storage.TupleBatch; import edu.washington.escience.myria.storage.TupleUtils; /** * Wraps a SQLiteStatement result set in a Iterator<TupleBatch>. * */ public class SQLiteTupleBatchIterator implements Iterator<TupleBatch> { /** The logger for this class. Uses SQLiteAccessMethod settings. */ private static final Logger LOGGER = LoggerFactory.getLogger(SQLiteAccessMethod.class); /** The results from a SQLite query that will be returned in TupleBatches by this Iterator. */ private final SQLiteStatement statement; /** The connection to the SQLite database. */ private final SQLiteConnection connection; /** The Schema of the TupleBatches returned by this Iterator. */ private final Schema schema; /** * Wraps a SQLiteStatement result set in an Iterator<TupleBatch>. * * @param statement the SQLiteStatement containing the results. If it has not yet stepped, this constructor will step * it. Then the Schema of the generated TupleBatchs will be extracted from the statement. * @param connection the connection to the SQLite database. * @param schema the Schema describing the format of the TupleBatch containing these results. */ public SQLiteTupleBatchIterator( final SQLiteStatement statement, final SQLiteConnection connection, final Schema schema) { this.connection = connection; this.statement = statement; try { if (!statement.hasStepped()) { statement.step(); } this.schema = schema; } catch (final SQLiteException e) { throw new RuntimeException(e); } } @Override public boolean hasNext() { final boolean hasRow = statement.hasRow(); if (!hasRow) { statement.dispose(); connection.dispose(); } return hasRow; } @Override public TupleBatch next() { if (!hasNext()) { throw new NoSuchElementException(); } /* Allocate TupleBatch parameters */ final int numFields = schema.numColumns(); final List<ColumnBuilder<?>> columnBuilders = ColumnFactory.allocateColumns(schema); /** * Loop through resultSet, adding one row at a time. Stop when numTuples hits BATCH_SIZE or there are no more * results. */ int batch_size = TupleUtils.getBatchSize(schema); int numTuples; try { for (numTuples = 0; numTuples < batch_size && statement.hasRow(); ++numTuples) { for (int column = 0; column < numFields; ++column) { columnBuilders.get(column).appendFromSQLite(statement, column); } statement.step(); } } catch (final SQLiteException e) { LOGGER.error("Got SQLiteException:" + e + "in TupleBatchIterator.next()"); throw new RuntimeException(e); } List<Column<?>> columns = new ArrayList<Column<?>>(columnBuilders.size()); for (ColumnBuilder<?> cb : columnBuilders) { columns.add(cb.build()); } return new TupleBatch(schema, columns, numTuples); } @Override public void remove() { throw new UnsupportedOperationException("SQLiteTupleBatchIterator.remove()"); } }