alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 * MM JDBC Drivers for MySQL
 *
 * $Id: Statement.java,v 1.2 1998/08/25 00:53:48 mmatthew Exp $
 *
 * Copyright (C) 1998 Mark Matthews <mmatthew@worldserver.com>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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 GNU
 * Library General Public License for more details.
 * 
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * See the COPYING file located in the top-level-directory of
 * the archive of this library for complete text of license.
 *
 * Some portions:
 *
 * Copyright (c) 1996 Bradley McLean / Jeffrey Medeiros
 * Modifications Copyright (c) 1996/1997 Martin Rode
 * Copyright (c) 1997 Peter T Mount
 */

/**
 * A Statement object is used for executing a static SQL statement and
 * obtaining the results produced by it.
 *
 * <p>Only one ResultSet per Statement can be open at any point in time.
 * Therefore, if the reading of one ResultSet is interleaved with the
 * reading of another, each must have been generated by different
 * Statements.  All statement execute methods implicitly close a
 * statement's current ResultSet if an open one exists.
 *
 * @see java.sql.Statement
 * @see ResultSet
 * @author Mark Matthews <mmatthew@worldserver.com>
 * @version $Id$
 */

package org.gjt.mm.mysql;

import java.sql.*;

public class Statement implements java.sql.Statement
{
    Connection      _Conn             = null; // The connection who created us
    ResultSet       _Results          = null; // The current results
    ResultSet       _NextResults      = null; // The next result set
    SQLWarning      _Warnings         = null; // The warnings chain.
    int             _timeout          = 0;    // The timeout for a query
    boolean         _escapeProcessing = true; // escape processing flag
    EscapeProcessor _Escaper          = null; // The escape processor

    int             _max_field_size   = MysqlIO.MAXBUF;
    int             _max_rows         = -1;

    long            _update_count            = -1;
    long            _last_insert_id          = -1;

    String          _Catalog = null;

    /**
     * Constructor for a Statement.  It simply sets the connection
     * that created us.
     *
     * @param c the Connection instantation that creates us
     */

    public Statement(Connection C, String Catalog)
    {
	if (Driver.trace) {
	    Object[] Args = {C};
	    Debug.methodCall(this, "constructor", Args);
	}

	_Conn = C;
	_Escaper = new EscapeProcessor();
	_Catalog = Catalog;
    }

    /**
     * Execute a SQL statement that retruns a single ResultSet
     *
     * @param Sql typically a static SQL SELECT statement
     * @return a ResulSet that contains the data produced by the query
     * @exception java.sql.SQLException if a database access error occurs
     */

    public java.sql.ResultSet executeQuery(String Sql) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {Sql};
	    Debug.methodCall(this, "executeQuery", Args);
	}
      
	if (_escapeProcessing) {
	    Sql = _Escaper.escapeSQL(Sql);
	}

	if (Sql.indexOf("||") != -1) {
	    Sql = _Escaper.doConcat(Sql);
	}
       
	if (_Results != null) {
	    _Results.close();
	}

	// If there isn't a limit clause in the SQL
	// then limit the number of rows to return in 
	// an efficient manner. Only do this if
	// setMaxRows() hasn't been used on any Statements
	// generated from the current Connection (saves
	// a query, and network traffic).

      synchronized (_Conn.getMutex()) {
          String OldCatalog = null;

          if (!_Conn.getCatalog().equals(_Catalog)) {
           OldCatalog = _Conn.getCatalog();
           _Conn.setCatalog(_Catalog);
          }

          if (_Conn.useMaxRows()) {

	      // We need to execute this all together
	      // So synchronize on the Connection's mutex (because 
	      // even queries going through there synchronize
	      // on the connection

  	      if (Sql.toUpperCase().indexOf("LIMIT") != -1) { 
	        _Results = _Conn.execSQL(Sql, _max_rows);
	      }
	      else {
		    if (_max_rows <= 0) {
			_Conn.execSQL("SET OPTION SQL_SELECT_LIMIT=" 
				      + MysqlDefs.MAX_ROWS, -1);
		    }
		    else {
			_Conn.execSQL("SET OPTION SQL_SELECT_LIMIT=" + _max_rows,-1);
		    }
                		            
		    _Results = _Conn.execSQL(Sql, -1);

                if (OldCatalog != null) {
                  _Conn.setCatalog(OldCatalog);
                }
		}
	  }
	  else {
	    _Results = _Conn.execSQL(Sql, -1);	    
	  }

        if (OldCatalog != null) {
          _Conn.setCatalog(OldCatalog);
        }
      }

	_last_insert_id = _Results.getUpdateID();
	_NextResults = _Results;
	_Results.setConnection(_Conn);
	return _Results;
    }

    /**
     * Execute a SQL INSERT, UPDATE or DELETE statement.  In addition
     * SQL statements that return nothing such as SQL DDL statements
     * can be executed
     *
     * Any IDs generated for AUTO_INCREMENT fields can be retrieved
     * by casting this Statement to org.gjt.mm.mysql.Statement and
     * calling the getLastInsertID() method.
     *
     * @param Sql a SQL statement
     * @return either a row count, or 0 for SQL commands
     * @exception java.sql.SQLException if a database access error occurs
     */

    public int executeUpdate(String Sql) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {Sql};
	    Debug.methodCall(this, "executeUpdate", Args);
	}
	
	if (_escapeProcessing) {
	    Sql = _Escaper.escapeSQL(Sql);
	}

	if (Sql.indexOf("||") != -1) {
	    Sql = _Escaper.doConcat(Sql);
	}

      // The checking and changing of catalogs
      // must happen in sequence, so synchronize
      // on the same mutex that _Conn is using

	ResultSet RS = null;

	synchronized (_Conn.getMutex()) {
        String OldCatalog = null;

        if (!_Conn.getCatalog().equals(_Catalog)) {
          OldCatalog = _Conn.getCatalog();
          _Conn.setCatalog(_Catalog);
        }

	  RS = _Conn.execSQL(Sql, -1);
   
          RS.setConnection(_Conn);

	  if (OldCatalog != null) {
          _Conn.setCatalog(OldCatalog);
        }
      }
	
	if (RS.reallyResult()) {
	    throw new java.sql.SQLException("Results returned for UPDATE ONLY.", "01S03");
	}
	else {
	    _update_count = RS.getUpdateCount();
	    
	    int truncated_update_count = 0;

	    if (_update_count > Integer.MAX_VALUE) {
		truncated_update_count = Integer.MAX_VALUE;
	    }
	    else {
		truncated_update_count = (int)_update_count;
	    }

	    _last_insert_id = RS.getUpdateID();
	    
	    return truncated_update_count;
	}
    }

    /**
     * In many cases, it is desirable to immediately release a
     * Statement's database and JDBC resources instead of waiting
     * for this to happen when it is automatically closed.  The
     * close method provides this immediate release.
     *
     * <p>Note: A Statement is automatically closed when it is
     * garbage collected.  When a Statement is closed, its current
     * ResultSet, if one exists, is also closed.
     *
     * @exception java.sql.SQLException if a database access error occurs
     */

    public void close() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "close", Args);
	}

	_Results  = null;
	_Conn     = null;
	_Warnings = null;
	_Escaper  = null;
    }
  
    /**
     * The maxFieldSize limit (in bytes) is the maximum amount of
     * data returned for any column value; it only applies to
     * BINARY, VARBINARY, LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR
     * columns.  If the limit is exceeded, the excess data is silently
     * discarded.
     *
     * @return the current max column size limit; zero means unlimited
     * @exception java.sql.SQLException if a database access error occurs
     */

    public int getMaxFieldSize() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getMaxFieldSize", Args);
	}

	return _max_field_size; // Init. set to MAXBUFFER in MysqlIO
    }

    /**
     * Sets the maxFieldSize
     *
     * @param max the new max column size limit; zero means unlimited
     * @exception java.sql.SQLException if size exceeds buffer size
     */

    public void setMaxFieldSize(int max) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {new Integer(max)};
	    Debug.methodCall(this, "setMaxFieldSize", Args);
	}

	if (max > MysqlIO.MAXBUF)
	    throw new java.sql.SQLException("Attempt to set max field size > " + MysqlIO.MAXBUF + " (compile time default)", "S1009");
	else
	    _max_field_size = max;
    }

    /**
     * The maxRows limit is set to limit the number of rows that
     * any ResultSet can contain.  If the limit is exceeded, the
     * excess rows are silently dropped.
     *
     * @return the current maximum row limit; zero means unlimited
     * @exception java.sql.SQLException if a database access error occurs
     */

    public int getMaxRows() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getMaxRows", Args);
	}

	if (_max_rows <=0) {
	    return 0;
	}
	else {
	    return _max_rows;
	}
    }

    /**
     * Set the maximum number of rows
     *
     * @param max the new max rows limit; zero means unlimited
     * @exception java.sql.SQLException if a database access error occurs
     * @see getMaxRows
     */

    public void setMaxRows(int max) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {new Integer(max)};
	    Debug.methodCall(this, "setMaxRows", Args);
	}

	if (max > MysqlDefs.MAX_ROWS) {
	    throw new java.sql.SQLException("setMaxRows() out of range. " + max + " > " + MysqlDefs.MAX_ROWS + ".", "S1009");
	}

	if (max == 0) {
	    max = -1;
	}

	_max_rows = max;

	// Most people don't use setMaxRows()
	// so don't penalize them
	// with the extra query it takes
	// to do it efficiently unless we need
	// to.

	_Conn.maxRowsChanged();
    }

    /**
     * If escape scanning is on (the default), the driver will do escape
     * substitution before sending the SQL to the database.
     *
     * @param enable true to enable; false to disable
     * @exception java.sql.SQLException if a database access error occurs
     */

    public void setEscapeProcessing(boolean enable) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {new Boolean(enable)};
	    Debug.methodCall(this, "setEscapeProcessing", Args);
	}

	_escapeProcessing = enable;
    } 

    /**
     * The queryTimeout limit is the number of seconds the driver
     * will wait for a Statement to execute.  If the limit is
     * exceeded, a java.sql.SQLException is thrown.
     *
     * @return the current query timeout limit in seconds; 0 = unlimited
     * @exception java.sql.SQLException if a database access error occurs
     */

    public int getQueryTimeout() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getQueryTimeout", Args);
	}

	return _timeout;
    }

    /**
     * Sets the queryTimeout limit
     *
     * @param seconds - the new query timeout limit in seconds
     * @exception java.sql.SQLException if a database access error occurs
     */

    public void setQueryTimeout(int seconds) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {new Integer(seconds)};
	    Debug.methodCall(this, "setQueryTimeout", Args);
	}

	_timeout = seconds;
    }
  
    /**
     * Cancel can be used by one thread to cancel a statement that
     * is being executed by another thread.  However this driver
     * is synchronous, so this really has no meaning - we
     * define it as a no-op (i.e. you can't cancel, but there is no
     * error if you try.)
     *
     * @exception java.sql.SQLException only because thats the spec.
     */

    public void cancel() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "cancel", Args);
	}

	// No-op
    }

  
    /**
     * The first warning reported by calls on this Statement is
     * returned.  A Statement's execute methods clear its java.sql.SQLWarning
     * chain.  Subsequent Statement warnings will be chained to this
     * java.sql.SQLWarning.
     *
     * <p>The Warning chain is automatically cleared each time a statement
     * is (re)executed.
     *
     * <p>Note:  If you are processing a ResultSet then any warnings
     * associated with ResultSet reads will be chained on the ResultSet
     * object.
     *
     * @return the first java.sql.SQLWarning on null
     * @exception java.sql.SQLException if a database access error occurs
     */

    public java.sql.SQLWarning getWarnings() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getWarnings", Args);
	}

	return _Warnings;
    }

  
    /**
     * After this call, getWarnings returns null until a new warning
     * is reported for this Statement.
     *
     * @exception java.sql.SQLException if a database access error occurs (why?)
     */

    public void clearWarnings() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "clearWarnings", Args);
	}

	_Warnings = null;
    }

    /**
     * setCursorName defines the SQL cursor name that will be used by
     * subsequent execute methods.  This name can then be used in SQL
     * positioned update/delete statements to identify the current row
     * in the ResultSet generated by this statement.  If a database
     * doesn't support positioned update/delete, this method is a
     * no-op.
     *
     * <p>Note: This MySQL driver does not support cursors.
     *
     *
     * @param name the new cursor name
     * @exception java.sql.SQLException if a database access error occurs
     */

    public void setCursorName(String Name) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {Name};
	    Debug.methodCall(this, "setCursorName", Args);
	}

	// No-op
    }

    /**
     * Execute a SQL statement that may return multiple results. We
     * don't have to worry about this since we do not support multiple
     * ResultSets.   You can use getResultSet or getUpdateCount to
     * retrieve the result.
     *
     * @param sql any SQL statement
     * @return true if the next result is a ResulSet, false if it is
     *      an update count or there are no more results
     * @exception java.sql.SQLException if a database access error occurs
     */

    public boolean execute(String Sql) throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = {Sql};
	    Debug.methodCall(this, "execute", Args);
	}

	if (_escapeProcessing) {
	    Sql = _Escaper.escapeSQL(Sql);
	}

	if (Sql.indexOf("||") != -1) {
	    Sql = _Escaper.doConcat(Sql);
	}

	if (_Results != null) {
	    _Results.close();
	}

	ResultSet RS = null;

	// If there isn't a limit clause in the SQL
	// then limit the number of rows to return in 
	// an efficient manner. Only do this if
	// setMaxRows() hasn't been used on any Statements
	// generated from the current Connection (saves
	// a query, and network traffic).
	
      synchronized (_Conn.getMutex()) {
        String OldCatalog = null;

        if (!_Conn.getCatalog().equals(_Catalog)) {
         OldCatalog = _Conn.getCatalog();
         _Conn.setCatalog(_Catalog);
        }

	  if (_Conn.useMaxRows()) {
	      if (Sql.toUpperCase().indexOf("LIMIT") != -1) { 
		    RS = _Conn.execSQL(Sql, _max_rows);
		}
		else {
		    if (_max_rows <= 0) {
			_Conn.execSQL("SET OPTION SQL_SELECT_LIMIT=" 
				      + MysqlDefs.MAX_ROWS, -1);
		    }
		    else {
			_Conn.execSQL("SET OPTION SQL_SELECT_LIMIT=" + _max_rows,-1);
		    }                
		    RS = _Conn.execSQL(Sql, -1);
		}
	    }
	    else {
	      RS = _Conn.execSQL(Sql, -1);	    
	    }

          if (OldCatalog != null) {
            _Conn.setCatalog(OldCatalog);
          }
      }
	    
	_last_insert_id = RS.getUpdateID();

	if (RS != null) {
	    _Results = RS;
	}

	RS.setConnection(_Conn);

	return (RS != null && RS.reallyResult());
    }
  
    /**
     * getResultSet returns the current result as a ResultSet.  It
     * should only be called once per result.
     *
     * @return the current result set; null if there are no more
     * @exception java.sql.SQLException if a database access error occurs (why?)
     */

    public java.sql.ResultSet getResultSet() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getResultSet", Args);
	}

	return _Results;
    }

    /**
     * getUpdateCount returns the current result as an update count,
     * if the result is a ResultSet or there are no more results, -1
     * is returned.  It should only be called once per result.
     *
     * @return the current result as an update count.
     * @exception java.sql.SQLException if a database access error occurs
     */

    public int getUpdateCount() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getUpdateCount", Args);
	}
	
	if (_Results == null) {
	    return -1;
	}
	if (_Results.reallyResult()) {
	    return -1;
	}
    
	int truncated_update_count = 0;
    
	if (_Results.getUpdateCount() > Integer.MAX_VALUE) {
	    truncated_update_count = Integer.MAX_VALUE;
	}
	else {
	    truncated_update_count = (int)_Results.getUpdateCount();
	}
    
	return truncated_update_count;
    }

    /**
     * getLongUpdateCount returns the current result as an update count,
     * if the result is a ResultSet or there are no more results, -1
     * is returned.  It should only be called once per result.
     *
     * <p>
     * This method returns longs as MySQL server versions newer than 
     * 3.22.4 return 64-bit values for update counts
     *
     * @return the current result as an update count.
     * @exception java.sql.SQLException if a database access error occurs
     */

    public long getLongUpdateCount()
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getLongUpdateCount", Args);
	}

	if (_Results == null) {
	    return -1;
	}
	
	if (_Results.reallyResult()) {
	    return -1;
	}
	
	return _update_count;
    }

    /**
     * getLastInsertID returns the value of the auto_incremented key
     * after an executeQuery() or excute() call.
     *
     * <p>
     * This gets around the un-threadsafe behavior of
     * "select LAST_INSERT_ID()" which is tied to the Connection
     * that created this Statement, and therefore could have had
     * many INSERTS performed before one gets a chance to call
     * "select LAST_INSERT_ID()".
     *
     * @return the last update ID.
     */

    public long getLastInsertID()
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getLastInsertID", Args);
	}

	return _last_insert_id;
    }

    /**
     * getMoreResults moves to a Statement's next result.  If it returns
     * true, this result is a ResulSet.
     *
     * @return true if the next ResultSet is valid
     * @exception java.sql.SQLException if a database access error occurs
     */

    public boolean getMoreResults() throws java.sql.SQLException
    {
	if (Driver.trace) {
	    Object[] Args = new Object[0];
	    Debug.methodCall(this, "getMoreResults", Args);
	}
	
	if (_NextResults != null) {
	    _Results = _NextResults;
	    _NextResults = null;
	    return true;
	}
	else {
	    return false;
	}
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.