All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.firebirdsql.jdbc.FBStatementFetcher Maven / Gradle / Ivy

There is a newer version: 4.0.10.java7
Show newest version
/*
 * Firebird Open Source JavaEE Connector - JDBC Driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program 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.  See the
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a source control history command.
 *
 * All rights reserved.
 */
package org.firebirdsql.jdbc;

import org.firebirdsql.gds.JaybirdErrorCodes;
import org.firebirdsql.gds.impl.GDSHelper;
import org.firebirdsql.gds.ng.FbExceptionBuilder;
import org.firebirdsql.gds.ng.FbStatement;
import org.firebirdsql.gds.ng.fields.RowValue;
import org.firebirdsql.gds.ng.listeners.DefaultStatementListener;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Statement fetcher for read-only case. It differs from updatable cursor case
 * by the cursor position after {@link #next()} call. This class changes cursor
 * position to point to the next row.
 */
class FBStatementFetcher implements FBFetcher {

    private boolean closed;
    private boolean wasFetched;

    protected final GDSHelper gdsHelper;
    protected final FBObjectListener.FetcherListener fetcherListener;

    protected final int maxRows;
    protected int fetchSize;

    protected final Synchronizable syncProvider;
    protected final FbStatement stmt;

    private List rows = new ArrayList<>();
    private final RowListener rowListener = new RowListener();
    private boolean allRowsFetched;
    protected RowValue _nextRow;

    private int rowNum = 0;
    private int rowPosition = 0;

    private boolean isEmpty = false;
    private boolean isBeforeFirst = false;
    private boolean isFirst = false;
    private boolean isLast = false;
    private boolean isAfterLast = false;

    FBStatementFetcher(GDSHelper gdsHelper, Synchronizable syncProvider,
            FbStatement stmth,
            FBObjectListener.FetcherListener fetcherListener, int maxRows,
            int fetchSize) throws SQLException {

        this.gdsHelper = gdsHelper;
        this.stmt = stmth;
        stmt.addStatementListener(rowListener);
        this.syncProvider = syncProvider;
        this.fetcherListener = fetcherListener;
        this.maxRows = maxRows;
        this.fetchSize = fetchSize;

        synchronized (syncProvider.getSynchronizationObject()) {
            isEmpty = false;
            isBeforeFirst = false;
            isFirst = false;
            isLast = false;
            isAfterLast = false;
            allRowsFetched = false;
        }
    }

    protected RowValue getNextRow() throws SQLException {
        if (!wasFetched) fetch();
        return _nextRow;
    }

    protected void setNextRow(RowValue nextRow) {
        _nextRow = nextRow;

        if (!wasFetched) {
            wasFetched = true;

            if (_nextRow == null) {
                isEmpty = true;
            } else {
                isBeforeFirst = true;
            }
        }
    }

    @Override
    public boolean next() throws SQLException {
        if (!wasFetched) fetch();

        setIsBeforeFirst(false);
        setIsFirst(false);
        setIsLast(false);
        setIsAfterLast(false);

        if (isEmpty()) {
            return false;
        } else if (getNextRow() == null || (maxRows != 0 && getRowNum() == maxRows)) {
            setIsAfterLast(true);
            allRowsFetched = true;
            fetcherListener.allRowsFetched(this);
            setRowNum(0);
            return false;
        } else {
            fetcherListener.rowChanged(this, getNextRow());
            fetch();
            setRowNum(getRowNum() + 1);

            if (getRowNum() == 1) {
                setIsFirst(true);
            }

            if ((getNextRow() == null) || (maxRows != 0 && getRowNum() == maxRows)) {
                setIsLast(true);
            }

            return true;
        }
    }

    @Override
    public boolean absolute(int row) throws SQLException {
        notScrollable();
        return false;
    }

    @Override
    public boolean first() throws SQLException {
        notScrollable();
        return false;
    }

    @Override
    public boolean last() throws SQLException {
        notScrollable();
        return false;
    }

    @Override
    public boolean previous() throws SQLException {
        notScrollable();
        return false;
    }

    @Override
    public boolean relative(int row) throws SQLException {
        notScrollable();
        return false;
    }

    @Override
    public void beforeFirst() throws SQLException {
        notScrollable();
    }

    @Override
    public void afterLast() throws SQLException {
        notScrollable();
    }

    public void fetch() throws SQLException {
        synchronized (syncProvider.getSynchronizationObject()) {
            checkClosed();
            int maxRows = 0;

            if (this.maxRows != 0) maxRows = this.maxRows - rowNum;

            int fetchSize = this.fetchSize;
            if (fetchSize == 0) fetchSize = MAX_FETCH_ROWS;

            if (maxRows != 0 && fetchSize > maxRows) fetchSize = maxRows;

            if (!allRowsFetched && (rows.isEmpty() || rows.size() == rowPosition)) {
                rows.clear();
                stmt.fetchRows(fetchSize);
                rowPosition = 0;
            }

            if (rows.size() > rowPosition) {
                setNextRow(rows.get(rowPosition));
                // help the garbage collector
                rows.set(rowPosition, null);
                rowPosition++;
            } else {
                setNextRow(null);
            }
        }
    }

    @Override
    public void close() throws SQLException {
        close(CompletionReason.OTHER);
    }

    @Override
    public void close(CompletionReason completionReason) throws SQLException {
        closed = true;
        try {
            stmt.closeCursor(completionReason.isTransactionEnd());
        } finally {
            stmt.removeStatementListener(rowListener);
            rows = Collections.emptyList();
            fetcherListener.fetcherClosed(this);
        }
    }

    private void checkClosed() throws SQLException {
        if (closed) throw new FBSQLException("Result set is already closed.");
    }

    @Override
    public int getRowNum() {
        return rowNum;
    }

    public void setRowNum(int rowNumValue) {
        this.rowNum = rowNumValue;
    }

    @Override
    public boolean isEmpty() throws SQLException {
        if (!wasFetched) fetch();
        return isEmpty;
    }

    public void setIsEmpty(boolean isEmptyValue) {
        this.isEmpty = isEmptyValue;
    }

    @Override
    public boolean isBeforeFirst() throws SQLException {
        if (!wasFetched) fetch();
        return isBeforeFirst;
    }

    public void setIsBeforeFirst(boolean isBeforeFirstValue) {
        this.isBeforeFirst = isBeforeFirstValue;
    }

    @Override
    public boolean isFirst() throws SQLException {
        if (!wasFetched) fetch();
        return isFirst;
    }

    public void setIsFirst(boolean isFirstValue) {
        this.isFirst = isFirstValue;
    }

    @Override
    public boolean isLast() throws SQLException {
        if (!wasFetched) fetch();
        return isLast;
    }

    public void setIsLast(boolean isLastValue) {
        this.isLast = isLastValue;
    }

    @Override
    public boolean isAfterLast() throws SQLException {
        if (!wasFetched) fetch();
        return isAfterLast;
    }

    public void setIsAfterLast(boolean isAfterLastValue) {
        this.isAfterLast = isAfterLastValue;
    }

    @Override
    public void deleteRow() throws SQLException {
        // empty
    }

    @Override
    public void insertRow(RowValue data) throws SQLException {
        // empty
    }

    @Override
    public void updateRow(RowValue data) throws SQLException {
        // empty
    }

    @Override
    public void setFetchSize(int fetchSize) {
        this.fetchSize = fetchSize;
    }

    @Override
    public int getFetchSize() {
        return fetchSize;
    }

    private void notScrollable() throws SQLException {
        throw new FbExceptionBuilder().nonTransientException(JaybirdErrorCodes.jb_operationNotAllowedOnForwardOnly)
                .toFlatSQLException();
    }

    private class RowListener extends DefaultStatementListener {
        @Override
        public void receivedRow(FbStatement sender, RowValue rowValue) {
            rows.add(rowValue);
        }

        @Override
        public void allRowsFetched(FbStatement sender) {
            allRowsFetched = true;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy