/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.db.table; import com.caucho.db.block.Block; import com.caucho.db.block.BlockStore; import com.caucho.db.sql.QueryContext; import com.caucho.db.sql.SelectResult; import com.caucho.db.xa.DbTransaction; import com.caucho.util.L10N; import java.io.IOException; import java.sql.SQLException; /** * Iterates over a table's rows. */ public class TableIterator { private static final L10N L = new L10N(TableIterator.class); private final static byte []_nullBuffer = new byte[256]; private Table _table; private DbTransaction _xa; private QueryContext _queryContext; private long _blockId; private int _rowLength; private int _rowEnd; private int _rowOffset; private Block _block; private byte []_buffer; public TableIterator() { } TableIterator(Table table) { init(table); } public void init(Table table) { _table = table; if (table.getId() == 0) { throw new IllegalStateException(L.l("iterating with closed table {0}", table)); } table.getColumns(); _rowLength = table.getRowLength(); _rowEnd = table.getRowEnd(); _rowOffset = _rowEnd; _blockId = 0; } /** * Returns the table of the iterator. */ public Table getTable() { return _table; } /** * Returns the current block id of the iterator. */ public final long getBlockId() { return _blockId; } /** * Sets the current block id of the iterator. */ public final void setBlockId(long blockId) { _blockId = blockId; } /** * Returns the current address. */ public final long getRowAddress() { return BlockStore.blockIdToAddress(_blockId) + _rowOffset; } /** * Returns the current row offset of the iterator. */ public final int getRowOffset() { return _rowOffset; } /** * Sets the current row offset of the iterator. */ public final void setRowOffset(int rowOffset) { _rowOffset = rowOffset; } /** * Gets the current block. */ public final byte []getBuffer() { return _buffer; } /** * Returns the transaction for the iterator. */ public DbTransaction getTransaction() { return _xa; } /** * Returns the query context for the iterator. */ public QueryContext getQueryContext() { return _queryContext; } public void init(QueryContext queryContext) throws SQLException { init(queryContext.getTransaction()); _queryContext = queryContext; } public void init(DbTransaction xa) throws SQLException { Block block = _block; _block = null; _buffer = null; if (block != null) { block.free(); } _blockId = 0; _rowOffset = Integer.MAX_VALUE / 2; _queryContext = null; _xa = xa; } public void initRow() throws IOException { _rowOffset = -_rowLength; } public void prevRow() { _rowOffset -= _rowLength; } /** * Sets the row. */ void setRow(Block block, int rowOffset) { _block = block; _buffer = block.getBuffer(); _blockId = block.getBlockId(); _rowOffset = rowOffset; } public Block getBlock() { return _block; } /** * Returns the next tuple in the current row. * * @return true if a tuple is found, * or false if the block has no more tuples */ public boolean nextRow() throws IOException { int rowOffset = _rowOffset; int rowLength = _rowLength; int rowEnd = _rowEnd; byte []buffer = _buffer; rowOffset += rowLength; for (; rowOffset < rowEnd; rowOffset += rowLength) { if ((buffer[rowOffset] & Table.ROW_VALID) != 0) { _rowOffset = rowOffset; return true; } } _rowOffset = rowOffset; return false; } /** * Returns the next row. */ public boolean next() throws IOException { do { if (nextRow()) return true; } while (nextBlock()); return false; } /** * Returns the following block. */ public boolean nextBlock() throws IOException { byte []buffer = _buffer; Block block = _block; _block = null; _buffer = null; if (block != null) { block.free(); } _blockId = _table.firstRowBlock(_blockId + Table.BLOCK_SIZE); if (_blockId < 0) { return false; } block = _xa.readBlock(_table, _blockId); buffer = block.getBuffer(); _block = block; _buffer = buffer; _rowOffset = 0; return true; } /** * Sets the next row. */ public boolean isValidRow(long rowAddr) throws IOException { long blockId = _table.addressToBlockId(rowAddr); if (! _table.isRowBlock(blockId)) { return false; } int rowOffset = (int) (rowAddr & BlockStore.BLOCK_OFFSET_MASK); if (rowOffset % _table.getRowLength() == 0) { return true; } else { return false; } } /** * Sets the next row. */ public void setRow(long rowAddr) throws IOException { long blockId = _table.addressToBlockId(rowAddr); if (blockId != _blockId) { _blockId = blockId; Block block = _block; _block = null; _buffer = null; if (block != null) { block.free(); } _block = _xa.readBlock(_table, _blockId); _buffer = _block.getBuffer(); } _rowOffset = (int) (rowAddr & BlockStore.BLOCK_OFFSET_MASK); } /** * Sets the next row. */ public void initNullRow() throws IOException { Block block = _block; _block = null; _buffer = null; if (block != null) block.free(); _rowOffset = 0; _buffer = _nullBuffer; } /** * Returns true for the null for (for OUTER JOINs) */ public boolean isNullRow() { return _buffer == _nullBuffer; } /** * Returns true if the column is null. */ public boolean isNull(Column column) throws SQLException { return column.isNull(_buffer, _rowOffset); } /** * Returns the string for the column at the given index. */ public String getString(Column column) throws SQLException { return column.getString(getBlockId(), _buffer, _rowOffset); } /** * Returns the string for the column at the given index. */ public void setString(DbTransaction xa, Column column, String value) throws SQLException { column.setString(xa, _buffer, _rowOffset, value); setDirty(); } /** * Returns the column's value as an integer * * @param index column index in the row * * @return the integer value */ public int getInteger(Column column) throws SQLException { return column.getInteger(getBlockId(), _buffer, _rowOffset); } /** * Returns the integer for the column at the given index. */ public void setInteger(DbTransaction xa, Column column, int value) throws SQLException { column.setInteger(xa, _buffer, _rowOffset, value); setDirty(); } /** * Returns the column's long value. * * @param index column index in the row * * @return the long value */ public long getLong(Column column) throws SQLException { return column.getLong(getBlockId(), _buffer, _rowOffset); } /** * Returns the long for the column at the given index. */ public void setLong(DbTransaction xa, Column column, long value) throws SQLException { column.setLong(xa, _buffer, _rowOffset, value); setDirty(); } /** * Returns the column's double value. * * @param index column index in the row * * @return the double value */ public double getDouble(Column column) throws SQLException { return column.getDouble(getBlockId(), _buffer, _rowOffset); } /** * Returns the double for the column at the given index. */ public void setDouble(DbTransaction xa, Column column, double value) throws SQLException { column.setDouble(xa, _buffer, _rowOffset, value); setDirty(); } /** * Returns the column's bytes value. * * @param index column index in the row * * @return the double value */ public byte []getBytes(Column column) throws SQLException { return column.getBytes(getBlockId(), _buffer, _rowOffset); } public boolean isEqual(Column column, byte []matchBuffer) throws SQLException { return column.isEqual(_buffer, _rowOffset, matchBuffer, 0, matchBuffer.length); } public boolean isEqual(Column column, byte []matchBuffer, int matchLength) throws SQLException { return column.isEqual(_buffer, _rowOffset, matchBuffer, 0, matchLength); } public boolean isEqual(Column column, String string) throws SQLException { System.out.println("IS_EQ: " + column + " " + string); return column.isEqual(getBlockId(), _buffer, _rowOffset, string); } /** * Evaluates the row to the result. */ public int getBuffer(Column column, byte []buffer, int offset) throws SQLException { return column.evalToBuffer(_buffer, _rowOffset, buffer, offset); } /** * Evaluates the row to the result. */ public void evalToResult(Column column, SelectResult result) throws SQLException { column.evalToResult(_blockId, _buffer, _rowOffset, result); } public void delete() throws SQLException { setDirty(); _table.delete(_xa, _block, _buffer, _rowOffset, true); } public void setDirty() throws SQLException { _xa.addUpdateBlock(_block); _block.setDirty(_rowOffset, _rowOffset + _rowLength); } public void free() { Block block = _block; _block = null; _table = null; _queryContext = null; _xa = null; if (block != null) block.free(); } @Override public String toString() { return (getClass().getSimpleName() + "[" + _table.getName() + "," + Long.toHexString(_blockId) + "," + _rowOffset + "]"); } }