Logo Search packages:      
Sourcecode: mysql-connector-java version File versions

ServerPreparedStatement.java

/*
      Copyright (C) 2002-2004 MySQL AB

      This program is free software; you can redistribute it and/or modify
      it under the terms of version 2 of the GNU General Public License as 
      published by the Free Software Foundation.

      There are special exceptions to the terms and conditions of the GPL 
      as it is applied to this software. View the full text of the 
      exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
      software distribution.

      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
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA



 */
package com.mysql.jdbc;

import com.mysql.jdbc.profiler.ProfileEventSink;
import com.mysql.jdbc.profiler.ProfilerEvent;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;

import java.math.BigDecimal;

import java.net.URL;

import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
import java.sql.ParameterMetaData;
import java.sql.Ref;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Types;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;


/**
 * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements.
 *
 * @author Mark Matthews
 * @version $Id: ServerPreparedStatement.java,v 1.1.2.68 2005/02/17 21:42:27 mmatthews Exp $
 */
00062 public class ServerPreparedStatement extends PreparedStatement {
    /* 1 (length) + 2 (year) + 1 (month) + 1 (day) */
    private static final byte MAX_DATE_REP_LENGTH = (byte) 5;

    /*
      1 (length) + 1 (is negative) + 4 (day count) + 1 (hour)
      + 1 (minute) + 1 (seconds) + 4 (microseconds)
    */
    private static final byte MAX_TIME_REP_LENGTH = 13;

    /*
      1 (length) + 2 (year) + 1 (month) + 1 (day) +
      1 (hour) + 1 (minute) + 1 (second) + 4 (microseconds)
    */
    private static final byte MAX_DATETIME_REP_LENGTH = 12;
    private Buffer outByteBuffer;

    /** The Calendar instance used to create date/time bindings */
00080     private Calendar dateTimeBindingCal = null;

    /** If this statement has been marked invalid, what was the reason? */
00083     private SQLException invalidationException;

    /** Bind values for individual fields */
00086     private BindValue[] parameterBindings;

    /** Field-level metadata for parameters */
00089     private Field[] parameterFields;

    /** Field-level metadata for result sets. */
00092     private Field[] resultFields;

    /**
     * Flag indicating whether or not the long parameters have been 'switched'
     * back to normal parameters. We can not execute() if clearParameters()
     * hasn't been called in this case.
     */
00099     private boolean detectedLongParameterSwitch = false;

    /** Has this prepared statement been marked invalid? */
00102     private boolean invalid = false;

    /** Does this query modify data? */
00105     private boolean isSelectQuery;

    /** Do we need to send/resend types to the server? */
00108     private boolean sendTypesToServer = false;

    /**
     * The number of fields in the result set (if any) for this
     * PreparedStatement.
     */
00114     private int fieldCount;

    /** The type used for string bindings, changes from version-to-version */
00117     private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;

    /** The ID that the server uses to identify this PreparedStatement */
00120     private long serverStatementId;

    /**
     * Creates a new ServerPreparedStatement object.
     *
     * @param conn the connection creating us.
     * @param sql the SQL containing the statement to prepare.
     * @param catalog the catalog in use when we were created.
     *
     * @throws SQLException If an error occurs
     */
00131     public ServerPreparedStatement(Connection conn, String sql, String catalog)
        throws SQLException {
        super(conn, catalog);

        checkNullOrEmptyQuery(sql);

        this.isSelectQuery = StringUtils.startsWithIgnoreCaseAndWs(sql, "SELECT"); //$NON-NLS-1$

        this.useTrueBoolean = this.connection.versionMeetsMinimum(3,
                21, 23);
        this.hasLimitClause = (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1); //$NON-NLS-1$
        this.firstCharOfStmt = StringUtils.firstNonWsCharUc(sql);
        this.originalSql = sql;

        if (this.connection.versionMeetsMinimum(4, 1, 2)) {
            this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING;
        } else {
            this.stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
        }

        try {
            serverPrepare(sql);
        } catch (SQLException sqlEx) {
            // don't wrap SQLExceptions
            throw sqlEx;
        } catch (Exception ex) {
            throw new SQLException(ex.toString(),
                SQLError.SQL_STATE_GENERAL_ERROR);
        }
    }

    /**
     * @see java.sql.PreparedStatement#setArray(int, java.sql.Array)
     */
00165     public void setArray(int i, Array x) throws SQLException {
        throw new NotImplemented();
    }

    /**
     * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream,
     *      int)
     */
00173     public void setAsciiStream(int parameterIndex, InputStream x, int length)
        throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.BINARY);
        } else {
            BindValue binding = getBinding(parameterIndex, true);
            setType(binding, MysqlDefs.FIELD_TYPE_BLOB);

            binding.value = x;
            binding.isNull = false;
            binding.isLongData = true;

            if (this.connection.getUseStreamLengthsInPrepStmts()) {
                binding.bindLength = length;
            } else {
                binding.bindLength = -1;
            }

            serverLongData(parameterIndex, binding);
        }
    }

    /**
     * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal)
     */
00200     public void setBigDecimal(int parameterIndex, BigDecimal x)
        throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.DECIMAL);
        } else {
            setString(parameterIndex, StringUtils.fixDecimalExponent(x.toString()));
        }
    }

    /**
     * @see java.sql.PreparedStatement#setBinaryStream(int,
     *      java.io.InputStream, int)
     */
00215     public void setBinaryStream(int parameterIndex, InputStream x, int length)
        throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.BINARY);
        } else {
            BindValue binding = getBinding(parameterIndex, true);
            setType(binding, MysqlDefs.FIELD_TYPE_BLOB);

            binding.value = x;
            binding.isNull = false;
            binding.isLongData = true;

            if (this.connection.getUseStreamLengthsInPrepStmts()) {
                binding.bindLength = length;
            } else {
                binding.bindLength = -1;
            }

            serverLongData(parameterIndex, binding);
        }
    }

    /**
     * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob)
     */
00242     public void setBlob(int parameterIndex, Blob x) throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.BINARY);
        } else {
            BindValue binding = getBinding(parameterIndex, true);
            setType(binding, MysqlDefs.FIELD_TYPE_BLOB);

            binding.value = x;
            binding.isNull = false;
            binding.isLongData = true;

            if (this.connection.getUseStreamLengthsInPrepStmts()) {
                binding.bindLength = x.length();
            } else {
                binding.bindLength = -1;
            }

            serverLongData(parameterIndex, binding);
        }
    }

    /**
     * @see java.sql.PreparedStatement#setBoolean(int, boolean)
     */
00268     public void setBoolean(int parameterIndex, boolean x)
        throws SQLException {
        setByte(parameterIndex, (x ? (byte) 1 : (byte) 0));
    }

    /**
     * @see java.sql.PreparedStatement#setByte(int, byte)
     */
00276     public void setByte(int parameterIndex, byte x) throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);
        setType(binding, MysqlDefs.FIELD_TYPE_TINY);

        binding.value = null;
        binding.byteBinding = x;
        binding.isNull = false;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#setBytes(int, byte)
     */
00291     public void setBytes(int parameterIndex, byte[] x)
        throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.BINARY);
        } else {
            BindValue binding = getBinding(parameterIndex, false);
            setType(binding, MysqlDefs.FIELD_TYPE_BLOB);

            binding.value = x;
            binding.isNull = false;
            binding.isLongData = false;
        }
    }

    /**
     * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader,
     *      int)
     */
00311     public void setCharacterStream(int parameterIndex, Reader reader, int length)
        throws SQLException {
        checkClosed();

        if (reader == null) {
            setNull(parameterIndex, java.sql.Types.BINARY);
        } else {
            BindValue binding = getBinding(parameterIndex, true);
            setType(binding, MysqlDefs.FIELD_TYPE_BLOB);

            binding.value = reader;
            binding.isNull = false;
            binding.isLongData = true;

            if (this.connection.getUseStreamLengthsInPrepStmts()) {
                binding.bindLength = length;
            } else {
                binding.bindLength = -1;
            }

            serverLongData(parameterIndex, binding);
        }
    }

    /**
     * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob)
     */
00338     public void setClob(int parameterIndex, Clob x) throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.BINARY);
        } else {
            BindValue binding = getBinding(parameterIndex, true);
            setType(binding, MysqlDefs.FIELD_TYPE_BLOB);

            binding.value = x.getCharacterStream();
            binding.isNull = false;
            binding.isLongData = true;

            if (this.connection.getUseStreamLengthsInPrepStmts()) {
                binding.bindLength = x.length();
            } else {
                binding.bindLength = -1;
            }

            serverLongData(parameterIndex, binding);
        }
    }

    /**
     * Set a parameter to a java.sql.Date value.  The driver converts this to a
     * SQL DATE value when it sends it to the database.
     *
     * @param parameterIndex the first parameter is 1, the second is 2, ...
     * @param x the parameter value
     * @param cal the calendar to interpret the date with
     *
     * @exception SQLException if a database-access error occurs.
     */
00371     public void setDate(int parameterIndex, Date x, Calendar cal)
        throws SQLException {
        if (x == null) {
            setNull(parameterIndex, java.sql.Types.DATE);
        } else {
            BindValue binding = getBinding(parameterIndex, false);
            setType(binding, MysqlDefs.FIELD_TYPE_DATE);

            binding.value = x;
            binding.isNull = false;
            binding.isLongData = false;
        }
    }

    /**
     * Set a parameter to a java.sql.Date value.  The driver converts this to a
     * SQL DATE value when it sends it to the database.
     *
     * @param parameterIndex the first parameter is 1, the second is 2, ...
     * @param x the parameter value
     *
     * @exception SQLException if a database-access error occurs.
     */
00394     public void setDate(int parameterIndex, Date x) throws SQLException {
        setDate(parameterIndex, x, null);
    }

    /**
     * @see java.sql.PreparedStatement#setDouble(int, double)
     */
00401     public void setDouble(int parameterIndex, double x)
        throws SQLException {
        checkClosed();
        
        if (!this.connection.getAllowNanAndInf() && 
                  (x == Double.POSITIVE_INFINITY || x == Double.NEGATIVE_INFINITY ||
                  Double.isNaN(x))) {
            throw new SQLException("'" + x + 
                        "' is not a valid numeric or approximate numeric value", 
                              SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
            
      }

        BindValue binding = getBinding(parameterIndex, false);
        setType(binding, MysqlDefs.FIELD_TYPE_DOUBLE);

        binding.value = null;
        binding.doubleBinding = x;
        binding.isNull = false;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#setFloat(int, float)
     */
00426     public void setFloat(int parameterIndex, float x) throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);
        setType(binding, MysqlDefs.FIELD_TYPE_FLOAT);

        binding.value = null;
        binding.floatBinding = x;
        binding.isNull = false;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#setInt(int, int)
     */
00441     public void setInt(int parameterIndex, int x) throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);
        setType(binding, MysqlDefs.FIELD_TYPE_LONG);

        binding.value = null;
        binding.intBinding = x;
        binding.isNull = false;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#setLong(int, long)
     */
00456     public void setLong(int parameterIndex, long x) throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);
        setType(binding, MysqlDefs.FIELD_TYPE_LONGLONG);

        binding.value = null;
        binding.longBinding = x;
        binding.isNull = false;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#getMetaData()
     */
00471     public java.sql.ResultSetMetaData getMetaData() throws SQLException {
        checkClosed();

        return new ResultSetMetaData(this.resultFields);
    }

    /**
     * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String)
     */
00480     public void setNull(int parameterIndex, int sqlType, String typeName)
        throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);

        //
        // Don't re-set types, but use something if this
        // parameter was never specified
        //
        if (binding.bufferType == 0) {
            setType(binding, MysqlDefs.FIELD_TYPE_NULL);
        }

        binding.value = null;
        binding.isNull = true;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#setNull(int, int)
     */
00502     public void setNull(int parameterIndex, int sqlType)
        throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);

        //
        // Don't re-set types, but use something if this
        // parameter was never specified
        //
        if (binding.bufferType == 0) {
            setType(binding, MysqlDefs.FIELD_TYPE_NULL);
        }

        binding.value = null;
        binding.isNull = true;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#getParameterMetaData()
     */
00524     public ParameterMetaData getParameterMetaData() throws SQLException {
        throw new NotImplemented();
    }

    /**
     * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref)
     */
00531     public void setRef(int i, Ref x) throws SQLException {
        throw new NotImplemented();
    }

    /**
     * @see java.sql.PreparedStatement#setShort(int, short)
     */
00538     public void setShort(int parameterIndex, short x) throws SQLException {
        checkClosed();

        BindValue binding = getBinding(parameterIndex, false);
        setType(binding, MysqlDefs.FIELD_TYPE_SHORT);

        binding.value = null;
        binding.shortBinding = x;
        binding.isNull = false;
        binding.isLongData = false;
    }

    /**
     * @see java.sql.PreparedStatement#setString(int, java.lang.String)
     */
00553     public void setString(int parameterIndex, String x)
        throws SQLException {
        checkClosed();

        if (x == null) {
            setNull(parameterIndex, java.sql.Types.CHAR);
        } else {
            BindValue binding = getBinding(parameterIndex, false);

            setType(binding, this.stringTypeCode);

            binding.value = x;
            binding.isNull = false;
            binding.isLongData = false;
        }
    }

    /**
     * Set a parameter to a java.sql.Time value.  The driver converts this to a
     * SQL TIME value when it sends it to the database, using the given
     * timezone.
     *
     * @param parameterIndex the first parameter is 1...));
     * @param x the parameter value
     * @param cal the timezone to use
     *
     * @throws SQLException if a database access error occurs
     */
00581     public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
        throws SQLException {
        setTimeInternal(parameterIndex, x, cal.getTimeZone(), true);
    }

    /**
     * Set a parameter to a java.sql.Time value.
     *
     * @param parameterIndex the first parameter is 1...));
     * @param x the parameter value
     *
     * @throws SQLException if a database access error occurs
     */
00594     public void setTime(int parameterIndex, java.sql.Time x)
        throws SQLException {
        setTimeInternal(parameterIndex, x, TimeZone.getDefault(), false);
    }

    /**
     * Set a parameter to a java.sql.Time value.  The driver converts this to a
     * SQL TIME value when it sends it to the database, using the given
     * timezone.
     *
     * @param parameterIndex the first parameter is 1...));
     * @param x the parameter value
     * @param tz the timezone to use
     *
     * @throws SQLException if a database access error occurs
     */
00610     public void setTimeInternal(int parameterIndex, java.sql.Time x, TimeZone tz, boolean rollForward)
        throws SQLException {
        if (x == null) {
            setNull(parameterIndex, java.sql.Types.TIME);
        } else {
            BindValue binding = getBinding(parameterIndex, false);
            setType(binding, MysqlDefs.FIELD_TYPE_TIME);

            binding.value = TimeUtil.changeTimezone(this.connection, x, tz,
                    this.connection.getServerTimezoneTZ(), rollForward);
            binding.isNull = false;
            binding.isLongData = false;
        }
    }

    /**
     * Set a parameter to a java.sql.Timestamp value.  The driver converts this
     * to a SQL TIMESTAMP value when it sends it to the database.
     *
     * @param parameterIndex the first parameter is 1, the second is 2, ...
     * @param x the parameter value
     *
     * @throws SQLException if a database-access error occurs.
     */
00634     public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
        throws SQLException {
        setTimestampInternal(parameterIndex, x, TimeZone.getDefault(), false);
    }

    /**
     * Set a parameter to a java.sql.Timestamp value.  The driver converts this
     * to a SQL TIMESTAMP value when it sends it to the database.
     *
     * @param parameterIndex the first parameter is 1, the second is 2, ...
     * @param x the parameter value
     * @param cal the timezone to use
     *
     * @throws SQLException if a database-access error occurs.
     */
00649     public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
        Calendar cal) throws SQLException {
        setTimestampInternal(parameterIndex, x, cal.getTimeZone(), true);
    }

    /**
     * @see java.sql.PreparedStatement#setURL(int, java.net.URL)
     */
00657     public void setURL(int parameterIndex, URL x) throws SQLException {
        checkClosed();

        setString(parameterIndex, x.toString());
    }

    /**
     * DOCUMENT ME!
     *
     * @param parameterIndex DOCUMENT ME!
     * @param x DOCUMENT ME!
     * @param length DOCUMENT ME!
     *
     * @throws SQLException DOCUMENT ME!
     * @throws NotImplemented DOCUMENT ME!
     *
     * @see java.sql.PreparedStatement#setUnicodeStream(int,
     *      java.io.InputStream, int)
     * @deprecated
     */
00677     public void setUnicodeStream(int parameterIndex, InputStream x, int length)
        throws SQLException {
        checkClosed();

        throw new NotImplemented();
    }

    /**
     * JDBC 2.0 Add a set of parameters to the batch.
     *
     * @exception SQLException if a database-access error occurs.
     *
     * @see Statement#addBatch
     */
00691     public synchronized void addBatch() throws SQLException {
        checkClosed();

        if (this.batchedArgs == null) {
            this.batchedArgs = new ArrayList();
        }

        this.batchedArgs.add(new BatchedBindValues(this.parameterBindings));
    }

    /**
     * @see java.sql.PreparedStatement#clearParameters()
     */
00704     public void clearParameters() throws SQLException {
        clearParametersInternal(true);
    }

    /**
     * @see java.sql.Statement#close()
     */
00711     public void close() throws SQLException {
        realClose(true);
    }

    /**
     * @see java.sql.Statement#executeBatch()
     */
00718     public synchronized int[] executeBatch() throws SQLException {
        if (this.connection.isReadOnly()) {
            throw new SQLException(Messages.getString(
                    "ServerPreparedStatement.2") //$NON-NLS-1$
                 +Messages.getString("ServerPreparedStatement.3"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
        }

        checkClosed();

        synchronized (this.connection.getMutex()) {
            clearWarnings();

            // Store this for later, we're going to 'swap' them out
            // as we execute each batched statement...
            BindValue[] oldBindValues = this.parameterBindings;

            try {
                int[] updateCounts = null;

                if (this.batchedArgs != null) {
                    int nbrCommands = this.batchedArgs.size();
                    updateCounts = new int[nbrCommands];

                    if (this.retrieveGeneratedKeys) {
                        this.batchedGeneratedKeys = new ArrayList(nbrCommands);
                    }

                    for (int i = 0; i < nbrCommands; i++) {
                        updateCounts[i] = -3;
                    }

                    SQLException sqlEx = null;

                    int commandIndex = 0;

                    BindValue[] previousBindValuesForBatch = null;
                    
                    for (commandIndex = 0; commandIndex < nbrCommands;
                            commandIndex++) {
                        Object arg = this.batchedArgs.get(commandIndex);

                        if (arg instanceof String) {
                            updateCounts[commandIndex] = executeUpdate((String) arg);
                        } else {
                            this.parameterBindings = ((BatchedBindValues) arg).batchedParameterValues;

                            try {
                              // We need to check types each time, as
                              // the user might have bound different
                              // types in each addBatch()

                              if (previousBindValuesForBatch != null) {
                                    for (int j = 0; j < this.parameterBindings.length; j++) {
                                          if (this.parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) {
                                                this.sendTypesToServer = true;
                                          
                                                break;
                                          }
                                    }
                              }

                              try {
                                    updateCounts[commandIndex] = executeUpdate(false);
                              } finally {
                                    previousBindValuesForBatch = this.parameterBindings;
                              }

                                if (this.retrieveGeneratedKeys) {
                                    java.sql.ResultSet rs = null;

                                    try {
                                        // we don't want to use our version,
                                        // because we've altered the behavior of ours to support batch updates
                                        // (catch-22)
                                        // Ideally, what we need here is super.super.getGeneratedKeys()
                                        // but that construct doesn't exist in Java, so that's why there's
                                        // this kludge.
                                        rs = getGeneratedKeysInternal();

                                        while (rs.next()) {
                                            this.batchedGeneratedKeys.add(new byte[][] {
                                                    rs.getBytes(1)
                                                });
                                        }
                                    } finally {
                                        if (rs != null) {
                                            rs.close();
                                        }
                                    }
                                }
                            } catch (SQLException ex) {
                                updateCounts[commandIndex] = EXECUTE_FAILED;

                                if (this.connection.getContinueBatchOnError()) {
                                    sqlEx = ex;
                                } else {
                                    int[] newUpdateCounts = new int[commandIndex];
                                    System.arraycopy(updateCounts, 0,
                                        newUpdateCounts, 0, commandIndex);

                                    throw new java.sql.BatchUpdateException(ex.getMessage(),
                                        ex.getSQLState(), ex.getErrorCode(),
                                        newUpdateCounts);
                                }
                            }
                        }
                    }

                    if (sqlEx != null) {
                        throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
                            sqlEx.getSQLState(), sqlEx.getErrorCode(),
                            updateCounts);
                    }
                }

                return (updateCounts != null) ? updateCounts : new int[0];
            } finally {
                this.parameterBindings = oldBindValues;
                this.sendTypesToServer = true;
                
                clearBatch();
            }
        }
    }

    /**
     * @see java.lang.Object#toString()
     */
00847     public String toString() {
        StringBuffer toStringBuf = new StringBuffer();

        toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); //$NON-NLS-1$
        toStringBuf.append(this.serverStatementId);
        toStringBuf.append("] - "); //$NON-NLS-1$

        PreparedStatement pStmtForSub = null;

        try {
            pStmtForSub = new PreparedStatement(this.connection,
                    this.originalSql, this.currentCatalog);

            int numParameters = pStmtForSub.parameterCount;
            int ourNumParameters = this.parameterCount;

            for (int i = 0; (i < numParameters) && (i < ourNumParameters);
                    i++) {
                if (this.parameterBindings[i] != null) {
                    if (this.parameterBindings[i].isNull) {
                        pStmtForSub.setNull(i + 1, Types.NULL);
                    } else {
                        BindValue bindValue = this.parameterBindings[i];
                        
                        //
                        // Handle primitives first
                        //
                        switch (bindValue.bufferType) {
             
                        case MysqlDefs.FIELD_TYPE_TINY:
                            pStmtForSub.setByte(i + 1,bindValue.byteBinding);
                            break;
                        case MysqlDefs.FIELD_TYPE_SHORT:
                              pStmtForSub.setShort(i + 1,bindValue.shortBinding);
                            break;
                        case MysqlDefs.FIELD_TYPE_LONG:
                              pStmtForSub.setInt(i + 1,bindValue.intBinding);
                              break;
                        case MysqlDefs.FIELD_TYPE_LONGLONG:
                              pStmtForSub.setLong(i + 1,bindValue.longBinding);
                              break;
                        case MysqlDefs.FIELD_TYPE_FLOAT:
                            pStmtForSub.setFloat(i + 1,bindValue.floatBinding);
                            break;
                        case MysqlDefs.FIELD_TYPE_DOUBLE:
                            pStmtForSub.setDouble(i + 1,bindValue.doubleBinding);
                            break;
                        default:
                              pStmtForSub.setObject(i + 1,
                                    this.parameterBindings[i].value);
                              break;
                        }
                    }
                }
            }

            toStringBuf.append(pStmtForSub.asSql());
        } catch (SQLException sqlEx) {
            toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); //$NON-NLS-1$
            toStringBuf.append(sqlEx);
        } finally {
            if (pStmtForSub != null) {
                try {
                    pStmtForSub.close();
                } catch (SQLException sqlEx) {
                    ; // ignore
                }
            }
        }

        return toStringBuf.toString();
    }

    protected void setTimestampInternal(int parameterIndex,
        java.sql.Timestamp x, TimeZone tz, boolean rollForward) throws SQLException {
        if (x == null) {
            setNull(parameterIndex, java.sql.Types.TIMESTAMP);
        } else {
            BindValue binding = getBinding(parameterIndex, false);
            setType(binding, MysqlDefs.FIELD_TYPE_DATETIME);

            binding.value = TimeUtil.changeTimezone(this.connection, x, tz,
                    this.connection.getServerTimezoneTZ(), rollForward);
            binding.isNull = false;
            binding.isLongData = false;
        }
    }

    /* (non-Javadoc)
     * @see com.mysql.jdbc.Statement#checkClosed()
     */
00938     protected void checkClosed() throws SQLException {
        if (this.invalid) {
            throw this.invalidationException;
        }

        super.checkClosed();
    }

    /**
     * @see com.mysql.jdbc.PreparedStatement#executeInternal(int,
     *      com.mysql.jdbc.Buffer, boolean, boolean)
     */
00950     protected com.mysql.jdbc.ResultSet executeInternal(int maxRowsToRetrieve,
        Buffer sendPacket, boolean createStreamingResultSet,
        boolean queryIsSelectOnly, boolean unpackFields)
        throws SQLException {
        this.numberOfExecutions++;

        // We defer to server-side execution
        try {
            return serverExecute(maxRowsToRetrieve, createStreamingResultSet);
        } catch (SQLException sqlEx) {
            // don't wrap SQLExceptions
            if (this.connection.getEnablePacketDebug()) {
                this.connection.getIO().dumpPacketRingBuffer();
            }

            if (this.connection.getDumpQueriesOnException()) {
                String extractedSql = toString();
                StringBuffer messageBuf = new StringBuffer(extractedSql.length() +
                        32);
                messageBuf.append(
                    "\n\nQuery being executed when exception was thrown:\n\n");
                messageBuf.append(extractedSql);

                sqlEx = Connection.appendMessageToException(sqlEx,
                        messageBuf.toString());
            }

            if (this.connection.getDumpQueriesOnException()) {
                String extractedSql = toString();
                StringBuffer messageBuf = new StringBuffer(extractedSql.length() +
                        32);
                messageBuf.append(
                    "\n\nQuery being executed when exception was thrown:\n\n");
                messageBuf.append(extractedSql);

                sqlEx = Connection.appendMessageToException(sqlEx,
                        messageBuf.toString());
            }

            throw sqlEx;
        } catch (Exception ex) {
            if (this.connection.getEnablePacketDebug()) {
                this.connection.getIO().dumpPacketRingBuffer();
            }

            SQLException sqlEx = new SQLException(ex.toString(),
                    SQLError.SQL_STATE_GENERAL_ERROR);

            if (this.connection.getDumpQueriesOnException()) {
                String extractedSql = toString();
                StringBuffer messageBuf = new StringBuffer(extractedSql.length() +
                        32);
                messageBuf.append(
                    "\n\nQuery being executed when exception was thrown:\n\n");
                messageBuf.append(extractedSql);

                sqlEx = Connection.appendMessageToException(sqlEx,
                        messageBuf.toString());
            }

            throw sqlEx;
        }
    }

    /**
     * @see com.mysql.jdbc.PreparedStatement#fillSendPacket()
     */
01017     protected Buffer fillSendPacket() throws SQLException {
        return null; // we don't use this type of packet
    }

    /**
     * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte,
     *      java.io.InputStream, boolean, int)
     */
01025     protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
        InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
        int[] batchedStreamLengths) throws SQLException {
        return null; // we don't use this type of packet
    }

    /**
     * Used by Connection when auto-reconnecting to retrieve 'lost' prepared
     * statements.
     *
     * @throws SQLException if an error occurs.
     */
01037     protected void rePrepare() throws SQLException {
        this.invalidationException = null;

        try {
            serverPrepare(this.originalSql);
        } catch (SQLException sqlEx) {
            // don't wrap SQLExceptions
            this.invalidationException = sqlEx;
        } catch (Exception ex) {
            this.invalidationException = new SQLException(ex.toString(),
                    SQLError.SQL_STATE_GENERAL_ERROR);
        }

        if (this.invalidationException != null) {
            this.invalid = true;

            this.parameterBindings = null;

            this.parameterFields = null;
            this.resultFields = null;

            if (this.results != null) {
                try {
                    this.results.close();
                } catch (Exception ex) {
                    ;
                }
            }

            if (this.connection != null) {
                if (this.maxRowsChanged) {
                    this.connection.unsetMaxRows(this);
                }

                if (!this.connection.getDontTrackOpenResources()) {
                  this.connection.unregisterStatement(this);
                }
            }
        }
    }

    /**
     * Closes this connection and frees all resources.
     *
     * @param calledExplicitly was this called from close()?
     *
     * @throws SQLException if an error occurs
     */
01085     protected void realClose(boolean calledExplicitly)
        throws SQLException {
        if (this.isClosed) {
            return;
        }

        SQLException exceptionDuringClose = null;

        try {
            synchronized (this.connection.getMutex()) {
                MysqlIO mysql = this.connection.getIO();

                Buffer packet = mysql.getSharedSendPacket();

                packet.writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT);
                packet.writeLong(this.serverStatementId);

                mysql.sendCommand(MysqlDefs.COM_CLOSE_STATEMENT, null, packet,
                    true, null);
            }
        } catch (SQLException sqlEx) {
            exceptionDuringClose = sqlEx;
        }

        clearParametersInternal(false);
        this.parameterBindings = null;

        this.parameterFields = null;
        this.resultFields = null;

        super.realClose(calledExplicitly);

        if (exceptionDuringClose != null) {
            throw exceptionDuringClose;
        }
    }

    /**
     * @see com.mysql.jdbc.PreparedStatement#getBytes(int)
     */
01125     synchronized byte[] getBytes(int parameterIndex) throws SQLException {
        BindValue bindValue = getBinding(parameterIndex, false);

        if (bindValue.isNull) {
            return null;
        } else if (bindValue.isLongData) {
            throw new NotImplemented();
        } else {
            if (this.outByteBuffer == null) {
                this.outByteBuffer = Buffer.allocateNew(this.connection.getNetBufferLength(),
                        false);
            }

            this.outByteBuffer.clear();

            int originalPosition = this.outByteBuffer.getPosition();

            storeBinding(this.outByteBuffer, bindValue, this.connection.getIO());

            int newPosition = this.outByteBuffer.getPosition();

            int length = newPosition - originalPosition;

            byte[] valueAsBytes = new byte[length];

            System.arraycopy(this.outByteBuffer.getByteBuffer(),
                originalPosition, valueAsBytes, 0, length);

            return valueAsBytes;
        }
    }

    /**
     * @see com.mysql.jdbc.PreparedStatement#isNull(int)
     */
01160     boolean isNull(int paramIndex) {
        throw new IllegalArgumentException(Messages.getString(
                "ServerPreparedStatement.7")); //$NON-NLS-1$
    }

    private BindValue getBinding(int parameterIndex, boolean forLongData)
        throws SQLException {
        if (this.parameterBindings.length == 0) {
            throw new SQLException(Messages.getString(
                    "ServerPreparedStatement.8"), //$NON-NLS-1$
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
        }

        parameterIndex--;

        if ((parameterIndex < 0) ||
                (parameterIndex >= this.parameterBindings.length)) {
            throw new SQLException(Messages.getString(
                    "ServerPreparedStatement.9") //$NON-NLS-1$
                 +(parameterIndex + 1) +
                Messages.getString("ServerPreparedStatement.10") //$NON-NLS-1$
                 +this.parameterBindings.length,
                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
        }

        if (this.parameterBindings[parameterIndex] == null) {
            this.parameterBindings[parameterIndex] = new BindValue();
        } else {
            if (this.parameterBindings[parameterIndex].isLongData &&
                    !forLongData) {
                this.detectedLongParameterSwitch = true;
            }  
        }

        this.parameterBindings[parameterIndex].isSet = true;
        
        return this.parameterBindings[parameterIndex];
    }

    private void setType(BindValue oldValue, int bufferType) {
        if (oldValue.bufferType != bufferType) {
            this.sendTypesToServer = true;
        }

        oldValue.bufferType = bufferType;
    }

    private void clearParametersInternal(boolean clearServerParameters)
        throws SQLException {
        boolean hadLongData = false;

        if (this.parameterBindings != null) {
            for (int i = 0; i < this.parameterCount; i++) {
                if ((this.parameterBindings[i] != null) &&
                        this.parameterBindings[i].isLongData) {
                    hadLongData = true;
                }

                this.parameterBindings[i].reset();
            }
        }

        if (clearServerParameters && hadLongData) {
            serverResetStatement();

            this.detectedLongParameterSwitch = false;
        }
    }

    /**
     * Tells the server to execute this prepared statement with the current
     * parameter bindings.
     * <pre>
     *
     *   -   Server gets the command 'COM_EXECUTE' to execute the
     *       previously         prepared query. If there is any param markers;
     * then client will send the data in the following format:
     *
     * [COM_EXECUTE:1]
     * [STMT_ID:4]
     * [NULL_BITS:(param_count+7)/8)]
     * [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
     * [[length]data]
     * [[length]data] .. [[length]data].
     *
     * (Note: Except for string/binary types; all other types will not be
     * supplied with length field)
     *
     * </pre>
     *
     * @param maxRowsToRetrieve DOCUMENT ME!
     * @param createStreamingResultSet DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     *
     * @throws SQLException
     */
01257     private com.mysql.jdbc.ResultSet serverExecute(int maxRowsToRetrieve,
        boolean createStreamingResultSet) throws SQLException {
        synchronized (this.connection.getMutex()) {
            if (this.detectedLongParameterSwitch) {
                throw new SQLException(Messages.getString(
                        "ServerPreparedStatement.11") //$NON-NLS-1$
                     +Messages.getString("ServerPreparedStatement.12"), //$NON-NLS-1$
                    SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
            }

            // Check bindings
            for (int i = 0; i < this.parameterCount; i++) {
                if (!this.parameterBindings[i].isSet) {
                    throw new SQLException(Messages.getString(
                            "ServerPreparedStatement.13") + (i + 1) //$NON-NLS-1$
                         +Messages.getString("ServerPreparedStatement.14"),
                        SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
                }
            }

            MysqlIO mysql = this.connection.getIO();

            Buffer packet = mysql.getSharedSendPacket();

            packet.clear();
            packet.writeByte((byte) MysqlDefs.COM_EXECUTE);
            packet.writeLong(this.serverStatementId);

            if (this.connection.versionMeetsMinimum(4, 1, 2)) {
                packet.writeByte((byte) 0); // placeholder for flags
                packet.writeLong(1); // placeholder for parameter iterations
            }

            /* Reserve place for null-marker bytes */
            int nullCount = (this.parameterCount + 7) / 8;

            //if (mysql.versionMeetsMinimum(4, 1, 2)) {
            //    nullCount = (this.parameterCount + 9) / 8;
            //}
            int nullBitsPosition = packet.getPosition();

            for (int i = 0; i < nullCount; i++) {
                packet.writeByte((byte) 0);
            }

            byte[] nullBitsBuffer = new byte[nullCount];

            /* In case if buffers (type) altered, indicate to server */
            packet.writeByte(this.sendTypesToServer ? (byte) 1 : (byte) 0);

            if (this.sendTypesToServer) {
                /*
                      Store types of parameters in first in first package
                      that is sent to the server.
                */
                for (int i = 0; i < this.parameterCount; i++) {
                    packet.writeInt(this.parameterBindings[i].bufferType);
                }
            }

            //
            // store the parameter values
            //
            for (int i = 0; i < this.parameterCount; i++) {
                if (!this.parameterBindings[i].isLongData) {
                    if (!this.parameterBindings[i].isNull) {
                        storeBinding(packet, this.parameterBindings[i], mysql);
                    } else {
                        nullBitsBuffer[i / 8] |= (1 << (i & 7));
                    }
                }
            }

            //
            // Go back and write the NULL flags
            // to the beginning of the packet
            //
            int endPosition = packet.getPosition();
            packet.setPosition(nullBitsPosition);
            packet.writeBytesNoNull(nullBitsBuffer);
            packet.setPosition(endPosition);

            long begin = 0;

            if (this.connection.getProfileSql() ||
                    this.connection.getLogSlowQueries() ||
                    this.connection.getGatherPerformanceMetrics()) {
                begin = System.currentTimeMillis();
            }

            Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE,
                    null, packet, false, null);

            if (this.connection.getLogSlowQueries() ||
                    this.connection.getGatherPerformanceMetrics()) {
                long elapsedTime = System.currentTimeMillis() - begin;

                if (this.connection.getLogSlowQueries() &&
                        (elapsedTime > this.connection.getSlowQueryThresholdMillis())) {
                    StringBuffer mesgBuf = new StringBuffer(48 +
                            this.originalSql.length());
                    mesgBuf.append(Messages.getString(
                            "ServerPreparedStatement.15")); //$NON-NLS-1$
                    mesgBuf.append(this.connection.getSlowQueryThresholdMillis());
                    mesgBuf.append(Messages.getString(
                            "ServerPreparedStatement.16")); //$NON-NLS-1$
                    mesgBuf.append(this.originalSql);

                    this.connection.getLog().logWarn(mesgBuf.toString());

                    if (this.connection.getExplainSlowQueries()) {
                        String queryAsString = asSql();

                        mysql.explainSlowQuery(queryAsString.getBytes(),
                            queryAsString);
                    }
                }

                if (this.connection.getGatherPerformanceMetrics()) {
                    this.connection.registerQueryExecutionTime(elapsedTime);
                }
            }

            this.connection.incrementNumberOfPreparedExecutes();

            if (this.connection.getProfileSql()) {
                this.eventSink = ProfileEventSink.getInstance(this.connection);

                this.eventSink.consumeEvent(new ProfilerEvent(
                        ProfilerEvent.TYPE_EXECUTE, "", this.currentCatalog, //$NON-NLS-1$
                        this.connection.getId(), this.statementId, -1,
                        System.currentTimeMillis(),
                        (int) (System.currentTimeMillis() - begin), null,
                        new Throwable(), null));
            }

            com.mysql.jdbc.ResultSet rs = mysql.readAllResults(this,
                    maxRowsToRetrieve, this.resultSetType,
                    this.resultSetConcurrency, createStreamingResultSet,
                    this.currentCatalog, resultPacket, true, this.fieldCount,
                    true);

            if (!createStreamingResultSet) {
                serverResetStatement(); // clear any long data...
            }

            this.sendTypesToServer = false;
            this.results = rs;

            return rs;
        }
    }

    /**
     * Sends stream-type data parameters to the server.
     * <pre>
     * Long data handling:
     *
     * - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
     * - The packet recieved will have the format as:
     *   [COM_LONG_DATA:     1][STMT_ID:4][parameter_number:2][type:2][data]
     * - Checks if the type is specified by client, and if yes reads the type,
     *   and  stores the data in that format.
     * - It's up to the client to check for read data ended. The server doesn't
     *   care;  and also server doesn't notify to the client that it got the
     *   data  or not; if there is any error; then during execute; the error
     *   will  be returned
     * </pre>
     *
     * @param parameterIndex DOCUMENT ME!
     * @param longData DOCUMENT ME!
     *
     * @throws SQLException if an error occurs.
     */
01431     private void serverLongData(int parameterIndex, BindValue longData)
        throws SQLException {
        synchronized (this.connection.getMutex()) {
            MysqlIO mysql = this.connection.getIO();

            Buffer packet = mysql.getSharedSendPacket();

            packet.clear();
            packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
            packet.writeLong(this.serverStatementId);
            packet.writeInt((parameterIndex - 1));

            Object value = longData.value;

            if (value instanceof byte[]) {
                packet.writeBytesNoNull((byte[]) longData.value);
            } else if (value instanceof InputStream) {
                storeStream(packet, (InputStream) value);
            } else if (value instanceof java.sql.Blob) {
                storeStream(packet, ((java.sql.Blob) value).getBinaryStream());
            } else if (value instanceof Reader) {
                storeReader(packet, (Reader) value);
            } else {
                throw new SQLException(Messages.getString(
                        "ServerPreparedStatement.18") //$NON-NLS-1$
                     +value.getClass().getName() + "'", //$NON-NLS-1$
                    SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
            }

            mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true, null);
        }
    }

    private void serverPrepare(String sql) throws SQLException {
      synchronized (this.connection.getMutex()) {
              MysqlIO mysql = this.connection.getIO();
      
              try {
                  long begin = 0;
      
                  if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
                      this.isLoadDataQuery = true;
                  } else {
                      this.isLoadDataQuery = false;
                  }
      
                  if (this.connection.getProfileSql()) {
                      begin = System.currentTimeMillis();
                  }
      
                  String characterEncoding = null;
                  String connectionEncoding = this.connection.getEncoding();
      
                  if (!this.isLoadDataQuery && this.connection.getUseUnicode() &&
                          (connectionEncoding != null)) {
                      characterEncoding = connectionEncoding;
                  }
      
                  Buffer prepareResultPacket = mysql.sendCommand(MysqlDefs.COM_PREPARE,
                          sql, null, false, characterEncoding);
      
                  if (this.connection.versionMeetsMinimum(4, 1, 1)) {
                      // 4.1.1 and newer use the first byte
                      // as an 'ok' or 'error' flag, so move
                      // the buffer pointer past it to
                      // start reading the statement id.
                      prepareResultPacket.setPosition(1);
                  } else {
                      // 4.1.0 doesn't use the first byte as an 
                      // 'ok' or 'error' flag
                      prepareResultPacket.setPosition(0);
                  }
      
                  this.serverStatementId = prepareResultPacket.readLong();
                  this.fieldCount = prepareResultPacket.readInt();
                  this.parameterCount = prepareResultPacket.readInt();
                  this.parameterBindings = new BindValue[this.parameterCount];
                  
                  for (int i = 0; i < this.parameterCount; i++) {
                        this.parameterBindings[i] = new BindValue();
                  }
      
                  this.connection.incrementNumberOfPrepares();
      
                  if (this.connection.getProfileSql()) {
                      this.eventSink = ProfileEventSink.getInstance(this.connection);
      
                      this.eventSink.consumeEvent(new ProfilerEvent(
                              ProfilerEvent.TYPE_PREPARE, "", this.currentCatalog, //$NON-NLS-1$
                              this.connection.getId(), this.statementId, -1,
                              System.currentTimeMillis(),
                              (int) (System.currentTimeMillis() - begin), null,
                              new Throwable(), sql));
                  }
      
                  if (this.parameterCount > 0) {
                      if (this.connection.versionMeetsMinimum(4, 1, 2) &&
                              !mysql.isVersion(5, 0, 0)) {
                          this.parameterFields = new Field[this.parameterCount];
      
                          Buffer metaDataPacket = mysql.readPacket();
      
                          int i = 0;
      
                          while (!metaDataPacket.isLastDataPacket() &&
                                  (i < this.parameterCount)) {
                              this.parameterFields[i++] = mysql.unpackField(metaDataPacket,
                                      false);
                              metaDataPacket = mysql.readPacket();
                          }
                      }
                  }
      
                  if (this.fieldCount > 0) {
                      this.resultFields = new Field[this.fieldCount];
      
                      Buffer fieldPacket = mysql.readPacket();
      
                      int i = 0;
      
                      // Read in the result set column information
                      while (!fieldPacket.isLastDataPacket() &&
                              (i < this.fieldCount)) {
                          this.resultFields[i++] = mysql.unpackField(fieldPacket,
                                  false);
                          fieldPacket = mysql.readPacket();
                      }
                  }
              } catch (SQLException sqlEx) {
                  if (this.connection.getDumpQueriesOnException()) {
                      StringBuffer messageBuf = new StringBuffer(this.originalSql.length() +
                              32);
                      messageBuf.append(
                          "\n\nQuery being prepared when exception was thrown:\n\n");
                      messageBuf.append(this.originalSql);
      
                      sqlEx = Connection.appendMessageToException(sqlEx,
                              messageBuf.toString());
                  }
      
                  throw sqlEx;
              } finally {
                  // Leave the I/O channel in a known state...there might be packets out there
                  // that we're not interested in
                  this.connection.getIO().clearInputStream();
              }
      }
    }

    private void serverResetStatement() throws SQLException {
        synchronized (this.connection.getMutex()) {
            MysqlIO mysql = this.connection.getIO();

            Buffer packet = mysql.getSharedSendPacket();

            packet.clear();
            packet.writeByte((byte) MysqlDefs.COM_RESET_STMT);
            packet.writeLong(this.serverStatementId);

            try {
                mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null, packet,
                    !this.connection.versionMeetsMinimum(4, 1, 2), null);
            } catch (SQLException sqlEx) {
                throw sqlEx;
            } catch (Exception ex) {
                throw new SQLException(ex.toString(),
                    SQLError.SQL_STATE_GENERAL_ERROR);
            } finally {
                mysql.clearInputStream();
            }
        }
    }

    /**
     * Method storeBinding.
     *
     * @param packet
     * @param bindValue
     * @param mysql DOCUMENT ME!
     *
     * @throws SQLException DOCUMENT ME!
     */
01613     private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql)
        throws SQLException {
        try {
            Object value = bindValue.value;

            //
            // Handle primitives first
            //
            switch (bindValue.bufferType) {
 
            case MysqlDefs.FIELD_TYPE_TINY:
                packet.writeByte(bindValue.byteBinding);
                return;
            case MysqlDefs.FIELD_TYPE_SHORT:
                packet.ensureCapacity(2);
                packet.writeInt(bindValue.shortBinding);
                return;
            case MysqlDefs.FIELD_TYPE_LONG:
                packet.ensureCapacity(4);
                packet.writeLong(bindValue.intBinding);
                return;
            case MysqlDefs.FIELD_TYPE_LONGLONG:
                packet.ensureCapacity(8);
                packet.writeLongLong(bindValue.longBinding);
                return;
            case MysqlDefs.FIELD_TYPE_FLOAT:
                packet.ensureCapacity(4);
                packet.writeFloat(bindValue.floatBinding);
                return;
            case MysqlDefs.FIELD_TYPE_DOUBLE:
                packet.ensureCapacity(8);
                packet.writeDouble(bindValue.doubleBinding);
                return;
            case MysqlDefs.FIELD_TYPE_TIME: 
                storeTime(packet, (Time) value);
                  return;
            case MysqlDefs.FIELD_TYPE_DATE:
            case MysqlDefs.FIELD_TYPE_DATETIME:
            case MysqlDefs.FIELD_TYPE_TIMESTAMP:
                storeDateTime(packet, (java.util.Date) value, mysql);
                  return;
            case MysqlDefs.FIELD_TYPE_VAR_STRING:
            case MysqlDefs.FIELD_TYPE_STRING:
            case MysqlDefs.FIELD_TYPE_VARCHAR:
                  if (!this.isLoadDataQuery) {
                    packet.writeLenString((String) value, this.charEncoding,
                        this.connection.getServerCharacterEncoding(),
                        this.charConverter, this.connection.parserKnowsUnicode());
                } else {
                    packet.writeLenBytes(((String) value).getBytes());
                }
                  
                  return;
            }
            
            if (value instanceof byte[]) {
                packet.writeLenBytes((byte[]) value);
            }
        } catch (UnsupportedEncodingException uEE) {
            throw new SQLException(Messages.getString(
                    "ServerPreparedStatement.22") //$NON-NLS-1$
                 +this.connection.getEncoding() + "'", //$NON-NLS-1$
                SQLError.SQL_STATE_GENERAL_ERROR);
        }
    }

    private void storeDataTime412AndOlder(Buffer intoBuf, java.util.Date dt)
        throws SQLException {
        // This is synchronized on the connection by callers, so it is
        // safe to lazily-instantiate this...
        if (this.dateTimeBindingCal == null) {
            this.dateTimeBindingCal = Calendar.getInstance();
        }

        this.dateTimeBindingCal.setTime(dt);

        intoBuf.ensureCapacity(8);
        intoBuf.writeByte((byte) 7); // length

        int year = this.dateTimeBindingCal.get(Calendar.YEAR);
        int month = this.dateTimeBindingCal.get(Calendar.MONTH) + 1;
        int date = this.dateTimeBindingCal.get(Calendar.DATE);

        intoBuf.writeInt(year);
        intoBuf.writeByte((byte) month);
        intoBuf.writeByte((byte) date);

        if (dt instanceof java.sql.Date) {
            intoBuf.writeByte((byte) 0);
            intoBuf.writeByte((byte) 0);
            intoBuf.writeByte((byte) 0);
        } else {
            intoBuf.writeByte((byte) this.dateTimeBindingCal.get(
                    Calendar.HOUR_OF_DAY));
            intoBuf.writeByte((byte) this.dateTimeBindingCal.get(Calendar.MINUTE));
            intoBuf.writeByte((byte) this.dateTimeBindingCal.get(Calendar.SECOND));
        }
    }

    private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql)
        throws SQLException {
        if (this.connection.versionMeetsMinimum(4, 1, 3)) {
            storeDateTime413AndNewer(intoBuf, dt);
        } else {
            storeDataTime412AndOlder(intoBuf, dt);
        }
    }

    private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt)
        throws SQLException {
        // This is synchronized on the connection by callers, so it is
        // safe to lazily-instantiate this...
        if (this.dateTimeBindingCal == null) {
            this.dateTimeBindingCal = Calendar.getInstance();
        }

        this.dateTimeBindingCal.setTime(dt);

        byte length = (byte) 7;

        intoBuf.ensureCapacity(length);

        if (dt instanceof java.sql.Timestamp) {
            length = (byte) 11;
        }

        intoBuf.writeByte(length); // length

        int year = this.dateTimeBindingCal.get(Calendar.YEAR);
        int month = this.dateTimeBindingCal.get(Calendar.MONTH) + 1;
        int date = this.dateTimeBindingCal.get(Calendar.DATE);

        intoBuf.writeInt(year);
        intoBuf.writeByte((byte) month);
        intoBuf.writeByte((byte) date);

        if (dt instanceof java.sql.Date) {
            intoBuf.writeByte((byte) 0);
            intoBuf.writeByte((byte) 0);
            intoBuf.writeByte((byte) 0);
        } else {
            intoBuf.writeByte((byte) this.dateTimeBindingCal.get(
                    Calendar.HOUR_OF_DAY));
            intoBuf.writeByte((byte) this.dateTimeBindingCal.get(Calendar.MINUTE));
            intoBuf.writeByte((byte) this.dateTimeBindingCal.get(Calendar.SECOND));
        }

        if (length == 11) {
            intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos());
        }
    }

    //
    // TO DO: Investigate using NIO to do this faster
    //
    private void storeReader(Buffer packet, Reader inStream)
        throws SQLException {
        char[] buf = new char[4096];
        StringBuffer valueAsString = new StringBuffer();

        int numRead = 0;

        try {
            while ((numRead = inStream.read(buf)) != -1) {
                valueAsString.append(buf, 0, numRead);
            }
        } catch (IOException ioEx) {
            throw new SQLException(Messages.getString(
                    "ServerPreparedStatement.24") //$NON-NLS-1$
                 +ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
        } finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException ioEx) {
                    ; // ignore
                }
            }
        }

        byte[] valueAsBytes = StringUtils.getBytes(valueAsString.toString(),
                this.connection.getEncoding(),
                this.connection.getServerCharacterEncoding(),
                this.connection.parserKnowsUnicode());

        packet.writeBytesNoNull(valueAsBytes);
    }

    private void storeStream(Buffer packet, InputStream inStream)
        throws SQLException {
        byte[] buf = new byte[4096];

        int numRead = 0;

        try {
            while ((numRead = inStream.read(buf)) != -1) {
                packet.writeBytesNoNull(buf, 0, numRead);
            }
        } catch (IOException ioEx) {
            throw new SQLException(Messages.getString(
                    "ServerPreparedStatement.25") //$NON-NLS-1$
                 +ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
        } finally {
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (IOException ioEx) {
                    ; // ignore
                }
            }
        }
    }

    private static void storeTime(Buffer intoBuf, Time tm)
        throws SQLException {
        intoBuf.ensureCapacity(9);
        intoBuf.writeByte((byte) 8); // length
        intoBuf.writeByte((byte) 0); // neg flag
        intoBuf.writeLong(0); // tm->day, not used

        Calendar cal = Calendar.getInstance();
        cal.setTime(tm);
        intoBuf.writeByte((byte) cal.get(Calendar.HOUR_OF_DAY));
        intoBuf.writeByte((byte) cal.get(Calendar.MINUTE));
        intoBuf.writeByte((byte) cal.get(Calendar.SECOND));

        //intoBuf.writeLongInt(0); // tm-second_part
    }


    static class BatchedBindValues {
        BindValue[] batchedParameterValues;

        BatchedBindValues(BindValue[] paramVals) {
            int numParams = paramVals.length;

            this.batchedParameterValues = new BindValue[numParams];

            for (int i = 0; i < numParams; i++) {
                  this.batchedParameterValues[i] = new BindValue(paramVals[i]);
            }
        }
    }

    static class BindValue {
            
      
            
        Object value; /* The value to store */
        boolean isLongData; /* long data indicator */
        boolean isNull; /* NULL indicator */
        int bufferType; /* buffer type */
        long bindLength; /* Default length of data */
        byte byteBinding;
        short shortBinding;
        int intBinding;
        long longBinding;
        double doubleBinding;
            float floatBinding;
        boolean isSet = false; /* has this parameter been set? */

        BindValue() {
        }
      
        void reset() {
            this.isSet = false;
            this.value = null;
            this.isLongData = false;
            
            this.byteBinding = 0;
            this.shortBinding = 0;
            this.intBinding = 0;
            this.longBinding = 0L;
            this.floatBinding = 0;
            this.doubleBinding = 0D;
        }
        
        BindValue(BindValue copyMe) {
            this.value = copyMe.value;
            this.isSet = copyMe.isSet;
            this.isLongData = copyMe.isLongData;
            this.isNull = copyMe.isNull;
            this.bufferType = copyMe.bufferType;
            this.bindLength = copyMe.bindLength;
            this.byteBinding = copyMe.byteBinding;
            this.shortBinding = copyMe.shortBinding;
            this.intBinding = copyMe.intBinding;
            this.longBinding = copyMe.longBinding;
            this.floatBinding = copyMe.floatBinding;
            this.doubleBinding = copyMe.doubleBinding;
        }
    }
}

Generated by  Doxygen 1.6.0   Back to index