Save This Page
Home » MySQL-JDBC-5.1.11 » com.mysql.jdbc » [javadoc | source]
    1   /*
    2    Copyright  2002-2007 MySQL AB, 2008 Sun Microsystems
    3   
    4    This program is free software; you can redistribute it and/or modify
    5    it under the terms of version 2 of the GNU General Public License as 
    6    published by the Free Software Foundation.
    7   
    8    There are special exceptions to the terms and conditions of the GPL 
    9    as it is applied to this software. View the full text of the 
   10    exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
   11    software distribution.
   12   
   13    This program is distributed in the hope that it will be useful,
   14    but WITHOUT ANY WARRANTY; without even the implied warranty of
   15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16    GNU General Public License for more details.
   17   
   18    You should have received a copy of the GNU General Public License
   19    along with this program; if not, write to the Free Software
   20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21   
   22   
   23   
   24    */
   25   package com.mysql.jdbc;
   26   
   27   import java.io.IOException;
   28   import java.io.InputStream;
   29   import java.io.Reader;
   30   import java.io.UnsupportedEncodingException;
   31   import java.lang.reflect.Constructor;
   32   import java.lang.reflect.InvocationTargetException;
   33   import java.math.BigDecimal;
   34   import java.net.URL;
   35   import java.sql.Array;
   36   import java.sql.Blob;
   37   import java.sql.Clob;
   38   import java.sql.Date;
   39   import java.sql.ParameterMetaData;
   40   import java.sql.Ref;
   41   import java.sql.ResultSet;
   42   import java.sql.SQLException;
   43   import java.sql.Time;
   44   import java.sql.Timestamp;
   45   import java.sql.Types;
   46   import java.util.ArrayList;
   47   import java.util.Calendar;
   48   import java.util.GregorianCalendar;
   49   import java.util.TimeZone;
   50   
   51   import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
   52   import com.mysql.jdbc.exceptions.MySQLTimeoutException;
   53   import com.mysql.jdbc.profiler.ProfilerEvent;
   54   import com.mysql.jdbc.profiler.ProfilerEventHandlerFactory;
   55   
   56   /**
   57    * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements.
   58    * 
   59    * @author Mark Matthews
   60    * @version $Id: ServerPreparedStatement.java,v 1.1.2.2 2005/05/17 14:58:56
   61    *          mmatthews Exp $
   62    */
   63   public class ServerPreparedStatement extends PreparedStatement {
   64   	private static final Constructor JDBC_4_SPS_CTOR;
   65   	
   66   	static {
   67   		if (Util.isJdbc4()) {
   68   			try {
   69   				JDBC_4_SPS_CTOR = Class.forName("com.mysql.jdbc.JDBC4ServerPreparedStatement")
   70   				.getConstructor(
   71   				new Class[] { ConnectionImpl.class, String.class, String.class,
   72   						Integer.TYPE, Integer.TYPE});
   73   			} catch (SecurityException e) {
   74   				throw new RuntimeException(e);
   75   			} catch (NoSuchMethodException e) {
   76   				throw new RuntimeException(e);
   77   			} catch (ClassNotFoundException e) {
   78   				throw new RuntimeException(e);
   79   			} 
   80   		} else {
   81   			JDBC_4_SPS_CTOR = null;
   82   		}
   83   	}
   84   	
   85   	protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192;
   86   
   87   	static class BatchedBindValues {
   88   		BindValue[] batchedParameterValues;
   89   
   90   		BatchedBindValues(BindValue[] paramVals) {
   91   			int numParams = paramVals.length;
   92   
   93   			this.batchedParameterValues = new BindValue[numParams];
   94   
   95   			for (int i = 0; i < numParams; i++) {
   96   				this.batchedParameterValues[i] = new BindValue(paramVals[i]);
   97   			}
   98   		}
   99   	}
  100   
  101   	public static class BindValue {
  102   
  103   		long boundBeforeExecutionNum = 0;
  104   		
  105   		public long bindLength; /* Default length of data */
  106   
  107   		int bufferType; /* buffer type */
  108   
  109   		byte byteBinding;
  110   
  111   		double doubleBinding;
  112   
  113   		float floatBinding;
  114   
  115   		int intBinding;
  116   
  117   		public boolean isLongData; /* long data indicator */
  118   
  119   		public boolean isNull; /* NULL indicator */
  120   
  121   		boolean isSet = false; /* has this parameter been set? */
  122   
  123   		long longBinding;
  124   
  125   		short shortBinding;
  126   
  127   		public Object value; /* The value to store */
  128   
  129   		BindValue() {
  130   		}
  131   
  132   		BindValue(BindValue copyMe) {
  133   			this.value = copyMe.value;
  134   			this.isSet = copyMe.isSet;
  135   			this.isLongData = copyMe.isLongData;
  136   			this.isNull = copyMe.isNull;
  137   			this.bufferType = copyMe.bufferType;
  138   			this.bindLength = copyMe.bindLength;
  139   			this.byteBinding = copyMe.byteBinding;
  140   			this.shortBinding = copyMe.shortBinding;
  141   			this.intBinding = copyMe.intBinding;
  142   			this.longBinding = copyMe.longBinding;
  143   			this.floatBinding = copyMe.floatBinding;
  144   			this.doubleBinding = copyMe.doubleBinding;
  145   		}
  146   
  147   		void reset() {
  148   			this.isSet = false;
  149   			this.value = null;
  150   			this.isLongData = false;
  151   
  152   			this.byteBinding = 0;
  153   			this.shortBinding = 0;
  154   			this.intBinding = 0;
  155   			this.longBinding = 0L;
  156   			this.floatBinding = 0;
  157   			this.doubleBinding = 0D;
  158   		}
  159   
  160   		public String toString() {
  161   			return toString(false);
  162   		}
  163   
  164   		public String toString(boolean quoteIfNeeded) {
  165   			if (this.isLongData) {
  166   				return "' STREAM DATA '";
  167   			}
  168   
  169   			switch (this.bufferType) {
  170   			case MysqlDefs.FIELD_TYPE_TINY:
  171   				return String.valueOf(byteBinding);
  172   			case MysqlDefs.FIELD_TYPE_SHORT:
  173   				return String.valueOf(shortBinding);
  174   			case MysqlDefs.FIELD_TYPE_LONG:
  175   				return String.valueOf(intBinding);
  176   			case MysqlDefs.FIELD_TYPE_LONGLONG:
  177   				return String.valueOf(longBinding);
  178   			case MysqlDefs.FIELD_TYPE_FLOAT:
  179   				return String.valueOf(floatBinding);
  180   			case MysqlDefs.FIELD_TYPE_DOUBLE:
  181   				return String.valueOf(doubleBinding);
  182   			case MysqlDefs.FIELD_TYPE_TIME:
  183   			case MysqlDefs.FIELD_TYPE_DATE:
  184   			case MysqlDefs.FIELD_TYPE_DATETIME:
  185   			case MysqlDefs.FIELD_TYPE_TIMESTAMP:
  186   			case MysqlDefs.FIELD_TYPE_VAR_STRING:
  187   			case MysqlDefs.FIELD_TYPE_STRING:
  188   			case MysqlDefs.FIELD_TYPE_VARCHAR:
  189   				if (quoteIfNeeded) {
  190   					return "'" + String.valueOf(value) + "'";
  191   				} else {
  192   					return String.valueOf(value);
  193   				}
  194   			default:
  195   				if (value instanceof byte[]) {
  196   					return "byte data";
  197   
  198   				} else {
  199   					if (quoteIfNeeded) {
  200   						return "'" + String.valueOf(value) + "'";
  201   					} else {
  202   						return String.valueOf(value);
  203   					}
  204   				}
  205   			}
  206   		}
  207   		
  208   		long getBoundLength() {
  209   			if (isNull) {
  210   				return 0;
  211   			}
  212   			
  213   			if (isLongData) {
  214   				return bindLength;
  215   			}
  216   
  217   			switch (bufferType) {
  218   
  219   			case MysqlDefs.FIELD_TYPE_TINY:
  220   				return 1;
  221   			case MysqlDefs.FIELD_TYPE_SHORT:
  222   				return 2;
  223   			case MysqlDefs.FIELD_TYPE_LONG:
  224   				return 4;
  225   			case MysqlDefs.FIELD_TYPE_LONGLONG:
  226   				return 8;
  227   			case MysqlDefs.FIELD_TYPE_FLOAT:
  228   				return 4;
  229   			case MysqlDefs.FIELD_TYPE_DOUBLE:
  230   				return 8;
  231   			case MysqlDefs.FIELD_TYPE_TIME:
  232   				return 9;
  233   			case MysqlDefs.FIELD_TYPE_DATE:
  234   				return 7;
  235   			case MysqlDefs.FIELD_TYPE_DATETIME:
  236   			case MysqlDefs.FIELD_TYPE_TIMESTAMP:
  237   				return 11;
  238   			case MysqlDefs.FIELD_TYPE_VAR_STRING:
  239   			case MysqlDefs.FIELD_TYPE_STRING:
  240   			case MysqlDefs.FIELD_TYPE_VARCHAR:
  241   			case MysqlDefs.FIELD_TYPE_DECIMAL:
  242   			case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
  243   				if (value instanceof byte[]) {
  244   					return ((byte[]) value).length;
  245   				} else {
  246   					return ((String) value).length();
  247   				}
  248   			default: 
  249   				return 0;
  250   			}
  251   		}
  252   	}
  253   
  254   	/* 1 (length) + 2 (year) + 1 (month) + 1 (day) */
  255   	private static final byte MAX_DATE_REP_LENGTH = (byte) 5;
  256   
  257   	/*
  258   	 * 1 (length) + 2 (year) + 1 (month) + 1 (day) + 1 (hour) + 1 (minute) + 1
  259   	 * (second) + 4 (microseconds)
  260   	 */
  261   	private static final byte MAX_DATETIME_REP_LENGTH = 12;
  262   
  263   	/*
  264   	 * 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) + 1 (minute) + 1
  265   	 * (seconds) + 4 (microseconds)
  266   	 */
  267   	private static final byte MAX_TIME_REP_LENGTH = 13;
  268   	
  269   	private boolean hasOnDuplicateKeyUpdate = false;
  270   
  271   	private void storeTime(Buffer intoBuf, Time tm) throws SQLException {
  272   		
  273   		intoBuf.ensureCapacity(9);
  274   		intoBuf.writeByte((byte) 8); // length
  275   		intoBuf.writeByte((byte) 0); // neg flag
  276   		intoBuf.writeLong(0); // tm->day, not used
  277   
  278   		Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
  279   		
  280   		synchronized (sessionCalendar) {
  281   			java.util.Date oldTime = sessionCalendar.getTime();
  282   			try {
  283   				sessionCalendar.setTime(tm);
  284   				intoBuf.writeByte((byte) sessionCalendar.get(Calendar.HOUR_OF_DAY));
  285   				intoBuf.writeByte((byte) sessionCalendar.get(Calendar.MINUTE));
  286   				intoBuf.writeByte((byte) sessionCalendar.get(Calendar.SECOND));
  287   
  288   				// intoBuf.writeLongInt(0); // tm-second_part
  289   			} finally {
  290   				sessionCalendar.setTime(oldTime);
  291   			}
  292   		}
  293   	}
  294   
  295   	/**
  296   	 * Flag indicating whether or not the long parameters have been 'switched'
  297   	 * back to normal parameters. We can not execute() if clearParameters()
  298   	 * hasn't been called in this case.
  299   	 */
  300   	private boolean detectedLongParameterSwitch = false;
  301   
  302   	/**
  303   	 * The number of fields in the result set (if any) for this
  304   	 * PreparedStatement.
  305   	 */
  306   	private int fieldCount;
  307   
  308   	/** Has this prepared statement been marked invalid? */
  309   	private boolean invalid = false;
  310   
  311   	/** If this statement has been marked invalid, what was the reason? */
  312   	private SQLException invalidationException;
  313   
  314   	/** Does this query modify data? */
  315   	private boolean isSelectQuery;
  316   
  317   	private Buffer outByteBuffer;
  318   
  319   	/** Bind values for individual fields */
  320   	private BindValue[] parameterBindings;
  321   
  322   	/** Field-level metadata for parameters */
  323   	private Field[] parameterFields;
  324   
  325   	/** Field-level metadata for result sets. */
  326   	private Field[] resultFields;
  327   
  328   	/** Do we need to send/resend types to the server? */
  329   	private boolean sendTypesToServer = false;
  330   
  331   	/** The ID that the server uses to identify this PreparedStatement */
  332   	private long serverStatementId;
  333   
  334   	/** The type used for string bindings, changes from version-to-version */
  335   	private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
  336   
  337   	private boolean serverNeedsResetBeforeEachExecution;
  338   
  339   	/**
  340   	 * Creates a prepared statement instance -- We need to provide factory-style
  341   	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  342   	 * otherwise the class verifier complains when it tries to load JDBC4-only
  343   	 * interface classes that are present in JDBC4 method signatures.
  344   	 */
  345   
  346   	protected static ServerPreparedStatement getInstance(ConnectionImpl conn,
  347   			String sql, String catalog, int resultSetType,
  348   			int resultSetConcurrency) throws SQLException {
  349   		if (!Util.isJdbc4()) {
  350   			return new ServerPreparedStatement(conn, sql, catalog,
  351   					resultSetType, resultSetConcurrency);
  352   		}
  353   
  354   		try {
  355   			return (ServerPreparedStatement) JDBC_4_SPS_CTOR.newInstance(new Object[] { conn,
  356   					sql, catalog, Constants.integerValueOf(resultSetType),
  357   					Constants.integerValueOf(resultSetConcurrency) });
  358   		} catch (IllegalArgumentException e) {
  359   			throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
  360   		} catch (InstantiationException e) {
  361   			throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
  362   		} catch (IllegalAccessException e) {
  363   			throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
  364   		} catch (InvocationTargetException e) {
  365   			Throwable target = e.getTargetException(); 
  366   			
  367   			if (target instanceof SQLException) {
  368   				throw (SQLException)target;
  369   			}
  370   			
  371   			throw new SQLException(target.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
  372   		}
  373   	}
  374   
  375   	/**
  376   	 * Creates a new ServerPreparedStatement object.
  377   	 * 
  378   	 * @param conn
  379   	 *            the connection creating us.
  380   	 * @param sql
  381   	 *            the SQL containing the statement to prepare.
  382   	 * @param catalog
  383   	 *            the catalog in use when we were created.
  384   	 * 
  385   	 * @throws SQLException
  386   	 *             If an error occurs
  387   	 */
  388   	protected ServerPreparedStatement(ConnectionImpl conn, String sql, String catalog,
  389   			int resultSetType, int resultSetConcurrency)
  390   			throws SQLException {
  391   		super(conn, catalog);
  392   
  393   		checkNullOrEmptyQuery(sql);
  394   
  395   		this.hasOnDuplicateKeyUpdate = containsOnDuplicateKeyInString(sql);
  396   		
  397   		int startOfStatement = findStartOfStatement(sql);
  398   		
  399   		this.firstCharOfStmt = StringUtils.firstAlphaCharUc(sql, startOfStatement);
  400   		
  401   		this.isSelectQuery = 'S' == this.firstCharOfStmt;
  402   		
  403   		if (this.connection.versionMeetsMinimum(5, 0, 0)) {
  404   			this.serverNeedsResetBeforeEachExecution = 
  405   				!this.connection.versionMeetsMinimum(5, 0, 3);
  406   		} else {
  407   			this.serverNeedsResetBeforeEachExecution =
  408   				!this.connection.versionMeetsMinimum(4, 1, 10);
  409   		}
  410   		
  411   		this.useAutoSlowLog = this.connection.getAutoSlowLog();
  412   		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
  413   		this.hasLimitClause = (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1); //$NON-NLS-1$
  414   		
  415   		String statementComment = this.connection.getStatementComment();
  416   
  417   		this.originalSql = (statementComment == null) ? sql : "/* "
  418   				+ statementComment + " */ " + sql;
  419   
  420   		if (this.connection.versionMeetsMinimum(4, 1, 2)) {
  421   			this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING;
  422   		} else {
  423   			this.stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
  424   		}
  425   
  426   		try {
  427   			serverPrepare(sql);
  428   		} catch (SQLException sqlEx) {
  429   			realClose(false, true);
  430   			// don't wrap SQLExceptions
  431   			throw sqlEx;
  432   		} catch (Exception ex) {
  433   			realClose(false, true);
  434   
  435   			SQLException sqlEx = SQLError.createSQLException(ex.toString(),
  436   					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
  437   			sqlEx.initCause(ex);
  438   			
  439   			throw sqlEx;
  440   		}
  441   		
  442   		setResultSetType(resultSetType);
  443   		setResultSetConcurrency(resultSetConcurrency);
  444   		
  445   		this.parameterTypes = new int[this.parameterCount];
  446   	}
  447   
  448   	/**
  449   	 * JDBC 2.0 Add a set of parameters to the batch.
  450   	 * 
  451   	 * @exception SQLException
  452   	 *                if a database-access error occurs.
  453   	 * 
  454   	 * @see StatementImpl#addBatch
  455   	 */
  456   	public synchronized void addBatch() throws SQLException {
  457   		checkClosed();
  458   
  459   		if (this.batchedArgs == null) {
  460   			this.batchedArgs = new ArrayList();
  461   		}
  462   
  463   		this.batchedArgs.add(new BatchedBindValues(this.parameterBindings));
  464   	}
  465   
  466   	protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
  467   
  468   		if (this.isClosed) {
  469   			return "statement has been closed, no further internal information available";
  470   		}
  471   		
  472   		PreparedStatement pStmtForSub = null;
  473   
  474   		try {
  475   			pStmtForSub = PreparedStatement.getInstance(this.connection,
  476   					this.originalSql, this.currentCatalog);
  477   
  478   			int numParameters = pStmtForSub.parameterCount;
  479   			int ourNumParameters = this.parameterCount;
  480   
  481   			for (int i = 0; (i < numParameters) && (i < ourNumParameters); i++) {
  482   				if (this.parameterBindings[i] != null) {
  483   					if (this.parameterBindings[i].isNull) {
  484   						pStmtForSub.setNull(i + 1, Types.NULL);
  485   					} else {
  486   						BindValue bindValue = this.parameterBindings[i];
  487   
  488   						//
  489   						// Handle primitives first
  490   						//
  491   						switch (bindValue.bufferType) {
  492   
  493   						case MysqlDefs.FIELD_TYPE_TINY:
  494   							pStmtForSub.setByte(i + 1, bindValue.byteBinding);
  495   							break;
  496   						case MysqlDefs.FIELD_TYPE_SHORT:
  497   							pStmtForSub.setShort(i + 1, bindValue.shortBinding);
  498   							break;
  499   						case MysqlDefs.FIELD_TYPE_LONG:
  500   							pStmtForSub.setInt(i + 1, bindValue.intBinding);
  501   							break;
  502   						case MysqlDefs.FIELD_TYPE_LONGLONG:
  503   							pStmtForSub.setLong(i + 1, bindValue.longBinding);
  504   							break;
  505   						case MysqlDefs.FIELD_TYPE_FLOAT:
  506   							pStmtForSub.setFloat(i + 1, bindValue.floatBinding);
  507   							break;
  508   						case MysqlDefs.FIELD_TYPE_DOUBLE:
  509   							pStmtForSub.setDouble(i + 1,
  510   									bindValue.doubleBinding);
  511   							break;
  512   						default:
  513   							pStmtForSub.setObject(i + 1,
  514   									this.parameterBindings[i].value);
  515   							break;
  516   						}
  517   					}
  518   				}
  519   			}
  520   
  521   			return pStmtForSub.asSql(quoteStreamsAndUnknowns);
  522   		} finally {
  523   			if (pStmtForSub != null) {
  524   				try {
  525   					pStmtForSub.close();
  526   				} catch (SQLException sqlEx) {
  527   					; // ignore
  528   				}
  529   			}
  530   		}
  531   	}
  532   
  533   	/*
  534   	 * (non-Javadoc)
  535   	 * 
  536   	 * @see com.mysql.jdbc.Statement#checkClosed()
  537   	 */
  538   	protected void checkClosed() throws SQLException {
  539   		if (this.invalid) {
  540   			throw this.invalidationException;
  541   		}
  542   
  543   		super.checkClosed();
  544   	}
  545   
  546   	/**
  547   	 * @see java.sql.PreparedStatement#clearParameters()
  548   	 */
  549   	public void clearParameters() throws SQLException {
  550   		checkClosed();
  551   		clearParametersInternal(true);
  552   	}
  553   
  554   	private void clearParametersInternal(boolean clearServerParameters)
  555   			throws SQLException {
  556   		boolean hadLongData = false;
  557   
  558   		if (this.parameterBindings != null) {
  559   			for (int i = 0; i < this.parameterCount; i++) {
  560   				if ((this.parameterBindings[i] != null)
  561   						&& this.parameterBindings[i].isLongData) {
  562   					hadLongData = true;
  563   				}
  564   
  565   				this.parameterBindings[i].reset();
  566   			}
  567   		}
  568   
  569   		if (clearServerParameters && hadLongData) {
  570   			serverResetStatement();
  571   
  572   			this.detectedLongParameterSwitch = false;
  573   		}
  574   	}
  575   
  576   	protected boolean isCached = false;
  577   
  578   	private boolean useAutoSlowLog;
  579   
  580   	private Calendar serverTzCalendar;
  581   
  582   	private Calendar defaultTzCalendar;
  583   
  584   	protected void setClosed(boolean flag) {
  585   		this.isClosed = flag;
  586   	}
  587   	
  588   	/**
  589   	 * @see java.sql.Statement#close()
  590   	 */
  591   	public synchronized void close() throws SQLException {
  592   		if (this.isCached && !this.isClosed) {
  593   			clearParameters();
  594   			
  595   			this.isClosed = true;
  596   			
  597   			this.connection.recachePreparedStatement(this);
  598   			return;
  599   		}
  600   		
  601   		realClose(true, true);
  602   	}
  603   
  604   	private void dumpCloseForTestcase() {
  605   		StringBuffer buf = new StringBuffer();
  606   		this.connection.generateConnectionCommentBlock(buf);
  607   		buf.append("DEALLOCATE PREPARE debug_stmt_");
  608   		buf.append(this.statementId);
  609   		buf.append(";\n");
  610   
  611   		this.connection.dumpTestcaseQuery(buf.toString());
  612   	}
  613   
  614   	private void dumpExecuteForTestcase() throws SQLException {
  615   		StringBuffer buf = new StringBuffer();
  616   
  617   		for (int i = 0; i < this.parameterCount; i++) {
  618   			this.connection.generateConnectionCommentBlock(buf);
  619   
  620   			buf.append("SET @debug_stmt_param");
  621   			buf.append(this.statementId);
  622   			buf.append("_");
  623   			buf.append(i);
  624   			buf.append("=");
  625   
  626   			if (this.parameterBindings[i].isNull) {
  627   				buf.append("NULL");
  628   			} else {
  629   				buf.append(this.parameterBindings[i].toString(true));
  630   			}
  631   
  632   			buf.append(";\n");
  633   		}
  634   
  635   		this.connection.generateConnectionCommentBlock(buf);
  636   
  637   		buf.append("EXECUTE debug_stmt_");
  638   		buf.append(this.statementId);
  639   
  640   		if (this.parameterCount > 0) {
  641   			buf.append(" USING ");
  642   			for (int i = 0; i < this.parameterCount; i++) {
  643   				if (i > 0) {
  644   					buf.append(", ");
  645   				}
  646   
  647   				buf.append("@debug_stmt_param");
  648   				buf.append(this.statementId);
  649   				buf.append("_");
  650   				buf.append(i);
  651   
  652   			}
  653   		}
  654   
  655   		buf.append(";\n");
  656   
  657   		this.connection.dumpTestcaseQuery(buf.toString());
  658   	}
  659   
  660   	private void dumpPrepareForTestcase() throws SQLException {
  661   
  662   		StringBuffer buf = new StringBuffer(this.originalSql.length() + 64);
  663   
  664   		this.connection.generateConnectionCommentBlock(buf);
  665   
  666   		buf.append("PREPARE debug_stmt_");
  667   		buf.append(this.statementId);
  668   		buf.append(" FROM \"");
  669   		buf.append(this.originalSql);
  670   		buf.append("\";\n");
  671   
  672   		this.connection.dumpTestcaseQuery(buf.toString());
  673   	}
  674   
  675   	protected int[] executeBatchSerially(int batchTimeout) throws SQLException {
  676   		ConnectionImpl locallyScopedConn = this.connection;
  677   		
  678   		if (locallyScopedConn == null) {
  679   			checkClosed();
  680   		}
  681   		
  682   		if (locallyScopedConn.isReadOnly()) {
  683   			throw SQLError.createSQLException(Messages
  684   					.getString("ServerPreparedStatement.2") //$NON-NLS-1$
  685   					+ Messages.getString("ServerPreparedStatement.3"), //$NON-NLS-1$
  686   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  687   		}
  688   
  689   		checkClosed();
  690   
  691   		synchronized (locallyScopedConn.getMutex()) {
  692   			clearWarnings();
  693   
  694   			// Store this for later, we're going to 'swap' them out
  695   			// as we execute each batched statement...
  696   			BindValue[] oldBindValues = this.parameterBindings;
  697   
  698   			try {
  699   				int[] updateCounts = null;
  700   
  701   				if (this.batchedArgs != null) {
  702   					int nbrCommands = this.batchedArgs.size();
  703   					updateCounts = new int[nbrCommands];
  704   
  705   					if (this.retrieveGeneratedKeys) {
  706   						this.batchedGeneratedKeys = new ArrayList(nbrCommands);
  707   					}
  708   
  709   					for (int i = 0; i < nbrCommands; i++) {
  710   						updateCounts[i] = -3;
  711   					}
  712   
  713   					SQLException sqlEx = null;
  714   
  715   					int commandIndex = 0;
  716   
  717   					BindValue[] previousBindValuesForBatch = null;
  718   
  719   					CancelTask timeoutTask = null;
  720   					
  721   					try {
  722   						if (locallyScopedConn.getEnableQueryTimeouts() &&
  723   								batchTimeout != 0
  724   								&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
  725   							timeoutTask = new CancelTask(this);
  726   							locallyScopedConn.getCancelTimer().schedule(timeoutTask,
  727   									batchTimeout);
  728   						}
  729   						
  730   						for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
  731   							Object arg = this.batchedArgs.get(commandIndex);
  732   	
  733   							if (arg instanceof String) {
  734   								updateCounts[commandIndex] = executeUpdate((String) arg);
  735   							} else {
  736   								this.parameterBindings = ((BatchedBindValues) arg).batchedParameterValues;
  737   	
  738   								try {
  739   									// We need to check types each time, as
  740   									// the user might have bound different
  741   									// types in each addBatch()
  742   	
  743   									if (previousBindValuesForBatch != null) {
  744   										for (int j = 0; j < this.parameterBindings.length; j++) {
  745   											if (this.parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) {
  746   												this.sendTypesToServer = true;
  747   	
  748   												break;
  749   											}
  750   										}
  751   									}
  752   	
  753   									try {
  754   										updateCounts[commandIndex] = executeUpdate(false, true);
  755   									} finally {
  756   										previousBindValuesForBatch = this.parameterBindings;
  757   									}
  758   	
  759   									if (this.retrieveGeneratedKeys) {
  760   										java.sql.ResultSet rs = null;
  761   	
  762   										try {
  763   											// we don't want to use our version,
  764   											// because we've altered the behavior of
  765   											// ours to support batch updates
  766   											// (catch-22)
  767   											// Ideally, what we need here is
  768   											// super.super.getGeneratedKeys()
  769   											// but that construct doesn't exist in
  770   											// Java, so that's why there's
  771   											// this kludge.
  772   											rs = getGeneratedKeysInternal();
  773   	
  774   											while (rs.next()) {
  775   												this.batchedGeneratedKeys
  776   														.add(new ByteArrayRow(new byte[][] { rs
  777   																.getBytes(1) }, getExceptionInterceptor()));
  778   											}
  779   										} finally {
  780   											if (rs != null) {
  781   												rs.close();
  782   											}
  783   										}
  784   									}
  785   								} catch (SQLException ex) {
  786   									updateCounts[commandIndex] = EXECUTE_FAILED;
  787   									
  788   									if (this.continueBatchOnError && 
  789   											!(ex instanceof MySQLTimeoutException) && 
  790   											!(ex instanceof MySQLStatementCancelledException)
  791   											&& !hasDeadlockOrTimeoutRolledBackTx(ex)) {
  792   										sqlEx = ex;
  793   									} else {
  794   										int[] newUpdateCounts = new int[commandIndex];
  795   										System.arraycopy(updateCounts, 0,
  796   												newUpdateCounts, 0, commandIndex);
  797   			
  798   										throw new java.sql.BatchUpdateException(ex
  799   												.getMessage(), ex.getSQLState(), ex
  800   												.getErrorCode(), newUpdateCounts);
  801   									}
  802   								}
  803   							}
  804   						}
  805   					} finally {
  806   						if (timeoutTask != null) {
  807   							timeoutTask.cancel();
  808   						}
  809   						
  810   						resetCancelledState();
  811   					}
  812   
  813   					if (sqlEx != null) {
  814   						throw new java.sql.BatchUpdateException(sqlEx
  815   								.getMessage(), sqlEx.getSQLState(), sqlEx
  816   								.getErrorCode(), updateCounts);
  817   					}
  818   				}
  819   
  820   				return (updateCounts != null) ? updateCounts : new int[0];
  821   			} finally {
  822   				this.parameterBindings = oldBindValues;
  823   				this.sendTypesToServer = true;
  824   
  825   				clearBatch();
  826   			}
  827   		}
  828   	}
  829   
  830   	/**
  831   	 * @see com.mysql.jdbc.PreparedStatement#executeInternal(int,
  832   	 *      com.mysql.jdbc.Buffer, boolean, boolean)
  833   	 */
  834   	protected com.mysql.jdbc.ResultSetInternalMethods executeInternal(int maxRowsToRetrieve,
  835   			Buffer sendPacket, boolean createStreamingResultSet,
  836   			boolean queryIsSelectOnly, Field[] metadataFromCache,
  837   			boolean isBatch)
  838   			throws SQLException {
  839   		this.numberOfExecutions++;
  840   
  841   		// We defer to server-side execution
  842   		try {
  843   			return serverExecute(maxRowsToRetrieve, createStreamingResultSet, 
  844   					metadataFromCache);
  845   		} catch (SQLException sqlEx) {
  846   			// don't wrap SQLExceptions
  847   			if (this.connection.getEnablePacketDebug()) {
  848   				this.connection.getIO().dumpPacketRingBuffer();
  849   			}
  850   
  851   			if (this.connection.getDumpQueriesOnException()) {
  852   				String extractedSql = toString();
  853   				StringBuffer messageBuf = new StringBuffer(extractedSql
  854   						.length() + 32);
  855   				messageBuf
  856   						.append("\n\nQuery being executed when exception was thrown:\n\n");
  857   				messageBuf.append(extractedSql);
  858   
  859   				sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf
  860   						.toString(), getExceptionInterceptor());
  861   			}
  862   
  863   			throw sqlEx;
  864   		} catch (Exception ex) {
  865   			if (this.connection.getEnablePacketDebug()) {
  866   				this.connection.getIO().dumpPacketRingBuffer();
  867   			}
  868   
  869   			SQLException sqlEx = SQLError.createSQLException(ex.toString(),
  870   					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
  871   
  872   			if (this.connection.getDumpQueriesOnException()) {
  873   				String extractedSql = toString();
  874   				StringBuffer messageBuf = new StringBuffer(extractedSql
  875   						.length() + 32);
  876   				messageBuf
  877   						.append("\n\nQuery being executed when exception was thrown:\n\n");
  878   				messageBuf.append(extractedSql);
  879   
  880   				sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf
  881   						.toString(), getExceptionInterceptor());
  882   			}
  883   
  884   			sqlEx.initCause(ex);
  885   			
  886   			throw sqlEx;
  887   		}
  888   	}
  889   
  890   	/**
  891   	 * @see com.mysql.jdbc.PreparedStatement#fillSendPacket()
  892   	 */
  893   	protected Buffer fillSendPacket() throws SQLException {
  894   		return null; // we don't use this type of packet
  895   	}
  896   
  897   	/**
  898   	 * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte,
  899   	 *      java.io.InputStream, boolean, int)
  900   	 */
  901   	protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
  902   			InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
  903   			int[] batchedStreamLengths) throws SQLException {
  904   		return null; // we don't use this type of packet
  905   	}
  906   
  907   	/**
  908   	 * Returns the structure representing the value that (can be)/(is)
  909   	 * bound at the given parameter index.
  910   	 * 
  911   	 * @param parameterIndex 1-based
  912   	 * @param forLongData is this for a stream?
  913   	 * @return
  914   	 * @throws SQLException
  915   	 */
  916   	protected BindValue getBinding(int parameterIndex, boolean forLongData)
  917   			throws SQLException {
  918   		checkClosed();
  919   		
  920   		if (this.parameterBindings.length == 0) {
  921   			throw SQLError.createSQLException(Messages
  922   					.getString("ServerPreparedStatement.8"), //$NON-NLS-1$
  923   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  924   		}
  925   
  926   		parameterIndex--;
  927   
  928   		if ((parameterIndex < 0)
  929   				|| (parameterIndex >= this.parameterBindings.length)) {
  930   			throw SQLError.createSQLException(Messages
  931   					.getString("ServerPreparedStatement.9") //$NON-NLS-1$
  932   					+ (parameterIndex + 1)
  933   					+ Messages.getString("ServerPreparedStatement.10") //$NON-NLS-1$
  934   					+ this.parameterBindings.length,
  935   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  936   		}
  937   
  938   		if (this.parameterBindings[parameterIndex] == null) {
  939   			this.parameterBindings[parameterIndex] = new BindValue();
  940   		} else {
  941   			if (this.parameterBindings[parameterIndex].isLongData
  942   					&& !forLongData) {
  943   				this.detectedLongParameterSwitch = true;
  944   			}
  945   		}
  946   
  947   		this.parameterBindings[parameterIndex].isSet = true;
  948   		this.parameterBindings[parameterIndex].boundBeforeExecutionNum = this.numberOfExecutions;
  949   
  950   		return this.parameterBindings[parameterIndex];
  951   	}
  952   
  953   	/**
  954   	 * @see com.mysql.jdbc.PreparedStatement#getBytes(int)
  955   	 */
  956   	byte[] getBytes(int parameterIndex) throws SQLException {
  957   		BindValue bindValue = getBinding(parameterIndex, false);
  958   
  959   		if (bindValue.isNull) {
  960   			return null;
  961   		} else if (bindValue.isLongData) {
  962   			throw SQLError.notImplemented();
  963   		} else {
  964   			if (this.outByteBuffer == null) {
  965   				this.outByteBuffer = new Buffer(this.connection
  966   						.getNetBufferLength());
  967   			}
  968   
  969   			this.outByteBuffer.clear();
  970   
  971   			int originalPosition = this.outByteBuffer.getPosition();
  972   
  973   			storeBinding(this.outByteBuffer, bindValue, this.connection.getIO());
  974   
  975   			int newPosition = this.outByteBuffer.getPosition();
  976   
  977   			int length = newPosition - originalPosition;
  978   
  979   			byte[] valueAsBytes = new byte[length];
  980   
  981   			System.arraycopy(this.outByteBuffer.getByteBuffer(),
  982   					originalPosition, valueAsBytes, 0, length);
  983   
  984   			return valueAsBytes;
  985   		}
  986   	}
  987   
  988   	/**
  989   	 * @see java.sql.PreparedStatement#getMetaData()
  990   	 */
  991   	public java.sql.ResultSetMetaData getMetaData() throws SQLException {
  992   		checkClosed();
  993   
  994   		if (this.resultFields == null) {
  995   			return null;
  996   		}
  997   
  998   		return new ResultSetMetaData(this.resultFields, 
  999   				this.connection.getUseOldAliasMetadataBehavior(), getExceptionInterceptor());
 1000   	}
 1001   
 1002   	/**
 1003   	 * @see java.sql.PreparedStatement#getParameterMetaData()
 1004   	 */
 1005   	public ParameterMetaData getParameterMetaData() throws SQLException {
 1006   		checkClosed();
 1007   		
 1008   		if (this.parameterMetaData == null) {
 1009   			this.parameterMetaData = new MysqlParameterMetadata(
 1010   					this.parameterFields, this.parameterCount, getExceptionInterceptor());
 1011   		}
 1012   		
 1013   		return this.parameterMetaData;
 1014   	}
 1015   
 1016   	/**
 1017   	 * @see com.mysql.jdbc.PreparedStatement#isNull(int)
 1018   	 */
 1019   	boolean isNull(int paramIndex) {
 1020   		throw new IllegalArgumentException(Messages
 1021   				.getString("ServerPreparedStatement.7")); //$NON-NLS-1$
 1022   	}
 1023   
 1024   	/**
 1025   	 * Closes this connection and frees all resources.
 1026   	 * 
 1027   	 * @param calledExplicitly
 1028   	 *            was this called from close()?
 1029   	 * 
 1030   	 * @throws SQLException
 1031   	 *             if an error occurs
 1032   	 */
 1033   	protected void realClose(boolean calledExplicitly, 
 1034   			boolean closeOpenResults) throws SQLException {
 1035   		if (this.isClosed) {
 1036   			return;
 1037   		}
 1038   
 1039   		if (this.connection != null) {
 1040   			if (this.connection.getAutoGenerateTestcaseScript()) {
 1041   				dumpCloseForTestcase();
 1042   			}
 1043   
 1044   			//
 1045   			// Don't communicate with the server if we're being
 1046   			// called from the finalizer...
 1047   			// 
 1048   			// This will leak server resources, but if we don't do this,
 1049   			// we'll deadlock (potentially, because there's no guarantee
 1050   			// when, what order, and what concurrency finalizers will be
 1051   			// called with). Well-behaved programs won't rely on finalizers
 1052   			// to clean up their statements.
 1053   			//
 1054   
 1055   			SQLException exceptionDuringClose = null;
 1056   
 1057   			if (calledExplicitly && !this.connection.isClosed()) {
 1058   				synchronized (this.connection.getMutex()) {
 1059   					try {
 1060   
 1061   						MysqlIO mysql = this.connection.getIO();
 1062   
 1063   						Buffer packet = mysql.getSharedSendPacket();
 1064   
 1065   						packet.writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT);
 1066   						packet.writeLong(this.serverStatementId);
 1067   
 1068   						mysql.sendCommand(MysqlDefs.COM_CLOSE_STATEMENT, null,
 1069   								packet, true, null, 0);
 1070   					} catch (SQLException sqlEx) {
 1071   						exceptionDuringClose = sqlEx;
 1072   					}
 1073   				}
 1074   			}
 1075   
 1076   			super.realClose(calledExplicitly, closeOpenResults);
 1077   
 1078   			clearParametersInternal(false);
 1079   			this.parameterBindings = null;
 1080   
 1081   			this.parameterFields = null;
 1082   			this.resultFields = null;
 1083   
 1084   			if (exceptionDuringClose != null) {
 1085   				throw exceptionDuringClose;
 1086   			}
 1087   		}
 1088   	}
 1089   
 1090   	/**
 1091   	 * Used by Connection when auto-reconnecting to retrieve 'lost' prepared
 1092   	 * statements.
 1093   	 * 
 1094   	 * @throws SQLException
 1095   	 *             if an error occurs.
 1096   	 */
 1097   	protected void rePrepare() throws SQLException {
 1098   		this.invalidationException = null;
 1099   
 1100   		try {
 1101   			serverPrepare(this.originalSql);
 1102   		} catch (SQLException sqlEx) {
 1103   			// don't wrap SQLExceptions
 1104   			this.invalidationException = sqlEx;
 1105   		} catch (Exception ex) {
 1106   			this.invalidationException = SQLError.createSQLException(ex.toString(),
 1107   					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 1108   			this.invalidationException.initCause(ex);
 1109   		}
 1110   
 1111   		if (this.invalidationException != null) {
 1112   			this.invalid = true;
 1113   
 1114   			this.parameterBindings = null;
 1115   
 1116   			this.parameterFields = null;
 1117   			this.resultFields = null;
 1118   
 1119   			if (this.results != null) {
 1120   				try {
 1121   					this.results.close();
 1122   				} catch (Exception ex) {
 1123   					;
 1124   				}
 1125   			}
 1126   
 1127   			if (this.connection != null) {
 1128   				if (this.maxRowsChanged) {
 1129   					this.connection.unsetMaxRows(this);
 1130   				}
 1131   
 1132   				if (!this.connection.getDontTrackOpenResources()) {
 1133   					this.connection.unregisterStatement(this);
 1134   				}
 1135   			}
 1136   		}
 1137   	}
 1138   
 1139   	/**
 1140   	 * Tells the server to execute this prepared statement with the current
 1141   	 * parameter bindings.
 1142   	 * 
 1143   	 * <pre>
 1144   	 * 
 1145   	 * 
 1146   	 *    -   Server gets the command 'COM_EXECUTE' to execute the
 1147   	 *        previously         prepared query. If there is any param markers;
 1148   	 *  then client will send the data in the following format:
 1149   	 * 
 1150   	 *  [COM_EXECUTE:1]
 1151   	 *  [STMT_ID:4]
 1152   	 *  [NULL_BITS:(param_count+7)/8)]
 1153   	 *  [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
 1154   	 *  [[length]data]
 1155   	 *  [[length]data] .. [[length]data].
 1156   	 * 
 1157   	 *  (Note: Except for string/binary types; all other types will not be
 1158   	 *  supplied with length field)
 1159   	 * 
 1160   	 *  
 1161   	 * </pre>
 1162   	 * 
 1163   	 * @param maxRowsToRetrieve
 1164   	 *            DOCUMENT ME!
 1165   	 * @param createStreamingResultSet
 1166   	 *            DOCUMENT ME!
 1167   	 * 
 1168   	 * @return DOCUMENT ME!
 1169   	 * 
 1170   	 * @throws SQLException
 1171   	 */
 1172   	private com.mysql.jdbc.ResultSetInternalMethods serverExecute(int maxRowsToRetrieve,
 1173   			boolean createStreamingResultSet, 
 1174   			Field[] metadataFromCache) throws SQLException {
 1175   		synchronized (this.connection.getMutex()) {
 1176   			MysqlIO mysql = this.connection.getIO();
 1177   
 1178   			if (mysql.shouldIntercept()) {
 1179   				ResultSetInternalMethods interceptedResults =
 1180   	    			mysql.invokeStatementInterceptorsPre(this.originalSql, this, true);
 1181   	
 1182   	    		if (interceptedResults != null) {
 1183   	    			return interceptedResults;
 1184   	    		}
 1185   			}
 1186       		
 1187   			if (this.detectedLongParameterSwitch) {
 1188   				// Check when values were bound
 1189   				boolean firstFound = false;
 1190   				long boundTimeToCheck = 0;
 1191   				
 1192   				for (int i = 0; i < this.parameterCount - 1; i++) {
 1193   					if (this.parameterBindings[i].isLongData) {
 1194   						if (firstFound && boundTimeToCheck != 
 1195   							this.parameterBindings[i].boundBeforeExecutionNum) { 					
 1196   							throw SQLError.createSQLException(Messages
 1197   									.getString("ServerPreparedStatement.11") //$NON-NLS-1$
 1198   									+ Messages.getString("ServerPreparedStatement.12"), //$NON-NLS-1$
 1199   									SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
 1200   						} else {
 1201   							firstFound = true;
 1202   							boundTimeToCheck = this.parameterBindings[i].boundBeforeExecutionNum;
 1203   						}
 1204   					}
 1205   				}
 1206   				
 1207   				// Okay, we've got all "newly"-bound streams, so reset 
 1208   				// server-side state to clear out previous bindings
 1209   				
 1210   				serverResetStatement();
 1211   			}
 1212   
 1213   
 1214   			// Check bindings
 1215   			for (int i = 0; i < this.parameterCount; i++) {
 1216   				if (!this.parameterBindings[i].isSet) {
 1217   					throw SQLError.createSQLException(Messages
 1218   							.getString("ServerPreparedStatement.13") + (i + 1) //$NON-NLS-1$
 1219   							+ Messages.getString("ServerPreparedStatement.14"),
 1220   							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
 1221   				}
 1222   			}
 1223   
 1224   			//
 1225   			// Send all long data
 1226   			//
 1227   			for (int i = 0; i < this.parameterCount; i++) {
 1228   				if (this.parameterBindings[i].isLongData) {
 1229   					serverLongData(i, this.parameterBindings[i]);
 1230   				}
 1231   			}
 1232   
 1233   			if (this.connection.getAutoGenerateTestcaseScript()) {
 1234   				dumpExecuteForTestcase();
 1235   			}
 1236   
 1237   			//
 1238   			// store the parameter values
 1239   			//
 1240   
 1241   			Buffer packet = mysql.getSharedSendPacket();
 1242   
 1243   			packet.clear();
 1244   			packet.writeByte((byte) MysqlDefs.COM_EXECUTE);
 1245   			packet.writeLong(this.serverStatementId);
 1246   
 1247   			boolean usingCursor = false;
 1248   
 1249   			if (this.connection.versionMeetsMinimum(4, 1, 2)) {
 1250   				// we only create cursor-backed result sets if
 1251   				// a) The query is a SELECT
 1252   				// b) The server supports it
 1253   				// c) We know it is forward-only (note this doesn't
 1254   				// preclude updatable result sets)
 1255   				// d) The user has set a fetch size
 1256   				if (this.resultFields != null &&
 1257   						this.connection.isCursorFetchEnabled()
 1258   						&& getResultSetType() == ResultSet.TYPE_FORWARD_ONLY
 1259   						&& getResultSetConcurrency() == ResultSet.CONCUR_READ_ONLY
 1260   						&& getFetchSize() > 0) {
 1261   					packet.writeByte(MysqlDefs.OPEN_CURSOR_FLAG);
 1262   					usingCursor = true;
 1263   				} else {
 1264   					packet.writeByte((byte) 0); // placeholder for flags
 1265   				}
 1266   
 1267   				packet.writeLong(1); // placeholder for parameter
 1268   				                     // iterations
 1269   			}
 1270   
 1271   			/* Reserve place for null-marker bytes */
 1272   			int nullCount = (this.parameterCount + 7) / 8;
 1273   
 1274   			// if (mysql.versionMeetsMinimum(4, 1, 2)) {
 1275   			// nullCount = (this.parameterCount + 9) / 8;
 1276   			// }
 1277   			int nullBitsPosition = packet.getPosition();
 1278   
 1279   			for (int i = 0; i < nullCount; i++) {
 1280   				packet.writeByte((byte) 0);
 1281   			}
 1282   
 1283   			byte[] nullBitsBuffer = new byte[nullCount];
 1284   
 1285   			/* In case if buffers (type) altered, indicate to server */
 1286   			packet.writeByte(this.sendTypesToServer ? (byte) 1 : (byte) 0);
 1287   
 1288   			if (this.sendTypesToServer) {
 1289   				/*
 1290   				 * Store types of parameters in first in first package that is
 1291   				 * sent to the server.
 1292   				 */
 1293   				for (int i = 0; i < this.parameterCount; i++) {
 1294   					packet.writeInt(this.parameterBindings[i].bufferType);
 1295   				}
 1296   			}
 1297   
 1298   			//
 1299   			// store the parameter values
 1300   			//
 1301   			for (int i = 0; i < this.parameterCount; i++) {
 1302   				if (!this.parameterBindings[i].isLongData) {
 1303   					if (!this.parameterBindings[i].isNull) {
 1304   						storeBinding(packet, this.parameterBindings[i], mysql);
 1305   					} else {
 1306   						nullBitsBuffer[i / 8] |= (1 << (i & 7));
 1307   					}
 1308   				}
 1309   			}
 1310   
 1311   			//
 1312   			// Go back and write the NULL flags
 1313   			// to the beginning of the packet
 1314   			//
 1315   			int endPosition = packet.getPosition();
 1316   			packet.setPosition(nullBitsPosition);
 1317   			packet.writeBytesNoNull(nullBitsBuffer);
 1318   			packet.setPosition(endPosition);
 1319   
 1320   			long begin = 0;
 1321   
 1322   			boolean logSlowQueries = this.connection.getLogSlowQueries();
 1323   			boolean gatherPerformanceMetrics = this.connection
 1324   					.getGatherPerformanceMetrics();
 1325   
 1326   			if (this.profileSQL || logSlowQueries || gatherPerformanceMetrics) {
 1327   				begin = mysql.getCurrentTimeNanosOrMillis();
 1328   			}
 1329   
 1330   			resetCancelledState();
 1331   			
 1332   			CancelTask timeoutTask = null;
 1333   
 1334   			try {
 1335   				if (this.connection.getEnableQueryTimeouts() &&
 1336   						this.timeoutInMillis != 0
 1337   						&& this.connection.versionMeetsMinimum(5, 0, 0)) {
 1338   					timeoutTask = new CancelTask(this);
 1339   					this.connection.getCancelTimer().schedule(timeoutTask, 
 1340   							this.timeoutInMillis);
 1341   				}
 1342   				
 1343   				Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE,
 1344   					null, packet, false, null, 0);
 1345   				
 1346   				long queryEndTime = 0L;
 1347   				
 1348   				if (logSlowQueries || gatherPerformanceMetrics || this.profileSQL) {
 1349   					queryEndTime = mysql.getCurrentTimeNanosOrMillis();
 1350   				}
 1351   				
 1352   				if (timeoutTask != null) {
 1353   					timeoutTask.cancel();
 1354   					
 1355   					if (timeoutTask.caughtWhileCancelling != null) {
 1356   						throw timeoutTask.caughtWhileCancelling;
 1357   					}
 1358   					
 1359   					timeoutTask = null;
 1360   				}
 1361   				
 1362   				synchronized (this.cancelTimeoutMutex) {
 1363   					if (this.wasCancelled) {
 1364   						SQLException cause = null;
 1365   						
 1366   						if (this.wasCancelledByTimeout) {
 1367   							cause = new MySQLTimeoutException();
 1368   						} else {
 1369   							cause = new MySQLStatementCancelledException();
 1370   						}
 1371   						
 1372   						resetCancelledState();
 1373   						
 1374   						throw cause;
 1375   					}
 1376   				}
 1377   
 1378   				boolean queryWasSlow = false;
 1379   				
 1380   				if (logSlowQueries || gatherPerformanceMetrics) {
 1381   					long elapsedTime = queryEndTime - begin;
 1382   
 1383   					if (logSlowQueries) {
 1384   		    			if (this.useAutoSlowLog) {
 1385   		    				queryWasSlow = elapsedTime > this.connection.getSlowQueryThresholdMillis();
 1386   		    			} else {
 1387   		    				queryWasSlow = this.connection.isAbonormallyLongQuery(elapsedTime);
 1388   		    				
 1389   		    				this.connection.reportQueryTime(elapsedTime);
 1390   		    			}
 1391   					}
 1392   
 1393   					if (queryWasSlow) {
 1394   						
 1395   						StringBuffer mesgBuf = new StringBuffer(
 1396   								48 + this.originalSql.length());
 1397   						mesgBuf.append(Messages
 1398   								.getString("ServerPreparedStatement.15")); //$NON-NLS-1$
 1399   						mesgBuf.append(mysql.getSlowQueryThreshold());
 1400   						mesgBuf.append(Messages
 1401   								.getString("ServerPreparedStatement.15a")); //$NON-NLS-1$
 1402   						mesgBuf.append(elapsedTime);
 1403   						mesgBuf.append(Messages
 1404   								.getString("ServerPreparedStatement.16")); //$NON-NLS-1$
 1405   
 1406   						mesgBuf.append("as prepared: ");
 1407   						mesgBuf.append(this.originalSql);
 1408   						mesgBuf.append("\n\n with parameters bound:\n\n");
 1409   						mesgBuf.append(asSql(true));
 1410   
 1411   						this.eventSink
 1412   								.consumeEvent(new ProfilerEvent(
 1413   										ProfilerEvent.TYPE_SLOW_QUERY,
 1414   										"", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$
 1415   										getId(), 0, System.currentTimeMillis(),
 1416   										elapsedTime, mysql
 1417   												.getQueryTimingUnits(), null,
 1418   										new Throwable(), mesgBuf.toString()));
 1419   					}
 1420   
 1421   					if (gatherPerformanceMetrics) {
 1422   						this.connection.registerQueryExecutionTime(elapsedTime);
 1423   					}
 1424   				}
 1425   
 1426   				this.connection.incrementNumberOfPreparedExecutes();
 1427   
 1428   				if (this.profileSQL) {
 1429   					this.eventSink = ProfilerEventHandlerFactory
 1430   							.getInstance(this.connection);
 1431   
 1432   					this.eventSink.consumeEvent(new ProfilerEvent(
 1433   							ProfilerEvent.TYPE_EXECUTE,
 1434   							"", this.currentCatalog, //$NON-NLS-1$
 1435   							this.connectionId, this.statementId, -1, System
 1436   									.currentTimeMillis(), (int) (mysql
 1437   									.getCurrentTimeNanosOrMillis() - begin),
 1438   							mysql.getQueryTimingUnits(), null, new Throwable(),
 1439   							truncateQueryToLog(asSql(true))));
 1440   				}
 1441   	
 1442   				com.mysql.jdbc.ResultSetInternalMethods rs = mysql.readAllResults(this,
 1443   						maxRowsToRetrieve, this.resultSetType,
 1444   						this.resultSetConcurrency, createStreamingResultSet,
 1445   						this.currentCatalog, resultPacket, true, this.fieldCount,
 1446   						metadataFromCache);
 1447   				
 1448   				if (mysql.shouldIntercept()) {
 1449   					ResultSetInternalMethods interceptedResults =
 1450   		    			mysql.invokeStatementInterceptorsPost(this.originalSql, this, rs, true, null);
 1451   		
 1452   		    		if (interceptedResults != null) {
 1453   		    			rs = interceptedResults;
 1454   		    		}
 1455   				}
 1456   				
 1457   				if (this.profileSQL) {
 1458   					long fetchEndTime = mysql.getCurrentTimeNanosOrMillis();
 1459   
 1460   					this.eventSink.consumeEvent(new ProfilerEvent(
 1461   							ProfilerEvent.TYPE_FETCH,
 1462   							"", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$
 1463   							getId(), 0 /* FIXME rs.resultId */, System.currentTimeMillis(),
 1464   							(fetchEndTime - queryEndTime), mysql
 1465   									.getQueryTimingUnits(), null,
 1466   							new Throwable(), null));
 1467   				}
 1468   	
 1469   				if (queryWasSlow && this.connection.getExplainSlowQueries()) {
 1470   					String queryAsString = asSql(true);
 1471   
 1472   					mysql.explainSlowQuery(queryAsString.getBytes(),
 1473   							queryAsString);
 1474   				}
 1475   				
 1476   				if (!createStreamingResultSet && 
 1477   						this.serverNeedsResetBeforeEachExecution) {
 1478   					serverResetStatement(); // clear any long data...
 1479   				}
 1480   	
 1481   				
 1482   				this.sendTypesToServer = false;
 1483   				this.results = rs;
 1484   	
 1485   				if (mysql.hadWarnings()) {
 1486   		            mysql.scanForAndThrowDataTruncation();
 1487   		        }
 1488   				
 1489   				return rs;
 1490   			} catch (SQLException sqlEx) {
 1491   				if (mysql.shouldIntercept()) {
 1492   					mysql.invokeStatementInterceptorsPost(this.originalSql, this, null, true, sqlEx);
 1493   				}
 1494   				
 1495   				throw sqlEx;
 1496   			} finally {
 1497   				if (timeoutTask != null) {
 1498   					timeoutTask.cancel();
 1499   				}
 1500   			}
 1501   		}
 1502   	}
 1503   
 1504   	/**
 1505   	 * Sends stream-type data parameters to the server.
 1506   	 * 
 1507   	 * <pre>
 1508   	 * 
 1509   	 *  Long data handling:
 1510   	 * 
 1511   	 *  - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
 1512   	 *  - The packet recieved will have the format as:
 1513   	 *    [COM_LONG_DATA:     1][STMT_ID:4][parameter_number:2][type:2][data]
 1514   	 *  - Checks if the type is specified by client, and if yes reads the type,
 1515   	 *    and  stores the data in that format.
 1516   	 *  - It's up to the client to check for read data ended. The server doesn't
 1517   	 *    care;  and also server doesn't notify to the client that it got the
 1518   	 *    data  or not; if there is any error; then during execute; the error
 1519   	 *    will  be returned
 1520   	 *  
 1521   	 * </pre>
 1522   	 * 
 1523   	 * @param parameterIndex
 1524   	 *            DOCUMENT ME!
 1525   	 * @param longData
 1526   	 *            DOCUMENT ME!
 1527   	 * 
 1528   	 * @throws SQLException
 1529   	 *             if an error occurs.
 1530   	 */
 1531   	private void serverLongData(int parameterIndex, BindValue longData)
 1532   			throws SQLException {
 1533   		synchronized (this.connection.getMutex()) {
 1534   			MysqlIO mysql = this.connection.getIO();
 1535   
 1536   			Buffer packet = mysql.getSharedSendPacket();
 1537   
 1538   			Object value = longData.value;
 1539   
 1540   			if (value instanceof byte[]) {
 1541   				packet.clear();
 1542   				packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
 1543   				packet.writeLong(this.serverStatementId);
 1544   				packet.writeInt((parameterIndex));
 1545   
 1546   				packet.writeBytesNoNull((byte[]) longData.value);
 1547   
 1548   				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
 1549   						null, 0);
 1550   			} else if (value instanceof InputStream) {
 1551   				storeStream(mysql, parameterIndex, packet, (InputStream) value);
 1552   			} else if (value instanceof java.sql.Blob) {
 1553   				storeStream(mysql, parameterIndex, packet,
 1554   						((java.sql.Blob) value).getBinaryStream());
 1555   			} else if (value instanceof Reader) {
 1556   				storeReader(mysql, parameterIndex, packet, (Reader) value);
 1557   			} else {
 1558   				throw SQLError.createSQLException(Messages
 1559   						.getString("ServerPreparedStatement.18") //$NON-NLS-1$
 1560   						+ value.getClass().getName() + "'", //$NON-NLS-1$
 1561   						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 1562   			}
 1563   		}
 1564   	}
 1565   
 1566   	private void serverPrepare(String sql) throws SQLException {
 1567   		synchronized (this.connection.getMutex()) {
 1568   			MysqlIO mysql = this.connection.getIO();
 1569   
 1570   			if (this.connection.getAutoGenerateTestcaseScript()) {
 1571   				dumpPrepareForTestcase();
 1572   			}
 1573   
 1574   			try {
 1575   				long begin = 0;
 1576   
 1577   				if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
 1578   					this.isLoadDataQuery = true;
 1579   				} else {
 1580   					this.isLoadDataQuery = false;
 1581   				}
 1582   
 1583   				if (this.connection.getProfileSql()) {
 1584   					begin = System.currentTimeMillis();
 1585   				}
 1586   
 1587   				String characterEncoding = null;
 1588   				String connectionEncoding = this.connection.getEncoding();
 1589   
 1590   				if (!this.isLoadDataQuery && this.connection.getUseUnicode()
 1591   						&& (connectionEncoding != null)) {
 1592   					characterEncoding = connectionEncoding;
 1593   				}
 1594   
 1595   				Buffer prepareResultPacket = mysql.sendCommand(
 1596   						MysqlDefs.COM_PREPARE, sql, null, false,
 1597   						characterEncoding, 0);
 1598   
 1599   				if (this.connection.versionMeetsMinimum(4, 1, 1)) {
 1600   					// 4.1.1 and newer use the first byte
 1601   					// as an 'ok' or 'error' flag, so move
 1602   					// the buffer pointer past it to
 1603   					// start reading the statement id.
 1604   					prepareResultPacket.setPosition(1);
 1605   				} else {
 1606   					// 4.1.0 doesn't use the first byte as an
 1607   					// 'ok' or 'error' flag
 1608   					prepareResultPacket.setPosition(0);
 1609   				}
 1610   
 1611   				this.serverStatementId = prepareResultPacket.readLong();
 1612   				this.fieldCount = prepareResultPacket.readInt();
 1613   				this.parameterCount = prepareResultPacket.readInt();
 1614   				this.parameterBindings = new BindValue[this.parameterCount];
 1615   
 1616   				for (int i = 0; i < this.parameterCount; i++) {
 1617   					this.parameterBindings[i] = new BindValue();
 1618   				}
 1619   
 1620   				this.connection.incrementNumberOfPrepares();
 1621   
 1622   				if (this.profileSQL) {
 1623   					this.eventSink.consumeEvent(new ProfilerEvent(
 1624   							ProfilerEvent.TYPE_PREPARE,
 1625   							"", this.currentCatalog, //$NON-NLS-1$
 1626   							this.connectionId, this.statementId, -1,
 1627   							System.currentTimeMillis(), 
 1628   							mysql.getCurrentTimeNanosOrMillis() - begin, 
 1629   							mysql.getQueryTimingUnits(), null,
 1630   							new Throwable(), truncateQueryToLog(sql)));
 1631   				}
 1632   
 1633   				if (this.parameterCount > 0) {
 1634   					if (this.connection.versionMeetsMinimum(4, 1, 2)
 1635   							&& !mysql.isVersion(5, 0, 0)) {
 1636   						this.parameterFields = new Field[this.parameterCount];
 1637   
 1638   						Buffer metaDataPacket = mysql.readPacket();
 1639   
 1640   						int i = 0;
 1641   
 1642   						while (!metaDataPacket.isLastDataPacket()
 1643   								&& (i < this.parameterCount)) {
 1644   							this.parameterFields[i++] = mysql.unpackField(
 1645   									metaDataPacket, false);
 1646   							metaDataPacket = mysql.readPacket();
 1647   						}
 1648   					}
 1649   				}
 1650   
 1651   				if (this.fieldCount > 0) {
 1652   					this.resultFields = new Field[this.fieldCount];
 1653   
 1654   					Buffer fieldPacket = mysql.readPacket();
 1655   
 1656   					int i = 0;
 1657   
 1658   					// Read in the result set column information
 1659   					while (!fieldPacket.isLastDataPacket()
 1660   							&& (i < this.fieldCount)) {
 1661   						this.resultFields[i++] = mysql.unpackField(fieldPacket,
 1662   								false);
 1663   						fieldPacket = mysql.readPacket();
 1664   					}
 1665   				}
 1666   			} catch (SQLException sqlEx) {
 1667   				if (this.connection.getDumpQueriesOnException()) {
 1668   					StringBuffer messageBuf = new StringBuffer(this.originalSql
 1669   							.length() + 32);
 1670   					messageBuf
 1671   							.append("\n\nQuery being prepared when exception was thrown:\n\n");
 1672   					messageBuf.append(this.originalSql);
 1673   
 1674   					sqlEx = ConnectionImpl.appendMessageToException(sqlEx,
 1675   							messageBuf.toString(), getExceptionInterceptor());
 1676   				}
 1677   
 1678   				throw sqlEx;
 1679   			} finally {
 1680   				// Leave the I/O channel in a known state...there might be
 1681   				// packets out there
 1682   				// that we're not interested in
 1683   				this.connection.getIO().clearInputStream();
 1684   			}
 1685   		}
 1686   	}
 1687   
 1688   	private String truncateQueryToLog(String sql) {
 1689   		String query = null;
 1690   		
 1691   		if (sql.length() > this.connection.getMaxQuerySizeToLog()) {
 1692   			StringBuffer queryBuf = new StringBuffer(
 1693   					this.connection.getMaxQuerySizeToLog() + 12);
 1694   			queryBuf.append(sql.substring(0, this.connection.getMaxQuerySizeToLog()));
 1695   			queryBuf.append(Messages.getString("MysqlIO.25"));
 1696   			
 1697   			query = queryBuf.toString();
 1698   		} else {
 1699   			query = sql;
 1700   		}
 1701   		
 1702   		return query;
 1703   	}
 1704   
 1705   	private void serverResetStatement() throws SQLException {
 1706   		synchronized (this.connection.getMutex()) {
 1707   
 1708   			MysqlIO mysql = this.connection.getIO();
 1709   
 1710   			Buffer packet = mysql.getSharedSendPacket();
 1711   
 1712   			packet.clear();
 1713   			packet.writeByte((byte) MysqlDefs.COM_RESET_STMT);
 1714   			packet.writeLong(this.serverStatementId);
 1715   
 1716   			try {
 1717   				mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null, packet,
 1718   						!this.connection.versionMeetsMinimum(4, 1, 2), null, 0);
 1719   			} catch (SQLException sqlEx) {
 1720   				throw sqlEx;
 1721   			} catch (Exception ex) {
 1722   				SQLException sqlEx = SQLError.createSQLException(ex.toString(),
 1723   						SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 1724   				sqlEx.initCause(ex);
 1725   				
 1726   				throw sqlEx;
 1727   			} finally {
 1728   				mysql.clearInputStream();
 1729   			}
 1730   		}
 1731   	}
 1732   
 1733   	/**
 1734   	 * @see java.sql.PreparedStatement#setArray(int, java.sql.Array)
 1735   	 */
 1736   	public void setArray(int i, Array x) throws SQLException {
 1737   		throw SQLError.notImplemented();
 1738   	}
 1739   
 1740   	/**
 1741   	 * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream,
 1742   	 *      int)
 1743   	 */
 1744   	public void setAsciiStream(int parameterIndex, InputStream x, int length)
 1745   			throws SQLException {
 1746   		checkClosed();
 1747   
 1748   		if (x == null) {
 1749   			setNull(parameterIndex, java.sql.Types.BINARY);
 1750   		} else {
 1751   			BindValue binding = getBinding(parameterIndex, true);
 1752   			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
 1753   
 1754   			binding.value = x;
 1755   			binding.isNull = false;
 1756   			binding.isLongData = true;
 1757   
 1758   			if (this.connection.getUseStreamLengthsInPrepStmts()) {
 1759   				binding.bindLength = length;
 1760   			} else {
 1761   				binding.bindLength = -1;
 1762   			}
 1763   		}
 1764   	}
 1765   
 1766   	/**
 1767   	 * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal)
 1768   	 */
 1769   	public void setBigDecimal(int parameterIndex, BigDecimal x)
 1770   			throws SQLException {
 1771   		checkClosed();
 1772   
 1773   		if (x == null) {
 1774   			setNull(parameterIndex, java.sql.Types.DECIMAL);
 1775   		} else {
 1776   		
 1777   			BindValue binding = getBinding(parameterIndex, false);
 1778   
 1779   			if (this.connection.versionMeetsMinimum(5, 0, 3)) {
 1780   				setType(binding, MysqlDefs.FIELD_TYPE_NEW_DECIMAL);
 1781   			} else {
 1782   				setType(binding, this.stringTypeCode);
 1783   			}
 1784   
 1785   			binding.value = StringUtils
 1786   				.fixDecimalExponent(StringUtils.consistentToString(x));
 1787   			binding.isNull = false;
 1788   			binding.isLongData = false;
 1789   		}
 1790   	}
 1791   
 1792   	/**
 1793   	 * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream,
 1794   	 *      int)
 1795   	 */
 1796   	public void setBinaryStream(int parameterIndex, InputStream x, int length)
 1797   			throws SQLException {
 1798   		checkClosed();
 1799   
 1800   		if (x == null) {
 1801   			setNull(parameterIndex, java.sql.Types.BINARY);
 1802   		} else {
 1803   			BindValue binding = getBinding(parameterIndex, true);
 1804   			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
 1805   
 1806   			binding.value = x;
 1807   			binding.isNull = false;
 1808   			binding.isLongData = true;
 1809   
 1810   			if (this.connection.getUseStreamLengthsInPrepStmts()) {
 1811   				binding.bindLength = length;
 1812   			} else {
 1813   				binding.bindLength = -1;
 1814   			}
 1815   		}
 1816   	}
 1817   
 1818   	/**
 1819   	 * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob)
 1820   	 */
 1821   	public void setBlob(int parameterIndex, Blob x) throws SQLException {
 1822   		checkClosed();
 1823   
 1824   		if (x == null) {
 1825   			setNull(parameterIndex, java.sql.Types.BINARY);
 1826   		} else {
 1827   			BindValue binding = getBinding(parameterIndex, true);
 1828   			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
 1829   
 1830   			binding.value = x;
 1831   			binding.isNull = false;
 1832   			binding.isLongData = true;
 1833   
 1834   			if (this.connection.getUseStreamLengthsInPrepStmts()) {
 1835   				binding.bindLength = x.length();
 1836   			} else {
 1837   				binding.bindLength = -1;
 1838   			}
 1839   		}
 1840   	}
 1841   
 1842   	/**
 1843   	 * @see java.sql.PreparedStatement#setBoolean(int, boolean)
 1844   	 */
 1845   	public void setBoolean(int parameterIndex, boolean x) throws SQLException {
 1846   		setByte(parameterIndex, (x ? (byte) 1 : (byte) 0));
 1847   	}
 1848   
 1849   	/**
 1850   	 * @see java.sql.PreparedStatement#setByte(int, byte)
 1851   	 */
 1852   	public void setByte(int parameterIndex, byte x) throws SQLException {
 1853   		checkClosed();
 1854   
 1855   		BindValue binding = getBinding(parameterIndex, false);
 1856   		setType(binding, MysqlDefs.FIELD_TYPE_TINY);
 1857   
 1858   		binding.value = null;
 1859   		binding.byteBinding = x;
 1860   		binding.isNull = false;
 1861   		binding.isLongData = false;
 1862   	}
 1863   
 1864   	/**
 1865   	 * @see java.sql.PreparedStatement#setBytes(int, byte)
 1866   	 */
 1867   	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
 1868   		checkClosed();
 1869   
 1870   		if (x == null) {
 1871   			setNull(parameterIndex, java.sql.Types.BINARY);
 1872   		} else {
 1873   			BindValue binding = getBinding(parameterIndex, false);
 1874   			setType(binding, MysqlDefs.FIELD_TYPE_VAR_STRING);
 1875   
 1876   			binding.value = x;
 1877   			binding.isNull = false;
 1878   			binding.isLongData = false;
 1879   		}
 1880   	}
 1881   
 1882   	/**
 1883   	 * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader,
 1884   	 *      int)
 1885   	 */
 1886   	public void setCharacterStream(int parameterIndex, Reader reader, int length)
 1887   			throws SQLException {
 1888   		checkClosed();
 1889   
 1890   		if (reader == null) {
 1891   			setNull(parameterIndex, java.sql.Types.BINARY);
 1892   		} else {
 1893   			BindValue binding = getBinding(parameterIndex, true);
 1894   			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
 1895   
 1896   			binding.value = reader;
 1897   			binding.isNull = false;
 1898   			binding.isLongData = true;
 1899   
 1900   			if (this.connection.getUseStreamLengthsInPrepStmts()) {
 1901   				binding.bindLength = length;
 1902   			} else {
 1903   				binding.bindLength = -1;
 1904   			}
 1905   		}
 1906   	}
 1907   
 1908   	/**
 1909   	 * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob)
 1910   	 */
 1911   	public void setClob(int parameterIndex, Clob x) throws SQLException {
 1912   		checkClosed();
 1913   
 1914   		if (x == null) {
 1915   			setNull(parameterIndex, java.sql.Types.BINARY);
 1916   		} else {
 1917   			BindValue binding = getBinding(parameterIndex, true);
 1918   			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
 1919   
 1920   			binding.value = x.getCharacterStream();
 1921   			binding.isNull = false;
 1922   			binding.isLongData = true;
 1923   
 1924   			if (this.connection.getUseStreamLengthsInPrepStmts()) {
 1925   				binding.bindLength = x.length();
 1926   			} else {
 1927   				binding.bindLength = -1;
 1928   			}
 1929   		}
 1930   	}
 1931   
 1932   	/**
 1933   	 * Set a parameter to a java.sql.Date value. The driver converts this to a
 1934   	 * SQL DATE value when it sends it to the database.
 1935   	 * 
 1936   	 * @param parameterIndex
 1937   	 *            the first parameter is 1, the second is 2, ...
 1938   	 * @param x
 1939   	 *            the parameter value
 1940   	 * 
 1941   	 * @exception SQLException
 1942   	 *                if a database-access error occurs.
 1943   	 */
 1944   	public void setDate(int parameterIndex, Date x) throws SQLException {
 1945   		setDate(parameterIndex, x, null);
 1946   	}
 1947   
 1948   	/**
 1949   	 * Set a parameter to a java.sql.Date value. The driver converts this to a
 1950   	 * SQL DATE value when it sends it to the database.
 1951   	 * 
 1952   	 * @param parameterIndex
 1953   	 *            the first parameter is 1, the second is 2, ...
 1954   	 * @param x
 1955   	 *            the parameter value
 1956   	 * @param cal
 1957   	 *            the calendar to interpret the date with
 1958   	 * 
 1959   	 * @exception SQLException
 1960   	 *                if a database-access error occurs.
 1961   	 */
 1962   	public void setDate(int parameterIndex, Date x, Calendar cal)
 1963   			throws SQLException {
 1964   		if (x == null) {
 1965   			setNull(parameterIndex, java.sql.Types.DATE);
 1966   		} else {
 1967   			BindValue binding = getBinding(parameterIndex, false);
 1968   			setType(binding, MysqlDefs.FIELD_TYPE_DATE);
 1969   
 1970   			binding.value = x;
 1971   			binding.isNull = false;
 1972   			binding.isLongData = false;
 1973   		}
 1974   	}
 1975   
 1976   	/**
 1977   	 * @see java.sql.PreparedStatement#setDouble(int, double)
 1978   	 */
 1979   	public void setDouble(int parameterIndex, double x) throws SQLException {
 1980   		checkClosed();
 1981   
 1982   		if (!this.connection.getAllowNanAndInf()
 1983   				&& (x == Double.POSITIVE_INFINITY
 1984   						|| x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) {
 1985   			throw SQLError.createSQLException("'" + x
 1986   					+ "' is not a valid numeric or approximate numeric value",
 1987   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 1988   
 1989   		}
 1990   
 1991   		BindValue binding = getBinding(parameterIndex, false);
 1992   		setType(binding, MysqlDefs.FIELD_TYPE_DOUBLE);
 1993   
 1994   		binding.value = null;
 1995   		binding.doubleBinding = x;
 1996   		binding.isNull = false;
 1997   		binding.isLongData = false;
 1998   	}
 1999   
 2000   	/**
 2001   	 * @see java.sql.PreparedStatement#setFloat(int, float)
 2002   	 */
 2003   	public void setFloat(int parameterIndex, float x) throws SQLException {
 2004   		checkClosed();
 2005   
 2006   		BindValue binding = getBinding(parameterIndex, false);
 2007   		setType(binding, MysqlDefs.FIELD_TYPE_FLOAT);
 2008   
 2009   		binding.value = null;
 2010   		binding.floatBinding = x;
 2011   		binding.isNull = false;
 2012   		binding.isLongData = false;
 2013   	}
 2014   
 2015   	/**
 2016   	 * @see java.sql.PreparedStatement#setInt(int, int)
 2017   	 */
 2018   	public void setInt(int parameterIndex, int x) throws SQLException {
 2019   		checkClosed();
 2020   
 2021   		BindValue binding = getBinding(parameterIndex, false);
 2022   		setType(binding, MysqlDefs.FIELD_TYPE_LONG);
 2023   
 2024   		binding.value = null;
 2025   		binding.intBinding = x;
 2026   		binding.isNull = false;
 2027   		binding.isLongData = false;
 2028   	}
 2029   
 2030   	/**
 2031   	 * @see java.sql.PreparedStatement#setLong(int, long)
 2032   	 */
 2033   	public void setLong(int parameterIndex, long x) throws SQLException {
 2034   		checkClosed();
 2035   
 2036   		BindValue binding = getBinding(parameterIndex, false);
 2037   		setType(binding, MysqlDefs.FIELD_TYPE_LONGLONG);
 2038   
 2039   		binding.value = null;
 2040   		binding.longBinding = x;
 2041   		binding.isNull = false;
 2042   		binding.isLongData = false;
 2043   	}
 2044   
 2045   	/**
 2046   	 * @see java.sql.PreparedStatement#setNull(int, int)
 2047   	 */
 2048   	public void setNull(int parameterIndex, int sqlType) throws SQLException {
 2049   		checkClosed();
 2050   
 2051   		BindValue binding = getBinding(parameterIndex, false);
 2052   
 2053   		//
 2054   		// Don't re-set types, but use something if this
 2055   		// parameter was never specified
 2056   		//
 2057   		if (binding.bufferType == 0) {
 2058   			setType(binding, MysqlDefs.FIELD_TYPE_NULL);
 2059   		}
 2060   
 2061   		binding.value = null;
 2062   		binding.isNull = true;
 2063   		binding.isLongData = false;
 2064   	}
 2065   
 2066   	/**
 2067   	 * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String)
 2068   	 */
 2069   	public void setNull(int parameterIndex, int sqlType, String typeName)
 2070   			throws SQLException {
 2071   		checkClosed();
 2072   
 2073   		BindValue binding = getBinding(parameterIndex, false);
 2074   
 2075   		//
 2076   		// Don't re-set types, but use something if this
 2077   		// parameter was never specified
 2078   		//
 2079   		if (binding.bufferType == 0) {
 2080   			setType(binding, MysqlDefs.FIELD_TYPE_NULL);
 2081   		}
 2082   
 2083   		binding.value = null;
 2084   		binding.isNull = true;
 2085   		binding.isLongData = false;
 2086   	}
 2087   
 2088   	/**
 2089   	 * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref)
 2090   	 */
 2091   	public void setRef(int i, Ref x) throws SQLException {
 2092   		throw SQLError.notImplemented();
 2093   	}
 2094   
 2095   	/**
 2096   	 * @see java.sql.PreparedStatement#setShort(int, short)
 2097   	 */
 2098   	public void setShort(int parameterIndex, short x) throws SQLException {
 2099   		checkClosed();
 2100   
 2101   		BindValue binding = getBinding(parameterIndex, false);
 2102   		setType(binding, MysqlDefs.FIELD_TYPE_SHORT);
 2103   
 2104   		binding.value = null;
 2105   		binding.shortBinding = x;
 2106   		binding.isNull = false;
 2107   		binding.isLongData = false;
 2108   	}
 2109   
 2110   	/**
 2111   	 * @see java.sql.PreparedStatement#setString(int, java.lang.String)
 2112   	 */
 2113   	public void setString(int parameterIndex, String x) throws SQLException {
 2114   		checkClosed();
 2115   
 2116   		if (x == null) {
 2117   			setNull(parameterIndex, java.sql.Types.CHAR);
 2118   		} else {
 2119   			BindValue binding = getBinding(parameterIndex, false);
 2120   
 2121   			setType(binding, this.stringTypeCode);
 2122   
 2123   			binding.value = x;
 2124   			binding.isNull = false;
 2125   			binding.isLongData = false;
 2126   		}
 2127   	}
 2128   
 2129   	/**
 2130   	 * Set a parameter to a java.sql.Time value.
 2131   	 * 
 2132   	 * @param parameterIndex
 2133   	 *            the first parameter is 1...));
 2134   	 * @param x
 2135   	 *            the parameter value
 2136   	 * 
 2137   	 * @throws SQLException
 2138   	 *             if a database access error occurs
 2139   	 */
 2140   	public void setTime(int parameterIndex, java.sql.Time x)
 2141   			throws SQLException {
 2142   		setTimeInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false);
 2143   	}
 2144   
 2145   	/**
 2146   	 * Set a parameter to a java.sql.Time value. The driver converts this to a
 2147   	 * SQL TIME value when it sends it to the database, using the given
 2148   	 * timezone.
 2149   	 * 
 2150   	 * @param parameterIndex
 2151   	 *            the first parameter is 1...));
 2152   	 * @param x
 2153   	 *            the parameter value
 2154   	 * @param cal
 2155   	 *            the timezone to use
 2156   	 * 
 2157   	 * @throws SQLException
 2158   	 *             if a database access error occurs
 2159   	 */
 2160   	public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
 2161   			throws SQLException {
 2162   		setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
 2163   	}
 2164   
 2165   	/**
 2166   	 * Set a parameter to a java.sql.Time value. The driver converts this to a
 2167   	 * SQL TIME value when it sends it to the database, using the given
 2168   	 * timezone.
 2169   	 * 
 2170   	 * @param parameterIndex
 2171   	 *            the first parameter is 1...));
 2172   	 * @param x
 2173   	 *            the parameter value
 2174   	 * @param tz
 2175   	 *            the timezone to use
 2176   	 * 
 2177   	 * @throws SQLException
 2178   	 *             if a database access error occurs
 2179   	 */
 2180   	public void setTimeInternal(int parameterIndex, java.sql.Time x,
 2181   			Calendar targetCalendar,
 2182   			TimeZone tz, boolean rollForward) throws SQLException {
 2183   		if (x == null) {
 2184   			setNull(parameterIndex, java.sql.Types.TIME);
 2185   		} else {
 2186   			BindValue binding = getBinding(parameterIndex, false);
 2187   			setType(binding, MysqlDefs.FIELD_TYPE_TIME);
 2188   
 2189   			if (!this.useLegacyDatetimeCode) {
 2190   				binding.value = x;
 2191   			} else {
 2192   				Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
 2193   				
 2194   				synchronized (sessionCalendar) {
 2195   					binding.value = TimeUtil.changeTimezone(this.connection, 
 2196   							sessionCalendar,
 2197   							targetCalendar,
 2198   							x, tz,
 2199   							this.connection.getServerTimezoneTZ(), 
 2200   							rollForward);
 2201   				}
 2202   			}
 2203   			
 2204   			binding.isNull = false;
 2205   			binding.isLongData = false;
 2206   		}
 2207   	}
 2208   
 2209   	/**
 2210   	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 2211   	 * to a SQL TIMESTAMP value when it sends it to the database.
 2212   	 * 
 2213   	 * @param parameterIndex
 2214   	 *            the first parameter is 1, the second is 2, ...
 2215   	 * @param x
 2216   	 *            the parameter value
 2217   	 * 
 2218   	 * @throws SQLException
 2219   	 *             if a database-access error occurs.
 2220   	 */
 2221   	public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
 2222   			throws SQLException {
 2223   		setTimestampInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false);
 2224   	}
 2225   
 2226   	/**
 2227   	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 2228   	 * to a SQL TIMESTAMP value when it sends it to the database.
 2229   	 * 
 2230   	 * @param parameterIndex
 2231   	 *            the first parameter is 1, the second is 2, ...
 2232   	 * @param x
 2233   	 *            the parameter value
 2234   	 * @param cal
 2235   	 *            the timezone to use
 2236   	 * 
 2237   	 * @throws SQLException
 2238   	 *             if a database-access error occurs.
 2239   	 */
 2240   	public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
 2241   			Calendar cal) throws SQLException {
 2242   		setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
 2243   	}
 2244   
 2245   	protected void setTimestampInternal(int parameterIndex,
 2246   			java.sql.Timestamp x, Calendar targetCalendar,
 2247   			TimeZone tz, boolean rollForward)
 2248   			throws SQLException {
 2249   		if (x == null) {
 2250   			setNull(parameterIndex, java.sql.Types.TIMESTAMP);
 2251   		} else {
 2252   			BindValue binding = getBinding(parameterIndex, false);
 2253   			setType(binding, MysqlDefs.FIELD_TYPE_DATETIME);
 2254   
 2255   			if (!this.useLegacyDatetimeCode) {
 2256   				binding.value = x;
 2257   			} else {
 2258   				Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
 2259   						this.connection.getUtcCalendar() : 
 2260   							getCalendarInstanceForSessionOrNew();
 2261   				
 2262   				synchronized (sessionCalendar) {
 2263   					binding.value = TimeUtil.changeTimezone(this.connection, 
 2264   							sessionCalendar,
 2265   							targetCalendar,
 2266   							x, tz,
 2267   							this.connection.getServerTimezoneTZ(), 
 2268   							rollForward);
 2269   				}
 2270   				
 2271   				binding.isNull = false;
 2272   				binding.isLongData = false;
 2273   			}
 2274   		}
 2275   	}
 2276   
 2277   	protected void setType(BindValue oldValue, int bufferType) {
 2278   		if (oldValue.bufferType != bufferType) {
 2279   			this.sendTypesToServer = true;
 2280   		}
 2281   
 2282   		oldValue.bufferType = bufferType;
 2283   	}
 2284   
 2285   	/**
 2286   	 * DOCUMENT ME!
 2287   	 * 
 2288   	 * @param parameterIndex
 2289   	 *            DOCUMENT ME!
 2290   	 * @param x
 2291   	 *            DOCUMENT ME!
 2292   	 * @param length
 2293   	 *            DOCUMENT ME!
 2294   	 * 
 2295   	 * @throws SQLException
 2296   	 *             DOCUMENT ME!
 2297   	 * @throws NotImplemented
 2298   	 *             DOCUMENT ME!
 2299   	 * 
 2300   	 * @see java.sql.PreparedStatement#setUnicodeStream(int,
 2301   	 *      java.io.InputStream, int)
 2302   	 * @deprecated
 2303   	 */
 2304   	public void setUnicodeStream(int parameterIndex, InputStream x, int length)
 2305   			throws SQLException {
 2306   		checkClosed();
 2307   
 2308   		throw SQLError.notImplemented();
 2309   	}
 2310   
 2311   	/**
 2312   	 * @see java.sql.PreparedStatement#setURL(int, java.net.URL)
 2313   	 */
 2314   	public void setURL(int parameterIndex, URL x) throws SQLException {
 2315   		checkClosed();
 2316   
 2317   		setString(parameterIndex, x.toString());
 2318   	}
 2319   
 2320   	/**
 2321   	 * Method storeBinding.
 2322   	 * 
 2323   	 * @param packet
 2324   	 * @param bindValue
 2325   	 * @param mysql
 2326   	 *            DOCUMENT ME!
 2327   	 * 
 2328   	 * @throws SQLException
 2329   	 *             DOCUMENT ME!
 2330   	 */
 2331   	private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql)
 2332   			throws SQLException {
 2333   		try {
 2334   			Object value = bindValue.value;
 2335   
 2336   			//
 2337   			// Handle primitives first
 2338   			//
 2339   			switch (bindValue.bufferType) {
 2340   
 2341   			case MysqlDefs.FIELD_TYPE_TINY:
 2342   				packet.writeByte(bindValue.byteBinding);
 2343   				return;
 2344   			case MysqlDefs.FIELD_TYPE_SHORT:
 2345   				packet.ensureCapacity(2);
 2346   				packet.writeInt(bindValue.shortBinding);
 2347   				return;
 2348   			case MysqlDefs.FIELD_TYPE_LONG:
 2349   				packet.ensureCapacity(4);
 2350   				packet.writeLong(bindValue.intBinding);
 2351   				return;
 2352   			case MysqlDefs.FIELD_TYPE_LONGLONG:
 2353   				packet.ensureCapacity(8);
 2354   				packet.writeLongLong(bindValue.longBinding);
 2355   				return;
 2356   			case MysqlDefs.FIELD_TYPE_FLOAT:
 2357   				packet.ensureCapacity(4);
 2358   				packet.writeFloat(bindValue.floatBinding);
 2359   				return;
 2360   			case MysqlDefs.FIELD_TYPE_DOUBLE:
 2361   				packet.ensureCapacity(8);
 2362   				packet.writeDouble(bindValue.doubleBinding);
 2363   				return;
 2364   			case MysqlDefs.FIELD_TYPE_TIME:
 2365   				storeTime(packet, (Time) value);
 2366   				return;
 2367   			case MysqlDefs.FIELD_TYPE_DATE:
 2368   			case MysqlDefs.FIELD_TYPE_DATETIME:
 2369   			case MysqlDefs.FIELD_TYPE_TIMESTAMP:
 2370   				storeDateTime(packet, (java.util.Date) value, mysql, bindValue.bufferType);
 2371   				return;
 2372   			case MysqlDefs.FIELD_TYPE_VAR_STRING:
 2373   			case MysqlDefs.FIELD_TYPE_STRING:
 2374   			case MysqlDefs.FIELD_TYPE_VARCHAR:
 2375   			case MysqlDefs.FIELD_TYPE_DECIMAL:
 2376   			case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
 2377   				if (value instanceof byte[]) {
 2378   					packet.writeLenBytes((byte[]) value);
 2379   				} else if (!this.isLoadDataQuery) {
 2380   					packet.writeLenString((String) value, this.charEncoding,
 2381   							this.connection.getServerCharacterEncoding(),
 2382   							this.charConverter, this.connection
 2383   									.parserKnowsUnicode(),
 2384   									this.connection);
 2385   				} else {
 2386   					packet.writeLenBytes(((String) value).getBytes());
 2387   				}
 2388   
 2389   				return;
 2390   			}
 2391   
 2392   			
 2393   		} catch (UnsupportedEncodingException uEE) {
 2394   			throw SQLError.createSQLException(Messages
 2395   					.getString("ServerPreparedStatement.22") //$NON-NLS-1$
 2396   					+ this.connection.getEncoding() + "'", //$NON-NLS-1$
 2397   					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 2398   		}
 2399   	}
 2400   
 2401   	private void storeDateTime412AndOlder(Buffer intoBuf, java.util.Date dt, int bufferType)
 2402   			throws SQLException {
 2403   		
 2404   		Calendar sessionCalendar = null;
 2405   		
 2406   		if (!this.useLegacyDatetimeCode) {
 2407   			if (bufferType == MysqlDefs.FIELD_TYPE_DATE) {
 2408   				sessionCalendar = getDefaultTzCalendar();
 2409   			} else {
 2410   				sessionCalendar = getServerTzCalendar();
 2411   			}
 2412   		} else {
 2413   			sessionCalendar = (dt instanceof Timestamp && 
 2414   				this.connection.getUseJDBCCompliantTimezoneShift()) ? 
 2415   				this.connection.getUtcCalendar() : getCalendarInstanceForSessionOrNew();
 2416   		}
 2417   		
 2418   		synchronized (sessionCalendar) {
 2419   			java.util.Date oldTime = sessionCalendar.getTime();
 2420   			
 2421   			try {
 2422   				intoBuf.ensureCapacity(8);
 2423   				intoBuf.writeByte((byte) 7); // length
 2424   	
 2425   				sessionCalendar.setTime(dt);
 2426   				
 2427   				int year = sessionCalendar.get(Calendar.YEAR);
 2428   				int month = sessionCalendar.get(Calendar.MONTH) + 1;
 2429   				int date = sessionCalendar.get(Calendar.DATE);
 2430   
 2431   				intoBuf.writeInt(year);
 2432   				intoBuf.writeByte((byte) month);
 2433   				intoBuf.writeByte((byte) date);
 2434   		
 2435   				if (dt instanceof java.sql.Date) {
 2436   					intoBuf.writeByte((byte) 0);
 2437   					intoBuf.writeByte((byte) 0);
 2438   					intoBuf.writeByte((byte) 0);
 2439   				} else {
 2440   					intoBuf.writeByte((byte) sessionCalendar
 2441   							.get(Calendar.HOUR_OF_DAY));
 2442   					intoBuf.writeByte((byte) sessionCalendar
 2443   							.get(Calendar.MINUTE));
 2444   					intoBuf.writeByte((byte) sessionCalendar
 2445   							.get(Calendar.SECOND));
 2446   				}
 2447   			} finally {
 2448   				sessionCalendar.setTime(oldTime);
 2449   			}
 2450   		}
 2451   	}
 2452   
 2453   	private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql, int bufferType)
 2454   			throws SQLException {
 2455   		if (this.connection.versionMeetsMinimum(4, 1, 3)) {
 2456   			storeDateTime413AndNewer(intoBuf, dt, bufferType);
 2457   		} else {
 2458   			storeDateTime412AndOlder(intoBuf, dt, bufferType);
 2459   		}
 2460   	}
 2461   
 2462   	private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt, int bufferType)
 2463   			throws SQLException {
 2464   		Calendar sessionCalendar = null;
 2465   		
 2466   		if (!this.useLegacyDatetimeCode) {
 2467   			if (bufferType == MysqlDefs.FIELD_TYPE_DATE) {
 2468   				sessionCalendar = getDefaultTzCalendar();
 2469   			} else {
 2470   				sessionCalendar = getServerTzCalendar();
 2471   			}
 2472   		} else {
 2473   			sessionCalendar = (dt instanceof Timestamp && 
 2474   				this.connection.getUseJDBCCompliantTimezoneShift()) ? 
 2475   				this.connection.getUtcCalendar() : getCalendarInstanceForSessionOrNew();
 2476   		}
 2477   		
 2478   		synchronized (sessionCalendar) {
 2479   			java.util.Date oldTime = sessionCalendar.getTime();
 2480   
 2481   			try {
 2482   				sessionCalendar.setTime(dt);
 2483   				
 2484   				if (dt instanceof java.sql.Date) {
 2485   					sessionCalendar.set(Calendar.HOUR_OF_DAY, 0);
 2486   					sessionCalendar.set(Calendar.MINUTE, 0);
 2487   					sessionCalendar.set(Calendar.SECOND, 0);
 2488   				}
 2489   
 2490   				byte length = (byte) 7;
 2491   
 2492   				if (dt instanceof java.sql.Timestamp) {
 2493   					length = (byte) 11;
 2494   				}
 2495   
 2496   				intoBuf.ensureCapacity(length);
 2497   				
 2498   				intoBuf.writeByte(length); // length
 2499   
 2500   				int year = sessionCalendar.get(Calendar.YEAR);
 2501   				int month = sessionCalendar.get(Calendar.MONTH) + 1;
 2502   				int date = sessionCalendar.get(Calendar.DAY_OF_MONTH);
 2503   				
 2504   				intoBuf.writeInt(year);
 2505   				intoBuf.writeByte((byte) month);
 2506   				intoBuf.writeByte((byte) date);
 2507   
 2508   				if (dt instanceof java.sql.Date) {
 2509   					intoBuf.writeByte((byte) 0);
 2510   					intoBuf.writeByte((byte) 0);
 2511   					intoBuf.writeByte((byte) 0);
 2512   				} else {
 2513   					intoBuf.writeByte((byte) sessionCalendar
 2514   							.get(Calendar.HOUR_OF_DAY));
 2515   					intoBuf.writeByte((byte) sessionCalendar
 2516   							.get(Calendar.MINUTE));
 2517   					intoBuf.writeByte((byte) sessionCalendar
 2518   							.get(Calendar.SECOND));
 2519   				}
 2520   
 2521   				if (length == 11) {
 2522   					//	MySQL expects microseconds, not nanos
 2523   					intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos() / 1000);
 2524   				}
 2525   			
 2526   			} finally {
 2527   				sessionCalendar.setTime(oldTime);
 2528   			}
 2529   		}
 2530   	}
 2531   
 2532   	private Calendar getServerTzCalendar() {
 2533   		synchronized (this) {
 2534   			if (serverTzCalendar == null) {
 2535   				serverTzCalendar = new GregorianCalendar(this.connection.getServerTimezoneTZ());
 2536   			}
 2537   			
 2538   			return this.serverTzCalendar;
 2539   		}
 2540   	}
 2541   	
 2542   	private Calendar getDefaultTzCalendar() {
 2543   		synchronized (this) {
 2544   			if (defaultTzCalendar == null) {
 2545   				defaultTzCalendar = new GregorianCalendar(TimeZone.getDefault());
 2546   			}
 2547   			
 2548   			return this.defaultTzCalendar;
 2549   		}
 2550   	}
 2551   
 2552   	//
 2553   	// TO DO: Investigate using NIO to do this faster
 2554   	//
 2555   	private void storeReader(MysqlIO mysql, int parameterIndex, Buffer packet,
 2556   			Reader inStream) throws SQLException {
 2557   		String forcedEncoding = this.connection.getClobCharacterEncoding();
 2558   		
 2559   		String clobEncoding = 
 2560   			(forcedEncoding == null ? this.connection.getEncoding() : forcedEncoding);
 2561   		
 2562   		int maxBytesChar = 2;
 2563   			
 2564   		if (clobEncoding != null) {
 2565   			if (!clobEncoding.equals("UTF-16")) {
 2566   				maxBytesChar = this.connection.getMaxBytesPerChar(clobEncoding);
 2567   				
 2568   				if (maxBytesChar == 1) {
 2569   					maxBytesChar = 2; // for safety
 2570   				}
 2571   			} else {
 2572   				maxBytesChar = 4;
 2573   			}
 2574   		}
 2575   			
 2576   		char[] buf = new char[BLOB_STREAM_READ_BUF_SIZE / maxBytesChar];
 2577   		
 2578   		int numRead = 0;
 2579   
 2580   		int bytesInPacket = 0;
 2581   		int totalBytesRead = 0;
 2582   		int bytesReadAtLastSend = 0;
 2583   		int packetIsFullAt = this.connection.getBlobSendChunkSize();
 2584   		
 2585   		
 2586   		
 2587   		try {
 2588   			packet.clear();
 2589   			packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
 2590   			packet.writeLong(this.serverStatementId);
 2591   			packet.writeInt((parameterIndex));
 2592   
 2593   			boolean readAny = false;
 2594   			
 2595   			while ((numRead = inStream.read(buf)) != -1) {
 2596   				readAny = true;
 2597   			
 2598   				byte[] valueAsBytes = StringUtils.getBytes(buf, null,
 2599   						clobEncoding, this.connection
 2600   								.getServerCharacterEncoding(), 0, numRead,
 2601   						this.connection.parserKnowsUnicode(), getExceptionInterceptor());
 2602   
 2603   				packet.writeBytesNoNull(valueAsBytes, 0, valueAsBytes.length);
 2604   
 2605   				bytesInPacket += valueAsBytes.length;
 2606   				totalBytesRead += valueAsBytes.length;
 2607   
 2608   				if (bytesInPacket >= packetIsFullAt) {
 2609   					bytesReadAtLastSend = totalBytesRead;
 2610   
 2611   					mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet,
 2612   							true, null, 0);
 2613   
 2614   					bytesInPacket = 0;
 2615   					packet.clear();
 2616   					packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
 2617   					packet.writeLong(this.serverStatementId);
 2618   					packet.writeInt((parameterIndex));
 2619   				}
 2620   			}
 2621   
 2622   			if (totalBytesRead != bytesReadAtLastSend) {
 2623   				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
 2624   						null, 0);
 2625   			}
 2626   			
 2627   			if (!readAny) {
 2628   				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
 2629   						null, 0);
 2630   			}
 2631   		} catch (IOException ioEx) {
 2632   			SQLException sqlEx = SQLError.createSQLException(Messages
 2633   					.getString("ServerPreparedStatement.24") //$NON-NLS-1$
 2634   					+ ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 2635   			sqlEx.initCause(ioEx);
 2636   			
 2637   			throw sqlEx;
 2638   		} finally {
 2639   			if (this.connection.getAutoClosePStmtStreams()) {
 2640   				if (inStream != null) {
 2641   					try {
 2642   						inStream.close();
 2643   					} catch (IOException ioEx) {
 2644   						; // ignore
 2645   					}
 2646   				}
 2647   			}
 2648   		}
 2649   	}
 2650   
 2651   	private void storeStream(MysqlIO mysql, int parameterIndex, Buffer packet,
 2652   			InputStream inStream) throws SQLException {
 2653   		byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE];
 2654   
 2655   		int numRead = 0;
 2656   		
 2657   		try {
 2658   			int bytesInPacket = 0;
 2659   			int totalBytesRead = 0;
 2660   			int bytesReadAtLastSend = 0;
 2661   			int packetIsFullAt = this.connection.getBlobSendChunkSize();
 2662   
 2663   			packet.clear();
 2664   			packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
 2665   			packet.writeLong(this.serverStatementId);
 2666   			packet.writeInt((parameterIndex));
 2667   
 2668   			boolean readAny = false;
 2669   			
 2670   			while ((numRead = inStream.read(buf)) != -1) {
 2671   
 2672   				readAny = true;
 2673   				
 2674   				packet.writeBytesNoNull(buf, 0, numRead);
 2675   				bytesInPacket += numRead;
 2676   				totalBytesRead += numRead;
 2677   
 2678   				if (bytesInPacket >= packetIsFullAt) {
 2679   					bytesReadAtLastSend = totalBytesRead;
 2680   
 2681   					mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet,
 2682   							true, null, 0);
 2683   
 2684   					bytesInPacket = 0;
 2685   					packet.clear();
 2686   					packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
 2687   					packet.writeLong(this.serverStatementId);
 2688   					packet.writeInt((parameterIndex));
 2689   				}
 2690   			}
 2691   
 2692   			if (totalBytesRead != bytesReadAtLastSend) {
 2693   				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
 2694   						null, 0);
 2695   			}
 2696   			
 2697   			if (!readAny) {
 2698   				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
 2699   						null, 0);
 2700   			}
 2701   		} catch (IOException ioEx) {
 2702   			SQLException sqlEx = SQLError.createSQLException(Messages
 2703   					.getString("ServerPreparedStatement.25") //$NON-NLS-1$
 2704   					+ ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 2705   			sqlEx.initCause(ioEx);
 2706   			
 2707   			throw sqlEx;
 2708   		} finally {
 2709   			if (this.connection.getAutoClosePStmtStreams()) {
 2710   				if (inStream != null) {
 2711   					try {
 2712   						inStream.close();
 2713   					} catch (IOException ioEx) {
 2714   						; // ignore
 2715   					}
 2716   				}
 2717   			}
 2718   		}
 2719   	}
 2720   
 2721   	/**
 2722   	 * @see java.lang.Object#toString()
 2723   	 */
 2724   	public String toString() {
 2725   		StringBuffer toStringBuf = new StringBuffer();
 2726   
 2727   		toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); //$NON-NLS-1$
 2728   		toStringBuf.append(this.serverStatementId);
 2729   		toStringBuf.append("] - "); //$NON-NLS-1$
 2730   
 2731   		try {
 2732   			toStringBuf.append(asSql());
 2733   		} catch (SQLException sqlEx) {
 2734   			toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); //$NON-NLS-1$
 2735   			toStringBuf.append(sqlEx);
 2736   		}
 2737   
 2738   		return toStringBuf.toString();
 2739   	}
 2740   
 2741   	protected long getServerStatementId() {
 2742   		return serverStatementId;
 2743   	}
 2744   
 2745   	private boolean hasCheckedRewrite = false;
 2746   	private boolean canRewrite = false;
 2747   	
 2748   	public synchronized boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException {
 2749   		if (!hasCheckedRewrite) {
 2750   			this.hasCheckedRewrite = true;
 2751   			this.canRewrite = canRewrite(this.originalSql, isOnDuplicateKeyUpdate(), getLocationOfOnDuplicateKeyUpdate(), 0);
 2752   			// We need to client-side parse this to get the VALUES clause, etc.
 2753   			this.parseInfo = new ParseInfo(this.originalSql, this.connection, this.connection.getMetaData(), this.charEncoding, this.charConverter);
 2754   		}
 2755   		
 2756   		return this.canRewrite;
 2757   	}
 2758   
 2759   	
 2760   	public synchronized boolean canRewriteAsMultivalueInsertStatement() throws SQLException {
 2761   		if (!canRewriteAsMultiValueInsertAtSqlLevel()) {
 2762   			return false;
 2763   		}
 2764   	
 2765   		BindValue[] currentBindValues = null;
 2766   		BindValue[] previousBindValues = null;
 2767   	
 2768   		int nbrCommands = this.batchedArgs.size();
 2769   	
 2770   		// Can't have type changes between sets of bindings for this to work...
 2771   	
 2772   		for (int commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
 2773   			Object arg = this.batchedArgs.get(commandIndex);
 2774   	
 2775   			if (!(arg instanceof String)) {
 2776   	
 2777   				currentBindValues = ((BatchedBindValues) arg).batchedParameterValues;
 2778   	
 2779   				// We need to check types each time, as
 2780   				// the user might have bound different
 2781   				// types in each addBatch()
 2782   	
 2783   				if (previousBindValues != null) {
 2784   					for (int j = 0; j < this.parameterBindings.length; j++) {
 2785   						if (currentBindValues[j].bufferType != previousBindValues[j].bufferType) {
 2786   							return false;
 2787   						}
 2788   					}
 2789   				}
 2790   			}
 2791   		}
 2792   		
 2793   		
 2794   		
 2795   		return true;
 2796   	}
 2797   	
 2798   	private int locationOfOnDuplicateKeyUpdate = -2;
 2799   	
 2800   	protected synchronized int getLocationOfOnDuplicateKeyUpdate() {
 2801   		if (this.locationOfOnDuplicateKeyUpdate == -2) {
 2802   			this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(this.originalSql);
 2803   		}
 2804   		
 2805   		return this.locationOfOnDuplicateKeyUpdate;
 2806   	}
 2807   	
 2808   	protected synchronized boolean isOnDuplicateKeyUpdate() {
 2809   		return getLocationOfOnDuplicateKeyUpdate() != -1;
 2810   	}
 2811   
 2812   	
 2813   	
 2814   
 2815   	/** 
 2816   	 *  Computes the maximum parameter set size, and entire batch size given 
 2817   	 *  the number of arguments in the batch.
 2818   	 */
 2819   	protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) {
 2820   		long sizeOfEntireBatch = 1 + /* com_execute */ + 4 /* stmt id */ + 1 /* flags */ + 4 /* batch count padding */; 
 2821   		long maxSizeOfParameterSet = 0;
 2822   		
 2823   		for (int i = 0; i < numBatchedArgs; i++) {
 2824   			BindValue[] paramArg = ((BatchedBindValues) this.batchedArgs.get(i)).batchedParameterValues;
 2825   	
 2826   			long sizeOfParameterSet = 0;
 2827   			
 2828   			sizeOfParameterSet += (this.parameterCount + 7) / 8; // for isNull
 2829   			
 2830   			sizeOfParameterSet += this.parameterCount * 2; // have to send types
 2831   			
 2832   			for (int j = 0; j < this.parameterBindings.length; j++) {
 2833   				if (!paramArg[j].isNull) {
 2834   	
 2835   					long size = paramArg[j].getBoundLength();
 2836   					
 2837   					if (paramArg[j].isLongData) {
 2838   						if (size != -1) {
 2839   							sizeOfParameterSet += size;
 2840   						}
 2841   					} else {
 2842   						sizeOfParameterSet += size;
 2843   					}
 2844   				}
 2845   			}
 2846   			
 2847   			sizeOfEntireBatch += sizeOfParameterSet;
 2848   			
 2849   			if (sizeOfParameterSet > maxSizeOfParameterSet) {
 2850   				maxSizeOfParameterSet = sizeOfParameterSet;
 2851   			}
 2852   		}	
 2853   		
 2854   		return new long[] {maxSizeOfParameterSet, sizeOfEntireBatch};
 2855   	}
 2856   
 2857   	protected int setOneBatchedParameterSet(
 2858   			java.sql.PreparedStatement batchedStatement, int batchedParamIndex,
 2859   			Object paramSet) throws SQLException {
 2860   		BindValue[] paramArg = ((BatchedBindValues) paramSet).batchedParameterValues;
 2861   	
 2862   		for (int j = 0; j < paramArg.length; j++) {
 2863   			if (paramArg[j].isNull) {
 2864   				batchedStatement.setNull(batchedParamIndex++, Types.NULL);
 2865   			} else {
 2866   				if (paramArg[j].isLongData) {
 2867   					Object value = paramArg[j].value;
 2868   	
 2869   					if (value instanceof InputStream) {
 2870   						batchedStatement.setBinaryStream(batchedParamIndex++,
 2871   								(InputStream) value,
 2872   								(int) paramArg[j].bindLength);
 2873   					} else {
 2874   						batchedStatement.setCharacterStream(
 2875   								batchedParamIndex++, (Reader) value,
 2876   								(int) paramArg[j].bindLength);
 2877   					}
 2878   				} else {
 2879   	
 2880   					switch (paramArg[j].bufferType) {
 2881   	
 2882   					case MysqlDefs.FIELD_TYPE_TINY:
 2883   						batchedStatement.setByte(batchedParamIndex++,
 2884   								paramArg[j].byteBinding);
 2885   						break;
 2886   					case MysqlDefs.FIELD_TYPE_SHORT:
 2887   						batchedStatement.setShort(batchedParamIndex++,
 2888   								paramArg[j].shortBinding);
 2889   						break;
 2890   					case MysqlDefs.FIELD_TYPE_LONG:
 2891   						batchedStatement.setInt(batchedParamIndex++,
 2892   								paramArg[j].intBinding);
 2893   						break;
 2894   					case MysqlDefs.FIELD_TYPE_LONGLONG:
 2895   						batchedStatement.setLong(batchedParamIndex++,
 2896   								paramArg[j].longBinding);
 2897   						break;
 2898   					case MysqlDefs.FIELD_TYPE_FLOAT:
 2899   						batchedStatement.setFloat(batchedParamIndex++,
 2900   								paramArg[j].floatBinding);
 2901   						break;
 2902   					case MysqlDefs.FIELD_TYPE_DOUBLE:
 2903   						batchedStatement.setDouble(batchedParamIndex++,
 2904   								paramArg[j].doubleBinding);
 2905   						break;
 2906   					case MysqlDefs.FIELD_TYPE_TIME:
 2907   						batchedStatement.setTime(batchedParamIndex++,
 2908   								(Time) paramArg[j].value);
 2909   						break;
 2910   					case MysqlDefs.FIELD_TYPE_DATE:
 2911   						batchedStatement.setDate(batchedParamIndex++,
 2912   								(Date) paramArg[j].value);
 2913   						break;
 2914   					case MysqlDefs.FIELD_TYPE_DATETIME:
 2915   					case MysqlDefs.FIELD_TYPE_TIMESTAMP:
 2916   						batchedStatement.setTimestamp(batchedParamIndex++,
 2917   								(Timestamp) paramArg[j].value);
 2918   						break;
 2919   					case MysqlDefs.FIELD_TYPE_VAR_STRING:
 2920   					case MysqlDefs.FIELD_TYPE_STRING:
 2921   					case MysqlDefs.FIELD_TYPE_VARCHAR:
 2922   					case MysqlDefs.FIELD_TYPE_DECIMAL:
 2923   					case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
 2924   						Object value = paramArg[j].value;
 2925   	
 2926   						if (value instanceof byte[]) {
 2927   							batchedStatement.setBytes(batchedParamIndex,
 2928   									(byte[]) value);
 2929   						} else {
 2930   							batchedStatement.setString(batchedParamIndex,
 2931   									(String) value);
 2932   						}
 2933   	
 2934   						// If we ended up here as a multi-statement, we're not working with a server prepared statement
 2935   						
 2936   						if (batchedStatement instanceof ServerPreparedStatement) {
 2937   							BindValue asBound = ((ServerPreparedStatement) batchedStatement)
 2938   									.getBinding(
 2939   											batchedParamIndex,
 2940   											false);
 2941   							asBound.bufferType = paramArg[j].bufferType;
 2942   						}
 2943   	
 2944   						batchedParamIndex++;
 2945   	
 2946   						break;
 2947   					default:
 2948   						throw new IllegalArgumentException(
 2949   								"Unknown type when re-binding parameter into batched statement for parameter index "
 2950   										+ batchedParamIndex);
 2951   					}
 2952   				}
 2953   			}
 2954   		}
 2955   
 2956   		return batchedParamIndex;
 2957   	}
 2958   
 2959   	protected boolean containsOnDuplicateKeyUpdateInSQL() {
 2960   		return this.hasOnDuplicateKeyUpdate;
 2961   	}
 2962   	
 2963   	protected PreparedStatement prepareBatchedInsertSQL(ConnectionImpl localConn, int numBatches) throws SQLException {
 2964   		try {
 2965   			PreparedStatement pstmt = new ServerPreparedStatement(localConn, this.parseInfo.getSqlForBatch(numBatches), this.currentCatalog, this.resultSetConcurrency, this.resultSetType);
 2966   			pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys);
 2967   			
 2968   			return pstmt;
 2969   		} catch (UnsupportedEncodingException e) {
 2970   			SQLException sqlEx = SQLError.createSQLException("Unable to prepare batch statement", SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 2971   			sqlEx.initCause(e);
 2972   			
 2973   			throw sqlEx;
 2974   		}
 2975   		
 2976   	}
 2977   }

Save This Page
Home » MySQL-JDBC-5.1.11 » com.mysql.jdbc » [javadoc | source]