Package org.sqlite

Source Code of org.sqlite.Stmt

/*
* Copyright (c) 2007 David Crawshaw <david@zentus.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package org.sqlite;

import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;

import org.sqlite.DB.ProgressObserver;
import org.sqlite.ExtendedCommand.SQLExtension;

class Stmt extends Unused implements Statement, Codes
{
    final Conn conn;
    final DB   db;
    final RS   rs;

    private MetaData metadata;

    long       pointer;
    String     sql            = null;

    int        batchPos;
    Object[]   batch          = null;
    boolean    resultsWaiting = false;

    Stmt(Conn c) {
        conn = c;
        db = conn.db();
        rs = new RS(this);
    }

    /**
     * @throws SQLException If the database is not opened.
     */
    protected final void checkOpen() throws SQLException {
        if (pointer == 0)
            throw new SQLException("statement is not executing");
    }

    /**
     * @return True if the database is opened; false otherwise.
     * @throws SQLException
     */
    boolean isOpen() throws SQLException {
        return (pointer != 0);
    }

    /**
     * Calls sqlite3_step() and sets up results. Expects a clean stmt.
     * @return True if the ResultSet has at least one row; false otherwise.
     * @throws SQLException If the given SQL statement is null or no database is open.
     */
    protected boolean exec() throws SQLException {
        if (sql == null)
            throw new SQLException("SQLiteJDBC internal error: sql==null");
        if (rs.isOpen())
            throw new SQLException("SQLite JDBC internal error: rs.isOpen() on exec.");

        boolean rc = false;
        try {
            rc = db.execute(this, null);
        }
        finally {
            resultsWaiting = rc;
        }

        return db.column_count(pointer) != 0;
    }

    /**
     * Executes SQL statement and throws SQLExceptions if the given SQL
     * statement is null or no database is open.
     * @param sql SQL statement.
     * @return True if the ResultSet has at least one row; false otherwise.
     * @throws SQLException If the given SQL statement is null or no database is open.
     */
    protected boolean exec(String sql) throws SQLException {
        if (sql == null)
            throw new SQLException("SQLiteJDBC internal error: sql==null");
        if (rs.isOpen())
            throw new SQLException("SQLite JDBC internal error: rs.isOpen() on exec.");

        boolean rc = false;
        try {
            rc = db.execute(sql);
        }
        finally {
            resultsWaiting = rc;
        }

        return db.column_count(pointer) != 0;
    }

    protected void internalClose() throws SQLException {
        if (db.conn.isClosed())
            throw DB.newSQLException(SQLITE_ERROR, "Connection is closed");

        if (pointer == 0)
            return;
       
        rs.close();
        batch = null;
        batchPos = 0;
        int resp = db.finalize(this);
        if (resp != SQLITE_OK && resp != SQLITE_MISUSE)
            db.throwex();
    }

    // PUBLIC INTERFACE /////////////////////////////////////////////

    /**
     * @see java.sql.Statement#close()
     */
    public void close() throws SQLException {
        if (metadata != null)
            metadata.close();

        internalClose();
    }
    /**
     * @see java.lang.Object#finalize()
     */
    @Override
    protected void finalize() throws SQLException {
        close();
    }

    /**
     * @see java.sql.Statement#execute(java.lang.String)
     */
    public boolean execute(String sql) throws SQLException {
        internalClose();
        this.sql = sql;

        db.prepare(this);
        return exec();
    }

    /**
     * @see java.sql.Statement#executeQuery(java.lang.String)
     */
    public ResultSet executeQuery(String sql) throws SQLException {
        internalClose();
        this.sql = sql;

        db.prepare(this);
        if (!exec()) {
            internalClose();
            throw new SQLException("query does not return ResultSet", "SQLITE_DONE", SQLITE_DONE);
        }
        return getResultSet();
    }

    static class BackupObserver implements ProgressObserver
    {
        public void progress(int remaining, int pageCount) {
            System.out.println(String.format("remaining:%d, page count:%d", remaining, pageCount));
        }
    }

    /**
     * @see java.sql.Statement#executeUpdate(java.lang.String)
     */
    public int executeUpdate(String sql) throws SQLException {
        internalClose();
        this.sql = sql;

        int changes = 0;
        SQLExtension ext = ExtendedCommand.parse(sql);
        if (ext != null) {
            // execute extended command
            ext.execute(db);
        }
        else {
            try {
                changes = db.total_changes();

                // directly invokes the exec API to support multiple SQL statements
                int statusCode = db._exec(sql);
                if (statusCode != SQLITE_OK)
                    throw DB.newSQLException(statusCode, "");

                changes = db.total_changes() - changes;
            }
            finally {
                internalClose();
            }
        }
        return changes;
    }

    /**
     * @see java.sql.Statement#getResultSet()
     */
    public ResultSet getResultSet() throws SQLException {
        checkOpen();
        if (rs.isOpen())
            throw new SQLException("ResultSet already requested");
        if (db.column_count(pointer) == 0)
            throw new SQLException("no ResultSet available");
        if (rs.colsMeta == null)
            rs.colsMeta = db.column_names(pointer);
        rs.cols = rs.colsMeta;

        rs.open = resultsWaiting;
        resultsWaiting = false;
        return rs;
    }

    /*
     * This function has a complex behaviour best understood by carefully
     * reading the JavaDoc for getMoreResults() and considering the test
     * StatementTest.execute().
     * @see java.sql.Statement#getUpdateCount()
     */
    public int getUpdateCount() throws SQLException {
        if (pointer != 0 && !rs.isOpen() && !resultsWaiting && db.column_count(pointer) == 0)
            return db.changes();
        return -1;
    }

    /**
     * @see java.sql.Statement#addBatch(java.lang.String)
     */
    public void addBatch(String sql) throws SQLException {
        internalClose();
        if (batch == null || batchPos + 1 >= batch.length) {
            Object[] nb = new Object[Math.max(10, batchPos * 2)];
            if (batch != null)
                System.arraycopy(batch, 0, nb, 0, batch.length);
            batch = nb;
        }
        batch[batchPos++] = sql;
    }

    /**
     * @see java.sql.Statement#clearBatch()
     */
    public void clearBatch() throws SQLException {
        batchPos = 0;
        if (batch != null)
            for (int i = 0; i < batch.length; i++)
                batch[i] = null;
    }

    /**
     * @see java.sql.Statement#executeBatch()
     */
    public int[] executeBatch() throws SQLException {
        // TODO: optimize
        internalClose();
        if (batch == null || batchPos == 0)
            return new int[] {};

        int[] changes = new int[batchPos];

        synchronized (db) {
            try {
                for (int i = 0; i < changes.length; i++) {
                    try {
                        this.sql = (String) batch[i];
                        db.prepare(this);
                        changes[i] = db.executeUpdate(this, null);
                    }
                    catch (SQLException e) {
                        throw new BatchUpdateException("batch entry " + i + ": " + e.getMessage(), changes);
                    }
                    finally {
                        db.finalize(this);
                    }
                }
            }
            finally {
                clearBatch();
            }
        }

        return changes;
    }

    /**
     * @see java.sql.Statement#setCursorName(java.lang.String)
     */
    public void setCursorName(String name) {}

    /**
     * @see java.sql.Statement#getWarnings()
     */
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    /**
     * @see java.sql.Statement#clearWarnings()
     */
    public void clearWarnings() throws SQLException {}

    /**
     * @see java.sql.Statement#getConnection()
     */
    public Connection getConnection() throws SQLException {
        return conn;
    }

    /**
     * @see java.sql.Statement#cancel()
     */
    public void cancel() throws SQLException {
        db.interrupt();
    }

    /**
     * @see java.sql.Statement#getQueryTimeout()
     */
    public int getQueryTimeout() throws SQLException {
        return conn.getTimeout();
    }

    /**
     * @see java.sql.Statement#setQueryTimeout(int)
     */
    public void setQueryTimeout(int seconds) throws SQLException {
        if (seconds < 0)
            throw new SQLException("query timeout must be >= 0");
        conn.setTimeout(1000 * seconds);
    }

    // TODO: write test
    /**
     * @see java.sql.Statement#getMaxRows()
     */
    public int getMaxRows() throws SQLException {
        //checkOpen();
        return rs.maxRows;
    }

    /**
     * @see java.sql.Statement#setMaxRows(int)
     */
    public void setMaxRows(int max) throws SQLException {
        //checkOpen();
        if (max < 0)
            throw new SQLException("max row count must be >= 0");
        rs.maxRows = max;
    }

    /**
     * @see java.sql.Statement#getMaxFieldSize()
     */
    public int getMaxFieldSize() throws SQLException {
        return 0;
    }

    /**
     * @see java.sql.Statement#setMaxFieldSize(int)
     */
    public void setMaxFieldSize(int max) throws SQLException {
        if (max < 0)
            throw new SQLException("max field size " + max + " cannot be negative");
    }

    /**
     * @see java.sql.Statement#getFetchSize()
     */
    public int getFetchSize() throws SQLException {
        return rs.getFetchSize();
    }

    /**
     * @see java.sql.Statement#setFetchSize(int)
     */
    public void setFetchSize(int r) throws SQLException {
        rs.setFetchSize(r);
    }

    /**
     * @see java.sql.Statement#getFetchDirection()
     */
    public int getFetchDirection() throws SQLException {
        return rs.getFetchDirection();
    }

    /**
     * @see java.sql.Statement#setFetchDirection(int)
     */
    public void setFetchDirection(int d) throws SQLException {
        rs.setFetchDirection(d);
    }

    /**
     * As SQLite's last_insert_rowid() function is DB-specific not statement
     * specific, this function introduces a race condition if the same
     * connection is used by two threads and both insert.
     * @see java.sql.Statement#getGeneratedKeys()
     */
    public ResultSet getGeneratedKeys() throws SQLException {
        if (metadata == null)
            metadata = (MetaData) conn.getMetaData();

        return metadata.getGeneratedKeys();
    }

    /**
     * SQLite does not support multiple results from execute().
     * @see java.sql.Statement#getMoreResults()
     */
    public boolean getMoreResults() throws SQLException {
        return getMoreResults(0);
    }

    /**
     * @see java.sql.Statement#getMoreResults(int)
     */
    public boolean getMoreResults(int c) throws SQLException {
        checkOpen();
        internalClose(); // as we never have another result, clean up pointer
        return false;
    }

    /**
     * @see java.sql.Statement#getResultSetConcurrency()
     */
    public int getResultSetConcurrency() throws SQLException {
        return ResultSet.CONCUR_READ_ONLY;
    }

    /**
     * @see java.sql.Statement#getResultSetHoldability()
     */
    public int getResultSetHoldability() throws SQLException {
        return ResultSet.CLOSE_CURSORS_AT_COMMIT;
    }

    /**
     * @see java.sql.Statement#getResultSetType()
     */
    public int getResultSetType() throws SQLException {
        return ResultSet.TYPE_FORWARD_ONLY;
    }

    /**
     * @see java.sql.Statement#setEscapeProcessing(boolean)
     */
    public void setEscapeProcessing(boolean enable) throws SQLException {
        if(enable) {
          throw unused();
        }
    }

}
TOP

Related Classes of org.sqlite.Stmt

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.