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.ByteArrayInputStream;
   28   import java.io.ByteArrayOutputStream;
   29   import java.io.IOException;
   30   import java.io.InputStream;
   31   import java.io.ObjectOutputStream;
   32   import java.io.Reader;
   33   import java.io.StringReader;
   34   import java.io.UnsupportedEncodingException;
   35   import java.lang.reflect.Constructor;
   36   import java.math.BigDecimal;
   37   import java.math.BigInteger;
   38   import java.net.URL;
   39   import java.nio.ByteBuffer;
   40   import java.nio.CharBuffer;
   41   import java.nio.charset.Charset;
   42   import java.nio.charset.CharsetEncoder;
   43   import java.sql.Array;
   44   import java.sql.Clob;
   45   import java.sql.DatabaseMetaData;
   46   import java.sql.Date;
   47   import java.sql.ParameterMetaData;
   48   import java.sql.Ref;
   49   import java.sql.ResultSet;
   50   import java.sql.SQLException;
   51   import java.sql.Time;
   52   import java.sql.Timestamp;
   53   import java.sql.Types;
   54   import java.text.ParsePosition;
   55   import java.text.SimpleDateFormat;
   56   import java.util.ArrayList;
   57   import java.util.Calendar;
   58   import java.util.Iterator;
   59   import java.util.LinkedList;
   60   import java.util.List;
   61   import java.util.Locale;
   62   import java.util.TimeZone;
   63   
   64   import com.mysql.jdbc.exceptions.DeadlockTimeoutRollbackMarker;
   65   import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
   66   import com.mysql.jdbc.exceptions.MySQLTimeoutException;
   67   import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException;
   68   import com.mysql.jdbc.profiler.ProfilerEvent;
   69   
   70   /**
   71    * A SQL Statement is pre-compiled and stored in a PreparedStatement object.
   72    * This object can then be used to efficiently execute this statement multiple
   73    * times.
   74    * 
   75    * <p>
   76    * <B>Note:</B> The setXXX methods for setting IN parameter values must specify
   77    * types that are compatible with the defined SQL type of the input parameter.
   78    * For instance, if the IN parameter has SQL type Integer, then setInt should be
   79    * used.
   80    * </p>
   81    * 
   82    * <p>
   83    * If arbitrary parameter type conversions are required, then the setObject
   84    * method should be used with a target SQL type.
   85    * </p>
   86    * 
   87    * @author Mark Matthews
   88    * @version $Id: PreparedStatement.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
   89    *          Exp $
   90    * 
   91    * @see java.sql.ResultSet
   92    * @see java.sql.PreparedStatement
   93    */
   94   public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements
   95   		java.sql.PreparedStatement {
   96   	private static final Constructor JDBC_4_PSTMT_2_ARG_CTOR;
   97   	private static final Constructor JDBC_4_PSTMT_3_ARG_CTOR;
   98   	private static final Constructor JDBC_4_PSTMT_4_ARG_CTOR;
   99   	
  100   	static {
  101   		if (Util.isJdbc4()) {
  102   			try {
  103   				JDBC_4_PSTMT_2_ARG_CTOR = Class.forName(
  104   						"com.mysql.jdbc.JDBC4PreparedStatement")
  105   						.getConstructor(
  106   								new Class[] { ConnectionImpl.class, String.class });
  107   				JDBC_4_PSTMT_3_ARG_CTOR = Class.forName(
  108   						"com.mysql.jdbc.JDBC4PreparedStatement")
  109   						.getConstructor(
  110   								new Class[] { ConnectionImpl.class, String.class,
  111   										String.class });
  112   				JDBC_4_PSTMT_4_ARG_CTOR = Class.forName(
  113   						"com.mysql.jdbc.JDBC4PreparedStatement")
  114   						.getConstructor(
  115   								new Class[] { ConnectionImpl.class, String.class,
  116   										String.class, ParseInfo.class });
  117   			} catch (SecurityException e) {
  118   				throw new RuntimeException(e);
  119   			} catch (NoSuchMethodException e) {
  120   				throw new RuntimeException(e);
  121   			} catch (ClassNotFoundException e) {
  122   				throw new RuntimeException(e);
  123   			}
  124   		} else {
  125   			JDBC_4_PSTMT_2_ARG_CTOR = null;
  126   			JDBC_4_PSTMT_3_ARG_CTOR = null;
  127   			JDBC_4_PSTMT_4_ARG_CTOR = null;
  128   		}
  129   	}
  130   	
  131   	class BatchParams {
  132   		boolean[] isNull = null;
  133   
  134   		boolean[] isStream = null;
  135   
  136   		InputStream[] parameterStreams = null;
  137   
  138   		byte[][] parameterStrings = null;
  139   
  140   		int[] streamLengths = null;
  141   
  142   		BatchParams(byte[][] strings, InputStream[] streams,
  143   				boolean[] isStreamFlags, int[] lengths, boolean[] isNullFlags) {
  144   			//
  145   			// Make copies
  146   			//
  147   			this.parameterStrings = new byte[strings.length][];
  148   			this.parameterStreams = new InputStream[streams.length];
  149   			this.isStream = new boolean[isStreamFlags.length];
  150   			this.streamLengths = new int[lengths.length];
  151   			this.isNull = new boolean[isNullFlags.length];
  152   			System.arraycopy(strings, 0, this.parameterStrings, 0,
  153   					strings.length);
  154   			System.arraycopy(streams, 0, this.parameterStreams, 0,
  155   					streams.length);
  156   			System.arraycopy(isStreamFlags, 0, this.isStream, 0,
  157   					isStreamFlags.length);
  158   			System.arraycopy(lengths, 0, this.streamLengths, 0, lengths.length);
  159   			System
  160   					.arraycopy(isNullFlags, 0, this.isNull, 0,
  161   							isNullFlags.length);
  162   		}
  163   	}
  164   
  165   	class EndPoint {
  166   		int begin;
  167   
  168   		int end;
  169   
  170   		EndPoint(int b, int e) {
  171   			this.begin = b;
  172   			this.end = e;
  173   		}
  174   	}
  175   
  176   	class ParseInfo {
  177   		char firstStmtChar = 0;
  178   
  179   		boolean foundLimitClause = false;
  180   
  181   		boolean foundLoadData = false;
  182   
  183   		long lastUsed = 0;
  184   
  185   		int statementLength = 0;
  186   
  187   		int statementStartPos = 0;
  188   
  189   		boolean canRewriteAsMultiValueInsert = false;
  190   		
  191   		byte[][] staticSql = null;
  192   
  193   		boolean isOnDuplicateKeyUpdate = false;
  194   		
  195   		int locationOfOnDuplicateKeyUpdate = -1;
  196   		
  197   		String valuesClause;
  198   		
  199   		boolean parametersInDuplicateKeyClause = false;
  200   		
  201   		/**
  202   		 * Represents the "parsed" state of a client-side
  203   		 * prepared statement, with the statement broken up into
  204   		 * it's static and dynamic (where parameters are bound) 
  205   		 * parts.
  206   		 */
  207   		ParseInfo(String sql, ConnectionImpl conn,
  208   				java.sql.DatabaseMetaData dbmd, String encoding,
  209   				SingleByteCharsetConverter converter) throws SQLException {
  210   			this(sql, conn, dbmd, encoding, converter, true);
  211   		}
  212   		
  213   		public ParseInfo(String sql, ConnectionImpl conn,
  214   				java.sql.DatabaseMetaData dbmd, String encoding,
  215   				SingleByteCharsetConverter converter, boolean buildRewriteInfo) throws SQLException {
  216   			try {
  217   				if (sql == null) {
  218   					throw SQLError.createSQLException(Messages
  219   							.getString("PreparedStatement.61"), //$NON-NLS-1$
  220   							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  221   				}
  222   
  223   				this.locationOfOnDuplicateKeyUpdate = getOnDuplicateKeyLocation(sql);
  224   				this.isOnDuplicateKeyUpdate = this.locationOfOnDuplicateKeyUpdate != -1;
  225   				
  226   				this.lastUsed = System.currentTimeMillis();
  227   
  228   				String quotedIdentifierString = dbmd.getIdentifierQuoteString();
  229   
  230   				char quotedIdentifierChar = 0;
  231   
  232   				if ((quotedIdentifierString != null)
  233   						&& !quotedIdentifierString.equals(" ") //$NON-NLS-1$
  234   						&& (quotedIdentifierString.length() > 0)) {
  235   					quotedIdentifierChar = quotedIdentifierString.charAt(0);
  236   				}
  237   
  238   				this.statementLength = sql.length();
  239   
  240   				ArrayList endpointList = new ArrayList();
  241   				boolean inQuotes = false;
  242   				char quoteChar = 0;
  243   				boolean inQuotedId = false;
  244   				int lastParmEnd = 0;
  245   				int i;
  246   
  247   				int stopLookingForLimitClause = this.statementLength - 5;
  248   
  249   				this.foundLimitClause = false;
  250   
  251   				boolean noBackslashEscapes = connection.isNoBackslashEscapesSet();
  252   
  253   				// we're not trying to be real pedantic here, but we'd like to 
  254   				// skip comments at the beginning of statements, as frameworks
  255   				// such as Hibernate use them to aid in debugging
  256   
  257   				statementStartPos = findStartOfStatement(sql);
  258   
  259   				for (i = statementStartPos; i < this.statementLength; ++i) {
  260   					char c = sql.charAt(i);
  261   
  262   					if ((this.firstStmtChar == 0) && Character.isLetter(c)) {
  263   						// Determine what kind of statement we're doing (_S_elect,
  264   						// _I_nsert, etc.)
  265   						this.firstStmtChar = Character.toUpperCase(c);
  266   					}
  267   
  268   					if (!noBackslashEscapes &&
  269   							c == '\\' && i < (this.statementLength - 1)) {
  270   						i++;
  271   						continue; // next character is escaped
  272   					}
  273   
  274   					// are we in a quoted identifier?
  275   					// (only valid when the id is not inside a 'string')
  276   					if (!inQuotes && (quotedIdentifierChar != 0)
  277   							&& (c == quotedIdentifierChar)) {
  278   						inQuotedId = !inQuotedId;
  279   					} else if (!inQuotedId) {
  280   						//	only respect quotes when not in a quoted identifier
  281   
  282   						if (inQuotes) {
  283   							if (((c == '\'') || (c == '"')) && c == quoteChar) {
  284   								if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) {
  285   									i++; 
  286   									continue; // inline quote escape
  287   								}
  288   
  289   								inQuotes = !inQuotes;
  290   								quoteChar = 0;
  291   							} else if (((c == '\'') || (c == '"')) && c == quoteChar) {
  292   								inQuotes = !inQuotes;
  293   								quoteChar = 0;
  294   							}
  295   						} else {
  296   							if (c == '#'
  297   								|| (c == '-' && (i + 1) < this.statementLength && sql
  298   										.charAt(i + 1) == '-')) {
  299   								// run out to end of statement, or newline,
  300   								// whichever comes first
  301   								int endOfStmt = this.statementLength - 1;
  302   
  303   								for (; i < endOfStmt; i++) {
  304   									c = sql.charAt(i);
  305   
  306   									if (c == '\r' || c == '\n') {
  307   										break;
  308   									}
  309   								}
  310   
  311   								continue;
  312   							} else if (c == '/' && (i + 1) < this.statementLength) {
  313   								// Comment?
  314   								char cNext = sql.charAt(i + 1);
  315   
  316   								if (cNext == '*') {
  317   									i+= 2;
  318   
  319   									for (int j = i; j < this.statementLength; j++) {
  320   										i++;
  321   										cNext = sql.charAt(j);
  322   
  323   										if (cNext == '*' && (j + 1) < this.statementLength) {
  324   											if (sql.charAt(j + 1) == '/') {
  325   												i++;
  326   
  327   												if (i < this.statementLength) {
  328   													c = sql.charAt(i);
  329   												}
  330   
  331   												break; // comment done
  332   											}
  333   										}
  334   									}
  335   								}
  336   							} else if ((c == '\'') || (c == '"')) {
  337   								inQuotes = true;
  338   								quoteChar = c;
  339   							}
  340   						}
  341   					}
  342   
  343   					if ((c == '?') && !inQuotes && !inQuotedId) {
  344   						endpointList.add(new int[] { lastParmEnd, i });
  345   						lastParmEnd = i + 1;
  346   						
  347   						if (isOnDuplicateKeyUpdate && i > locationOfOnDuplicateKeyUpdate) {
  348   							parametersInDuplicateKeyClause = true;
  349   						}
  350   					}
  351   
  352   					if (!inQuotes && (i < stopLookingForLimitClause)) {
  353   						if ((c == 'L') || (c == 'l')) {
  354   							char posI1 = sql.charAt(i + 1);
  355   
  356   							if ((posI1 == 'I') || (posI1 == 'i')) {
  357   								char posM = sql.charAt(i + 2);
  358   
  359   								if ((posM == 'M') || (posM == 'm')) {
  360   									char posI2 = sql.charAt(i + 3);
  361   
  362   									if ((posI2 == 'I') || (posI2 == 'i')) {
  363   										char posT = sql.charAt(i + 4);
  364   
  365   										if ((posT == 'T') || (posT == 't')) {
  366   											foundLimitClause = true;
  367   										}
  368   									}
  369   								}
  370   							}
  371   						}
  372   					}
  373   				}
  374   
  375   				if (this.firstStmtChar == 'L') {
  376   					if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
  377   						this.foundLoadData = true;
  378   					} else {
  379   						this.foundLoadData = false;
  380   					}
  381   				} else {
  382   					this.foundLoadData = false;
  383   				}
  384   
  385   				endpointList.add(new int[] { lastParmEnd, this.statementLength });
  386   				this.staticSql = new byte[endpointList.size()][];
  387   				char[] asCharArray = sql.toCharArray();
  388   
  389   				for (i = 0; i < this.staticSql.length; i++) {
  390   					int[] ep = (int[]) endpointList.get(i);
  391   					int end = ep[1];
  392   					int begin = ep[0];
  393   					int len = end - begin;
  394   
  395   					if (this.foundLoadData) {
  396   						String temp = new String(asCharArray, begin, len);
  397   						this.staticSql[i] = temp.getBytes();
  398   					} else if (encoding == null) {
  399   						byte[] buf = new byte[len];
  400   
  401   						for (int j = 0; j < len; j++) {
  402   							buf[j] = (byte) sql.charAt(begin + j);
  403   						}
  404   
  405   						this.staticSql[i] = buf;
  406   					} else {
  407   						if (converter != null) {
  408   							this.staticSql[i] = StringUtils.getBytes(sql,
  409   									converter, encoding, connection
  410   									.getServerCharacterEncoding(), begin,
  411   									len, connection.parserKnowsUnicode(), getExceptionInterceptor());
  412   						} else {
  413   							String temp = new String(asCharArray, begin, len);
  414   
  415   							this.staticSql[i] = StringUtils.getBytes(temp,
  416   									encoding, connection
  417   									.getServerCharacterEncoding(),
  418   									connection.parserKnowsUnicode(), conn, getExceptionInterceptor());
  419   						}
  420   					}
  421   				}
  422   			} catch (StringIndexOutOfBoundsException oobEx) {
  423   				SQLException sqlEx = new SQLException("Parse error for " + sql);
  424   				sqlEx.initCause(oobEx);
  425   
  426   				throw sqlEx;
  427   			}
  428   			
  429   			
  430   			if (buildRewriteInfo) {
  431   				this.canRewriteAsMultiValueInsert = PreparedStatement
  432   						.canRewrite(sql, this.isOnDuplicateKeyUpdate,
  433   								this.locationOfOnDuplicateKeyUpdate,
  434   								this.statementStartPos) && !this.parametersInDuplicateKeyClause;
  435   
  436   				if (this.canRewriteAsMultiValueInsert
  437   						&& conn.getRewriteBatchedStatements()) {
  438   					buildRewriteBatchedParams(sql, conn, dbmd, encoding,
  439   							converter);
  440   				}
  441   			}
  442   		}
  443   
  444   		private ParseInfo batchHead;
  445   
  446   		private ParseInfo batchValues;
  447   
  448   		private ParseInfo batchODKUClause;
  449   
  450   		private void buildRewriteBatchedParams(String sql, ConnectionImpl conn,
  451   				DatabaseMetaData metadata, String encoding,
  452   				SingleByteCharsetConverter converter) throws SQLException {
  453   			this.valuesClause = extractValuesClause(sql);
  454   			String odkuClause = isOnDuplicateKeyUpdate ? sql
  455   					.substring(locationOfOnDuplicateKeyUpdate) : null;
  456   
  457   			String headSql = null;
  458   
  459   			if (isOnDuplicateKeyUpdate) {
  460   				headSql = sql.substring(0, locationOfOnDuplicateKeyUpdate);
  461   			} else {
  462   				headSql = sql;
  463   			}
  464   
  465   			this.batchHead = new ParseInfo(headSql, conn, metadata, encoding,
  466   					converter, false);
  467   			this.batchValues = new ParseInfo("," + this.valuesClause, conn,
  468   					metadata, encoding, converter, false);
  469   			this.batchODKUClause = null;
  470   
  471   			if (odkuClause != null && odkuClause.length() > 0) {
  472   				this.batchODKUClause = new ParseInfo("," + this.valuesClause
  473   						+ " " + odkuClause, conn, metadata, encoding,
  474   						converter, false);
  475   			}
  476   		}
  477   
  478   		private String extractValuesClause(String sql) throws SQLException {
  479   			String quoteCharStr = connection.getMetaData()
  480   					.getIdentifierQuoteString();
  481   
  482   			int indexOfValues = -1;
  483   			int valuesSearchStart = statementStartPos;
  484   
  485   			while (indexOfValues == -1) {
  486   				if (quoteCharStr.length() > 0) {
  487   					indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(
  488   							valuesSearchStart,
  489   							originalSql, "VALUES", quoteCharStr.charAt(0), false);
  490   				} else {
  491   					indexOfValues = StringUtils.indexOfIgnoreCase(valuesSearchStart, 
  492   							originalSql,
  493   							"VALUES");
  494   				}
  495   				
  496   				if (indexOfValues > 0) {
  497   					/* check if the char immediately preceding VALUES may be part of the table name */
  498   					char c = originalSql.charAt(indexOfValues - 1);
  499   					if(!(Character.isWhitespace(c) || c == ')' || c == '`')){
  500   						valuesSearchStart = indexOfValues + 6;
  501   						indexOfValues = -1;
  502   					} else {
  503   						/* check if the char immediately following VALUES may be whitespace or open parenthesis */
  504   						c = originalSql.charAt(indexOfValues + 6);
  505   						if(!(Character.isWhitespace(c) || c == '(')){
  506   							valuesSearchStart = indexOfValues + 6;
  507   							indexOfValues = -1;
  508   						}
  509   					}
  510   				} else {
  511   					break;
  512   				}
  513   			}
  514   
  515   			if (indexOfValues == -1) {
  516   				return null;
  517   			}
  518   
  519   			int indexOfFirstParen = sql.indexOf('(', indexOfValues + 6);
  520   
  521   			if (indexOfFirstParen == -1) {
  522   				return null;
  523   			}
  524   
  525   			int endOfValuesClause = sql.lastIndexOf(')');
  526   
  527   			if (endOfValuesClause == -1) {
  528   				return null;
  529   			}
  530   
  531   			if (isOnDuplicateKeyUpdate) {
  532   				endOfValuesClause = this.locationOfOnDuplicateKeyUpdate - 1;
  533   			}
  534   
  535   			return sql.substring(indexOfFirstParen, endOfValuesClause + 1);
  536   		}
  537   
  538   		/**
  539   		 * Returns a ParseInfo for a multi-value INSERT for a batch of size numBatch (without parsing!).
  540   		 */
  541   		synchronized ParseInfo getParseInfoForBatch(int numBatch) {
  542   			AppendingBatchVisitor apv = new AppendingBatchVisitor();
  543   			buildInfoForBatch(numBatch, apv);
  544   
  545   			ParseInfo batchParseInfo = new ParseInfo(apv.getStaticSqlStrings(),
  546   					this.firstStmtChar, this.foundLimitClause,
  547   					this.foundLoadData, this.isOnDuplicateKeyUpdate,
  548   					this.locationOfOnDuplicateKeyUpdate, this.statementLength,
  549   					this.statementStartPos);
  550   
  551   			return batchParseInfo;
  552   		}
  553   		
  554   		/**
  555   		 * Returns a preparable SQL string for the number of batched parameters, used by server-side prepared statements
  556   		 * when re-writing batch INSERTs.
  557   		 */
  558   		
  559   		String getSqlForBatch(int numBatch) throws UnsupportedEncodingException {
  560   			ParseInfo batchInfo = getParseInfoForBatch(numBatch);
  561   			
  562   			return getSqlForBatch(batchInfo);
  563   		}
  564   		
  565   		/** 
  566   		 * Used for filling in the SQL for getPreparedSql() - for debugging 
  567   		 */
  568   		String getSqlForBatch(ParseInfo batchInfo) throws UnsupportedEncodingException {
  569   			int size = 0;
  570   			final byte[][] sqlStrings = batchInfo.staticSql;
  571   			final int sqlStringsLength = sqlStrings.length;
  572   			
  573   			for (int i = 0; i < sqlStringsLength; i++) {
  574   				size += sqlStrings[i].length;
  575   				size++; // for the '?'
  576   			}
  577   			
  578   			StringBuffer buf = new StringBuffer(size);
  579   		
  580   			for (int i = 0; i < sqlStringsLength - 1; i++) {
  581   				buf.append(new String(sqlStrings[i], charEncoding));
  582   				buf.append("?");
  583   			}
  584   			
  585   			buf.append(new String(sqlStrings[sqlStringsLength - 1]));
  586   			
  587   			return buf.toString();
  588   		}
  589   
  590   		/**
  591   		 * Builds a ParseInfo for the given batch size, without parsing. We use
  592   		 * a visitor pattern here, because the if {}s make computing a size for the
  593   		 * resultant byte[][] make this too complex, and we don't necessarily want to 
  594   		 * use a List for this, because the size can be dynamic, and thus we'll not be
  595   		 * able to guess a good initial size for an array-based list, and it's not
  596   		 * efficient to convert a LinkedList to an array.
  597   		 */
  598   		private void buildInfoForBatch(int numBatch, BatchVisitor visitor) {
  599   			final byte[][] headStaticSql = this.batchHead.staticSql;
  600   			final int headStaticSqlLength = headStaticSql.length;
  601   
  602   			if (headStaticSqlLength > 1) {
  603   				for (int i = 0; i < headStaticSqlLength - 1; i++) {
  604   					visitor.append(headStaticSql[i]).increment();
  605   				}
  606   			}
  607   
  608   			// merge end of head, with beginning of a value clause
  609   			byte[] endOfHead = headStaticSql[headStaticSqlLength - 1];
  610   			final byte[][] valuesStaticSql = this.batchValues.staticSql;
  611   			byte[] beginOfValues = valuesStaticSql[0];
  612   
  613   			visitor.merge(endOfHead, beginOfValues).increment();
  614   
  615   			int numValueRepeats = numBatch - 1; // first one is in the "head"
  616   
  617   			if (this.batchODKUClause != null) {
  618   				numValueRepeats--; // Last one is in the ODKU clause
  619   			}
  620   
  621   			final int valuesStaticSqlLength = valuesStaticSql.length;
  622   			byte[] endOfValues = valuesStaticSql[valuesStaticSqlLength - 1];
  623   
  624   			for (int i = 0; i < numValueRepeats; i++) {
  625   				for (int j = 1; j < valuesStaticSqlLength - 1; j++) {
  626   					visitor.append(valuesStaticSql[j]).increment();
  627   				}
  628   				visitor.merge(endOfValues, beginOfValues).increment();
  629   			}
  630   
  631   			if (this.batchODKUClause != null) {
  632   				final byte[][] batchOdkuStaticSql = this.batchODKUClause.staticSql;
  633   				byte[] beginOfOdku = batchOdkuStaticSql[0];
  634   				visitor.decrement().merge(endOfValues, beginOfOdku).increment();
  635   
  636   				final int batchOdkuStaticSqlLength = batchOdkuStaticSql.length;
  637   				
  638   				if (numBatch > 1) {
  639   					for (int i = 1; i < batchOdkuStaticSqlLength; i++) {
  640   						visitor.append(batchOdkuStaticSql[i])
  641   								.increment();
  642   					}
  643   				} else {
  644   					visitor.decrement().append(batchOdkuStaticSql[(batchOdkuStaticSqlLength - 1)]);
  645   				}
  646   			} else {
  647   				// Everything after the values clause, but not ODKU, which today is nothing
  648   				// but a syntax error, but we should still not mangle the SQL!
  649   				visitor.decrement().append(this.staticSql[this.staticSql.length - 1]);
  650   			}
  651   		}
  652   
  653   		private ParseInfo(byte[][] staticSql, char firstStmtChar,
  654   				boolean foundLimitClause, boolean foundLoadData,
  655   				boolean isOnDuplicateKeyUpdate,
  656   				int locationOfOnDuplicateKeyUpdate, int statementLength,
  657   				int statementStartPos) {
  658   			this.firstStmtChar = firstStmtChar;
  659   			this.foundLimitClause = foundLimitClause;
  660   			this.foundLoadData = foundLoadData;
  661   			this.isOnDuplicateKeyUpdate = isOnDuplicateKeyUpdate;
  662   			this.locationOfOnDuplicateKeyUpdate = locationOfOnDuplicateKeyUpdate;
  663   			this.statementLength = statementLength;
  664   			this.statementStartPos = statementStartPos;
  665   			this.staticSql = staticSql;
  666   		}
  667   	}
  668   
  669   	interface BatchVisitor {
  670   		abstract BatchVisitor increment();
  671   		
  672   		abstract BatchVisitor decrement();
  673   		
  674   		abstract BatchVisitor append(byte[] values);
  675   		
  676   		abstract BatchVisitor merge(byte[] begin, byte[] end);
  677   	}
  678   	
  679   	class AppendingBatchVisitor implements BatchVisitor {
  680   		LinkedList statementComponents = new LinkedList();
  681   		
  682   		public BatchVisitor append(byte[] values) {
  683   			statementComponents.addLast(values);
  684   
  685   			return this;
  686   		}
  687   
  688   		public BatchVisitor increment() {
  689   			// no-op
  690   			return this;
  691   		}
  692   
  693   		public BatchVisitor decrement() {
  694   			statementComponents.removeLast();
  695   			
  696   			return this;
  697   		}
  698   
  699   		public BatchVisitor merge(byte[] front, byte[] back) {
  700   			int mergedLength = front.length + back.length;
  701   			byte[] merged = new byte[mergedLength];
  702   			System.arraycopy(front, 0, merged, 0, front.length);
  703   			System.arraycopy(back, 0, merged, front.length, back.length);
  704   			statementComponents.addLast(merged);
  705   			return this;
  706   		}
  707   		
  708   		public byte[][] getStaticSqlStrings() {
  709   			byte[][] asBytes = new byte[this.statementComponents.size()][];
  710   			this.statementComponents.toArray(asBytes);
  711   			
  712   			return asBytes;
  713   		}
  714   		
  715   		public String toString() {
  716   			StringBuffer buf = new StringBuffer();
  717   			Iterator iter = this.statementComponents.iterator();
  718   			while (iter.hasNext()) {
  719   				buf.append(new String((byte[]) iter.next()));
  720   			}
  721   			
  722   			return buf.toString();
  723   		}
  724   		
  725   	}
  726   	
  727   	private final static byte[] HEX_DIGITS = new byte[] { (byte) '0',
  728   			(byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
  729   			(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
  730   			(byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };
  731   
  732   	/**
  733   	 * Reads length bytes from reader into buf. Blocks until enough input is
  734   	 * available
  735   	 * 
  736   	 * @param reader
  737   	 *            DOCUMENT ME!
  738   	 * @param buf
  739   	 *            DOCUMENT ME!
  740   	 * @param length
  741   	 *            DOCUMENT ME!
  742   	 * 
  743   	 * @return DOCUMENT ME!
  744   	 * 
  745   	 * @throws IOException
  746   	 *             DOCUMENT ME!
  747   	 */
  748   	protected static int readFully(Reader reader, char[] buf, int length)
  749   			throws IOException {
  750   		int numCharsRead = 0;
  751   
  752   		while (numCharsRead < length) {
  753   			int count = reader.read(buf, numCharsRead, length - numCharsRead);
  754   
  755   			if (count < 0) {
  756   				break;
  757   			}
  758   
  759   			numCharsRead += count;
  760   		}
  761   
  762   		return numCharsRead;
  763   	}
  764   
  765   	/**
  766   	 * Does the batch (if any) contain "plain" statements added by
  767   	 * Statement.addBatch(String)?
  768   	 * 
  769   	 * If so, we can't re-write it to use multi-value or multi-queries.
  770   	 */
  771   	protected boolean batchHasPlainStatements = false;
  772   
  773   	private java.sql.DatabaseMetaData dbmd = null;
  774   
  775   	/**
  776   	 * What is the first character of the prepared statement (used to check for
  777   	 * SELECT vs. INSERT/UPDATE/DELETE)
  778   	 */
  779   	protected char firstCharOfStmt = 0;
  780   
  781   	/** Does the SQL for this statement contain a 'limit' clause? */
  782   	protected boolean hasLimitClause = false;
  783   	
  784   	/** Is this query a LOAD DATA query? */
  785   	protected boolean isLoadDataQuery = false;
  786   
  787   	private boolean[] isNull = null;
  788   
  789   	private boolean[] isStream = null;
  790   
  791   	protected int numberOfExecutions = 0;
  792   
  793   	/** The SQL that was passed in to 'prepare' */
  794   	protected String originalSql = null;
  795   
  796   	/** The number of parameters in this PreparedStatement */
  797   	protected int parameterCount;
  798   
  799   	protected MysqlParameterMetadata parameterMetaData;
  800   
  801   	private InputStream[] parameterStreams = null;
  802   
  803   	private byte[][] parameterValues = null;
  804   
  805   	/**
  806   	 * Only used by statement interceptors at the moment to
  807   	 * provide introspection of bound values
  808   	 */
  809   	protected int[] parameterTypes = null;
  810   	
  811   	protected ParseInfo parseInfo;
  812   
  813   	private java.sql.ResultSetMetaData pstmtResultMetaData;
  814   
  815   	private byte[][] staticSqlStrings = null;
  816   
  817   	private byte[] streamConvertBuf = new byte[4096];
  818   
  819   	private int[] streamLengths = null;
  820   
  821   	private SimpleDateFormat tsdf = null;
  822   
  823   	/**
  824   	 * Are we using a version of MySQL where we can use 'true' boolean values?
  825   	 */
  826   	protected boolean useTrueBoolean = false;
  827   
  828   	protected boolean usingAnsiMode;
  829   
  830   	protected String batchedValuesClause;
  831   
  832   	private boolean doPingInstead;
  833   	private SimpleDateFormat ddf;
  834   	private SimpleDateFormat tdf;
  835   	
  836   	private boolean compensateForOnDuplicateKeyUpdate = false;
  837   	
  838   	/** Charset encoder used to escape if needed, such as Yen sign in SJIS */
  839   	private CharsetEncoder charsetEncoder;
  840   	
  841   	/** Command index of currently executing batch command. */
  842   	private int batchCommandIndex = -1;
  843   	
  844   	/**
  845   	 * Creates a prepared statement instance -- We need to provide factory-style
  846   	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  847   	 * otherwise the class verifier complains when it tries to load JDBC4-only
  848   	 * interface classes that are present in JDBC4 method signatures.
  849   	 */
  850   
  851   	protected static PreparedStatement getInstance(ConnectionImpl conn,
  852   			String catalog) throws SQLException {
  853   		if (!Util.isJdbc4()) {
  854   			return new PreparedStatement(conn, catalog);
  855   		}
  856   
  857   		return (PreparedStatement) Util.handleNewInstance(
  858   				JDBC_4_PSTMT_2_ARG_CTOR, new Object[] { conn, catalog }, conn.getExceptionInterceptor());
  859   	}
  860   
  861   	/**
  862   	 * Creates a prepared statement instance -- We need to provide factory-style
  863   	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  864   	 * otherwise the class verifier complains when it tries to load JDBC4-only
  865   	 * interface classes that are present in JDBC4 method signatures.
  866   	 */
  867   
  868   	protected static PreparedStatement getInstance(ConnectionImpl conn, String sql,
  869   			String catalog) throws SQLException {
  870   		if (!Util.isJdbc4()) {
  871   			return new PreparedStatement(conn, sql, catalog);
  872   		}
  873   
  874   		return (PreparedStatement) Util.handleNewInstance(
  875   				JDBC_4_PSTMT_3_ARG_CTOR, new Object[] { conn, sql, catalog }, conn.getExceptionInterceptor());
  876   	}
  877   
  878   	/**
  879   	 * Creates a prepared statement instance -- We need to provide factory-style
  880   	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
  881   	 * otherwise the class verifier complains when it tries to load JDBC4-only
  882   	 * interface classes that are present in JDBC4 method signatures.
  883   	 */
  884   
  885   	protected static PreparedStatement getInstance(ConnectionImpl conn, String sql,
  886   			String catalog, ParseInfo cachedParseInfo) throws SQLException {
  887   		if (!Util.isJdbc4()) {
  888   			return new PreparedStatement(conn, sql, catalog, cachedParseInfo);
  889   		}
  890   
  891   		return (PreparedStatement) Util.handleNewInstance(
  892   				JDBC_4_PSTMT_4_ARG_CTOR, new Object[] { conn, sql, catalog,
  893   						cachedParseInfo }, conn.getExceptionInterceptor());
  894   	}
  895   	
  896   	/**
  897   	 * Constructor used by server-side prepared statements
  898   	 * 
  899   	 * @param conn
  900   	 *            the connection that created us
  901   	 * @param catalog
  902   	 *            the catalog in use when we were created
  903   	 * 
  904   	 * @throws SQLException
  905   	 *             if an error occurs
  906   	 */
  907   	public PreparedStatement(ConnectionImpl conn, String catalog)
  908   			throws SQLException {
  909   		super(conn, catalog);
  910   		
  911   		this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
  912   	}
  913   
  914   	/**
  915   	 * Constructor for the PreparedStatement class.
  916   	 * 
  917   	 * @param conn
  918   	 *            the connection creating this statement
  919   	 * @param sql
  920   	 *            the SQL for this statement
  921   	 * @param catalog
  922   	 *            the catalog/database this statement should be issued against
  923   	 * 
  924   	 * @throws SQLException
  925   	 *             if a database error occurs.
  926   	 */
  927   	public PreparedStatement(ConnectionImpl conn, String sql, String catalog)
  928   			throws SQLException {
  929   		super(conn, catalog);
  930   
  931   		if (sql == null) {
  932   			throw SQLError.createSQLException(Messages.getString("PreparedStatement.0"), //$NON-NLS-1$
  933   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  934   		}
  935   
  936   		this.originalSql = sql;
  937   
  938   		if (this.originalSql.startsWith(PING_MARKER)) {
  939   			this.doPingInstead = true;
  940   		} else {
  941   			this.doPingInstead = false;
  942   		}
  943   		
  944   		this.dbmd = this.connection.getMetaData();
  945   
  946   		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
  947   
  948   		this.parseInfo = new ParseInfo(sql, this.connection, this.dbmd,
  949   				this.charEncoding, this.charConverter);
  950   
  951   		initializeFromParseInfo();
  952   		
  953   		this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
  954   		
  955   		if (conn.getRequiresEscapingEncoder())
  956   			charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder();
  957   	}
  958   
  959   	/**
  960   	 * Creates a new PreparedStatement object.
  961   	 * 
  962   	 * @param conn
  963   	 *            the connection creating this statement
  964   	 * @param sql
  965   	 *            the SQL for this statement
  966   	 * @param catalog
  967   	 *            the catalog/database this statement should be issued against
  968   	 * @param cachedParseInfo
  969   	 *            already created parseInfo.
  970   	 * 
  971   	 * @throws SQLException
  972   	 *             DOCUMENT ME!
  973   	 */
  974   	public PreparedStatement(ConnectionImpl conn, String sql, String catalog,
  975   			ParseInfo cachedParseInfo) throws SQLException {
  976   		super(conn, catalog);
  977   
  978   		if (sql == null) {
  979   			throw SQLError.createSQLException(Messages.getString("PreparedStatement.1"), //$NON-NLS-1$
  980   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
  981   		}
  982   
  983   		this.originalSql = sql;
  984   
  985   		this.dbmd = this.connection.getMetaData();
  986   
  987   		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
  988   
  989   		this.parseInfo = cachedParseInfo;
  990   
  991   		this.usingAnsiMode = !this.connection.useAnsiQuotedIdentifiers();
  992   
  993   		initializeFromParseInfo();
  994   		
  995   		this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts();
  996   
  997   		if (conn.getRequiresEscapingEncoder())
  998   			charsetEncoder = Charset.forName(conn.getEncoding()).newEncoder();
  999   	}
 1000   
 1001   	/**
 1002   	 * JDBC 2.0 Add a set of parameters to the batch.
 1003   	 * 
 1004   	 * @exception SQLException
 1005   	 *                if a database-access error occurs.
 1006   	 * 
 1007   	 * @see StatementImpl#addBatch
 1008   	 */
 1009   	public void addBatch() throws SQLException {
 1010   		if (this.batchedArgs == null) {
 1011   			this.batchedArgs = new ArrayList();
 1012   		}
 1013   
 1014   		for (int i = 0; i < this.parameterValues.length; i++) {
 1015   			checkAllParametersSet(this.parameterValues[i],
 1016   					this.parameterStreams[i], i);
 1017   		}
 1018   		
 1019   		this.batchedArgs.add(new BatchParams(this.parameterValues,
 1020   				this.parameterStreams, this.isStream, this.streamLengths,
 1021   				this.isNull));
 1022   	}
 1023   
 1024   	public synchronized void addBatch(String sql) throws SQLException {
 1025   		this.batchHasPlainStatements = true;
 1026   
 1027   		super.addBatch(sql);
 1028   	}
 1029   
 1030   	protected String asSql() throws SQLException {
 1031   		return asSql(false);
 1032   	}
 1033   
 1034   	protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
 1035   		if (this.isClosed) {
 1036   			return "statement has been closed, no further internal information available";
 1037   		}
 1038   		
 1039   		StringBuffer buf = new StringBuffer();
 1040   
 1041   		try {
 1042   			int realParameterCount = this.parameterCount + getParameterIndexOffset();
 1043   			Object batchArg = null;
 1044   			if (batchCommandIndex != -1)
 1045   				batchArg = batchedArgs.get(batchCommandIndex);
 1046   			
 1047   			for (int i = 0; i < realParameterCount; ++i) {
 1048   				if (this.charEncoding != null) {
 1049   					buf.append(new String(this.staticSqlStrings[i],
 1050   							this.charEncoding));
 1051   				} else {
 1052   					buf.append(new String(this.staticSqlStrings[i]));
 1053   				}
 1054   
 1055   				byte val[] = null;
 1056   				if (batchArg != null && batchArg instanceof String) {
 1057   					buf.append((String)batchArg);
 1058   					continue;
 1059   				}
 1060   				if (batchCommandIndex == -1)
 1061   					val = parameterValues[i];
 1062   				else
 1063   					val = ((BatchParams)batchArg).parameterStrings[i]; 
 1064   				
 1065   				boolean isStreamParam = false;
 1066   				if (batchCommandIndex == -1)
 1067   					isStreamParam = isStream[i];
 1068   				else
 1069   					isStreamParam = ((BatchParams)batchArg).isStream[i];
 1070   
 1071   				if ((val == null) && !isStreamParam) {
 1072   					if (quoteStreamsAndUnknowns) {
 1073   						buf.append("'");
 1074   					}
 1075   
 1076   					buf.append("** NOT SPECIFIED **"); //$NON-NLS-1$
 1077   
 1078   					if (quoteStreamsAndUnknowns) {
 1079   						buf.append("'");
 1080   					}
 1081   				} else if (isStreamParam) {
 1082   					if (quoteStreamsAndUnknowns) {
 1083   						buf.append("'");
 1084   					}
 1085   
 1086   					buf.append("** STREAM DATA **"); //$NON-NLS-1$
 1087   
 1088   					if (quoteStreamsAndUnknowns) {
 1089   						buf.append("'");
 1090   					}
 1091   				} else {
 1092   					if (this.charConverter != null) {
 1093   						buf.append(this.charConverter.toString(val));
 1094   					} else {
 1095   						if (this.charEncoding != null) {
 1096   							buf.append(new String(val, this.charEncoding));
 1097   						} else {
 1098   							buf.append(StringUtils.toAsciiString(val));
 1099   						}
 1100   					}
 1101   				}
 1102   			}
 1103   
 1104   			if (this.charEncoding != null) {
 1105   				buf.append(new String(
 1106   						this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()],
 1107   						this.charEncoding));
 1108   			} else {
 1109   				buf
 1110   						.append(StringUtils
 1111   								.toAsciiString(this.staticSqlStrings[this.parameterCount + getParameterIndexOffset()]));
 1112   			}
 1113   		} catch (UnsupportedEncodingException uue) {
 1114   			throw new RuntimeException(Messages
 1115   					.getString("PreparedStatement.32") //$NON-NLS-1$
 1116   					+ this.charEncoding
 1117   					+ Messages.getString("PreparedStatement.33")); //$NON-NLS-1$
 1118   		}
 1119   
 1120   		return buf.toString();
 1121   	}
 1122   
 1123   	public synchronized void clearBatch() throws SQLException {
 1124   		this.batchHasPlainStatements = false;
 1125   
 1126   		super.clearBatch();
 1127   	}
 1128   
 1129   	/**
 1130   	 * In general, parameter values remain in force for repeated used of a
 1131   	 * Statement. Setting a parameter value automatically clears its previous
 1132   	 * value. However, in some cases, it is useful to immediately release the
 1133   	 * resources used by the current parameter values; this can be done by
 1134   	 * calling clearParameters
 1135   	 * 
 1136   	 * @exception SQLException
 1137   	 *                if a database access error occurs
 1138   	 */
 1139   	public synchronized void clearParameters() throws SQLException {
 1140   		checkClosed();
 1141   		
 1142   		for (int i = 0; i < this.parameterValues.length; i++) {
 1143   			this.parameterValues[i] = null;
 1144   			this.parameterStreams[i] = null;
 1145   			this.isStream[i] = false;
 1146   			this.isNull[i] = false;
 1147   			this.parameterTypes[i] = Types.NULL;
 1148   		}
 1149   	}
 1150   
 1151   	/**
 1152   	 * Closes this prepared statement and releases all resources.
 1153   	 * 
 1154   	 * @throws SQLException
 1155   	 *             if database error occurs.
 1156   	 */
 1157   	public synchronized void close() throws SQLException {
 1158   		realClose(true, true);
 1159   	}
 1160   
 1161   	private final void escapeblockFast(byte[] buf, Buffer packet, int size)
 1162   			throws SQLException {
 1163   		int lastwritten = 0;
 1164   
 1165   		for (int i = 0; i < size; i++) {
 1166   			byte b = buf[i];
 1167   
 1168   			if (b == '\0') {
 1169   				// write stuff not yet written
 1170   				if (i > lastwritten) {
 1171   					packet.writeBytesNoNull(buf, lastwritten, i - lastwritten);
 1172   				}
 1173   
 1174   				// write escape
 1175   				packet.writeByte((byte) '\\');
 1176   				packet.writeByte((byte) '0');
 1177   				lastwritten = i + 1;
 1178   			} else {
 1179   				if ((b == '\\') || (b == '\'')
 1180   						|| (!this.usingAnsiMode && b == '"')) {
 1181   					// write stuff not yet written
 1182   					if (i > lastwritten) {
 1183   						packet.writeBytesNoNull(buf, lastwritten, i
 1184   								- lastwritten);
 1185   					}
 1186   
 1187   					// write escape
 1188   					packet.writeByte((byte) '\\');
 1189   					lastwritten = i; // not i+1 as b wasn't written.
 1190   				}
 1191   			}
 1192   		}
 1193   
 1194   		// write out remaining stuff from buffer
 1195   		if (lastwritten < size) {
 1196   			packet.writeBytesNoNull(buf, lastwritten, size - lastwritten);
 1197   		}
 1198   	}
 1199   
 1200   	private final void escapeblockFast(byte[] buf,
 1201   			ByteArrayOutputStream bytesOut, int size) {
 1202   		int lastwritten = 0;
 1203   
 1204   		for (int i = 0; i < size; i++) {
 1205   			byte b = buf[i];
 1206   
 1207   			if (b == '\0') {
 1208   				// write stuff not yet written
 1209   				if (i > lastwritten) {
 1210   					bytesOut.write(buf, lastwritten, i - lastwritten);
 1211   				}
 1212   
 1213   				// write escape
 1214   				bytesOut.write('\\');
 1215   				bytesOut.write('0');
 1216   				lastwritten = i + 1;
 1217   			} else {
 1218   				if ((b == '\\') || (b == '\'')
 1219   						|| (!this.usingAnsiMode && b == '"')) {
 1220   					// write stuff not yet written
 1221   					if (i > lastwritten) {
 1222   						bytesOut.write(buf, lastwritten, i - lastwritten);
 1223   					}
 1224   
 1225   					// write escape
 1226   					bytesOut.write('\\');
 1227   					lastwritten = i; // not i+1 as b wasn't written.
 1228   				}
 1229   			}
 1230   		}
 1231   
 1232   		// write out remaining stuff from buffer
 1233   		if (lastwritten < size) {
 1234   			bytesOut.write(buf, lastwritten, size - lastwritten);
 1235   		}
 1236   	}
 1237   	
 1238   	/**
 1239   	 * Check to see if the statement is safe for read-only slaves after failover.
 1240   	 * 
 1241   	 * @return true if safe for read-only.
 1242   	 * @throws SQLException
 1243   	 */
 1244   	protected boolean checkReadOnlySafeStatement() throws SQLException {
 1245   		return ((!this.connection.isReadOnly()) || (this.firstCharOfStmt == 'S'));
 1246   	}
 1247   
 1248   	/**
 1249   	 * Some prepared statements return multiple results; the execute method
 1250   	 * handles these complex statements as well as the simpler form of
 1251   	 * statements handled by executeQuery and executeUpdate
 1252   	 * 
 1253   	 * @return true if the next result is a ResultSet; false if it is an update
 1254   	 *         count or there are no more results
 1255   	 * 
 1256   	 * @exception SQLException
 1257   	 *                if a database access error occurs
 1258   	 */
 1259   	public boolean execute() throws SQLException {
 1260   		checkClosed();
 1261   		
 1262   		ConnectionImpl locallyScopedConn = this.connection;
 1263   		
 1264   		if(!checkReadOnlySafeStatement()) {
 1265   			 throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") //$NON-NLS-1$
 1266   							+ Messages.getString("PreparedStatement.21"), //$NON-NLS-1$
 1267   							SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 1268   		}
 1269   		
 1270   		ResultSetInternalMethods rs = null;
 1271   
 1272   		CachedResultSetMetaData cachedMetadata = null;
 1273   
 1274   		synchronized (locallyScopedConn.getMutex()) {
 1275   			lastQueryIsOnDupKeyUpdate = false;
 1276   			if (retrieveGeneratedKeys)
 1277   				lastQueryIsOnDupKeyUpdate = containsOnDuplicateKeyUpdateInSQL();
 1278   			boolean doStreaming = createStreamingResultSet();
 1279   			
 1280   			clearWarnings();
 1281   
 1282   			// Adjust net_write_timeout to a higher value if we're
 1283   			// streaming result sets. More often than not, someone runs into
 1284   			// an issue where they blow net_write_timeout when using this
 1285   			// feature, and if they're willing to hold a result set open
 1286   			// for 30 seconds or more, one more round-trip isn't going to hurt
 1287   			//
 1288   			// This is reset by RowDataDynamic.close().
 1289   			
 1290   			if (doStreaming
 1291   					&& this.connection.getNetTimeoutForStreamingResults() > 0) {
 1292   				executeSimpleNonQuery(locallyScopedConn,
 1293   						"SET net_write_timeout="
 1294   								+ this.connection
 1295   										.getNetTimeoutForStreamingResults());
 1296   			}
 1297   			
 1298   			this.batchedGeneratedKeys = null;
 1299   
 1300   			Buffer sendPacket = fillSendPacket();
 1301   
 1302   			String oldCatalog = null;
 1303   
 1304   			if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
 1305   				oldCatalog = locallyScopedConn.getCatalog();
 1306   				locallyScopedConn.setCatalog(this.currentCatalog);
 1307   			}
 1308   
 1309   			//
 1310   			// Check if we have cached metadata for this query...
 1311   			//
 1312   			if (locallyScopedConn.getCacheResultSetMetadata()) {
 1313   				cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
 1314   			}
 1315   
 1316   			Field[] metadataFromCache = null;
 1317   			
 1318   			if (cachedMetadata != null) {
 1319   				metadataFromCache = cachedMetadata.fields;
 1320   			}
 1321   			
 1322   			boolean oldInfoMsgState = false;
 1323   
 1324   			if (this.retrieveGeneratedKeys) {
 1325   				oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
 1326   				locallyScopedConn.setReadInfoMsgEnabled(true);
 1327   			}
 1328   
 1329   			// If there isn't a limit clause in the SQL
 1330   			// then limit the number of rows to return in
 1331   			// an efficient manner. Only do this if
 1332   			// setMaxRows() hasn't been used on any Statements
 1333   			// generated from the current Connection (saves
 1334   			// a query, and network traffic).
 1335   			//
 1336   			// Only apply max_rows to selects
 1337   			//
 1338   			if (locallyScopedConn.useMaxRows()) {
 1339   				int rowLimit = -1;
 1340   
 1341   				if (this.firstCharOfStmt == 'S') {
 1342   					if (this.hasLimitClause) {
 1343   						rowLimit = this.maxRows;
 1344   					} else {
 1345   						if (this.maxRows <= 0) {
 1346   							executeSimpleNonQuery(locallyScopedConn,
 1347   									"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 1348   						} else {
 1349   							executeSimpleNonQuery(locallyScopedConn,
 1350   									"SET OPTION SQL_SELECT_LIMIT="
 1351   											+ this.maxRows);
 1352   						}
 1353   					}
 1354   				} else {
 1355   					executeSimpleNonQuery(locallyScopedConn,
 1356   							"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 1357   				}
 1358   
 1359   				// Finally, execute the query
 1360   				rs = executeInternal(rowLimit, sendPacket,
 1361   						doStreaming,
 1362   						(this.firstCharOfStmt == 'S'), metadataFromCache, false);
 1363   			} else {
 1364   				rs = executeInternal(-1, sendPacket,
 1365   						doStreaming,
 1366   						(this.firstCharOfStmt == 'S'), metadataFromCache, false);
 1367   			}
 1368   
 1369   			if (cachedMetadata != null) {
 1370   				locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
 1371   						cachedMetadata, this.results);
 1372   			} else {
 1373   				if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) {
 1374   					locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
 1375   							null /* will be created */, rs);
 1376   				}
 1377   			}
 1378   			
 1379   			if (this.retrieveGeneratedKeys) {
 1380   				locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
 1381   				rs.setFirstCharOfQuery(this.firstCharOfStmt);
 1382   			}
 1383   
 1384   			if (oldCatalog != null) {
 1385   				locallyScopedConn.setCatalog(oldCatalog);
 1386   			}
 1387   
 1388   			if (rs != null) {
 1389   				this.lastInsertId = rs.getUpdateID();
 1390   				
 1391   				this.results = rs;
 1392   			}
 1393   		}
 1394   
 1395   		return ((rs != null) && rs.reallyResult());
 1396   	}
 1397   
 1398   	/**
 1399   	 * JDBC 2.0 Submit a batch of commands to the database for execution. This
 1400   	 * method is optional.
 1401   	 * 
 1402   	 * @return an array of update counts containing one element for each command
 1403   	 *         in the batch. The array is ordered according to the order in
 1404   	 *         which commands were inserted into the batch
 1405   	 * 
 1406   	 * @exception SQLException
 1407   	 *                if a database-access error occurs, or the driver does not
 1408   	 *                support batch statements
 1409   	 * @throws java.sql.BatchUpdateException
 1410   	 *             DOCUMENT ME!
 1411   	 */
 1412   	public int[] executeBatch() throws SQLException {
 1413   		checkClosed();
 1414   		
 1415   		if (this.connection.isReadOnly()) {
 1416   			throw new SQLException(Messages.getString("PreparedStatement.25") //$NON-NLS-1$
 1417   					+ Messages.getString("PreparedStatement.26"), //$NON-NLS-1$
 1418   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 1419   		}
 1420   
 1421   		synchronized (this.connection.getMutex()) {
 1422   			if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
 1423                   return new int[0];
 1424               }
 1425   
 1426   			// we timeout the entire batch, not individual statements
 1427   			int batchTimeout = this.timeoutInMillis;
 1428   			this.timeoutInMillis = 0;
 1429   		
 1430   			resetCancelledState();
 1431   			
 1432   			try {
 1433   				clearWarnings();
 1434   
 1435   				if (!this.batchHasPlainStatements
 1436   						&& this.connection.getRewriteBatchedStatements()) {
 1437   					
 1438   					
 1439   					if (canRewriteAsMultiValueInsertAtSqlLevel()) {
 1440   						return executeBatchedInserts(batchTimeout);
 1441   					}
 1442   					
 1443   					if (this.connection.versionMeetsMinimum(4, 1, 0) 
 1444   							&& !this.batchHasPlainStatements
 1445   							&& this.batchedArgs != null 
 1446   							&& this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) {
 1447   						return executePreparedBatchAsMultiStatement(batchTimeout);
 1448   					}
 1449   				}
 1450   
 1451   				return executeBatchSerially(batchTimeout);
 1452   			} finally {
 1453   				clearBatch();
 1454   			}
 1455   		}
 1456   	}
 1457   
 1458   	public boolean canRewriteAsMultiValueInsertAtSqlLevel() throws SQLException {
 1459   		return this.parseInfo.canRewriteAsMultiValueInsert;
 1460   	}
 1461   	
 1462   	protected int getLocationOfOnDuplicateKeyUpdate() {
 1463   		return this.parseInfo.locationOfOnDuplicateKeyUpdate;
 1464   	}
 1465   
 1466   	/**
 1467   	 * Rewrites the already prepared statement into a multi-statement
 1468   	 * query of 'statementsPerBatch' values and executes the entire batch
 1469   	 * using this new statement.
 1470   	 * 
 1471   	 * @return update counts in the same fashion as executeBatch()
 1472   	 * 
 1473   	 * @throws SQLException
 1474   	 */
 1475   	
 1476   	protected int[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException {
 1477   		synchronized (this.connection.getMutex()) {
 1478   			// This is kind of an abuse, but it gets the job done
 1479   			if (this.batchedValuesClause == null) {
 1480   				this.batchedValuesClause = this.originalSql + ";";
 1481   			}
 1482   			
 1483   			ConnectionImpl locallyScopedConn = this.connection;
 1484   			
 1485   			boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
 1486   			CancelTask timeoutTask = null;
 1487   			
 1488   			try {
 1489   				clearWarnings();
 1490   				
 1491   				int numBatchedArgs = this.batchedArgs.size();
 1492   				
 1493   				if (this.retrieveGeneratedKeys) {
 1494   					this.batchedGeneratedKeys = new ArrayList(numBatchedArgs);
 1495   				}
 1496   
 1497   				int numValuesPerBatch = computeBatchSize(numBatchedArgs);
 1498   
 1499   				if (numBatchedArgs < numValuesPerBatch) {
 1500   					numValuesPerBatch = numBatchedArgs;
 1501   				}
 1502   
 1503   				java.sql.PreparedStatement batchedStatement = null;
 1504   
 1505   				int batchedParamIndex = 1;
 1506   				int numberToExecuteAsMultiValue = 0;
 1507   				int batchCounter = 0;
 1508   				int updateCountCounter = 0;
 1509   				int[] updateCounts = new int[numBatchedArgs];
 1510   				SQLException sqlEx = null;
 1511   				
 1512   				try {
 1513   					if (!multiQueriesEnabled) {
 1514   						locallyScopedConn.getIO().enableMultiQueries();
 1515   					}
 1516   					
 1517   					if (this.retrieveGeneratedKeys) {
 1518   						batchedStatement = locallyScopedConn.prepareStatement(
 1519   								generateMultiStatementForBatch(numValuesPerBatch),
 1520   								RETURN_GENERATED_KEYS);
 1521   					} else {
 1522   						batchedStatement = locallyScopedConn
 1523   								.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch));
 1524   					}
 1525   
 1526   					if (locallyScopedConn.getEnableQueryTimeouts() &&
 1527   							batchTimeout != 0
 1528   							&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
 1529   						timeoutTask = new CancelTask((StatementImpl)batchedStatement);
 1530   						locallyScopedConn.getCancelTimer().schedule(timeoutTask,
 1531   								batchTimeout);
 1532   					}
 1533   					
 1534   					if (numBatchedArgs < numValuesPerBatch) {
 1535   						numberToExecuteAsMultiValue = numBatchedArgs;
 1536   					} else {
 1537   						numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch;
 1538   					}
 1539   			
 1540   					int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch;
 1541   			
 1542   					for (int i = 0; i < numberArgsToExecute; i++) {
 1543   						if (i != 0 && i % numValuesPerBatch == 0) {
 1544   							try {
 1545   								batchedStatement.execute();
 1546   							} catch (SQLException ex) {
 1547   								sqlEx = handleExceptionForBatch(batchCounter, numValuesPerBatch, 
 1548   										updateCounts, ex);
 1549   							}
 1550   							
 1551   							updateCountCounter = processMultiCountsAndKeys(
 1552   									(StatementImpl)batchedStatement, updateCountCounter,
 1553   									updateCounts);
 1554   							
 1555   							batchedStatement.clearParameters();
 1556   							batchedParamIndex = 1;
 1557   						}
 1558   			
 1559   						batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
 1560   								batchedParamIndex, this.batchedArgs
 1561   								.get(batchCounter++));
 1562   					}
 1563   			
 1564   					try {
 1565   						batchedStatement.execute();
 1566   					} catch (SQLException ex) {
 1567   						sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, 
 1568   								updateCounts, ex);
 1569   					}
 1570   					
 1571   					updateCountCounter = processMultiCountsAndKeys(
 1572   							(StatementImpl)batchedStatement, updateCountCounter,
 1573   							updateCounts);
 1574   					
 1575   					batchedStatement.clearParameters();
 1576   			
 1577   					numValuesPerBatch = numBatchedArgs - batchCounter;
 1578   				} finally {
 1579   					if (batchedStatement != null) {
 1580   						batchedStatement.close();
 1581   					}
 1582   				}
 1583   				
 1584   				try {
 1585   					if (numValuesPerBatch > 0) {
 1586   			
 1587   						if (this.retrieveGeneratedKeys) {
 1588   							batchedStatement = locallyScopedConn.prepareStatement(
 1589   									generateMultiStatementForBatch(numValuesPerBatch),
 1590   								RETURN_GENERATED_KEYS);
 1591   						} else {
 1592   							batchedStatement = locallyScopedConn.prepareStatement(
 1593   									generateMultiStatementForBatch(numValuesPerBatch));
 1594   						}
 1595   						
 1596   						if (timeoutTask != null) {
 1597   							timeoutTask.toCancel = (StatementImpl)batchedStatement;
 1598   						}
 1599   						
 1600   						batchedParamIndex = 1;
 1601   			
 1602   						while (batchCounter < numBatchedArgs) {
 1603   							batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
 1604   									batchedParamIndex, this.batchedArgs
 1605   									.get(batchCounter++));
 1606   						}
 1607   			
 1608   						try {
 1609   							batchedStatement.execute();
 1610   						} catch (SQLException ex) {
 1611   							sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, 
 1612   									updateCounts, ex);
 1613   						}
 1614   						
 1615   						updateCountCounter = processMultiCountsAndKeys(
 1616   								(StatementImpl)batchedStatement, updateCountCounter,
 1617   								updateCounts);
 1618   						
 1619   						batchedStatement.clearParameters();
 1620   					}
 1621   			
 1622   					if (timeoutTask != null) {
 1623   						if (timeoutTask.caughtWhileCancelling != null) {
 1624   							throw timeoutTask.caughtWhileCancelling;
 1625   						}
 1626   
 1627   						timeoutTask.cancel();
 1628   						timeoutTask = null;
 1629   					}
 1630   					
 1631   					if (sqlEx != null) {
 1632   						throw new java.sql.BatchUpdateException(sqlEx
 1633   								.getMessage(), sqlEx.getSQLState(), sqlEx
 1634   								.getErrorCode(), updateCounts);
 1635   					}
 1636   					
 1637   					return updateCounts;
 1638   				} finally {
 1639   					if (batchedStatement != null) {
 1640   						batchedStatement.close();
 1641   					}
 1642   				}
 1643   			} finally {
 1644   				if (timeoutTask != null) {
 1645   					timeoutTask.cancel();
 1646   				}
 1647   				
 1648   				resetCancelledState();
 1649   				
 1650   				if (!multiQueriesEnabled) {
 1651   					locallyScopedConn.getIO().disableMultiQueries();
 1652   				}
 1653   				
 1654   				clearBatch();
 1655   			}
 1656   		}
 1657   	}
 1658   	
 1659   	private String generateMultiStatementForBatch(int numBatches) {
 1660   		StringBuffer newStatementSql = new StringBuffer((this.originalSql
 1661   				.length() + 1) * numBatches);
 1662   				
 1663   		newStatementSql.append(this.originalSql);
 1664   
 1665   		for (int i = 0; i < numBatches - 1; i++) {
 1666   			newStatementSql.append(';');
 1667   			newStatementSql.append(this.originalSql);
 1668   		}
 1669   
 1670   		return newStatementSql.toString();
 1671   	}
 1672   	
 1673   	/**
 1674   	 * Rewrites the already prepared statement into a multi-value insert
 1675   	 * statement of 'statementsPerBatch' values and executes the entire batch
 1676   	 * using this new statement.
 1677   	 * 
 1678   	 * @return update counts in the same fashion as executeBatch()
 1679   	 * 
 1680   	 * @throws SQLException
 1681   	 */
 1682   	protected int[] executeBatchedInserts(int batchTimeout) throws SQLException {
 1683   		String valuesClause = getValuesClause(); 
 1684   
 1685   		ConnectionImpl locallyScopedConn = this.connection;
 1686   
 1687   		if (valuesClause == null) {
 1688   			return executeBatchSerially(batchTimeout);
 1689   		}
 1690   
 1691   		int numBatchedArgs = this.batchedArgs.size();
 1692   
 1693   		if (this.retrieveGeneratedKeys) {
 1694   			this.batchedGeneratedKeys = new ArrayList(numBatchedArgs);
 1695   		}
 1696   
 1697   		int numValuesPerBatch = computeBatchSize(numBatchedArgs);
 1698   
 1699   		if (numBatchedArgs < numValuesPerBatch) {
 1700   			numValuesPerBatch = numBatchedArgs;
 1701   		}
 1702   
 1703   		java.sql.PreparedStatement batchedStatement = null;
 1704   
 1705   		int batchedParamIndex = 1;
 1706   		int updateCountRunningTotal = 0;
 1707   		int numberToExecuteAsMultiValue = 0;
 1708   		int batchCounter = 0;
 1709   		CancelTask timeoutTask = null;
 1710   		SQLException sqlEx = null;
 1711   		
 1712   		int[] updateCounts = new int[numBatchedArgs];
 1713   
 1714   		for (int i = 0; i < this.batchedArgs.size(); i++) {
 1715   			updateCounts[i] = 1;
 1716   		}
 1717   		
 1718   		try {
 1719   			try {
 1720   					batchedStatement = /* FIXME -if we ever care about folks proxying our ConnectionImpl */
 1721   							prepareBatchedInsertSQL((ConnectionImpl) locallyScopedConn, numValuesPerBatch);
 1722   
 1723   				if (locallyScopedConn.getEnableQueryTimeouts()
 1724   						&& batchTimeout != 0
 1725   						&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
 1726   					timeoutTask = new CancelTask(
 1727   							(StatementImpl) batchedStatement);
 1728   					locallyScopedConn.getCancelTimer().schedule(timeoutTask,
 1729   							batchTimeout);
 1730   				}
 1731   
 1732   				if (numBatchedArgs < numValuesPerBatch) {
 1733   					numberToExecuteAsMultiValue = numBatchedArgs;
 1734   				} else {
 1735   					numberToExecuteAsMultiValue = numBatchedArgs
 1736   							/ numValuesPerBatch;
 1737   				}
 1738   
 1739   				int numberArgsToExecute = numberToExecuteAsMultiValue
 1740   						* numValuesPerBatch;
 1741   
 1742   				for (int i = 0; i < numberArgsToExecute; i++) {
 1743   					if (i != 0 && i % numValuesPerBatch == 0) {
 1744   						try {
 1745   							updateCountRunningTotal += batchedStatement
 1746   								.executeUpdate();
 1747   						} catch (SQLException ex) {
 1748   							sqlEx = handleExceptionForBatch(batchCounter - 1,
 1749   									numValuesPerBatch, updateCounts, ex);
 1750   						}
 1751   
 1752   						getBatchedGeneratedKeys(batchedStatement);
 1753   						batchedStatement.clearParameters();
 1754   						batchedParamIndex = 1;
 1755   
 1756   					}
 1757   
 1758   					batchedParamIndex = setOneBatchedParameterSet(
 1759   							batchedStatement, batchedParamIndex,
 1760   							this.batchedArgs.get(batchCounter++));
 1761   				}
 1762   
 1763   				try {
 1764   					updateCountRunningTotal += batchedStatement.executeUpdate();
 1765   				} catch (SQLException ex) {
 1766   					sqlEx = handleExceptionForBatch(batchCounter - 1,
 1767   							numValuesPerBatch, updateCounts, ex);
 1768   				}
 1769   				
 1770   				getBatchedGeneratedKeys(batchedStatement);
 1771   
 1772   				numValuesPerBatch = numBatchedArgs - batchCounter;
 1773   			} finally {
 1774   				if (batchedStatement != null) {
 1775   					batchedStatement.close();
 1776   				}
 1777   			}
 1778   
 1779   			try {
 1780   				if (numValuesPerBatch > 0) {
 1781   						batchedStatement = 
 1782   								prepareBatchedInsertSQL((ConnectionImpl) locallyScopedConn, 
 1783   										numValuesPerBatch);
 1784   					
 1785   					if (timeoutTask != null) {
 1786   						timeoutTask.toCancel = (StatementImpl) batchedStatement;
 1787   					}
 1788   
 1789   					batchedParamIndex = 1;
 1790   
 1791   					while (batchCounter < numBatchedArgs) {
 1792   						batchedParamIndex = setOneBatchedParameterSet(
 1793   								batchedStatement, batchedParamIndex,
 1794   								this.batchedArgs.get(batchCounter++));
 1795   					}
 1796   
 1797   					try {
 1798   						updateCountRunningTotal += batchedStatement.executeUpdate();
 1799   					} catch (SQLException ex) {
 1800   						sqlEx = handleExceptionForBatch(batchCounter - 1,
 1801   								numValuesPerBatch, updateCounts, ex);
 1802   					}
 1803   					
 1804   					getBatchedGeneratedKeys(batchedStatement);
 1805   				}
 1806   
 1807   				if (sqlEx != null) {
 1808   					throw new java.sql.BatchUpdateException(sqlEx
 1809   							.getMessage(), sqlEx.getSQLState(), sqlEx
 1810   							.getErrorCode(), updateCounts);
 1811   				}
 1812   				
 1813   				return updateCounts;
 1814   			} finally {
 1815   				if (batchedStatement != null) {
 1816   					batchedStatement.close();
 1817   				}
 1818   			}
 1819   		} finally {
 1820   			if (timeoutTask != null) {
 1821   				timeoutTask.cancel();
 1822   			}
 1823   
 1824   			resetCancelledState();
 1825   		}
 1826   	}
 1827   
 1828   	protected String getValuesClause() throws SQLException {
 1829   		return this.parseInfo.valuesClause;
 1830   	}
 1831   
 1832   	/**
 1833   	 * Computes the optimum number of batched parameter lists to send
 1834   	 * without overflowing max_allowed_packet.
 1835   	 * 
 1836   	 * @param numBatchedArgs
 1837   	 * @return
 1838   	 * @throws SQLException 
 1839   	 */
 1840   	protected int computeBatchSize(int numBatchedArgs) throws SQLException {
 1841   		long[] combinedValues = computeMaxParameterSetSizeAndBatchSize(numBatchedArgs);
 1842   		
 1843   		long maxSizeOfParameterSet = combinedValues[0];
 1844   		long sizeOfEntireBatch = combinedValues[1];
 1845   		
 1846   		int maxAllowedPacket = this.connection.getMaxAllowedPacket();
 1847   		
 1848   		if (sizeOfEntireBatch < maxAllowedPacket - this.originalSql.length()) {
 1849   			return numBatchedArgs;
 1850   		}
 1851   		
 1852   		return (int)Math.max(1, (maxAllowedPacket - this.originalSql.length()) / maxSizeOfParameterSet);
 1853   	}
 1854   	
 1855   	/** 
 1856   	 *  Computes the maximum parameter set size, and entire batch size given 
 1857   	 *  the number of arguments in the batch.
 1858   	 * @throws SQLException 
 1859   	 */
 1860   	protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) throws SQLException {
 1861   		long sizeOfEntireBatch = 0;
 1862   		long maxSizeOfParameterSet = 0;
 1863   		
 1864   		for (int i = 0; i < numBatchedArgs; i++) {
 1865   			BatchParams paramArg = (BatchParams) this.batchedArgs
 1866   			.get(i);
 1867   
 1868   			boolean[] isNullBatch = paramArg.isNull;
 1869   			boolean[] isStreamBatch = paramArg.isStream;
 1870   
 1871   			long sizeOfParameterSet = 0;
 1872   			
 1873   			for (int j = 0; j < isNullBatch.length; j++) {
 1874   				if (!isNullBatch[j]) {
 1875   
 1876   					if (isStreamBatch[j]) {
 1877   						int streamLength = paramArg.streamLengths[j];
 1878   						
 1879   						if (streamLength != -1) {
 1880   							sizeOfParameterSet += streamLength * 2; // for safety in escaping
 1881   						} else {
 1882   							int paramLength = paramArg.parameterStrings[j].length;
 1883   							sizeOfParameterSet += paramLength;
 1884   						}
 1885   					} else {
 1886   						sizeOfParameterSet += paramArg.parameterStrings[j].length;
 1887   					}
 1888   				} else {
 1889   					sizeOfParameterSet += 4; // for NULL literal in SQL 
 1890   				}
 1891   			}
 1892   			
 1893   			//
 1894   			// Account for static part of values clause
 1895   			// This is a little naiive, because the ?s will be replaced
 1896   			// but it gives us some padding, and is less housekeeping
 1897   			// to ignore them. We're looking for a "fuzzy" value here
 1898   			// anyway
 1899   			//
 1900   			
 1901   			if (getValuesClause() != null) {
 1902   				sizeOfParameterSet += getValuesClause().length() + 1;
 1903   			} else {
 1904   				sizeOfParameterSet += this.originalSql.length() + 1;
 1905   			}
 1906   			
 1907   			sizeOfEntireBatch += sizeOfParameterSet;
 1908   			
 1909   			if (sizeOfParameterSet > maxSizeOfParameterSet) {
 1910   				maxSizeOfParameterSet = sizeOfParameterSet;
 1911   			}
 1912   		}	
 1913   		
 1914   		return new long[] {maxSizeOfParameterSet, sizeOfEntireBatch};
 1915   	}
 1916   
 1917   
 1918   	/**
 1919   	 * Executes the current batch of statements by executing them one-by-one.
 1920   	 * 
 1921   	 * @return a list of update counts
 1922   	 * @throws SQLException
 1923   	 *             if an error occurs
 1924   	 */
 1925   	protected int[] executeBatchSerially(int batchTimeout) throws SQLException {
 1926   		
 1927   		ConnectionImpl locallyScopedConn = this.connection;
 1928   		
 1929   		if (locallyScopedConn == null) {
 1930   			checkClosed();
 1931   		}
 1932   
 1933   		int[] updateCounts = null;
 1934   
 1935   		if (this.batchedArgs != null) {
 1936   			int nbrCommands = this.batchedArgs.size();
 1937   			updateCounts = new int[nbrCommands];
 1938   
 1939   			for (int i = 0; i < nbrCommands; i++) {
 1940   				updateCounts[i] = -3;
 1941   			}
 1942   
 1943   			SQLException sqlEx = null;
 1944   
 1945   			CancelTask timeoutTask = null;
 1946   			
 1947   			try {
 1948   				if (locallyScopedConn.getEnableQueryTimeouts() &&
 1949   						batchTimeout != 0
 1950   						&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
 1951   					timeoutTask = new CancelTask(this);
 1952   					locallyScopedConn.getCancelTimer().schedule(timeoutTask,
 1953   							batchTimeout);
 1954   				}
 1955   				
 1956   				if (this.retrieveGeneratedKeys) {
 1957   					this.batchedGeneratedKeys = new ArrayList(nbrCommands);
 1958   				}
 1959   	
 1960   				for (batchCommandIndex = 0; batchCommandIndex < nbrCommands; batchCommandIndex++) {
 1961   					Object arg = this.batchedArgs.get(batchCommandIndex);
 1962   	
 1963   					if (arg instanceof String) {
 1964   						updateCounts[batchCommandIndex] = executeUpdate((String) arg);
 1965   					} else {
 1966   						BatchParams paramArg = (BatchParams) arg;
 1967   	
 1968   						try {
 1969   							updateCounts[batchCommandIndex] = executeUpdate(
 1970   									paramArg.parameterStrings,
 1971   									paramArg.parameterStreams, paramArg.isStream,
 1972   									paramArg.streamLengths, paramArg.isNull, true);
 1973   	
 1974   							if (this.retrieveGeneratedKeys) {
 1975   								java.sql.ResultSet rs = null;
 1976   	
 1977   								try {
 1978   									if (containsOnDuplicateKeyUpdateInSQL())
 1979   										rs = getGeneratedKeysInternal(1);
 1980   									else
 1981   										rs = getGeneratedKeysInternal();
 1982   	
 1983   									while (rs.next()) {
 1984   										this.batchedGeneratedKeys
 1985   												.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }, getExceptionInterceptor()));
 1986   									}
 1987   								} finally {
 1988   									if (rs != null) {
 1989   										rs.close();
 1990   									}
 1991   								}
 1992   							}
 1993   						} catch (SQLException ex) {
 1994   							updateCounts[batchCommandIndex] = EXECUTE_FAILED;
 1995   	
 1996   							if (this.continueBatchOnError && 
 1997   									!(ex instanceof MySQLTimeoutException) && 
 1998   									!(ex instanceof MySQLStatementCancelledException) &&
 1999   									!hasDeadlockOrTimeoutRolledBackTx(ex)) {
 2000   								sqlEx = ex;
 2001   							} else {
 2002   								int[] newUpdateCounts = new int[batchCommandIndex];
 2003   								System.arraycopy(updateCounts, 0,
 2004   										newUpdateCounts, 0, batchCommandIndex);
 2005   	
 2006   								throw new java.sql.BatchUpdateException(ex
 2007   										.getMessage(), ex.getSQLState(), ex
 2008   										.getErrorCode(), newUpdateCounts);
 2009   							}
 2010   						}
 2011   					}
 2012   				}
 2013   	
 2014   				if (sqlEx != null) {
 2015   					throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
 2016   							sqlEx.getSQLState(), sqlEx.getErrorCode(), updateCounts);
 2017   				}
 2018   			} catch (NullPointerException npe) {
 2019   				try {
 2020   					checkClosed();
 2021   				} catch (SQLException connectionClosedEx) {
 2022   					updateCounts[batchCommandIndex] = EXECUTE_FAILED;
 2023   					
 2024   					int[] newUpdateCounts = new int[batchCommandIndex];
 2025   					
 2026   					System.arraycopy(updateCounts, 0,
 2027   							newUpdateCounts, 0, batchCommandIndex);
 2028   
 2029   					throw new java.sql.BatchUpdateException(connectionClosedEx
 2030   							.getMessage(), connectionClosedEx.getSQLState(), connectionClosedEx
 2031   							.getErrorCode(), newUpdateCounts);
 2032   				}
 2033   				
 2034   				throw npe; // we don't know why this happened, punt
 2035   			} finally {
 2036   				batchCommandIndex = -1;
 2037   				
 2038   				if (timeoutTask != null) {
 2039   					timeoutTask.cancel();
 2040   				}
 2041   				
 2042   				resetCancelledState();
 2043   			}
 2044   		}
 2045   	
 2046   		return (updateCounts != null) ? updateCounts : new int[0];
 2047   		
 2048   	}
 2049   
 2050   	/**
 2051   	 * Actually execute the prepared statement. This is here so server-side
 2052   	 * PreparedStatements can re-use most of the code from this class.
 2053   	 * 
 2054   	 * @param maxRowsToRetrieve
 2055   	 *            the max number of rows to return
 2056   	 * @param sendPacket
 2057   	 *            the packet to send
 2058   	 * @param createStreamingResultSet
 2059   	 *            should a 'streaming' result set be created?
 2060   	 * @param queryIsSelectOnly
 2061   	 *            is this query doing a SELECT?
 2062   	 * @param unpackFields
 2063   	 *            DOCUMENT ME!
 2064   	 * 
 2065   	 * @return the results as a ResultSet
 2066   	 * 
 2067   	 * @throws SQLException
 2068   	 *             if an error occurs.
 2069   	 */
 2070   	protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve,
 2071   			Buffer sendPacket, boolean createStreamingResultSet,
 2072   			boolean queryIsSelectOnly, Field[] metadataFromCache,
 2073   			boolean isBatch)
 2074   			throws SQLException {
 2075   		try {
 2076   			
 2077   			resetCancelledState();
 2078   			
 2079   			ConnectionImpl locallyScopedConnection = this.connection;
 2080   			
 2081   			this.numberOfExecutions++;
 2082   	
 2083   			if (this.doPingInstead) {
 2084   				doPingInstead();
 2085   				
 2086   				return this.results;
 2087   			}
 2088   			
 2089   			ResultSetInternalMethods rs;
 2090   			
 2091   			CancelTask timeoutTask = null;
 2092   	
 2093   			try {
 2094   				if (locallyScopedConnection.getEnableQueryTimeouts() &&
 2095   						this.timeoutInMillis != 0
 2096   						&& locallyScopedConnection.versionMeetsMinimum(5, 0, 0)) {
 2097   					timeoutTask = new CancelTask(this);
 2098   					locallyScopedConnection.getCancelTimer().schedule(timeoutTask, 
 2099   							this.timeoutInMillis);
 2100   				}
 2101   				
 2102   				rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket,
 2103   					this.resultSetType, this.resultSetConcurrency,
 2104   					createStreamingResultSet, this.currentCatalog,
 2105   					metadataFromCache, isBatch);
 2106   				
 2107   				if (timeoutTask != null) {
 2108   					timeoutTask.cancel();
 2109   					
 2110   					if (timeoutTask.caughtWhileCancelling != null) {
 2111   						throw timeoutTask.caughtWhileCancelling;
 2112   					}
 2113   					
 2114   					timeoutTask = null;
 2115   				}
 2116   			
 2117   				synchronized (this.cancelTimeoutMutex) {
 2118   					if (this.wasCancelled) {
 2119   						SQLException cause = null;
 2120   						
 2121   						if (this.wasCancelledByTimeout) {
 2122   							cause = new MySQLTimeoutException();
 2123   						} else {
 2124   							cause = new MySQLStatementCancelledException();
 2125   						}
 2126   						
 2127   						resetCancelledState();
 2128   						
 2129   						throw cause;
 2130   					}
 2131   				}
 2132   			} finally {
 2133   				if (timeoutTask != null) {
 2134   					timeoutTask.cancel();
 2135   				}
 2136   			}
 2137   			
 2138   			return rs;
 2139   		} catch (NullPointerException npe) {
 2140   			checkClosed(); // we can't synchronize ourselves against async connection-close
 2141   			               // due to deadlock issues, so this is the next best thing for
 2142   			 			   // this particular corner case.
 2143   			
 2144   			throw npe;
 2145   		}
 2146   	}
 2147   
 2148   	/**
 2149   	 * A Prepared SQL query is executed and its ResultSet is returned
 2150   	 * 
 2151   	 * @return a ResultSet that contains the data produced by the query - never
 2152   	 *         null
 2153   	 * 
 2154   	 * @exception SQLException
 2155   	 *                if a database access error occurs
 2156   	 */
 2157   	public java.sql.ResultSet executeQuery() throws SQLException {
 2158   		checkClosed();
 2159   		
 2160   		ConnectionImpl locallyScopedConn = this.connection;
 2161   		
 2162   		checkForDml(this.originalSql, this.firstCharOfStmt);
 2163   
 2164   		CachedResultSetMetaData cachedMetadata = null;
 2165   
 2166   		// We need to execute this all together
 2167   		// So synchronize on the Connection's mutex (because
 2168   		// even queries going through there synchronize
 2169   		// on the same mutex.
 2170   		synchronized (locallyScopedConn.getMutex()) {
 2171   			clearWarnings();
 2172   
 2173   			boolean doStreaming = createStreamingResultSet();
 2174   			
 2175   			this.batchedGeneratedKeys = null;
 2176   
 2177   			// Adjust net_write_timeout to a higher value if we're
 2178   			// streaming result sets. More often than not, someone runs into
 2179   			// an issue where they blow net_write_timeout when using this
 2180   			// feature, and if they're willing to hold a result set open
 2181   			// for 30 seconds or more, one more round-trip isn't going to hurt
 2182   			//
 2183   			// This is reset by RowDataDynamic.close().
 2184   			
 2185   			if (doStreaming
 2186   					&& this.connection.getNetTimeoutForStreamingResults() > 0) {
 2187   				
 2188   				java.sql.Statement stmt = null;
 2189   				
 2190   				try {
 2191   					stmt = this.connection.createStatement();
 2192   					
 2193   					((com.mysql.jdbc.StatementImpl)stmt).executeSimpleNonQuery(this.connection, "SET net_write_timeout=" 
 2194   							+ this.connection.getNetTimeoutForStreamingResults());
 2195   				} finally {
 2196   					if (stmt != null) {
 2197   						stmt.close();
 2198   					}
 2199   				}
 2200   			}
 2201   			
 2202   			Buffer sendPacket = fillSendPacket();
 2203   
 2204   			if (this.results != null) {
 2205   				if (!this.connection.getHoldResultsOpenOverStatementClose()) {
 2206   					if (!this.holdResultsOpenOverClose) {
 2207   						this.results.realClose(false);
 2208   					}
 2209   				}
 2210   			}
 2211   
 2212   			String oldCatalog = null;
 2213   
 2214   			if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
 2215   				oldCatalog = locallyScopedConn.getCatalog();
 2216   				locallyScopedConn.setCatalog(this.currentCatalog);
 2217   			}
 2218   
 2219   			//
 2220   			// Check if we have cached metadata for this query...
 2221   			//
 2222   			if (locallyScopedConn.getCacheResultSetMetadata()) {
 2223   				cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
 2224   			}
 2225   
 2226   			Field[] metadataFromCache = null;
 2227   			
 2228   			if (cachedMetadata != null) {
 2229   				metadataFromCache = cachedMetadata.fields;
 2230   			}
 2231   			
 2232   			if (locallyScopedConn.useMaxRows()) {
 2233   				// If there isn't a limit clause in the SQL
 2234   				// then limit the number of rows to return in
 2235   				// an efficient manner. Only do this if
 2236   				// setMaxRows() hasn't been used on any Statements
 2237   				// generated from the current Connection (saves
 2238   				// a query, and network traffic).
 2239   				if (this.hasLimitClause) {
 2240   					this.results = executeInternal(this.maxRows, sendPacket,
 2241   							createStreamingResultSet(), true,
 2242   							metadataFromCache, false);
 2243   				} else {
 2244   					if (this.maxRows <= 0) {
 2245   						executeSimpleNonQuery(locallyScopedConn,
 2246   								"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 2247   					} else {
 2248   						executeSimpleNonQuery(locallyScopedConn,
 2249   								"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows);
 2250   					}
 2251   
 2252   					this.results = executeInternal(-1, sendPacket,
 2253   							doStreaming, true,
 2254   							metadataFromCache, false);
 2255   
 2256   					if (oldCatalog != null) {
 2257   						this.connection.setCatalog(oldCatalog);
 2258   					}
 2259   				}
 2260   			} else {
 2261   				this.results = executeInternal(-1, sendPacket,
 2262   						doStreaming, true,
 2263   						metadataFromCache, false);
 2264   			}
 2265   
 2266   			if (oldCatalog != null) {
 2267   				locallyScopedConn.setCatalog(oldCatalog);
 2268   			}
 2269   			
 2270   			if (cachedMetadata != null) {
 2271   				locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
 2272   						cachedMetadata, this.results);
 2273   			} else {
 2274   				if (locallyScopedConn.getCacheResultSetMetadata()) {
 2275   					locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
 2276   							null /* will be created */, this.results);
 2277   				}
 2278   			}
 2279   		}
 2280   
 2281   		this.lastInsertId = this.results.getUpdateID();
 2282   
 2283   		return this.results;
 2284   	}
 2285   	
 2286   	/**
 2287   	 * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, SQL
 2288   	 * statements that return nothing such as SQL DDL statements can be
 2289   	 * executed.
 2290   	 * 
 2291   	 * @return either the row count for INSERT, UPDATE or DELETE; or 0 for SQL
 2292   	 *         statements that return nothing.
 2293   	 * 
 2294   	 * @exception SQLException
 2295   	 *                if a database access error occurs
 2296   	 */
 2297   	public int executeUpdate() throws SQLException {
 2298   		return executeUpdate(true, false);
 2299   	}
 2300   
 2301   	/*
 2302   	 * We need this variant, because ServerPreparedStatement calls this for
 2303   	 * batched updates, which will end up clobbering the warnings and generated
 2304   	 * keys we need to gather for the batch.
 2305   	 */
 2306   	protected int executeUpdate(
 2307   			boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch) throws SQLException {
 2308   		if (clearBatchedGeneratedKeysAndWarnings) {
 2309   			clearWarnings();
 2310   			this.batchedGeneratedKeys = null;
 2311   		}
 2312   
 2313   		return executeUpdate(this.parameterValues, this.parameterStreams,
 2314   				this.isStream, this.streamLengths, this.isNull, isBatch);
 2315   	}
 2316   
 2317   	/**
 2318   	 * Added to allow batch-updates
 2319   	 * 
 2320   	 * @param batchedParameterStrings
 2321   	 *            string values used in single statement
 2322   	 * @param batchedParameterStreams
 2323   	 *            stream values used in single statement
 2324   	 * @param batchedIsStream
 2325   	 *            flags for streams used in single statement
 2326   	 * @param batchedStreamLengths
 2327   	 *            lengths of streams to be read.
 2328   	 * @param batchedIsNull
 2329   	 *            flags for parameters that are null
 2330   	 * 
 2331   	 * @return the update count
 2332   	 * 
 2333   	 * @throws SQLException
 2334   	 *             if a database error occurs
 2335   	 */
 2336   	protected int executeUpdate(byte[][] batchedParameterStrings,
 2337   			InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
 2338   			int[] batchedStreamLengths, boolean[] batchedIsNull, boolean isReallyBatch)
 2339   			throws SQLException {
 2340   
 2341   		checkClosed();
 2342   
 2343   		ConnectionImpl locallyScopedConn = this.connection;
 2344   		
 2345   		if (locallyScopedConn.isReadOnly()) {
 2346   			throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") //$NON-NLS-1$
 2347   					+ Messages.getString("PreparedStatement.35"), //$NON-NLS-1$
 2348   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 2349   		}
 2350   
 2351   		if ((this.firstCharOfStmt == 'S')
 2352   				&& isSelectQuery()) { //$NON-NLS-1$
 2353   			throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), //$NON-NLS-1$
 2354   					"01S03", getExceptionInterceptor()); //$NON-NLS-1$
 2355   		}
 2356   
 2357   		if (this.results != null) {
 2358   			if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
 2359   				this.results.realClose(false);
 2360   			}
 2361   		}
 2362   
 2363   		ResultSetInternalMethods rs = null;
 2364   
 2365   		// The checking and changing of catalogs
 2366   		// must happen in sequence, so synchronize
 2367   		// on the same mutex that _conn is using
 2368   		synchronized (locallyScopedConn.getMutex()) {
 2369   			Buffer sendPacket = fillSendPacket(batchedParameterStrings,
 2370   					batchedParameterStreams, batchedIsStream,
 2371   					batchedStreamLengths);
 2372   
 2373   			String oldCatalog = null;
 2374   
 2375   			if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
 2376   				oldCatalog = locallyScopedConn.getCatalog();
 2377   				locallyScopedConn.setCatalog(this.currentCatalog);
 2378   			}
 2379   
 2380   			//
 2381   			// Only apply max_rows to selects
 2382   			//
 2383   			if (locallyScopedConn.useMaxRows()) {
 2384   				executeSimpleNonQuery(locallyScopedConn,
 2385   						"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 2386   			}
 2387   
 2388   			boolean oldInfoMsgState = false;
 2389   
 2390   			if (this.retrieveGeneratedKeys) {
 2391   				oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
 2392   				locallyScopedConn.setReadInfoMsgEnabled(true);
 2393   			}
 2394   
 2395   			rs = executeInternal(-1, sendPacket, false, false, null, 
 2396   					isReallyBatch);
 2397   
 2398   			if (this.retrieveGeneratedKeys) {
 2399   				locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
 2400   				rs.setFirstCharOfQuery(this.firstCharOfStmt);
 2401   			}
 2402   
 2403   			if (oldCatalog != null) {
 2404   				locallyScopedConn.setCatalog(oldCatalog);
 2405   			}
 2406   		}
 2407   
 2408   		this.results = rs;
 2409   
 2410   		this.updateCount = rs.getUpdateCount();
 2411   		
 2412   		if (containsOnDuplicateKeyUpdateInSQL() && 
 2413   				this.compensateForOnDuplicateKeyUpdate) {
 2414   			if (this.updateCount == 2 || this.updateCount == 0) {
 2415   				this.updateCount = 1;
 2416   			}
 2417   		}
 2418   
 2419   		int truncatedUpdateCount = 0;
 2420   
 2421   		if (this.updateCount > Integer.MAX_VALUE) {
 2422   			truncatedUpdateCount = Integer.MAX_VALUE;
 2423   		} else {
 2424   			truncatedUpdateCount = (int) this.updateCount;
 2425   		}
 2426   
 2427   		this.lastInsertId = rs.getUpdateID();
 2428   
 2429   		return truncatedUpdateCount;
 2430   	}
 2431   
 2432   	protected boolean containsOnDuplicateKeyUpdateInSQL() {
 2433   		return this.parseInfo.isOnDuplicateKeyUpdate;
 2434   	}
 2435   
 2436   
 2437   
 2438   	/**
 2439   	 * Creates the packet that contains the query to be sent to the server.
 2440   	 * 
 2441   	 * @return A Buffer filled with the query representing the
 2442   	 *         PreparedStatement.
 2443   	 * 
 2444   	 * @throws SQLException
 2445   	 *             if an error occurs.
 2446   	 */
 2447   	protected Buffer fillSendPacket() throws SQLException {
 2448   		return fillSendPacket(this.parameterValues, this.parameterStreams,
 2449   				this.isStream, this.streamLengths);
 2450   	}
 2451   
 2452   	/**
 2453   	 * Creates the packet that contains the query to be sent to the server.
 2454   	 * 
 2455   	 * @param batchedParameterStrings
 2456   	 *            the parameters as strings
 2457   	 * @param batchedParameterStreams
 2458   	 *            the parameters as streams
 2459   	 * @param batchedIsStream
 2460   	 *            is the given parameter a stream?
 2461   	 * @param batchedStreamLengths
 2462   	 *            the lengths of the streams (if appropriate)
 2463   	 * 
 2464   	 * @return a Buffer filled with the query that represents this statement
 2465   	 * 
 2466   	 * @throws SQLException
 2467   	 *             if an error occurs.
 2468   	 */
 2469   	protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
 2470   			InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
 2471   			int[] batchedStreamLengths) throws SQLException {
 2472   		Buffer sendPacket = this.connection.getIO().getSharedSendPacket();
 2473   
 2474   		sendPacket.clear();
 2475   
 2476   		sendPacket.writeByte((byte) MysqlDefs.QUERY);
 2477   
 2478   		boolean useStreamLengths = this.connection
 2479   				.getUseStreamLengthsInPrepStmts();
 2480   
 2481   		//
 2482   		// Try and get this allocation as close as possible
 2483   		// for BLOBs
 2484   		//
 2485   		int ensurePacketSize = 0;
 2486   
 2487   		String statementComment = this.connection.getStatementComment();
 2488   		
 2489   		byte[] commentAsBytes = null;
 2490   		
 2491   		if (statementComment != null) {
 2492   			if (this.charConverter != null) {
 2493   				commentAsBytes = this.charConverter.toBytes(statementComment);
 2494   			} else {
 2495   				commentAsBytes = StringUtils.getBytes(statementComment, this.charConverter,
 2496   						this.charEncoding, this.connection
 2497   								.getServerCharacterEncoding(), this.connection
 2498   								.parserKnowsUnicode(), getExceptionInterceptor());
 2499   			}
 2500   			
 2501   			ensurePacketSize += commentAsBytes.length;
 2502   			ensurePacketSize += 6; // for /*[space] [space]*/
 2503   		}
 2504   	
 2505   		for (int i = 0; i < batchedParameterStrings.length; i++) {
 2506   			if (batchedIsStream[i] && useStreamLengths) {
 2507   				ensurePacketSize += batchedStreamLengths[i];
 2508   			}
 2509   		}
 2510   
 2511   		if (ensurePacketSize != 0) {
 2512   			sendPacket.ensureCapacity(ensurePacketSize);
 2513   		}
 2514   
 2515   		if (commentAsBytes != null) {
 2516   			sendPacket.writeBytesNoNull(Constants.SLASH_STAR_SPACE_AS_BYTES);
 2517   			sendPacket.writeBytesNoNull(commentAsBytes);
 2518   			sendPacket.writeBytesNoNull(Constants.SPACE_STAR_SLASH_SPACE_AS_BYTES);
 2519   		}
 2520   		
 2521   		for (int i = 0; i < batchedParameterStrings.length; i++) {
 2522   			checkAllParametersSet(batchedParameterStrings[i],
 2523   					batchedParameterStreams[i], i);
 2524   
 2525   			sendPacket.writeBytesNoNull(this.staticSqlStrings[i]);
 2526   
 2527   			if (batchedIsStream[i]) {
 2528   				streamToBytes(sendPacket, batchedParameterStreams[i], true,
 2529   						batchedStreamLengths[i], useStreamLengths);
 2530   			} else {
 2531   				sendPacket.writeBytesNoNull(batchedParameterStrings[i]);
 2532   			}
 2533   		}
 2534   
 2535   		sendPacket
 2536   				.writeBytesNoNull(this.staticSqlStrings[batchedParameterStrings.length]);
 2537   
 2538   		return sendPacket;
 2539   	}
 2540   
 2541   	private void checkAllParametersSet(byte[] parameterString,
 2542   			InputStream parameterStream, int columnIndex) throws SQLException {
 2543   		if ((parameterString == null)
 2544   				&& parameterStream == null) {
 2545   			System.out.println(toString());
 2546   			throw SQLError.createSQLException(Messages
 2547   					.getString("PreparedStatement.40") //$NON-NLS-1$
 2548   					+ (columnIndex + 1), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS, getExceptionInterceptor());
 2549   		}
 2550   	}
 2551   
 2552   	/**
 2553   	 * Returns a prepared statement for the number of batched parameters, used when re-writing batch INSERTs.
 2554   	 */
 2555   	protected PreparedStatement prepareBatchedInsertSQL(ConnectionImpl localConn, int numBatches) throws SQLException {
 2556   		PreparedStatement pstmt = new PreparedStatement(localConn, "Rewritten batch of: " + this.originalSql, this.currentCatalog, this.parseInfo.getParseInfoForBatch(numBatches));
 2557   		pstmt.setRetrieveGeneratedKeys(this.retrieveGeneratedKeys);
 2558   		pstmt.rewrittenBatchSize = numBatches;
 2559   		
 2560   		return pstmt;
 2561   	}
 2562   
 2563   	protected int rewrittenBatchSize = 0;
 2564   	
 2565   	public int getRewrittenBatchSize() {
 2566   		return this.rewrittenBatchSize;
 2567   	}
 2568   	
 2569   	public String getNonRewrittenSql() {
 2570   		int indexOfBatch = this.originalSql.indexOf(" of: ");
 2571   		
 2572   		if (indexOfBatch != -1) {
 2573   			return this.originalSql.substring(indexOfBatch + 5);
 2574   		}
 2575   		
 2576   		return this.originalSql;
 2577   	}
 2578   	
 2579   	
 2580   	/**
 2581   	 * DOCUMENT ME!
 2582   	 * 
 2583   	 * @param parameterIndex
 2584   	 *            DOCUMENT ME!
 2585   	 * 
 2586   	 * @return DOCUMENT ME!
 2587   	 * 
 2588   	 * @throws SQLException
 2589   	 *             DOCUMENT ME!
 2590   	 */
 2591   	public byte[] getBytesRepresentation(int parameterIndex)
 2592   			throws SQLException {
 2593   		if (this.isStream[parameterIndex]) {
 2594   			return streamToBytes(this.parameterStreams[parameterIndex], false,
 2595   					this.streamLengths[parameterIndex], this.connection
 2596   							.getUseStreamLengthsInPrepStmts());
 2597   		}
 2598   
 2599   		byte[] parameterVal = this.parameterValues[parameterIndex];
 2600   
 2601   		if (parameterVal == null) {
 2602   			return null;
 2603   		}
 2604   
 2605   		if ((parameterVal[0] == '\'')
 2606   				&& (parameterVal[parameterVal.length - 1] == '\'')) {
 2607   			byte[] valNoQuotes = new byte[parameterVal.length - 2];
 2608   			System.arraycopy(parameterVal, 1, valNoQuotes, 0,
 2609   					parameterVal.length - 2);
 2610   
 2611   			return valNoQuotes;
 2612   		}
 2613   
 2614   		return parameterVal;
 2615   	}
 2616   	
 2617   	/**
 2618   	 * Get bytes representation for a parameter in a statement batch.
 2619   	 * @param parameterIndex
 2620   	 * @param commandIndex
 2621   	 * @return
 2622   	 * @throws SQLException
 2623   	 */
 2624   	protected byte[] getBytesRepresentationForBatch(int parameterIndex, int commandIndex)
 2625   				throws SQLException {
 2626   		Object batchedArg = batchedArgs.get(commandIndex);
 2627   		if (batchedArg instanceof String) {
 2628   			try {
 2629   				return ((String)batchedArg).getBytes(charEncoding);
 2630   				
 2631   			} catch (UnsupportedEncodingException uue) {
 2632   				throw new RuntimeException(Messages
 2633   						.getString("PreparedStatement.32") //$NON-NLS-1$
 2634   						+ this.charEncoding
 2635   						+ Messages.getString("PreparedStatement.33")); //$NON-NLS-1$
 2636   			}
 2637   		}
 2638   		
 2639   		BatchParams params = (BatchParams)batchedArg;
 2640   		if (params.isStream[parameterIndex])
 2641   			return streamToBytes(params.parameterStreams[parameterIndex], false,
 2642   					params.streamLengths[parameterIndex],
 2643   					connection.getUseStreamLengthsInPrepStmts());
 2644   		byte parameterVal[] = params.parameterStrings[parameterIndex];
 2645   		if (parameterVal == null)
 2646   			return null;
 2647   		
 2648   		if ((parameterVal[0] == '\'')
 2649   				&& (parameterVal[parameterVal.length - 1] == '\'')) {
 2650   			byte[] valNoQuotes = new byte[parameterVal.length - 2];
 2651   			System.arraycopy(parameterVal, 1, valNoQuotes, 0,
 2652   					parameterVal.length - 2);
 2653   
 2654   			return valNoQuotes;
 2655   		}
 2656   
 2657   		return parameterVal;
 2658   	}
 2659   
 2660   	// --------------------------JDBC 2.0-----------------------------
 2661   
 2662   	private final String getDateTimePattern(String dt, boolean toTime)
 2663   			throws Exception {
 2664   		//
 2665   		// Special case
 2666   		//
 2667   		int dtLength = (dt != null) ? dt.length() : 0;
 2668   
 2669   		if ((dtLength >= 8) && (dtLength <= 10)) {
 2670   			int dashCount = 0;
 2671   			boolean isDateOnly = true;
 2672   
 2673   			for (int i = 0; i < dtLength; i++) {
 2674   				char c = dt.charAt(i);
 2675   
 2676   				if (!Character.isDigit(c) && (c != '-')) {
 2677   					isDateOnly = false;
 2678   
 2679   					break;
 2680   				}
 2681   
 2682   				if (c == '-') {
 2683   					dashCount++;
 2684   				}
 2685   			}
 2686   
 2687   			if (isDateOnly && (dashCount == 2)) {
 2688   				return "yyyy-MM-dd"; //$NON-NLS-1$
 2689   			}
 2690   		}
 2691   
 2692   		//
 2693   		// Special case - time-only
 2694   		//
 2695   		boolean colonsOnly = true;
 2696   
 2697   		for (int i = 0; i < dtLength; i++) {
 2698   			char c = dt.charAt(i);
 2699   
 2700   			if (!Character.isDigit(c) && (c != ':')) {
 2701   				colonsOnly = false;
 2702   
 2703   				break;
 2704   			}
 2705   		}
 2706   
 2707   		if (colonsOnly) {
 2708   			return "HH:mm:ss"; //$NON-NLS-1$
 2709   		}
 2710   
 2711   		int n;
 2712   		int z;
 2713   		int count;
 2714   		int maxvecs;
 2715   		char c;
 2716   		char separator;
 2717   		StringReader reader = new StringReader(dt + " "); //$NON-NLS-1$
 2718   		ArrayList vec = new ArrayList();
 2719   		ArrayList vecRemovelist = new ArrayList();
 2720   		Object[] nv = new Object[3];
 2721   		Object[] v;
 2722   		nv[0] = Constants.characterValueOf('y');
 2723   		nv[1] = new StringBuffer();
 2724   		nv[2] = Constants.integerValueOf(0);
 2725   		vec.add(nv);
 2726   
 2727   		if (toTime) {
 2728   			nv = new Object[3];
 2729   			nv[0] = Constants.characterValueOf('h');
 2730   			nv[1] = new StringBuffer();
 2731   			nv[2] = Constants.integerValueOf(0);
 2732   			vec.add(nv);
 2733   		}
 2734   
 2735   		while ((z = reader.read()) != -1) {
 2736   			separator = (char) z;
 2737   			maxvecs = vec.size();
 2738   
 2739   			for (count = 0; count < maxvecs; count++) {
 2740   				v = (Object[]) vec.get(count);
 2741   				n = ((Integer) v[2]).intValue();
 2742   				c = getSuccessor(((Character) v[0]).charValue(), n);
 2743   
 2744   				if (!Character.isLetterOrDigit(separator)) {
 2745   					if ((c == ((Character) v[0]).charValue()) && (c != 'S')) {
 2746   						vecRemovelist.add(v);
 2747   					} else {
 2748   						((StringBuffer) v[1]).append(separator);
 2749   
 2750   						if ((c == 'X') || (c == 'Y')) {
 2751   							v[2] = Constants.integerValueOf(4);
 2752   						}
 2753   					}
 2754   				} else {
 2755   					if (c == 'X') {
 2756   						c = 'y';
 2757   						nv = new Object[3];
 2758   						nv[1] = (new StringBuffer(((StringBuffer) v[1])
 2759   								.toString())).append('M');
 2760   						nv[0] = Constants.characterValueOf('M');
 2761   						nv[2] = Constants.integerValueOf(1);
 2762   						vec.add(nv);
 2763   					} else if (c == 'Y') {
 2764   						c = 'M';
 2765   						nv = new Object[3];
 2766   						nv[1] = (new StringBuffer(((StringBuffer) v[1])
 2767   								.toString())).append('d');
 2768   						nv[0] = Constants.characterValueOf('d');
 2769   						nv[2] = Constants.integerValueOf(1);
 2770   						vec.add(nv);
 2771   					}
 2772   
 2773   					((StringBuffer) v[1]).append(c);
 2774   
 2775   					if (c == ((Character) v[0]).charValue()) {
 2776   						v[2] = Constants.integerValueOf(n + 1);
 2777   					} else {
 2778   						v[0] = Constants.characterValueOf(c);
 2779   						v[2] = Constants.integerValueOf(1);
 2780   					}
 2781   				}
 2782   			}
 2783   
 2784   			int size = vecRemovelist.size();
 2785   
 2786   			for (int i = 0; i < size; i++) {
 2787   				v = (Object[]) vecRemovelist.get(i);
 2788   				vec.remove(v);
 2789   			}
 2790   
 2791   			vecRemovelist.clear();
 2792   		}
 2793   
 2794   		int size = vec.size();
 2795   
 2796   		for (int i = 0; i < size; i++) {
 2797   			v = (Object[]) vec.get(i);
 2798   			c = ((Character) v[0]).charValue();
 2799   			n = ((Integer) v[2]).intValue();
 2800   
 2801   			boolean bk = getSuccessor(c, n) != c;
 2802   			boolean atEnd = (((c == 's') || (c == 'm') || ((c == 'h') && toTime)) && bk);
 2803   			boolean finishesAtDate = (bk && (c == 'd') && !toTime);
 2804   			boolean containsEnd = (((StringBuffer) v[1]).toString()
 2805   					.indexOf('W') != -1);
 2806   
 2807   			if ((!atEnd && !finishesAtDate) || (containsEnd)) {
 2808   				vecRemovelist.add(v);
 2809   			}
 2810   		}
 2811   
 2812   		size = vecRemovelist.size();
 2813   
 2814   		for (int i = 0; i < size; i++) {
 2815   			vec.remove(vecRemovelist.get(i));
 2816   		}
 2817   
 2818   		vecRemovelist.clear();
 2819   		v = (Object[]) vec.get(0); // might throw exception
 2820   
 2821   		StringBuffer format = (StringBuffer) v[1];
 2822   		format.setLength(format.length() - 1);
 2823   
 2824   		return format.toString();
 2825   	}
 2826   
 2827   	/**
 2828   	 * The number, types and properties of a ResultSet's columns are provided by
 2829   	 * the getMetaData method.
 2830   	 * 
 2831   	 * @return the description of a ResultSet's columns
 2832   	 * 
 2833   	 * @exception SQLException
 2834   	 *                if a database-access error occurs.
 2835   	 */
 2836   	public java.sql.ResultSetMetaData getMetaData()
 2837   			throws SQLException {
 2838   
 2839   		//
 2840   		// We could just tack on a LIMIT 0 here no matter what the 
 2841   		// statement, and check if a result set was returned or not,
 2842   		// but I'm not comfortable with that, myself, so we take
 2843   		// the "safer" road, and only allow metadata for _actual_
 2844   		// SELECTS (but not SHOWs).
 2845   		// 
 2846   		// CALL's are trapped further up and you end up with a 
 2847   		// CallableStatement anyway.
 2848   		//
 2849   		
 2850   		if (!isSelectQuery()) {
 2851   			return null;
 2852   		}
 2853   
 2854   		PreparedStatement mdStmt = null;
 2855   		java.sql.ResultSet mdRs = null;
 2856   
 2857   		if (this.pstmtResultMetaData == null) {
 2858   			try {
 2859   				mdStmt = new PreparedStatement(this.connection,
 2860   						this.originalSql, this.currentCatalog, this.parseInfo);
 2861   
 2862   				mdStmt.setMaxRows(0);
 2863   
 2864   				int paramCount = this.parameterValues.length;
 2865   
 2866   				for (int i = 1; i <= paramCount; i++) {
 2867   					mdStmt.setString(i, ""); //$NON-NLS-1$
 2868   				}
 2869   
 2870   				boolean hadResults = mdStmt.execute();
 2871   
 2872   				if (hadResults) {
 2873   					mdRs = mdStmt.getResultSet();
 2874   
 2875   					this.pstmtResultMetaData = mdRs.getMetaData();
 2876   				} else {
 2877   					this.pstmtResultMetaData = new ResultSetMetaData(
 2878   							new Field[0], 
 2879   							this.connection.getUseOldAliasMetadataBehavior(), getExceptionInterceptor());
 2880   				}
 2881   			} finally {
 2882   				SQLException sqlExRethrow = null;
 2883   
 2884   				if (mdRs != null) {
 2885   					try {
 2886   						mdRs.close();
 2887   					} catch (SQLException sqlEx) {
 2888   						sqlExRethrow = sqlEx;
 2889   					}
 2890   
 2891   					mdRs = null;
 2892   				}
 2893   
 2894   				if (mdStmt != null) {
 2895   					try {
 2896   						mdStmt.close();
 2897   					} catch (SQLException sqlEx) {
 2898   						sqlExRethrow = sqlEx;
 2899   					}
 2900   
 2901   					mdStmt = null;
 2902   				}
 2903   
 2904   				if (sqlExRethrow != null) {
 2905   					throw sqlExRethrow;
 2906   				}
 2907   			}
 2908   		}
 2909   
 2910   		return this.pstmtResultMetaData;
 2911   	}
 2912   
 2913   	protected boolean isSelectQuery() {
 2914   		return StringUtils.startsWithIgnoreCaseAndWs(
 2915   				StringUtils.stripComments(this.originalSql,
 2916   						"'\"", "'\"", true, false, true, true), 
 2917   						"SELECT");
 2918   	}
 2919   
 2920   	/**
 2921   	 * @see PreparedStatement#getParameterMetaData()
 2922   	 */
 2923   	public ParameterMetaData getParameterMetaData() 
 2924   		throws SQLException {
 2925   	if (this.parameterMetaData == null) {
 2926   		if (this.connection.getGenerateSimpleParameterMetadata()) {
 2927   			this.parameterMetaData = new MysqlParameterMetadata(this.parameterCount);
 2928   		} else {
 2929   			this.parameterMetaData = new MysqlParameterMetadata(
 2930   				null, this.parameterCount, getExceptionInterceptor());
 2931   		}
 2932   	}
 2933   	
 2934   	return this.parameterMetaData;
 2935   }
 2936   
 2937   	ParseInfo getParseInfo() {
 2938   		return this.parseInfo;
 2939   	}
 2940   
 2941   	private final char getSuccessor(char c, int n) {
 2942   		return ((c == 'y') && (n == 2)) ? 'X'
 2943   				: (((c == 'y') && (n < 4)) ? 'y'
 2944   						: ((c == 'y') ? 'M'
 2945   								: (((c == 'M') && (n == 2)) ? 'Y'
 2946   										: (((c == 'M') && (n < 3)) ? 'M'
 2947   												: ((c == 'M') ? 'd'
 2948   														: (((c == 'd') && (n < 2)) ? 'd'
 2949   																: ((c == 'd') ? 'H'
 2950   																		: (((c == 'H') && (n < 2)) ? 'H'
 2951   																				: ((c == 'H') ? 'm'
 2952   																						: (((c == 'm') && (n < 2)) ? 'm'
 2953   																								: ((c == 'm') ? 's'
 2954   																										: (((c == 's') && (n < 2)) ? 's'
 2955   																												: 'W'))))))))))));
 2956   	}
 2957   
 2958   	/**
 2959   	 * Used to escape binary data with hex for mb charsets
 2960   	 * 
 2961   	 * @param buf
 2962   	 * @param packet
 2963   	 * @param size
 2964   	 * @throws SQLException
 2965   	 */
 2966   	private final void hexEscapeBlock(byte[] buf, Buffer packet, int size)
 2967   			throws SQLException {
 2968   		for (int i = 0; i < size; i++) {
 2969   			byte b = buf[i];
 2970   			int lowBits = (b & 0xff) / 16;
 2971   			int highBits = (b & 0xff) % 16;
 2972   
 2973   			packet.writeByte(HEX_DIGITS[lowBits]);
 2974   			packet.writeByte(HEX_DIGITS[highBits]);
 2975   		}
 2976   	}
 2977   
 2978   	private void initializeFromParseInfo() throws SQLException {
 2979   		this.staticSqlStrings = this.parseInfo.staticSql;
 2980   		this.hasLimitClause = this.parseInfo.foundLimitClause;
 2981   		this.isLoadDataQuery = this.parseInfo.foundLoadData;
 2982   		this.firstCharOfStmt = this.parseInfo.firstStmtChar;
 2983   
 2984   		this.parameterCount = this.staticSqlStrings.length - 1;
 2985   
 2986   		this.parameterValues = new byte[this.parameterCount][];
 2987   		this.parameterStreams = new InputStream[this.parameterCount];
 2988   		this.isStream = new boolean[this.parameterCount];
 2989   		this.streamLengths = new int[this.parameterCount];
 2990   		this.isNull = new boolean[this.parameterCount];
 2991   		this.parameterTypes = new int[this.parameterCount];
 2992   	
 2993   		clearParameters();
 2994   
 2995   		for (int j = 0; j < this.parameterCount; j++) {
 2996   			this.isStream[j] = false;
 2997   		}
 2998   	}
 2999   
 3000   	boolean isNull(int paramIndex) {
 3001   		return this.isNull[paramIndex];
 3002   	}
 3003   
 3004   	private final int readblock(InputStream i, byte[] b) throws SQLException {
 3005   		try {
 3006   			return i.read(b);
 3007   		} catch (Throwable ex) {
 3008   			SQLException sqlEx = SQLError.createSQLException(Messages.getString("PreparedStatement.56") //$NON-NLS-1$
 3009   					+ ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 3010   			sqlEx.initCause(ex);
 3011   			
 3012   			throw sqlEx;
 3013   		}
 3014   	}
 3015   
 3016   	private final int readblock(InputStream i, byte[] b, int length)
 3017   			throws SQLException {
 3018   		try {
 3019   			int lengthToRead = length;
 3020   
 3021   			if (lengthToRead > b.length) {
 3022   				lengthToRead = b.length;
 3023   			}
 3024   
 3025   			return i.read(b, 0, lengthToRead);
 3026   		} catch (Throwable ex) {
 3027   			SQLException sqlEx = SQLError.createSQLException(Messages.getString("PreparedStatement.56") //$NON-NLS-1$
 3028   					+ ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 3029   			sqlEx.initCause(ex);
 3030   			
 3031   			throw sqlEx;
 3032   		}
 3033   	}
 3034   
 3035   	/**
 3036   	 * Closes this statement, releasing all resources
 3037   	 * 
 3038   	 * @param calledExplicitly
 3039   	 *            was this called by close()?
 3040   	 * 
 3041   	 * @throws SQLException
 3042   	 *             if an error occurs
 3043   	 */
 3044   	protected void realClose(boolean calledExplicitly, 
 3045   			boolean closeOpenResults) throws SQLException {
 3046   		if (this.useUsageAdvisor) {
 3047   			if (this.numberOfExecutions <= 1) {
 3048   				String message = Messages.getString("PreparedStatement.43"); //$NON-NLS-1$
 3049   
 3050   				this.eventSink.consumeEvent(new ProfilerEvent(
 3051   						ProfilerEvent.TYPE_WARN, "", this.currentCatalog, //$NON-NLS-1$
 3052   						this.connectionId, this.getId(), -1, System
 3053   								.currentTimeMillis(), 0, Constants.MILLIS_I18N,
 3054   								null,
 3055   						this.pointOfOrigin, message));
 3056   			}
 3057   		}
 3058   		
 3059   		super.realClose(calledExplicitly, closeOpenResults);
 3060   
 3061   		this.dbmd = null;
 3062   		this.originalSql = null;
 3063   		this.staticSqlStrings = null;
 3064   		this.parameterValues = null;
 3065   		this.parameterStreams = null;
 3066   		this.isStream = null;
 3067   		this.streamLengths = null;
 3068   		this.isNull = null;
 3069   		this.streamConvertBuf = null;
 3070   		this.parameterTypes = null;
 3071   	}
 3072   
 3073   	/**
 3074   	 * JDBC 2.0 Set an Array parameter.
 3075   	 * 
 3076   	 * @param i
 3077   	 *            the first parameter is 1, the second is 2, ...
 3078   	 * @param x
 3079   	 *            an object representing an SQL array
 3080   	 * 
 3081   	 * @throws SQLException
 3082   	 *             because this method is not implemented.
 3083   	 * @throws NotImplemented
 3084   	 *             DOCUMENT ME!
 3085   	 */
 3086   	public void setArray(int i, Array x) throws SQLException {
 3087   		throw SQLError.notImplemented();
 3088   	}
 3089   
 3090   	/**
 3091   	 * When a very large ASCII value is input to a LONGVARCHAR parameter, it may
 3092   	 * be more practical to send it via a java.io.InputStream. JDBC will read
 3093   	 * the data from the stream as needed, until it reaches end-of-file. The
 3094   	 * JDBC driver will do any necessary conversion from ASCII to the database
 3095   	 * char format.
 3096   	 * 
 3097   	 * <P>
 3098   	 * <B>Note:</B> This stream object can either be a standard Java stream
 3099   	 * object or your own subclass that implements the standard interface.
 3100   	 * </p>
 3101   	 * 
 3102   	 * @param parameterIndex
 3103   	 *            the first parameter is 1...
 3104   	 * @param x
 3105   	 *            the parameter value
 3106   	 * @param length
 3107   	 *            the number of bytes in the stream
 3108   	 * 
 3109   	 * @exception SQLException
 3110   	 *                if a database access error occurs
 3111   	 */
 3112   	public void setAsciiStream(int parameterIndex, InputStream x,
 3113   			int length) throws SQLException {
 3114   		if (x == null) {
 3115   			setNull(parameterIndex, java.sql.Types.VARCHAR);
 3116   		} else {
 3117   			setBinaryStream(parameterIndex, x, length);
 3118   		}
 3119   	}
 3120   
 3121   	/**
 3122   	 * Set a parameter to a java.math.BigDecimal value. The driver converts this
 3123   	 * to a SQL NUMERIC value when it sends it to the database.
 3124   	 * 
 3125   	 * @param parameterIndex
 3126   	 *            the first parameter is 1...
 3127   	 * @param x
 3128   	 *            the parameter value
 3129   	 * 
 3130   	 * @exception SQLException
 3131   	 *                if a database access error occurs
 3132   	 */
 3133   	public void setBigDecimal(int parameterIndex, BigDecimal x)
 3134   			throws SQLException {
 3135   		if (x == null) {
 3136   			setNull(parameterIndex, java.sql.Types.DECIMAL);
 3137   		} else {
 3138   			setInternal(parameterIndex, StringUtils
 3139   					.fixDecimalExponent(StringUtils.consistentToString(x)));
 3140   			
 3141   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DECIMAL;
 3142   		}
 3143   	}
 3144   
 3145   	/**
 3146   	 * When a very large binary value is input to a LONGVARBINARY parameter, it
 3147   	 * may be more practical to send it via a java.io.InputStream. JDBC will
 3148   	 * read the data from the stream as needed, until it reaches end-of-file.
 3149   	 * 
 3150   	 * <P>
 3151   	 * <B>Note:</B> This stream object can either be a standard Java stream
 3152   	 * object or your own subclass that implements the standard interface.
 3153   	 * </p>
 3154   	 * 
 3155   	 * @param parameterIndex
 3156   	 *            the first parameter is 1...
 3157   	 * @param x
 3158   	 *            the parameter value
 3159   	 * @param length
 3160   	 *            the number of bytes to read from the stream (ignored)
 3161   	 * 
 3162   	 * @throws SQLException
 3163   	 *             if a database access error occurs
 3164   	 */
 3165   	public void setBinaryStream(int parameterIndex, InputStream x, int length)
 3166   			throws SQLException {
 3167   		if (x == null) {
 3168   			setNull(parameterIndex, java.sql.Types.BINARY);
 3169   		} else {
 3170   			int parameterIndexOffset = getParameterIndexOffset();
 3171   			
 3172   			if ((parameterIndex < 1)
 3173   					|| (parameterIndex > this.staticSqlStrings.length)) {
 3174   				throw SQLError.createSQLException(
 3175   						Messages.getString("PreparedStatement.2") //$NON-NLS-1$
 3176   								+ parameterIndex
 3177   								+ Messages.getString("PreparedStatement.3") + this.staticSqlStrings.length + Messages.getString("PreparedStatement.4"), //$NON-NLS-1$ //$NON-NLS-2$
 3178   						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3179   			} else if (parameterIndexOffset == -1 && parameterIndex == 1) {
 3180   				throw SQLError.createSQLException("Can't set IN parameter for return value of stored function call.", 
 3181   						SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3182   			}
 3183   
 3184   
 3185   			this.parameterStreams[parameterIndex - 1 + parameterIndexOffset] = x;
 3186   			this.isStream[parameterIndex - 1 + parameterIndexOffset] = true;
 3187   			this.streamLengths[parameterIndex - 1 + parameterIndexOffset] = length;
 3188   			this.isNull[parameterIndex - 1 + parameterIndexOffset] = false;
 3189   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BLOB;
 3190   		}
 3191   	}
 3192   
 3193   	public void setBlob(int parameterIndex, InputStream inputStream, long length)
 3194   			throws SQLException {
 3195   		setBinaryStream(parameterIndex, inputStream, (int)length);
 3196   	}
 3197   
 3198   	/**
 3199   	 * JDBC 2.0 Set a BLOB parameter.
 3200   	 * 
 3201   	 * @param i
 3202   	 *            the first parameter is 1, the second is 2, ...
 3203   	 * @param x
 3204   	 *            an object representing a BLOB
 3205   	 * 
 3206   	 * @throws SQLException
 3207   	 *             if a database error occurs
 3208   	 */
 3209   	public void setBlob(int i, java.sql.Blob x) throws SQLException {
 3210   		if (x == null) {
 3211   			setNull(i, Types.BLOB);
 3212   		} else {
 3213   			ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
 3214   
 3215   			bytesOut.write('\'');
 3216   			escapeblockFast(x.getBytes(1, (int) x.length()), bytesOut, (int) x
 3217   					.length());
 3218   			bytesOut.write('\'');
 3219   
 3220   			setInternal(i, bytesOut.toByteArray());
 3221   			
 3222   			this.parameterTypes[i - 1 + getParameterIndexOffset()] = Types.BLOB;
 3223   		}
 3224   	}
 3225   
 3226   	/**
 3227   	 * Set a parameter to a Java boolean value. The driver converts this to a
 3228   	 * SQL BIT value when it sends it to the database.
 3229   	 * 
 3230   	 * @param parameterIndex
 3231   	 *            the first parameter is 1...
 3232   	 * @param x
 3233   	 *            the parameter value
 3234   	 * 
 3235   	 * @throws SQLException
 3236   	 *             if a database access error occurs
 3237   	 */
 3238   	public void setBoolean(int parameterIndex, boolean x) throws SQLException {
 3239   		if (this.useTrueBoolean) {
 3240   			setInternal(parameterIndex, x ? "1" : "0"); //$NON-NLS-1$ //$NON-NLS-2$
 3241   		} else {
 3242   			setInternal(parameterIndex, x ? "'t'" : "'f'"); //$NON-NLS-1$ //$NON-NLS-2$
 3243   			
 3244   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BOOLEAN;
 3245   		}
 3246   	}
 3247   
 3248   	/**
 3249   	 * Set a parameter to a Java byte value. The driver converts this to a SQL
 3250   	 * TINYINT value when it sends it to the database.
 3251   	 * 
 3252   	 * @param parameterIndex
 3253   	 *            the first parameter is 1...
 3254   	 * @param x
 3255   	 *            the parameter value
 3256   	 * 
 3257   	 * @exception SQLException
 3258   	 *                if a database access error occurs
 3259   	 */
 3260   	public void setByte(int parameterIndex, byte x) throws SQLException {
 3261   		setInternal(parameterIndex, String.valueOf(x));
 3262   		
 3263   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TINYINT;
 3264   	}
 3265   
 3266   	/**
 3267   	 * Set a parameter to a Java array of bytes. The driver converts this to a
 3268   	 * SQL VARBINARY or LONGVARBINARY (depending on the argument's size relative
 3269   	 * to the driver's limits on VARBINARYs) when it sends it to the database.
 3270   	 * 
 3271   	 * @param parameterIndex
 3272   	 *            the first parameter is 1...
 3273   	 * @param x
 3274   	 *            the parameter value
 3275   	 * 
 3276   	 * @exception SQLException
 3277   	 *                if a database access error occurs
 3278   	 */
 3279   	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
 3280   		setBytes(parameterIndex, x, true, true);
 3281   		
 3282   		if (x != null) {
 3283   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BINARY;
 3284   		}
 3285   	}
 3286   
 3287   	protected void setBytes(int parameterIndex, byte[] x,
 3288   			boolean checkForIntroducer, boolean escapeForMBChars)
 3289   			throws SQLException {
 3290   		if (x == null) {
 3291   			setNull(parameterIndex, java.sql.Types.BINARY);
 3292   		} else {
 3293   			String connectionEncoding = this.connection.getEncoding();
 3294   
 3295   			if (this.connection.isNoBackslashEscapesSet()
 3296   					|| (escapeForMBChars 
 3297   							&& this.connection.getUseUnicode()
 3298   							&& connectionEncoding != null
 3299   							&& CharsetMapping.isMultibyteCharset(connectionEncoding))) {
 3300   
 3301   				// Send as hex
 3302   
 3303   				ByteArrayOutputStream bOut = new ByteArrayOutputStream(
 3304   						(x.length * 2) + 3);
 3305   				bOut.write('x');
 3306   				bOut.write('\'');
 3307   
 3308   				for (int i = 0; i < x.length; i++) {
 3309   					int lowBits = (x[i] & 0xff) / 16;
 3310   					int highBits = (x[i] & 0xff) % 16;
 3311   
 3312   					bOut.write(HEX_DIGITS[lowBits]);
 3313   					bOut.write(HEX_DIGITS[highBits]);
 3314   				}
 3315   
 3316   				bOut.write('\'');
 3317   
 3318   				setInternal(parameterIndex, bOut.toByteArray());
 3319   
 3320   				return;
 3321   			}
 3322   
 3323   			// escape them
 3324   			int numBytes = x.length;
 3325   
 3326   			int pad = 2;
 3327   
 3328   			boolean needsIntroducer = checkForIntroducer
 3329   					&& this.connection.versionMeetsMinimum(4, 1, 0);
 3330   
 3331   			if (needsIntroducer) {
 3332   				pad += 7;
 3333   			}
 3334   
 3335   			ByteArrayOutputStream bOut = new ByteArrayOutputStream(numBytes
 3336   					+ pad);
 3337   
 3338   			if (needsIntroducer) {
 3339   				bOut.write('_');
 3340   				bOut.write('b');
 3341   				bOut.write('i');
 3342   				bOut.write('n');
 3343   				bOut.write('a');
 3344   				bOut.write('r');
 3345   				bOut.write('y');
 3346   			}
 3347   			bOut.write('\'');
 3348   
 3349   			for (int i = 0; i < numBytes; ++i) {
 3350   				byte b = x[i];
 3351   
 3352   				switch (b) {
 3353   				case 0: /* Must be escaped for 'mysql' */
 3354   					bOut.write('\\');
 3355   					bOut.write('0');
 3356   
 3357   					break;
 3358   
 3359   				case '\n': /* Must be escaped for logs */
 3360   					bOut.write('\\');
 3361   					bOut.write('n');
 3362   
 3363   					break;
 3364   
 3365   				case '\r':
 3366   					bOut.write('\\');
 3367   					bOut.write('r');
 3368   
 3369   					break;
 3370   
 3371   				case '\\':
 3372   					bOut.write('\\');
 3373   					bOut.write('\\');
 3374   
 3375   					break;
 3376   
 3377   				case '\'':
 3378   					bOut.write('\\');
 3379   					bOut.write('\'');
 3380   
 3381   					break;
 3382   
 3383   				case '"': /* Better safe than sorry */
 3384   					bOut.write('\\');
 3385   					bOut.write('"');
 3386   
 3387   					break;
 3388   
 3389   				case '\032': /* This gives problems on Win32 */
 3390   					bOut.write('\\');
 3391   					bOut.write('Z');
 3392   
 3393   					break;
 3394   
 3395   				default:
 3396   					bOut.write(b);
 3397   				}
 3398   			}
 3399   
 3400   			bOut.write('\'');
 3401   
 3402   			setInternal(parameterIndex, bOut.toByteArray());
 3403   		}
 3404   	}
 3405   
 3406   	/**
 3407   	 * Used by updatable result sets for refreshRow() because the parameter has
 3408   	 * already been escaped for updater or inserter prepared statements.
 3409   	 * 
 3410   	 * @param parameterIndex
 3411   	 *            the parameter to set.
 3412   	 * @param parameterAsBytes
 3413   	 *            the parameter as a string.
 3414   	 * 
 3415   	 * @throws SQLException
 3416   	 *             if an error occurs
 3417   	 */
 3418   	protected void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes)
 3419   			throws SQLException {
 3420   		byte[] parameterWithQuotes = new byte[parameterAsBytes.length + 2];
 3421   		parameterWithQuotes[0] = '\'';
 3422   		System.arraycopy(parameterAsBytes, 0, parameterWithQuotes, 1,
 3423   				parameterAsBytes.length);
 3424   		parameterWithQuotes[parameterAsBytes.length + 1] = '\'';
 3425   
 3426   		setInternal(parameterIndex, parameterWithQuotes);
 3427   	}
 3428   
 3429   	protected void setBytesNoEscapeNoQuotes(int parameterIndex,
 3430   			byte[] parameterAsBytes) throws SQLException {
 3431   		setInternal(parameterIndex, parameterAsBytes);
 3432   	}
 3433   
 3434   	/**
 3435   	 * JDBC 2.0 When a very large UNICODE value is input to a LONGVARCHAR
 3436   	 * parameter, it may be more practical to send it via a java.io.Reader. JDBC
 3437   	 * will read the data from the stream as needed, until it reaches
 3438   	 * end-of-file. The JDBC driver will do any necessary conversion from
 3439   	 * UNICODE to the database char format.
 3440   	 * 
 3441   	 * <P>
 3442   	 * <B>Note:</B> This stream object can either be a standard Java stream
 3443   	 * object or your own subclass that implements the standard interface.
 3444   	 * </p>
 3445   	 * 
 3446   	 * @param parameterIndex
 3447   	 *            the first parameter is 1, the second is 2, ...
 3448   	 * @param reader
 3449   	 *            the java reader which contains the UNICODE data
 3450   	 * @param length
 3451   	 *            the number of characters in the stream
 3452   	 * 
 3453   	 * @exception SQLException
 3454   	 *                if a database-access error occurs.
 3455   	 */
 3456   	public void setCharacterStream(int parameterIndex, java.io.Reader reader,
 3457   			int length) throws SQLException {
 3458   		try {
 3459   			if (reader == null) {
 3460   				setNull(parameterIndex, Types.LONGVARCHAR);
 3461   			} else {
 3462   				char[] c = null;
 3463   				int len = 0;
 3464   
 3465   				boolean useLength = this.connection
 3466   						.getUseStreamLengthsInPrepStmts();
 3467   
 3468   				String forcedEncoding = this.connection.getClobCharacterEncoding();
 3469   				
 3470   				if (useLength && (length != -1)) {
 3471   					c = new char[length];
 3472   
 3473   					int numCharsRead = readFully(reader, c, length); // blocks
 3474   					// until
 3475   					// all
 3476   					// read
 3477   
 3478   					if (forcedEncoding == null) {
 3479   						setString(parameterIndex, new String(c, 0, numCharsRead));
 3480   					} else {
 3481   						try {
 3482   							setBytes(parameterIndex, new String(c, 
 3483   									0, 
 3484   									numCharsRead).getBytes(forcedEncoding));
 3485   						} catch (UnsupportedEncodingException uee) {
 3486   							throw SQLError.createSQLException("Unsupported character encoding " + 
 3487   									forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3488   						}
 3489   					}
 3490   				} else {
 3491   					c = new char[4096];
 3492   
 3493   					StringBuffer buf = new StringBuffer();
 3494   
 3495   					while ((len = reader.read(c)) != -1) {
 3496   						buf.append(c, 0, len);
 3497   					}
 3498   
 3499   					if (forcedEncoding == null) {
 3500   						setString(parameterIndex, buf.toString());
 3501   					} else {
 3502   						try {
 3503   							setBytes(parameterIndex, 
 3504   									buf.toString().getBytes(forcedEncoding));
 3505   						} catch (UnsupportedEncodingException uee) {
 3506   							throw SQLError.createSQLException("Unsupported character encoding " + 
 3507   									forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3508   						}
 3509   					}
 3510   				}
 3511   				
 3512   				this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB;
 3513   			}
 3514   		} catch (java.io.IOException ioEx) {
 3515   			throw SQLError.createSQLException(ioEx.toString(),
 3516   					SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 3517   		}
 3518   	}
 3519   
 3520   	/**
 3521   	 * JDBC 2.0 Set a CLOB parameter.
 3522   	 * 
 3523   	 * @param i
 3524   	 *            the first parameter is 1, the second is 2, ...
 3525   	 * @param x
 3526   	 *            an object representing a CLOB
 3527   	 * 
 3528   	 * @throws SQLException
 3529   	 *             if a database error occurs
 3530   	 */
 3531   	public void setClob(int i, Clob x) throws SQLException {
 3532   		if (x == null) {
 3533   			setNull(i, Types.CLOB);
 3534   		} else {
 3535   
 3536   			String forcedEncoding = this.connection.getClobCharacterEncoding();
 3537   			
 3538   			if (forcedEncoding == null) {
 3539   				setString(i, x.getSubString(1L, (int) x.length()));
 3540   			} else {
 3541   				try {
 3542   					setBytes(i, x.getSubString(1L, 
 3543   							(int)x.length()).getBytes(forcedEncoding));
 3544   				} catch (UnsupportedEncodingException uee) {
 3545   					throw SQLError.createSQLException("Unsupported character encoding " + 
 3546   							forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3547   				}
 3548   			}
 3549   		
 3550   			this.parameterTypes[i - 1 + getParameterIndexOffset()] = Types.CLOB;
 3551   		}
 3552   	}
 3553   
 3554   	/**
 3555   	 * Set a parameter to a java.sql.Date value. The driver converts this to a
 3556   	 * SQL DATE value when it sends it to the database.
 3557   	 * 
 3558   	 * @param parameterIndex
 3559   	 *            the first parameter is 1...
 3560   	 * @param x
 3561   	 *            the parameter value
 3562   	 * 
 3563   	 * @exception java.sql.SQLException
 3564   	 *                if a database access error occurs
 3565   	 */
 3566   	public void setDate(int parameterIndex, java.sql.Date x)
 3567   			throws java.sql.SQLException {
 3568   		setDate(parameterIndex, x, null);
 3569   	}
 3570   
 3571   	/**
 3572   	 * Set a parameter to a java.sql.Date value. The driver converts this to a
 3573   	 * SQL DATE value when it sends it to the database.
 3574   	 * 
 3575   	 * @param parameterIndex
 3576   	 *            the first parameter is 1, the second is 2, ...
 3577   	 * @param x
 3578   	 *            the parameter value
 3579   	 * @param cal
 3580   	 *            the calendar to interpret the date with
 3581   	 * 
 3582   	 * @exception SQLException
 3583   	 *                if a database-access error occurs.
 3584   	 */
 3585   	public void setDate(int parameterIndex, java.sql.Date x, Calendar cal)
 3586   			throws SQLException {
 3587   		if (x == null) {
 3588   			setNull(parameterIndex, java.sql.Types.DATE);
 3589   		} else {
 3590   			checkClosed();
 3591   			
 3592   			if (!this.useLegacyDatetimeCode) {
 3593   				newSetDateInternal(parameterIndex, x, cal);
 3594   			} else {
 3595   				// FIXME: Have instance version of this, problem as it's
 3596   				// not thread-safe :(
 3597   				SimpleDateFormat dateFormatter = new SimpleDateFormat(
 3598   						"''yyyy-MM-dd''", Locale.US); //$NON-NLS-1$
 3599   				setInternal(parameterIndex, dateFormatter.format(x));
 3600   				
 3601   				this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DATE;
 3602   			}
 3603   		}
 3604   	}
 3605   
 3606   	/**
 3607   	 * Set a parameter to a Java double value. The driver converts this to a SQL
 3608   	 * DOUBLE value when it sends it to the database
 3609   	 * 
 3610   	 * @param parameterIndex
 3611   	 *            the first parameter is 1...
 3612   	 * @param x
 3613   	 *            the parameter value
 3614   	 * 
 3615   	 * @exception SQLException
 3616   	 *                if a database access error occurs
 3617   	 */
 3618   	public void setDouble(int parameterIndex, double x) throws SQLException {
 3619   
 3620   		if (!this.connection.getAllowNanAndInf()
 3621   				&& (x == Double.POSITIVE_INFINITY
 3622   						|| x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) {
 3623   			throw SQLError.createSQLException("'" + x
 3624   					+ "' is not a valid numeric or approximate numeric value",
 3625   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3626   
 3627   		}
 3628   
 3629   		setInternal(parameterIndex, StringUtils.fixDecimalExponent(String
 3630   				.valueOf(x)));
 3631   		
 3632   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DOUBLE;
 3633   	}
 3634   
 3635   	/**
 3636   	 * Set a parameter to a Java float value. The driver converts this to a SQL
 3637   	 * FLOAT value when it sends it to the database.
 3638   	 * 
 3639   	 * @param parameterIndex
 3640   	 *            the first parameter is 1...
 3641   	 * @param x
 3642   	 *            the parameter value
 3643   	 * 
 3644   	 * @exception SQLException
 3645   	 *                if a database access error occurs
 3646   	 */
 3647   	public void setFloat(int parameterIndex, float x) throws SQLException {
 3648   		setInternal(parameterIndex, StringUtils.fixDecimalExponent(String
 3649   				.valueOf(x)));
 3650   		
 3651   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.FLOAT;
 3652   	}
 3653   
 3654   	/**
 3655   	 * Set a parameter to a Java int value. The driver converts this to a SQL
 3656   	 * INTEGER value when it sends it to the database.
 3657   	 * 
 3658   	 * @param parameterIndex
 3659   	 *            the first parameter is 1...
 3660   	 * @param x
 3661   	 *            the parameter value
 3662   	 * 
 3663   	 * @exception SQLException
 3664   	 *                if a database access error occurs
 3665   	 */
 3666   	public void setInt(int parameterIndex, int x) throws SQLException {
 3667   		setInternal(parameterIndex, String.valueOf(x));
 3668   		
 3669   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.INTEGER;
 3670   	}
 3671   
 3672   	protected final void setInternal(int paramIndex, byte[] val)
 3673   			throws SQLException {
 3674   		if (this.isClosed) {
 3675   			throw SQLError.createSQLException(Messages.getString("PreparedStatement.48"), //$NON-NLS-1$
 3676   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3677   		}
 3678   
 3679   		int parameterIndexOffset = getParameterIndexOffset();
 3680   		
 3681   		checkBounds(paramIndex, parameterIndexOffset);
 3682   
 3683   		this.isStream[paramIndex - 1 + parameterIndexOffset] = false;
 3684   		this.isNull[paramIndex - 1 + parameterIndexOffset] = false;
 3685   		this.parameterStreams[paramIndex - 1 + parameterIndexOffset] = null;
 3686   		this.parameterValues[paramIndex - 1 + parameterIndexOffset] = val;
 3687   	}
 3688   
 3689   	private void checkBounds(int paramIndex, int parameterIndexOffset)
 3690   			throws SQLException {
 3691   		if ((paramIndex < 1)) {
 3692   			throw SQLError.createSQLException(
 3693   					Messages.getString("PreparedStatement.49") //$NON-NLS-1$
 3694   							+ paramIndex
 3695   							+ Messages.getString("PreparedStatement.50"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor()); //$NON-NLS-1$
 3696   		} else if (paramIndex > this.parameterCount) {
 3697   			throw SQLError.createSQLException(
 3698   					Messages.getString("PreparedStatement.51") //$NON-NLS-1$
 3699   							+ paramIndex
 3700   							+ Messages.getString("PreparedStatement.52") + (this.parameterValues.length) + Messages.getString("PreparedStatement.53"), //$NON-NLS-1$ //$NON-NLS-2$
 3701   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3702   		} else if (parameterIndexOffset == -1 && paramIndex == 1) {
 3703   			throw SQLError.createSQLException("Can't set IN parameter for return value of stored function call.", 
 3704   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3705   		}
 3706   	}
 3707   
 3708   	protected final void setInternal(int paramIndex, String val)
 3709   			throws SQLException {
 3710   		checkClosed();
 3711   		
 3712   		byte[] parameterAsBytes = null;
 3713   
 3714   		if (this.charConverter != null) {
 3715   			parameterAsBytes = this.charConverter.toBytes(val);
 3716   		} else {
 3717   			parameterAsBytes = StringUtils.getBytes(val, this.charConverter,
 3718   					this.charEncoding, this.connection
 3719   							.getServerCharacterEncoding(), this.connection
 3720   							.parserKnowsUnicode(), getExceptionInterceptor());
 3721   		}
 3722   
 3723   		setInternal(paramIndex, parameterAsBytes);
 3724   	}
 3725   
 3726   	/**
 3727   	 * Set a parameter to a Java long value. The driver converts this to a SQL
 3728   	 * BIGINT value when it sends it to the database.
 3729   	 * 
 3730   	 * @param parameterIndex
 3731   	 *            the first parameter is 1...
 3732   	 * @param x
 3733   	 *            the parameter value
 3734   	 * 
 3735   	 * @exception SQLException
 3736   	 *                if a database access error occurs
 3737   	 */
 3738   	public void setLong(int parameterIndex, long x) throws SQLException {
 3739   		setInternal(parameterIndex, String.valueOf(x));
 3740   		
 3741   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BIGINT;
 3742   	}
 3743   
 3744   	/**
 3745   	 * Set a parameter to SQL NULL
 3746   	 * 
 3747   	 * <p>
 3748   	 * <B>Note:</B> You must specify the parameters SQL type (although MySQL
 3749   	 * ignores it)
 3750   	 * </p>
 3751   	 * 
 3752   	 * @param parameterIndex
 3753   	 *            the first parameter is 1, etc...
 3754   	 * @param sqlType
 3755   	 *            the SQL type code defined in java.sql.Types
 3756   	 * 
 3757   	 * @exception SQLException
 3758   	 *                if a database access error occurs
 3759   	 */
 3760   	public void setNull(int parameterIndex, int sqlType) throws SQLException {
 3761   		setInternal(parameterIndex, "null"); //$NON-NLS-1$
 3762   		this.isNull[parameterIndex - 1 + getParameterIndexOffset()] = true;
 3763   		
 3764   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.NULL;
 3765   	}
 3766   
 3767   	/**
 3768   	 * Set a parameter to SQL NULL.
 3769   	 * 
 3770   	 * <P>
 3771   	 * <B>Note:</B> You must specify the parameter's SQL type.
 3772   	 * </p>
 3773   	 * 
 3774   	 * @param parameterIndex
 3775   	 *            the first parameter is 1, the second is 2, ...
 3776   	 * @param sqlType
 3777   	 *            SQL type code defined by java.sql.Types
 3778   	 * @param arg
 3779   	 *            argument parameters for null
 3780   	 * 
 3781   	 * @exception SQLException
 3782   	 *                if a database-access error occurs.
 3783   	 */
 3784   	public void setNull(int parameterIndex, int sqlType, String arg)
 3785   			throws SQLException {
 3786   		setNull(parameterIndex, sqlType);
 3787   		
 3788   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.NULL;
 3789   	}
 3790   
 3791   	private void setNumericObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException {
 3792   		Number parameterAsNum;
 3793   
 3794   		if (parameterObj instanceof Boolean) {
 3795   			parameterAsNum = ((Boolean) parameterObj)
 3796   					.booleanValue() ? Constants.integerValueOf(1) : Constants.integerValueOf(
 3797   					0);
 3798   		} else if (parameterObj instanceof String) {
 3799   			switch (targetSqlType) {
 3800   			case Types.BIT:
 3801   				if ("1".equals((String) parameterObj)
 3802   						|| "0".equals((String) parameterObj)) {
 3803   					parameterAsNum = Integer.valueOf((String) parameterObj);
 3804   				} else {
 3805   					boolean parameterAsBoolean = "true"
 3806   							.equalsIgnoreCase((String) parameterObj);
 3807   
 3808   				parameterAsNum = parameterAsBoolean ? Constants.integerValueOf(1)
 3809   						: Constants.integerValueOf(0);
 3810   				}
 3811   				
 3812   				break;
 3813   
 3814   			case Types.TINYINT:
 3815   			case Types.SMALLINT:
 3816   			case Types.INTEGER:
 3817   				parameterAsNum = Integer
 3818   						.valueOf((String) parameterObj);
 3819   
 3820   				break;
 3821   
 3822   			case Types.BIGINT:
 3823   				parameterAsNum = Long
 3824   						.valueOf((String) parameterObj);
 3825   
 3826   				break;
 3827   
 3828   			case Types.REAL:
 3829   				parameterAsNum = Float
 3830   						.valueOf((String) parameterObj);
 3831   
 3832   				break;
 3833   
 3834   			case Types.FLOAT:
 3835   			case Types.DOUBLE:
 3836   				parameterAsNum = Double
 3837   						.valueOf((String) parameterObj);
 3838   
 3839   				break;
 3840   
 3841   			case Types.DECIMAL:
 3842   			case Types.NUMERIC:
 3843   			default:
 3844   				parameterAsNum = new java.math.BigDecimal(
 3845   						(String) parameterObj);
 3846   			}
 3847   		} else {
 3848   			parameterAsNum = (Number) parameterObj;
 3849   		}
 3850   
 3851   		switch (targetSqlType) {
 3852   		case Types.BIT:
 3853   		case Types.TINYINT:
 3854   		case Types.SMALLINT:
 3855   		case Types.INTEGER:
 3856   			setInt(parameterIndex, parameterAsNum.intValue());
 3857   
 3858   			break;
 3859   
 3860   		case Types.BIGINT:
 3861   			setLong(parameterIndex, parameterAsNum.longValue());
 3862   
 3863   			break;
 3864   
 3865   		case Types.REAL:
 3866   			setFloat(parameterIndex, parameterAsNum.floatValue());
 3867   
 3868   			break;
 3869   
 3870   		case Types.FLOAT:
 3871   		case Types.DOUBLE:
 3872   			setDouble(parameterIndex, parameterAsNum.doubleValue());
 3873   
 3874   			break;
 3875   
 3876   		case Types.DECIMAL:
 3877   		case Types.NUMERIC:
 3878   
 3879   			if (parameterAsNum instanceof java.math.BigDecimal) {
 3880   				BigDecimal scaledBigDecimal = null;
 3881   
 3882   				try {
 3883   					scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum)
 3884   							.setScale(scale);
 3885   				} catch (ArithmeticException ex) {
 3886   					try {
 3887   						scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum)
 3888   								.setScale(scale,
 3889   										BigDecimal.ROUND_HALF_UP);
 3890   					} catch (ArithmeticException arEx) {
 3891   						throw SQLError.createSQLException(
 3892   								"Can't set scale of '"
 3893   										+ scale
 3894   										+ "' for DECIMAL argument '"
 3895   										+ parameterAsNum + "'",
 3896   								SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 3897   					}
 3898   				}
 3899   
 3900   				setBigDecimal(parameterIndex, scaledBigDecimal);
 3901   			} else if (parameterAsNum instanceof java.math.BigInteger) {
 3902   				setBigDecimal(
 3903   						parameterIndex,
 3904   						new java.math.BigDecimal(
 3905   								(java.math.BigInteger) parameterAsNum,
 3906   								scale));
 3907   			} else {
 3908   				setBigDecimal(parameterIndex,
 3909   						new java.math.BigDecimal(parameterAsNum
 3910   								.doubleValue()));
 3911   			}
 3912   
 3913   			break;
 3914   		}
 3915   	}
 3916   
 3917   	/**
 3918   	 * DOCUMENT ME!
 3919   	 * 
 3920   	 * @param parameterIndex
 3921   	 *            DOCUMENT ME!
 3922   	 * @param parameterObj
 3923   	 *            DOCUMENT ME!
 3924   	 * 
 3925   	 * @throws SQLException
 3926   	 *             DOCUMENT ME!
 3927   	 */
 3928   	public void setObject(int parameterIndex, Object parameterObj)
 3929   			throws SQLException {
 3930   		if (parameterObj == null) {
 3931   			setNull(parameterIndex, java.sql.Types.OTHER);
 3932   		} else {
 3933   			if (parameterObj instanceof Byte) {
 3934   				setInt(parameterIndex, ((Byte) parameterObj).intValue());
 3935   			} else if (parameterObj instanceof String) {
 3936   				setString(parameterIndex, (String) parameterObj);
 3937   			} else if (parameterObj instanceof BigDecimal) {
 3938   				setBigDecimal(parameterIndex, (BigDecimal) parameterObj);
 3939   			} else if (parameterObj instanceof Short) {
 3940   				setShort(parameterIndex, ((Short) parameterObj).shortValue());
 3941   			} else if (parameterObj instanceof Integer) {
 3942   				setInt(parameterIndex, ((Integer) parameterObj).intValue());
 3943   			} else if (parameterObj instanceof Long) {
 3944   				setLong(parameterIndex, ((Long) parameterObj).longValue());
 3945   			} else if (parameterObj instanceof Float) {
 3946   				setFloat(parameterIndex, ((Float) parameterObj).floatValue());
 3947   			} else if (parameterObj instanceof Double) {
 3948   				setDouble(parameterIndex, ((Double) parameterObj).doubleValue());
 3949   			} else if (parameterObj instanceof byte[]) {
 3950   				setBytes(parameterIndex, (byte[]) parameterObj);
 3951   			} else if (parameterObj instanceof java.sql.Date) {
 3952   				setDate(parameterIndex, (java.sql.Date) parameterObj);
 3953   			} else if (parameterObj instanceof Time) {
 3954   				setTime(parameterIndex, (Time) parameterObj);
 3955   			} else if (parameterObj instanceof Timestamp) {
 3956   				setTimestamp(parameterIndex, (Timestamp) parameterObj);
 3957   			} else if (parameterObj instanceof Boolean) {
 3958   				setBoolean(parameterIndex, ((Boolean) parameterObj)
 3959   						.booleanValue());
 3960   			} else if (parameterObj instanceof InputStream) {
 3961   				setBinaryStream(parameterIndex, (InputStream) parameterObj, -1);
 3962   			} else if (parameterObj instanceof java.sql.Blob) {
 3963   				setBlob(parameterIndex, (java.sql.Blob) parameterObj);
 3964   			} else if (parameterObj instanceof java.sql.Clob) {
 3965   				setClob(parameterIndex, (java.sql.Clob) parameterObj);
 3966   			} else if (this.connection.getTreatUtilDateAsTimestamp() && 
 3967   				parameterObj instanceof java.util.Date) {
 3968   				setTimestamp(parameterIndex, new Timestamp(
 3969   				((java.util.Date) parameterObj).getTime()));
 3970   			} else if (parameterObj instanceof BigInteger) {
 3971   				setString(parameterIndex, parameterObj.toString());
 3972   			} else {
 3973   				setSerializableObject(parameterIndex, parameterObj);
 3974   			}
 3975   		}
 3976   	}
 3977   	
 3978   
 3979   	/**
 3980   	 * DOCUMENT ME!
 3981   	 * 
 3982   	 * @param parameterIndex
 3983   	 *            DOCUMENT ME!
 3984   	 * @param parameterObj
 3985   	 *            DOCUMENT ME!
 3986   	 * @param targetSqlType
 3987   	 *            DOCUMENT ME!
 3988   	 * 
 3989   	 * @throws SQLException
 3990   	 *             DOCUMENT ME!
 3991   	 */
 3992   	public void setObject(int parameterIndex, Object parameterObj,
 3993   			int targetSqlType) throws SQLException {
 3994   		if (!(parameterObj instanceof BigDecimal)) {
 3995   			setObject(parameterIndex, parameterObj, targetSqlType, 0);
 3996   		} else {
 3997   			setObject(parameterIndex, parameterObj, targetSqlType,
 3998   					((BigDecimal)parameterObj).scale());
 3999   		}
 4000   	}
 4001   
 4002   	/**
 4003   	 * Set the value of a parameter using an object; use the java.lang
 4004   	 * equivalent objects for integral values.
 4005   	 * 
 4006   	 * <P>
 4007   	 * The given Java object will be converted to the targetSqlType before being
 4008   	 * sent to the database.
 4009   	 * </p>
 4010   	 * 
 4011   	 * <P>
 4012   	 * note that this method may be used to pass database-specific abstract data
 4013   	 * types. This is done by using a Driver-specific Java type and using a
 4014   	 * targetSqlType of java.sql.Types.OTHER
 4015   	 * </p>
 4016   	 * 
 4017   	 * @param parameterIndex
 4018   	 *            the first parameter is 1...
 4019   	 * @param parameterObj
 4020   	 *            the object containing the input parameter value
 4021   	 * @param targetSqlType
 4022   	 *            The SQL type to be send to the database
 4023   	 * @param scale
 4024   	 *            For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
 4025   	 *            this is the number of digits after the decimal. For all other
 4026   	 *            types this value will be ignored.
 4027   	 * 
 4028   	 * @throws SQLException
 4029   	 *             if a database access error occurs
 4030   	 */
 4031   	public void setObject(int parameterIndex, Object parameterObj,
 4032   			int targetSqlType, int scale) throws SQLException {
 4033   		if (parameterObj == null) {
 4034   			setNull(parameterIndex, java.sql.Types.OTHER);
 4035   		} else {
 4036   			try {
 4037   				switch (targetSqlType) {
 4038   				case Types.BOOLEAN:
 4039   					/*
 4040   					 From Table-B5 in the JDBC-3.0 Spec
 4041   				
 4042   					        T S I B R F D D N B B C V L
 4043   					        I M N I E L O E U I O H A O
 4044   					        N A T G A O U C M T O A R N
 4045   					        Y L E I L A B I E   L R C G
 4046   					        I L G N   T L M R   E   H V
 4047   					        N I E T     E A I   A   A A
 4048   					        T N R         L C   N   R R
 4049   					          T                       C
 4050                                                         H
 4051   				                                      A
 4052   					                                  R
 4053   					-----------------------------------
 4054   					Boolean x x x x x x x x x x x x x x
 4055   					*/
 4056   					
 4057   					if (parameterObj instanceof Boolean) {
 4058   						setBoolean(parameterIndex, ((Boolean)parameterObj).booleanValue());
 4059   						
 4060   						break;
 4061   					} else if (parameterObj instanceof String) {
 4062   						setBoolean(parameterIndex, "true".equalsIgnoreCase((String)parameterObj) ||
 4063   								!"0".equalsIgnoreCase((String)parameterObj));
 4064   						
 4065   						break;
 4066   					} else if (parameterObj instanceof Number) {
 4067   						int intValue = ((Number)parameterObj).intValue();
 4068   						
 4069   						setBoolean(parameterIndex, intValue != 0);
 4070   						
 4071   						break;
 4072   					} else {
 4073   						throw SQLError.createSQLException("No conversion from " + parameterObj.getClass().getName() + 
 4074   								" to Types.BOOLEAN possible.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 4075   					}
 4076   					
 4077   					
 4078   				case Types.BIT:
 4079   				case Types.TINYINT:
 4080   				case Types.SMALLINT:
 4081   				case Types.INTEGER:
 4082   				case Types.BIGINT:
 4083   				case Types.REAL:
 4084   				case Types.FLOAT:
 4085   				case Types.DOUBLE:
 4086   				case Types.DECIMAL:
 4087   				case Types.NUMERIC:
 4088   
 4089   					setNumericObject(parameterIndex, parameterObj, targetSqlType, scale);
 4090   
 4091   					break;
 4092   
 4093   				case Types.CHAR:
 4094   				case Types.VARCHAR:
 4095   				case Types.LONGVARCHAR:
 4096   					if (parameterObj instanceof BigDecimal) {
 4097   						setString(
 4098   								parameterIndex,
 4099   								(StringUtils
 4100   										.fixDecimalExponent(StringUtils
 4101   												.consistentToString((BigDecimal) parameterObj))));
 4102   					} else {
 4103   						setString(parameterIndex, parameterObj.toString());
 4104   					}
 4105   
 4106   					break;
 4107   
 4108   				case Types.CLOB:
 4109   
 4110   					if (parameterObj instanceof java.sql.Clob) {
 4111   						setClob(parameterIndex, (java.sql.Clob) parameterObj);
 4112   					} else {
 4113   						setString(parameterIndex, parameterObj.toString());
 4114   					}
 4115   
 4116   					break;
 4117   
 4118   				case Types.BINARY:
 4119   				case Types.VARBINARY:
 4120   				case Types.LONGVARBINARY:
 4121   				case Types.BLOB:
 4122   
 4123   					if (parameterObj instanceof byte[]) {
 4124   						setBytes(parameterIndex, (byte[]) parameterObj);
 4125   					} else if (parameterObj instanceof java.sql.Blob) {
 4126   						setBlob(parameterIndex, (java.sql.Blob) parameterObj);
 4127   					} else {
 4128   						setBytes(parameterIndex, StringUtils.getBytes(
 4129   								parameterObj.toString(), this.charConverter,
 4130   								this.charEncoding, this.connection
 4131   										.getServerCharacterEncoding(),
 4132   								this.connection.parserKnowsUnicode(), getExceptionInterceptor()));
 4133   					}
 4134   
 4135   					break;
 4136   
 4137   				case Types.DATE:
 4138   				case Types.TIMESTAMP:
 4139   
 4140   					java.util.Date parameterAsDate;
 4141   
 4142   					if (parameterObj instanceof String) {
 4143   						ParsePosition pp = new ParsePosition(0);
 4144   						java.text.DateFormat sdf = new java.text.SimpleDateFormat(
 4145   								getDateTimePattern((String) parameterObj, false), Locale.US);
 4146   						parameterAsDate = sdf.parse((String) parameterObj, pp);
 4147   					} else {
 4148   						parameterAsDate = (java.util.Date) parameterObj;
 4149   					}
 4150   
 4151   					switch (targetSqlType) {
 4152   					case Types.DATE:
 4153   
 4154   						if (parameterAsDate instanceof java.sql.Date) {
 4155   							setDate(parameterIndex,
 4156   									(java.sql.Date) parameterAsDate);
 4157   						} else {
 4158   							setDate(parameterIndex, new java.sql.Date(
 4159   									parameterAsDate.getTime()));
 4160   						}
 4161   
 4162   						break;
 4163   
 4164   					case Types.TIMESTAMP:
 4165   
 4166   						if (parameterAsDate instanceof java.sql.Timestamp) {
 4167   							setTimestamp(parameterIndex,
 4168   									(java.sql.Timestamp) parameterAsDate);
 4169   						} else {
 4170   							setTimestamp(parameterIndex,
 4171   									new java.sql.Timestamp(parameterAsDate
 4172   											.getTime()));
 4173   						}
 4174   
 4175   						break;
 4176   					}
 4177   
 4178   					break;
 4179   
 4180   				case Types.TIME:
 4181   
 4182   					if (parameterObj instanceof String) {
 4183   						java.text.DateFormat sdf = new java.text.SimpleDateFormat(
 4184   								getDateTimePattern((String) parameterObj, true), Locale.US);
 4185   						setTime(parameterIndex, new java.sql.Time(sdf.parse(
 4186   								(String) parameterObj).getTime()));
 4187   					} else if (parameterObj instanceof Timestamp) {
 4188   						Timestamp xT = (Timestamp) parameterObj;
 4189   						setTime(parameterIndex, new java.sql.Time(xT.getTime()));
 4190   					} else {
 4191   						setTime(parameterIndex, (java.sql.Time) parameterObj);
 4192   					}
 4193   
 4194   					break;
 4195   
 4196   				case Types.OTHER:
 4197   					setSerializableObject(parameterIndex, parameterObj);
 4198   
 4199   					break;
 4200   
 4201   				default:
 4202   					throw SQLError.createSQLException(Messages
 4203   							.getString("PreparedStatement.16"), //$NON-NLS-1$
 4204   							SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 4205   				}
 4206   			} catch (Exception ex) {
 4207   				if (ex instanceof SQLException) {
 4208   					throw (SQLException) ex;
 4209   				}
 4210   
 4211   				SQLException sqlEx = SQLError.createSQLException(
 4212   						Messages.getString("PreparedStatement.17") //$NON-NLS-1$
 4213   								+ parameterObj.getClass().toString()
 4214   								+ Messages.getString("PreparedStatement.18") //$NON-NLS-1$
 4215   								+ ex.getClass().getName()
 4216   								+ Messages.getString("PreparedStatement.19") + ex.getMessage(), //$NON-NLS-1$
 4217   						SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 4218   				
 4219   				sqlEx.initCause(ex);
 4220   				
 4221   				throw sqlEx;
 4222   			}
 4223   		}
 4224   	}
 4225   
 4226   	protected int setOneBatchedParameterSet(
 4227   			java.sql.PreparedStatement batchedStatement, int batchedParamIndex,
 4228   			Object paramSet) throws SQLException {
 4229   		BatchParams paramArg = (BatchParams)paramSet;
 4230   		
 4231   		boolean[] isNullBatch = paramArg.isNull;
 4232   		boolean[] isStreamBatch = paramArg.isStream;
 4233   
 4234   		for (int j = 0; j < isNullBatch.length; j++) {
 4235   			if (isNullBatch[j]) {
 4236   				batchedStatement.setNull(batchedParamIndex++, Types.NULL);
 4237   			} else {
 4238   				if (isStreamBatch[j]) {
 4239   					batchedStatement.setBinaryStream(batchedParamIndex++,
 4240   							paramArg.parameterStreams[j],
 4241   							paramArg.streamLengths[j]);
 4242   				} else {
 4243   					((com.mysql.jdbc.PreparedStatement) batchedStatement)
 4244   							.setBytesNoEscapeNoQuotes(batchedParamIndex++,
 4245   									paramArg.parameterStrings[j]);
 4246   				}
 4247   			}
 4248   		}
 4249   
 4250   		return batchedParamIndex;
 4251   	}
 4252   	
 4253   	/**
 4254   	 * JDBC 2.0 Set a REF(&lt;structured-type&gt;) parameter.
 4255   	 * 
 4256   	 * @param i
 4257   	 *            the first parameter is 1, the second is 2, ...
 4258   	 * @param x
 4259   	 *            an object representing data of an SQL REF Type
 4260   	 * 
 4261   	 * @throws SQLException
 4262   	 *             if a database error occurs
 4263   	 * @throws NotImplemented
 4264   	 *             DOCUMENT ME!
 4265   	 */
 4266   	public void setRef(int i, Ref x) throws SQLException {
 4267   		throw SQLError.notImplemented();
 4268   	}
 4269   
 4270   	/**
 4271   	 * Sets the concurrency for result sets generated by this statement
 4272   	 * 
 4273   	 * @param concurrencyFlag
 4274   	 *            DOCUMENT ME!
 4275   	 */
 4276   	void setResultSetConcurrency(int concurrencyFlag) {
 4277   		this.resultSetConcurrency = concurrencyFlag;
 4278   	}
 4279   
 4280   	/**
 4281   	 * Sets the result set type for result sets generated by this statement
 4282   	 * 
 4283   	 * @param typeFlag
 4284   	 *            DOCUMENT ME!
 4285   	 */
 4286   	void setResultSetType(int typeFlag) {
 4287   		this.resultSetType = typeFlag;
 4288   	}
 4289   
 4290   	/**
 4291   	 * DOCUMENT ME!
 4292   	 * 
 4293   	 * @param retrieveGeneratedKeys
 4294   	 */
 4295   	protected void setRetrieveGeneratedKeys(boolean retrieveGeneratedKeys) {
 4296   		this.retrieveGeneratedKeys = retrieveGeneratedKeys;
 4297   	}
 4298   
 4299   
 4300   
 4301   	/**
 4302   	 * Sets the value for the placeholder as a serialized Java object (used by
 4303   	 * various forms of setObject()
 4304   	 * 
 4305   	 * @param parameterIndex
 4306   	 *            DOCUMENT ME!
 4307   	 * @param parameterObj
 4308   	 *            DOCUMENT ME!
 4309   	 * 
 4310   	 * @throws SQLException
 4311   	 *             DOCUMENT ME!
 4312   	 */
 4313   	private final void setSerializableObject(int parameterIndex,
 4314   			Object parameterObj) throws SQLException {
 4315   		try {
 4316   			ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
 4317   			ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut);
 4318   			objectOut.writeObject(parameterObj);
 4319   			objectOut.flush();
 4320   			objectOut.close();
 4321   			bytesOut.flush();
 4322   			bytesOut.close();
 4323   
 4324   			byte[] buf = bytesOut.toByteArray();
 4325   			ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf);
 4326   			setBinaryStream(parameterIndex, bytesIn, buf.length);
 4327   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BINARY;
 4328   		} catch (Exception ex) {
 4329   			SQLException sqlEx = SQLError.createSQLException(Messages.getString("PreparedStatement.54") //$NON-NLS-1$
 4330   					+ ex.getClass().getName(),
 4331   					SQLError.SQL_STATE_ILLEGAL_ARGUMENT, getExceptionInterceptor());
 4332   			sqlEx.initCause(ex);
 4333   			
 4334   			throw sqlEx;
 4335   		}
 4336   	}
 4337   
 4338   	/**
 4339   	 * Set a parameter to a Java short value. The driver converts this to a SQL
 4340   	 * SMALLINT value when it sends it to the database.
 4341   	 * 
 4342   	 * @param parameterIndex
 4343   	 *            the first parameter is 1...
 4344   	 * @param x
 4345   	 *            the parameter value
 4346   	 * 
 4347   	 * @exception SQLException
 4348   	 *                if a database access error occurs
 4349   	 */
 4350   	public void setShort(int parameterIndex, short x) throws SQLException {
 4351   		setInternal(parameterIndex, String.valueOf(x));
 4352   		
 4353   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.SMALLINT;
 4354   	}
 4355   
 4356   	/**
 4357   	 * Set a parameter to a Java String value. The driver converts this to a SQL
 4358   	 * VARCHAR or LONGVARCHAR value (depending on the arguments size relative to
 4359   	 * the driver's limits on VARCHARs) when it sends it to the database.
 4360   	 * 
 4361   	 * @param parameterIndex
 4362   	 *            the first parameter is 1...
 4363   	 * @param x
 4364   	 *            the parameter value
 4365   	 * 
 4366   	 * @exception SQLException
 4367   	 *                if a database access error occurs
 4368   	 */
 4369   	public void setString(int parameterIndex, String x) throws SQLException {
 4370   		// if the passed string is null, then set this column to null
 4371   		if (x == null) {
 4372   			setNull(parameterIndex, Types.CHAR);
 4373   		} else {
 4374   			checkClosed();
 4375   			
 4376   			int stringLength = x.length();
 4377   
 4378   			if (this.connection.isNoBackslashEscapesSet()) {
 4379   				// Scan for any nasty chars
 4380   
 4381   				boolean needsHexEscape = isEscapeNeededForString(x,
 4382   						stringLength);
 4383   
 4384   				if (!needsHexEscape) {
 4385   					byte[] parameterAsBytes = null;
 4386   
 4387   					StringBuffer quotedString = new StringBuffer(x.length() + 2);
 4388   					quotedString.append('\'');
 4389   					quotedString.append(x);
 4390   					quotedString.append('\'');
 4391   					
 4392   					if (!this.isLoadDataQuery) {
 4393   						parameterAsBytes = StringUtils.getBytes(quotedString.toString(),
 4394   								this.charConverter, this.charEncoding,
 4395   								this.connection.getServerCharacterEncoding(),
 4396   								this.connection.parserKnowsUnicode(), getExceptionInterceptor());
 4397   					} else {
 4398   						// Send with platform character encoding
 4399   						parameterAsBytes = quotedString.toString().getBytes();
 4400   					}
 4401   					
 4402   					setInternal(parameterIndex, parameterAsBytes);
 4403   				} else {
 4404   					byte[] parameterAsBytes = null;
 4405   
 4406   					if (!this.isLoadDataQuery) {
 4407   						parameterAsBytes = StringUtils.getBytes(x,
 4408   								this.charConverter, this.charEncoding,
 4409   								this.connection.getServerCharacterEncoding(),
 4410   								this.connection.parserKnowsUnicode(), getExceptionInterceptor());
 4411   					} else {
 4412   						// Send with platform character encoding
 4413   						parameterAsBytes = x.getBytes();
 4414   					}
 4415   					
 4416   					setBytes(parameterIndex, parameterAsBytes);
 4417   				}
 4418   
 4419   				return;
 4420   			}
 4421   
 4422   			String parameterAsString = x;
 4423   			boolean needsQuoted = true;
 4424   			
 4425   			if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
 4426   				needsQuoted = false; // saves an allocation later
 4427   				
 4428   				StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
 4429   				
 4430   				buf.append('\'');
 4431   	
 4432   				//
 4433   				// Note: buf.append(char) is _faster_ than
 4434   				// appending in blocks, because the block
 4435   				// append requires a System.arraycopy()....
 4436   				// go figure...
 4437   				//
 4438   	
 4439   				for (int i = 0; i < stringLength; ++i) {
 4440   					char c = x.charAt(i);
 4441   	
 4442   					switch (c) {
 4443   					case 0: /* Must be escaped for 'mysql' */
 4444   						buf.append('\\');
 4445   						buf.append('0');
 4446   	
 4447   						break;
 4448   	
 4449   					case '\n': /* Must be escaped for logs */
 4450   						buf.append('\\');
 4451   						buf.append('n');
 4452   	
 4453   						break;
 4454   	
 4455   					case '\r':
 4456   						buf.append('\\');
 4457   						buf.append('r');
 4458   	
 4459   						break;
 4460   	
 4461   					case '\\':
 4462   						buf.append('\\');
 4463   						buf.append('\\');
 4464   	
 4465   						break;
 4466   	
 4467   					case '\'':
 4468   						buf.append('\\');
 4469   						buf.append('\'');
 4470   	
 4471   						break;
 4472   	
 4473   					case '"': /* Better safe than sorry */
 4474   						if (this.usingAnsiMode) {
 4475   							buf.append('\\');
 4476   						}
 4477   	
 4478   						buf.append('"');
 4479   	
 4480   						break;
 4481   	
 4482   					case '\032': /* This gives problems on Win32 */
 4483   						buf.append('\\');
 4484   						buf.append('Z');
 4485   	
 4486   						break;
 4487   
 4488   					case '\u00a5':
 4489   					case '\u20a9':
 4490   						// escape characters interpreted as backslash by mysql
 4491   						if(charsetEncoder != null) {
 4492   							CharBuffer cbuf = CharBuffer.allocate(1);
 4493   							ByteBuffer bbuf = ByteBuffer.allocate(1); 
 4494   							cbuf.put(c);
 4495   							cbuf.position(0);
 4496   							charsetEncoder.encode(cbuf, bbuf, true);
 4497   							if(bbuf.get(0) == '\\') {
 4498   								buf.append('\\');
 4499   							}
 4500   						}
 4501   						// fall through
 4502   
 4503   					default:
 4504   						buf.append(c);
 4505   					}
 4506   				}
 4507   	
 4508   				buf.append('\'');
 4509   	
 4510   				parameterAsString = buf.toString();
 4511   			}
 4512   
 4513   			byte[] parameterAsBytes = null;
 4514   
 4515   			if (!this.isLoadDataQuery) {
 4516   				if (needsQuoted) {
 4517   					parameterAsBytes = StringUtils.getBytesWrapped(parameterAsString,
 4518   						'\'', '\'', this.charConverter, this.charEncoding, this.connection
 4519   								.getServerCharacterEncoding(), this.connection
 4520   								.parserKnowsUnicode(), getExceptionInterceptor());
 4521   				} else {
 4522   					parameterAsBytes = StringUtils.getBytes(parameterAsString,
 4523   							this.charConverter, this.charEncoding, this.connection
 4524   									.getServerCharacterEncoding(), this.connection
 4525   									.parserKnowsUnicode(), getExceptionInterceptor());
 4526   				}
 4527   			} else {
 4528   				// Send with platform character encoding
 4529   				parameterAsBytes = parameterAsString.getBytes();
 4530   			}
 4531   
 4532   			setInternal(parameterIndex, parameterAsBytes);
 4533   			
 4534   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.VARCHAR;
 4535   		}
 4536   	}
 4537   
 4538   	private boolean isEscapeNeededForString(String x, int stringLength) {
 4539   		boolean needsHexEscape = false;
 4540   
 4541   		for (int i = 0; i < stringLength; ++i) {
 4542   			char c = x.charAt(i);
 4543   
 4544   			switch (c) {
 4545   			case 0: /* Must be escaped for 'mysql' */
 4546   
 4547   				needsHexEscape = true;
 4548   				break;
 4549   
 4550   			case '\n': /* Must be escaped for logs */
 4551   				needsHexEscape = true;
 4552   
 4553   				break;
 4554   
 4555   			case '\r':
 4556   				needsHexEscape = true;
 4557   				break;
 4558   
 4559   			case '\\':
 4560   				needsHexEscape = true;
 4561   
 4562   				break;
 4563   
 4564   			case '\'':
 4565   				needsHexEscape = true;
 4566   
 4567   				break;
 4568   
 4569   			case '"': /* Better safe than sorry */
 4570   				needsHexEscape = true;
 4571   
 4572   				break;
 4573   
 4574   			case '\032': /* This gives problems on Win32 */
 4575   				needsHexEscape = true;
 4576   				break;
 4577   			}
 4578   
 4579   			if (needsHexEscape) {
 4580   				break; // no need to scan more
 4581   			}
 4582   		}
 4583   		return needsHexEscape;
 4584   	}
 4585   
 4586   	/**
 4587   	 * Set a parameter to a java.sql.Time value. The driver converts this to a
 4588   	 * SQL TIME value when it sends it to the database.
 4589   	 * 
 4590   	 * @param parameterIndex
 4591   	 *            the first parameter is 1, the second is 2, ...
 4592   	 * @param x
 4593   	 *            the parameter value
 4594   	 * @param cal
 4595   	 *            the cal specifying the timezone
 4596   	 * 
 4597   	 * @throws SQLException
 4598   	 *             if a database-access error occurs.
 4599   	 */
 4600   	public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
 4601   			throws SQLException {
 4602   		setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
 4603   	}
 4604   
 4605   	/**
 4606   	 * Set a parameter to a java.sql.Time value. The driver converts this to a
 4607   	 * SQL TIME value when it sends it to the database.
 4608   	 * 
 4609   	 * @param parameterIndex
 4610   	 *            the first parameter is 1...));
 4611   	 * @param x
 4612   	 *            the parameter value
 4613   	 * 
 4614   	 * @throws java.sql.SQLException
 4615   	 *             if a database access error occurs
 4616   	 */
 4617   	public void setTime(int parameterIndex, Time x)
 4618   			throws java.sql.SQLException {
 4619   		setTimeInternal(parameterIndex, x, null, Util.getDefaultTimeZone(), false);
 4620   	}
 4621   
 4622   	/**
 4623   	 * Set a parameter to a java.sql.Time value. The driver converts this to a
 4624   	 * SQL TIME value when it sends it to the database, using the given
 4625   	 * timezone.
 4626   	 * 
 4627   	 * @param parameterIndex
 4628   	 *            the first parameter is 1...));
 4629   	 * @param x
 4630   	 *            the parameter value
 4631   	 * @param tz
 4632   	 *            the timezone to use
 4633   	 * 
 4634   	 * @throws java.sql.SQLException
 4635   	 *             if a database access error occurs
 4636   	 */
 4637   	private void setTimeInternal(int parameterIndex, Time x, Calendar targetCalendar,
 4638   			TimeZone tz,
 4639   			boolean rollForward) throws java.sql.SQLException {
 4640   		if (x == null) {
 4641   			setNull(parameterIndex, java.sql.Types.TIME);
 4642   		} else {
 4643   			checkClosed();
 4644   			
 4645   			if (!this.useLegacyDatetimeCode) {
 4646   				newSetTimeInternal(parameterIndex, x, targetCalendar);
 4647   			} else {
 4648   				Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
 4649   				
 4650   				synchronized (sessionCalendar) {
 4651   					x = TimeUtil.changeTimezone(this.connection, 
 4652   							sessionCalendar,
 4653   							targetCalendar,
 4654   							 x, tz, this.connection
 4655   							.getServerTimezoneTZ(), rollForward);
 4656   				}
 4657   				
 4658   				setInternal(parameterIndex, "'" + x.toString() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
 4659   			}
 4660   			
 4661   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIME;
 4662   		}
 4663   	}
 4664   
 4665   	/**
 4666   	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 4667   	 * to a SQL TIMESTAMP value when it sends it to the database.
 4668   	 * 
 4669   	 * @param parameterIndex
 4670   	 *            the first parameter is 1, the second is 2, ...
 4671   	 * @param x
 4672   	 *            the parameter value
 4673   	 * @param cal
 4674   	 *            the calendar specifying the timezone to use
 4675   	 * 
 4676   	 * @throws SQLException
 4677   	 *             if a database-access error occurs.
 4678   	 */
 4679   	public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
 4680   			Calendar cal) throws SQLException {
 4681   		setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
 4682   	}
 4683   
 4684   	/**
 4685   	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 4686   	 * to a SQL TIMESTAMP value when it sends it to the database.
 4687   	 * 
 4688   	 * @param parameterIndex
 4689   	 *            the first parameter is 1...
 4690   	 * @param x
 4691   	 *            the parameter value
 4692   	 * 
 4693   	 * @throws java.sql.SQLException
 4694   	 *             if a database access error occurs
 4695   	 */
 4696   	public void setTimestamp(int parameterIndex, Timestamp x)
 4697   			throws java.sql.SQLException {
 4698   		setTimestampInternal(parameterIndex, x, null, Util.getDefaultTimeZone(), false);
 4699   	}
 4700   
 4701   	/**
 4702   	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
 4703   	 * to a SQL TIMESTAMP value when it sends it to the database.
 4704   	 * 
 4705   	 * @param parameterIndex
 4706   	 *            the first parameter is 1, the second is 2, ...
 4707   	 * @param x
 4708   	 *            the parameter value
 4709   	 * @param tz
 4710   	 *            the timezone to use
 4711   	 * 
 4712   	 * @throws SQLException
 4713   	 *             if a database-access error occurs.
 4714   	 */
 4715   	private void setTimestampInternal(int parameterIndex,
 4716   			Timestamp x, Calendar targetCalendar,
 4717   			TimeZone tz, boolean rollForward) throws SQLException {
 4718   		if (x == null) {
 4719   			setNull(parameterIndex, java.sql.Types.TIMESTAMP);
 4720   		} else {
 4721   			checkClosed();
 4722   			
 4723   			if (!this.useLegacyDatetimeCode) {
 4724   				newSetTimestampInternal(parameterIndex, x, targetCalendar);
 4725   			} else {
 4726   				String timestampString = null;
 4727   
 4728   				Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
 4729   						this.connection.getUtcCalendar() : 
 4730   							getCalendarInstanceForSessionOrNew();
 4731   					
 4732   				synchronized (sessionCalendar) {
 4733   					x = TimeUtil.changeTimezone(this.connection, 
 4734   							sessionCalendar,
 4735   							targetCalendar,
 4736   							x, tz, this.connection
 4737   						.getServerTimezoneTZ(), rollForward);
 4738   				}
 4739   	
 4740   				if (this.connection.getUseSSPSCompatibleTimezoneShift()) {
 4741   					doSSPSCompatibleTimezoneShift(parameterIndex, x, sessionCalendar);
 4742   				} else {
 4743   					synchronized (this) {
 4744   						if (this.tsdf == null) {
 4745   							this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); //$NON-NLS-1$
 4746   						}
 4747   						
 4748   						timestampString = this.tsdf.format(x);
 4749   						StringBuffer buf = new StringBuffer();
 4750   						buf.append(timestampString);
 4751   						buf.append('.');
 4752   						buf.append(formatNanos(x.getNanos()));
 4753   						buf.append('\'');
 4754   						
 4755   						setInternal(parameterIndex, buf.toString()); // SimpleDateFormat is not
 4756   																	  // thread-safe
 4757   					}
 4758   				}
 4759   			}
 4760   			
 4761   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIMESTAMP;
 4762   		}
 4763   	}
 4764   	
 4765   	private synchronized void newSetTimestampInternal(int parameterIndex,
 4766   			Timestamp x, Calendar targetCalendar) throws SQLException {
 4767   		if (this.tsdf == null) {
 4768   			this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); //$NON-NLS-1$
 4769   		}
 4770   		
 4771   		String timestampString = null;
 4772   		
 4773   		if (targetCalendar != null) {
 4774   			targetCalendar.setTime(x);
 4775   			this.tsdf.setTimeZone(targetCalendar.getTimeZone());
 4776   			
 4777   			timestampString = this.tsdf.format(x);
 4778   		} else {
 4779   			this.tsdf.setTimeZone(this.connection.getServerTimezoneTZ());
 4780   			timestampString = this.tsdf.format(x);
 4781   		}
 4782   
 4783   		StringBuffer buf = new StringBuffer();
 4784   		buf.append(timestampString);
 4785   		buf.append('.');
 4786   		buf.append(formatNanos(x.getNanos()));
 4787   		buf.append('\'');
 4788   		
 4789   		setInternal(parameterIndex, buf.toString()); 
 4790   	}
 4791   	
 4792   	private String formatNanos(int nanos) {
 4793   		if (true /* for now */ || nanos == 0) {
 4794   		    return "0";
 4795   		}
 4796   		
 4797   		boolean usingMicros = true;
 4798   		
 4799   		if (usingMicros) {
 4800   			nanos /= 1000;
 4801   		}
 4802   		
 4803   		final int digitCount = usingMicros ? 6 : 9;
 4804   		
 4805   	    String nanosString = Integer.toString(nanos);
 4806   	    final String zeroPadding = usingMicros ? "000000" : "000000000";
 4807   	    
 4808   	    nanosString = zeroPadding.substring(0, (digitCount-nanosString.length())) +
 4809   		nanosString; 
 4810   	    
 4811   	    int pos = digitCount; // the end, we're padded to the end by the code above
 4812   	    
 4813   	    while (nanosString.charAt(pos) == '0') {
 4814   	    	pos--;
 4815   	    }
 4816   	
 4817   	    nanosString = nanosString.substring(0, pos + 1);
 4818   	    
 4819   	    return nanosString;
 4820   	}
 4821   	
 4822   	private synchronized void newSetTimeInternal(int parameterIndex,
 4823   			Time x, Calendar targetCalendar) throws SQLException {
 4824   		if (this.tdf == null) {
 4825   			this.tdf = new SimpleDateFormat("''HH:mm:ss''", Locale.US); //$NON-NLS-1$
 4826   			
 4827   		}
 4828   
 4829   		String timeString = null;
 4830   		
 4831   		if (targetCalendar != null) {
 4832   			targetCalendar.setTime(x);
 4833   			this.tdf.setTimeZone(targetCalendar.getTimeZone());
 4834   			
 4835   			timeString = this.tdf.format(x);
 4836   		} else {
 4837   			this.tdf.setTimeZone(this.connection.getServerTimezoneTZ());
 4838   			timeString = this.tdf.format(x);
 4839   		}
 4840   
 4841   		setInternal(parameterIndex, timeString); 
 4842   	}
 4843   	
 4844   	private synchronized void newSetDateInternal(int parameterIndex,
 4845   			Date x, Calendar targetCalendar) throws SQLException {
 4846   		if (this.ddf == null) {
 4847   			this.ddf = new SimpleDateFormat("''yyyy-MM-dd''", Locale.US); //$NON-NLS-1$
 4848   			
 4849   		}
 4850   
 4851   		String timeString = null;
 4852   		
 4853   		if (targetCalendar != null) {
 4854   			targetCalendar.setTime(x);
 4855   			this.ddf.setTimeZone(targetCalendar.getTimeZone());
 4856   			
 4857   			timeString = this.ddf.format(x);
 4858   		} else {
 4859   			this.ddf.setTimeZone(this.connection.getServerTimezoneTZ());
 4860   			timeString = this.ddf.format(x);
 4861   		}
 4862   
 4863   		setInternal(parameterIndex, timeString); 
 4864   	}
 4865   
 4866   	private void doSSPSCompatibleTimezoneShift(int parameterIndex, Timestamp x, Calendar sessionCalendar) throws SQLException {
 4867   		Calendar sessionCalendar2 = (this.connection
 4868   				.getUseJDBCCompliantTimezoneShift()) ? this.connection
 4869   				.getUtcCalendar()
 4870   				: getCalendarInstanceForSessionOrNew();
 4871   
 4872   		synchronized (sessionCalendar2) {
 4873   			java.util.Date oldTime = sessionCalendar2.getTime();
 4874   
 4875   			try {
 4876   				sessionCalendar2.setTime(x);
 4877   
 4878   				int year = sessionCalendar2.get(Calendar.YEAR);
 4879   				int month = sessionCalendar2.get(Calendar.MONTH) + 1;
 4880   				int date = sessionCalendar2.get(Calendar.DAY_OF_MONTH);
 4881   
 4882   				int hour = sessionCalendar2.get(Calendar.HOUR_OF_DAY);
 4883   				int minute = sessionCalendar2.get(Calendar.MINUTE);
 4884   				int seconds = sessionCalendar2.get(Calendar.SECOND);
 4885   
 4886   				StringBuffer tsBuf = new StringBuffer();
 4887   
 4888   				tsBuf.append('\'');
 4889   				tsBuf.append(year);
 4890   
 4891   				tsBuf.append("-");
 4892   
 4893   				if (month < 10) {
 4894   					tsBuf.append('0');
 4895   				}
 4896   
 4897   				tsBuf.append(month);
 4898   
 4899   				tsBuf.append('-');
 4900   
 4901   				if (date < 10) {
 4902   					tsBuf.append('0');
 4903   				}
 4904   
 4905   				tsBuf.append(date);
 4906   
 4907   				tsBuf.append(' ');
 4908   
 4909   				if (hour < 10) {
 4910   					tsBuf.append('0');
 4911   				}
 4912   
 4913   				tsBuf.append(hour);
 4914   
 4915   				tsBuf.append(':');
 4916   
 4917   				if (minute < 10) {
 4918   					tsBuf.append('0');
 4919   				}
 4920   
 4921   				tsBuf.append(minute);
 4922   
 4923   				tsBuf.append(':');
 4924   
 4925   				if (seconds < 10) {
 4926   					tsBuf.append('0');
 4927   				}
 4928   
 4929   				tsBuf.append(seconds);
 4930   
 4931   				tsBuf.append('.');
 4932   				tsBuf.append(formatNanos(x.getNanos()));
 4933   				tsBuf.append('\'');
 4934   
 4935   				setInternal(parameterIndex, tsBuf.toString());
 4936   
 4937   			} finally {
 4938   				sessionCalendar.setTime(oldTime);
 4939   			}
 4940   		}
 4941   	}
 4942   
 4943   	/**
 4944   	 * When a very large Unicode value is input to a LONGVARCHAR parameter, it
 4945   	 * may be more practical to send it via a java.io.InputStream. JDBC will
 4946   	 * read the data from the stream as needed, until it reaches end-of-file.
 4947   	 * The JDBC driver will do any necessary conversion from UNICODE to the
 4948   	 * database char format.
 4949   	 * 
 4950   	 * <P>
 4951   	 * <B>Note:</B> This stream object can either be a standard Java stream
 4952   	 * object or your own subclass that implements the standard interface.
 4953   	 * </p>
 4954   	 * 
 4955   	 * @param parameterIndex
 4956   	 *            the first parameter is 1...
 4957   	 * @param x
 4958   	 *            the parameter value
 4959   	 * @param length
 4960   	 *            the number of bytes to read from the stream
 4961   	 * 
 4962   	 * @throws SQLException
 4963   	 *             if a database access error occurs
 4964   	 * 
 4965   	 * @deprecated
 4966   	 */
 4967   	public void setUnicodeStream(int parameterIndex, InputStream x, int length)
 4968   			throws SQLException {
 4969   		if (x == null) {
 4970   			setNull(parameterIndex, java.sql.Types.VARCHAR);
 4971   		} else {
 4972   			setBinaryStream(parameterIndex, x, length);
 4973   			
 4974   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB;
 4975   		}
 4976   	}
 4977   
 4978   	/**
 4979   	 * @see PreparedStatement#setURL(int, URL)
 4980   	 */
 4981   	public void setURL(int parameterIndex, URL arg) throws SQLException {
 4982   		if (arg != null) {
 4983   			setString(parameterIndex, arg.toString());
 4984   			
 4985   			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DATALINK;
 4986   		} else {
 4987   			setNull(parameterIndex, Types.CHAR);
 4988   		}
 4989   	}
 4990   
 4991   	private final void streamToBytes(Buffer packet, InputStream in,
 4992   			boolean escape, int streamLength, boolean useLength)
 4993   			throws SQLException {
 4994   		try {
 4995   			String connectionEncoding = this.connection.getEncoding();
 4996   
 4997   			boolean hexEscape = false;
 4998   
 4999   			if (this.connection.isNoBackslashEscapesSet()
 5000   					|| (this.connection.getUseUnicode() 
 5001   							&& connectionEncoding != null
 5002   							&& CharsetMapping.isMultibyteCharset(connectionEncoding)
 5003   							&& !this.connection.parserKnowsUnicode())) {
 5004   				hexEscape = true;
 5005   			}
 5006   
 5007   			if (streamLength == -1) {
 5008   				useLength = false;
 5009   			}
 5010   
 5011   			int bc = -1;
 5012   
 5013   			if (useLength) {
 5014   				bc = readblock(in, streamConvertBuf, streamLength);
 5015   			} else {
 5016   				bc = readblock(in, streamConvertBuf);
 5017   			}
 5018   
 5019   			int lengthLeftToRead = streamLength - bc;
 5020   
 5021   			if (hexEscape) {
 5022   				packet.writeStringNoNull("x");
 5023   			} else if (this.connection.getIO().versionMeetsMinimum(4, 1, 0)) {
 5024   				packet.writeStringNoNull("_binary");
 5025   			}
 5026   
 5027   			if (escape) {
 5028   				packet.writeByte((byte) '\'');
 5029   			}
 5030   
 5031   			while (bc > 0) {
 5032   				if (hexEscape) {
 5033   					hexEscapeBlock(streamConvertBuf, packet, bc);
 5034   				} else if (escape) {
 5035   					escapeblockFast(streamConvertBuf, packet, bc);
 5036   				} else {
 5037   					packet.writeBytesNoNull(streamConvertBuf, 0, bc);
 5038   				}
 5039   
 5040   				if (useLength) {
 5041   					bc = readblock(in, streamConvertBuf, lengthLeftToRead);
 5042   
 5043   					if (bc > 0) {
 5044   						lengthLeftToRead -= bc;
 5045   					}
 5046   				} else {
 5047   					bc = readblock(in, streamConvertBuf);
 5048   				}
 5049   			}
 5050   
 5051   			if (escape) {
 5052   				packet.writeByte((byte) '\'');
 5053   			}
 5054   		} finally {
 5055   			if (this.connection.getAutoClosePStmtStreams()) {
 5056   				try {
 5057   					in.close();
 5058   				} catch (IOException ioEx) {
 5059   					;
 5060   				}
 5061   
 5062   				in = null;
 5063   			}
 5064   		}
 5065   	}
 5066   
 5067   	private final byte[] streamToBytes(InputStream in, boolean escape,
 5068   			int streamLength, boolean useLength) throws SQLException {
 5069   		try {
 5070   			if (streamLength == -1) {
 5071   				useLength = false;
 5072   			}
 5073   
 5074   			ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
 5075   
 5076   			int bc = -1;
 5077   
 5078   			if (useLength) {
 5079   				bc = readblock(in, this.streamConvertBuf, streamLength);
 5080   			} else {
 5081   				bc = readblock(in, this.streamConvertBuf);
 5082   			}
 5083   
 5084   			int lengthLeftToRead = streamLength - bc;
 5085   
 5086   			if (escape) {
 5087   				if (this.connection.versionMeetsMinimum(4, 1, 0)) {
 5088   					bytesOut.write('_');
 5089   					bytesOut.write('b');
 5090   					bytesOut.write('i');
 5091   					bytesOut.write('n');
 5092   					bytesOut.write('a');
 5093   					bytesOut.write('r');
 5094   					bytesOut.write('y');
 5095   				}
 5096   				
 5097   				bytesOut.write('\'');
 5098   			}
 5099   
 5100   			while (bc > 0) {
 5101   				if (escape) {
 5102   					escapeblockFast(this.streamConvertBuf, bytesOut, bc);
 5103   				} else {
 5104   					bytesOut.write(this.streamConvertBuf, 0, bc);
 5105   				}
 5106   
 5107   				if (useLength) {
 5108   					bc = readblock(in, this.streamConvertBuf, lengthLeftToRead);
 5109   
 5110   					if (bc > 0) {
 5111   						lengthLeftToRead -= bc;
 5112   					}
 5113   				} else {
 5114   					bc = readblock(in, this.streamConvertBuf);
 5115   				}
 5116   			}
 5117   
 5118   			if (escape) {
 5119   				bytesOut.write('\'');
 5120   			}
 5121   
 5122   			return bytesOut.toByteArray();
 5123   		} finally {
 5124   			if (this.connection.getAutoClosePStmtStreams()) {
 5125   				try {
 5126   					in.close();
 5127   				} catch (IOException ioEx) {
 5128   					;
 5129   				}
 5130   
 5131   				in = null;
 5132   			}
 5133   		}
 5134   	}
 5135   	
 5136   	/**
 5137   	 * Returns this PreparedStatement represented as a string.
 5138   	 * 
 5139   	 * @return this PreparedStatement represented as a string.
 5140   	 */
 5141   	public String toString() {
 5142   		StringBuffer buf = new StringBuffer();
 5143   		buf.append(super.toString());
 5144   		buf.append(": "); //$NON-NLS-1$
 5145   
 5146   		try {
 5147   			buf.append(asSql());
 5148   		} catch (SQLException sqlEx) {
 5149   			buf.append("EXCEPTION: " + sqlEx.toString());
 5150   		}
 5151   
 5152   		return buf.toString();
 5153   	}
 5154   
 5155   
 5156   
 5157   	public synchronized boolean isClosed() throws SQLException {
 5158   		return this.isClosed;
 5159   	}
 5160   	
 5161   	/** 
 5162   	 * For calling stored functions, this will be -1 as we don't really count
 5163   	 * the first '?' parameter marker, it's only syntax, but JDBC counts it
 5164   	 * as #1, otherwise it will return 0 
 5165   	 *
 5166   	 */
 5167   	
 5168   	protected int getParameterIndexOffset() {
 5169   		return 0;
 5170   	}
 5171   	
 5172   	public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
 5173   		setAsciiStream(parameterIndex, x, -1);
 5174   	}
 5175   
 5176   	public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
 5177   		setAsciiStream(parameterIndex, x, (int)length);
 5178   		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB;
 5179   	}
 5180   
 5181   	public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
 5182   		setBinaryStream(parameterIndex, x, -1);
 5183   	}
 5184   
 5185   	public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
 5186   		setBinaryStream(parameterIndex, x, (int)length);	
 5187   	}
 5188   
 5189   	public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
 5190   		setBinaryStream(parameterIndex, inputStream);
 5191   	}
 5192   
 5193   	public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
 5194   		setCharacterStream(parameterIndex, reader, -1);
 5195   	}
 5196   
 5197   	public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
 5198   		setCharacterStream(parameterIndex, reader, (int)length);
 5199   		
 5200   	}
 5201   
 5202   	public void setClob(int parameterIndex, Reader reader) throws SQLException {
 5203   		setCharacterStream(parameterIndex, reader);
 5204   		
 5205   	}
 5206   
 5207   	public void setClob(int parameterIndex, Reader reader, long length)
 5208   			throws SQLException {
 5209   		setCharacterStream(parameterIndex, reader, length);
 5210   	}
 5211   	
 5212   	public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
 5213   		setNCharacterStream(parameterIndex, value, -1);
 5214   	}
 5215   	
 5216   	/**
 5217   	 * Set a parameter to a Java String value. The driver converts this to a SQL
 5218   	 * VARCHAR or LONGVARCHAR value with introducer _utf8 (depending on the 
 5219   	 * arguments size relative to the driver's limits on VARCHARs) when it sends 
 5220   	 * it to the database. If charset is set as utf8, this method just call setString.
 5221   	 * 
 5222   	 * @param parameterIndex
 5223   	 *            the first parameter is 1...
 5224   	 * @param x
 5225   	 *            the parameter value
 5226   	 * 
 5227   	 * @exception SQLException
 5228   	 *                if a database access error occurs
 5229   	 */
 5230   	public void setNString(int parameterIndex, String x) throws SQLException {
 5231   	    if (this.charEncoding.equalsIgnoreCase("UTF-8")
 5232   	            || this.charEncoding.equalsIgnoreCase("utf8")) {
 5233   	        setString(parameterIndex, x);
 5234   	        return;
 5235   	    }
 5236   	
 5237   	    // if the passed string is null, then set this column to null
 5238   	    if (x == null) {
 5239   	        setNull(parameterIndex, java.sql.Types.CHAR); 
 5240   	    } else {
 5241   	        int stringLength = x.length();
 5242   	        // Ignore sql_mode=NO_BACKSLASH_ESCAPES in current implementation.
 5243   	
 5244   	        // Add introducer _utf8 for NATIONAL CHARACTER
 5245   	        StringBuffer buf = new StringBuffer((int) (x.length() * 1.1 + 4));
 5246   	        buf.append("_utf8");
 5247   	        buf.append('\'');
 5248   	
 5249   	        //
 5250   	        // Note: buf.append(char) is _faster_ than
 5251   	        // appending in blocks, because the block
 5252   	        // append requires a System.arraycopy()....
 5253   	        // go figure...
 5254   	        //
 5255   	
 5256   	        for (int i = 0; i < stringLength; ++i) {
 5257   	            char c = x.charAt(i);
 5258   	
 5259   	            switch (c) {
 5260   	            case 0: /* Must be escaped for 'mysql' */
 5261   	                buf.append('\\');
 5262   	                buf.append('0');
 5263   	
 5264   	                break;
 5265   	
 5266   	            case '\n': /* Must be escaped for logs */
 5267   	                buf.append('\\');
 5268   	                buf.append('n');
 5269   	
 5270   	                break;
 5271   	
 5272   	            case '\r':
 5273   	                buf.append('\\');
 5274   	                buf.append('r');
 5275   	
 5276   	                break;
 5277   	
 5278   	            case '\\':
 5279   	                buf.append('\\');
 5280   	                buf.append('\\');
 5281   	
 5282   	                break;
 5283   	
 5284   	            case '\'':
 5285   	                buf.append('\\');
 5286   	                buf.append('\'');
 5287   	
 5288   	                break;
 5289   	
 5290   	            case '"': /* Better safe than sorry */
 5291   	                if (this.usingAnsiMode) {
 5292   	                    buf.append('\\');
 5293   	                }
 5294   	
 5295   	                buf.append('"');
 5296   	
 5297   	                break;
 5298   	
 5299   	            case '\032': /* This gives problems on Win32 */
 5300   	                buf.append('\\');
 5301   	                buf.append('Z');
 5302   	
 5303   	                break;
 5304   	
 5305   	            default:
 5306   	                buf.append(c);
 5307   	            }
 5308   	        }
 5309   	
 5310   	        buf.append('\'');
 5311   	
 5312   	        String parameterAsString = buf.toString();
 5313   	
 5314   	        byte[] parameterAsBytes = null;
 5315   	
 5316   	        if (!this.isLoadDataQuery) {
 5317   	            parameterAsBytes = StringUtils.getBytes(parameterAsString,
 5318   	                    this.connection.getCharsetConverter("UTF-8"), "UTF-8", 
 5319   	                            this.connection.getServerCharacterEncoding(),
 5320   	                            this.connection.parserKnowsUnicode(), getExceptionInterceptor());
 5321   	        } else {
 5322   	            // Send with platform character encoding
 5323   	            parameterAsBytes = parameterAsString.getBytes();
 5324   	        }
 5325   	
 5326   	        setInternal(parameterIndex, parameterAsBytes);
 5327   	        
 5328   	        this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = -9; /* Types.NVARCHAR */
 5329   	    }
 5330   	}
 5331   	
 5332   	/**
 5333   	 * JDBC 2.0 When a very large UNICODE value is input to a LONGVARCHAR
 5334   	 * parameter, it may be more practical to send it via a java.io.Reader. JDBC
 5335   	 * will read the data from the stream as needed, until it reaches
 5336   	 * end-of-file. The JDBC driver will do any necessary conversion from
 5337   	 * UNICODE to the database char format.
 5338   	 * 
 5339   	 * <P>
 5340   	 * <B>Note:</B> This stream object can either be a standard Java stream
 5341   	 * object or your own subclass that implements the standard interface.
 5342   	 * </p>
 5343   	 * 
 5344   	 * @param parameterIndex
 5345   	 *            the first parameter is 1, the second is 2, ...
 5346   	 * @param reader
 5347   	 *            the java reader which contains the UNICODE data
 5348   	 * @param length
 5349   	 *            the number of characters in the stream
 5350   	 * 
 5351   	 * @exception SQLException
 5352   	 *                if a database-access error occurs.
 5353   	 */
 5354   	public void setNCharacterStream(int parameterIndex, Reader reader,
 5355   			long length) throws SQLException {
 5356   	    try {
 5357   	        if (reader == null) {
 5358   	            setNull(parameterIndex, java.sql.Types.LONGVARCHAR);
 5359   	            
 5360   	        } else {
 5361   	            char[] c = null;
 5362   	            int len = 0;
 5363   	
 5364   	            boolean useLength = this.connection
 5365   	                    .getUseStreamLengthsInPrepStmts();
 5366   	            
 5367   	            // Ignore "clobCharacterEncoding" because utf8 should be used this time.
 5368   	
 5369   	            if (useLength && (length != -1)) {
 5370   	                c = new char[(int) length];  // can't take more than Integer.MAX_VALUE
 5371   	
 5372   	                int numCharsRead = readFully(reader, c, (int) length); // blocks
 5373   	                // until
 5374   	                // all
 5375   	                // read
 5376   	                setNString(parameterIndex, new String(c, 0, numCharsRead));
 5377   	
 5378   	            } else {
 5379   	                c = new char[4096];
 5380   	
 5381   	                StringBuffer buf = new StringBuffer();
 5382   	
 5383   	                while ((len = reader.read(c)) != -1) {
 5384   	                    buf.append(c, 0, len);
 5385   	                }
 5386   	
 5387   	                setNString(parameterIndex, buf.toString());
 5388   	            }
 5389   	            
 5390   	            this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = 2011; /* Types.NCLOB */
 5391   	        }
 5392   	    } catch (java.io.IOException ioEx) {
 5393   	        throw SQLError.createSQLException(ioEx.toString(),
 5394   	                SQLError.SQL_STATE_GENERAL_ERROR, getExceptionInterceptor());
 5395   	    }
 5396   	}
 5397   	
 5398   	public void setNClob(int parameterIndex, Reader reader) throws SQLException {
 5399   		setNCharacterStream(parameterIndex, reader);		
 5400   	}
 5401   
 5402   	/**
 5403   	 * JDBC 4.0 Set a NCLOB parameter.
 5404   	 * 
 5405   	 * @param parameterIndex
 5406   	 *            the first parameter is 1, the second is 2, ...
 5407   	 * @param reader
 5408   	 *            the java reader which contains the UNICODE data
 5409   	 * @param length
 5410   	 *            the number of characters in the stream
 5411   	 * 
 5412   	 * @throws SQLException
 5413   	 *             if a database error occurs
 5414   	 */
 5415   	public void setNClob(int parameterIndex, Reader reader, long length)
 5416   			throws SQLException {
 5417   	    if (reader == null) {
 5418   	        setNull(parameterIndex, java.sql.Types.LONGVARCHAR);
 5419   	    } else {
 5420   	        setNCharacterStream(parameterIndex, reader, length);
 5421   	    }
 5422   	}
 5423   	
 5424   	public ParameterBindings getParameterBindings() throws SQLException {
 5425   		return new EmulatedPreparedStatementBindings();
 5426   	}
 5427   	
 5428   	class EmulatedPreparedStatementBindings implements ParameterBindings {
 5429   
 5430   		private ResultSetImpl bindingsAsRs;
 5431   		private boolean[] parameterIsNull;
 5432   		
 5433   		public EmulatedPreparedStatementBindings() throws SQLException {
 5434   			List rows = new ArrayList();
 5435   			parameterIsNull = new boolean[parameterCount];
 5436   			System
 5437   					.arraycopy(isNull, 0, this.parameterIsNull, 0,
 5438   							parameterCount);
 5439   			byte[][] rowData = new byte[parameterCount][];
 5440   			Field[] typeMetadata = new Field[parameterCount];
 5441   
 5442   			for (int i = 0; i < parameterCount; i++) {
 5443   				if (batchCommandIndex == -1)
 5444   					rowData[i] = getBytesRepresentation(i);
 5445   				else
 5446   					rowData[i] = getBytesRepresentationForBatch(i, batchCommandIndex);
 5447   
 5448   				int charsetIndex = 0;
 5449   
 5450   				if (parameterTypes[i] == Types.BINARY
 5451   						|| parameterTypes[i] == Types.BLOB) {
 5452   					charsetIndex = 63;
 5453   				} else {
 5454   					String mysqlEncodingName = CharsetMapping
 5455   							.getMysqlEncodingForJavaEncoding(connection
 5456   									.getEncoding(), connection);
 5457   					charsetIndex = CharsetMapping
 5458   							.getCharsetIndexForMysqlEncodingName(mysqlEncodingName);
 5459   				}
 5460   
 5461   				Field parameterMetadata = new Field(null, "parameter_"
 5462   						+ (i + 1), charsetIndex, parameterTypes[i],
 5463   						rowData[i].length);
 5464   				parameterMetadata.setConnection(connection);
 5465   				typeMetadata[i] = parameterMetadata;
 5466   			}
 5467   
 5468   			rows.add(new ByteArrayRow(rowData, getExceptionInterceptor()));
 5469   
 5470   			this.bindingsAsRs = new ResultSetImpl(connection.getCatalog(),
 5471   					typeMetadata, new RowDataStatic(rows), connection, null);
 5472   			this.bindingsAsRs.next();
 5473   		}
 5474   		
 5475   		public Array getArray(int parameterIndex) throws SQLException {
 5476   			return this.bindingsAsRs.getArray(parameterIndex);
 5477   		}
 5478   
 5479   		public InputStream getAsciiStream(int parameterIndex)
 5480   				throws SQLException {
 5481   			return this.bindingsAsRs.getAsciiStream(parameterIndex);
 5482   		}
 5483   
 5484   		public BigDecimal getBigDecimal(int parameterIndex) throws SQLException {
 5485   			return this.bindingsAsRs.getBigDecimal(parameterIndex);
 5486   		}
 5487   
 5488   		public InputStream getBinaryStream(int parameterIndex)
 5489   				throws SQLException {
 5490   			return this.bindingsAsRs.getBinaryStream(parameterIndex);
 5491   		}
 5492   
 5493   		public java.sql.Blob getBlob(int parameterIndex) throws SQLException {
 5494   			return this.bindingsAsRs.getBlob(parameterIndex);
 5495   		}
 5496   
 5497   		public boolean getBoolean(int parameterIndex) throws SQLException {
 5498   			return this.bindingsAsRs.getBoolean(parameterIndex);
 5499   		}
 5500   
 5501   		public byte getByte(int parameterIndex) throws SQLException {
 5502   			return this.bindingsAsRs.getByte(parameterIndex);
 5503   		}
 5504   
 5505   		public byte[] getBytes(int parameterIndex) throws SQLException {
 5506   			return this.bindingsAsRs.getBytes(parameterIndex);
 5507   		}
 5508   
 5509   		public Reader getCharacterStream(int parameterIndex)
 5510   				throws SQLException {
 5511   			return this.bindingsAsRs.getCharacterStream(parameterIndex);
 5512   		}
 5513   
 5514   		public java.sql.Clob getClob(int parameterIndex) throws SQLException {
 5515   			return this.bindingsAsRs.getClob(parameterIndex);
 5516   		}
 5517   
 5518   		public Date getDate(int parameterIndex) throws SQLException {
 5519   			return this.bindingsAsRs.getDate(parameterIndex);
 5520   		}
 5521   
 5522   		public double getDouble(int parameterIndex) throws SQLException {
 5523   			return this.bindingsAsRs.getDouble(parameterIndex);
 5524   		}
 5525   
 5526   		public float getFloat(int parameterIndex) throws SQLException {
 5527   			return this.bindingsAsRs.getFloat(parameterIndex);
 5528   		}
 5529   
 5530   		public int getInt(int parameterIndex) throws SQLException {
 5531   			return this.bindingsAsRs.getInt(parameterIndex);
 5532   		}
 5533   
 5534   		public long getLong(int parameterIndex) throws SQLException {
 5535   			return this.bindingsAsRs.getLong(parameterIndex);
 5536   		}
 5537   
 5538   		public Reader getNCharacterStream(int parameterIndex)
 5539   				throws SQLException {
 5540   			return this.bindingsAsRs.getCharacterStream(parameterIndex);
 5541   		}
 5542   
 5543   		public Reader getNClob(int parameterIndex) throws SQLException {
 5544   			return this.bindingsAsRs.getCharacterStream(parameterIndex);
 5545   		}
 5546   
 5547   		public Object getObject(int parameterIndex) throws SQLException {
 5548   			checkBounds(parameterIndex, 0);
 5549   			
 5550   			if (parameterIsNull[parameterIndex - 1]) {
 5551   				return null;
 5552   			}
 5553   			
 5554   			// we can't rely on the default mapping for JDBC's 
 5555   			// ResultSet.getObject() for numerics, they're not one-to-one with
 5556   			// PreparedStatement.setObject
 5557   			
 5558   			switch (parameterTypes[parameterIndex - 1]) {
 5559   			case Types.TINYINT:
 5560   				return new Byte(getByte(parameterIndex));
 5561   			case Types.SMALLINT:
 5562   				return new Short(getShort(parameterIndex));
 5563   			case Types.INTEGER:
 5564   				return new Integer(getInt(parameterIndex));
 5565   			case Types.BIGINT:
 5566   				return new Long(getLong(parameterIndex));
 5567   			case Types.FLOAT:
 5568   				return new Float(getFloat(parameterIndex));
 5569   			case Types.DOUBLE: 
 5570   				return new Double(getDouble(parameterIndex));
 5571   			default:
 5572   				return this.bindingsAsRs.getObject(parameterIndex);
 5573   			}
 5574   		}
 5575   
 5576   		public Ref getRef(int parameterIndex) throws SQLException {
 5577   			return this.bindingsAsRs.getRef(parameterIndex);
 5578   		}
 5579   
 5580   		public short getShort(int parameterIndex) throws SQLException {
 5581   			return this.bindingsAsRs.getShort(parameterIndex);
 5582   		}
 5583   
 5584   		public String getString(int parameterIndex) throws SQLException {
 5585   			return this.bindingsAsRs.getString(parameterIndex);
 5586   		}
 5587   
 5588   		public Time getTime(int parameterIndex) throws SQLException {
 5589   			return this.bindingsAsRs.getTime(parameterIndex);
 5590   		}
 5591   
 5592   		public Timestamp getTimestamp(int parameterIndex) throws SQLException {
 5593   			return this.bindingsAsRs.getTimestamp(parameterIndex);
 5594   		}
 5595   
 5596   		public URL getURL(int parameterIndex) throws SQLException {
 5597   			return this.bindingsAsRs.getURL(parameterIndex);
 5598   		}
 5599   
 5600   		public boolean isNull(int parameterIndex) throws SQLException {
 5601   			checkBounds(parameterIndex, 0);
 5602   			
 5603   			return this.parameterIsNull[parameterIndex -1];
 5604   		}	
 5605   	}
 5606   	
 5607   	public String getPreparedSql() {
 5608   		if (this.rewrittenBatchSize == 0) {
 5609   			return this.originalSql;
 5610   		}
 5611   		
 5612   		try {
 5613   			return this.parseInfo.getSqlForBatch(this.parseInfo);
 5614   		} catch (UnsupportedEncodingException e) {
 5615   			throw new RuntimeException(e);
 5616   		}
 5617   	}
 5618   
 5619   	public int getUpdateCount() throws SQLException {
 5620   		int count = super.getUpdateCount();
 5621   		
 5622   		if (containsOnDuplicateKeyUpdateInSQL() && 
 5623   				this.compensateForOnDuplicateKeyUpdate) {
 5624   			if (count == 2 || count == 0) {
 5625   				count = 1;
 5626   			}
 5627   		}
 5628   		
 5629   		return count;
 5630   	}
 5631   	
 5632   	protected static boolean canRewrite(String sql, boolean isOnDuplicateKeyUpdate, int locationOfOnDuplicateKeyUpdate, int statementStartPos) {
 5633   		// Needs to be INSERT, can't have INSERT ... SELECT or
 5634   		// INSERT ... ON DUPLICATE KEY UPDATE with an id=LAST_INSERT_ID(...)
 5635   
 5636   		boolean rewritableOdku = true;
 5637   
 5638   		if (isOnDuplicateKeyUpdate) {
 5639   			int updateClausePos = StringUtils.indexOfIgnoreCase(
 5640   					locationOfOnDuplicateKeyUpdate, sql, " UPDATE ");
 5641   
 5642   			if (updateClausePos != -1) {
 5643   				rewritableOdku = StringUtils
 5644   						.indexOfIgnoreCaseRespectMarker(updateClausePos,
 5645   								sql, "LAST_INSERT_ID", "\"'`", "\"'`",
 5646   								false) == -1;
 5647   			}
 5648   		}
 5649   
 5650   		return StringUtils
 5651   				.startsWithIgnoreCaseAndWs(sql, "INSERT",
 5652   						statementStartPos)
 5653   				&& StringUtils.indexOfIgnoreCaseRespectMarker(
 5654   						statementStartPos, sql, "SELECT", "\"'`",
 5655   						"\"'`", false) == -1 && rewritableOdku;
 5656   	}
 5657   }

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