[pkg-java] r4921 - in trunk/mysql-connector-java: . debian docs src/com/mysql/jdbc src/com/mysql/jdbc/configs src/com/mysql/jdbc/exceptions src/com/mysql/jdbc/exceptions/jdbc4 src/com/mysql/jdbc/integration/c3p0 src/com/mysql/jdbc/integration/jboss src/com/mysql/jdbc/interceptors src/com/mysql/jdbc/jdbc2/optional src/com/mysql/jdbc/log src/com/mysql/jdbc/profiler src/com/mysql/jdbc/util src/doc/sources src/testsuite src/testsuite/regression src/testsuite/simple src/testsuite/simple/jdbc4 src/testsuite/ssl-test-certs

mkoch at alioth.debian.org mkoch at alioth.debian.org
Fri Nov 30 10:46:52 UTC 2007


Author: mkoch
Date: 2007-11-30 10:46:51 +0000 (Fri, 30 Nov 2007)
New Revision: 4921

Added:
   trunk/mysql-connector-java/debian/README.Debian
   trunk/mysql-connector-java/src/com/mysql/jdbc/BufferRow.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ByteArrayRow.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/CachedResultSetMetaData.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionImpl.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Extension.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/IterateBlock.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CallableStatement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4Connection.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4NClob.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ResultSet.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ParameterBindings.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/PingTarget.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetImpl.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetInternalMethods.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetRow.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataCursor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataKeyset.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/StatementImpl.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/StatementInterceptor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/StreamingNotifiable.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/configs/5-0-Compat.properties
   trunk/mysql-connector-java/src/com/mysql/jdbc/configs/coldFusion.properties
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/NotYetImplementedException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/
   trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java
   trunk/mysql-connector-java/src/testsuite/simple/ResultSetTest.java
   trunk/mysql-connector-java/src/testsuite/simple/jdbc4/
   trunk/mysql-connector-java/src/testsuite/simple/jdbc4/StatementsTest.java
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-cert.pem
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-key.pem
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-cert.pem
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-key.pem
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-req.pem
   trunk/mysql-connector-java/src/testsuite/ssl-test-certs/test-cert-store
Modified:
   trunk/mysql-connector-java/
   trunk/mysql-connector-java/CHANGES
   trunk/mysql-connector-java/EXCEPTIONS-CONNECTOR-J
   trunk/mysql-connector-java/build.xml
   trunk/mysql-connector-java/debian/changelog
   trunk/mysql-connector-java/debian/control
   trunk/mysql-connector-java/debian/rules
   trunk/mysql-connector-java/docs/README
   trunk/mysql-connector-java/docs/README.txt
   trunk/mysql-connector-java/docs/connector-j.html
   trunk/mysql-connector-java/docs/connector-j.pdf
   trunk/mysql-connector-java/src/com/mysql/jdbc/Blob.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/BlobFromLocator.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Buffer.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/CallableStatement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/CharsetMapping.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Clob.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/CommunicationsException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Connection.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionProperties.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Constants.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaData.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/DocsConnectionPropsHelper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessor.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessorResult.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeTokenizer.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ExportControlled.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Field.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/LocalizedErrorMessages.properties
   trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlDefs.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlIO.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlParameterMetadata.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/NonRegisteringDriver.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/NotUpdatable.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/PreparedStatement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ReplicationConnection.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetMetaData.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/RowData.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataDynamic.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataStatic.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/SQLError.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/ServerPreparedStatement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/SingleByteCharsetConverter.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/StandardSocketFactory.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Statement.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/StringUtils.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/TimeUtil.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/UpdatableResultSet.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/Util.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/configs/3-0-Compat.properties
   trunk/mysql-connector-java/src/com/mysql/jdbc/configs/maxPerformance.properties
   trunk/mysql-connector-java/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/log/LogUtils.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfileEventSink.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfilerEvent.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/util/PropertiesDocGenerator.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/util/ServerController.java
   trunk/mysql-connector-java/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java
   trunk/mysql-connector-java/src/doc/sources/errorMapToDocbook.xsl
   trunk/mysql-connector-java/src/testsuite/BaseTestCase.java
   trunk/mysql-connector-java/src/testsuite/regression/CallableStatementRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/ConnectionRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/DataSourceRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/EscapeProcessorRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/MetaDataRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/ResultSetRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/StatementRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/StressRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/StringRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/regression/SubqueriesRegressionTest.java
   trunk/mysql-connector-java/src/testsuite/simple/BlobTest.java
   trunk/mysql-connector-java/src/testsuite/simple/CallableStatementTest.java
   trunk/mysql-connector-java/src/testsuite/simple/CharsetTests.java
   trunk/mysql-connector-java/src/testsuite/simple/ConnectionTest.java
   trunk/mysql-connector-java/src/testsuite/simple/DataSourceTest.java
   trunk/mysql-connector-java/src/testsuite/simple/MetadataTest.java
   trunk/mysql-connector-java/src/testsuite/simple/StatementsTest.java
Log:
mysql-connector-java (5.1.5+dfsg-1) unstable; urgency=low

  * New upstream release. Closes: #450718.
  * Add Homepage field to debian/control.

 -- Michael Koch <konqueror at gmx.de>  Fri, 30 Nov 2007 10:34:13 +0100




Property changes on: trunk/mysql-connector-java
___________________________________________________________________
Name: svn:ignore
   + build
build-stamp
dist


Modified: trunk/mysql-connector-java/CHANGES
===================================================================
--- trunk/mysql-connector-java/CHANGES	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/CHANGES	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,594 +1,1346 @@
 # Changelog
-# $Id: CHANGES 5908 2006-10-19 15:47:48Z mmatthews $
+# $Id: CHANGES 6622 2007-10-05 19:05:36Z mmatthews $
+10-09-07 - Version 5.1.5
 
+    - Released instead of 5.1.4 to pickup patch for BUG#31053
+      from 5.0.8.
+      
+10-09-07 - Version 5.1.4 
+
+    - Added "autoSlowLog" configuration property, overrides 
+      "slowQueryThreshold*" properties, driver determines slow
+      queries by those that are slower than 5 * stddev of the mean
+      query time (outside the 96% percentile).
+      
+    - Fixed BUG#28256 - When connection is in read-only mode, 
+      queries that are wrapped in parentheses incorrectly identified 
+      as DML.
+       
+09-07-07 - Version 5.1.3 RC
+
+	- Setting "useBlobToStoreUTF8OutsideBMP" to "true" tells the
+	  driver to treat [MEDIUM/LONG/TINY]BLOB columns as [LONG]VARCHAR
+	  columns holding text encoded in UTF-8 that has characters
+	  outside the BMP (4-byte encodings), which MySQL server
+	  can't handle natively.
+
+	  Set "utf8OutsideBmpExcludedColumnNamePattern" to a regex so that
+	  column names matching the given regex will still be treated
+	  as BLOBs The regex must follow the patterns used for the
+	  java.util.regex package. The default is to exclude no columns,
+	  and include all columns.
+
+	  Set "utf8OutsideBmpIncludedColumnNamePattern" to specify exclusion
+	  rules to "utf8OutsideBmpExcludedColumnNamePattern". The regex must
+	  follow the patterns used for the java.util.regex package.
+
+	- New methods on com.mysql.jdbc.Statement: setLocalInfileInputStream()
+	  and getLocalInfileInputStream().
+
+	  setLocalInfileInputStream() sets an InputStream instance that will be used to send data
+      to the MySQL server for a "LOAD DATA LOCAL INFILE" statement
+      rather than a FileInputStream or URLInputStream that represents
+      the path given as an argument to the statement.
+
+      This stream will be read to completion upon execution of a
+      "LOAD DATA LOCAL INFILE" statement, and will automatically
+      be closed by the driver, so it needs to be reset
+      before each call to execute*() that would cause the MySQL
+      server to request data to fulfill the request for
+      "LOAD DATA LOCAL INFILE".
+
+      If this value is set to NULL, the driver will revert to using
+      a FileInputStream or URLInputStream as required.
+
+      getLocalInfileInputStream() returns the InputStream instance that will be used to send
+      data in response to a "LOAD DATA LOCAL INFILE" statement.
+
+      This method returns NULL if no such stream has been set
+      via setLocalInfileInputStream().
+
+    - The driver now connects with an initial character set
+      of "utf-8" solely for the purposes of authentication to
+      allow usernames and database names in any character set to
+      be used in the JDBC URL.
+
+    - Errors encountered during Statement/PreparedStatement/CallableStatement.executeBatch()
+      when "rewriteBatchStatements" has been set to "true" now return
+      BatchUpdateExceptions according to the setting of "continueBatchOnError".
+      
+      If "continueBatchOnError" is set to "true", the update counts for the
+      "chunk" that were sent as one unit will all be set to EXECUTE_FAILED, but
+      the driver will attempt to process the remainder of the batch. You can determine which
+      "chunk" failed by looking at the update counts returned in the BatchUpdateException.
+      
+      If "continueBatchOnError" is set to "false", the update counts returned
+      will contain the failed "chunk", and stop with the failed chunk, with all 
+      counts for the failed "chunk" set to EXECUTE_FAILED.
+      
+      Since MySQL doesn't return multiple error codes for multiple-statements, or
+      for multi-value INSERT/REPLACE, it is the application's responsibility to handle 
+      determining which item(s) in the "chunk" actually failed.
+      
+    - Statement.setQueryTimeout()s now affect the entire batch for batched 
+      statements, rather than the individual statements that make up the batch.
+      
+06-29-07 - Version 5.1.2 Beta
+
+    - Setting the configuration property "rewriteBatchedStatements"
+      to "true" will now cause the driver to rewrite batched prepared
+      statements with more than 3 parameter sets in a batch into
+      multi-statements (separated by ";") if they are not plain
+      (i.e. without SELECT or ON DUPLICATE KEY UPDATE clauses) INSERT
+      or REPLACE statements.
+
+06-22-07 - Version 5.1.1 Alpha
+
+    - Pulled vendor-extension methods of Connection implementation out
+      into an interface to support java.sql.Wrapper functionality from
+      ConnectionPoolDataSource. The vendor extensions are javadoc'd in
+      the com.mysql.jdbc.Connection interface.
+
+      For those looking further into the driver implementation, it is not
+      an API that is used for plugability of implementations inside our driver
+      (which is why there are still references to ConnectionImpl throughout the
+      code).
+
+      Incompatible change: Connection.serverPrepare(String) has been re-named
+      to Connection.serverPrepareStatement() for consistency with
+      Connection.clientPrepareStatement().
+
+      We've also added server and client prepareStatement() methods that cover
+      all of the variants in the JDBC API.
+
+    - Similar to Connection, we pulled out vendor extensions to Statement
+      into an interface named "com.mysql.Statement", and moved the Statement
+      class into com.mysql.StatementImpl. The two methods (javadoc'd in
+      "com.mysql.Statement" are enableStreamingResults(), which already existed,
+      and disableStreamingResults() which sets the statement instance back to
+      the fetch size and result set type it had before enableStreamingResults()
+      was called.
+
+    - Added experimental support for statement "interceptors" via the
+      com.mysql.jdbc.StatementInterceptor interface, examples are
+      in com/mysql/jdbc/interceptors.
+
+      Implement this interface to be placed "in between" query execution, so that
+      you can influence it. (currently experimental).
+
+      StatementInterceptors are "chainable" when configured by the user, the
+      results returned by the "current" interceptor will be passed on to the next
+      on in the chain, from left-to-right order, as specified by the user in the
+      JDBC configuration property "statementInterceptors".
+
+      See the sources (fully javadoc'd) for com.mysql.jdbc.StatementInterceptor
+      for more details until we iron out the API and get it documented in the
+      manual.
+
+    - Externalized the descriptions of connection properties.
+
+    - The data (and how it's stored) for ResultSet rows are now behind an
+      interface which allows us (in some cases) to allocate less memory
+      per row, in that for "streaming" result sets, we re-use the packet
+      used to read rows, since only one row at a time is ever active.
+
+    - Made it possible to retrieve prepared statement parameter bindings
+      (to be used in StatementInterceptors, primarily).
+
+    - Row navigation now causes any streams/readers open on the result set
+      to be closed, as in some cases we're reading directly from a shared network
+      packet and it will be overwritten by the "next" row.
+
+    - Setting "rewriteBatchedStatements" to "true" now causes CallableStatements
+      with batched arguments to be re-written in the form "CALL (...); CALL (...); ..."
+      to send the batch in as few client-server round trips as possible.
+
+    - Driver now picks appropriate internal row representation (whole row in one
+      buffer, or individual byte[]s for each column value) depending on heuristics,
+      including whether or not the row has BLOB or TEXT types and the overall
+      row-size. The threshold for row size that will cause the driver to
+      use a buffer rather than individual byte[]s is configured by the
+      configuration property "largeRowSizeThreshold", which has a default
+      value of 2KB.
+
+04-11-07 - Version 5.1.0 Alpha
+
+	- Bumped JDBC Specification version number in jar-file manifest.
+
+	- Re-worked Ant buildfile to build JDBC-4.0 classes separately, as well
+	  as support building under Eclipse (since Eclipse can't mix/match JDKs).
+
+	  To build, you must set JAVA_HOME to J2SDK-1.4.2 or Java-5, and set
+	  the following properties on your Ant commandline:
+
+	  com.mysql.jdbc.java6.javac - full path to your Java-6 javac executable
+	  com.mysql.jdbc.java6.rtjar - full path to your Java-6 rt.jar file
+
+	- New feature - driver will automatically adjust session variable
+	  "net_write_timeout" when it determines its been asked for a "streaming"
+	  result, and resets it to the previous value when the result set
+	  has been consumed. (configuration property is named
+	  "netTimeoutForStreamingResults", value has unit of seconds,
+	  the value '0' means the driver will not try and adjust this value).
+
+    - Added support for JDBC-4.0 categorized SQLExceptions.
+
+	- Refactored CommunicationsException into a JDBC3 version, and a JDBC4
+	  version (which extends SQLRecoverableException, now that it exists).
+
+	  This change means that if you were catching
+	  com.mysql.jdbc.CommunicationsException in your applications instead
+	  of looking at the SQLState class of "08", and are moving to Java 6
+	  (or newer), you need to change your imports to that exception
+	  to be com.mysql.jdbc.exceptions.jdbc4.CommunicationsException, as
+	  the old class will not be instantiated for communications link-related
+	  errors under Java 6.
+
+	- Added support for JDBC-4.0's client information. The backend storage
+	  of information provided via Connection.setClientInfo() and retrieved
+	  by Connection.getClientInfo() is pluggable by any class that implements
+	  the com.mysql.jdbc.JDBC4ClientInfoProvider interface and has a no-args
+	  constructor.
+
+	  The implementation used by the driver is configured using the
+	  "clientInfoProvider" configuration property (with a default of value
+	  of "com.mysql.jdbc.JDBC4CommentClientInfoProvider", an implementation
+	  which lists the client info as a comment prepended to every query
+	  sent to the server).
+
+	  This functionality is only available when using Java-6 or newer.
+
+	- Added support for JDBC-4.0's SQLXML interfaces.
+
+	- Added support for JDBC-4.0's Wrapper interface.
+
+	- Added support for JDBC-4.0's NCLOB, and NCHAR/NVARCHAR types.
+
+10-09-07 - Version 5.0.8
+
+    - Fixed BUG#30550, executeBatch() would fail with an ArithmeticException
+      and/or NullPointerException when the batch had zero members and
+      "rewriteBatchedStatements" was set to "true" for the connection.
+    
+    - Added two configuration parameters (both default to "false")
+    
+            * blobsAreStrings  - Should the driver always treat BLOBs as Strings 
+                                 specifically to work around dubious metadata returned 
+                                 by the server for GROUP BY clauses?
+            
+            * functionsNeverReturnBlobs - Should the driver always treat data from 
+                                          functions returning BLOBs as Strings - 
+                                          specifically to work around dubious metadata 
+                                          returned by the server for GROUP BY clauses?
+
+    - Fixed BUG#29106 - Connection checker for JBoss didn't use same method parameters
+      via reflection, causing connections to always seem "bad".
+      
+    - Fixed BUG#30664 - Note that this fix only works for MySQL server 
+      versions 5.0.25 and newer, since earlier versions didn't consistently 
+      return correct metadata for functions, and thus results from 
+      subqueries and functions were indistinguishable from each other, 
+      leading to type-related bugs.
+
+    - Fixed BUG#28972 - DatabaseMetaData.getTypeInfo() for the types DECIMAL
+      and NUMERIC will return a precision of 254 for server versions older than
+      5.0.3, 64 for versions 5.0.3-5.0.5 and 65 for versions newer than 5.0.5.
+    
+    - Fixed BUG#29852 - Closing a load-balanced connection would cause a
+      ClassCastException.
+    
+    - Fixed BUG#27867 - Schema objects with identifiers other than
+      the connection character aren't retrieved correctly in 
+      ResultSetMetadata.
+      
+    - Fixed BUG#28689 - CallableStatement.executeBatch() doesn't work when 
+      connection property "noAccessToProcedureBodies" has been set to "true".
+     
+      The fix involves changing the behavior of "noAccessToProcedureBodies",in 
+      that the driver will now report all paramters as "IN" paramters
+      but allow callers to call registerOutParameter() on them without throwing
+      an exception.
+      
+    - Fixed BUG#27182 - Connection.getServerCharacterEncoding() doesn't work
+      for servers with version >= 4.1.
+
+    - Fixed BUG#27915 - DatabaseMetaData.getColumns() doesn't
+      contain SCOPE_* or IS_AUTOINCREMENT columns.
+
+    - Fixed BUG#30851, NPE with null column values when
+      "padCharsWithSpace" is set to "true".
+    
+    - Specifying a "validation query" in your connection pool 
+      that starts with "/* ping */" _exactly_ will cause the driver to 
+      instead send a ping to the server and return a fake result set (much 
+      lighter weight), and when using a ReplicationConnection or a LoadBalancedConnection, 
+      will send the ping across all active connections.
+      
+    - Fixed Bug#30892 setObject(int, Object, int, int) delegate in
+      PreparedStatmentWrapper delegates to wrong method.
+      
+    - XAConnections now start in auto-commit mode (as per JDBC-4.0 specification
+      clarification).
+     
+    - Fixed Bug#27412 - cached metadata with PreparedStatement.execute()
+      throws NullPointerException.
+      
+    - Driver will now fall back to sane defaults for max_allowed_packet and
+      net_buffer_length if the server reports them incorrectly (and will log
+      this situation at WARN level, since it's actually an error condition).
+    
+    - Fixed BUG#27916 - UNSIGNED types not reported via DBMD.getTypeInfo(), and 
+      capitalization of type names is not consistent between DBMD.getColumns(), 
+      RSMD.getColumnTypeName() and DBMD.getTypeInfo().
+
+      This fix also ensures that the precision of UNSIGNED MEDIUMINT
+      and UNSIGNED BIGINT is reported correctly via DBMD.getColumns().
+
+    - Fixed BUG#31053 - Connections established using URLs of the form
+      "jdbc:mysql:loadbalance://" weren't doing failover if they tried to 
+      connect to a MySQL server that was down. The driver now attempts
+      connections to the next "best" (depending on the load balance strategy
+      in use) server, and continues to attempt connecting to the next "best"
+      server every 250 milliseconds until one is found that is up and running 
+      or 5 minutes has passed.
+      
+      If the driver gives up, it will throw the last-received SQLException.
+      
+07-19-07 - Version 5.0.7
+
+    - Setting the configuration parameter "useCursorFetch" to "true" for
+      MySQL-5.0+ enables the use of cursors that allow Connector/J to save
+      memory by fetching result set rows in chunks (where the chunk size
+      is set by calling setFetchSize() on a Statement or ResultSet) by
+      using fully-materialized cursors on the server.
+
+      The driver will will now automatically set "useServerPrepStmts" to
+      "true" when "useCursorFetch" has been set to "true", since the feature
+      requires server-side prepared statements in order to function.
+
+	- Fixed BUG#28469 - PreparedStatement.getMetaData() for statements
+	  containing leading one-line comments is not returned correctly.
+
+	  As part of this fix, we also overhauled detection of DML for
+	  executeQuery() and SELECTs for executeUpdate() in plain and
+	  prepared statements to be aware of the same  types of comments.
+
+    - Added configuration property "useNanosForElapsedTime" - for
+      profiling/debugging functionality that measures elapsed time,
+      should the driver try to use nanoseconds resolution if available
+      (requires JDK >= 1.5)?
+
+    - Added configuration property "slowQueryThresholdNanos" - if
+      "useNanosForElapsedTime" is set to "true", and this property
+      is set to a non-zero value the driver will use this threshold
+      (in nanosecond units) to determine if a query was slow, instead
+      of using millisecond units.
+
+      Note, that if "useNanosForElapsedTime" is set to "true", and this
+      property is set to "0" (or left default), then elapsed times will
+      still be measured in nanoseconds (if possible), but the slow query
+      threshold will be converted from milliseconds to nanoseconds, and thus
+      have an upper bound of approximately 2000 millesconds (as that threshold
+      is represented as an integer, not a long).
+
+	- Added configuration properties to allow tuning of TCP/IP socket
+	  parameters:
+
+	  	"tcpNoDelay" - Should the driver set SO_TCP_NODELAY (disabling the
+	  	               Nagle Algorithm, default "true")?
+
+		"tcpKeepAlive" - Should the driver set SO_KEEPALIVE (default "true")?
+
+		"tcpRcvBuf" - Should the driver set SO_RCV_BUF to the given value?
+		              The default value of '0', means use the platform default
+		              value for this property.
+
+		"tcpSndBuf" - Should the driver set SO_SND_BUF to the given value?
+		              The default value of '0', means use the platform default
+		              value for this property.
+
+		"tcpTrafficClass" - Should the driver set traffic class or
+		                    type-of-service fields? See the documentation
+		                    for java.net.Socket.setTrafficClass() for more
+		                    information.
+
+	- Give more information in EOFExceptions thrown out of MysqlIO (how many
+	  bytes the driver expected to read, how many it actually read, say that
+	  communications with the server were unexpectedly lost).
+
+	- Setting "useDynamicCharsetInfo" to "false" now causes driver to use
+	  static lookups for collations as well (makes
+	  ResultSetMetadata.isCaseSensitive() much more efficient, which leads
+	  to performance increase for ColdFusion, which calls this method for
+	  every column on every table it sees, it appears).
+
+	- Driver detects when it is running in a ColdFusion MX server (tested
+	  with version 7), and uses the configuration bundle "coldFusion",
+	  which sets useDynamicCharsetInfo to "false" (see previous entry), and
+	  sets useLocalSessionState and autoReconnect to "true".
+
+	- Fixed BUG#28851 - parser in client-side prepared statements
+	  eats character following '/' if it's not a multi-line comment.
+
+	- Fixed BUG#28956 - parser in client-side prepared statements
+	  runs to end of statement, rather than end-of-line for '#' comments.
+
+	  Also added support for '--' single-line comments.
+
+	- Don't send any file data in response to LOAD DATA LOCAL INFILE
+	  if the feature is disabled at the client side. This is to prevent
+	  a malicious server or man-in-the-middle from asking the client for
+	  data that the client is not expecting. Thanks to Jan Kneschke for
+	  discovering the exploit and Andrey "Poohie" Hristov, Konstantin Osipov
+	  and Sergei Golubchik for discussions about implications and possible
+	  fixes. This fixes BUG 29605 for JDBC.
+
+	- Added new debugging functionality - Setting configuration property
+	  "includeInnodbStatusInDeadlockExceptions" to "true" will cause the driver
+	  to append the output of "SHOW ENGINE INNODB STATUS" to deadlock-related
+	  exceptions, which will enumerate the current locks held inside InnoDB.
+
+05-15-07 - Version 5.0.6
+
+	- Fixed BUG#25545 - Client options not sent correctly when using SSL,
+	  leading to stored procedures not being able to return results. Thanks
+	  to Don Cohen for the bug report, testcase and patch.
+
+	- Fixed BUG#26592 - PreparedStatement is not closed in
+	  BlobFromLocator.getBytes().
+
+	- Fixed BUG#25624 - Whitespace surrounding storage/size specifiers in
+	  stored procedure parameters declaration causes NumberFormatException to
+	  be thrown when calling stored procedure on JDK-1.5 or newer, as the Number
+	  classes in JDK-1.5+ are whitespace intolerant.
+
+	- Fixed BUG#26173 - When useCursorFetch=true, sometimes server would return
+	  new, more exact metadata during the execution of the server-side prepared
+	  statement that enables this functionality, which the driver ignored (using
+	  the original metadata returned during prepare()), causing corrupt reading
+	  of data due to type mismatch when the actual rows were returned.
+
+	- Fixed BUG#26959 - comments in DDL of stored procedures/functions confuse
+	  procedure parser, and thus metadata about them can not be created, leading to
+	  inability to retrieve said metadata, or execute procedures that have certain
+	  comments in them.
+
+	- Give better error message when "streaming" result sets, and the connection
+	  gets clobbered because of exceeding net_write_timeout on the server. (which is
+	  basically what the error message says too).
+
+	- Fixed BUG#26789 - fast date/time parsing doesn't take into
+	  account 00:00:00 as a legal value.
+
+	- Fixed BUG#27317 - ResultSet.get*() with a column index < 1 returns
+	  misleading error message.
+
+	- Fixed BUG#25517 - Statement.setMaxRows() is not effective on result
+	  sets materialized from cursors.
+
+	- New configuration property, "enableQueryTimeouts" (default "true").
+	  When enabled, query timeouts set via Statement.setQueryTimeout() use a
+	  shared java.util.Timer instance for scheduling. Even if the timeout
+	  doesn't expire before the query is processed, there will be
+	  memory used by the TimerTask for the given timeout which won't be
+	  reclaimed until the time the timeout would have expired if it
+	  hadn't been cancelled by the driver. High-load environments
+	  might want to consider disabling this functionality. (this configuration
+	  property is part of the "maxPerformance" configuration bundle).
+
+	- Fixed BUG#27400 - CALL /* ... */ some_proc() doesn't work. As a side effect
+	  of this fix, you can now use /* */ and # comments when preparing statements using
+	  client-side prepared statement emulation.
+
+	  If the comments happen to contain parameter markers '?', they will be treated
+	  as belonging to the comment (i.e. not recognized) rather than being a parameter
+	  of the statement.
+
+	  Note that the statement when sent to the server will contain the comments
+	  as-is, they're not stripped during the process of preparing the PreparedStatement
+	  or CallableStatement.
+
+	- Fixed BUG#25328 - BIT(> 1) is returned as java.lang.String from ResultSet.getObject()
+	  rather than byte[].
+
+	- Fixed BUG#25715 - CallableStatements with OUT/INOUT parameters that
+	  are "binary" (blobs, bits, (var)binary, java_object) have extra 7 bytes
+	  (which happens to be the _binary introducer!)
+
+	- Added configuration property "padCharsWithSpace" (defaults to "false"). If set
+	  to "true", and a result set column has the CHAR type and the value does not
+	  fill the amount of characters specified in the DDL for the column, the driver
+	  will pad the remaining characters with space (for ANSI compliance).
+
+	- Fixed BUG#27655 - Connection.getTransactionIsolation() uses
+	  "SHOW VARIABLES LIKE" which is very inefficient on MySQL-5.0+
+
+	- Added configuration property "useDynamicCharsetInfo". If set to "false"
+	  (the default), the driver will use a per-connection cache of character set
+	  information queried from the server when necessary, or when set to "true",
+	  use a built-in static mapping that is more efficient, but isn't aware of
+	  custom character sets or character sets implemented after the release of
+	  the JDBC driver.
+
+	  Note: this only affects the "padCharsWithSpace" configuration property and the
+            ResultSetMetaData.getColumnDisplayWidth() method.
+
+	- More intelligent initial packet sizes for the "shared" packets are used
+	  (512 bytes, rather than 16K), and initial packets used during handshake are
+	  now sized appropriately as to not require reallocation.
+
+	- Fixed issue where calling getGeneratedKeys() on a prepared statement after
+	  calling execute() didn't always return the generated keys (executeUpdate()
+	  worked fine however).
+
+	- Fixed issue where a failed-over connection would let an application call
+	  setReadOnly(false), when that call should be ignored until the connection
+	  is reconnected to a writable master unless "failoverReadOnly" had been set
+	  to "false".
+
+	- Fixed BUG#28085 - Generate more useful error messages for diagnostics
+	  when the driver thinks a result set isn't updatable. (Thanks to Ashley Martens
+	  for the patch).
+
+	- Driver will now use INSERT INTO ... VALUES (DEFAULT) form of statement
+	  for updatable result sets for ResultSet.insertRow(), rather than
+	  pre-populating the insert row with values from DatabaseMetaData.getColumns()
+	  (which results in a "SHOW FULL COLUMNS" on the server for every result
+	  set). If an application requires access to the default values before
+	  insertRow() has been called, the JDBC URL should be configured with
+	  "populateInsertRowWithDefaultValues" set to "true".
+
+	  This fix specifically targets performance issues with ColdFusion and the
+	  fact that it seems to ask for updatable result sets no matter what the
+	  application does with them.
+
+	- com.mysql.jdbc.[NonRegistering]Driver now understands URLs of the format
+	  "jdbc:mysql:replication://" and "jdbc:mysql:loadbalance://" which will
+	  create a ReplicationConnection (exactly like when
+	  using [NonRegistering]ReplicationDriver) and an experimenal load-balanced
+	  connection designed for use with SQL nodes in a MySQL Cluster/NDB environment,
+	  respectively.
+
+	  In an effort to simplify things, we're working on deprecating multiple
+	  drivers, and instead specifying different core behavior based upon JDBC URL
+	  prefixes, so watch for [NonRegistering]ReplicationDriver to eventually
+	  disappear, to be replaced with com.mysql.jdbc[NonRegistering]Driver with
+	  the new URL prefix.
+
+	- Added an experimental load-balanced connection designed for use with SQL nodes
+      in a MySQL Cluster/NDB environment (This is not for master-slave replication.
+      For that, we suggest you look at ReplicationConnection or "lbpool").
+
+	  If the JDBC URL starts with "jdbc:mysql:loadbalance://host-1,host-2,...host-n",
+	  the driver will create an implementation of java.sql.Connection that load
+	  balances requests across a series of MySQL JDBC connections to the given hosts,
+	  where the balancing takes place after transaction commit.
+
+      Therefore, for this to work (at all), you must use transactions, even if only
+      reading data.
+
+      Physical connections to the given hosts will not be created until needed.
+
+      The driver will invalidate connections that it detects have had
+      communication errors when processing a request. A new connection to the
+      problematic host will be attempted the next time it is selected by the load
+      balancing algorithm.
+
+      There are two choices for load balancing algorithms, which may be specified
+      by the "loadBalanceStrategy" JDBC URL configuration property:
+
+      * "random" - the driver will pick a random host for each request. This tends
+        to work better than round-robin, as the randomness will somewhat account for
+        spreading loads where requests vary in response time, while round-robin
+        can sometimes lead to overloaded nodes if there are variations in response times
+        across the workload.
+
+      * "bestResponseTime" - the driver will route the request to the host that had
+        the best response time for the previous transaction.
+
+    - When "useLocalSessionState" is set to "true" and connected to a MySQL-5.0 or
+      later server, the JDBC driver will now determine whether an actual "commit" or
+      "rollback" statement needs to be sent to the database when Connection.commit()
+      or Connection.rollback() is called.
+
+      This is especially helpful for high-load situations with connection pools that
+      always call Connection.rollback() on connection check-in/check-out because it
+      avoids a round-trip to the server.
+
+03-01-07 - Version 5.0.5
+
+    - Fixed BUG#23645 - Some collations/character sets reported as "unknown"
+	  (specifically cias variants of existing character sets), and inability to override
+	  the detected server character set.
+
+	- Performance enhancement of initial character set configuration, driver
+      will only send commands required to configure connection character set
+      session variables if the current values on the server do not match
+      what is required.
+
+    - Fixed BUG#24360 .setFetchSize() breaks prepared SHOW and other commands.
+
+    - Fixed BUG#24344 - useJDBCCompliantTimezoneShift with server-side prepared
+	  statements gives different behavior than when using client-side prepared
+	  statements. (this is now fixed if moving from server-side prepared statements
+	  to client-side prepared statements by setting "useSSPSCompatibleTimezoneShift" to
+	  true", as the driver can't tell if this is a new deployment that never used
+	  server-side prepared statements, or if it is an existing deployment that is
+	  switching to client-side prepared statements from server-side prepared statements.
+
+    - Fixed BUG#23304 - DBMD using "show" and DBMD using information_schema do
+      not return results consistent with each other. (note this fix only
+      addresses the inconsistencies, not the issue that the driver is
+      treating schemas differently than some users expect. We will revisit
+      this behavior when there is full support for schemas in MySQL).
+
+    - Fixed BUG#25073 - rewriting batched statements leaks internal statement
+	  instances, and causes a memory leak.
+
+	- Fixed issue where field-level for metadata from DatabaseMetaData when using
+	  INFORMATION_SCHEMA didn't have references to current connections,
+	  sometimes leading to NullPointerExceptions when intropsecting them via
+	  ResultSetMetaData.
+
+	- Fixed BUG#25025 - Client-side prepared statement parser gets confused by
+	  in-line (/* ... */) comments and therefore can't rewrite batched statements
+	  or reliably detect type of statements when they're used.
+
+	- Fixed BUG#24065 - Better error message when server doesn't return enough
+	  information to determine stored procedure/function parameter types.
+
+	- Fixed BUG#21438 - Driver sending nanoseconds to server for timestamps when
+	  using server-side prepared statements, when server expects microseconds.
+
+	- Fixed BUG#25514 - Timer instance used for Statement.setQueryTimeout()
+	  created per-connection, rather than per-VM, causing memory leak
+
+	- Fixed BUG#25009 - Results from updates not handled correctly in
+	  multi-statement queries, leading to erroneous "Result is from UPDATE"
+	  exceptions.
+
+	- Fixed BUG#25047 - StringUtils.indexOfIgnoreCaseRespectQuotes() isn't
+	  case-insensitive on the first character of the target. This bug broke
+	  rewriteBatchedStatements functionality when prepared statements don't
+	  use upper-case for the VALUES clause in their statements.
+
+	- Fixed BUG#21480 - Some exceptions thrown out of StandardSocketFactory
+	  were needlessly wrapped, obscurring their true cause, especially when
+	  using socket timeouts.
+
+	- Fixed BUG#23303 - DatabaseMetaData.getSchemas() doesn't return a
+	  TABLE_CATALOG column.
+
+    - Fixed BUG#25399 - EscapeProcessor gets confused by multiple
+      backslashes. We now push the responsibility of syntax errors back
+      on to the server for most escape sequences.
+
+	- Fixed BUG#25379 - INOUT parameters in CallableStatements get
+	  doubly-escaped.
+
+	- Removed non-short-circuited logical ORs from "if" statements.
+
+	- Re-worked stored procedure parameter parser to be more robust. Driver no
+	  longer requires "BEGIN" in stored procedure definition, but does have
+	  requirement that if a stored function begins with a label directly after the
+	  "returns" clause, that the label is not a quoted identifier.
+    - Reverted back to internal character conversion routines for single-byte
+      character sets, as the ones internal to the JVM are using much more CPU
+      time than our internal implementation.
+
+	- Changed cached result set metadata (when using
+	  "cacheResultSetMetadata=true") to be cached per-connection rather
+	  than per-statement as previously implemented.
+
+	- Use a java.util.TreeMap to map column names to ordinal indexes for
+	  ResultSet.findColumn() instead of a HashMap. This allows us to have
+	  case-insensitive lookups (required by the JDBC specification) without
+	  resorting to the many transient object instances needed to support this
+	  requirement with a normal HashMap with either case-adjusted keys, or
+	  case-insensitive keys. (In the worst case scenario for lookups of a 1000
+	  column result set, TreeMaps are about half as fast wall-clock time as
+	  a HashMap, however in normal applications their use gives many orders
+	  of magnitude reduction in transient object instance creation which pays
+	  off later for CPU usage in garbage collection).
+
+	- Avoid static synchronized code in JVM class libraries for dealing with
+	  default timezones.
+
+	- Fixed cases where ServerPreparedStatements weren't using cached metadata
+	  when "cacheResultSetMetadata=true" was configured.
+
+	- Use faster datetime parsing for ResultSets that come from plain or
+	  non-server-side prepared statements. (Enable old implementation with
+	  "useFastDateParsing=false" as a configuration parameter).
+
+	- Fixed BUG#24794 - DatabaseMetaData.getSQLKeywords() doesn't return
+	  all reserved words for current MySQL version. The current fix/implementation
+	  returns keywords for MySQL-5.1, and doesn't distinguish between different
+	  versions of the server.
+
+	- When using cached metadata, skip field-level metadata packets coming from
+	  the server, rather than reading them and discarding them without creating
+	  com.mysql.jdbc.Field instances.
+
+	- Fixed BUG#25836 - Statement execution which timed out doesn't always
+	  throw MySQLTimeoutException.
+
+	- Throw exceptions encountered during timeout to thread
+	  calling Statement.execute*(), rather than RuntimeException.
+
+	- Added configuration property "localSocketAddress",which is the hostname or
+	  IP address given to explicitly configure the interface that the driver will
+	  bind the client side of the TCP/IP connection to when connecting.
+
+	- Take "localSocketAddress" property into account when creating instances
+	  of CommunicationsException when the underyling exception is a
+	  java.net.BindException, so that a friendlier error message is given with
+	  a little internal diagnostics.
+
+	- Fixed some NPEs when cached metadata was used with UpdatableResultSets.
+
+	- The "rewriteBatchedStatements" feature can now be used with server-side
+	  prepared statements.
+
+	- Fixed BUG#26326 - Connection property "socketFactory" wasn't exposed via
+	  correctly named mutator/accessor, causing data source implementations that
+	  use JavaBean naming conventions to set properties to fail to set the property
+	  (and in the case of SJAS, fail silently when trying to set this parameter).
+
+	- Fixed BUG#25787 - java.util.Date should be serialized for
+	  PreparedStatement.setObject().
+
+	  We've added a new configuration option "treatUtilDateAsTimestamp", which is
+	  false by default, as (1) We already had specific behavior to treat
+	  java.util.Date as a java.sql.Timestamp because it's useful to many folks,
+	  and (2) that behavior will very likely be required for drivers JDBC-post-4.0.
+
+    - Fixed BUG#22628 - Driver.getPropertyInfo() throws NullPointerException for
+      URL that only specifies host and/or port.
+
+	- Fixed BUG#21267, ParameterMetaData throws NullPointerException when
+	  prepared SQL actually has a syntax error. Added
+	  "generateSimpleParameterMetadata" configuration property, which when set
+	  to "true" will generate metadata reflecting VARCHAR for every parameter
+	  (the default is "false", which will cause an exception to be thrown if no
+	  parameter metadata for the statement is actually available).
+
+	- When extracting foreign key information from "SHOW CREATE TABLE " in
+	  DatabaseMetaData, ignore exceptions relating to tables being missing
+	  (which could happen for cross-reference or imported-key requests, as
+	  the list of tables is generated first, then iterated).
+
+	- Fixed logging of XA commands sent to server, it's now configurable
+	  via "logXaCommands" property (defaults to "false").
+
+	- Fixed issue where XADataSources couldn't be bound into JNDI,
+	  as the DataSourceFactory didn't know how to create instances
+	  of them.
+
+	- Fixed issue where XADataSources couldn't be bound into JNDI,
+	  as the DataSourceFactory didn't know how to create instances
+	  of them.
+
+	- Usage advisor will now issue warnings for result sets with large numbers
+	  of rows (size configured by "resultSetSizeThreshold" property, default
+	  value is 100).
+
 10-20-06 - Version 5.0.4
 
-    - Fixed BUG#21379 - column names don't match metadata in cases 
+    - Fixed BUG#21379 - column names don't match metadata in cases
       where server doesn't return original column names (column functions)
 	  thus breaking compatibility with applications that expect 1-1 mappings
 	  between findColumn() and rsmd.getColumnName(), usually manifests itself
 	  as "Can't find column ('')" exceptions.
-	 
-    - Fixed BUG#21544 - When using information_schema for metadata, 
-	  COLUMN_SIZE for getColumns() is not clamped to range of 
-	  java.lang.Integer as is the case when not using 
-	  information_schema, thus leading to a truncation exception that 
+
+    - Fixed BUG#21544 - When using information_schema for metadata,
+	  COLUMN_SIZE for getColumns() is not clamped to range of
+	  java.lang.Integer as is the case when not using
+	  information_schema, thus leading to a truncation exception that
 	  isn't present when not using information_schema.
-	 
+
     - Fixed configuration property "jdbcCompliantTruncation" was not
       being used for reads of result set values.
-      
+
     - Fixed BUG#22024 - Newlines causing whitespace to span confuse
-	  procedure parser when getting parameter metadata for stored 
+	  procedure parser when getting parameter metadata for stored
 	  procedures.
-	  
+
 	- Driver now supports {call sp} (without "()" if procedure has no
 	  arguments).
-	  
+
 	- Fixed BUG#22359 - Driver was using milliseconds for
 	  Statement.setQueryTimeout() when specification says argument is
 	  to be in seconds.
-	  
+
 	- Workaround for server crash when calling stored procedures
-	  via a server-side prepared statement (driver now detects 
-	  prepare(stored procedure) and substitutes client-side prepared 
+	  via a server-side prepared statement (driver now detects
+	  prepare(stored procedure) and substitutes client-side prepared
 	  statement), addresses BUG#22297.
-	  
-	- Added new _ci collations to CharsetMapping, fixing 
+
+	- Added new _ci collations to CharsetMapping, fixing
 	  Bug#22456 - utf8_unicode_ci not working.
-	  
+
 	- Fixed BUG#22290 - Driver issues truncation on write exception when
 	  it shouldn't (due to sending big decimal incorrectly to server with
 	  server-side prepared statement).
-	  
+
 	- Fixed BUG#22613 - DBMD.getColumns() does not return expected
 	  COLUMN_SIZE for the SET type, now returns length of largest possible
-	  set disregarding whitespace or the "," delimitters to be consistent 
+	  set disregarding whitespace or the "," delimitters to be consistent
 	  with the ODBC driver.
-	  
+
 	- Driver now sends numeric 1 or 0 for client-prepared statement
 	  setBoolean() calls instead of '1' or '0'.
-	  
+
 	- DatabaseMetaData correctly reports true for supportsCatalog*()
 	  methods.
-	  	  
+
 07-26-06 - Version 5.0.3
 
     - Fixed BUG#20650 - Statement.cancel() causes NullPointerException
       if underlying connection has been closed due to server failure.
-    
+
     - Added configuration option "noAccessToProcedureBodies" which will
       cause the driver to create basic parameter metadata for
       CallableStatements when the user does not have access to procedure
       bodies via "SHOW CREATE PROCEDURE" or selecting from mysql.proc
       instead of throwing an exception. The default value for this option
       is "false".
-      
+
 07-11-06 - Version 5.0.2-beta (5.0.1 not released due to packaging error)
 
     - Fixed BUG#17401 - Can't use XAConnection for local transactions when
       no global transaction is in progress.
-      
+
     - Fixed BUG#18086 - Driver fails on non-ASCII platforms. The driver
-      was assuming that the platform character set would be a superset 
+      was assuming that the platform character set would be a superset
       of MySQL's "latin1" when doing the handshake for authentication,
       and when reading error messages. We now use Cp1252 for all strings
       sent to the server during the handshake phase, and a hard-coded mapping
-      of the "language" server variable to the character set that 
+      of the "language" server variable to the character set that
       is used for error messages.
-      
+
     - Fixed BUG#19169 - ConnectionProperties (and thus some
 	  subclasses) are not serializable, even though some J2EE containers
 	  expect them to be.
-	  
+
 	- Fixed BUG#20242 - MysqlValidConnectionChecker for JBoss doesn't
 	  work with MySQLXADataSources.
-	  
+
 	- Better caching of character set converters (per-connection)
 	  to remove a bottleneck for multibyte character sets.
-	  
-	- Added connection/datasource property  "pinGlobalTxToPhysicalConnection" 
-	  (defaults to "false"). When set to "true", when using XAConnections, the 
-	  driver ensures that operations on a given XID are always routed to the 
-	  same physical connection. This allows the XAConnection to support 
-	  "XA START ... JOIN" after "XA END" has been called, and is also a 
+
+	- Added connection/datasource property  "pinGlobalTxToPhysicalConnection"
+	  (defaults to "false"). When set to "true", when using XAConnections, the
+	  driver ensures that operations on a given XID are always routed to the
+	  same physical connection. This allows the XAConnection to support
+	  "XA START ... JOIN" after "XA END" has been called, and is also a
 	  workaround for transaction managers that don't maintain thread affinity
-	  for a global transaction (most either always maintain thread affinity, 
+	  for a global transaction (most either always maintain thread affinity,
 	  or have it as a configuration option).
-	  
-	- MysqlXaConnection.recover(int flags) now allows combinations of 
+
+	- MysqlXaConnection.recover(int flags) now allows combinations of
 	  XAResource.TMSTARTRSCAN and TMENDRSCAN. To simulate the "scanning"
 	  nature of the interface, we return all prepared XIDs for TMSTARTRSCAN,
 	  and no new XIDs for calls with TMNOFLAGS, or TMENDRSCAN when not in
 	  combination with TMSTARTRSCAN. This change was made for API compliance,
 	  as well as integration with IBM WebSphere's transaction manager.
-	  
+
 12-23-05 - Version 5.0.0-beta
 
-    - XADataSource implemented (ported from 3.2 branch which won't be 
-      released as a product). Use 
+    - XADataSource implemented (ported from 3.2 branch which won't be
+      released as a product). Use
       "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" as your datasource
       class name in your application server to utilize XA transactions
       in MySQL-5.0.10 and newer.
-      
+
     - PreparedStatement.setString() didn't work correctly when
       sql_mode on server contained NO_BACKSLASH_ESCAPES, and no characters
       that needed escaping were present in the string.
-      
+
     - Attempt detection of the MySQL type "BINARY" (it's an alias, so this isn't
       always reliable), and use the java.sql.Types.BINARY type mapping for it.
-      
+
     - Moved -bin-g.jar file into separate "debug" subdirectory to avoid confusion.
-      
+
     - Don't allow .setAutoCommit(true), or .commit() or .rollback() on an XA-managed
       connection as-per the JDBC specification.
-      
+
     - If the connection "useTimezone" is set to "true", then also respect timezone
-      conversions in escape-processed string literals (e.g. "{ts ...}" and 
+      conversions in escape-processed string literals (e.g. "{ts ...}" and
       "{t ...}").
-      
+
     - Return original column name for RSMD.getColumnName() if the column was aliased,
       alias name for .getColumnLabel() (if aliased), and original table name
       for .getTableName(). Note this only works for MySQL-4.1 and newer, as
       older servers don't make this information available to clients.
-      
+
     - Setting "useJDBCCompliantTimezoneShift=true" (it's not the default)
       causes the driver to use GMT for _all_ TIMESTAMP/DATETIME timezones,
       and the current VM timezone for any other type that refers to timezones.
       This feature can not be used when "useTimezone=true" to convert between
       server and client timezones.
-      
+
     - Add one level of indirection of internal representation of CallableStatement
       parameter metadata to avoid class not found issues on JDK-1.3 for
       ParameterMetadata interface (which doesn't exist prior to JDBC-3.0).
-      
+
     - Added unit tests for XADatasource, as well as friendlier exceptions
       for XA failures compared to the "stock" XAException (which has no
       messages).
-      
-    - Fixed BUG#14279 - Idle timeouts cause XAConnections to whine about rolling 
+
+    - Fixed BUG#14279 - Idle timeouts cause XAConnections to whine about rolling
       themselves back
-      
+
     - Added support for Connector/MXJ integration via url subprotocol
       "jdbc:mysql:mxj://....".
-      
+
     - Moved all SQLException constructor usage to a factory in SQLError
       (ground-work for JDBC-4.0 SQLState-based exception classes).
-      
+
     - Removed Java5-specific calls to BigDecimal constructor (when
       result set value is '', (int)0 was being used as an argument
       in-directly via method return value. This signature doesn't exist
       prior to Java5.)
-      
+
     - Moved all SQLException creation to a factory method in SQLError,
       groundwork for JDBC-4.0 SQLState class-based exceptions.
-      
+
     - Added service-provider entry to META-INF/services/java.sql.Driver
       for JDBC-4.0 support.
-      
+
     - Return "[VAR]BINARY" for RSMD.getColumnTypeName() when that is actually
       the type, and it can be distinguished (MySQL-4.1 and newer).
-      
-    - When fix for BUG#14562 was merged from 3.1.12, added functionality 
+
+    - When fix for BUG#14562 was merged from 3.1.12, added functionality
       for CallableStatement's parameter metadata to return correct
       information for .getParameterClassName().
-      
+
     - Fuller synchronization of Connection to avoid deadlocks when
       using multithreaded frameworks that multithread a single connection
-      (usually not recommended, but the JDBC spec allows it anyways), 
+      (usually not recommended, but the JDBC spec allows it anyways),
       part of fix to BUG#14972).
-      
+
     - Implementation of Statement.cancel() and Statement.setQueryTimeout().
       Both require MySQL-5.0.0 or newer server, require a separate connection
       to issue the "KILL QUERY" command, and in the case of setQueryTimeout()
       creates an additional thread to handle the timeout functionality.
-      
+
       Note: Failures to cancel the statement for setQueryTimeout() may manifest
       themselves as RuntimeExceptions rather than failing silently, as there
       is currently no way to unblock the thread that is executing the query being
       cancelled due to timeout expiration and have it throw the exception
       instead.
-      
-    - Removed dead code in com.mysql.jdbc.Connection. 
-    
+
+    - Removed dead code in com.mysql.jdbc.Connection.
+
     - Made construction of com.mysql.jdbc.Field (result set metadata)
       instances more efficient for non-string types by not doing
       character set initialization, or detection of type changes due to
       temporary tables.
-      
+
     - Removed redundant code in com.mysql.jdbc.MysqlIO.
-    
+
     - Removed work done for BUG#14652, and instead loosened synchronization
       to solve a number of deadlock issues in BUG#18719, BUG#18367, BUG#17709
       and BUG#15067. New strategy basically makes Connection instances threadsafe
-      and thus shareable across threads, and anything else threadsafe, but not 
-      necessarily shareable across threads due to JDBC API interactions that 
+      and thus shareable across threads, and anything else threadsafe, but not
+      necessarily shareable across threads due to JDBC API interactions that
       can cause non-obvious behavior and/or deadlock scenarios to occur since
-      the API is not designed to be used from multiple threads at once. 
-      
+      the API is not designed to be used from multiple threads at once.
+
       Therefore, unless external synchronization is provided, clients should
       not allow multiple threads to share a given statement or result set. Examples
-      of issues with the API itself not being multi-thread suitable include, 
-      but are not limited to race conditions between modifiers and execution and 
-      retrieval methods on statements and result sets that are not synchronizable 
-      such as ResultSet.get*() and traversal methods, or Statement.execute*() closing 
-      result sets without effectively making the driver itself serializable across the 
+      of issues with the API itself not being multi-thread suitable include,
+      but are not limited to race conditions between modifiers and execution and
+      retrieval methods on statements and result sets that are not synchronizable
+      such as ResultSet.get*() and traversal methods, or Statement.execute*() closing
+      result sets without effectively making the driver itself serializable across the
       board.
-      
+
       These changes should not have any effect on "normal" J(2)EE use cases
       where only one thread ever uses a connection instance and the objects created by
       it.
-      
+
     - Use a java.util.Timer to schedule cancellation of queries via
       Statement.setQueryTimeout() rather than one thread per potential cancellation.
-      
+
       A new thread will be used to actually cancel a running query, as there's potential
       for a cancel request to block other cancel requests if all run from the
       same thread.
-      
-nn-nn-06 - Version 3.1.15
 
+nn-nn-07 - Version 3.1.15
+
 	- Fixed BUG#23281 - Downed slave caused round-robin load balance to
 	  not cycle back to first host in list.
-	  
+
+	- Disabled use of server-side prepared statements by default.
+
+	- Handle YYYY-MM-DD hh:mm:ss format of timestamp in
+	  ResultSet.getTimeFromString().
+
+	- Fixed BUG#24840 - character encoding of "US-ASCII" doesn't map correctly
+	  for 4.1 or newer
+
+	- Added Implementation-Vendor-Id attribute to jar manifest per request
+	  in BUG#15641.
+
+	- C3P0 >= version 0.9.1 passes non-proxied connections to
+	  MysqlConnectionTester,  thus it began throwing ClassCastExceptions.
+	  MysqlConnectionTester now checks if it has a plain Connection and uses
+	  that if possible. Thanks to Brian Skrab for the fix.
+
 10-19-06 - Version 3.1.14
 
     - Fixed BUG#20479 - Updatable result set throws ClassCastException
 	  when there is row data and moveToInsertRow() is called.
-	  
+
 	- Fixed BUG#20485 - Updatable result set that contains
 	  a BIT column fails when server-side prepared statements are used.
-	  
+
 	- Fixed BUG#16987 - Memory leak with profileSQL=true.
-	
-	- Fixed BUG#19726 - Connection fails to localhost when using 
+
+	- Fixed BUG#19726 - Connection fails to localhost when using
 	  timeout and IPv6 is configured.
-	  
+
 	- Fixed BUG#16791 - NullPointerException in MysqlDataSourceFactory
 	  due to Reference containing RefAddrs with null content.
-	  
+
 	- Fixed BUG#20306 - ResultSet.getShort() for UNSIGNED TINYINT
 	  returns incorrect values when using server-side prepared statements.
-	  
+
 	- Fixed BUG#20687 - Can't pool server-side prepared statements, exception
 	  raised when re-using them.
-	  
+
 	- Fixed BUG#21062 - ResultSet.getSomeInteger() doesn't work for BIT(>1).
-	
+
 	- Fixed BUG#18880 - ResultSet.getFloatFromString() can't retrieve
 	  values near Float.MIN/MAX_VALUE.
-	  
-	- Fixed BUG#20888 - escape of quotes in client-side prepared 
-	  statements parsing not respected. Patch covers more than bug report, 
+
+	- Fixed BUG#20888 - escape of quotes in client-side prepared
+	  statements parsing not respected. Patch covers more than bug report,
 	  including NO_BACKSLASH_ESCAPES being set, and stacked quote characters
 	  forms of escaping (i.e. '' or "").
-	  
+
 	- Fixed BUG#19993 - ReplicationDriver does not always round-robin load
 	  balance depending on URL used for slaves list.
-	
+
 	- Fixed calling toString() on ResultSetMetaData for driver-generated
 	  (i.e. from DatabaseMetaData method calls, or from getGeneratedKeys())
 	  result sets would raise a NullPointerException.
-	  
+
 	- Fixed Bug#21207 - Driver throws NPE when tracing prepared statements that
 	  have been closed (in asSQL()).
-	  
-	- Removed logger autodectection altogether, must now specify logger 
+
+	- Removed logger autodectection altogether, must now specify logger
 	  explitly if you want to use a logger other than one that logs
 	  to STDERR.
-	  
+
 	- Fixed BUG#22290 - Driver issues truncation on write exception when
 	  it shouldn't (due to sending big decimal incorrectly to server with
 	  server-side prepared statement).
-	  
+
 	- Driver now sends numeric 1 or 0 for client-prepared statement
 	  setBoolean() calls instead of '1' or '0'.
-	  
-	- Fixed bug where driver would not advance to next host if 
+
+	- Fixed bug where driver would not advance to next host if
 	  roundRobinLoadBalance=true and the last host in the list is down.
 
 	- Fixed BUG#18258 - DatabaseMetaData.getTables(), columns() with bad
 	  catalog parameter threw exception rather than return empty result
 	  set (as required by spec).
-	
+
 	- Check and store value for continueBatchOnError property in constructor
       of Statements, rather than when executing batches, so that Connections
       closed out from underneath statements don't cause NullPointerExceptions
       when it's required to check this property.
-      
-    - Fixed bug when calling stored functions, where parameters weren't 
+
+    - Fixed bug when calling stored functions, where parameters weren't
       numbered correctly (first parameter is now the return value, subsequent
       parameters if specified start at index "2").
-       
+
 	- Fixed BUG#21814 - time values outside valid range silently wrap.
-	       	  	        
-    - Fixed bug when calling stored functions, where parameters weren't 
-      numbered correctly (first parameter is now the return value, subsequent
-      parameters if specified start at index "2").
-       
-	- Fixed BUG#21814 - time values outside valid range silently wrap.
-	       	  	      	  	  	  	  
+
 05-26-06 - Version 3.1.13
 
     - Fixed BUG#15464 - INOUT parameter does not store IN value.
-    
-    - Fixed BUG#14609 - Exception thrown for new decimal type when 
+
+    - Fixed BUG#14609 - Exception thrown for new decimal type when
       using updatable result sets.
-            
+
     - Fixed BUG#15544, no "dos" character set in MySQL > 4.1.0
-    
-    - Fixed BUG#15383 - PreparedStatement.setObject() serializes 
-      BigInteger as object, rather than sending as numeric value 
-      (and is thus not complementary to .getObject() on an UNSIGNED 
+
+    - Fixed BUG#15383 - PreparedStatement.setObject() serializes
+      BigInteger as object, rather than sending as numeric value
+      (and is thus not complementary to .getObject() on an UNSIGNED
       LONG type).
-      
+
     - Fixed BUG#11874 - ResultSet.getShort() for UNSIGNED TINYINT
-      returned wrong values. 
-      
-    - Fixed BUG#15676 - lib-nodist directory missing from  
-      package breaks out-of-box build   
-      
-    - Fixed BUG#15854 - DBMD.getColumns() returns wrong type for BIT.  
-    
+      returned wrong values.
+
+    - Fixed BUG#15676 - lib-nodist directory missing from
+      package breaks out-of-box build
+
+    - Fixed BUG#15854 - DBMD.getColumns() returns wrong type for BIT.
+
     - Fixed BUG#16169 - ResultSet.getNativeShort() causes stack overflow error
       via recurisve calls.
-      
-    - Fixed BUG#14938 - Unable to initialize character set mapping tables. 
+
+    - Fixed BUG#14938 - Unable to initialize character set mapping tables.
       Removed reliance on .properties files to hold this information, as it
       turns out to be too problematic to code around class loader hierarchies
       that change depending on how an application is deployed. Moved information
-      back into the CharsetMapping class.      
-      
-    - Fixed BUG#16841 - updatable result set doesn't return AUTO_INCREMENT 
+      back into the CharsetMapping class.
+
+    - Fixed BUG#16841 - updatable result set doesn't return AUTO_INCREMENT
       values for insertRow() when multiple column primary keys are used. (the
       driver was checking for the existence of single-column primary keys and
-      an autoincrement value > 0 instead of a straightforward isAutoIncrement() 
+      an autoincrement value > 0 instead of a straightforward isAutoIncrement()
       check).
-      
-    - Fixed BUG#17099 - Statement.getGeneratedKeys() throws NullPointerException 
+
+    - Fixed BUG#17099 - Statement.getGeneratedKeys() throws NullPointerException
       when no query has been processed.
-      
+
     - Fixed BUG#13469 - Driver tries to call methods that don't exist on older and
-      newer versions of Log4j. The fix is not trying to auto-detect presense of log4j, 
+      newer versions of Log4j. The fix is not trying to auto-detect presense of log4j,
       too many different incompatible versions out there in the wild to do this reliably.
-      
-      If you relied on autodetection before, you will need to add 
+
+      If you relied on autodetection before, you will need to add
       "logger=com.mysql.jdbc.log.Log4JLogger" to your JDBC URL to enable Log4J usage,
       or alternatively use the new "CommonsLogger" class to take care of this.
-      
+
     - Added support for Apache Commons logging, use "com.mysql.jdbc.log.CommonsLogger"
       as the value for the "logger" configuration property.
-      
+
     - LogFactory now prepends "com.mysql.jdbc.log" to log class name if it can't be
       found as-specified. This allows you to use "short names" for the built-in log
-      factories, for example "logger=CommonsLogger" instead of 
+      factories, for example "logger=CommonsLogger" instead of
       "logger=com.mysql.jdbc.log.CommonsLogger".
-      
+
     - Fixed BUG#15570 - ReplicationConnection incorrectly copies state,
-	  doesn't transfer connection context correctly when transitioning between 
+	  doesn't transfer connection context correctly when transitioning between
 	  the same read-only states.
-	  	  
-	- Fixed BUG#18041 - Server-side prepared statements don't cause 
+
+	- Fixed BUG#18041 - Server-side prepared statements don't cause
 	  truncation exceptions to be thrown when truncation happens.
-	  
+
 	- Added performance feature, re-writing of batched executes for
-	  Statement.executeBatch() (for all DML statements) and 
-	  PreparedStatement.executeBatch() (for INSERTs with VALUE clauses 
+	  Statement.executeBatch() (for all DML statements) and
+	  PreparedStatement.executeBatch() (for INSERTs with VALUE clauses
 	  only). Enable by using "rewriteBatchedStatements=true" in your JDBC URL.
-	  
-	- Fixed BUG#17898 - registerOutParameter not working when some 
+
+	- Fixed BUG#17898 - registerOutParameter not working when some
 	  parameters pre-populated. Still waiting for feedback from JDBC experts
-	  group to determine what correct parameter count from getMetaData() 
+	  group to determine what correct parameter count from getMetaData()
 	  should be, however.
 
 	- Fixed BUG#17587 - clearParameters() on a closed prepared statement
 	  causes NPE.
-    	  
+
 	- Map "latin1" on MySQL server to CP1252 for MySQL > 4.1.0.
-	
+
 	- Added additional accessor and mutator methods on ConnectionProperties
 	  so that DataSource users can use same naming as regular URL properties.
-	  
-	- Fixed BUG#18740 - Data truncation and getWarnings() only returns last 
+
+	- Fixed BUG#18740 - Data truncation and getWarnings() only returns last
 	  warning in set.
-	  
+
 	- Improved performance of retrieving BigDecimal, Time, Timestamp and Date
 	  values from server-side prepared statements by creating fewer short-lived
 	  instances of Strings when the native type is not an exact match for
 	  the requested type. Fixes BUG#18496 for BigDecimals.
-	  
-	- Fixed BUG#18554 - Aliased column names where length of name > 251 
+
+	- Fixed BUG#18554 - Aliased column names where length of name > 251
 	  are corrupted.
-	  	  
+
 	- Fixed BUG#17450 - ResultSet.wasNull() not always reset
 	  correctly for booleans when done via conversion for server-side
 	  prepared statements.
-	
-	- Fixed BUG#16277 - Invalid classname returned for 
+
+	- Fixed BUG#16277 - Invalid classname returned for
 	  RSMD.getColumnClassName() for BIGINT type.
-	  
-	- Fixed case where driver wasn't reading server status correctly when 
-	  fetching server-side prepared statement rows, which in some cases 
-	  could cause warning counts to be off, or multiple result sets to not 
+
+	- Fixed case where driver wasn't reading server status correctly when
+	  fetching server-side prepared statement rows, which in some cases
+	  could cause warning counts to be off, or multiple result sets to not
 	  be read off the wire.
-	  
-	- Driver now aware of fix for BIT type metadata that went into 
-	  MySQL-5.0.21 for server not reporting length consistently (bug 
+
+	- Driver now aware of fix for BIT type metadata that went into
+	  MySQL-5.0.21 for server not reporting length consistently (bug
 	  number 13601).
-	  
-	- Fixed BUG#19282 - ResultSet.wasNull() returns incorrect value 
-	  when extracting native string from server-side prepared statement 
+
+	- Fixed BUG#19282 - ResultSet.wasNull() returns incorrect value
+	  when extracting native string from server-side prepared statement
 	  generated result set.
-	  
+
 11-30-05 - Version 3.1.12
 
     - Fixed client-side prepared statement bug with embedded ? inside
       quoted identifiers (it was recognized as a placeholder, when it
       was not).
-      
+
     - Don't allow executeBatch() for CallableStatements with registered
       OUT/INOUT parameters (JDBC compliance).
-      
+
     - Fall back to platform-encoding for URLDecoder.decode() when
-      parsing driver URL properties if the platform doesn't have a 
+      parsing driver URL properties if the platform doesn't have a
       two-argument version of this method.
-      
-    - Fixed BUG#14562 - Java type conversion may be incorrect for 
+
+    - Fixed BUG#14562 - Java type conversion may be incorrect for
       mediumint.
-      
+
     - Added configuration property "useGmtMillisForDatetimes" which
       when set to true causes ResultSet.getDate(), .getTimestamp() to
-      return correct millis-since GMT when .getTime() is called on 
+      return correct millis-since GMT when .getTime() is called on
       the return value (currently default is "false" for legacy
       behavior).
 
     - Fixed DatabaseMetaData.stores*Identifiers():
-    
+
         * if lower_case_table_names=0 (on server):
-        
+
             storesLowerCaseIdentifiers() returns false
             storesLowerCaseQuotedIdentifiers() returns false
             storesMixedCaseIdentifiers() returns true
             storesMixedCaseQuotedIdentifiers() returns true
             storesUpperCaseIdentifiers() returns false
-            storesUpperCaseQuotedIdentifiers() returns true 
-                
+            storesUpperCaseQuotedIdentifiers() returns true
+
         * if lower_case_table_names=1 (on server):
-        
+
             storesLowerCaseIdentifiers() returns true
             storesLowerCaseQuotedIdentifiers() returns true
             storesMixedCaseIdentifiers() returns false
             storesMixedCaseQuotedIdentifiers() returns false
             storesUpperCaseIdentifiers() returns false
-            storesUpperCaseQuotedIdentifiers() returns true 
- 
+            storesUpperCaseQuotedIdentifiers() returns true
+
     - Fixed BUG#14815 - DatabaseMetaData.getColumns() doesn't
       return TABLE_NAME correctly.
-      
-    - Fixed BUG#14909 - escape processor replaces quote character 
+
+    - Fixed BUG#14909 - escape processor replaces quote character
       in quoted string with string delimiter.
-      
-    - Fixed BUG#12975 - OpenOffice expects 
-      DBMD.supportsIntegrityEnhancementFacility() to return "true" 
-      if foreign keys are supported by the datasource, even though 
+
+    - Fixed BUG#12975 - OpenOffice expects
+      DBMD.supportsIntegrityEnhancementFacility() to return "true"
+      if foreign keys are supported by the datasource, even though
       this method also covers support for check constraints,
 	  which MySQL _doesn't_ have. Setting the configuration property
 	  "overrideSupportsIntegrityEnhancementFacility" to "true" causes
 	  the driver to return "true" for this method.
-	  
+
     - Added "com.mysql.jdbc.testsuite.url.default" system property to
 	  set default JDBC url for testsuite (to speed up bug resolution
 	  when I'm working in Eclipse).
-	
-	- Fixed BUG#14938 - Unable to initialize character set mapping 
+
+	- Fixed BUG#14938 - Unable to initialize character set mapping
 	  tables (due to J2EE classloader differences).
-	  
+
 	- Fixed BUG#14972 - Deadlock while closing server-side prepared
 	  statements from multiple threads sharing one connection.
-	  
+
 	- Fixed BUG#12230 -	logSlowQueries should give better info.
-	
+
 	- Fixed BUG#13775 - Extraneous sleep on autoReconnect.
-	
-	- Fixed BUG#15024 - Driver incorrectly closes streams passed as 
+
+	- Fixed BUG#15024 - Driver incorrectly closes streams passed as
 	  arguments to PreparedStatements. Reverts to legacy behavior by
 	  setting the JDBC configuration property "autoClosePStmtStreams"
 	  to "true" (also included in the 3-0-Compat configuration "bundle").
-	  
+
 	- Fixed BUG#13048 - maxQuerySizeToLog is not respected. Added logging of
 	  bound values for execute() phase of server-side prepared statements
 	  when profileSQL=true as well.
-	  
-	- Fixed BUG#15065 - Usage advisor complains about unreferenced 
+
+	- Fixed BUG#15065 - Usage advisor complains about unreferenced
 	  columns, even though they've been referenced.
-	  
+
 	- Don't increase timeout for failover/reconnect (BUG#6577)
-	
+
 	- Process escape tokens in Connection.prepareStatement(...), fix
-	  for BUG#15141. You can disable this behavior by setting 
+	  for BUG#15141. You can disable this behavior by setting
 	  the JDBC URL configuration property "processEscapeCodesForPrepStmts"
 	  to "false".
-	  
-	- Fixed BUG#13255 - Reconnect during middle of executeBatch() 
+
+	- Fixed BUG#13255 - Reconnect during middle of executeBatch()
 	  should not occur if autoReconnect is enabled.
-      
+
 10-07-05 - Version 3.1.11
 
     - Fixed BUG#11629 - Spurious "!" on console when character
       encoding is "utf8".
-      
+
     - Fixed statements generated for testcases missing ";" for
       "plain" statements.
-      
-    - Fixed BUG#11663 - Incorrect generation of testcase scripts 
+
+    - Fixed BUG#11663 - Incorrect generation of testcase scripts
       for server-side prepared statements.
-      
+
     - Fixed regression caused by fix for BUG#11552 that caused driver
-      to return incorrect values for unsigned integers when those 
+      to return incorrect values for unsigned integers when those
       integers where within the range of the positive signed type.
-    
+
     - Moved source code to svn repo.
-    
+
     - Fixed BUG#11797 - Escape tokenizer doesn't respect stacked single quotes
 	  for escapes.
-	  
+
 	- GEOMETRY type not recognized when using server-side prepared statements.
-    
-    - Fixed BUG#11879 -- ReplicationConnection won't switch to slave, throws 
+
+    - Fixed BUG#11879 -- ReplicationConnection won't switch to slave, throws
       "Catalog can't be null" exception.
-      
-    - Fixed BUG#12218, properties shared between master and slave with 
+
+    - Fixed BUG#12218, properties shared between master and slave with
       replication connection.
-      
-    - Fixed BUG#10630, Statement.getWarnings() fails with NPE if statement 
+
+    - Fixed BUG#10630, Statement.getWarnings() fails with NPE if statement
       has been closed.
-      
+
     - Only get char[] from SQL in PreparedStatement.ParseInfo() when needed.
-    
-    - Fixed BUG#12104 - Geometry types not handled with server-side prepared 
+
+    - Fixed BUG#12104 - Geometry types not handled with server-side prepared
       statements.
-      
-    - Fixed BUG#11614 - StringUtils.getBytes() doesn't work when using 
-      multibyte character encodings and a length in  _characters_ is 
+
+    - Fixed BUG#11614 - StringUtils.getBytes() doesn't work when using
+      multibyte character encodings and a length in  _characters_ is
       specified.
-      
+
     - Fixed BUG#11798 - Pstmt.setObject(...., Types.BOOLEAN) throws exception.
-    
-    - Fixed BUG#11976 - maxPerformance.properties mis-spells 
+
+    - Fixed BUG#11976 - maxPerformance.properties mis-spells
 	  "elideSetAutoCommits".
-	  
+
 	- Fixed BUG#11575 -- DBMD.storesLower/Mixed/UpperIdentifiers()
 	  reports incorrect values for servers deployed on Windows.
-	  
-	- Fixed BUG#11190 - ResultSet.moveToCurrentRow() fails to work when 
+
+	- Fixed BUG#11190 - ResultSet.moveToCurrentRow() fails to work when
 	  preceeded by a call to ResultSet.moveToInsertRow().
-	  
+
 	- Fixed BUG#11115, VARBINARY data corrupted when using server-side
 	  prepared statements and .setBytes().
-	
+
 	- Fixed BUG#12229 - explainSlowQueries hangs with server-side
 	  prepared statements.
-	  
+
 	- Fixed BUG#11498 - Escape processor didn't honor strings demarcated
 	  with double quotes.
-	  
+
 	- Lifted restriction of changing streaming parameters with server-side
 	  prepared statements. As long as _all_ streaming parameters were set
-	  before execution, .clearParameters() does not have to be called. 
+	  before execution, .clearParameters() does not have to be called.
 	  (due to limitation of client/server protocol, prepared statements
 	   can not reset _individual_ stream data on the server side).
-	   
+
 	- Reworked Field class, *Buffer, and MysqlIO to be aware of field
 	  lengths > Integer.MAX_VALUE.
-	  
-	- Updated DBMD.supportsCorrelatedQueries() to return true for versions > 
-	  4.1, supportsGroupByUnrelated() to return true and 
+
+	- Updated DBMD.supportsCorrelatedQueries() to return true for versions >
+	  4.1, supportsGroupByUnrelated() to return true and
 	  getResultSetHoldability() to return HOLD_CURSORS_OVER_COMMIT.
-	  
-	- Fixed BUG#12541 - Handling of catalog argument in 
+
+	- Fixed BUG#12541 - Handling of catalog argument in
 	  DatabaseMetaData.getIndexInfo(), which also means changes to the following
 	  methods in DatabaseMetaData:
-	  
+
 	    - getBestRowIdentifier()
 	    - getColumns()
 	    - getCrossReference()
@@ -598,349 +1350,349 @@
 	    - getPrimaryKeys()
 	    - getProcedures() (and thus indirectly getProcedureColumns())
 	    - getTables()
-	  
+
 	  The "catalog" argument in all of these methods now behaves in the following
 	  way:
-	  
+
 	    - Specifying NULL means that catalog will not be used to filter the
 	      results (thus all databases will be searched), unless you've
 	      set "nullCatalogMeansCurrent=true" in your JDBC URL properties.
-	      
+
 	    - Specifying "" means "current" catalog, even though this isn't quite
 	      JDBC spec compliant, it's there for legacy users.
-	      
+
 	    - Specifying a catalog works as stated in the API docs.
-	    
+
 	- Made Connection.clientPrepare() available from "wrapped" connections
-	  in the jdbc2.optional package (connections built by 
+	  in the jdbc2.optional package (connections built by
 	  ConnectionPoolDataSource instances).
-      
+
     - Added Connection.isMasterConnection() for clients to be able to determine
       if a multi-host master/slave connection is connected to the first host
       in the list.
-      
+
     - Fixed BUG#12753 - Tokenizer for "=" in URL properties was causing
       sessionVariables=.... to be parameterized incorrectly.
 
-    - Fixed BUG#11781, foreign key information that is quoted is 
+    - Fixed BUG#11781, foreign key information that is quoted is
       parsed incorrectly when DatabaseMetaData methods use that
       information.
-      
+
     - The "sendBlobChunkSize" property is now clamped to "max_allowed_packet"
       with consideration of stream buffer size and packet headers to avoid
       PacketTooBigExceptions when "max_allowed_packet" is similar in size
       to the default "sendBlobChunkSize" which is 1M.
-      
+
     - CallableStatement.clearParameters() now clears resources associated
       with INOUT/OUTPUT parameters as well as INPUT parameters.
-      
-    - Fixed BUG#12417 - Connection.prepareCall() is database name 
+
+    - Fixed BUG#12417 - Connection.prepareCall() is database name
       case-sensitive (on Windows systems).
-      
-    - Fixed BUG#12752 - Cp1251 incorrectly mapped to win1251 for 
+
+    - Fixed BUG#12752 - Cp1251 incorrectly mapped to win1251 for
       servers newer than 4.0.x.
-      
-    - Fixed BUG#12970 - java.sql.Types.OTHER returned for 
-	  BINARY and VARBINARY columns when using 
-	  DatabaseMetaData.getColumns(). 
-	  
+
+    - Fixed BUG#12970 - java.sql.Types.OTHER returned for
+	  BINARY and VARBINARY columns when using
+	  DatabaseMetaData.getColumns().
+
 	- ServerPreparedStatement.getBinding() now checks if the statement
 	  is closed before attempting to reference the list of parameter
 	  bindings, to avoid throwing a NullPointerException.
-    
-    - Fixed BUG#13277 - ResultSetMetaData from 
+
+    - Fixed BUG#13277 - ResultSetMetaData from
       Statement.getGeneratedKeys() caused NullPointerExceptions to be
       thrown whenever a method that required a connection reference
       was called.
-      
+
     - Removed support for java.nio I/O. Too many implementations
       turned out to be buggy, and there was no performance difference
       since MySQL is a blocking protocol anyway.
-      
+
 06-23-05 - Version 3.1.10-stable
 
 	- Fixed connecting without a database specified raised an exception
 	  in MysqlIO.changeDatabaseTo().
-	  
-	- Initial implemention of ParameterMetadata for 
+
+	- Initial implemention of ParameterMetadata for
 	  PreparedStatement.getParameterMetadata(). Only works fully
 	  for CallableStatements, as current server-side prepared statements
 	  return every parameter as a VARCHAR type.
-	  
+
 	- Fixed BUG#11552 - Server-side prepared statements return incorrect
 	  values for unsigned TINYINT, SMALLINT, INT and Long.
-	  
-	- Fixed BUG#11540 - Incorrect year conversion in setDate(..) for 
+
+	- Fixed BUG#11540 - Incorrect year conversion in setDate(..) for
 	  system that use B.E. year in default locale.
-	  
+
 06-22-05 - Version 3.1.9-stable
-	
+
 	- Overhaul of character set configuration, everything now
 	  lives in a properties file.
-	  
+
 	- Driver now correctly uses CP932 if available on the server
-	  for Windows-31J, CP932 and MS932 java encoding names, 
-	  otherwise it resorts to SJIS, which is only a close 
+	  for Windows-31J, CP932 and MS932 java encoding names,
+	  otherwise it resorts to SJIS, which is only a close
 	  approximation. Currently only MySQL-5.0.3 and newer (and
 	  MySQL-4.1.12 or .13, depending on when the character set
 	  gets backported) can reliably support any variant of CP932.
-	  
-	- Fixed BUG#9064 - com.mysql.jdbc.PreparedStatement.ParseInfo 
+
+	- Fixed BUG#9064 - com.mysql.jdbc.PreparedStatement.ParseInfo
 	  does unnecessary call to toCharArray().
-	  
-	- Fixed Bug#10144 - Memory leak in ServerPreparedStatement if 
+
+	- Fixed Bug#10144 - Memory leak in ServerPreparedStatement if
 	  serverPrepare() fails.
-	  
+
 	- Actually write manifest file to correct place so it ends up
 	  in the binary jar file.
-	  
+
 	- Added "createDatabaseIfNotExist" property (default is "false"),
-	  which will cause the driver to ask the server to create the 
+	  which will cause the driver to ask the server to create the
 	  database specified in the URL if it doesn't exist. You must have
 	  the appropriate privileges for database creation for this to
 	  work.
-	  
+
 	- Fixed BUG#10156 - Unsigned SMALLINT treated as signed for ResultSet.getInt(),
 	  fixed all cases for UNSIGNED integer values and server-side prepared statements,
 	  as well as ResultSet.getObject() for UNSIGNED TINYINT.
-	  
-	- Fixed BUG#10155, double quotes not recognized when parsing 
+
+	- Fixed BUG#10155, double quotes not recognized when parsing
 	  client-side prepared statements.
-	  
-	- Made enableStreamingResults() visible on 
+
+	- Made enableStreamingResults() visible on
 	  com.mysql.jdbc.jdbc2.optional.StatementWrapper.
-	  
+
 	- Made ServerPreparedStatement.asSql() work correctly so auto-explain
 	  functionality would work with server-side prepared statements.
-	  
+
 	- Made JDBC2-compliant wrappers public in order to allow access to
 	  vendor extensions.
-	  
+
 	- Cleaned up logging of profiler events, moved code to dump a profiler
 	  event as a string to com.mysql.jdbc.log.LogUtils so that third
 	  parties can use it.
-	  
+
 	- DatabaseMetaData.supportsMultipleOpenResults() now returns true. The
 	  driver has supported this for some time, DBMD just missed that fact.
-	  
+
 	- Fixed BUG#10310 - Driver doesn't support {?=CALL(...)} for calling
 	  stored functions. This involved adding support for function retrieval
 	  to DatabaseMetaData.getProcedures() and getProcedureColumns() as well.
-	  
-	- Fixed BUG#10485, SQLException thrown when retrieving YEAR(2) 
+
+	- Fixed BUG#10485, SQLException thrown when retrieving YEAR(2)
 	  with ResultSet.getString(). The driver will now always treat YEAR types
-	  as java.sql.Dates and return the correct values for getString(). 
+	  as java.sql.Dates and return the correct values for getString().
 	  Alternatively, the "yearIsDateType" connection property can be set to
 	  "false" and the values will be treated as SHORTs.
-	  
-	- The datatype returned for TINYINT(1) columns when "tinyInt1isBit=true" 
+
+	- The datatype returned for TINYINT(1) columns when "tinyInt1isBit=true"
 	  (the default) can be switched between Types.BOOLEAN and Types.BIT
 	  using the new configuration property "transformedBitIsBoolean", which
-	  defaults to "false". If set to "false" (the default), 
-	  DatabaseMetaData.getColumns() and ResultSetMetaData.getColumnType() 
-	  will return Types.BOOLEAN for TINYINT(1) columns. If "true", 
+	  defaults to "false". If set to "false" (the default),
+	  DatabaseMetaData.getColumns() and ResultSetMetaData.getColumnType()
+	  will return Types.BOOLEAN for TINYINT(1) columns. If "true",
 	  Types.BOOLEAN will be returned instead. Irregardless of this configuration
 	  property, if "tinyInt1isBit" is enabled, columns with the type TINYINT(1)
-	  will be returned as java.lang.Boolean instances from 
+	  will be returned as java.lang.Boolean instances from
 	  ResultSet.getObject(..), and ResultSetMetaData.getColumnClassName()
 	  will return "java.lang.Boolean".
-	 
-	- Fixed BUG#10496 - SQLException is thrown when using property 
+
+	- Fixed BUG#10496 - SQLException is thrown when using property
 	  "characterSetResults" with cp932 or eucjpms.
-	  
+
 	- Reorganized directory layout, sources now in "src" folder,
 	  don't pollute parent directory when building, now output goes
 	  to "./build", distribution goes to "./dist".
-	  
+
 	- Added support/bug hunting feature that generates .sql test
 	  scripts to STDERR when "autoGenerateTestcaseScript" is set
 	  to "true".
-	  
+
 	- Fixed BUG#10850 - 0-length streams not sent to server when
 	  using server-side prepared statements.
-	
-	- Setting "cachePrepStmts=true" now causes the Connection to also 
-	  cache the check the driver performs to determine if a prepared 
+
+	- Setting "cachePrepStmts=true" now causes the Connection to also
+	  cache the check the driver performs to determine if a prepared
 	  statement can be server-side or not, as well as caches server-side
 	  prepared statements for the lifetime of a connection. As before,
 	  the "prepStmtCacheSize" parameter controls the size of these
 	  caches.
-	  
+
 	- Try to handle OutOfMemoryErrors more gracefully. Although not
 	  much can be done, they will in most cases close the connection
-	  they happened on so that further operations don't run into 
-	  a connection in some unknown state. When an OOM has happened, 
-	  any further operations on the connection will fail with a 
+	  they happened on so that further operations don't run into
+	  a connection in some unknown state. When an OOM has happened,
+	  any further operations on the connection will fail with a
 	  "Connection closed" exception that will also list the OOM exception
 	  as the reason for the implicit connection close event.
-	  
+
 	- Don't send COM_RESET_STMT for each execution of a server-side
 	  prepared statement if it isn't required.
-	  
+
 	- Driver detects if you're running MySQL-5.0.7 or later, and does
 	  not scan for "LIMIT ?[,?]" in statements being prepared, as the
 	  server supports those types of queries now.
-	  
+
 	- Fixed BUG#11115, Varbinary data corrupted when using server-side
 	  prepared statements and ResultSet.getBytes().
-	  
+
 	- Connection.setCatalog() is now aware of the "useLocalSessionState"
 	  configuration property, which when set to true will prevent
 	  the driver from sending "USE ..." to the server if the requested
 	  catalog is the same as the current catalog.
-	  
+
 	- Added the following configuration bundles, use one or many via
 	  the "useConfigs" configuration property:
-	
+
 	    * maxPerformance -- maximum performance without being reckless
 	    * solarisMaxPerformance -- maximum performance for Solaris,
 	                               avoids syscalls where it can
 	    * 3-0-Compat -- Compatibility with Connector/J 3.0.x functionality
-	    
+
 	- Added "maintainTimeStats" configuration property (defaults to "true"),
 	  which tells the driver whether or not to keep track of the last query time
 	  and the last successful packet sent to the server's time. If set to
 	  false, removes two syscalls per query.
-	
-	- Fixed BUG#11259, autoReconnect ping causes exception on connection 
+
+	- Fixed BUG#11259, autoReconnect ping causes exception on connection
 	  startup.
-	  
+
 	- Fixed BUG#11360 Connector/J dumping query into SQLException twice
-	
+
 	- Fixed PreparedStatement.setClob() not accepting null as a parameter.
-	
-	- Fixed BUG#11411 - Production package doesn't include JBoss integration 
+
+	- Fixed BUG#11411 - Production package doesn't include JBoss integration
 	  classes.
-	  
-	- Removed nonsensical "costly type conversion" warnings when using 
+
+	- Removed nonsensical "costly type conversion" warnings when using
 	  usage advisor.
 
 04-14-05 - Version 3.1.8-stable
 
-	- Fixed DatabaseMetaData.getTables() returning views when they were 
+	- Fixed DatabaseMetaData.getTables() returning views when they were
 	  not asked for as one of the requested table types.
-	  
+
 	- Added support for new precision-math DECIMAL type in MySQL >= 5.0.3.
-	
+
 	- Fixed ResultSet.getTime() on a NULL value for server-side prepared
 	  statements throws NPE.
-	  
+
 	- Made Connection.ping() a public method.
-	
+
 	- Fixed Bug#8868, DATE_FORMAT() queries returned as BLOBs from getObject().
-	
+
 	- ServerPreparedStatements now correctly 'stream' BLOB/CLOB data to the
 	  server. You can configure the threshold chunk size using the
 	  JDBC URL property 'blobSendChunkSize' (the default is one megabyte).
-	  
+
     - BlobFromLocator now uses correct identifier quoting when generating
       prepared statements.
-      
+
     - Server-side session variables can be preset at connection time by
-      passing them as a comma-delimited list for the connection property 
+      passing them as a comma-delimited list for the connection property
       'sessionVariables'.
 
 	- Fixed regression in ping() for users using autoReconnect=true.
-	
-	- Fixed BUG#9040 - PreparedStatement.addBatch() doesn't work with server-side 
+
+	- Fixed BUG#9040 - PreparedStatement.addBatch() doesn't work with server-side
 	  prepared statements and streaming BINARY data.
-	  
+
 	- Fixed BUG#8800 - DBMD.supportsMixedCase*Identifiers() returns wrong
 	  value on servers running on case-sensitive filesystems.
-	
+
 	- Fixed BUG#9206, can not use 'UTF-8' for characterSetResults
       configuration property.
-      
-    - Fixed BUG#9236, a continuation of BUG#8868, where functions used in queries 
-      that should return non-string types when resolved by temporary tables suddenly 
+
+    - Fixed BUG#9236, a continuation of BUG#8868, where functions used in queries
+      that should return non-string types when resolved by temporary tables suddenly
       become opaque binary strings (work-around for server limitation). Also fixed
       fields with type of CHAR(n) CHARACTER SET BINARY to return correct/matching
       classes for RSMD.getColumnClassName() and ResultSet.getObject().
-    
+
     - Fixed BUG#8792 - DBMD.supportsResultSetConcurrency() not returning
 	  true for forward-only/read-only result sets (we obviously support this).
-	  
+
 	- Fixed BUG#8803, 'DATA_TYPE' column from DBMD.getBestRowIdentifier()
 	  causes ArrayIndexOutOfBoundsException when accessed (and in fact, didn't
 	  return any value).
-	  
+
 	- Check for empty strings ('') when converting char/varchar column data to numbers,
 	  throw exception if 'emptyStringsConvertToZero' configuration property is set
-	  to 'false' (for backwards-compatibility with 3.0, it is now set to 'true' 
+	  to 'false' (for backwards-compatibility with 3.0, it is now set to 'true'
 	  by default, but will most likely default to 'false' in 3.2).
-	  
-	- Fixed BUG#9320 - PreparedStatement.getMetaData() inserts blank row in database 
+
+	- Fixed BUG#9320 - PreparedStatement.getMetaData() inserts blank row in database
 	  under certain conditions when not using server-side prepared statements.
-	  
+
 	- Connection.canHandleAsPreparedStatement() now makes 'best effort' to distinguish
 	  LIMIT clauses with placeholders in them from ones without in order to have fewer
-	  false positives when generating work-arounds for statements the server cannot 
+	  false positives when generating work-arounds for statements the server cannot
 	  currently handle as server-side prepared statements.
-	  
+
 	- Fixed build.xml to not compile log4j logging if log4j not available.
-	
-	- Added support for the c3p0 connection pool's (http://c3p0.sf.net/) 
-	  validation/connection checker interface which uses the lightweight 
+
+	- Added support for the c3p0 connection pool's (http://c3p0.sf.net/)
+	  validation/connection checker interface which uses the lightweight
 	  'COM_PING' call to the server if available. To use it, configure your
 	  c3p0 connection pool's 'connectionTesterClassName' property to use
 	  'com.mysql.jdbc.integration.c3p0.MysqlConnectionTester'.
-	  
+
 	- Better detection of LIMIT inside/outside of quoted strings so that
 	  the driver can more correctly determine whether a prepared statement
 	  can be prepared on the server or not.
-	
-	- Fixed BUG#9319 - Stored procedures with same name in 
+
+	- Fixed BUG#9319 - Stored procedures with same name in
 	  different databases confuse the driver when it tries to determine
 	  parameter counts/types.
-  
+
     - Added finalizers to ResultSet and Statement implementations to be JDBC
-      spec-compliant, which requires that if not explicitly closed, these 
+      spec-compliant, which requires that if not explicitly closed, these
       resources should be closed upon garbage collection.
-      
+
     - Fixed BUG#9682 - Stored procedures with DECIMAL parameters with
 	  storage specifications that contained "," in them would fail.
-	  
-	- PreparedStatement.setObject(int, Object, int type, int scale) now 
+
+	- PreparedStatement.setObject(int, Object, int type, int scale) now
 	  uses scale value for BigDecimal instances.
-	  
+
 	- Fixed BUG#9704 - Statement.getMoreResults() could throw NPE when
 	  existing result set was .close()d.
-	  
+
 	- The performance metrics feature now gathers information about
 	  number of tables referenced in a SELECT.
-	
-	- The logging system is now automatically configured. If the value has 
-	  been set by the user, via the URL property "logger" or the system 
-	  property "com.mysql.jdbc.logger", then use that, otherwise, autodetect 
+
+	- The logging system is now automatically configured. If the value has
+	  been set by the user, via the URL property "logger" or the system
+	  property "com.mysql.jdbc.logger", then use that, otherwise, autodetect
 	  it using the following steps:
-	  
+
     	 Log4j, if it's available,
     	 Then JDK1.4 logging,
     	 Then fallback to our STDERR logging.
-    	 
+
    	- Fixed BUG#9778, DBMD.getTables() shouldn't return tables if views
 	  are asked for, even if the database version doesn't support views.
-	  
+
 	- Fixed driver not returning 'true' for '-1' when ResultSet.getBoolean()
 	  was called on result sets returned from server-side prepared statements.
-	  
+
 	- Added a Manifest.MF file with implementation information to the .jar
 	  file.
-	  
-	- More tests in Field.isOpaqueBinary() to distinguish opaque binary (i.e. 
-	  fields with type CHAR(n) and CHARACTER SET BINARY) from output of 
+
+	- More tests in Field.isOpaqueBinary() to distinguish opaque binary (i.e.
+	  fields with type CHAR(n) and CHARACTER SET BINARY) from output of
 	  various scalar and aggregate functions that return strings.
-	  
-	- Fixed BUG#9917 - Should accept null for catalog (meaning use current) 
+
+	- Fixed BUG#9917 - Should accept null for catalog (meaning use current)
 	  in DBMD methods, even though it's not JDBC-compliant for legacy's sake.
 	  Disable by setting connection property "nullCatalogMeansCurrent" to "false"
 	  (which will be the default value in C/J 3.2.x).
-	  
-	- Fixed BUG#9769 - Should accept null for name patterns in DBMD (meaning "%"), 
+
+	- Fixed BUG#9769 - Should accept null for name patterns in DBMD (meaning "%"),
 	  even though it isn't JDBC compliant, for legacy's sake. Disable by setting
-	  connection property "nullNamePatternMatchesAll" to "false" (which will be 
+	  connection property "nullNamePatternMatchesAll" to "false" (which will be
 	  the default value in C/J 3.2.x).
-	  	 
+
 02-18-05 - Version 3.1.7-stable
 
 
@@ -950,7 +1702,7 @@
     - Fixed BUG#7715 - Timestamps converted incorrectly to strings
       with Server-side prepared statements and updatable result sets.
 
-    - Detect new sql_mode variable in string form (it used to be 
+    - Detect new sql_mode variable in string form (it used to be
       integer) and adjust quoting method for strings appropriately.
 
     - Added 'holdResultsOpenOverStatementClose' property (default is
@@ -1034,8 +1786,8 @@
       round-trips to the database server.
 
     - Added enableStreamingResults() to Statement for connection pool
-      implementations that check Statement.setFetchSize() for 
-      specification-compliant values. Call Statement.setFetchSize(>=0) 
+      implementations that check Statement.setFetchSize() for
+      specification-compliant values. Call Statement.setFetchSize(>=0)
       to disable the streaming results for that statement.
 
     - Added support for BIT type in MySQL-5.0.3. The driver will treat
@@ -1044,69 +1796,69 @@
       information to determine the size of a bitfield when < 9 bits are
       declared. BIT(>9) will be treated as VARBINARY, and will return
       byte[] when getObject() is called.
-      
+
 12-23-04 - Version 3.1.6-stable
 
-    - Fixed hang on SocketInputStream.read() with Statement.setMaxRows() and 
-      multiple result sets when driver has to truncate result set directly, 
+    - Fixed hang on SocketInputStream.read() with Statement.setMaxRows() and
+      multiple result sets when driver has to truncate result set directly,
       rather than tacking a 'LIMIT n' on the end of it.
-      
+
     - Fixed BUG#7026 - DBMD.getProcedures() doesn't respect catalog parameter.
-    
+
     - Respect bytes-per-character for RSMD.getPrecision().
-      
+
 12-02-04 - Version 3.1.5-gamma
 
 	- Fix comparisons made between string constants and dynamic strings that
-	  are either toUpperCase()d or toLowerCase()d to use Locale.ENGLISH, as 
-	  some locales 'override' case rules for English. Also use 
-	  StringUtils.indexOfIgnoreCase() instead of .toUpperCase().indexOf(), 
+	  are either toUpperCase()d or toLowerCase()d to use Locale.ENGLISH, as
+	  some locales 'override' case rules for English. Also use
+	  StringUtils.indexOfIgnoreCase() instead of .toUpperCase().indexOf(),
 	  avoids creating a very short-lived transient String instance.
-	
+
 	- Fixed BUG#5235 - Server-side prepared statements did not honor
       'zeroDateTimeBehavior' property, and would cause class-cast
       exceptions when using ResultSet.getObject(), as the all-zero string
       was always returned.
-      
+
     - Fixed batched updates with server prepared statements weren't looking if
       the types had changed for a given batched set of parameters compared
-      to the previous set, causing the server to return the error 
+      to the previous set, causing the server to return the error
       'Wrong arguments to mysql_stmt_execute()'.
-      
+
     - Handle case when string representation of timestamp contains trailing '.'
       with no numbers following it.
-      
+
     - Fixed BUG#5706 - Inefficient detection of pre-existing string instances
       in ResultSet.getNativeString().
-      
+
     - Don't throw exceptions for Connection.releaseSavepoint().
-    
+
     - Use a per-session Calendar instance by default when decoding dates
-      from ServerPreparedStatements (set to old, less performant behavior by 
+      from ServerPreparedStatements (set to old, less performant behavior by
       setting property 'dynamicCalendars=true').
-      
-    - Added experimental configuration property 'dontUnpackBinaryResults', 
-      which delays unpacking binary result set values until they're asked for, 
-      and only creates object instances for non-numerical values (it is set 
-      to 'false' by default). For some usecase/jvm combinations, this is 
+
+    - Added experimental configuration property 'dontUnpackBinaryResults',
+      which delays unpacking binary result set values until they're asked for,
+      and only creates object instances for non-numerical values (it is set
+      to 'false' by default). For some usecase/jvm combinations, this is
       friendlier on the garbage collector.
-      
+
     - Fixed BUG#5729 - UNSIGNED BIGINT unpacked incorrectly from
       server-side prepared statement result sets.
-      
+
     - Fixed BUG#6225 - ServerSidePreparedStatement allocating short-lived
       objects un-necessarily.
-      
+
     - Removed un-wanted new Throwable() in ResultSet constructor due to bad
       merge (caused a new object instance that was never used for every result
       set created) - Found while profiling for BUG#6359.
-      
+
     - Fixed too-early creation of StringBuffer in EscapeProcessor.escapeSQL(),
       also return String when escaping not needed (to avoid unnecssary object
       allocations). Found while profiling for BUG#6359.
-      
+
     - Use null-safe-equals for key comparisons in updatable result sets.
-    
+
     - Fixed BUG#6537, SUM() on Decimal with server-side prepared statement ignores
       scale if zero-padding is needed (this ends up being due to conversion to DOUBLE
       by server, which when converted to a string to parse into BigDecimal, loses all
@@ -1114,442 +1866,442 @@
 
     - Use DatabaseMetaData.getIdentifierQuoteString() when building DBMD
       queries.
-      
+
     - Use 1MB packet for sending file for LOAD DATA LOCAL INFILE if that
       is < 'max_allowed_packet' on server.
-      
+
     - Fixed BUG#6399, ResultSetMetaData.getColumnDisplaySize() returns incorrect
       values for multibyte charsets.
-      
-    - Make auto-deserialization of java.lang.Objects stored in BLOBs 
+
+    - Make auto-deserialization of java.lang.Objects stored in BLOBs
       configurable via 'autoDeserialize' property (defaults to 'false').
-      
+
     - Re-work Field.isOpaqueBinary() to detect 'CHAR(n) CHARACTER SET BINARY'
       to support fixed-length binary fields for ResultSet.getObject().
-      
-    - Use our own implementation of buffered input streams to get around 
+
+    - Use our own implementation of buffered input streams to get around
       blocking behavior of java.io.BufferedInputStream. Disable this with
       'useReadAheadInput=false'.
-    
+
     - Fixed BUG#6348, failing to connect to the server when one of the
       addresses for the given host name is IPV6 (which the server does
       not yet bind on). The driver now loops through _all_ IP addresses
-      for a given host, and stops on the first one that accepts() a 
+      for a given host, and stops on the first one that accepts() a
       socket.connect().
-      
+
 09-04-04 - Version 3.1.4-beta
 
-    - Fixed BUG#4510 - connector/j 3.1.3 beta does not handle integers 
+    - Fixed BUG#4510 - connector/j 3.1.3 beta does not handle integers
       correctly (caused by changes to support unsigned reads in
       Buffer.readInt() -> Buffer.readShort()).
-    
-    - Added support in DatabaseMetaData.getTables() and getTableTypes() 
+
+    - Added support in DatabaseMetaData.getTables() and getTableTypes()
       for VIEWs which are now available in MySQL server version 5.0.x.
-    
+
     - Fixed BUG#4642 -- ServerPreparedStatement.execute*() sometimes
       threw ArrayIndexOutOfBoundsException when unpacking field metadata.
-    
-    - Optimized integer number parsing, enable 'old' slower integer parsing 
+
+    - Optimized integer number parsing, enable 'old' slower integer parsing
       using JDK classes via 'useFastIntParsing=false' property.
-    
+
     - Added 'useOnlyServerErrorMessages' property, which causes message text
       in exceptions generated by the server to only contain the text sent by
       the server (as opposed to the SQLState's 'standard' description, followed
       by the server's error message). This property is set to 'true' by default.
-    
-    - Fixed BUG#4689 - ResultSet.wasNull() does not work for primatives if a 
+
+    - Fixed BUG#4689 - ResultSet.wasNull() does not work for primatives if a
       previous null was returned.
-    
+
     - Track packet sequence numbers if enablePacketDebug=true, and throw an
       exception if packets received out-of-order.
-    
+
     - Fixed BUG#4482, ResultSet.getObject() returns wrong type for strings
       when using prepared statements.
-    
+
     - Calling MysqlPooledConnection.close() twice (even though an application
       error), caused NPE. Fixed.
-    
-    - Fixed BUG#5012 -- ServerPreparedStatements dealing with return of 
+
+    - Fixed BUG#5012 -- ServerPreparedStatements dealing with return of
 	  DECIMAL type don't work.
-	  
-	- Fixed BUG#5032 -- ResultSet.getObject() doesn't return 
+
+	- Fixed BUG#5032 -- ResultSet.getObject() doesn't return
       type Boolean for pseudo-bit types from prepared statements on 4.1.x
       (shortcut for avoiding extra type conversion when using binary-encoded
       result sets obscurred test in getObject() for 'pseudo' bit type)
-      
+
     - You can now use URLs in 'LOAD DATA LOCAL INFILE' statements, and the
       driver will use Java's built-in handlers for retreiving the data and
-      sending it to the server. This feature is not enabled by default, 
+      sending it to the server. This feature is not enabled by default,
       you must set the 'allowUrlInLocalInfile' connection property to 'true'.
-      
-    - The driver is more strict about truncation of numerics on 
+
+    - The driver is more strict about truncation of numerics on
       ResultSet.get*(), and will throw a SQLException when truncation is
       detected. You can disable this by setting 'jdbcCompliantTruncation' to
       false (it is enabled by default, as this functionality is required
       for JDBC compliance).
-      
+
     - Added three ways to deal with all-zero datetimes when reading them from
-      a ResultSet, 'exception' (the default), which throws a SQLException 
+      a ResultSet, 'exception' (the default), which throws a SQLException
       with a SQLState of 'S1009', 'convertToNull', which returns NULL instead of
       the date, and 'round', which rounds the date to the nearest closest value
       which is '0001-01-01'.
-      
-    - Fixed ServerPreparedStatement to read prepared statement metadata off 
+
+    - Fixed ServerPreparedStatement to read prepared statement metadata off
       the wire, even though it's currently a placeholder instead of using
       MysqlIO.clearInputStream() which didn't work at various times because
       data wasn't available to read from the server yet. This fixes sporadic
-      errors users were having with ServerPreparedStatements throwing 
+      errors users were having with ServerPreparedStatements throwing
       ArrayIndexOutOfBoundExceptions.
-     
+
     - Use com.mysql.jdbc.Message's classloader when loading resource bundle,
       should fix sporadic issues when the caller's classloader can't locate
       the resource bundle.
 
 07-07-04 - Version 3.1.3-beta
-	
-	- Mangle output parameter names for CallableStatements so they 
+
+	- Mangle output parameter names for CallableStatements so they
 	  will not clash with user variable names.
-	  
+
 	- Added support for INOUT parameters in CallableStatements.
-	
+
 	- Fix for BUG#4119, null bitmask sent for server-side prepared
 	  statements was incorrect.
-	  
-	- Use SQL Standard SQL states by default, unless 'useSqlStateCodes' 
+
+	- Use SQL Standard SQL states by default, unless 'useSqlStateCodes'
 	  property is set to 'false'.
-	  
+
 	- Added packet debuging code (see the 'enablePacketDebug' property
 	  documentation).
-	  
+
 	- Added constants for MySQL error numbers (publicly-accessible,
 	  see com.mysql.jdbc.MysqlErrorNumbers), and the ability to
 	  generate the mappings of vendor error codes to SQLStates
 	  that the driver uses (for documentation purposes).
-	  
+
 	- Externalized more messages (on-going effort).
-	
+
 	- Fix for BUG#4311 - Error in retrieval of mediumint column with
 	  prepared statements and binary protocol.
-	  
-	- Support new timezone variables in MySQL-4.1.3 when 
+
+	- Support new timezone variables in MySQL-4.1.3 when
 	  'useTimezone=true'
-	  
+
 	- Support for unsigned numerics as return types from prepared statements.
 	  This also causes a change in ResultSet.getObject() for the 'bigint unsigned'
 	  type, which used to return BigDecimal instances, it now returns instances
 	  of java.lang.BigInteger.
-	   
+
 06-09-04 - Version 3.1.2-alpha
 
 	- Fixed stored procedure parameter parsing info when size was
 	  specified for a parameter (i.e. char(), varchar()).
-	  
+
 	- Enabled callable statement caching via 'cacheCallableStmts'
 	  property.
-	  
-	- Fixed case when no output parameters specified for a 
-	  stored procedure caused a bogus query to be issued 
+
+	- Fixed case when no output parameters specified for a
+	  stored procedure caused a bogus query to be issued
 	  to retrieve out parameters, leading to a syntax error
 	  from the server.
-	  
+
 	- Fixed case when no parameters could cause a NullPointerException
 	  in CallableStatement.setOutputParameters().
-	  	  
+
 	- Removed wrapping of exceptions in MysqlIO.changeUser().
-	
+
 	- Fixed sending of split packets for large queries, enabled nio
 	  ability to send large packets as well.
-	  
+
 	- Added .toString() functionality to ServerPreparedStatement,
 	  which should help if you're trying to debug a query that is
 	  a prepared statement (it shows SQL as the server would process).
-	  
+
 	- Added 'gatherPerformanceMetrics' property, along with properties
-	  to control when/where this info gets logged (see docs for more 
+	  to control when/where this info gets logged (see docs for more
 	  info).
-	  
+
 	- ServerPreparedStatements weren't actually de-allocating
 	  server-side resources when .close() was called.
-	  
-	- Added 'logSlowQueries' property, along with property 
+
+	- Added 'logSlowQueries' property, along with property
 	  'slowQueriesThresholdMillis' to control when a query should
 	  be considered 'slow'.
-	  
+
 	- Correctly map output parameters to position given in
 	  prepareCall() vs. order implied during registerOutParameter() -
 	  fixes BUG#3146.
-	  
+
 	- Correctly detect initial character set for servers >= 4.1.0
-	
-	- Cleaned up detection of server properties. 
-	
+
+	- Cleaned up detection of server properties.
+
 	- Support placeholder for parameter metadata for server >= 4.1.2
-	
-	- Fix for BUG#3539 getProcedures() does not return any procedures in 
+
+	- Fix for BUG#3539 getProcedures() does not return any procedures in
 	  result set
-	
-	- Fix for BUG#3540 getProcedureColumns() doesn't work with wildcards 
+
+	- Fix for BUG#3540 getProcedureColumns() doesn't work with wildcards
 	  for procedure name
-	  
+
 	- Fixed BUG#3520 -- DBMD.getSQLStateType() returns incorrect value.
-	
-	- Added 'connectionCollation' property to cause driver to issue 
+
+	- Added 'connectionCollation' property to cause driver to issue
 	  'set collation_connection=...' query on connection init if default
 	  collation for given charset is not appropriate.
-	  
+
 	- Fixed DatabaseMetaData.getProcedures() when run on MySQL-5.0.0 (output of
 	'show procedure status' changed between 5.0.1 and 5.0.0.
-	
+
 	- Fixed BUG#3804 -- getWarnings() returns SQLWarning instead of DataTruncation
-	
+
 	- Don't enable server-side prepared statements for server version 5.0.0 or 5.0.1,
 	as they aren't compatible with the '4.1.2+' style that the driver uses (the driver
 	expects information to come back that isn't there, so it hangs).
-	
-	  
+
+
 02-14-04 - Version 3.1.1-alpha
 
     - Fixed bug with UpdatableResultSets not using client-side
 	  prepared statements.
-	  
+
 	- Fixed character encoding issues when converting bytes to
 	  ASCII when MySQL doesn't provide the character set, and
 	  the JVM is set to a multibyte encoding (usually affecting
 	  retrieval of numeric values).
-	
+
 	- Unpack 'unknown' data types from server prepared statements
 	  as Strings.
-	  
+
 	- Implemented long data (Blobs, Clobs, InputStreams, Readers)
 	  for server prepared statements.
-	  
+
 	- Implemented Statement.getWarnings() for MySQL-4.1 and newer
 	  (using 'SHOW WARNINGS').
-	
+
 	- Default result set type changed to TYPE_FORWARD_ONLY
 	  (JDBC compliance).
-	  
+
 	- Centralized setting of result set type and concurrency.
-	
+
 	- Re-factored how connection properties are set and exposed
 	  as DriverPropertyInfo as well as Connection and DataSource
 	  properties.
-	  
+
 	- Support for NIO. Use 'useNIO=true' on platforms that support
 	  NIO.
-	  
+
 	- Support for SAVEPOINTs (MySQL >= 4.0.14 or 4.1.1).
-	
+
 	- Support for mysql_change_user()...See the changeUser() method
 	  in com.mysql.jdbc.Connection.
-	  
+
 	- Reduced number of methods called in average query to be more
 	  efficient.
-	  
+
 	- Prepared Statements will be re-prepared on auto-reconnect. Any errors
 	  encountered are postponed until first attempt to re-execute the
 	  re-prepared statement.
-	  
+
 	- Ensure that warnings are cleared before executing queries
 	  on prepared statements, as-per JDBC spec (now that we support
 	  warnings).
-	 
+
 	- Support 'old' profileSql capitalization in ConnectionProperties.
 	  This property is deprecated, you should use 'profileSQL' if possible.
-	  
-	- Optimized Buffer.readLenByteArray() to return shared empty byte array 
+
+	- Optimized Buffer.readLenByteArray() to return shared empty byte array
 	  when length is 0.
-	 
+
 	- Allow contents of PreparedStatement.setBlob() to be retained
 	  between calls to .execute*().
-	  
+
 	- Deal with 0-length tokens in EscapeProcessor (caused by callable
 	  statement escape syntax).
-	  
-	- Check for closed connection on delete/update/insert row operations in 
-	  UpdatableResultSet. 
-	
-	- Fix support for table aliases when checking for all primary keys in 
+
+	- Check for closed connection on delete/update/insert row operations in
 	  UpdatableResultSet.
-	
-	- Removed useFastDates connection property. 
-	
-	- Correctly initialize datasource properties from JNDI Refs, including 
+
+	- Fix support for table aliases when checking for all primary keys in
+	  UpdatableResultSet.
+
+	- Removed useFastDates connection property.
+
+	- Correctly initialize datasource properties from JNDI Refs, including
 	  explicitly specified URLs.
-	  
-	- DatabaseMetaData now reports supportsStoredProcedures() for 
+
+	- DatabaseMetaData now reports supportsStoredProcedures() for
 	  MySQL versions >= 5.0.0
-	  
+
 	- Fixed stack overflow in Connection.prepareCall() (bad merge).
-	
+
 	- Fixed IllegalAccessError to Calendar.getTimeInMillis() in DateTimeValue
-	  (for JDK < 1.4). 
-	  
+	  (for JDK < 1.4).
+
 	- Fix for BUG#1673, where DatabaseMetaData.getColumns() is not
       returning correct column ordinal info for non '%' column name patterns.
-      
-    - Merged fix of datatype mapping from MySQL type 'FLOAT' to 
+
+    - Merged fix of datatype mapping from MySQL type 'FLOAT' to
       java.sql.Types.REAL from 3.0 branch.
-      
+
     - Detect collation of column for RSMD.isCaseSensitive().
-    
+
     - Fixed sending of queries > 16M.
-    
+
     - Added named and indexed input/output parameter support to CallableStatement.
       MySQL-5.0.x or newer.
-      
+
     - Fixed NullPointerException in ServerPreparedStatement.setTimestamp(),
-      as well as year and month descrepencies in 
+      as well as year and month descrepencies in
       ServerPreparedStatement.setTimestamp(), setDate().
-      
+
     - Added ability to have multiple database/JVM targets for compliance
       and regression/unit tests in build.xml.
-      
-    - Fixed NPE and year/month bad conversions when accessing some 
-      datetime functionality in ServerPreparedStatements and their 
+
+    - Fixed NPE and year/month bad conversions when accessing some
+      datetime functionality in ServerPreparedStatements and their
       resultant result sets.
-      
+
     - Display where/why a connection was implicitly closed (to
       aid debugging).
-      
+
     - CommunicationsException implemented, that tries to determine
       why communications was lost with a server, and displays
       possible reasons when .getMessage() is called.
-      
+
     - Fixed BUG#2359, NULL values for numeric types in binary
       encoded result sets causing NullPointerExceptions.
-      
+
     - Implemented Connection.prepareCall(), and DatabaseMetaData.
       getProcedures() and getProcedureColumns().
-      
+
     - Reset 'long binary' parameters in ServerPreparedStatement when
-      clearParameters() is called, by sending COM_RESET_STMT to the 
+      clearParameters() is called, by sending COM_RESET_STMT to the
       server.
-      
+
     - Merged prepared statement caching, and .getMetaData() support
       from 3.0 branch.
-    
-    - Fixed off-by-1900 error in some cases for 
+
+    - Fixed off-by-1900 error in some cases for
       years in TimeUtil.fastDate/TimeCreate() when unpacking results
       from server-side prepared statements.
-      
+
     - Fixed BUG#2502 -- charset conversion issue in getTables().
-    
-    - Implemented multiple result sets returned from a statement 
+
+    - Implemented multiple result sets returned from a statement
       or stored procedure.
-      
+
     - Fixed BUG#2606 -- Server side prepared statements not returning
       datatype 'YEAR' correctly.
-      
+
     - Enabled streaming of result sets from server-side prepared
       statements.
-      
-    - Fixed BUG#2623 -- Class-cast exception when using 
+
+    - Fixed BUG#2623 -- Class-cast exception when using
       scrolling result sets and server-side prepared statements.
-	  
+
 	- Merged unbuffered input code from 3.0.
-	
+
 	- Fixed ConnectionProperties that weren't properly exposed
 	  via accessors, cleaned up ConnectionProperties code.
-	  
+
 	- Fixed BUG#2671, NULL fields not being encoded correctly in
 	  all cases in server side prepared statements.
-	  
+
 	- Fixed rare buffer underflow when writing numbers into buffers
 	  for sending prepared statement execution requests.
-	  
+
 	- Use DocBook version of docs for shipped versions of drivers.
-	
-	  
+
+
 02-18-03 - Version 3.1.0-alpha
 
     - Added 'requireSSL' property.
-    
+
     - Added 'useServerPrepStmts' property (default 'false'). The
       driver will use server-side prepared statements when the
       server version supports them (4.1 and newer) when this
-      property is set to 'true'. It is currently set to 'false' 
-      by default until all bind/fetch functionality has been 
+      property is set to 'true'. It is currently set to 'false'
+      by default until all bind/fetch functionality has been
       implemented. Currently only DML prepared statements are
       implemented for 4.1 server-side prepared statements.
-  
+
     - Track open Statements, close all when Connection.close()
       is called (JDBC compliance).
 
 06-22-05 - Version 3.0.17-ga
-    
-    - Fixed BUG#5874, Timestamp/Time conversion goes in the wrong 'direction' 
+
+    - Fixed BUG#5874, Timestamp/Time conversion goes in the wrong 'direction'
       when useTimeZone='true' and server timezone differs from client timezone.
-	
-	- Fixed BUG#7081, DatabaseMetaData.getIndexInfo() ignoring 'unique' 
+
+	- Fixed BUG#7081, DatabaseMetaData.getIndexInfo() ignoring 'unique'
 	  parameter.
-	
+
 	- Support new protocol type 'MYSQL_TYPE_VARCHAR'.
-	
+
 	- Added 'useOldUTF8Behavoior' configuration property, which causes
 	  JDBC driver to act like it did with MySQL-4.0.x and earlier when
-	  the character encoding is 'utf-8' when connected to MySQL-4.1 or 
+	  the character encoding is 'utf-8' when connected to MySQL-4.1 or
 	  newer.
-	
+
 	- Fixed BUG#7316 - Statements created from a pooled connection were
 	  returning physical connection instead of logical connection when
 	  getConnection() was called.
-	  
-	- Fixed BUG#7033 - PreparedStatements don't encode Big5 (and other 
+
+	- Fixed BUG#7033 - PreparedStatements don't encode Big5 (and other
 	  multibyte) character sets correctly in static SQL strings.
-	  
+
 	- Fixed BUG#6966, connections starting up failed-over (due to down master)
       never retry master.
-      
-    - Fixed BUG#7061, PreparedStatement.fixDecimalExponent() adding extra 
+
+    - Fixed BUG#7061, PreparedStatement.fixDecimalExponent() adding extra
       '+', making number unparseable by MySQL server.
 
-    - Fixed BUG#7686, Timestamp key column data needed "_binary'" stripped for 
+    - Fixed BUG#7686, Timestamp key column data needed "_binary'" stripped for
       UpdatableResultSet.refreshRow().
-    
+
     - Backported SQLState codes mapping from Connector/J 3.1, enable with
       'useSqlStateCodes=true' as a connection property, it defaults to
       'false' in this release, so that we don't break legacy applications (it
       defaults to 'true' starting with Connector/J 3.1).
-      
-    - Fixed BUG#7601, PreparedStatement.fixDecimalExponent() adding extra 
+
+    - Fixed BUG#7601, PreparedStatement.fixDecimalExponent() adding extra
       '+', making number unparseable by MySQL server.
-      
+
     - Escape sequence {fn convert(..., type)} now supports ODBC-style types
       that are prepended by 'SQL_'.
-    
+
     - Fixed duplicated code in configureClientCharset() that prevented
       useOldUTF8Behavior=true from working properly.
-    
+
     - Handle streaming result sets with > 2 billion rows properly by fixing
       wraparound of row number counter.
-    
-    - Fixed BUG#7607 - MS932, SHIFT_JIS and Windows_31J not recog. as 
+
+    - Fixed BUG#7607 - MS932, SHIFT_JIS and Windows_31J not recog. as
       aliases for sjis.
-    
+
     - Fixed BUG#6549 (while fixing #7607), adding 'CP943' to aliases for
       sjis.
-    
+
     - Fixed BUG#8064, which requires hex escaping of binary data when using
       multibyte charsets with prepared statements.
-      
-    - Fixed BUG#8812, NON_UNIQUE column from DBMD.getIndexInfo() returned 
+
+    - Fixed BUG#8812, NON_UNIQUE column from DBMD.getIndexInfo() returned
       inverted value.
-      
-    - Workaround for server BUG#9098 - default values of CURRENT_* for 
+
+    - Workaround for server BUG#9098 - default values of CURRENT_* for
       DATE/TIME/TIMESTAMP/TIMESTAMP columns can't be distinguished from
       'string' values, so UpdatableResultSet.moveToInsertRow() generates
       bad SQL for inserting default values.
-      
+
     - Fixed BUG#8629 - 'EUCKR' charset is sent as 'SET NAMES euc_kr' which
       MySQL-4.1 and newer doesn't understand.
-      
+
     - DatabaseMetaData.supportsSelectForUpdate() returns correct value based
       on server version.
-      
+
     - Use hex escapes for PreparedStatement.setBytes() for double-byte charsets
       including 'aliases' Windows-31J, CP934, MS932.
-      
+
     - Added support for the "EUC_JP_Solaris" character encoding, which maps
       to a MySQL encoding of "eucjpms" (backported from 3.1 branch). This only
       works on servers that support eucjpms, namely 5.0.3 or later.
@@ -1559,183 +2311,183 @@
 	- Re-issue character set configuration commands when re-using pooled
 	  connections and/or Connection.changeUser() when connected to MySQL-4.1
 	  or newer.
-	  
+
 	- Fixed ResultSetMetaData.isReadOnly() to detect non-writable columns
 	  when connected to MySQL-4.1 or newer, based on existence of 'original'
 	  table and column names.
-	  
+
 	- Fixed BUG#5664, ResultSet.updateByte() when on insert row
       throws ArrayOutOfBoundsException.
-      
+
     - Fixed DatabaseMetaData.getTypes() returning incorrect (i.e. non-negative)
       scale for the 'NUMERIC' type.
-      
+
     - Fixed BUG#6198, off-by-one bug in Buffer.readString(string).
-    
+
     - Made TINYINT(1) -> BIT/Boolean conversion configurable via 'tinyInt1isBit'
       property (default 'true' to be JDBC compliant out of the box).
-      
+
     - Only set 'character_set_results' during connection establishment if
       server version >= 4.1.1.
-      
+
     - Fixed regression where useUnbufferedInput was defaulting to 'false'.
-    
+
     - Fixed BUG#6231, ResultSet.getTimestamp() on a column with TIME in it
       fails.
-      
+
 09-04-04 - Version 3.0.15-ga
 
 	- Fixed BUG#4010 - StringUtils.escapeEasternUnicodeByteStream is still
 	  broken for GBK
-	  
+
 	- Fixed BUG#4334 - Failover for autoReconnect not using port #'s for any
-	  hosts, and not retrying all hosts. (WARN: This required a change to 
+	  hosts, and not retrying all hosts. (WARN: This required a change to
 	  the SocketFactory connect() method signature, which is now
-	  
+
 	    public Socket connect(String host, int portNumber, Properties props),
-	  
+
 	  therefore any third-party socket factories will have to be changed
 	  to support this signature.
-	  
+
 	- Logical connections created by MysqlConnectionPoolDataSource will
 	  now issue a rollback() when they are closed and sent back to the pool.
-	  If your application server/connection pool already does this for you, you 
+	  If your application server/connection pool already does this for you, you
 	  can set the 'rollbackOnPooledClose' property to false to avoid the
 	  overhead of an extra rollback().
-	 
+
 	- Removed redundant calls to checkRowPos() in ResultSet.
-	
+
 	- Fixed BUG#4742, 'DOUBLE' mapped twice in DBMD.getTypeInfo().
 
 	- Added FLOSS license exemption.
-	
+
 	- Fixed BUG#4808, calling .close() twice on a PooledConnection causes NPE.
-	
-	- Fixed BUG#4138 and BUG#4860, DBMD.getColumns() returns incorrect JDBC 
-	  type for unsigned columns. This affects type mappings for all numeric 
-	  types in the RSMD.getColumnType() and RSMD.getColumnTypeNames() methods 
-	  as well, to ensure that 'like' types from DBMD.getColumns() match up 
+
+	- Fixed BUG#4138 and BUG#4860, DBMD.getColumns() returns incorrect JDBC
+	  type for unsigned columns. This affects type mappings for all numeric
+	  types in the RSMD.getColumnType() and RSMD.getColumnTypeNames() methods
+	  as well, to ensure that 'like' types from DBMD.getColumns() match up
 	  with what RSMD.getColumnType() and getColumnTypeNames() return.
-	  
+
 	- 'Production' - 'GA' in naming scheme of distributions.
-	
+
 	- Fix for BUG#4880, RSMD.getPrecision() returning 0 for non-numeric types
 	  (should return max length in chars for non-binary types, max length
-	  in bytes for binary types). This fix also fixes mapping of 
+	  in bytes for binary types). This fix also fixes mapping of
 	  RSMD.getColumnType() and RSMD.getColumnTypeName() for the BLOB types based
 	  on the length sent from the server (the server doesn't distinguish between
 	  TINYBLOB, BLOB, MEDIUMBLOB or LONGBLOB at the network protocol level).
-	  
+
 	- Fixed BUG#5022 - ResultSet should release Field[] instance in .close().
-	       
-    - Fixed BUG#5069 -- ResultSet.getMetaData() should not return 
-	  incorrectly-initialized metadata if the result set has been closed, but 
-	  should instead throw a SQLException. Also fixed for getRow() and 
+
+    - Fixed BUG#5069 -- ResultSet.getMetaData() should not return
+	  incorrectly-initialized metadata if the result set has been closed, but
+	  should instead throw a SQLException. Also fixed for getRow() and
 	  getWarnings() and traversal methods by calling checkClosed() before
 	  operating on instance-level fields that are nullified during .close().
-	  
+
 	- Parse new timezone variables from 4.1.x servers.
-	
-	- Use _binary introducer for PreparedStatement.setBytes() and 
-	  set*Stream() when connected to MySQL-4.1.x or newer to avoid 
+
+	- Use _binary introducer for PreparedStatement.setBytes() and
+	  set*Stream() when connected to MySQL-4.1.x or newer to avoid
 	  misinterpretation during character conversion.
-	  
+
 05-28-04 - Version 3.0.14-production
 
 	- Fixed URL parsing error
-	
+
 05-27-04 - Version 3.0.13-production
 
 	- Fixed BUG#3848 - Using a MySQLDatasource without server name fails
-	
-	- Fixed BUG#3920 - "No Database Selected" when using 
+
+	- Fixed BUG#3920 - "No Database Selected" when using
 	  MysqlConnectionPoolDataSource.
-	  
-	- Fixed BUG#3873 - PreparedStatement.getGeneratedKeys() method returns only 
+
+	- Fixed BUG#3873 - PreparedStatement.getGeneratedKeys() method returns only
 	  1 result for batched insertions
-	  
+
 05-18-04 - Version 3.0.12-production
 
 	- Add unsigned attribute to DatabaseMetaData.getColumns() output
 	  in the TYPE_NAME column.
-	  
+
 	- Added 'failOverReadOnly' property, to allow end-user to configure
 	  state of connection (read-only/writable) when failed over.
-	  
+
 	- Backported 'change user' and 'reset server state' functionality
       from 3.1 branch, to allow clients of MysqlConnectionPoolDataSource
       to reset server state on getConnection() on a pooled connection.
-      
+
     - Don't escape SJIS/GBK/BIG5 when using MySQL-4.1 or newer.
-    
+
     - Allow 'url' parameter for MysqlDataSource and MysqlConnectionPool
       DataSource so that passing of other properties is possible from
       inside appservers.
-      
+
     - Map duplicate key and foreign key errors to SQLState of
       '23000'.
-      
+
     - Backport documentation tooling from 3.1 branch.
-    
+
     - Return creating statement for ResultSets created by
       getGeneratedKeys() (BUG#2957)
-      
+
     - Allow java.util.Date to be sent in as parameter to
       PreparedStatement.setObject(), converting it to a Timestamp
       to maintain full precision (BUG#3103).
-      
+
     - Don't truncate BLOBs/CLOBs when using setBytes() and/or
       setBinary/CharacterStream() (BUG#2670).
-      
+
     - Dynamically configure character set mappings for field-level
       character sets on MySQL-4.1.0 and newer using 'SHOW COLLATION'
       when connecting.
-      
+
     - Map 'binary' character set to 'US-ASCII' to support DATETIME
       charset recognition for servers >= 4.1.2
-      
-    - Use 'SET character_set_results" during initialization to allow any 
+
+    - Use 'SET character_set_results" during initialization to allow any
       charset to be returned to the driver for result sets.
-      
+
     - Use charsetnr returned during connect to encode queries before
       issuing 'SET NAMES' on MySQL >= 4.1.0.
-      
+
     - Add helper methods to ResultSetMetaData (getColumnCharacterEncoding()
       and getColumnCharacterSet()) to allow end-users to see what charset
       the driver thinks it should be using for the column.
-      
+
     - Only set character_set_results for MySQL >= 4.1.0.
-    
+
     - Fixed BUG#3511, StringUtils.escapeSJISByteStream() not covering all
       eastern double-byte charsets correctly.
-      
+
     - Renamed StringUtils.escapeSJISByteStream() to more appropriate
       escapeEasternUnicodeByteStream().
-      
+
     - Fixed BUG#3554 - Not specifying database in URL caused MalformedURL
       exception.
-      
+
     - Auto-convert MySQL encoding names to Java encoding names if used
       for characterEncoding property.
-      
+
     - Added encoding names that are recognized on some JVMs to fix case
       where they were reverse-mapped to MySQL encoding names incorrectly.
-      
+
     - Use junit.textui.TestRunner for all unit tests (to allow them to be
       run from the command line outside of Ant or Eclipse).
-      
+
     - Fixed BUG#3557 - UpdatableResultSet not picking up default values
       for moveToInsertRow().
-      
+
     - Fixed BUG#3570 - inconsistent reporting of column type. The server
       still doesn't return all types for *BLOBs *TEXT correctly, so the
       driver won't return those correctly.
-      
+
     - Fixed BUG#3520 -- DBMD.getSQLStateType() returns incorrect value.
-    
+
     - Fixed regression in PreparedStatement.setString() and eastern character
       encodings.
-      
+
     - Made StringRegressionTest 4.1-unicode aware.
 
 02-19-04 - Version 3.0.11-stable
@@ -1743,823 +2495,823 @@
 	- Trigger a 'SET NAMES utf8' when encoding is forced to 'utf8' _or_
 	  'utf-8' via the 'characterEncoding' property. Previously, only the
 	  Java-style encoding name of 'utf-8' would trigger this.
-	  
+
 	- AutoReconnect time was growing faster than exponentially (BUG#2447).
-	
+
 	- Fixed failover always going to last host in list (BUG#2578)
-	
+
 	- Added 'useUnbufferedInput' parameter, and now use it by default
-	  (due to JVM issue 
+	  (due to JVM issue
 	  http://developer.java.sun.com/developer/bugParade/bugs/4401235.html)
-	  
-	- Detect 'on/off' or '1','2','3' form of lower_case_table_names on 
+
+	- Detect 'on/off' or '1','2','3' form of lower_case_table_names on
 	  server.
-	  
+
 	- Return 'java.lang.Integer' for TINYINT and SMALLINT types from
 	  ResultSetMetaData.getColumnClassName() (fix for BUG#2852).
-	  
+
 	- Return 'java.lang.Double' for FLOAT type from ResultSetMetaData.
 	  getColumnClassName() (fix for BUG#2855).
-	  
-	- Return '[B' instead of java.lang.Object for BINARY, VARBINARY and 
-	  LONGVARBINARY types from ResultSetMetaData.getColumnClassName() 
+
+	- Return '[B' instead of java.lang.Object for BINARY, VARBINARY and
+	  LONGVARBINARY types from ResultSetMetaData.getColumnClassName()
 	  (JDBC compliance).
-	  
+
 01-13-04 - Version 3.0.10-stable
 
     - Don't count quoted id's when inside a 'string' in PreparedStatement
       parsing (fix for BUG#1511).
-      
+
     - 'Friendlier' exception message for PacketTooLargeException
        (BUG#1534).
-       
-    - Backported fix for aliased tables and UpdatableResultSets in 
+
+    - Backported fix for aliased tables and UpdatableResultSets in
       checkUpdatability() method from 3.1 branch.
-      
+
     - Fix for ArrayIndexOutOfBounds exception when using Statement.setMaxRows()
       (BUG#1695).
-      
-    - Fixed BUG#1576, dealing with large blobs and split packets not being 
+
+    - Fixed BUG#1576, dealing with large blobs and split packets not being
       read correctly.
-      
+
     - Fixed regression of Statement.getGeneratedKeys() and REPLACE statements.
-    
+
     - Fixed BUG#1630, subsequent call to ResultSet.updateFoo() causes NPE if
       result set is not updatable.
-      
+
     - Fix for 4.1.1-style auth with no password.
-    
+
     - Fix for BUG#1731, Foreign Keys column sequence is not consistent in
       DatabaseMetaData.getImported/Exported/CrossReference().
-      
-    - Fix for BUG#1775 - DatabaseMetaData.getSystemFunction() returning 
+
+    - Fix for BUG#1775 - DatabaseMetaData.getSystemFunction() returning
       bad function 'VResultsSion'.
-      
+
     - Fix for BUG#1592 -- cross-database updatable result sets
       are not checked for updatability correctly.
-      
+
     - DatabaseMetaData.getColumns() should return Types.LONGVARCHAR for
       MySQL LONGTEXT type.
-      
-    - ResultSet.getObject() on TINYINT and SMALLINT columns should return 
+
+    - ResultSet.getObject() on TINYINT and SMALLINT columns should return
       Java type 'Integer' (BUG#1913)
-      
+
     - Added 'alwaysClearStream' connection property, which causes the driver
       to always empty any remaining data on the input stream before
       each query.
-      
-    - Added more descriptive error message 'Server Configuration Denies 
+
+    - Added more descriptive error message 'Server Configuration Denies
       Access to DataSource', as well as retrieval of message from server.
-      
-    - Autoreconnect code didn't set catalog upon reconnect if it had been 
+
+    - Autoreconnect code didn't set catalog upon reconnect if it had been
       changed.
-      
+
     - Implement ResultSet.updateClob().
-    
+
     - ResultSetMetaData.isCaseSensitive() returned wrong value for CHAR/VARCHAR
       columns.
-      
+
     - Fix for BUG#1933 -- Connection property "maxRows" not honored.
-    
-    - Fix for BUG#1925 -- Statements being created too many times in 
+
+    - Fix for BUG#1925 -- Statements being created too many times in
       DBMD.extractForeignKeyFromCreateTable().
-      
+
     - Fix for BUG#1914 -- Support escape sequence {fn convert ... }
-    
-    - Fix for BUG#1958 -- ArrayIndexOutOfBounds when parameter number == 
+
+    - Fix for BUG#1958 -- ArrayIndexOutOfBounds when parameter number ==
       number of parameters + 1.
-      
+
     - Fix for BUG#2006 -- ResultSet.findColumn() should use first matching
-      column name when there are duplicate column names in SELECT query 
+      column name when there are duplicate column names in SELECT query
       (JDBC-compliance).
-      
-    - Removed static synchronization bottleneck from 
+
+    - Removed static synchronization bottleneck from
       PreparedStatement.setTimestamp().
-      
-    - Removed static synchronization bottleneck from instance factory 
+
+    - Removed static synchronization bottleneck from instance factory
       method of SingleByteCharsetConverter.
-      
-    - Enable caching of the parsing stage of prepared statements via 
+
+    - Enable caching of the parsing stage of prepared statements via
       the 'cachePrepStmts', 'prepStmtCacheSize' and 'prepStmtCacheSqlLimit'
       properties (disabled by default).
-      
+
     - Speed up parsing of PreparedStatements, try to use one-pass whenever
       possible.
-      
+
     - Fixed security exception when used in Applets (applets can't
       read the system property 'file.encoding' which is needed
       for LOAD DATA LOCAL INFILE).
-      
+
     - Use constants for SQLStates.
-    
+
     - Map charset 'ko18_ru' to 'ko18r' when connected to MySQL-4.1.0 or
       newer.
-      
+
     - Ensure that Buffer.writeString() saves room for the \0.
-    
+
     - Fixed exception 'Unknown character set 'danish' on connect w/ JDK-1.4.0
-    
+
     - Fixed mappings in SQLError to report deadlocks with SQLStates of '41000'.
-    
-    - 'maxRows' property would affect internal statements, so check it for all 
+
+    - 'maxRows' property would affect internal statements, so check it for all
       statement creation internal to the driver, and set to 0 when it is not.
-    
+
 10-07-03 - Version 3.0.9-stable
 
 	- Faster date handling code in ResultSet and PreparedStatement (no longer
 	  uses Date methods that synchronize on static calendars).
-	
+
 	- Fixed test for end of buffer in Buffer.readString().
-	
-	- Fixed ResultSet.previous() behavior to move current 
+
+	- Fixed ResultSet.previous() behavior to move current
 	  position to before result set when on first row
 	  of result set (bugs.mysql.com BUG#496)
-	
+
 	- Fixed Statement and PreparedStatement issuing bogus queries
 	  when setMaxRows() had been used and a LIMIT clause was present
 	  in the query.
-	
+
 	- Fixed BUG#661 - refreshRow didn't work when primary key values
 	  contained values that needed to be escaped (they ended up being
 	  doubly-escaped).
-	
+
 	- Support InnoDB contraint names when extracting foreign key info
 	  in DatabaseMetaData BUG#517 and BUG#664
 	  (impl. ideas from Parwinder Sekhon)
-	
+
 	- Backported 4.1 protocol changes from 3.1 branch (server-side SQL
-	  states, new field info, larger client capability flags, 
+	  states, new field info, larger client capability flags,
 	  connect-with-database, etc).
-	
+
 	- Fix UpdatableResultSet to return values for getXXX() when on
 	  insert row (BUG#675).
-	
-	- The insertRow in an UpdatableResultSet is now loaded with 
+
+	- The insertRow in an UpdatableResultSet is now loaded with
 	  the default column values when moveToInsertRow() is called
 	  (BUG#688)
-	
-	- DatabaseMetaData.getColumns() wasn't returning NULL for 
+
+	- DatabaseMetaData.getColumns() wasn't returning NULL for
 	  default values that are specified as NULL.
-	
+
 	- Change default statement type/concurrency to TYPE_FORWARD_ONLY
 	  and CONCUR_READ_ONLY (spec compliance).
-	
+
 	- Don't try and reset isolation level on reconnect if MySQL doesn't
 	  support them.
-	
+
 	- Don't wrap SQLExceptions in RowDataDynamic.
-	
+
 	- Don't change timestamp TZ twice if useTimezone==true (BUG#774)
-	
+
 	- Fixed regression in large split-packet handling (BUG#848).
-	
+
 	- Better diagnostic error messages in exceptions for 'streaming'
 	  result sets.
-	
+
 	- Issue exception on ResultSet.getXXX() on empty result set (wasn't
 	  caught in some cases).
-	
+
 	- Don't hide messages from exceptions thrown in I/O layers.
-	
+
 	- Don't fire connection closed events when closing pooled connections, or
 	  on PooledConnection.getConnection() with already open connections (BUG#884).
-	
-	- Clip +/- INF (to smallest and largest representative values for the type in 
+
+	- Clip +/- INF (to smallest and largest representative values for the type in
 	  MySQL) and NaN (to 0) for setDouble/setFloat(), and issue a warning on the
 	  statement when the server does not support +/- INF or NaN.
-	  
+
 	- Fix for BUG#879, double-escaping of '\' when charset is SJIS or GBK and '\'
 	  appears in non-escaped input.
-	  
+
 	- When emptying input stream of unused rows for 'streaming' result sets,
-	  have the current thread yield() every 100 rows in order to not monopolize 
+	  have the current thread yield() every 100 rows in order to not monopolize
 	  CPU time.
-	  
+
 	- Fixed BUG#1099, DatabaseMetaData.getColumns() getting confused about the
 	  keyword 'set' in character columns.
-	  
+
 	- Fixed deadlock issue with Statement.setMaxRows().
-	
+
 	- Fixed CLOB.truncate(), BUG#1130
-	
+
 	- Optimized CLOB.setChracterStream(), BUG#1131
-	
+
 	- Made databaseName, portNumber and serverName optional parameters
 	  for MysqlDataSourceFactory (BUG#1246)
-	  
+
 	- Fix for BUG#1247 -- ResultSet.get/setString mashing char 127
-	
-	- Backported auth. changes for 4.1.1 and newer from 3.1 branch. 
-	
+
+	- Backported auth. changes for 4.1.1 and newer from 3.1 branch.
+
 	- Added com.mysql.jdbc.util.BaseBugReport to help creation of testcases
 	  for bug reports.
-	  
-	- Added property to 'clobber' streaming results, by setting the 
-	  'clobberStreamingResults' property to 'true' (the default is 'false'). 
+
+	- Added property to 'clobber' streaming results, by setting the
+	  'clobberStreamingResults' property to 'true' (the default is 'false').
 	  This will cause a 'streaming' ResultSet to be automatically
 	  closed, and any oustanding data still streaming from the server to
 	  be discarded if another query is executed before all the data has been
 	  read from the server.
-       
+
 05-23-03 - Version 3.0.8-stable
 
 	- Allow bogus URLs in Driver.getPropertyInfo().
-	
+
 	- Return list of generated keys when using multi-value INSERTS
 	  with Statement.getGeneratedKeys().
-	  
+
 	- Use JVM charset with filenames and 'LOAD DATA [LOCAL] INFILE'
-	
+
 	- Fix infinite loop with Connection.cleanup().
-	
-	- Changed Ant target 'compile-core' to 'compile-driver', and 
+
+	- Changed Ant target 'compile-core' to 'compile-driver', and
 	  made testsuite compilation a separate target.
-	  
+
 	- Fixed result set not getting set for Statement.executeUpdate(),
 	  which affected getGeneratedKeys() and getUpdateCount() in
 	  some cases.
-	  
+
 	- Unicode character 0xFFFF in a string would cause the driver to
 	  throw an ArrayOutOfBoundsException (Bug #378)
-	  
-	- Return correct amount of generated keys when using 'REPLACE' 
+
+	- Return correct amount of generated keys when using 'REPLACE'
 	  statements.
-	  
+
 	- Fix problem detecting server character set in some cases.
-	
+
 	- Fix row data decoding error when using _very_ large packets.
-	
+
 	- Optimized row data decoding.
-	
+
 	- Issue exception when operating on an already-closed
 	  prepared statement.
-	  
+
 	- Fixed SJIS encoding bug, thanks to Naoto Sato.
-	
+
     - Optimized usage of EscapeProcessor.
-    
+
     - Allow multiple calls to Statement.close()
-	
+
 04-08-03 - Version 3.0.7-stable
 
     - Fixed MysqlPooledConnection.close() calling wrong event type.
-    
+
     - Fixed StringIndexOutOfBoundsException in PreparedStatement.
       setClob().
-      
+
     - 4.1 Column Metadata fixes
-    
-    - Remove synchronization from Driver.connect() and 
+
+    - Remove synchronization from Driver.connect() and
       Driver.acceptsUrl().
-      
-    - IOExceptions during a transaction now cause the Connection to 
+
+    - IOExceptions during a transaction now cause the Connection to
       be closed.
-      
+
     - Fixed missing conversion for 'YEAR' type in ResultSetMetaData.
       getColumnTypeName().
-      
+
     - Don't pick up indexes that start with 'pri' as primary keys
       for DBMD.getPrimaryKeys().
-      
+
     - Throw SQLExceptions when trying to do operations on a forcefully
       closed Connection (i.e. when a communication link failure occurs).
-      
-    - You can now toggle profiling on/off using 
+
+    - You can now toggle profiling on/off using
       Connection.setProfileSql(boolean).
-      
+
     - Fixed charset issues with database metadata (charset was not
       getting set correctly).
-      
+
     - Updatable ResultSets can now be created for aliased tables/columns
       when connected to MySQL-4.1 or newer.
-      
+
     - Fixed 'LOAD DATA LOCAL INFILE' bug when file > max_allowed_packet.
-    
+
     - Fixed escaping of 0x5c ('\') character for GBK and Big5 charsets.
-    
+
     - Fixed ResultSet.getTimestamp() when underlying field is of type DATE.
-    
+
     - Ensure that packet size from alignPacketSize() does not
       exceed MAX_ALLOWED_PACKET (JVM bug)
-      
+
     - Don't reset Connection.isReadOnly() when autoReconnecting.
-      
+
 02-18-03 - Version 3.0.6-stable
 
     - Fixed ResultSetMetaData to return "" when catalog not known.
       Fixes NullPointerExceptions with Sun's CachedRowSet.
-      
-    - Fixed DBMD.getTypeInfo() and DBMD.getColumns() returning 
+
+    - Fixed DBMD.getTypeInfo() and DBMD.getColumns() returning
       different value for precision in TEXT/BLOB types.
-      
+
     - Allow ignoring of warning for 'non transactional tables' during
       rollback (compliance/usability) by setting 'ignoreNonTxTables'
       property to 'true'.
-      
+
     - Fixed SQLExceptions getting swallowed on initial connect.
-    
+
     - Fixed Statement.setMaxRows() to stop sending 'LIMIT' type queries
       when not needed (performance)
-      
+
     - Clean up Statement query/method mismatch tests (i.e. INSERT not
       allowed with .executeQuery()).
-      
+
     - More checks added in ResultSet traversal method to catch
       when in closed state.
-      
+
     - Fixed ResultSetMetaData.isWritable() to return correct value.
-    
-    - Add 'window' of different NULL sorting behavior to 
+
+    - Add 'window' of different NULL sorting behavior to
       DBMD.nullsAreSortedAtStart (4.0.2 to 4.0.10, true, otherwise,
       no).
-      
-    - Implemented Blob.setBytes(). You still need to pass the 
+
+    - Implemented Blob.setBytes(). You still need to pass the
       resultant Blob back into an updatable ResultSet or
       PreparedStatement to persist the changes, as MySQL does
       not support 'locators'.
-      
+
     - Backported 4.1 charset field info changes from Connector/J 3.1
-      
+
 01-22-03 - Version 3.0.5-gamma
 
     - Fixed Buffer.fastSkipLenString() causing ArrayIndexOutOfBounds
       exceptions with some queries when unpacking fields.
-      
+
     - Implemented an empty TypeMap for Connection.getTypeMap() so that
       some third-party apps work with MySQL (IBM WebSphere 5.0 Connection
       pool).
-      
+
     - Added missing LONGTEXT type to DBMD.getColumns().
-    
-    - Retrieve TX_ISOLATION from database for 
-      Connection.getTransactionIsolation() when the MySQL version 
+
+    - Retrieve TX_ISOLATION from database for
+      Connection.getTransactionIsolation() when the MySQL version
       supports it, instead of an instance variable.
-      
+
     - Quote table names in DatabaseMetaData.getColumns(),
       getPrimaryKeys(), getIndexInfo(), getBestRowIdentifier()
-      
+
     - Greatly reduce memory required for setBinaryStream() in
       PreparedStatements.
-      
+
     - Fixed ResultSet.isBeforeFirst() for empty result sets.
-    
+
     - Added update options for foreign key metadata.
-    
-      
+
+
 01-06-03 - Version 3.0.4-gamma
 
-    - Added quoted identifiers to database names for 
+    - Added quoted identifiers to database names for
       Connection.setCatalog.
-      
+
     - Added support for quoted identifiers in PreparedStatement
       parser.
-      
-    - Streamlined character conversion and byte[] handling in 
+
+    - Streamlined character conversion and byte[] handling in
       PreparedStatements for setByte().
-      
-    - Reduce memory footprint of PreparedStatements by sharing 
+
+    - Reduce memory footprint of PreparedStatements by sharing
       outbound packet with MysqlIO.
-      
+
     - Added 'strictUpdates' property to allow control of amount
       of checking for 'correctness' of updatable result sets. Set this
       to 'false' if you want faster updatable result sets and you know
       that you create them from SELECTs on tables with primary keys and
       that you have selected all primary keys in your query.
-      
-    - Added support for 4.0.8-style large packets. 
-    
+
+    - Added support for 4.0.8-style large packets.
+
     - Fixed PreparedStatement.executeBatch() parameter overwriting.
-	  
+
 12-17-02 - Version 3.0.3-dev
 
     - Changed charsToByte in SingleByteCharConverter to be non-static
-    
+
     - Changed SingleByteCharConverter to use lazy initialization of each
       converter.
-      
+
     - Fixed charset handling in Fields.java
-    
+
     - Implemented Connection.nativeSQL()
-    
+
     - More robust escape tokenizer -- recognize '--' comments, and allow
       nested escape sequences (see testsuite.EscapeProcessingTest)
-      
-    - DBMD.getImported/ExportedKeys() now handles multiple foreign keys 
+
+    - DBMD.getImported/ExportedKeys() now handles multiple foreign keys
       per table.
-      
-    - Fixed ResultSetMetaData.getPrecision() returning incorrect values 
+
+    - Fixed ResultSetMetaData.getPrecision() returning incorrect values
       for some floating point types.
-      
-    - Fixed ResultSetMetaData.getColumnTypeName() returning BLOB for 
+
+    - Fixed ResultSetMetaData.getColumnTypeName() returning BLOB for
       TEXT and TEXT for BLOB types.
-      
+
     - Fixed Buffer.isLastDataPacket() for 4.1 and newer servers.
-    
-    - Added CLIENT_LONG_FLAG to be able to get more column flags 
+
+    - Added CLIENT_LONG_FLAG to be able to get more column flags
       (isAutoIncrement() being the most important)
-      
+
     - Because of above, implemented ResultSetMetaData.isAutoIncrement()
       to use Field.isAutoIncrement().
-      
+
     - Honor 'lower_case_table_names' when enabled in the server when
       doing table name comparisons in DatabaseMetaData methods.
-      
+
     - Some MySQL-4.1 protocol support (extended field info from selects)
-    
-    - Use non-aliased table/column names and database names to fullly 
-      qualify tables and columns in UpdatableResultSet (requires 
+
+    - Use non-aliased table/column names and database names to fullly
+      qualify tables and columns in UpdatableResultSet (requires
       MySQL-4.1 or newer)
-      
+
     - Allow user to alter behavior of Statement/
       PreparedStatement.executeBatch() via 'continueBatchOnError' property
       (defaults to 'true').
-      
-    - Check for connection closed in more Connection methods 
+
+    - Check for connection closed in more Connection methods
       (createStatement, prepareStatement, setTransactionIsolation,
       setAutoCommit).
-      
+
     - More robust implementation of updatable result sets. Checks that
       _all_ primary keys of the table have been selected.
-      
+
     - 'LOAD DATA LOCAL INFILE ...' now works, if your server is configured
-      to allow it. Can be turned off with the 'allowLoadLocalInfile' 
+      to allow it. Can be turned off with the 'allowLoadLocalInfile'
       property (see the README).
-      
+
     - Substitute '?' for unknown character conversions in single-byte
       character sets instead of '\0'.
-      
+
     - NamedPipeSocketFactory now works (only intended for Windows), see
       README for instructions.
-	  
+
 11-08-02 - Version 3.0.2-dev
 
-    - Fixed issue with updatable result sets and PreparedStatements not 
+    - Fixed issue with updatable result sets and PreparedStatements not
       working
-      
+
     - Fixed ResultSet.setFetchDirection(FETCH_UNKNOWN)
-    
-    - Fixed issue when calling Statement.setFetchSize() when using 
+
+    - Fixed issue when calling Statement.setFetchSize() when using
       arbitrary values
-      
+
     - Fixed incorrect conversion in ResultSet.getLong()
-    
+
     - Implemented ResultSet.updateBlob().
-    
-    - Removed duplicate code from UpdatableResultSet (it can be inherited 
+
+    - Removed duplicate code from UpdatableResultSet (it can be inherited
       from ResultSet, the extra code for each method to handle updatability
       I thought might someday be necessary has not been needed).
-      
-    - Fixed "UnsupportedEncodingException" thrown when "forcing" a 
+
+    - Fixed "UnsupportedEncodingException" thrown when "forcing" a
       character encoding via properties.
-      
+
     - Fixed various non-ASCII character encoding issues.
-    
-    - Added driver property 'useHostsInPrivileges'. Defaults to true. 
-      Affects whether or not '@hostname' will be used in 
+
+    - Added driver property 'useHostsInPrivileges'. Defaults to true.
+      Affects whether or not '@hostname' will be used in
       DBMD.getColumn/TablePrivileges.
-      
+
     - All DBMD result set columns describing schemas now return NULL
       to be more compliant with the behavior of other JDBC drivers
       for other databases (MySQL does not support schemas).
-      
+
     - Added SSL support. See README for information on how to use it.
-    
+
     - Properly restore connection properties when autoReconnecting
       or failing-over, including autoCommit state, and isolation level.
-      
+
     - Use 'SHOW CREATE TABLE' when possible for determining foreign key
       information for DatabaseMetaData...also allows cascade options for
       DELETE information to be returned
-      
+
     - Escape 0x5c character in strings for the SJIS charset.
-    
+
     - Fixed start position off-by-1 error in Clob.getSubString()
-    
+
     - Implemented Clob.truncate()
-    
+
     - Implemented Clob.setString()
-    
+
     - Implemented Clob.setAsciiStream()
-    
+
     - Implemented Clob.setCharacterStream()
-    
+
     - Added com.mysql.jdbc.MiniAdmin class, which allows you to send
       'shutdown' command to MySQL server...Intended to be used when 'embedding'
       Java and MySQL server together in an end-user application.
-      
+
     - Added 'connectTimeout' parameter that allows users of JDK-1.4 and newer
       to specify a maxium time to wait to establish a connection.
-      
-    - Failover and autoReconnect only work when the connection is in a 
+
+    - Failover and autoReconnect only work when the connection is in a
       autoCommit(false) state, in order to stay transaction safe
-      
+
     - Added 'queriesBeforeRetryMaster' property that specifies how many
-      queries to issue when failed over before attempting to reconnect 
+      queries to issue when failed over before attempting to reconnect
       to the master (defaults to 50)
-      
+
     - Fixed DBMD.supportsResultSetConcurrency() so that it returns true
       for ResultSet.TYPE_SCROLL_INSENSITIVE and ResultSet.CONCUR_READ_ONLY or
       ResultSet.CONCUR_UPDATABLE
-      
+
     - Fixed ResultSet.isLast() for empty result sets (should return false).
-    
+
     - PreparedStatement now honors stream lengths in setBinary/Ascii/Character
-      Stream() unless you set the connection property 
+      Stream() unless you set the connection property
       'useStreamLengthsInPrepStmts' to 'false'.
-      
+
     - Removed some not-needed temporary object creation by using Strings
       smarter in EscapeProcessor, Connection and DatabaseMetaData classes.
-         	
+
 09-21-02 - Version 3.0.1-dev
-	
+
     - Fixed ResultSet.getRow() off-by-one bug.
-    
+
     - Fixed RowDataStatic.getAt() off-by-one bug.
-    
+
     - Added limited Clob functionality (ResultSet.getClob(),
-      PreparedStatemtent.setClob(), 
+      PreparedStatemtent.setClob(),
       PreparedStatement.setObject(Clob).
-      
+
     - Added socketTimeout parameter to URL.
-    
+
     - Connection.isClosed() no longer "pings" the server.
-    
+
     - Connection.close() issues rollback() when getAutoCommit() == false
-    
+
     - Added "paranoid" parameter...sanitizes error messages removing
       "sensitive" information from them (i.e. hostnames, ports,
       usernames, etc.), as well as clearing "sensitive" data structures
       when possible.
-      
+
     - Fixed ResultSetMetaData.isSigned() for TINYINT and BIGINT.
-    
+
     - Charsets now automatically detected. Optimized code for single-byte
       character set conversion.
-      
+
     - Implemented ResultSet.getCharacterStream()
-    
+
     - Added "LOCAL TEMPORARY" to table types in DatabaseMetaData.getTableTypes()
-    
+
     - Massive code clean-up to follow Java coding conventions (the time had come)
-  
-	
+
+
 07-31-02 - Version 3.0.0-dev
 
     - !!! LICENSE CHANGE !!! The driver is now GPL. If you need
       non-GPL licenses, please contact me <mark at mysql.com>
-      
-    - JDBC-3.0 functionality including 
+
+    - JDBC-3.0 functionality including
       Statement/PreparedStatement.getGeneratedKeys() and
       ResultSet.getURL()
-      
+
     - Performance enchancements - driver is now 50-100% faster
       in most situations, and creates fewer temporary objects
-      
+
     - Repackaging...new driver name is "com.mysql.jdbc.Driver",
-      old name still works, though (the driver is now provided 
+      old name still works, though (the driver is now provided
       by MySQL-AB)
-      
+
     - Better checking for closed connections in Statement
       and PreparedStatement.
-      
+
     - Support for streaming (row-by-row) result sets (see README)
       Thanks to Doron.
-      
+
     - Support for large packets (new addition to MySQL-4.0 protocol),
       see README for more information.
-      
+
     - JDBC Compliance -- Passes all tests besides stored procedure tests
-    
-    
+
+
     - Fix and sort primary key names in DBMetaData (SF bugs 582086 and 582086)
-    
+
     - Float types now reported as java.sql.Types.FLOAT (SF bug 579573)
-    
+
     - ResultSet.getTimestamp() now works for DATE types (SF bug 559134)
-    
+
     - ResultSet.getDate/Time/Timestamp now recognizes all forms of invalid
       values that have been set to all zeroes by MySQL (SF bug 586058)
-      
+
     - Testsuite now uses Junit (which you can get from www.junit.org)
-    
+
     - The driver now only works with JDK-1.2 or newer.
-    
+
     - Added multi-host failover support (see README)
-    
+
     - General source-code cleanup.
-    
+
     - Overall speed improvements via controlling transient object
       creation in MysqlIO class when reading packets
-      
-    - Performance improvements in  string handling and field 
+
+    - Performance improvements in  string handling and field
       metadata creation (lazily instantiated) contributed by
       Alex Twisleton-Wykeham-Fiennes
-      
-    
+
+
 05-16-02 - Version 2.0.14
 
     - More code cleanup
-    
+
     - PreparedStatement now releases resources on .close() (SF bug 553268)
-    
+
     - Quoted identifiers not used if server version does not support them. Also,
-      if server started with --ansi or --sql-mode=ANSI_QUOTES then '"' will be 
+      if server started with --ansi or --sql-mode=ANSI_QUOTES then '"' will be
       used as an identifier quote, otherwise '`' will be used.
-      
+
     - ResultSet.getDouble() now uses code built into JDK to be more precise (but slower)
-    
+
     - LogicalHandle.isClosed() calls through to physical connection
-    
-    - Added SQL profiling (to STDERR). Set "profileSql=true" in your JDBC url. 
+
+    - Added SQL profiling (to STDERR). Set "profileSql=true" in your JDBC url.
       See README for more information.
-      
+
     - Fixed typo for relaxAutoCommit parameter.
-	
+
 04-24-02 - Version 2.0.13
 
     - More code cleanup.
-    
+
     - Fixed unicode chars being read incorrectly (SF bug 541088)
-    
+
     - Faster blob escaping for PrepStmt
-    
+
     - Added set/getPortNumber() to DataSource(s) (SF bug 548167)
-    
+
     - Added setURL() to MySQLXADataSource (SF bug 546019)
-    
+
     - PreparedStatement.toString() fixed (SF bug 534026)
-    
+
     - ResultSetMetaData.getColumnClassName() now implemented
-    
+
     - Rudimentary version of Statement.getGeneratedKeys() from JDBC-3.0
       now implemented (you need to be using JDK-1.4 for this to work, I
       believe)
-	  
+
     - DBMetaData.getIndexInfo() - bad PAGES fixed (SF BUG 542201)
-	
+
 04-07-02 - Version 2.0.12
 
-    - General code cleanup. 
-    
+    - General code cleanup.
+
     - Added getIdleFor() method to Connection and MysqlLogicalHandle.
-    
+
     - Relaxed synchronization in all classes, should fix 520615 and 520393.
-    
+
     - Added getTable/ColumnPrivileges() to DBMD (fixes 484502).
-    
+
     - Added new types to getTypeInfo(), fixed existing types thanks to
       Al Davis and Kid Kalanon.
-      
+
     - Added support for BIT types (51870) to PreparedStatement.
-    
+
     - Fixed getRow() bug (527165) in ResultSet
-    
+
     - Fixes for ResultSet updatability in PreparedStatement.
     - Fixed timezone off by 1-hour bug in PreparedStatement (538286, 528785).
-    
-    - ResultSet: Fixed updatability (values being set to null 
+
+    - ResultSet: Fixed updatability (values being set to null
       if not updated).
-      
-    - DataSources - fixed setUrl bug (511614, 525565), 
+
+    - DataSources - fixed setUrl bug (511614, 525565),
       wrong datasource class name (532816, 528767)
-      
+
     - Added identifier quoting to all DatabaseMetaData methods
       that need them (should fix 518108)
-      
+
     - Added support for YEAR type (533556)
-    
+
     - ResultSet.insertRow() should now detect auto_increment fields
       in most cases and use that value in the new row. This detection
       will not work in multi-valued keys, however, due to the fact that
       the MySQL protocol does not return this information.
-      
+
     - ResultSet.refreshRow() implemented.
-    
+
     - Fixed testsuite.Traversal afterLast() bug, thanks to Igor Lastric.
-	
+
 01-27-02 - Version 2.0.11
 
-    - Fixed missing DELETE_RULE value in 
+    - Fixed missing DELETE_RULE value in
       DBMD.getImported/ExportedKeys() and getCrossReference().
-      
+
     - Full synchronization of Statement.java.
-    
+
     - More changes to fix "Unexpected end of input stream"
       errors when reading BLOBs. This should be the last fix.
-       
+
 01-24-02 - Version 2.0.10
 
-     - Fixed spurious "Unexpected end of input stream" errors in 
+     - Fixed spurious "Unexpected end of input stream" errors in
        MysqlIO (bug 507456).
-       
-     - Fixed null-pointer-exceptions when using 
+
+     - Fixed null-pointer-exceptions when using
        MysqlConnectionPoolDataSource with Websphere 4 (bug 505839).
-       
+
 01-13-02 - Version 2.0.9
 
-     - Ant build was corrupting included jar files, fixed 
+     - Ant build was corrupting included jar files, fixed
        (bug 487669).
-       
-     - Fixed extra memory allocation in MysqlIO.readPacket() 
+
+     - Fixed extra memory allocation in MysqlIO.readPacket()
        (bug 488663).
-       
+
      - Implementation of DatabaseMetaData.getExported/ImportedKeys() and
        getCrossReference().
-       
+
      - Full synchronization on methods modifying instance and class-shared
        references, driver should be entirely thread-safe now (please
        let me know if you have problems)
-       
+
      - DataSource implementations moved to org.gjt.mm.mysql.jdbc2.optional
        package, and (initial) implementations of PooledConnectionDataSource
-       and XADataSource are in place (thanks to Todd Wolff for the 
-       implementation and testing of PooledConnectionDataSource with 
+       and XADataSource are in place (thanks to Todd Wolff for the
+       implementation and testing of PooledConnectionDataSource with
        IBM WebSphere 4).
-       
+
      - Added detection of network connection being closed when reading packets
        (thanks to Todd Lizambri).
-       
+
      - Fixed quoting error with escape processor (bug 486265).
-     
+
      - Report batch update support through DatabaseMetaData (bug 495101).
-     
-     - Fixed off-by-one-hour error in PreparedStatement.setTimestamp() 
+
+     - Fixed off-by-one-hour error in PreparedStatement.setTimestamp()
        (bug 491577).
-       
+
      - Removed concatenation support from driver (the '||' operator),
        as older versions of VisualAge seem to be the only thing that
        use it, and it conflicts with the logical '||' operator. You will
-       need to start mysqld with the "--ansi" flag to use the '||' 
+       need to start mysqld with the "--ansi" flag to use the '||'
        operator as concatenation (bug 491680)
-       
+
      - Fixed casting bug in PreparedStatement (bug 488663).
-     
+
 11-25-01 - Version 2.0.8
 
-     - Batch updates now supported (thanks to some inspiration 
+     - Batch updates now supported (thanks to some inspiration
        from Daniel Rall).
-       
+
      - XADataSource/ConnectionPoolDataSource code (experimental)
-     
+
      - PreparedStatement.setAnyNumericType() now handles positive
        exponents correctly (adds "+" so MySQL can understand it).
-       
+
      - DatabaseMetaData.getPrimaryKeys() and getBestRowIdentifier()
-       are now more robust in identifying primary keys (matches 
+       are now more robust in identifying primary keys (matches
        regardless of case or abbreviation/full spelling of Primary Key
        in Key_type column).
-       
+
 10-24-01 - Version 2.0.7
 
      - PreparedStatement.setCharacterStream() now implemented
-         
+
      - Fixed dangling socket problem when in high availability
        (autoReconnect=true) mode, and finalizer for Connection will
        close any dangling sockets on GC.
-       
+
      - Fixed ResultSetMetaData.getPrecision() returning one
        less than actual on newer versions of MySQL.
-       
+
      - ResultSet.getBlob() now returns null if column value
        was null.
-       
+
      - Character sets read from database if useUnicode=true
-       and characterEncoding is not set. (thanks to 
+       and characterEncoding is not set. (thanks to
        Dmitry Vereshchagin)
-       
-     - Initial transaction isolation level read from 
+
+     - Initial transaction isolation level read from
        database (if avaialable) (thanks to Dmitry Vereshchagin)
-       
+
      - Fixed DatabaseMetaData.supportsTransactions(), and
        supportsTransactionIsolationLevel() and getTypeInfo()
        SQL_DATETIME_SUB and SQL_DATA_TYPE fields not being
        readable.
-        
+
      - Fixed PreparedStatement generating SQL that would end
        up with syntax errors for some queries.
-       
+
      - Fixed ResultSet.isAfterLast() always returning false.
-         
+
      - Fixed timezone issue in PreparedStatement.setTimestamp()
        (thanks to Erik Olofsson)
-         
+
      - Captialize type names when "captializeTypeNames=true"
        is passed in URL or properties (for WebObjects, thanks
        to Anjo Krank)
-         
+
      - Updatable result sets now correctly handle NULL
        values in fields.
-       
+
      - PreparedStatement.setDouble() now uses full-precision
        doubles (reverting a fix made earlier to truncate them).
-       
+
      - PreparedStatement.setBoolean() will use 1/0 for values
        if your MySQL Version >= 3.21.23.
 
 06-16-01 - Version 2.0.6
 
 Fixed PreparedStatement parameter checking
-	 
+
      - Fixed case-sensitive column names in ResultSet.java
 
 06-13-01 - Version 2.0.5
@@ -2568,7 +3320,7 @@
 
      - Fixed ResultSetMetaData.getColumnTypeName for TEXT/BLOB
 
-     - Fixed ArrayIndexOutOfBounds when sending large BLOB queries 
+     - Fixed ArrayIndexOutOfBounds when sending large BLOB queries
        (Max size packet was not being set)
 
      - Added ISOLATION level support to Connection.setIsolationLevel()
@@ -2608,12 +3360,12 @@
 
      - Fixed off-by-one error in java.sql.Blob implementation code.
 
-     - Added "ultraDevHack" URL parameter, set to "true" to allow 
+     - Added "ultraDevHack" URL parameter, set to "true" to allow
        (broken) Macromedia UltraDev to use the driver.
 
 04-06-00 - Version 2.0.1
 
-     - Fixed RSMD.isWritable() returning wrong value. 
+     - Fixed RSMD.isWritable() returning wrong value.
        Thanks to Moritz Maass.
 
      - Cleaned up exception handling when driver connects
@@ -2648,14 +3400,14 @@
        to John Baker.
 
      - Fixed ResultSet to return correct row numbers
-     
+
      - Statement.getUpdateCount() now returns rows matched,
        instead of rows actually updated, which is more SQL-92
        like.
 
-10-29-99 
+10-29-99
 
-     - Statement/PreparedStatement.getMoreResults() bug fixed. 
+     - Statement/PreparedStatement.getMoreResults() bug fixed.
        Thanks to Noel J. Bergman.
 
      - Added Short as a type to PreparedStatement.setObject().
@@ -2690,12 +3442,12 @@
 
      - Better Documentation (in progress), in doc/mm.doc/book1.html
 
-     - DBMD now allows null for a column name pattern (not in 
+     - DBMD now allows null for a column name pattern (not in
        spec), which it changes to '%'.
 
      - DBMD now has correct types/lengths for getXXX().
 
-     - ResultSet.getDate(), getTime(), and getTimestamp() fixes. 
+     - ResultSet.getDate(), getTime(), and getTimestamp() fixes.
        (contributed by Alan Wilken)
 
      - EscapeProcessor now handles \{ \} and { or } inside quotes
@@ -2720,7 +3472,7 @@
        EVERY JVM must support "ISO8859_1", but they don't.
 
      - Fixed Connection to use the platform character encoding
-       instead of "ISO8859_1" if one isn't explicitly set. This 
+       instead of "ISO8859_1" if one isn't explicitly set. This
        fixes problems people were having loading the character-
        converter classes that didn't always exist (JVM bug).
        (thanks to Fritz Elfert for pointing out this problem)
@@ -2762,7 +3514,7 @@
 03-24-99 - Version 1.1i
 
      - Fixed Timestamps for PreparedStatements
-    
+
      - Fixed null pointer exceptions in RSMD and RS
 
      - Re-compiled with jikes for valid class files (thanks ms!)
@@ -2771,50 +3523,50 @@
 
      - Fixed escape processor to deal with un-matched { and }
        (thanks to Craig Coles)
-       
+
      - Fixed escape processor to create more portable (between
        DATETIME and TIMESTAMP types) representations so that
        it will work with BETWEEN clauses.
        (thanks to Craig Longman)
-       
+
      - MysqlIO.quit() now closes the socket connection. Before,
        after many failed connections some OS's would run out
        of file descriptors. (thanks to Michael Brinkman)
-       
+
      - Fixed NullPointerException in Driver.getPropertyInfo.
        (thanks to Dave Potts)
-        
+
      - Fixes to MysqlDefs to allow all *text fields to be
        retrieved as Strings.
        (thanks to Chris at Leverage)
-       
+
      - Fixed setDouble in PreparedStatement for large numbers
        to avoid sending scientific notation to the database.
        (thanks to J.S. Ferguson)
-       
+
      - Fixed getScale() and getPrecision() in RSMD.
        (contrib'd by James Klicman)
-       
+
      - Fixed getObject() when field was DECIMAL or NUMERIC
        (thanks to Bert Hobbs)
-       
+
      - DBMD.getTables() bombed when passed a null table-name
        pattern. Fixed. (thanks to Richard Lobb)
-       
+
      - Added check for "client not authorized" errors during
        connect. (thanks to Hannes Wallnoefer)
-       
+
 02-19-99 - Version 1.1g
 
      - Result set rows are now byte arrays. Blobs and Unicode
        work bidriectonally now. The useUnicode and encoding
        options are implemented now.
-       
+
      - Fixes to PreparedStatement to send binary set by
        setXXXStream to be sent un-touched to the MySQL server.
-       
+
      - Fixes to getDriverPropertyInfo().
-       
+
 12-31-98 - Version 1.1f
 
      - Changed all ResultSet fields to Strings, this should allow
@@ -2836,12 +3588,12 @@
        introduced in 1.1d when changing to all byte-arrays for
        result sets. (Pointed out by Samo Login)
 
-11-03-98 - Version 1.1b 
+11-03-98 - Version 1.1b
 
      - Fixes to DatabaseMetaData to allow both IBM VA and J-Builder
        to work. Let me know how it goes. (thanks to Jac Kersing)
 
-     - Fix to ResultSet.getBoolean() for NULL strings 
+     - Fix to ResultSet.getBoolean() for NULL strings
        (thanks to Barry Lagerweij)
 
      - Beginning of code cleanup, and formatting. Getting ready
@@ -2850,13 +3602,13 @@
      - Added "final" modifier to critical sections in MysqlIO and
        Buffer to allow compiler to inline methods for speed.
 
-9-29-98 
+9-29-98
 
      - If object references passed to setXXX() in PreparedStatement are
        null, setNull() is automatically called for you. (Thanks for the
        suggestion goes to Erik Ostrom)
 
-     - setObject() in PreparedStatement will now attempt to write a 
+     - setObject() in PreparedStatement will now attempt to write a
        serialized  representation of the object to the database for
        objects of Types.OTHER and objects of unknown type.
 
@@ -2868,7 +3620,7 @@
 
      - Got rid of "ugly hack" in MysqlIO.nextRow(). Rather than
        catch an exception, Buffer.isLastDataPacket() was fixed.
-          
+
      - Connection.getCatalog() and Connection.setCatalog()
        should work now.
 
@@ -2880,87 +3632,87 @@
        to "ping" the database before each query, it is
        turned off by default. To use it, pass in "autoReconnect=true"
        in the connection URL. You may also change the number of
-       reconnect tries, and the initial timeout value via 
-       "maxReconnects=n" (default 3) and "initialTimeout=n" 
-       (seconds, default 2) parameters. The timeout is an 
-       exponential backoff type of timeout, e.g. if you have initial 
+       reconnect tries, and the initial timeout value via
+       "maxReconnects=n" (default 3) and "initialTimeout=n"
+       (seconds, default 2) parameters. The timeout is an
+       exponential backoff type of timeout, e.g. if you have initial
        timeout of 2 seconds, and maxReconnects of 3, then the driver
        will timeout 2 seconds, 4 seconds, then 16 seconds between each
        re-connection attempt.
 
-8-24-98 - Version 1.0 
+8-24-98 - Version 1.0
 
      - Fixed handling of blob data in Buffer.java
-	
+
      - Fixed bug with authentication packet being
        sized too small.
 
      - The JDBC Driver is now under the LPGL
-	
-8-14-98 - 
 
+8-14-98 -
+
      - Fixed Buffer.readLenString() to correctly
           read data for BLOBS.
-          
+
      - Fixed PreparedStatement.stringToStream to
           correctly read data for BLOBS.
-          
+
      - Fixed PreparedStatement.setDate() to not
        add a day.
        (above fixes thanks to Vincent Partington)
-          
+
      - Added URL parameter parsing (?user=... etc).
-      
 
+
 8-04-98 - Version 0.9d
 
      - Big news! New package name. Tim Endres from ICE
        Engineering is starting a new source tree for
        GNU GPL'd Java software. He's graciously given
-       me the org.gjt.mm package directory to use, so now 
+       me the org.gjt.mm package directory to use, so now
        the driver is in the org.gjt.mm.mysql package scheme.
        I'm "legal" now. Look for more information on Tim's
        project soon.
-          
+
      - Now using dynamically sized packets to reduce
        memory usage when sending commands to the DB.
-          
+
      - Small fixes to getTypeInfo() for parameters, etc.
-          
+
      - DatabaseMetaData is now fully implemented. Let me
        know if these drivers work with the various IDEs
        out there. I've heard that they're working with
        JBuilder right now.
-          
+
      - Added JavaDoc documentation to the package.
-          
+
      - Package now available in .zip or .tar.gz.
-          
+
 7-28-98 - Version 0.9
 
-     - Implemented getTypeInfo(). 
+     - Implemented getTypeInfo().
        Connection.rollback() now throws an SQLException
        per the JDBC spec.
-          
+
      - Added PreparedStatement that supports all JDBC API
        methods for PreparedStatement including InputStreams.
        Please check this out and let me know if anything is
        broken.
-          
+
      - Fixed a bug in ResultSet that would break some
        queries that only returned 1 row.
-          
-     - Fixed bugs in DatabaseMetaData.getTables(), 
+
+     - Fixed bugs in DatabaseMetaData.getTables(),
        DatabaseMetaData.getColumns() and
        DatabaseMetaData.getCatalogs().
-          
+
      - Added functionality to Statement that allows
        executeUpdate() to store values for IDs that are
        automatically generated for AUTO_INCREMENT fields.
        Basically, after an executeUpdate(), look at the
        SQLWarnings for warnings like "LAST_INSERTED_ID =
        'some number', COMMAND = 'your SQL query'".
-          
+
        If you are using AUTO_INCREMENT fields in your
        tables and are executing a lot of executeUpdate()s
        on one Statement, be sure to clearWarnings() every
@@ -2970,7 +3722,7 @@
 
      - Split MysqlIO and Buffer to separate classes. Some
        ClassLoaders gave an IllegalAccess error for some
-       fields in those two classes. Now mm.mysql works in 
+       fields in those two classes. Now mm.mysql works in
        applets and all classloaders.
 
        Thanks to Joe Ennis <jce at mail.boone.com> for pointing
@@ -2981,21 +3733,21 @@
      - Fixed DatabaseMetadata problems in getColumns() and
        bug in switch statement in the Field constructor.
 
-       Thanks to Costin Manolache <costin at tdiinc.com> for 
+       Thanks to Costin Manolache <costin at tdiinc.com> for
        pointing these out.
 
 5-21-98 - Version 0.6
 
-     - Incorporated efficiency changes from 
+     - Incorporated efficiency changes from
        Richard Swift <Richard.Swift at kanatek.ca> in
        MysqlIO.java and ResultSet.java
 
      - We're now 15% faster than gwe's driver.
 
      - Started working on DatabaseMetaData.
-          
+
        The following methods are implemented:
-        * getTables() 
+        * getTables()
         * getTableTypes()
         * getColumns
         * getCatalogs()

Modified: trunk/mysql-connector-java/EXCEPTIONS-CONNECTOR-J
===================================================================
--- trunk/mysql-connector-java/EXCEPTIONS-CONNECTOR-J	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/EXCEPTIONS-CONNECTOR-J	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,99 +1,119 @@
 MySQL FLOSS License Exception
 
-The MySQL AB Exception for Free/Libre and Open Source Software-only
-Applications Using MySQL Client Libraries (the "FLOSS Exception").
+The MySQL AB Exception for Free/Libre and Open Source
+Software-only Applications Using MySQL Client Libraries (the
+"FLOSS Exception").
 
+Version 0.6, 7 March 2007
 
 Exception Intent
-We want specified Free/Libre and Open Source Software ("FLOSS") applications
-to be able to use specified GPL-licensed MySQL client libraries (the
-"Program") despite the fact that not all FLOSS licenses are compatible with
-version 2 of the GNU General Public License (the "GPL"). 
 
+We want specified Free/Libre and Open Source Software (``FLOSS'')
+applications to be able to use specified GPL-licensed MySQL client
+libraries (the ``Program'') despite the fact that not all FLOSS
+licenses are compatible with version 2 of the GNU General Public
+License (the ``GPL'').
 
 Legal Terms and Conditions
-As a special exception to the terms and conditions of version 2.0 of the
-GPL:
 
-0. You are free to distribute a Derivative Work that is formed entirely from
-   the Program and one or more works (each, a "FLOSS Work") licensed under
-   one or more of the licenses listed below in section 1, as long as:
+As a special exception to the terms and conditions of version 2.0
+of the GPL:
 
-    a. You obey the GPL in all respects for the Program and the Derivative
-       Work, except for identifiable sections of the Derivative Work which
-       are not derived from the Program, and which can reasonably be
-       considered independent and separate works in themselves, 
+ 1. You are free to distribute a Derivative Work that is formed
+    entirely from the Program and one or more works (each, a
+    "FLOSS Work") licensed under one or more of the licenses
+    listed below in section 1, as long as:
+      a. You obey the GPL in all respects for the Program and the
+         Derivative Work, except for identifiable sections of the
+         Derivative Work which are not derived from the Program,
+         and which can reasonably be considered independent and
+         separate works in themselves,
+      b. all identifiable sections of the Derivative Work which
+         are not derived from the Program, and which can
+         reasonably be considered independent and separate works
+         in themselves,
+           i. are distributed subject to one of the FLOSS licenses
+              listed below, and
+          ii. the object code or executable form of those sections
+              are accompanied by the complete corresponding
+              machine-readable source code for those sections on
+              the same medium and under the same FLOSS license as
+              the corresponding object code or executable forms of
+              those sections, and
+      c. any works which are aggregated with the Program or with a
+         Derivative Work on a volume of a storage or distribution
+         medium in accordance with the GPL, can reasonably be
+         considered independent and separate works in themselves
+         which are not derivatives of either the Program, a
+         Derivative Work or a FLOSS Work.
+    If the above conditions are not met, then the Program may only
+    be copied, modified, distributed or used under the terms and
+    conditions of the GPL or another valid licensing option from
+    MySQL AB.
 
-    b. all identifiable sections of the Derivative Work which are not
-       derived from the Program, and which can reasonably be considered
-       independent and separate works in themselves,
+ 2. FLOSS License List
 
-        (i) are distributed subject to one of the FLOSS licenses listed
-            below, and
-           
-       (ii) the object code or executable form of those sections are
-            accompanied by the complete corresponding machine-readable
-            source code for those sections on the same medium and under the
-            same FLOSS license as the corresponding object code or
-            executable forms of those sections, and
+License name Version(s)/Copyright Date
+Academic Free License 2.0
+Apache Software License 1.0/1.1/2.0
+Apple Public Source License 2.0
+Artistic license From Perl 5.8.0
+BSD license "July 22 1999"
+Common Development and Distribution License (CDDL) 1.0
+Common Public License 1.0
+Eclipse Public License 1.0
+GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1
+Jabber Open Source License 1.0
+MIT license (As listed in file MIT-License.txt) ---
+Mozilla Public License (MPL) 1.0/1.1
+Open Software License 2.0
+OpenSSL license (with original SSLeay license) "2003" ("1998")
+PHP License 3.0
+Python license (CNRI Python License) ---
+Python Software Foundation License 2.1.1
+Sleepycat License "1999"
+University of Illinois/NCSA Open Source License ---
+W3C License "2001"
+X11 License "2001"
+Zlib/libpng License ---
+Zope Public License 2.0
 
-    c. any works which are aggregated with the Program or with a Derivative
-       Work on a volume of a storage or distribution medium in accordance
-       with the GPL, can reasonably be considered independent and separate
-       works in themselves which are not derivatives of either the Program,
-       a Derivative Work or a FLOSS Work.
+    Due to the many variants of some of the above licenses, we
+    require that any version follow the 2003 version of the Free
+    Software Foundation's Free Software Definition
+    (http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of
+    the Open Source Definition by the Open Source Initiative
+    (http://www.opensource.org/docs/definition.php).
 
-If the above conditions are not met, then the Program may only be copied,
-modified, distributed or used under the terms and conditions of the GPL or
-another valid licensing option from MySQL AB.
+ 3. Definitions
 
-1. FLOSS License List
+      a. Terms used, but not defined, herein shall have the
+         meaning provided in the GPL.
+      b. Derivative Work means a derivative work under copyright
+         law.
 
-License name                                       Version(s)/Copyright Date
-Academic Free License                                                    2.0
-Apache Software License                                          1.0/1.1/2.0
-Apple Public Source License                                              2.0
-Artistic license                                             From Perl 5.8.0
-BSD license                                                   "July 22 1999"
-Common Public License                                                    1.0
-GNU Library or "Lesser" General Public License (LGPL)                2.0/2.1
-Jabber Open Source License                                               1.0
-MIT License (As listed in file MIT-License.txt)                            -
-Mozilla Public License (MPL)                                         1.0/1.1
-Open Software License                                                    2.0
-PHP License                                                              3.0
-Python license (CNRI Python License)                                       -
-Python Software Foundation License                                     2.1.1
-Sleepycat License                                                     "1999"
-W3C License                                                           "2001"
-X11 License                                                           "2001"
-Zlib/libpng License                                                        -
-Zope Public License                                                      2.0
+ 4. Applicability: This FLOSS Exception applies to all Programs
+    that contain a notice placed by MySQL AB saying that the
+    Program may be distributed under the terms of this FLOSS
+    Exception. If you create or distribute a work which is a
+    Derivative Work of both the Program and any other work
+    licensed under the GPL, then this FLOSS Exception is not
+    available for that work; thus, you must remove the FLOSS
+    Exception notice from that work and comply with the GPL in all
+    respects, including by retaining all GPL notices. You may
+    choose to redistribute a copy of the Program exclusively under
+    the terms of the GPL by removing the FLOSS Exception notice
+    from that copy of the Program, provided that the copy has
+    never been modified by you or any third party.
 
-Due to the many variants of some of the above licenses, we require that any
-version follow the 2003 version of the Free Software Foundation's Free
-Software Definition (http://www.gnu.org/philosophy/free-sw.html) or version
-1.9 of the Open Source Definition by the Open Source Initiative
-(http://www.opensource.org/docs/definition.php).
+Appendix A. Qualified Libraries and Packages
 
-2. Definitions
+The following is a non-exhaustive list of libraries and packages
+which are covered by the FLOSS License Exception. Please note that
+this appendix is provided merely as an additional service to
+specific FLOSS projects wishing to simplify licensing information
+for their users. Compliance with one of the licenses noted under
+the "FLOSS license list" section remains a prerequisite.
 
-   a. Terms used, but not defined, herein shall have the meaning provided in
-      the GPL.
-
-   b. Derivative Work means a derivative work under copyright law.
-
-3. Applicability
-
-This FLOSS Exception applies to all Programs that contain a notice placed by
-MySQL AB saying that the Program may be distributed under the terms of this
-FLOSS Exception.  If you create or distribute a work which is a Derivative
-Work of both the Program and any other work licensed under the GPL, then
-this FLOSS Exception is not available for that work; thus, you must remove
-the FLOSS Exception notice from that work and comply with the GPL in all
-respects, including by retaining all GPL notices.  You may choose to
-redistribute a copy of the Program exclusively under the terms of the GPL by
-removing the FLOSS Exception notice from that copy of the Program, provided
-that the copy has never been modified by you or any third party.  
-
-$Id: FLOSS-exception.txt,v 1.5 2004/07/15 15:24:19 z Exp $
+Package Name                  Qualifying License and Version
+Apache Portable Runtime (APR) Apache Software License 2.0
\ No newline at end of file

Modified: trunk/mysql-connector-java/build.xml
===================================================================
--- trunk/mysql-connector-java/build.xml	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/build.xml	2007-11-30 10:46:51 UTC (rev 4921)
@@ -21,12 +21,12 @@
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 -->
 
-<!-- version $Id: build.xml 5899 2006-10-18 20:47:34Z mmatthews $ -->
+<!-- version $Id: build.xml 6622 2007-10-05 19:05:36Z mmatthews $ -->
 
 <project name="MySQL Connector/J" default="dist" basedir=".">
 	<property name="major_version" value="5"/>
-	<property name="minor_version" value="0"/>
-	<property name="subminor_version" value="4"/>
+	<property name="minor_version" value="1"/>
+	<property name="subminor_version" value="5"/>
 	<property name="version_status" value=""/>
 
 	<property name="version" value="${major_version}.${minor_version}.${subminor_version}${version_status}"/>
@@ -39,9 +39,14 @@
 	<property name="distDir" value="./dist"/>
 	<property name="junit.results" value="${buildDir}/junit"/>
 	<property name="debug.enable" value="on" />
+	
+	<!-- Send class files to correct location if running in eclipse -->
+	
+	 <condition property="compiler.output" value="bin" else="${buildDir}/${fullProdName}">
+	    <isset property="eclipse.pdebuild.home" />
+	 </condition>
 
 	<!-- 
-	
 	     The following property is needed for building the docs. It's
 	     set to the defaults for my development environment, but can be passed
 	     on the command line to ant via -D switches. 
@@ -49,6 +54,15 @@
 
 	<property name="com.mysql.jdbc.docs.sourceDir" value="/home/mmatthew/work/docs/prebuilt"/>
 
+	<!-- 
+		The following properties are needed for finding JDK6, set to the defaults
+		for my development environment, but can be passed on the command line
+		to ant via -D switches
+	-->
+	
+	<property name="com.mysql.jdbc.java6.javac" value="C:\jvms\jdk1.6.0\bin\javac.exe" />
+	<property name="com.mysql.jdbc.java6.rtjar" value="C:\jvms\jdk1.6.0\jre\lib\rt.jar" />
+	
 	<taskdef resource="net/sf/antcontrib/antlib.xml">
 		<classpath>
 			<pathelement location="${sourceDir}/lib/ant-contrib.jar"/>
@@ -72,8 +86,19 @@
 	</path>
 
 	<target name="init" depends="-init-copy, -init-no-crypto">
-		<!-- Check that files needed to generate documentation are in place -->
-
+		<!-- If we're building under cruise control, update ourself -->
+		
+		<!--
+		<if>
+			<isset property="cctimestamp"/>
+			<then>
+				<exec executable="svn">
+					<arg line="up"/>
+				</exec>
+			</then>
+		</if>
+		-->
+		
 		<!-- We need the following for source distributions as there we
              can't dynamically alter the classpath, and not having this
 		     directory present causes the build to fail -->
@@ -100,11 +125,22 @@
 
 		<filter token="VERSION" value="${version}"/>
 
+		<mkdir dir="${buildDir}" />
+		
+		<exec executable="svn" 
+			output="${buildDir}/svn.properties" 
+			failifexecutionfails="false"
+			failonerror="false">
+			  <arg value="info" />
+		</exec>
+		    
+		<property prefix="svn" file="${buildDir}/svn.properties"/>
+		
 		<copy todir="${buildDir}/${fullProdName}" filtering="true">
 			<fileset dir="${sourceDir}/" excludes="**/CVS">
 				<patternset id="classjar" >
 					<exclude name="**/*.class*"/>
-					<exclude name="**/*.jar"/>
+					<exclude name="**/*.jar"/>	
 				</patternset>
 			</fileset>
 
@@ -114,6 +150,7 @@
 				<filter token="MYSQL_CJ_SUBMINOR_VERSION" value="${subminor_version}"/>
 				<filter token="MYSQL_CJ_VERSION_STATUS" value="${version_status}"/>
 				<filter token="MYSQL_CJ_VERSION" value="${version}"/>
+				<filter token="MYSQL_CJ_REVISION" value="${svn.Revision}" />
 				<filter token="MYSQL_CJ_FULL_PROD_NAME" value="${fullProdName}"/>
 			</filterset>
 		</copy>
@@ -145,9 +182,16 @@
 
 	<target name="full-package-no-sources" description="Builds driver, binary .jar file, docs and packages (.zip, .tar.gz) suitable for distribution that do _not_ contain sources"
 			depends="full-dist, -make-packages, -remove-sources, -create-archives"/>
+	
+    <!-- No trace build until we figure out what's going on with iajc on our
+         production build box 
+	<target name="full-dist" description="Builds driver, binary .jar file and docs, basically a distribution 'image'"
+			depends="-dist-trace, dist, -bundle-docs"/>
+			-->
+	
 
 	<target name="full-dist" description="Builds driver, binary .jar file and docs, basically a distribution 'image'"
-		depends="-dist-trace, dist, -bundle-docs"/>
+			depends="dist, -bundle-docs"/>
 
 	<target name="package" depends="dist, -make-packages, -create-archives"/>
 
@@ -182,29 +226,37 @@
 
 		<checksum file="${distDir}/${fullProdName}.zip" forceOverwrite="yes" fileext=".md5"/>
 		
-		<!-- Build a Maven bundle for upload to their repository -->
-		
-		<checksum file="${distDir}/${fullProdName}.zip" forceOverwrite="yes" fileext=".md5"/>
-		
-		<tempfile destdir="${buildDir}" prefix="maven-bundle-" property="mavenUploadDir" />
-		
-		<mkdir dir="${mavenUploadDir}" />
-		
-		<copy file="${buildDir}/${fullProdName}/${fullProdName}-bin.jar"
-		  	toFile="${mavenUploadDir}/${fullProdName}.jar" />
-		
-		<copy file="${buildDir}/${fullProdName}/doc/sources/pom.xml"
-			toDir="${mavenUploadDir}" filtering="true">
-		</copy>
-		
-		<copy file="./COPYING"
-			toDir="${mavenUploadDir}" />
-		
-		<copy file="./EXCEPTIONS-CONNECTOR-J"
+		<if>
+			<isset property="com.mysql.jdbc.commercialBuild"/>
+			<then>
+				<echo message="Not building maven bundle for commercial build" />
+			</then>
+			<else>
+				<!-- Build a Maven bundle for upload to their repository -->
+				
+				<checksum file="${distDir}/${fullProdName}.zip" forceOverwrite="yes" fileext=".md5"/>
+				
+				<tempfile destdir="${buildDir}" prefix="maven-bundle-" property="mavenUploadDir" />
+				
+				<mkdir dir="${mavenUploadDir}" />
+				
+				<copy file="${buildDir}/${fullProdName}/${fullProdName}-bin.jar"
+				  	toFile="${mavenUploadDir}/${fullProdName}.jar" />
+				
+				<copy file="${buildDir}/${fullProdName}/doc/sources/pom.xml"
+					toDir="${mavenUploadDir}" filtering="true">
+				</copy>
+				
+				<copy file="./COPYING"
 					toDir="${mavenUploadDir}" />
-		
-		<jar jarfile="${distDir}/${fullProdName}.bundle.jar" 
-					basedir="${mavenUploadDir}" />
+				
+				<copy file="./EXCEPTIONS-CONNECTOR-J"
+							toDir="${mavenUploadDir}" />
+				
+				<jar jarfile="${distDir}/${fullProdName}.bundle.jar" 
+							basedir="${mavenUploadDir}" />
+			</else>
+		</if>
 	</target>
 	
 	<target name="-make-packages" depends="dist">
@@ -266,7 +318,7 @@
 
 				<!-- Pull in the commercial license itself -->
 
-				<copy file="${sourceDir}/lib-nodist/MySQLEULA.txt" toDir="${packageDest}"/>
+				<copy file="${sourceDir}/lib-nodist/LICENSE.mysql" toDir="${packageDest}"/>
 
 				<loadfile property="commercialLicenseText"
 					srcFile="${sourceDir}/lib-nodist/commercialLicense.txt"/>
@@ -277,7 +329,7 @@
 				<fileset dir="${packageDest}" includes="**/*"/>
 				</replaceregexp>
 			</then>
-		</if>	
+		</if>
 	</target>
 
 	<target name="dist" depends="init, compile">
@@ -299,10 +351,14 @@
 
 			<section name="common">
 				<attribute name="Specification-Title" value="JDBC" />
+				<!--
+				<attribute name="Specification-Version" value="4.0" />
+				-->
 				<attribute name="Specification-Version" value="3.0" />
 				<attribute name="Specification-Vendor" value="Sun Microsystems Inc." />
 				<attribute name="Implementation-Title" value="MySQL Connector/J" />
 				<attribute name="Implementation-Version" value="${full.version}" />
+				<attribute name="Implementation-Vendor-Id" value="com.mysql" />
 				<attribute name="Implementation-Vendor" value="MySQL AB" />
 			</section>
 		</manifest>
@@ -364,6 +420,9 @@
 		<delete dir="${buildDir}/junit" />
 		<antcall target="test-multijvm" />
 		<antcall target="test-compliance-multijvm" />
+
+		<fail message="Tests failed. Check logs and/or reports in ${buildDir}/junit." 
+			if="tests.compliance.failed"/>
 	</target>
 
 	<!-- Runs compliance testsuite against multiple JVMs and server configs -->
@@ -424,10 +483,11 @@
 
 		<junit printSummary="yes" 
 			fork="on" 
+			forkmode="once"
 			jvm="${com.mysql.jdbc.testsuite.jvm}"
 			errorProperty="tests.failed"
 			failureProperty="tests.failed">
-			<jvmarg value="-Xmx256m" />
+			<jvmarg value="-Xmx128m" />
 
 			<!-- For java.sql.SavePoint on old JVMs -->
 
@@ -448,7 +508,7 @@
 
 			<formatter type="xml"/>
 
-			<batchtest fork="yes" todir="${junit.unitregress.results}" >
+			<batchtest todir="${junit.unitregress.results}" >
 				<fileset dir="${buildDir}/${fullProdName}">
 					<include name="**/*Test.java" />
 					<exclude name="**/perf/*.java"/>
@@ -539,10 +599,11 @@
 		<mkdir dir="${junit.compliance.results}"/>
 		<mkdir dir="${junit.compliance.results}/report"/>
 
-		<junit printsummary="yes" jvm="${com.mysql.jdbc.testsuite.jvm}"
+		<junit fork="on" forkmode="once"
+		    printsummary="yes" jvm="${com.mysql.jdbc.testsuite.jvm}"
 			errorProperty="tests.compliance.failed"
 			failureProperty="tests.compliance.failed">
-			<jvmarg value="-Xmx256m"/>
+			<jvmarg value="-Xmx128m"/>
 			
 			<!-- For java.sql.SavePoint on old JVMs -->
 				
@@ -564,21 +625,21 @@
 
 			<formatter type="xml"/>
 
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.BatchUpdateTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.ConnectionTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.DatabaseMetaDataTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.EscapeSyntaxTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.PreparedStatementTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.ResultSetMetadataTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.ResultSetTest"/>
-			<test fork="yes" todir="${junit.compliance.results}" 
+			<test todir="${junit.compliance.results}" 
 		    		name="com.mysql.jdbc.compliance.tests.StatementTest"/>
 		</junit>
 
@@ -602,7 +663,8 @@
 		
 	</target>
 
-	<target name="compile" depends="init, compile-driver, compile-testsuite, compile.integration">
+
+	<target name="compile" depends="init, compile-driver, compile.integration">
 	</target>
 
 	<target name="compile-trace" depends="init, compile-driver-trace">
@@ -610,17 +672,43 @@
 
 	<!-- Compiles the driver itself -->
 
-	<target name="compile-driver" depends="init, -clean-output">
-		<javac srcdir="${buildDir}/${fullProdName}" 
-			destdir="${buildDir}/${fullProdName}" 
+	<target name="compile-driver" depends="compile-driver-jdbc3" />
+	
+	<target name="compile-driver-jdbc3" depends="init, -clean-output">
+		<javac sourcepath="" srcdir="${buildDir}/${fullProdName}" 
+			destdir="${compiler.output}" 
 			deprecation="off" 
-			debug="${debug.enable}" 
-			excludes="testsuite/**, 
-			   com/mysql/jdbc/integration/**,
-			   com/mysql/jdbc/log/Log4JLogger.java">
+			debug="${debug.enable}">
+			<include name="**/*.java" />
+			<exclude name="testsuite/**" />
+			<exclude name="com/mysql/jdbc/integration/**" />
+			<exclude name="com/mysql/jdbc/log/Log4JLogger.java" />
+			<exclude name="**/JDBC4*.java" />
+			<exclude name="com/mysql/jdbc/exceptions/jdbc4/*" />
+			
 			<classpath refid="project.build.classpath" />
 		</javac>
 	</target>
+	
+	<target name="compile-driver-jdbc4" depends="compile-driver-jdbc3">
+		
+		<javac destdir="${compiler.output}" 
+			deprecation="off" 
+			debug="${debug.enable}"
+			fork="yes"
+			executable="${com.mysql.jdbc.java6.javac}"
+			compiler="modern"
+			source="1.5"
+			target="1.5"
+			sourcepath="" srcdir="${buildDir}/${fullProdName}"
+			bootclasspath="${com.mysql.jdbc.java6.rtjar}">
+			<include name="**/JDBC4*.java" />
+			<include name="com/mysql/jdbc/exceptions/jdbc4/*" />
+			
+			<classpath refid="project.build.classpath" />
+		</javac>
+		
+	</target>
 
 	<!-- Compiles a version of the driver with AspectJ tracing -->
 
@@ -633,7 +721,7 @@
 		</taskdef>
 
 
-		<iajc destDir="${buildDir}/${fullProdName}">
+		<iajc destdir="${compiler.output}">
 			<sourceroots>
 				<pathelement location="${buildDir}/${fullProdName}/org"/>
 				<pathelement location="${buildDir}/${fullProdName}/com"/>
@@ -656,7 +744,7 @@
 		depends="compile-driver"
 		if="com.mysql.jdbc.c3p0Present">
 		<javac srcdir="${buildDir}/${fullProdName}" 
-				destdir="${buildDir}/${fullProdName}" 
+				destdir="${compiler.output}" 
 				deprecation="off" 
 				debug="${debug.enable}" 
 				includes="com/mysql/jdbc/integration/c3p0/**">
@@ -668,7 +756,7 @@
 			depends="compile-driver"
 			if="com.mysql.jdbc.jbossPresent">
 		<javac srcdir="${buildDir}/${fullProdName}" 
-					destdir="${buildDir}/${fullProdName}" 
+					destdir="${compiler.output}" 
 					deprecation="off" 
 					debug="${debug.enable}" 
 					includes="com/mysql/jdbc/integration/jboss/**">
@@ -680,7 +768,7 @@
 			depends="compile-driver"
 			if="com.mysql.jdbc.log4jPresent">
 		<javac srcdir="${buildDir}/${fullProdName}" 
-				destdir="${buildDir}/${fullProdName}" 
+				destdir="${compiler.output}" 
 				deprecation="off" 
 				debug="${debug.enable}" 
 				includes="com/mysql/jdbc/log/Log4JLogger.java">
@@ -693,15 +781,26 @@
 	<target name="compile-testsuite" depends="init, compile-driver">
 		<javac 
 			srcdir="${buildDir}/${fullProdName}" 
-			destdir="${buildDir}/${fullProdName}" 
+			destdir="${compiler.output}" 
 			deprecation="off" 
 			debug="${debug.enable}"
 			includes="testsuite/**"
-			excludes="testsuite/requiresNonRedists/**, 
-                          testsuite/regression/AppletRegressionTest.java,
-                          testsuite/regression/DataSourceRegressionTest.java">
+			excludes="testsuite/requiresNonRedists/**, testsuite/**/jdbc4/**">
 			<classpath refid="project.build.classpath"/>
 		</javac>
+		
+		<javac destdir="${compiler.output}" 
+					deprecation="off" 
+					debug="${debug.enable}"
+					fork="yes"
+					executable="${com.mysql.jdbc.java6.javac}"
+					compiler="modern"
+					sourcepath="" srcdir="${buildDir}/${fullProdName}"
+					bootclasspath="${com.mysql.jdbc.java6.rtjar}">
+					<include name="testsuite/**/jdbc4/**" />
+					
+					<classpath refid="project.build.classpath" />
+				</javac>
 	</target>
 
 	<target name="real-clean">
@@ -723,6 +822,9 @@
 		</delete>
 	</target>
 
+	<target name="docs-generate-dynamic-docs" 
+		depends="docs-generate-properties-table, docs-generate-error-mapping-table"/>
+	
 	<target name="docs-generate-properties-table" depends="compile-driver">
 		<tempfile property="properties-xml" suffix=".xml" />
 
@@ -776,7 +878,7 @@
 
 		<emma enabled="${emma.enabled}" >
 			<instr instrpathref="built.driver.only.classpath"
-	             destdir="${buildDir}/${fullProdName}"	
+	             destdir="${compiler.output}"	
 	             metadatafile="${emma.coverage.dir}/metadata.emma"
 	             merge="true"
 	      		 mode="overwrite">
@@ -827,7 +929,7 @@
 		<mkdir dir="${junit.unitregress.results}/report"/>
 
 		<junit printSummary="yes" fork="on" jvm="${com.mysql.jdbc.testsuite.jvm}">
-			<jvmarg value="-Xmx256m" />
+			<jvmarg value="-Xmx128m" />
 			<jvmarg value="-Xverify:none" />
 			<!-- For java.sql.SavePoint on old JVMs -->
 			<jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />
@@ -878,7 +980,7 @@
 		<mkdir dir="${junit.compliance.results}/report"/>
 
 		<junit printsummary="yes" jvm="${com.mysql.jdbc.testsuite.jvm}">
-			<jvmarg value="-Xmx256m"/>
+			<jvmarg value="-Xmx128m"/>
 			<jvmarg value="-Xverify:none"/>
 			<!-- For java.sql.SavePoint on old JVMs -->
 			<jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />
@@ -938,7 +1040,7 @@
 		<sequential>
 			<java fork="true" jvm="@{jvm}" classname="com.mysql.jdbc.util.VersionFSHierarchyMaker">
 
-				<jvmarg value="-Xmx256m" />
+				<jvmarg value="-Xmx128m" />
 				<jvmarg value="-Xverify:none" />
 				<!-- For java.sql.SavePoint on old JVMs -->
 				<jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />

Added: trunk/mysql-connector-java/debian/README.Debian
===================================================================
--- trunk/mysql-connector-java/debian/README.Debian	                        (rev 0)
+++ trunk/mysql-connector-java/debian/README.Debian	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,7 @@
+MySQL Connector/J for Debian
+----------------------------
+
+In this version the JDBC 4 support is disabled because we have no runtime
+in main currently that can compile it.
+
+ -- Michael Koch <konqueror at gmx.de>  Fri, 30 Nov 2007 10:34:13 +0100

Modified: trunk/mysql-connector-java/debian/changelog
===================================================================
--- trunk/mysql-connector-java/debian/changelog	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/debian/changelog	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,3 +1,10 @@
+mysql-connector-java (5.1.5+dfsg-1) unstable; urgency=low
+
+  * New upstream release. Closes: #450718.
+  * Add Homepage field to debian/control.
+
+ -- Michael Koch <konqueror at gmx.de>  Fri, 30 Nov 2007 10:34:13 +0100
+
 mysql-connector-java (5.0.4+dfsg-3) unstable; urgency=low
 
   * Delete build-stamp in clean target. Closes: #424589.

Modified: trunk/mysql-connector-java/debian/control
===================================================================
--- trunk/mysql-connector-java/debian/control	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/debian/control	2007-11-30 10:46:51 UTC (rev 4921)
@@ -6,9 +6,9 @@
 Build-Depends: debhelper (>= 5), ant
 Build-Depends-Indep: java-gcj-compat-dev, ant-optional, liblog4j1.2-java, libcommons-logging-java, junit
 Standards-Version: 3.7.2
-Homepage: http://www.mysql.com/products/connector/j/
-Vcs-Svn: svn://svn.debian.org/pkg-java/trunk/mysql-connector-java
-Vcs-Browser: http://svn.debian.org/wsvn/pkg-java/trunk/mysql-connector-java/
+Homepage: Homepage: http://www.mysql.com/products/connector/j/
+XS-Vcs-Svn: svn://svn.debian.org/pkg-java/trunk/mysql-connector-java
+XS-Vcs-Browse: http://svn.debian.org/wsvn/pkg-java/trunk/mysql-connector-java/
 
 Package: libmysql-java
 Architecture: all

Modified: trunk/mysql-connector-java/debian/rules
===================================================================
--- trunk/mysql-connector-java/debian/rules	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/debian/rules	2007-11-30 10:46:51 UTC (rev 4921)
@@ -26,7 +26,7 @@
 build: build-stamp
 build-stamp:
 	dh_testdir
-	$(ANT_INVOKE) dist
+	$(ANT_INVOKE) dist -Dcom.mysql.jdbc.java6.javac=/usr/lib/jvm/java-gcj/bin/javac -Dcom.mysql.jdbc.java6.rtjar=/usr/lib/jvm/java-gcj/lib/rt.jar
 	touch build-stamp
 
 clean:

Modified: trunk/mysql-connector-java/docs/README
===================================================================
--- trunk/mysql-connector-java/docs/README	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/docs/README	2007-11-30 10:46:51 UTC (rev 4921)
@@ -34,10 +34,52 @@
        BCShortCourse/index.html) --- A more in-depth tutorial
        from Sun and JGuru
 
+   Key topics:
+     * For help with connection strings, connection options
+       setting up your connection through JDBC, see Section
+       Section, "Driver/Datasource Class Names, URL Syntax and
+       Configuration Properties for Connector/J."
+     * For tips on using Connector/J and JDBC with generic J2EE
+       toolkits, see Section Section, "Using Connector/J with
+       J2EE and Other Java Frameworks."
+     * Developers using the Tomcat server platform, see Section
+       Section, "Using Connector/J with Tomcat."
+     * Developers using JBoss, see Section Section, "Using
+       Connector/J with JBoss."
+     * Developers using Spring, see Section Section, "Using
+       Connector/J with Spring."
+
+   MySQL Enterprise MySQL Enterprise subscribers will find more
+   information about using JDBC with MySQL in the Knowledge Base
+   articles about JDBC
+   (https://kb.mysql.com/search.php?cat=search&category=10).
+   Access to the MySQL Knowledge Base collection of articles is
+   one of the advantages of subscribing to MySQL Enterprise. For
+   more information see
+   http://www.mysql.com/products/enterprise/advisors.html.
+
 1.1. Connector/J Versions
 
-   There are currently three version of MySQL Connector/J
+   There are currently four versions of MySQL Connector/J
    available:
+     * Connector/J 5.1 is current in alpha status. It provides
+       compatibility with all the functionality of MySQL,
+       including 4.1, 5.0, 5.1 and the 6.0 alpha release
+       featuring the new Falcon storage engine. Connector/J 5.1
+       provides ease of development features, including
+       auto-registration with the Driver Manager, standardized
+       validity checks, categorized SQLExceptions, support for
+       the JDBC-4.0 XML processing, per connection client
+       information, NCHAR, NVARCHAR and NCLOB types. This
+       release also includes all bug fixes up to and including
+       Connector/J 5.0.6.
+     * Connector/J 5.0 provides support for all the
+       functionality offered by Connector/J 3.1 and includes
+       distributed transaction (XA) support.
+     * Connector/J 3.1 was designed for connectivity to MySQL
+       4.1 and MySQL 5.0 servers and provides support for all
+       the functionality in MySQL 5.0 except distributed
+       transaction (XA) support.
      * Connector/J 3.0 provides core functionality and was
        designed with connectivity to MySQL 3.x or MySQL 4.1
        servers, although it will provide basic compatibility
@@ -45,13 +87,6 @@
        support server-side prepared statements, and does not
        support any of the features in versions of MySQL later
        than 4.1.
-     * Connector/J 3.1 was designed for connectivity to MySQL
-       4.1 and MySQL 5.0 servers and provides support for all
-       the functionality in MySQL 5.0 except distributed
-       transaction (XA) support.
-     * Connector/J 5.0 provides support for all the
-       functionality offered by Connector/J 3.1 and includes
-       distributed transaction (XA) support.
 
    The current recommended version for Connector/J is 5.0. This
    guide covers all three connector versions, with specific
@@ -66,11 +101,11 @@
      * JDK 1.5.x
 
    If you are building Connector/J from source using the source
-   distribution (see Section A.2.4, "Installing from the
+   distribution (see Section Section, "Installing from the
    Development Source Tree") then you must use JDK 1.4.x or
    newer to compiler the Connector package.
 
-   MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x
+   MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x.
 
    Because of the implementation of java.sql.Savepoint,
    Connector/J 3.1.0 and newer will not run on JDKs older than
@@ -85,13 +120,14 @@
    on java.util.LinkedHashMap which was first available in
    JDK-1.4.0.
 
-1.2. Installing Connector/J
+1.2. Connector/J Installation
 
    You can install the Connector/J package using two methods,
    using either the binary or source distribution. The binary
    distribution provides the easiest methods for installation;
    the source distribution enables you to customize your
-   installation further. With with either solution, you must
+   installation further. With either solution, you must manually
+   add the Connector/J location to your Java CLASSPATH.
 
 1.2.1. Installing Connector/J from a Binary Distribution
 
@@ -100,7 +136,7 @@
    distribution is available either as a Tar/Gzip or Zip file
    which you must extract to a suitable location and then
    optionally make the information about the package available
-   by changing your CLASSPATH (see Section A.2.2, "Installing
+   by changing your CLASSPATH (see Section Section, "Installing
    the Driver and Configuring the CLASSPATH").
 
    MySQL Connector/J is distributed as a .zip or .tar.gz archive
@@ -114,15 +150,15 @@
    driver JAR file.
 
    You should not use the debug build of the driver unless
-   instructed to do so when reporting a problem ors bug to MySQL
-   AB, as it is not designed to be run in production
+   instructed to do so when reporting a problem or a bug to
+   MySQL AB, as it is not designed to be run in production
    environments, and will have adverse performance impact when
    used. The debug binary also depends on the Aspect/J runtime
    library, which is located in the src/lib/aspectjrt.jar file
    that comes with the Connector/J distribution.
 
    You will need to use the appropriate graphical or
-   command-line utility to un-archive the distribution (for
+   command-line utility to extract the distribution (for
    example, WinZip for the .zip archive, and tar for the .tar.gz
    archive). Because there are potentially long filenames in the
    distribution, we use the GNU tar archive format. You will
@@ -143,19 +179,19 @@
    DriverManager, you would use com.mysql.jdbc.Driver as the
    class that implements java.sql.Driver.
 
-   You can set the CLASSPATH environment variableunder UNIX,
+   You can set the CLASSPATH environment variable under UNIX,
    Linux or Mac OS X either locally for a user within their
    .profile, .login or other login file. You can also set it
    globally by editing the global /etc/profile file.
 
    For example, under a C shell (csh, tcsh) you would add the
    Connector/J driver to your CLASSPATH using the following:
-shell> setenv CLASSPATH /path/to/mysql-connector-java-[version]-bin.ja
-r:$CLASSPATH
+shell> setenv CLASSPATH /path/mysql-connector-java-[ver]-bin.jar:$CLAS
+SPATH
 
    Or with a Bourne-compatible shell (sh, ksh, bash):
-export set CLASSPATH=/path/to/mysql-connector-java-[version]-bin.jar:$
-CLASSPATH
+export set CLASSPATH=/path/mysql-connector-java-[ver]-bin.jar:$CLASSPA
+TH
 
    Within Windows 2000, Windows XP and Windows Server 2003, you
    must set the environment variable through the System control
@@ -167,7 +203,7 @@
    configure third-party class libraries, as most application
    servers ignore the CLASSPATH environment variable. For
    configuration examples for some J2EE application servers, see
-   Section A.5.2, "Using Connector/J with J2EE and Other Java
+   Section Section, "Using Connector/J with J2EE and Other Java
    Frameworks." However, the authoritative source for JDBC
    connection pool configuration information for your particular
    application server is the documentation for that application
@@ -221,8 +257,9 @@
    server and various performance enhancements that can be
    enabled or disabled via configuration properties.
      * Unicode Character Sets --- See the next section, as well
-       as [WARNING: missing xref target (id=charset)] for
-       information on this new feature of MySQL. If you have
+       as Character Set Support
+       (http://dev.mysql.com/doc/refman/5.0/en/charset.html),
+       for information on this new feature of MySQL. If you have
        something misconfigured, it will usually show up as an
        error with a message similar to Illegal mix of
        collations.
@@ -328,11 +365,13 @@
 
 1.2.4. Installing from the Development Source Tree
 
-   Caution.  You should read this section only if you are
-   interested in helping us test our new code. If you just want
-   to get MySQL Connector/J up and running on your system, you
-   should use a standard release distribution.
+Caution
 
+   You should read this section only if you are interested in
+   helping us test our new code. If you just want to get MySQL
+   Connector/J up and running on your system, you should use a
+   standard release distribution.
+
    To install MySQL Connector/J from the development source
    tree, make sure that you have the following prerequisites:
      * Subversion, to check out the sources from our repository
@@ -382,34 +421,35 @@
        build/mysql-connector-java-[version].
        Install the newly created JDBC driver as you would a
        binary .jar file that you download from MySQL by
-       following the instructions in Section A.2.2, "Installing
-       the Driver and Configuring the CLASSPATH."
+       following the instructions in Section Section,
+       "Installing the Driver and Configuring the CLASSPATH."
 
 1.3. Connector/J Examples
 
    Examples of using Connector/J are located throughout this
    document, this section provides a summary and links to these
    examples.
-     * Section A.5.1.1, "Obtaining a connection from the
+     * Example Section, "Obtaining a connection from the
        DriverManager"
-     * Section A.5.1.2, "Using java.sql.Statement to execute a
+     * Example Section, "Using java.sql.Statement to execute a
        SELECT query"
-     * Section A.5.1.3, "Stored Procedures"
-     * Section A.5.1.3, "Using Connection.prepareCall()"
-     * Section A.5.1.3, "Registering output parameters"
-     * Section A.5.1.3, "Setting CallableStatement input
+     * Example Section, "Stored Procedures"
+     * Example Section, "Using Connection.prepareCall()"
+     * Example Section, "Registering output parameters"
+     * Example Section, "Setting CallableStatement input
        parameters"
-     * Section A.5.1.3, "Retrieving results and output parameter
+     * Example Section, "Retrieving results and output parameter
        values"
-     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+     * Example Section, "Retrieving AUTO_INCREMENT column values
        using Statement.getGeneratedKeys()"
-     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+     * Example Section, "Retrieving AUTO_INCREMENT column values
        using SELECT LAST_INSERT_ID()"
-     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+     * Example Section, "Retrieving AUTO_INCREMENT column values
        in Updatable ResultSets"
-     * Section A.5.2.1.1, "Using a connection pool with a J2EE
+     * Example Section, "Using a connection pool with a J2EE
        application server"
-     * Section A.5.3, "Example of transaction with retry logic"
+     * Example Section, "Example of transaction with retry
+       logic"
 
 1.4. Connector/J (JDBC) Reference
 
@@ -493,17 +533,19 @@
        java.sql.DriverManager.getConnection(),
        java.sql.Driver.connect() or the MySQL implementations of
        the javax.sql.DataSource setURL() method.
-       Note.  If the mechanism you use to configure a JDBC URL
-       is XML-based, you will need to use the XML character
-       literal &amp; to separate configuration parameters, as
-       the ampersand is a reserved character for XML.
 
+Note
+       If the mechanism you use to configure a JDBC URL is
+       XML-based, you will need to use the XML character literal
+       &amp; to separate configuration parameters, as the
+       ampersand is a reserved character for XML.
+
    The properties are listed in the following tables.
 
    Connection/Authentication.
    Property Name Definition Default Value Since Version
-   user The user to connect as all
-   password The password to use when connecting all
+   user The user to connect as   all versions
+   password The password to use when connecting   all versions
    socketFactory The name of the class that the driver should
    use for creating socket connections to the server. This class
    must implement the interface 'com.mysql.jdbc.SocketFactory'
@@ -514,20 +556,52 @@
    Defaults to '0'. 0 3.0.1
    socketTimeout Timeout on network socket operations (0, the
    default means no timeout). 0 3.0.1
+   connectionLifecycleInterceptors A comma-delimited list of
+   classes that implement
+   "com.mysql.jdbc.ConnectionLifecycleInterceptor" that should
+   notified of connection lifecycle events (creation,
+   destruction, commit, rollback, setCatalog and setAutoCommit)
+   and potentially alter the execution of these commands.
+   ConnectionLifecycleInterceptors are "stackable", more than
+   one interceptor may be specified via the configuration
+   property as a comma-delimited list, with the interceptors
+   executed in order from left to right.   5.1.4
    useConfigs Load the comma-delimited list of configuration
    properties before parsing the URL or applying user-specified
    properties. These configurations are explained in the
-   'Configurations' of the documentation. 3.1.5
+   'Configurations' of the documentation.   3.1.5
    interactiveClient Set the CLIENT_INTERACTIVE flag, which
    tells MySQL to timeout connections based on
    INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT false 3.1.0
+   localSocketAddress Hostname or IP address given to explicitly
+   configure the interface that the driver will bind the client
+   side of the TCP/IP connection to when connecting.   5.0.5
    propertiesTransform An implementation of
    com.mysql.jdbc.ConnectionPropertiesTransform that the driver
    will use to modify URL properties passed to the driver before
-   attempting a connection 3.1.4
+   attempting a connection   3.1.4
    useCompression Use zlib compression when communicating with
    the server (true/false)? Defaults to 'false'. false 3.0.17
 
+   Networking.
+   Property Name Definition Default Value Since Version
+   tcpKeepAlive If connecting using TCP/IP, should the driver
+   set SO_KEEPALIVE? true 5.0.7
+   tcpNoDelay If connecting using TCP/IP, should the driver set
+   SO_TCP_NODELAY (disabling the Nagle Algorithm)? true 5.0.7
+   tcpRcvBuf If connecting using TCP/IP, should the driver set
+   SO_RCV_BUF to the given value? The default value of '0',
+   means use the platform default value for this property) 0
+   5.0.7
+   tcpSndBuf If connecting using TCP/IP, shuold the driver set
+   SO_SND_BUF to the given value? The default value of '0',
+   means use the platform default value for this property) 0
+   5.0.7
+   tcpTrafficClass If connecting using TCP/IP, should the driver
+   set traffic class or type-of-service fields ?See the
+   documentation for java.net.Socket.setTrafficClass() for more
+   information. 0 5.0.7
+
    High Availability and Clustering.
    Property Name Definition Default Value Since Version
    autoReconnect Should the driver try to re-establish stale
@@ -537,20 +611,25 @@
    reconnect before the next query issued on the connection in a
    new transaction. The use of this feature is not recommended,
    because it has side effects related to session state and data
-   consistency when applications don'thandle SQLExceptions
+   consistency when applications don't handle SQLExceptions
    properly, and is only designed to be used when you are unable
    to configure your application to handle SQLExceptions
-   resulting from dead andstale connections properly.
+   resulting from dead and stale connections properly.
    Alternatively, investigate setting the MySQL server variable
-   "wait_timeout"to some high value rather than the default of 8
-   hours. false 1.1
+   "wait_timeout" to some high value rather than the default of
+   8 hours. false 1.1
    autoReconnectForPools Use a reconnection strategy appropriate
    for connection pools (defaults to 'false') false 3.1.3
    failOverReadOnly When failing over in autoReconnect mode,
    should the connection be set to 'read-only'? true 3.0.12
+   maxReconnects Maximum number of reconnects to attempt if
+   autoReconnect is true, default is '3'. 3 1.1
    reconnectAtTxEnd If autoReconnect is set to true, should the
-   driver attempt reconnectionsat the end of every transaction?
+   driver attempt reconnections at the end of every transaction?
    false 3.0.10
+   initialTimeout If autoReconnect is enabled, the initial time
+   to wait between re-connect attempts (in seconds, defaults to
+   '2'). 2 1.1
    roundRobinLoadBalance When autoReconnect is enabled, and
    failoverReadonly is false, should we pick hosts to connect to
    on a round-robin basis? false 3.1.2
@@ -561,31 +640,46 @@
    cause an attempt to be made to reconnect to the master.
    Defaults to 50. 50 3.0.2
    secondsBeforeRetryMaster How long should the driver wait,
-   when failed over, before attempting to reconnect to the
-   master server? Whichever condition is met first,
-   'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will
-   cause an attempt to be made to reconnect to the master. Time
-   in seconds, defaults to 30 30 3.0.2
-   enableDeprecatedAutoreconnect Auto-reconnect functionality is
-   deprecated starting with version 3.2, and will be removed in
-   version 3.3. Set this property to 'true' to disable the check
-   for the feature being configured. false 3.2.1
+   when failed over, before attempting 30 3.0.2
    resourceId A globally unique name that identifies the
    resource that this datasource or connection is connected to,
    used for XAResource.isSameRM() when the driver can't
-   determine this value based on hostnames used in the URL 5.0.1
+   determine this value based on hostnames used in the URL
+   5.0.1
 
    Security.
    Property Name Definition Default Value Since Version
    allowMultiQueries Allow the use of ';' to delimit multiple
-   queries during one statement (true/false, defaults to 'false'
-   false 3.1.1
+   queries during one statement (true/false), defaults to
+   'false' false 3.1.1
    useSSL Use SSL when communicating with the server
    (true/false), defaults to 'false' false 3.0.2
    requireSSL Require SSL connection if useSSL=true? (defaults
    to 'false'). false 3.1.0
+   allowLoadLocalInfile Should the driver allow use of 'LOAD
+   DATA LOCAL INFILE...' (defaults to 'true'). true 3.0.3
    allowUrlInLocalInfile Should the driver allow URLs in 'LOAD
    DATA LOCAL INFILE' statements? false 3.1.4
+   clientCertificateKeyStorePassword Password for the client
+   certificates KeyStore   5.1.0
+   clientCertificateKeyStoreType KeyStore type for client
+   certificates (NULL or empty means use default, standard
+   keystore types supported by the JVM are "JKS" and "PKCS12",
+   your environment may have more available depending on what
+   security products are installed and available to the JVM.
+   5.1.0
+   clientCertificateKeyStoreUrl URL to the client certificate
+   KeyStore (if not specified, use defaults)   5.1.0
+   trustCertificateKeyStorePassword Password for the trusted
+   root certificates KeyStore   5.1.0
+   trustCertificateKeyStoreType KeyStore type for trusted root
+   certificates (NULL or empty means use default, standard
+   keystore types supported by the JVM are "JKS" and "PKCS12",
+   your environment may have more available depending on what
+   security products are installed and available to the JVM.
+   5.1.0
+   trustCertificateKeyStoreUrl URL to the trusted root
+   certificate KeyStore (if not specified, use defaults)   5.1.0
    paranoid Take measures to prevent exposure sensitive
    information in error messages and clear data structures
    holding sensitive data when possible? (defaults to 'false')
@@ -593,14 +687,29 @@
 
    Performance Extensions.
    Property Name Definition Default Value Since Version
-   metadataCacheSize The number of queries to
-   cacheResultSetMetadata for if cacheResultSetMetaData is set
-   to 'true' (default 50) 50 3.1.1
+   callableStmtCacheSize If 'cacheCallableStmts' is enabled, how
+   many callable statements should be cached? 100 3.1.2
+   metadataCacheSize The number of queries to cache
+   ResultSetMetadata for if cacheResultSetMetaData is set to
+   'true' (default 50) 50 3.1.1
    prepStmtCacheSize If prepared statement caching is enabled,
    how many prepared statements should be cached? 25 3.0.10
    prepStmtCacheSqlLimit If prepared statement caching is
    enabled, what's the largest SQL the driver will cache the
    parsing for? 256 3.0.10
+   alwaysSendSetIsolation Should the driver always communicate
+   with the database when Connection.setTransactionIsolation()
+   is called? If set to false, the driver will only communicate
+   with the database when the requested transaction isolation is
+   different than the whichever is newer, the last value that
+   was set via Connection.setTransactionIsolation(), or the
+   value that was read from the server when the connection was
+   established. true 3.1.7
+   maintainTimeStats Should the driver maintain various internal
+   timers to enable idle time calculations as well as more
+   verbose error messages when the connection to the server
+   fails? Setting this property to false removes at least two
+   calls to System.getCurrentTimeMillis() per query. true 3.1.9
    useCursorFetch If connected to MySQL > 5.0.2, and
    setFetchSize() > 0 on a statement, should that statement use
    cursor-based fetching to retrieve rows? false 5.0.0
@@ -634,9 +743,33 @@
    driver only issue 'set autocommit=n' queries when the
    server's state doesn't match the requested state by
    Connection.setAutoCommit(boolean)? false 3.1.3
+   enableQueryTimeouts When enabled, query timeouts set via
+   Statement.setQueryTimeout() use a shared java.util.Timer
+   instance for scheduling. Even if the timeout doesn't expire
+   before the query is processed, there will be memory used by
+   the TimerTask for the given timeout which won't be reclaimed
+   until the time the timeout would have expired if it hadn't
+   been cancelled by the driver. High-load environments might
+   want to consider disabling this functionality. true 5.0.6
    holdResultsOpenOverStatementClose Should the driver close
    result sets on Statement.close() as required by the JDBC
    specification? false 3.1.7
+   largeRowSizeThreshold What size result set row should the
+   JDBC driver consider "large", and thus use a more
+   memory-efficient way of representing the row internally? 2048
+   5.1.1
+   loadBalanceStrategy If using a load-balanced connection to
+   connect to SQL nodes in a MySQL Cluster/NDB configuration (by
+   using the URL prefix "jdbc:mysql:loadbalance://"), which load
+   balancing algorithm should the driver use: (1) "random" - the
+   driver will pick a random host for each request. This tends
+   to work better than round-robin, as the randomness will
+   somewhat account for spreading loads where requests vary in
+   response time, while round-robin can sometimes lead to
+   overloaded nodes if there are variations in response times
+   across the workload. (2) "bestResponseTime" - the driver will
+   route the request to the host that had the best response time
+   for the previous transaction. random 5.0.6
    locatorFetchBufferSize If 'emulateLocators' is configured to
    'true', what size buffer should be used when fetching BLOB
    data for getBinaryInputStream? 1048576 3.2.1
@@ -649,35 +782,55 @@
    correctly. Notice that for prepared statements, server-side
    prepared statements can not currently take advantage of this
    rewrite option, and that if you don't specify stream lengths
-   when using PreparedStatement.set*Stream(),the driver won't be
-   able to determine the optimium number of parameters per batch
-   and you might receive an error from the driver that the
+   when using PreparedStatement.set*Stream(), the driver won't
+   be able to determine the optimum number of parameters per
+   batch and you might receive an error from the driver that the
    resultant packet is too large. Statement.getGeneratedKeys()
    for these rewritten statements only works when the entire
    batch includes INSERT statements. false 3.1.13
+   useDirectRowUnpack Use newer result set row unpacking code
+   that skips a copy from network buffers to a MySQL packet
+   instance and instead reads directly into the result set row
+   data buffers. true 5.1.1
+   useDynamicCharsetInfo Should the driver use a per-connection
+   cache of character set information queried from the server
+   when necessary, or use a built-in static mapping that is more
+   efficient, but isn't aware of custom character sets or
+   character sets implemented after the release of the JDBC
+   driver? true 5.0.6
+   useFastDateParsing Use internal String->Date/Time/Timestamp
+   conversion routines to avoid excessive object creation? true
+   5.0.5
    useFastIntParsing Use internal String->Integer conversion
    routines to avoid excessive object creation? true 3.1.4
    useJvmCharsetConverters Always use the character encoding
    routines built into the JVM, rather than using lookup tables
-   for single-byte character sets? (The default of "true" for
-   this is appropriate for newer JVMs true 5.0.1
+   for single-byte character sets? false 5.0.1
    useLocalSessionState Should the driver refer to the internal
    values of autocommit and transaction isolation that are set
    by Connection.setAutoCommit() and
-   Connection.setTransactionIsolation(), rather than querying
-   the database? false 3.1.7
+   Connection.setTransactionIsolation() and transaction state as
+   maintained by the protocol, rather than querying the database
+   or blindly sending commands to the database for commit() or
+   rollback() method calls? false 3.1.7
    useReadAheadInput Use newer, optimized non-blocking, buffered
    input stream when reading from the server? true 3.1.5
 
-   Debuging/Profiling.
+   Debugging/Profiling.
    Property Name Definition Default Value Since Version
    logger The name of a class that implements
-   'com.mysql.jdbc.log.Log' that will be used to log messages
-   to.(default is 'com.mysql.jdbc.log.StandardLogger', which
+   "com.mysql.jdbc.log.Log" that will be used to log messages
+   to. (default is "com.mysql.jdbc.log.StandardLogger", which
    logs to STDERR) com.mysql.jdbc.log.StandardLogger 3.1.1
+   gatherPerfMetrics Should the driver gather performance
+   metrics, and report them via the configured logger every
+   'reportMetricsIntervalMillis' milliseconds? false 3.1.2
    profileSQL Trace queries and their execution/fetch times to
    the configured logger (true/false) defaults to 'false' false
    3.1.0
+   profileSql Deprecated, use 'profileSQL' instead. Trace
+   queries and their execution/fetch times on STDERR
+   (true/false) defaults to 'false'   2.0.14
    reportMetricsIntervalMillis If 'gatherPerfMetrics' is
    enabled, how often should they be logged (in ms)? 30000 3.1.2
    maxQuerySizeToLog Controls the maximum length/size of a query
@@ -687,6 +840,10 @@
    slowQueryThresholdMillis If 'logSlowQueries' is enabled, how
    long should a query (in ms) before it is logged as 'slow'?
    2000 3.1.2
+   slowQueryThresholdNanos If 'useNanosForElapsedTime' is set to
+   true, and this property is set to a non-zero value, the
+   driver will use this threshold (in nanosecond units) to
+   determine if a query was slow. 0 5.0.7
    useUsageAdvisor Should the driver issue 'usage' warnings
    advising proper and efficient usage of JDBC and MySQL
    Connector/J to the log (true/false, defaults to 'false')?
@@ -694,6 +851,14 @@
    autoGenerateTestcaseScript Should the driver dump the SQL it
    is executing, including server-side prepared statements to
    STDERR? false 3.1.9
+   autoSlowLog Instead of using slowQueryThreshold* to determine
+   if a query is slow enough to be logged, maintain statistics
+   that allow the driver to determine queries that are outside
+   the 99th percentile? true 5.1.4
+   clientInfoProvider The name of a class that implements the
+   com.mysql.jdbc.JDBC4ClientInfoProvider interface in order to
+   support JDBC-4.0's Connection.get/setClientInfo() methods
+   com.mysql.jdbc.JDBC4CommentClientInfoProvider 5.1.0
    dumpMetadataOnColumnNotFound Should the driver dump the
    field-level metadata of a result set into the exception
    message when ResultSet.findColumn() fails? false 3.1.13
@@ -708,10 +873,22 @@
    driver automatically issue an 'EXPLAIN' on the server and
    send the results to the configured log at a WARN level? false
    3.1.2
+   includeInnodbStatusInDeadlockExceptions Include the output of
+   "SHOW ENGINE INNODB STATUS" in exception messages when
+   deadlock exceptions are detected? false 5.0.7
    logSlowQueries Should queries that take longer than
    'slowQueryThresholdMillis' be logged? false 3.1.2
+   logXaCommands Should the driver log XA commands sent by
+   MysqlXaConnection to the server, at the DEBUG level of
+   logging? false 5.0.5
+   resultSetSizeThreshold If the usage advisor is enabled, how
+   many rows should a result set contain before the driver warns
+   that it is suspiciously large? 100 5.0.5
    traceProtocol Should trace-level network protocol be logged?
    false 3.1.2
+   useNanosForElapsedTime For profiling/debugging functionality
+   that measures elapsed time, should the driver try to use
+   nanoseconds resolution if available (JDK >= 1.5)? false 5.0.7
 
    Miscellaneous.
    Property Name Definition Default Value Since Version
@@ -723,14 +900,30 @@
    defaults to 'true' true 1.1g
    characterEncoding If 'useUnicode' is set to true, what
    character encoding should the driver use when dealing with
-   strings? (defaults is to 'autodetect') 1.1g
+   strings? (defaults is to 'autodetect')   1.1g
    characterSetResults Character set to tell the server to
-   return results as. 3.0.13
+   return results as.   3.0.13
    connectionCollation If set, tells the server to use this
-   collation via 'set collation_connection' 3.0.13
+   collation via 'set collation_connection'   3.0.13
+   useBlobToStoreUTF8OutsideBMP Tells the driver to treat
+   [MEDIUM/LONG]BLOB columns as [LONG]VARCHAR columns holding
+   text encoded in UTF-8 that has characters outside the BMP
+   (4-byte encodings), which MySQL server can't handle natively.
+   false 5.1.3
+   utf8OutsideBmpExcludedColumnNamePattern When
+   "useBlobToStoreUTF8OutsideBMP" is set to "true", column names
+   matching the given regex will still be treated as BLOBs
+   unless they match the regex specified for
+   "utf8OutsideBmpIncludedColumnNamePattern". The regex must
+   follow the patterns used for the java.util.regex package.
+   5.1.3
+   utf8OutsideBmpIncludedColumnNamePattern Used to specify
+   exclusion rules to "utf8OutsideBmpExcludedColumnNamePattern".
+   The regex must follow the patterns used for the
+   java.util.regex package.   5.1.3
    sessionVariables A comma-separated list of name/value pairs
    to be sent as SET SESSION ... to the server when the driver
-   connects. 3.1.8
+   connects.   3.1.8
    allowNanAndInf Should the driver allow NaN or +/- INF values
    in PreparedStatement.setDouble()? false 3.1.5
    autoClosePStmtStreams Should the driver automatically call
@@ -738,12 +931,16 @@
    methods? false 3.1.12
    autoDeserialize Should the driver automatically detect and
    de-serialize objects stored in BLOB fields? false 3.1.5
+   blobsAreStrings Should the driver always treat BLOBs as
+   Strings - specifically to work around dubious metadata
+   returned by the server for GROUP BY clauses? false 5.0.8
    capitalizeTypeNames Capitalize type names in
    DatabaseMetaData? (usually only useful when using WebObjects,
-   true/false, defaults to 'false') false 2.0.7
+   true/false, defaults to 'false') true 2.0.7
    clobCharacterEncoding The character encoding to use for
    sending and retrieving TEXT, MEDIUMTEXT and LONGTEXT values
-   instead of the configured connection characterEncoding 5.0.0
+   instead of the configured connection characterEncoding
+   5.0.0
    clobberStreamingResults This will cause a 'streaming'
    ResultSet to be automatically closed, and any outstanding
    data still streaming from the server to be discarded if
@@ -757,25 +954,52 @@
    permissions to create databases. false 3.1.9
    emptyStringsConvertToZero Should the driver allow conversions
    from empty string fields to numeric values of '0'? true 3.1.8
-   emulateLocators N/A false 3.1.0
+   emulateLocators Should the driver emulate java.sql.Blobs with
+   locators? With this feature enabled, the driver will delay
+   loading the actual Blob data until the one of the retrieval
+   methods (getInputStream(), getBytes(), and so forth) on the
+   blob data stream has been accessed. For this to work, you
+   must use a column alias with the value of the column to the
+   actual name of the Blob. The feature also has the following
+   restrictions: The SELECT that created the result set must
+   reference only one table, the table must have a primary key;
+   the SELECT must alias the original blob column name,
+   specified as a string, to an alternate name; the SELECT must
+   cover all columns that make up the primary key. false 3.1.0
    emulateUnsupportedPstmts Should the driver detect prepared
    statements that are not supported by the server, and replace
    them with client-side emulated versions? true 3.1.7
+   functionsNeverReturnBlobs Should the driver always treat data
+   from functions returning BLOBs as Strings - specifically to
+   work around dubious metadata returned by the server for GROUP
+   BY clauses? false 5.0.8
+   generateSimpleParameterMetadata Should the driver generate
+   simplified parameter metadata for PreparedStatements when no
+   metadata is available either because the server couldn't
+   support preparing the statement, or server-side prepared
+   statements are disabled? false 5.0.5
    ignoreNonTxTables Ignore non-transactional table warning for
    rollback? (defaults to 'false'). false 3.0.9
    jdbcCompliantTruncation Should the driver throw
    java.sql.DataTruncation exceptions when data is truncated as
    is required by the JDBC specification when connected to a
-   server that supports warnings(MySQL 4.1.0 and newer)? true
-   3.1.2
+   server that supports warnings (MySQL 4.1.0 and newer)? This
+   property has no effect if the server sql-mode includes
+   STRICT_TRANS_TABLES. true 3.1.2
    maxRows The maximum number of rows to return (0, the default
    means return all rows). -1 all versions
+   netTimeoutForStreamingResults What value should the driver
+   automatically set the server setting 'net_write_timeout' to
+   when the streaming result sets feature is in use? (value has
+   unit of seconds, the value '0' means the driver will not try
+   and adjust this value) 600 5.1.0
    noAccessToProcedureBodies When determining procedure
    parameter types for CallableStatements, and the connected
    user can't access procedure bodies through "SHOW CREATE
    PROCEDURE" or select on mysql.proc should the driver instead
-   create basic metadata (all parameters reported as INOUT
-   VARCHARs) instead of throwing an exception? false 5.0.3
+   create basic metadata (all parameters reported as IN
+   VARCHARs, but allowing registerOutParameter() to be called on
+   them anyway) instead of throwing an exception? false 5.0.3
    noDatetimeStringSync Don't ensure that
    ResultSet.getDatetimeType().toString().equals(ResultSet.getSt
    ring()) false 3.1.7
@@ -799,12 +1023,27 @@
    of foreign keys, even though the SQL specification states
    that this facility contains much more than just foreign key
    support (one such application being OpenOffice)? false 3.1.12
+   padCharsWithSpace If a result set column has the CHAR type
+   and the value does not fill the amount of characters
+   specified in the DDL for the column, should the driver pad
+   the remaining characters with space (for ANSI compliance)?
+   false 5.0.6
    pedantic Follow the JDBC spec to the letter. false 3.0.0
    pinGlobalTxToPhysicalConnection When using XAConnections,
    should the driver ensure that operations on a given XID are
    always routed to the same physical connection? This allows
    the XAConnection to support "XA START ... JOIN" after "XA
    END" has been called false 5.0.1
+   populateInsertRowWithDefaultValues When using ResultSets that
+   are CONCUR_UPDATABLE, should the driver pre-populate the
+   "insert" row with default values from the DDL for the table
+   used in the query so those values are immediately available
+   for ResultSet accessors? This functionality requires a call
+   to the database for metadata each time a result set of this
+   type is created. If disabled (the default), the default
+   values will be populated by the an internal call to
+   refreshRow() which pulls back default values and/or values
+   changed by triggers. false 5.0.5
    processEscapeCodesForPrepStmts Should the driver process
    escape codes in queries that are prepared? true 3.1.12
    relaxAutoCommit If the version of MySQL the driver connects
@@ -820,7 +1059,15 @@
    runningCTS13 Enables workarounds for bugs in Sun's JDBC
    compliance testsuite version 1.3 false 3.1.7
    serverTimezone Override detection/mapping of timezone. Used
-   when timezone from server doesn't map to Java timezone 3.0.2
+   when timezone from server doesn't map to Java timezone
+   3.0.2
+   statementInterceptors A comma-delimited list of classes that
+   implement "com.mysql.jdbc.StatementInterceptor" that should
+   be placed "in between" query execution to influence the
+   results. StatementInterceptors are "chainable", the results
+   returned by the "current" interceptor will be passed on to
+   the next in in the chain, from left-to-right order, as
+   specified in this property.   5.1.1
    strictFloatingPoint Used only in older versions of compliance
    test false 3.0.0
    strictUpdates Should the driver do strict checking (all
@@ -833,6 +1080,9 @@
    a different type, should it use BOOLEAN instead of BIT for
    future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT
    type? false 3.1.9
+   treatUtilDateAsTimestamp Should the driver treat
+   java.util.Date as a TIMESTAMP for the purposes of
+   PreparedStatement.setObject()? true 5.0.5
    ultraDevHack Create PreparedStatements for prepareCall() when
    required, because UltraDev is broken and issues a
    prepareCall() for _all_ statements? (true/false, defaults to
@@ -857,14 +1107,20 @@
    behavior for "AS" clauses on columns and tables, and only
    return aliases (if any) for ResultSetMetaData.getColumnName()
    or ResultSetMetaData.getTableName() rather than the original
-   column/table name? true 5.0.4
+   column/table name? false 5.0.4
    useOldUTF8Behavior Use the UTF-8 behavior the driver did when
    communicating with 4.0 and older servers false 3.1.6
    useOnlyServerErrorMessages Don't prepend 'standard' SQLState
    error messages to error messages returned by the server. true
    3.0.15
+   useSSPSCompatibleTimezoneShift If migrating from an
+   environment that was using server-side prepared statements,
+   and the configuration property
+   "useJDBCCompliantTimeZoneShift" set to "true", use compatible
+   behavior when not using server-side prepared statements when
+   sending TIMESTAMP values to the MySQL server. false 5.0.5
    useServerPrepStmts Use server-side prepared statements if the
-   server supports them? (defaults to 'true'). true 3.1.0
+   server supports them? false 3.1.0
    useSqlStateCodes Use SQL Standard state codes instead of
    'legacy' X/Open/SQL state codes (true/false), default is
    'true' true 3.1.3
@@ -880,7 +1136,7 @@
    zeroDateTimeBehavior What should happen when the driver
    encounters DATETIME values that are composed entirely of
    zeroes (used by MySQL to represent invalid dates)? Valid
-   values are 'exception', 'round' and 'convertToNull'.
+   values are "exception", "round" and "convertToNull".
    exception 3.1.4
 
    Connector/J also supports access to MySQL via named pipes on
@@ -915,6 +1171,25 @@
    about how certain implementation decisions may affect how you
    use MySQL Connector/J.
      * Blob
+       Starting with Connector/J version 3.1.0, you can emulate
+       Blobs with locators by adding the property
+       'emulateLocators=true' to your JDBC URL. Using this
+       method, the driver will delay loading the actual Blob
+       data until you retrieve the other data and then use
+       retrieval methods (getInputStream(), getBytes(), and so
+       forth) on the blob data stream.
+       For this to work, you must use a column alias with the
+       value of the column to the actual name of the Blob, for
+       example:
+SELECT id, 'data' as blob_data from blobtable
+       For this to work, you must also follow follow these
+       rules:
+          + The SELECT must also reference only one table, the
+            table must have a primary key.
+          + The SELECT must alias the original blob column name,
+            specified as a string, to an alternate name.
+          + The SELECT must cover all columns that make up the
+            primary key.
        The Blob implementation does not allow in-place
        modification (they are copies, as reported by the
        DatabaseMetaData.locatorsUpdateCopies() method). Because
@@ -922,18 +1197,12 @@
        PreparedStatement.setBlob() or ResultSet.updateBlob() (in
        the case of updatable result sets) methods to save
        changes back to the database.
-       Starting with Connector/J version 3.1.0, you can emulate
-       Blobs with locators by adding the property
-       'emulateLocators=true' to your JDBC URL. You must then
-       use a column alias with the value of the column set to
-       the actual name of the Blob column in the SELECT that you
-       write to retrieve the Blob. The SELECT must also
-       reference only one table, the table must have a primary
-       key, and the SELECT must cover all columns that make up
-       the primary key. The driver will then delay loading the
-       actual Blob data until you retrieve the Blob and call
-       retrieval methods (getInputStream(), getBytes(), and so
-       forth) on it.
+       MySQL Enterprise MySQL Enterprise subscribers will find
+       more information about type conversion in the Knowledge
+       Base article, Type Conversions Supported by MySQL
+       Connector/J (https://kb.mysql.com/view.php?id=4929). To
+       subscribe to MySQL Enterprise see
+       http://www.mysql.com/products/enterprise/advisors.html.
      * CallableStatement
        Starting with Connector/J 3.1.1, stored procedures are
        supported when connecting to MySQL version 5.0 or newer
@@ -981,10 +1250,9 @@
        any large parameter changed to a non-large parameter, it
        is necessary to call clearParameters() and set all
        parameters again. The reason for this is as follows:
-          + The driver streams the large data out-of-band to the
-            prepared statement on the server side when the
-            parameter is set (before execution of the prepared
-            statement).
+          + During both server-side prepared statements and
+            client-side emulation, large data is exchanged only
+            when PreparedStatement.execute() is called.
           + Once that has been done, the stream used to read the
             data on the client side is closed (as per the JDBC
             spec), and can't be read from again.
@@ -1044,10 +1312,46 @@
      * Statement
        When using versions of the JDBC driver earlier than
        3.2.1, and connected to server versions earlier than
-       5.0.3, the "setFetchSize()" method has no effect, other
+       5.0.3, the setFetchSize() method has no effect, other
        than to toggle result set streaming as described above.
+       Connector/J 5.0.0 and later include support for both
+       Statement.cancel() and Statement.setQueryTimeout(). Both
+       require MySQL 5.0.0 or newer server, and require a
+       separate connection to issue the KILL QUERY statement. In
+       the case of setQueryTimeout(), the implementation creates
+       an additional thread to handle the timeout functionality.
+
+Note
+       Failures to cancel the statement for setQueryTimeout()
+       may manifest themselves as RuntimeException rather than
+       failing silently, as there is currently no way to unblock
+       the thread that is executing the query being cancelled
+       due to timeout expiration and have it throw the exception
+       instead.
        MySQL does not support SQL cursors, and the JDBC driver
        doesn't emulate them, so "setCursorName()" has no effect.
+       Connector/J 5.1.3 and later include two additional
+       methods:
+          + setLocalInfileInputStream() sets an InputStream
+            instance that will be used to send data to the MySQL
+            server for a LOAD DATA LOCAL INFILE statement rather
+            than a FileInputStream or URLInputStream that
+            represents the path given as an argument to the
+            statement.
+            This stream will be read to completion upon
+            execution of a LOAD DATA LOCAL INFILE statement, and
+            will automatically be closed by the driver, so it
+            needs to be reset before each call to execute*()
+            that would cause the MySQL server to request data to
+            fulfill the request for LOAD DATA LOCAL INFILE.
+            If this value is set to NULL, the driver will revert
+            to using a FileInputStream or URLInputStream as
+            required.
+          + getLocalInfileInputStream() returns the InputStream
+            instance that will be used to send data in response
+            to a LOAD DATA LOCAL INFILE statement.
+            This method returns NULL if no such stream has been
+            set via setLocalInfileInputStream().
 
 1.4.3. Java, JDBC and MySQL Types
 
@@ -1081,11 +1385,12 @@
    DATE, TIME, DATETIME, TIMESTAMP java.lang.String,
    java.sql.Date, java.sql.Timestamp
 
-   Note: round-off, overflow or loss of precision may occur if
-   you choose a Java numeric data type that has less precision
-   or capacity than the MySQL data type you are converting
-   to/from.
+Note
 
+   Round-off, overflow or loss of precision may occur if you
+   choose a Java numeric data type that has less precision or
+   capacity than the MySQL data type you are converting to/from.
+
    The ResultSet.getObject() method uses the type conversions
    between MySQL and Java types, following the JDBC
    specification where appropriate. The value returned by
@@ -1158,7 +1463,7 @@
    character encoding per connection, which could either be
    automatically detected from the server configuration, or
    could be configured by the user through the useUnicode and
-   "characterEncoding" properties.
+   characterEncoding properties.
 
    Starting with MySQL Server 4.1, Connector/J supports a single
    character encoding between client and server, and any number
@@ -1170,8 +1475,8 @@
    the driver is specified on the server via the character_set
    system variable for server versions older than 4.1.0 and
    character_set_server for server versions 4.1.0 and newer. For
-   more information, see [WARNING: missing xref target
-   (id=charset-server)]
+   more information, see Server Character Set and Collation
+   (http://dev.mysql.com/doc/refman/5.0/en/charset-server.html).
 
    To override the automatically-detected encoding on the client
    side, use the characterEncoding property in the URL used to
@@ -1205,11 +1510,13 @@
    utf8 UTF-8
    ucs2 UnicodeBig
 
-   Warning.  Do not issue the query 'set names' with
-   Connector/J, as the driver will not detect that the character
-   set has changed, and will continue to use the character set
-   detected during the initial connection setup.
+Warning
 
+   Do not issue the query 'set names' with Connector/J, as the
+   driver will not detect that the character set has changed,
+   and will continue to use the character set detected during
+   the initial connection setup.
+
    To allow multiple character sets to be sent from the client,
    the UTF-8 encoding should be used, either by configuring utf8
    as the default server character set, or by configuring the
@@ -1233,8 +1540,9 @@
        73544.html
      * A MySQL server that supports SSL and has been compiled
        and configured to do so, which is MySQL-4.0.4 or later,
-       see [WARNING: missing xref target
-       (id=secure-connections)] for more information.
+       see Using Secure Connections
+       (http://dev.mysql.com/doc/refman/5.0/en/secure-connection
+       s.html), for more information.
      * A client certificate (covered later in this section)
 
    You will first need to import the MySQL server CA Certificate
@@ -1248,31 +1556,29 @@
    (cacert.pem), you can do the following (assuming that keytool
    is in your path. The keytool should be located in the bin
    subdirectory of your JDK or JRE):
-shell> keytool -import -alias mysqlServerCACert -file cacert.pem -keys
-tore truststore
+shell> keytool -import -alias mysqlServerCACert \
+                                  -file cacert.pem -keystore truststor
+e
 
    Keytool will respond with the following information:
 Enter keystore password:  *********
-Owner: EMAILADDRESS=walrus at example.com, CN=Walrus, O=MySQL AB, L=Orenb
-urg, ST=Some
--State, C=RU
-Issuer: EMAILADDRESS=walrus at example.com, CN=Walrus, O=MySQL AB, L=Oren
-burg, ST=Som
-e-State, C=RU
+Owner: EMAILADDRESS=walrus at example.com, CN=Walrus,
+       O=MySQL AB, L=Orenburg, ST=Some-State, C=RU
+Issuer: EMAILADDRESS=walrus at example.com, CN=Walrus,
+       O=MySQL AB, L=Orenburg, ST=Some-State, C=RU
 Serial number: 0
-Valid from: Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CD
-T 2003
+Valid from:
+   Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003
 Certificate fingerprints:
-         MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
-         SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4
-D:6C
+    MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
+    SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4D:6C
 Trust this certificate? [no]:  yes
 Certificate was added to keystore
 
    You will then need to generate a client certificate, so that
    the MySQL server knows that it is talking to a secure client:
- shell> keytool -genkey -keyalg rsa -alias mysqlClientCertificate -key
-store keystore
+ shell> keytool -genkey -keyalg rsa \
+     -alias mysqlClientCertificate -keystore keystore
 
    Keytool will prompt you for the following information, and
    create a keystore named keystore in the current directory.
@@ -1305,12 +1611,20 @@
    path_to_keystore_file with the full path to the keystore file
    you created, path_to_truststore_file with the path to the
    truststore file you created, and using the appropriate
-   password values for each property.
+   password values for each property. You can do this either on
+   the command line:
 -Djavax.net.ssl.keyStore=path_to_keystore_file
--Djavax.net.ssl.keyStorePassword=*********
+-Djavax.net.ssl.keyStorePassword=password
 -Djavax.net.ssl.trustStore=path_to_truststore_file
--Djavax.net.ssl.trustStorePassword=*********
+-Djavax.net.ssl.trustStorePassword=password
 
+   Or you can set the values directly within the application:
+ System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file");
+System.setProperty("javax.net.ssl.keyStorePassword","password");
+System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file
+");
+System.setProperty("javax.net.ssl.trustStorePassword","password");
+
    You will also need to set useSSL to true in your connection
    parameters for MySQL Connector/J, either by adding
    useSSL=true to your URL, or by setting the property useSSL to
@@ -1320,49 +1634,51 @@
    You can test that SSL is working by turning on JSSE debugging
    (as detailed below), and look for the following key events:
 ...
- *** ClientHello, v3.1
- RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12,
-54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, 217
-, 219, 239, 202, 19, 121, 78 }
- Session ID:  {}
- Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17
-}
- Compression Methods:  { 0 }
- ***
- [write] MD5 and SHA1 hashes:  len = 59
- 0000: 01 00 00 37 03 01 3D B6   90 FA C7 94 B4 D7 4A 0C  ...7..=.....
-..J.
- 0010: 36 F4 00 A8 37 67 D7 40   10 8A E1 BE 84 99 02 D9  6...7g. at ....
-....
- 0020: DB EF CA 13 79 4E 00 00   10 00 05 00 04 00 09 00  ....yN......
-....
- 0030: 0A 00 12 00 13 00 03 00   11 01 00                 ...........
- main, WRITE:  SSL v3.1 Handshake, length = 59
- main, READ:  SSL v3.1 Handshake, length = 74
- *** ServerHello, v3.1
- RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58
-, 202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3,
-132, 110, 82, 148, 160, 92 }
- Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63,
-182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177, 219
-, 158, 177, 187, 143}
- Cipher Suite:  { 0, 5 }
- Compression Method: 0
- ***
- %% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
- ** SSL_RSA_WITH_RC4_128_SHA
- [read] MD5 and SHA1 hashes:  len = 74
- 0000: 02 00 00 46 03 01 3D B6   43 98 74 32 04 67 19 64  ...F..=.C.t2
-.g.d
- 0010: 3A CA 4F B9 B2 64 D7 42   FE 15 53 BB BE 2A AA 03  :.O..d.B..S.
-.*..
- 0020: 84 6E 52 94 A0 5C 20 A3   E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q
-....
- 0030: B3 44 3F B6 9E 1E 0B 96   4F AA 4C FF 5C 0F E2 18  .D?.....O.L.
-\...
- 0040: 11 B1 DB 9E B1 BB 8F 00   05 00                    ..........
- main, READ:  SSL v3.1 Handshake, length = 1712
- ...
+*** ClientHello, v3.1
+RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12, �
+  54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, �
+  217, 219, 239, 202, 19, 121, 78 }
+Session ID:  {}
+Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17 }
+Compression Methods:  { 0 }
+***
+[write] MD5 and SHA1 hashes:  len = 59
+0000: 01 00 00 37 03 01 3D B6 90 FA C7 94 B4 D7 4A 0C  ...7..=.......J
+.
+0010: 36 F4 00 A8 37 67 D7 40 10 8A E1 BE 84 99 02 D9  6...7g. at .......
+.
+0020: DB EF CA 13 79 4E 00 00 10 00 05 00 04 00 09 00  ....yN.........
+.
+0030: 0A 00 12 00 13 00 03 00 11 01 00                 ...........
+main, WRITE:  SSL v3.1 Handshake, length = 59
+main, READ:  SSL v3.1 Handshake, length = 74
+*** ServerHello, v3.1
+RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58,
+ �
+   202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3,
+ �
+   132, 110, 82, 148, 160, 92 }
+Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63, �
+   182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177,
+�
+   219, 158, 177, 187, 143}
+Cipher Suite:  { 0, 5 }
+Compression Method: 0
+***
+%% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
+** SSL_RSA_WITH_RC4_128_SHA
+[read] MD5 and SHA1 hashes:  len = 74
+0000: 02 00 00 46 03 01 3D B6 43 98 74 32 04 67 19 64  ...F..=.C.t2.g.
+d
+0010: 3A CA 4F B9 B2 64 D7 42 FE 15 53 BB BE 2A AA 03  :.O..d.B..S..*.
+.
+0020: 84 6E 52 94 A0 5C 20 A3 E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q...
+.
+0030: B3 44 3F B6 9E 1E 0B 96 4F AA 4C FF 5C 0F E2 18  .D?.....O.L.\..
+.
+0040: 11 B1 DB 9E B1 BB 8F 00 05 00                    ..........
+main, READ:  SSL v3.1 Handshake, length = 1712
+...
 
    JSSE provides debugging (to STDOUT) when you set the
    following system property: -Djavax.net.debug=all This will
@@ -1416,59 +1732,200 @@
 
 public class ReplicationDriverDemo {
 
-    public static void main(String[] args) throws Exception {
-        ReplicationDriver driver = new ReplicationDriver();
+  public static void main(String[] args) throws Exception {
+    ReplicationDriver driver = new ReplicationDriver();
 
-        Properties props = new Properties();
+    Properties props = new Properties();
 
-        // We want this for failover on the slaves
-        props.put("autoReconnect", "true");
+    // We want this for failover on the slaves
+    props.put("autoReconnect", "true");
 
-        // We want to load balance between the slaves
-        props.put("roundRobinLoadBalance", "true");
+    // We want to load balance between the slaves
+    props.put("roundRobinLoadBalance", "true");
 
-        props.put("user", "foo");
-        props.put("password", "bar");
+    props.put("user", "foo");
+    props.put("password", "bar");
 
-        //
-        // Looks like a normal MySQL JDBC url, with a comma-separated
-list
-        // of hosts, the first being the 'master', the rest being any
-number
-        // of slaves that the driver will load balance against
-        //
+    //
+    // Looks like a normal MySQL JDBC url, with a
+    // comma-separated list of hosts, the first
+    // being the 'master', the rest being any number
+    // of slaves that the driver will load balance against
+    //
 
-        Connection conn =
-            driver.connect("jdbc:mysql://master,slave1,slave2,slave3/t
-est",
-                props);
+    Connection conn =
+        driver.connect("jdbc:mysql://master,slave1,slave2,slave3/test"
+,
+            props);
 
-        //
-        // Perform read/write work on the master
-        // by setting the read-only flag to "false"
-        //
+    //
+    // Perform read/write work on the master
+    // by setting the read-only flag to "false"
+    //
 
-        conn.setReadOnly(false);
-        conn.setAutoCommit(false);
-        conn.createStatement().executeUpdate("UPDATE some_table ....")
-;
-        conn.commit();
+    conn.setReadOnly(false);
+    conn.setAutoCommit(false);
+    conn.createStatement().executeUpdate("UPDATE some_table ....");
+    conn.commit();
 
-        //
-        // Now, do a query from a slave, the driver automatically pick
-s one
-        // from the list
-        //
+    //
+    // Now, do a query from a slave, the driver automatically picks on
+e
+    // from the list
+    //
 
-        conn.setReadOnly(true);
+    conn.setReadOnly(true);
 
-        ResultSet rs = conn.createStatement().executeQuery("SELECT a,b
-,c FROM some_other_table");
+    ResultSet rs =
+      conn.createStatement().executeQuery("SELECT a,b FROM alt_table")
+;
 
-         .......
-    }
+     .......
+  }
 }
 
+1.4.7. Mapping MySQL Error Numbers to SQLStates
+
+   The table below provides a mapping of the MySQL Error Numbers
+   to SQL States
+
+   Table 1. Mapping of MySQL Error Numbers to SQLStates
+   MySQL Error Number MySQL Error Name Legacy (X/Open) SQLState
+   SQL Standard SQLState
+   1022 ER_DUP_KEY S1000 23000
+   1037 ER_OUTOFMEMORY S1001 HY001
+   1038 ER_OUT_OF_SORTMEMORY S1001 HY001
+   1040 ER_CON_COUNT_ERROR 08004 08004
+   1042 ER_BAD_HOST_ERROR 08004 08S01
+   1043 ER_HANDSHAKE_ERROR 08004 08S01
+   1044 ER_DBACCESS_DENIED_ERROR S1000 42000
+   1045 ER_ACCESS_DENIED_ERROR 28000 28000
+   1047 ER_UNKNOWN_COM_ERROR 08S01 HY000
+   1050 ER_TABLE_EXISTS_ERROR S1000 42S01
+   1051 ER_BAD_TABLE_ERROR 42S02 42S02
+   1052 ER_NON_UNIQ_ERROR S1000 23000
+   1053 ER_SERVER_SHUTDOWN S1000 08S01
+   1054 ER_BAD_FIELD_ERROR S0022 42S22
+   1055 ER_WRONG_FIELD_WITH_GROUP S1009 42000
+   1056 ER_WRONG_GROUP_FIELD S1009 42000
+   1057 ER_WRONG_SUM_SELECT S1009 42000
+   1058 ER_WRONG_VALUE_COUNT 21S01 21S01
+   1059 ER_TOO_LONG_IDENT S1009 42000
+   1060 ER_DUP_FIELDNAME S1009 42S21
+   1061 ER_DUP_KEYNAME S1009 42000
+   1062 ER_DUP_ENTRY S1009 23000
+   1063 ER_WRONG_FIELD_SPEC S1009 42000
+   1064 ER_PARSE_ERROR 42000 42000
+   1065 ER_EMPTY_QUERY 42000 42000
+   1066 ER_NONUNIQ_TABLE S1009 42000
+   1067 ER_INVALID_DEFAULT S1009 42000
+   1068 ER_MULTIPLE_PRI_KEY S1009 42000
+   1069 ER_TOO_MANY_KEYS S1009 42000
+   1070 ER_TOO_MANY_KEY_PARTS S1009 42000
+   1071 ER_TOO_LONG_KEY S1009 42000
+   1072 ER_KEY_COLUMN_DOES_NOT_EXITS S1009 42000
+   1073 ER_BLOB_USED_AS_KEY S1009 42000
+   1074 ER_TOO_BIG_FIELDLENGTH S1009 42000
+   1075 ER_WRONG_AUTO_KEY S1009 42000
+   1080 ER_FORCING_CLOSE S1000 08S01
+   1081 ER_IPSOCK_ERROR 08S01 08S01
+   1082 ER_NO_SUCH_INDEX S1009 42S12
+   1083 ER_WRONG_FIELD_TERMINATORS S1009 42000
+   1084 ER_BLOBS_AND_NO_TERMINATED S1009 42000
+   1090 ER_CANT_REMOVE_ALL_FIELDS S1000 42000
+   1091 ER_CANT_DROP_FIELD_OR_KEY S1000 42000
+   1101 ER_BLOB_CANT_HAVE_DEFAULT S1000 42000
+   1102 ER_WRONG_DB_NAME S1000 42000
+   1103 ER_WRONG_TABLE_NAME S1000 42000
+   1104 ER_TOO_BIG_SELECT S1000 42000
+   1106 ER_UNKNOWN_PROCEDURE S1000 42000
+   1107 ER_WRONG_PARAMCOUNT_TO_PROCEDURE S1000 42000
+   1109 ER_UNKNOWN_TABLE S1000 42S02
+   1110 ER_FIELD_SPECIFIED_TWICE S1000 42000
+   1112 ER_UNSUPPORTED_EXTENSION S1000 42000
+   1113 ER_TABLE_MUST_HAVE_COLUMNS S1000 42000
+   1115 ER_UNKNOWN_CHARACTER_SET S1000 42000
+   1118 ER_TOO_BIG_ROWSIZE S1000 42000
+   1120 ER_WRONG_OUTER_JOIN S1000 42000
+   1121 ER_NULL_COLUMN_IN_INDEX S1000 42000
+   1129 ER_HOST_IS_BLOCKED 08004 HY000
+   1130 ER_HOST_NOT_PRIVILEGED 08004 HY000
+   1131 ER_PASSWORD_ANONYMOUS_USER S1000 42000
+   1132 ER_PASSWORD_NOT_ALLOWED S1000 42000
+   1133 ER_PASSWORD_NO_MATCH S1000 42000
+   1136 ER_WRONG_VALUE_COUNT_ON_ROW S1000 21S01
+   1138 ER_INVALID_USE_OF_NULL S1000 42000
+   1139 ER_REGEXP_ERROR S1000 42000
+   1140 ER_MIX_OF_GROUP_FUNC_AND_FIELDS S1000 42000
+   1141 ER_NONEXISTING_GRANT S1000 42000
+   1142 ER_TABLEACCESS_DENIED_ERROR S1000 42000
+   1143 ER_COLUMNACCESS_DENIED_ERROR S1000 42000
+   1144 ER_ILLEGAL_GRANT_FOR_TABLE S1000 42000
+   1145 ER_GRANT_WRONG_HOST_OR_USER S1000 42000
+   1146 ER_NO_SUCH_TABLE S1000 42S02
+   1147 ER_NONEXISTING_TABLE_GRANT S1000 42000
+   1148 ER_NOT_ALLOWED_COMMAND S1000 42000
+   1149 ER_SYNTAX_ERROR S1000 42000
+   1152 ER_ABORTING_CONNECTION S1000 08S01
+   1153 ER_NET_PACKET_TOO_LARGE S1000 08S01
+   1154 ER_NET_READ_ERROR_FROM_PIPE S1000 08S01
+   1155 ER_NET_FCNTL_ERROR S1000 08S01
+   1156 ER_NET_PACKETS_OUT_OF_ORDER S1000 08S01
+   1157 ER_NET_UNCOMPRESS_ERROR S1000 08S01
+   1158 ER_NET_READ_ERROR S1000 08S01
+   1159 ER_NET_READ_INTERRUPTED S1000 08S01
+   1160 ER_NET_ERROR_ON_WRITE S1000 08S01
+   1161 ER_NET_WRITE_INTERRUPTED S1000 08S01
+   1162 ER_TOO_LONG_STRING S1000 42000
+   1163 ER_TABLE_CANT_HANDLE_BLOB S1000 42000
+   1164 ER_TABLE_CANT_HANDLE_AUTO_INCREMENT S1000 42000
+   1166 ER_WRONG_COLUMN_NAME S1000 42000
+   1167 ER_WRONG_KEY_COLUMN S1000 42000
+   1169 ER_DUP_UNIQUE S1000 23000
+   1170 ER_BLOB_KEY_WITHOUT_LENGTH S1000 42000
+   1171 ER_PRIMARY_CANT_HAVE_NULL S1000 42000
+   1172 ER_TOO_MANY_ROWS S1000 42000
+   1173 ER_REQUIRES_PRIMARY_KEY S1000 42000
+   1177 ER_CHECK_NO_SUCH_TABLE S1000 42000
+   1178 ER_CHECK_NOT_IMPLEMENTED S1000 42000
+   1179 ER_CANT_DO_THIS_DURING_AN_TRANSACTION S1000 25000
+   1184 ER_NEW_ABORTING_CONNECTION S1000 08S01
+   1189 ER_MASTER_NET_READ S1000 08S01
+   1190 ER_MASTER_NET_WRITE S1000 08S01
+   1203 ER_TOO_MANY_USER_CONNECTIONS S1000 42000
+   1205 ER_LOCK_WAIT_TIMEOUT 41000 41000
+   1207 ER_READ_ONLY_TRANSACTION S1000 25000
+   1211 ER_NO_PERMISSION_TO_CREATE_USER S1000 42000
+   1213 ER_LOCK_DEADLOCK 41000 40001
+   1216 ER_NO_REFERENCED_ROW S1000 23000
+   1217 ER_ROW_IS_REFERENCED S1000 23000
+   1218 ER_CONNECT_TO_MASTER S1000 08S01
+   1222 ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT S1000 21000
+   1226 ER_USER_LIMIT_REACHED S1000 42000
+   1230 ER_NO_DEFAULT S1000 42000
+   1231 ER_WRONG_VALUE_FOR_VAR S1000 42000
+   1232 ER_WRONG_TYPE_FOR_VAR S1000 42000
+   1234 ER_CANT_USE_OPTION_HERE S1000 42000
+   1235 ER_NOT_SUPPORTED_YET S1000 42000
+   1239 ER_WRONG_FK_DEF S1000 42000
+   1241 ER_OPERAND_COLUMNS S1000 21000
+   1242 ER_SUBQUERY_NO_1_ROW S1000 21000
+   1247 ER_ILLEGAL_REFERENCE S1000 42S22
+   1248 ER_DERIVED_MUST_HAVE_ALIAS S1000 42000
+   1249 ER_SELECT_REDUCED S1000 01000
+   1250 ER_TABLENAME_NOT_ALLOWED_HERE S1000 42000
+   1251 ER_NOT_SUPPORTED_AUTH_MODE S1000 08004
+   1252 ER_SPATIAL_CANT_HAVE_NULL S1000 42000
+   1253 ER_COLLATION_CHARSET_MISMATCH S1000 42000
+   1261 ER_WARN_TOO_FEW_RECORDS S1000 01000
+   1262 ER_WARN_TOO_MANY_RECORDS S1000 01000
+   1263 ER_WARN_NULL_TO_NOTNULL S1000 01000
+   1264 ER_WARN_DATA_OUT_OF_RANGE S1000 01000
+   1265 ER_WARN_DATA_TRUNCATED S1000 01000
+   1280 ER_WRONG_NAME_FOR_INDEX S1000 42000
+   1281 ER_WRONG_NAME_FOR_CATALOG S1000 42000
+   1286 ER_UNKNOWN_STORAGE_ENGINE S1000 42000
+
 1.5. Connector/J Notes and Tips
 
 1.5.1. Basic JDBC Concepts
@@ -1526,19 +1983,21 @@
 import java.sql.DriverManager;
 import java.sql.SQLException;
 
-    ... try {
-            Connection conn = DriverManager.getConnection("jdbc:mysql:
-//localhost/test?user=monty&password=greatsqldb");
+...
+try {
+    Connection conn =
+       DriverManager.getConnection("jdbc:mysql://localhost/test?" +
+                                   "user=monty&password=greatsqldb");
 
-            // Do something with the Connection
+    // Do something with the Connection
 
-           ....
-        } catch (SQLException ex) {
-            // handle any errors
-            System.out.println("SQLException: " + ex.getMessage());
-            System.out.println("SQLState: " + ex.getSQLState());
-            System.out.println("VendorError: " + ex.getErrorCode());
-        }
+   ...
+} catch (SQLException ex) {
+    // handle any errors
+    System.out.println("SQLException: " + ex.getMessage());
+    System.out.println("SQLState: " + ex.getSQLState());
+    System.out.println("VendorError: " + ex.getErrorCode());
+}
 
    Once a Connection is established, it can be used to create
    Statement and PreparedStatement objects, as well as retrieve
@@ -1622,13 +2081,15 @@
    interface is fully implemented with the exception of the
    getParameterMetaData() method.
 
-   See [WARNING: missing xref target (id=stored-procedures)] for
-   more information on MySQL stored procedures.
+   For more information on MySQL stored procedures, please refer
+   to http://dev.mysql.com/doc/mysql/en/stored-procedures.html.
 
    Connector/J exposes stored procedure functionality through
    JDBC's CallableStatement interface.
 
-   Note.  Current versions of MySQL server do not return enough
+Note
+
+   Current versions of MySQL server do not return enough
    information for the JDBC driver to provide result set
    metadata for callable statements. This means that when using
    CallableStatement, ResultSetMetaData may return NULL.
@@ -1638,8 +2099,8 @@
    passed in via inputParam as a ResultSet:
 
    Example 3. Stored Procedures
-CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam I
-NT)
+CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), \
+                                        INOUT inOutParam INT)
 BEGIN
     DECLARE z INT;
     SET z = inOutParam + 1;
@@ -1674,8 +2135,10 @@
 
 
     cStmt.setString(1, "abcdefg");
-       Note.  Connection.prepareCall() is an expensive method,
-       due to the metadata retrieval that the driver performs to
+
+Note
+       Connection.prepareCall() is an expensive method, due to
+       the metadata retrieval that the driver performs to
        support output parameters. For performance reasons, you
        should try to minimize unnecessary calls to
        Connection.prepareCall() by reusing CallableStatement
@@ -1941,9 +2404,9 @@
 
     rs.close();
 
-    System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID(
-)': "
-        + autoIncKeyFromFunc);
+    System.out.println("Key returned from " +
+                       "'SELECT LAST_INSERT_ID()': " +
+                       autoIncKeyFromFunc);
 
 } finally {
 
@@ -2175,8 +2638,8 @@
           * variable, as JNDI lookups can be expensive as well.
           */
 
-        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MyS
-QLDB");
+        DataSource ds =
+          (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
 
         /*
          * The following code is what would actually be in your
@@ -2329,8 +2792,7 @@
     <!-- Don't use autoReconnect=true, it's going away eventually
          and it's a crutch for older connection pools that couldn't
          test connections. You need to decide whether your application
- is
-         supposed to deal with SQLExceptions (hint, it should), and
+         is supposed to deal with SQLExceptions (hint, it should), and
          how much of a performance penalty you're willing to pay
          to ensure 'freshness' of the connection -->
 
@@ -2470,15 +2932,360 @@
              our implementation of these to increase the robustness
              of the connection pool. -->
 
-        <exception-sorter-class-name>com.mysql.jdbc.integration.jboss.
-ExtendedMysqlExceptionSorter</exception-sorter-class-name>
-        <valid-connection-checker-class-name>com.mysql.jdbc.integratio
-n.jboss.MysqlValidConnectionChecker</valid-connection-checker-class-na
-me>
+        <exception-sorter-class-name>
+  com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
+        </exception-sorter-class-name>
+        <valid-connection-checker-class-name>
+  com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
+        </valid-connection-checker-class-name>
 
     </local-tx-datasource>
 </datasources>
 
+1.5.2.4. Using Connector/J with Spring
+
+   The Spring Framework is a Java-based application framework
+   designed for assisting in application design by providing a
+   way to configure components. The technique used by Spring is
+   a well known design pattern called Dependency Injection (see
+   Inversion of Control Containers and the Dependency Injection
+   pattern
+   (http://www.martinfowler.com/articles/injection.html)). This
+   article will focus on Java-oriented access to MySQL databases
+   with Spring 2.0. For those wondering, there is a .NET port of
+   Spring appropriately named Spring.NET.
+
+   Spring is not only a system for configuring components, but
+   also includes support for aspect oriented programming (AOP).
+   This is one of the main benefits and the foundation for
+   Spring's resource and transaction management. Spring also
+   provides utilities for integrating resource management with
+   JDBC and Hibernate.
+
+   For the examples in this section the MySQL world sample
+   database will be used. The first task is to setup a MySQL
+   data source through Spring. Components within Spring use the
+   "bean" terminology. For example, to configure a connection to
+   a MySQL server supporting the world sample database you might
+   use:
+<util:map id="dbProps">
+    <entry key="db.driver" value="com.mysql.jdbc.Driver"/>
+    <entry key="db.jdbcurl" value="jdbc:mysql://localhost/world"/>
+    <entry key="db.username" value="myuser"/>
+    <entry key="db.password" value="mypass"/>
+</util:map>
+
+
+   In the above example we are assigning values to properties
+   that will be used in the configuration. For the datasource
+   configuration:
+<bean id="dataSource"
+       class="org.springframework.jdbc.datasource.DriverManagerDataSou
+rce">
+    <property name="driverClassName" value="${db.driver}"/>
+    <property name="url" value="${db.jdbcurl}"/>
+    <property name="username" value="${db.username}"/>
+    <property name="password" value="${db.password}"/>
+</bean>
+
+   The placeholders are used to provide values for properties of
+   this bean. This means that you can specify all the properties
+   of the configuration in one place instead of entering the
+   values for each property on each bean. We do, however, need
+   one more bean to pull this all together. The last bean is
+   responsible for actually replacing the placeholders with the
+   property values.
+<bean
+ class="org.springframework.beans.factory.config.PropertyPlaceholderCo
+nfigurer">
+    <property name="properties" ref="dbProps"/>
+</bean>
+
+   Now that we have our MySQL data source configured and ready
+   to go, we write some Java code to access it. The example
+   below will retrieve three random cities and their
+   corresponding country using the data source we configured
+   with Spring.
+// Create a new application context. this processes the Spring config
+ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex1appContext.xml");
+// Retrieve the data source from the application context
+    DataSource ds = (DataSource) ctx.getBean("dataSource");
+// Open a database connection using Spring's DataSourceUtils
+Connection c = DataSourceUtils.getConnection(ds);
+try {
+    // retrieve a list of three random cities
+    PreparedStatement ps = c.prepareStatement(
+        "select City.Name as 'City', Country.Name as 'Country' " +
+        "from City inner join Country on City.CountryCode = Country.Co
+de " +
+        "order by rand() limit 3");
+    ResultSet rs = ps.executeQuery();
+    while(rs.next()) {
+        String city = rs.getString("City");
+        String country = rs.getString("Country");
+        System.out.printf("The city %s is in %s%n", city, country);
+    }
+} catch (SQLException ex) {
+    // something has failed and we print a stack trace to analyse the
+error
+    ex.printStackTrace();
+    // ignore failure closing connection
+    try { c.close(); } catch (SQLException e) { }
+} finally {
+    // properly release our connection
+    DataSourceUtils.releaseConnection(c, ds);
+}
+
+   This is very similar to normal JDBC access to MySQL with the
+   main difference being that we are using DataSourceUtils
+   instead of the DriverManager to create the connection.
+
+   While it may seem like a small difference, the implications
+   are somewhat far reaching. Spring manages this resource in a
+   way similar to a container managed data source in a J2EE
+   application server. When a connection is opened, it can be
+   subsequently accessed in other parts of the code if it is
+   synchronized with a transaction. This makes it possible to
+   treat different parts of your application as transactional
+   instead of passing around a database connection.
+
+1.5.2.4.1. Using JdbcTemplate
+
+   Spring makes extensive use of the Template method design
+   pattern (see Template Method Pattern
+   (http://en.wikipedia.org/wiki/Template_method_pattern)). Our
+   immediate focus will be on the JdbcTemplate and related
+   classes, specifically NamedParameterJdbcTemplate. The
+   template classes handle obtaining and releasing a connection
+   for data access when one is needed.
+
+   The next example shows how to use NamedParameterJdbcTemplate
+   inside of a DAO (Data Access Object) class to retrieve a
+   random city given a country code.
+public class Ex2JdbcDao {
+     /**
+     * Data source reference which will be provided by Spring.
+     */
+     private DataSource dataSource;
+
+     /**
+     * Our query to find a random city given a country code. Notice
+     * the ":country" parameter towards the end. This is called a
+     * named parameter.
+     */
+     private String queryString = "select Name from City " +
+        "where CountryCode = :country order by rand() limit 1";
+
+     /**
+     * Retrieve a random city using Spring JDBC access classes.
+     */
+     public String getRandomCityByCountryCode(String cntryCode) {
+         // A template that allows using queries with named parameters
+         NamedParameterJdbcTemplate template =
+         new NamedParameterJdbcTemplate(dataSource);
+         // A java.util.Map is used to provide values for the paramete
+rs
+         Map params = new HashMap();
+         params.put("country", cntryCode);
+         // We query for an Object and specify what class we are expec
+ting
+         return (String)template.queryForObject(queryString, params, S
+tring.class);
+     }
+
+    /**
+    * A JavaBean setter-style method to allow Spring to inject the dat
+a source.
+    * @param dataSource
+    */
+    public void setDataSource(DataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+}
+
+   The focus in the above code is on the
+   getRandomCityByCountryCode() method. We pass a country code
+   and use the NamedParameterJdbcTemplate to query for a city.
+   The country code is placed in a Map with the key "country",
+   which is the parameter is named in the SQL query.
+
+   To access this code, you need to configure it with Spring by
+   providing a reference to the data source.
+<bean id="dao" class="code.Ex2JdbcDao">
+    <property name="dataSource" ref="dataSource"/>
+</bean>
+
+   At this point, we can just grab a reference to the DAO from
+   Spring and call getRandomCityByCountryCode().
+// Create the application context
+    ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex2appContext.xml");
+    // Obtain a reference to our DAO
+    Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao");
+
+    String countryCode = "USA";
+
+    // Find a few random cities in the US
+    for(int i = 0; i < 4; ++i)
+        System.out.printf("A random city in %s is %s%n", countryCode,
+            dao.getRandomCityByCountryCode(countryCode));
+
+   This example shows how to use Spring's JDBC classes to
+   completely abstract away the use of traditional JDBC classes
+   including Connection and PreparedStatement.
+
+1.5.2.4.2. Transactional JDBC Access
+
+   You might be wondering how we can add transactions into our
+   code if we don't deal directly with the JDBC classes. Spring
+   provides a transaction management package that not only
+   replaces JDBC transaction management, but also allows
+   declarative transaction management (configuration instead of
+   code).
+
+   In order to use transactional database access, we will need
+   to change the storage engine of the tables in the world
+   database. The downloaded script explicitly creates MyISAM
+   tables which do not support transactional semantics. The
+   InnoDB storage engine does support transactions and this is
+   what we will be using. We can change the storage engine with
+   the following statements.
+ALTER TABLE City ENGINE=InnoDB;
+ALTER TABLE Country ENGINE=InnoDB;
+ALTER TABLE CountryLanguage ENGINE=InnoDB;
+
+   A good programming practice emphasized by Spring is
+   separating interfaces and implementations. What this means is
+   that we can create a Java interface and only use the
+   operations on this interface without any internal knowledge
+   of what the actual implementation is. We will let Spring
+   manage the implementation and with this it will manage the
+   transactions for our implementation.
+
+   First you create a simple interface:
+public interface Ex3Dao {
+    Integer createCity(String name, String countryCode,
+    String district, Integer population);
+}
+
+   This interface contains one method that will create a new
+   city record in the database and return the id of the new
+   record. Next you need to create an implementation of this
+   interface.
+public class Ex3DaoImpl implements Ex3Dao {
+    protected DataSource dataSource;
+    protected SqlUpdate updateQuery;
+    protected SqlFunction idQuery;
+
+    public Integer createCity(String name, String countryCode,
+        String district, Integer population) {
+            updateQuery.update(new Object[] { name, countryCode,
+                   district, population });
+            return getLastId();
+        }
+
+    protected Integer getLastId() {
+        return idQuery.run();
+    }
+}
+
+   You can see that we only operate on abstract query objects
+   here and don't deal directly with the JDBC API. Also, this is
+   the complete implementation. All of our transaction
+   management will be dealt with in the configuration. To get
+   the configuration started, we need to create the DAO.
+<bean id="dao" class="code.Ex3DaoImpl">
+    <property name="dataSource" ref="dataSource"/>
+    <property name="updateQuery">...</property>
+    <property name="idQuery">...</property>
+</bean>
+
+   Now you need to setup the transaction configuration. The
+   first thing you must do is create transaction manager to
+   manage the data source and a specification of what
+   transaction properties are required for for the dao methods.
+<bean id="transactionManager"
+  class="org.springframework.jdbc.datasource.DataSourceTransactionMana
+ger">
+    <property name="dataSource" ref="dataSource"/>
+</bean>
+
+<tx:advice id="txAdvice" transaction-manager="transactionManager">
+    <tx:attributes>
+        <tx:method name="*"/>
+    </tx:attributes>
+</tx:advice>
+
+   The preceding code creates a transaction manager that handles
+   transactions for the data source provided to it. The txAdvice
+   uses this transaction manager and the attributes specify to
+   create a transaction for all methods. Finally you need to
+   apply this advice with an AOP pointcut.
+<aop:config>
+    <aop:pointcut id="daoMethods"
+        expression="execution(* code.Ex3Dao.*(..))"/>
+     <aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/>
+</aop:config>
+
+   This basically says that all methods called on the Ex3Dao
+   interface will be wrapped in a transaction. To make use of
+   this, you only have to retrieve the dao from the application
+   context and call a method on the dao instance.
+Ex3Dao dao = (Ex3Dao) ctx.getBean("dao");
+Integer id = dao.createCity(name,  countryCode, district, pop);
+
+   We can verify from this that there is no transaction
+   management happening in our Java code and it's all configured
+   with Spring. This is a very powerful notion and regarded as
+   one of the most beneficial features of Spring.
+
+1.5.2.4.3. Connection Pooling
+
+   In many sitations, such as web applications, there will be a
+   large number of small database transactions. When this is the
+   case, it usually makes sense to create a pool of database
+   connections available for web requests as needed. Although
+   MySQL does not spawn an extra process when a connection is
+   made, there is still a small amount of overhead to create and
+   setup the connection. Pooling of connections also alleviates
+   problems such as collecting large amounts of sockets in the
+   TIME_WAIT state.
+
+   Setting up pooling of MySQL connections with Spring is as
+   simple as changing the data source configuration in the
+   application context. There are a number of configurations
+   that we can use. The first example is based on the Jakarta
+   Commons DBCP library
+   (http://jakarta.apache.org/commons/dbcp/). The example below
+   replaces the source configuration that was based on
+   DriverManagerDataSource with DBCP's BasicDataSource.
+<bean id="dataSource" destroy-method="close"
+  class="org.apache.commons.dbcp.BasicDataSource">
+    <property name="driverClassName" value="${db.driver}"/>
+    <property name="url" value="${db.jdbcurl}"/>
+    <property name="username" value="${db.username}"/>
+    <property name="password" value="${db.password}"/>
+    <property name="initialSize" value="3"/>
+</bean>
+
+   The configuration of the two solutions is very similar. The
+   difference is that DBCP will pool connections to the database
+   instead of creating a new connection every time one is
+   requested. We have also set a parameter here called
+   initialSize. This tells DBCP that we want three connections
+   in the pool when it is created.
+
+   Another way to configure connection pooling is to configure a
+   data source in our J2EE application server. Using JBoss as an
+   example, you can set up the MySQL connection pool by creating
+   a file called mysql-local-ds.xml and placing it in the
+   server/default/deploy directory in JBoss. Once we have this
+   setup, we can use JNDI to look it up. With Spring, this
+   lookup is very simple. The data source configuration looks
+   like this.
+<jee:jndi-lookup id="dataSource" jndi-name="java:MySQL_DS"/>
+
 1.5.3. Common Problems and Solutions
 
    There are a few issues that seem to be commonly encountered
@@ -2509,7 +3316,41 @@
      * [5]1.5.3.5: I'm trying to use JDBC-2.0 updatable result
        sets, and I get an exception saying my result set is not
        updatable.
+     * [6]1.5.3.6: I cannot connect to the MySQL server using
+       Connector/J, and I'm sure the connection paramters are
+       correct.
+     * [7]1.5.3.7: I am trying to connect to my MySQL server
+       within my application, but I get the following error and
+       stack trace:
+java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
 
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv faile
+d
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.<init>(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.ja
+va:411)
+     * [8]1.5.3.8: My application is deployed through JBoss and
+       I am using transactions to handle the statements on the
+       MySQL database. Under heavy loads I am getting a error
+       and stack trace, but these only occur after a fixed
+       period of heavy activity.
+     * [9]1.5.3.9: When using gcj an
+       java.io.CharConversionException is raised when working
+       with certain character sequences.
+     * [10]1.5.3.10: Updating a table that contains a primary
+       key that is either FLOAT or compound primary key that
+       uses FLOAT fails to update the table and raises an
+       exception.
+
    Questions and Answers
 
    1.5.3.1: When I try to connect to the database with MySQL
@@ -2529,27 +3370,32 @@
 
    You must add the necessary security credentials to the MySQL
    server for this to happen, using the GRANT statement to your
-   MySQL Server. See [WARNING: missing xref target (id=grant)]
-   for more information.
+   MySQL Server. See GRANT Syntax
+   (http://dev.mysql.com/doc/refman/5.0/en/grant.html), for more
+   information.
 
-   Note.  Testing your connectivity with the mysql command-line
-   client will not work unless you add the --host flag, and use
+Note
+
+   Testing your connectivity with the mysql command-line client
+   will not work unless you add the --host flag, and use
    something other than localhost for the host. The mysql
    command-line client will use Unix domain sockets if you use
    the special hostname localhost. If you are testing
    connectivity to localhost, use 127.0.0.1 as the hostname
    instead.
 
-   Warning.  Changing privileges and permissions improperly in
-   MySQL can potentially cause your server installation to not
-   have optimal security properties.
+Warning
 
+   Changing privileges and permissions improperly in MySQL can
+   potentially cause your server installation to not have
+   optimal security properties.
+
    1.5.3.2: My application throws an SQLException 'No Suitable
    Driver'. Why is this happening?
 
    There are three possible causes for this error:
      * The Connector/J driver is not in your CLASSPATH, see
-       Section A.2, "Installing Connector/J."
+       Section Section, "Connector/J Installation."
      * The format of your connection URL is incorrect, or you
        are referencing the wrong JDBC driver.
      * When using DriverManager, the jdbc.drivers system
@@ -2603,7 +3449,7 @@
    MySQL closes connections after 8 hours of inactivity. You
    either need to use a connection pool that handles stale
    connections or use the "autoReconnect" parameter (see Section
-   A.4.1, "Driver/Datasource Class Names, URL Syntax and
+   Section, "Driver/Datasource Class Names, URL Syntax and
    Configuration Properties for Connector/J").
 
    Also, you should be catching SQLExceptions in your
@@ -2621,146 +3467,138 @@
 
    Example 12. Example of transaction with retry logic
 public void doBusinessOp() throws SQLException {
-        Connection conn = null;
-        Statement stmt = null;
-        ResultSet rs = null;
+    Connection conn = null;
+    Statement stmt = null;
+    ResultSet rs = null;
 
-        //
-        // How many times do you want to retry the transaction
-        // (or at least _getting_ a connection)?
-        //
-        int retryCount = 5;
+    //
+    // How many times do you want to retry the transaction
+    // (or at least _getting_ a connection)?
+    //
+    int retryCount = 5;
 
-        boolean transactionCompleted = false;
+    boolean transactionCompleted = false;
 
-        do {
-            try {
-                conn = getConnection(); // assume getting this from a
-                                        // javax.sql.DataSource, or th
-e
-                                        // java.sql.DriverManager
+    do {
+        try {
+            conn = getConnection(); // assume getting this from a
+                                    // javax.sql.DataSource, or the
+                                    // java.sql.DriverManager
 
-                conn.setAutoCommit(false);
+            conn.setAutoCommit(false);
 
-                //
-                // Okay, at this point, the 'retry-ability' of the
-                // transaction really depends on your application logi
-c,
-                // whether or not you're using autocommit (in this cas
-e
-                // not), and whether you're using transacational stora
-ge
-                // engines
-                //
-                // For this example, we'll assume that it's _not_ safe
-                // to retry the entire transaction, so we set retry co
-unt
-                // to 0 at this point
-                //
-                // If you were using exclusively transaction-safe tabl
-es,
-                // or your application could recover from a connection
- going
-                // bad in the middle of an operation, then you would n
-ot
-                // touch 'retryCount' here, and just let the loop repe
-at
-                // until retryCount == 0.
-                //
-                retryCount = 0;
+            //
+            // Okay, at this point, the 'retry-ability' of the
+            // transaction really depends on your application logic,
+            // whether or not you're using autocommit (in this case
+            // not), and whether you're using transacational storage
+            // engines
+            //
+            // For this example, we'll assume that it's _not_ safe
+            // to retry the entire transaction, so we set retry
+            // count to 0 at this point
+            //
+            // If you were using exclusively transaction-safe tables,
+            // or your application could recover from a connection goi
+ng
+            // bad in the middle of an operation, then you would not
+            // touch 'retryCount' here, and just let the loop repeat
+            // until retryCount == 0.
+            //
+            retryCount = 0;
 
-                stmt = conn.createStatement();
+            stmt = conn.createStatement();
 
-                String query = "SELECT foo FROM bar ORDER BY baz";
+            String query = "SELECT foo FROM bar ORDER BY baz";
 
-                rs = stmt.executeQuery(query);
+            rs = stmt.executeQuery(query);
 
-                while (rs.next()) {
-                }
+            while (rs.next()) {
+            }
 
-                rs.close();
-                rs = null;
+            rs.close();
+            rs = null;
 
-                stmt.close();
-                stmt = null;
+            stmt.close();
+            stmt = null;
 
-                conn.commit();
-                conn.close();
-                conn = null;
+            conn.commit();
+            conn.close();
+            conn = null;
 
-                transactionCompleted = true;
-            } catch (SQLException sqlEx) {
+            transactionCompleted = true;
+        } catch (SQLException sqlEx) {
 
-                //
-                // The two SQL states that are 'retry-able' are 08S01
-                // for a communications error, and 40001 for deadlock.
-                //
-                // Only retry if the error was due to a stale connecti
-on,
-                // communications problem or deadlock
-                //
+            //
+            // The two SQL states that are 'retry-able' are 08S01
+            // for a communications error, and 40001 for deadlock.
+            //
+            // Only retry if the error was due to a stale connection,
+            // communications problem or deadlock
+            //
 
-                String sqlState = sqlEx.getSQLState();
+            String sqlState = sqlEx.getSQLState();
 
-                if ("08S01".equals(sqlState) || "40001".equals(sqlStat
-e)) {
-                    retryCount--;
-                } else {
-                    retryCount = 0;
+            if ("08S01".equals(sqlState) || "40001".equals(sqlState))
+{
+                retryCount--;
+            } else {
+                retryCount = 0;
+            }
+        } finally {
+            if (rs != null) {
+                try {
+                    rs.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this . . .
                 }
-            } finally {
-                if (rs != null) {
-                    try {
-                        rs.close();
-                    } catch (SQLException sqlEx) {
-                        // You'd probably want to log this . . .
-                    }
+            }
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this as well . . .
                 }
+            }
 
-                if (stmt != null) {
+            if (conn != null) {
+                try {
+                    //
+                    // If we got here, and conn is not null, the
+                    // transaction should be rolled back, as not
+                    // all work has been done
+
                     try {
-                        stmt.close();
-                    } catch (SQLException sqlEx) {
-                        // You'd probably want to log this as well . .
- .
+                        conn.rollback();
+                    } finally {
+                        conn.close();
                     }
-                }
+                } catch (SQLException sqlEx) {
+                    //
+                    // If we got an exception here, something
+                    // pretty serious is going on, so we better
+                    // pass it up the stack, rather than just
+                    // logging it. . .
 
-                if (conn != null) {
-                    try {
-                        //
-                        // If we got here, and conn is not null, the
-                        // transaction should be rolled back, as not
-                        // all work has been done
-
-                        try {
-                            conn.rollback();
-                        } finally {
-                            conn.close();
-                        }
-                    } catch (SQLException sqlEx) {
-                        //
-                        // If we got an exception here, something
-                        // pretty serious is going on, so we better
-                        // pass it up the stack, rather than just
-                        // logging it. . .
-
-                        throw sqlEx;
-                    }
+                    throw sqlEx;
                 }
             }
-        } while (!transactionCompleted && (retryCount > 0));
-    }
+        }
+    } while (!transactionCompleted && (retryCount > 0));
+}
 
-   Note.  Use of the autoReconnect option is not recommended
-   because there is no safe method of reconnecting to the MySQL
-   server without risking some corruption of the connection
-   state or database state information. Instead, you should use
-   a connection pool which will enable your application to
-   connect to the MySQL server using an available connection
-   from the pool. The autoReconnect facility is deprecated, and
-   may be removed in a future release.
+Note
 
+   Use of the autoReconnect option is not recommended because
+   there is no safe method of reconnecting to the MySQL server
+   without risking some corruption of the connection state or
+   database state information. Instead, you should use a
+   connection pool which will enable your application to connect
+   to the MySQL server using an available connection from the
+   pool. The autoReconnect facility is deprecated, and may be
+   removed in a future release.
+
    1.5.3.5: I'm trying to use JDBC-2.0 updatable result sets,
    and I get an exception saying my result set is not updatable.
 
@@ -2780,6 +3618,96 @@
    where you can individually specify the criteria to be matched
    using a WHERE clause.
 
+   1.5.3.6: I cannot connect to the MySQL server using
+   Connector/J, and I'm sure the connection paramters are
+   correct.
+
+   Make sure that the skip-networking option has not been
+   enabled on your server. Connector/J must be able to
+   communicate with your server over TCP/IP, named sockets are
+   not supported. Also ensure that you are not filtering
+   connections through a Firewall or other network security
+   system. For more informaiton, see Can't connect to [local]
+   MySQL server
+   (http://dev.mysql.com/doc/refman/5.0/en/can-not-connect-to-se
+   rver.html).
+
+   1.5.3.7: I am trying to connect to my MySQL server within my
+   application, but I get the following error and stack trace:
+java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
+
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv faile
+d
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.<init>(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.ja
+va:411)
+
+   The error probably indicates that you are using a older
+   version of the Connector/J JDBC driver (2.0.14 or 3.0.x) and
+   you are trying to connect to a MySQL server with version 4.1x
+   or newer. The older drivers are not compatible with 4.1 or
+   newer of MySQL as they do not support the newer
+   authentication mechanisms.
+
+   It is likely that the older version of the Connector/J driver
+   exists within your application directory or your CLASSPATH
+   includes the older Connector/J package.
+
+   1.5.3.8: My application is deployed through JBoss and I am
+   using transactions to handle the statements on the MySQL
+   database. Under heavy loads I am getting a error and stack
+   trace, but these only occur after a fixed period of heavy
+   activity.
+
+   This is a JBoss, not Connector/J, issue and is connected to
+   the use of transactions. Under heavy loads the time taken for
+   transactions to complete can increase, and the error is
+   caused because you have exceeded the predefined timeout.
+
+   You can increase the timeout value by setting the
+   TransactionTimeout attribute to the TransactionManagerService
+   within the /conf/jboss-service.xml file (pre-4.0.3) or
+   /deploy/jta-service.xml for JBoss 4.0.3 or later. See
+   TransactionTimeoute
+   (http://wiki.jboss.org/wiki/Wiki.jsp?page=TransactionTimeout)
+   within the JBoss wiki for more information.
+
+   1.5.3.9: When using gcj an java.io.CharConversionException is
+   raised when working with certain character sequences.
+
+   This is a known issue with gcj which raises an exception when
+   it reaches an unknown character or one it cannot convert. You
+   should add useJvmCharsetConverters=true to your connection
+   string to force character conversion outside of the gcj
+   libraries, or try a different JDK.
+
+   1.5.3.10: Updating a table that contains a primary key that
+   is either FLOAT or compound primary key that uses FLOAT fails
+   to update the table and raises an exception.
+
+   Connector/J adds conditions to the WHERE clause during an
+   UPDATE to check the old values of the primary key. If there
+   is no match then Connector/J considers this a failure
+   condition and raises an exception.
+
+   The problem is that rounding differences between supplied
+   values and the values stored in the database may mean that
+   the values never match, and hence the update fails. The issue
+   will affect all queries, not just those from Connector/J.
+
+   To prevent this issue, use a primary key that does not use
+   FLOAT. If you have to use a floating point column in your
+   primary key use DOUBLE or DECIMAL types in place of FLOAT.
+
 1.6. Connector/J Support
 
 1.6.1. Connector/J Community Support
@@ -2926,8 +3854,13 @@
 
 References
 
-   1. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-1
-   2. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-2
-   3. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-3
-   4. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-4
-   5. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-5
+   1. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-1
+   2. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-2
+   3. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-3
+   4. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-4
+   5. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-5
+   6. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-6
+   7. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-7
+   8. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-8
+   9. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-9
+  10. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-10

Modified: trunk/mysql-connector-java/docs/README.txt
===================================================================
--- trunk/mysql-connector-java/docs/README.txt	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/docs/README.txt	2007-11-30 10:46:51 UTC (rev 4921)
@@ -34,10 +34,52 @@
        BCShortCourse/index.html) --- A more in-depth tutorial
        from Sun and JGuru
 
+   Key topics:
+     * For help with connection strings, connection options
+       setting up your connection through JDBC, see Section
+       Section, "Driver/Datasource Class Names, URL Syntax and
+       Configuration Properties for Connector/J."
+     * For tips on using Connector/J and JDBC with generic J2EE
+       toolkits, see Section Section, "Using Connector/J with
+       J2EE and Other Java Frameworks."
+     * Developers using the Tomcat server platform, see Section
+       Section, "Using Connector/J with Tomcat."
+     * Developers using JBoss, see Section Section, "Using
+       Connector/J with JBoss."
+     * Developers using Spring, see Section Section, "Using
+       Connector/J with Spring."
+
+   MySQL Enterprise MySQL Enterprise subscribers will find more
+   information about using JDBC with MySQL in the Knowledge Base
+   articles about JDBC
+   (https://kb.mysql.com/search.php?cat=search&category=10).
+   Access to the MySQL Knowledge Base collection of articles is
+   one of the advantages of subscribing to MySQL Enterprise. For
+   more information see
+   http://www.mysql.com/products/enterprise/advisors.html.
+
 1.1. Connector/J Versions
 
-   There are currently three version of MySQL Connector/J
+   There are currently four versions of MySQL Connector/J
    available:
+     * Connector/J 5.1 is current in alpha status. It provides
+       compatibility with all the functionality of MySQL,
+       including 4.1, 5.0, 5.1 and the 6.0 alpha release
+       featuring the new Falcon storage engine. Connector/J 5.1
+       provides ease of development features, including
+       auto-registration with the Driver Manager, standardized
+       validity checks, categorized SQLExceptions, support for
+       the JDBC-4.0 XML processing, per connection client
+       information, NCHAR, NVARCHAR and NCLOB types. This
+       release also includes all bug fixes up to and including
+       Connector/J 5.0.6.
+     * Connector/J 5.0 provides support for all the
+       functionality offered by Connector/J 3.1 and includes
+       distributed transaction (XA) support.
+     * Connector/J 3.1 was designed for connectivity to MySQL
+       4.1 and MySQL 5.0 servers and provides support for all
+       the functionality in MySQL 5.0 except distributed
+       transaction (XA) support.
      * Connector/J 3.0 provides core functionality and was
        designed with connectivity to MySQL 3.x or MySQL 4.1
        servers, although it will provide basic compatibility
@@ -45,13 +87,6 @@
        support server-side prepared statements, and does not
        support any of the features in versions of MySQL later
        than 4.1.
-     * Connector/J 3.1 was designed for connectivity to MySQL
-       4.1 and MySQL 5.0 servers and provides support for all
-       the functionality in MySQL 5.0 except distributed
-       transaction (XA) support.
-     * Connector/J 5.0 provides support for all the
-       functionality offered by Connector/J 3.1 and includes
-       distributed transaction (XA) support.
 
    The current recommended version for Connector/J is 5.0. This
    guide covers all three connector versions, with specific
@@ -66,11 +101,11 @@
      * JDK 1.5.x
 
    If you are building Connector/J from source using the source
-   distribution (see Section A.2.4, "Installing from the
+   distribution (see Section Section, "Installing from the
    Development Source Tree") then you must use JDK 1.4.x or
    newer to compiler the Connector package.
 
-   MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x
+   MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x.
 
    Because of the implementation of java.sql.Savepoint,
    Connector/J 3.1.0 and newer will not run on JDKs older than
@@ -85,13 +120,14 @@
    on java.util.LinkedHashMap which was first available in
    JDK-1.4.0.
 
-1.2. Installing Connector/J
+1.2. Connector/J Installation
 
    You can install the Connector/J package using two methods,
    using either the binary or source distribution. The binary
    distribution provides the easiest methods for installation;
    the source distribution enables you to customize your
-   installation further. With with either solution, you must
+   installation further. With either solution, you must manually
+   add the Connector/J location to your Java CLASSPATH.
 
 1.2.1. Installing Connector/J from a Binary Distribution
 
@@ -100,7 +136,7 @@
    distribution is available either as a Tar/Gzip or Zip file
    which you must extract to a suitable location and then
    optionally make the information about the package available
-   by changing your CLASSPATH (see Section A.2.2, "Installing
+   by changing your CLASSPATH (see Section Section, "Installing
    the Driver and Configuring the CLASSPATH").
 
    MySQL Connector/J is distributed as a .zip or .tar.gz archive
@@ -114,15 +150,15 @@
    driver JAR file.
 
    You should not use the debug build of the driver unless
-   instructed to do so when reporting a problem ors bug to MySQL
-   AB, as it is not designed to be run in production
+   instructed to do so when reporting a problem or a bug to
+   MySQL AB, as it is not designed to be run in production
    environments, and will have adverse performance impact when
    used. The debug binary also depends on the Aspect/J runtime
    library, which is located in the src/lib/aspectjrt.jar file
    that comes with the Connector/J distribution.
 
    You will need to use the appropriate graphical or
-   command-line utility to un-archive the distribution (for
+   command-line utility to extract the distribution (for
    example, WinZip for the .zip archive, and tar for the .tar.gz
    archive). Because there are potentially long filenames in the
    distribution, we use the GNU tar archive format. You will
@@ -143,19 +179,19 @@
    DriverManager, you would use com.mysql.jdbc.Driver as the
    class that implements java.sql.Driver.
 
-   You can set the CLASSPATH environment variableunder UNIX,
+   You can set the CLASSPATH environment variable under UNIX,
    Linux or Mac OS X either locally for a user within their
    .profile, .login or other login file. You can also set it
    globally by editing the global /etc/profile file.
 
    For example, under a C shell (csh, tcsh) you would add the
    Connector/J driver to your CLASSPATH using the following:
-shell> setenv CLASSPATH /path/to/mysql-connector-java-[version]-bin.ja
-r:$CLASSPATH
+shell> setenv CLASSPATH /path/mysql-connector-java-[ver]-bin.jar:$CLAS
+SPATH
 
    Or with a Bourne-compatible shell (sh, ksh, bash):
-export set CLASSPATH=/path/to/mysql-connector-java-[version]-bin.jar:$
-CLASSPATH
+export set CLASSPATH=/path/mysql-connector-java-[ver]-bin.jar:$CLASSPA
+TH
 
    Within Windows 2000, Windows XP and Windows Server 2003, you
    must set the environment variable through the System control
@@ -167,7 +203,7 @@
    configure third-party class libraries, as most application
    servers ignore the CLASSPATH environment variable. For
    configuration examples for some J2EE application servers, see
-   Section A.5.2, "Using Connector/J with J2EE and Other Java
+   Section Section, "Using Connector/J with J2EE and Other Java
    Frameworks." However, the authoritative source for JDBC
    connection pool configuration information for your particular
    application server is the documentation for that application
@@ -221,8 +257,9 @@
    server and various performance enhancements that can be
    enabled or disabled via configuration properties.
      * Unicode Character Sets --- See the next section, as well
-       as [WARNING: missing xref target (id=charset)] for
-       information on this new feature of MySQL. If you have
+       as Character Set Support
+       (http://dev.mysql.com/doc/refman/5.0/en/charset.html),
+       for information on this new feature of MySQL. If you have
        something misconfigured, it will usually show up as an
        error with a message similar to Illegal mix of
        collations.
@@ -328,11 +365,13 @@
 
 1.2.4. Installing from the Development Source Tree
 
-   Caution.  You should read this section only if you are
-   interested in helping us test our new code. If you just want
-   to get MySQL Connector/J up and running on your system, you
-   should use a standard release distribution.
+Caution
 
+   You should read this section only if you are interested in
+   helping us test our new code. If you just want to get MySQL
+   Connector/J up and running on your system, you should use a
+   standard release distribution.
+
    To install MySQL Connector/J from the development source
    tree, make sure that you have the following prerequisites:
      * Subversion, to check out the sources from our repository
@@ -382,34 +421,35 @@
        build/mysql-connector-java-[version].
        Install the newly created JDBC driver as you would a
        binary .jar file that you download from MySQL by
-       following the instructions in Section A.2.2, "Installing
-       the Driver and Configuring the CLASSPATH."
+       following the instructions in Section Section,
+       "Installing the Driver and Configuring the CLASSPATH."
 
 1.3. Connector/J Examples
 
    Examples of using Connector/J are located throughout this
    document, this section provides a summary and links to these
    examples.
-     * Section A.5.1.1, "Obtaining a connection from the
+     * Example Section, "Obtaining a connection from the
        DriverManager"
-     * Section A.5.1.2, "Using java.sql.Statement to execute a
+     * Example Section, "Using java.sql.Statement to execute a
        SELECT query"
-     * Section A.5.1.3, "Stored Procedures"
-     * Section A.5.1.3, "Using Connection.prepareCall()"
-     * Section A.5.1.3, "Registering output parameters"
-     * Section A.5.1.3, "Setting CallableStatement input
+     * Example Section, "Stored Procedures"
+     * Example Section, "Using Connection.prepareCall()"
+     * Example Section, "Registering output parameters"
+     * Example Section, "Setting CallableStatement input
        parameters"
-     * Section A.5.1.3, "Retrieving results and output parameter
+     * Example Section, "Retrieving results and output parameter
        values"
-     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+     * Example Section, "Retrieving AUTO_INCREMENT column values
        using Statement.getGeneratedKeys()"
-     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+     * Example Section, "Retrieving AUTO_INCREMENT column values
        using SELECT LAST_INSERT_ID()"
-     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+     * Example Section, "Retrieving AUTO_INCREMENT column values
        in Updatable ResultSets"
-     * Section A.5.2.1.1, "Using a connection pool with a J2EE
+     * Example Section, "Using a connection pool with a J2EE
        application server"
-     * Section A.5.3, "Example of transaction with retry logic"
+     * Example Section, "Example of transaction with retry
+       logic"
 
 1.4. Connector/J (JDBC) Reference
 
@@ -493,17 +533,19 @@
        java.sql.DriverManager.getConnection(),
        java.sql.Driver.connect() or the MySQL implementations of
        the javax.sql.DataSource setURL() method.
-       Note.  If the mechanism you use to configure a JDBC URL
-       is XML-based, you will need to use the XML character
-       literal &amp; to separate configuration parameters, as
-       the ampersand is a reserved character for XML.
 
+Note
+       If the mechanism you use to configure a JDBC URL is
+       XML-based, you will need to use the XML character literal
+       &amp; to separate configuration parameters, as the
+       ampersand is a reserved character for XML.
+
    The properties are listed in the following tables.
 
    Connection/Authentication.
    Property Name Definition Default Value Since Version
-   user The user to connect as all
-   password The password to use when connecting all
+   user The user to connect as   all versions
+   password The password to use when connecting   all versions
    socketFactory The name of the class that the driver should
    use for creating socket connections to the server. This class
    must implement the interface 'com.mysql.jdbc.SocketFactory'
@@ -514,20 +556,52 @@
    Defaults to '0'. 0 3.0.1
    socketTimeout Timeout on network socket operations (0, the
    default means no timeout). 0 3.0.1
+   connectionLifecycleInterceptors A comma-delimited list of
+   classes that implement
+   "com.mysql.jdbc.ConnectionLifecycleInterceptor" that should
+   notified of connection lifecycle events (creation,
+   destruction, commit, rollback, setCatalog and setAutoCommit)
+   and potentially alter the execution of these commands.
+   ConnectionLifecycleInterceptors are "stackable", more than
+   one interceptor may be specified via the configuration
+   property as a comma-delimited list, with the interceptors
+   executed in order from left to right.   5.1.4
    useConfigs Load the comma-delimited list of configuration
    properties before parsing the URL or applying user-specified
    properties. These configurations are explained in the
-   'Configurations' of the documentation. 3.1.5
+   'Configurations' of the documentation.   3.1.5
    interactiveClient Set the CLIENT_INTERACTIVE flag, which
    tells MySQL to timeout connections based on
    INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT false 3.1.0
+   localSocketAddress Hostname or IP address given to explicitly
+   configure the interface that the driver will bind the client
+   side of the TCP/IP connection to when connecting.   5.0.5
    propertiesTransform An implementation of
    com.mysql.jdbc.ConnectionPropertiesTransform that the driver
    will use to modify URL properties passed to the driver before
-   attempting a connection 3.1.4
+   attempting a connection   3.1.4
    useCompression Use zlib compression when communicating with
    the server (true/false)? Defaults to 'false'. false 3.0.17
 
+   Networking.
+   Property Name Definition Default Value Since Version
+   tcpKeepAlive If connecting using TCP/IP, should the driver
+   set SO_KEEPALIVE? true 5.0.7
+   tcpNoDelay If connecting using TCP/IP, should the driver set
+   SO_TCP_NODELAY (disabling the Nagle Algorithm)? true 5.0.7
+   tcpRcvBuf If connecting using TCP/IP, should the driver set
+   SO_RCV_BUF to the given value? The default value of '0',
+   means use the platform default value for this property) 0
+   5.0.7
+   tcpSndBuf If connecting using TCP/IP, shuold the driver set
+   SO_SND_BUF to the given value? The default value of '0',
+   means use the platform default value for this property) 0
+   5.0.7
+   tcpTrafficClass If connecting using TCP/IP, should the driver
+   set traffic class or type-of-service fields ?See the
+   documentation for java.net.Socket.setTrafficClass() for more
+   information. 0 5.0.7
+
    High Availability and Clustering.
    Property Name Definition Default Value Since Version
    autoReconnect Should the driver try to re-establish stale
@@ -537,20 +611,25 @@
    reconnect before the next query issued on the connection in a
    new transaction. The use of this feature is not recommended,
    because it has side effects related to session state and data
-   consistency when applications don'thandle SQLExceptions
+   consistency when applications don't handle SQLExceptions
    properly, and is only designed to be used when you are unable
    to configure your application to handle SQLExceptions
-   resulting from dead andstale connections properly.
+   resulting from dead and stale connections properly.
    Alternatively, investigate setting the MySQL server variable
-   "wait_timeout"to some high value rather than the default of 8
-   hours. false 1.1
+   "wait_timeout" to some high value rather than the default of
+   8 hours. false 1.1
    autoReconnectForPools Use a reconnection strategy appropriate
    for connection pools (defaults to 'false') false 3.1.3
    failOverReadOnly When failing over in autoReconnect mode,
    should the connection be set to 'read-only'? true 3.0.12
+   maxReconnects Maximum number of reconnects to attempt if
+   autoReconnect is true, default is '3'. 3 1.1
    reconnectAtTxEnd If autoReconnect is set to true, should the
-   driver attempt reconnectionsat the end of every transaction?
+   driver attempt reconnections at the end of every transaction?
    false 3.0.10
+   initialTimeout If autoReconnect is enabled, the initial time
+   to wait between re-connect attempts (in seconds, defaults to
+   '2'). 2 1.1
    roundRobinLoadBalance When autoReconnect is enabled, and
    failoverReadonly is false, should we pick hosts to connect to
    on a round-robin basis? false 3.1.2
@@ -561,31 +640,46 @@
    cause an attempt to be made to reconnect to the master.
    Defaults to 50. 50 3.0.2
    secondsBeforeRetryMaster How long should the driver wait,
-   when failed over, before attempting to reconnect to the
-   master server? Whichever condition is met first,
-   'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will
-   cause an attempt to be made to reconnect to the master. Time
-   in seconds, defaults to 30 30 3.0.2
-   enableDeprecatedAutoreconnect Auto-reconnect functionality is
-   deprecated starting with version 3.2, and will be removed in
-   version 3.3. Set this property to 'true' to disable the check
-   for the feature being configured. false 3.2.1
+   when failed over, before attempting 30 3.0.2
    resourceId A globally unique name that identifies the
    resource that this datasource or connection is connected to,
    used for XAResource.isSameRM() when the driver can't
-   determine this value based on hostnames used in the URL 5.0.1
+   determine this value based on hostnames used in the URL
+   5.0.1
 
    Security.
    Property Name Definition Default Value Since Version
    allowMultiQueries Allow the use of ';' to delimit multiple
-   queries during one statement (true/false, defaults to 'false'
-   false 3.1.1
+   queries during one statement (true/false), defaults to
+   'false' false 3.1.1
    useSSL Use SSL when communicating with the server
    (true/false), defaults to 'false' false 3.0.2
    requireSSL Require SSL connection if useSSL=true? (defaults
    to 'false'). false 3.1.0
+   allowLoadLocalInfile Should the driver allow use of 'LOAD
+   DATA LOCAL INFILE...' (defaults to 'true'). true 3.0.3
    allowUrlInLocalInfile Should the driver allow URLs in 'LOAD
    DATA LOCAL INFILE' statements? false 3.1.4
+   clientCertificateKeyStorePassword Password for the client
+   certificates KeyStore   5.1.0
+   clientCertificateKeyStoreType KeyStore type for client
+   certificates (NULL or empty means use default, standard
+   keystore types supported by the JVM are "JKS" and "PKCS12",
+   your environment may have more available depending on what
+   security products are installed and available to the JVM.
+   5.1.0
+   clientCertificateKeyStoreUrl URL to the client certificate
+   KeyStore (if not specified, use defaults)   5.1.0
+   trustCertificateKeyStorePassword Password for the trusted
+   root certificates KeyStore   5.1.0
+   trustCertificateKeyStoreType KeyStore type for trusted root
+   certificates (NULL or empty means use default, standard
+   keystore types supported by the JVM are "JKS" and "PKCS12",
+   your environment may have more available depending on what
+   security products are installed and available to the JVM.
+   5.1.0
+   trustCertificateKeyStoreUrl URL to the trusted root
+   certificate KeyStore (if not specified, use defaults)   5.1.0
    paranoid Take measures to prevent exposure sensitive
    information in error messages and clear data structures
    holding sensitive data when possible? (defaults to 'false')
@@ -593,14 +687,29 @@
 
    Performance Extensions.
    Property Name Definition Default Value Since Version
-   metadataCacheSize The number of queries to
-   cacheResultSetMetadata for if cacheResultSetMetaData is set
-   to 'true' (default 50) 50 3.1.1
+   callableStmtCacheSize If 'cacheCallableStmts' is enabled, how
+   many callable statements should be cached? 100 3.1.2
+   metadataCacheSize The number of queries to cache
+   ResultSetMetadata for if cacheResultSetMetaData is set to
+   'true' (default 50) 50 3.1.1
    prepStmtCacheSize If prepared statement caching is enabled,
    how many prepared statements should be cached? 25 3.0.10
    prepStmtCacheSqlLimit If prepared statement caching is
    enabled, what's the largest SQL the driver will cache the
    parsing for? 256 3.0.10
+   alwaysSendSetIsolation Should the driver always communicate
+   with the database when Connection.setTransactionIsolation()
+   is called? If set to false, the driver will only communicate
+   with the database when the requested transaction isolation is
+   different than the whichever is newer, the last value that
+   was set via Connection.setTransactionIsolation(), or the
+   value that was read from the server when the connection was
+   established. true 3.1.7
+   maintainTimeStats Should the driver maintain various internal
+   timers to enable idle time calculations as well as more
+   verbose error messages when the connection to the server
+   fails? Setting this property to false removes at least two
+   calls to System.getCurrentTimeMillis() per query. true 3.1.9
    useCursorFetch If connected to MySQL > 5.0.2, and
    setFetchSize() > 0 on a statement, should that statement use
    cursor-based fetching to retrieve rows? false 5.0.0
@@ -634,9 +743,33 @@
    driver only issue 'set autocommit=n' queries when the
    server's state doesn't match the requested state by
    Connection.setAutoCommit(boolean)? false 3.1.3
+   enableQueryTimeouts When enabled, query timeouts set via
+   Statement.setQueryTimeout() use a shared java.util.Timer
+   instance for scheduling. Even if the timeout doesn't expire
+   before the query is processed, there will be memory used by
+   the TimerTask for the given timeout which won't be reclaimed
+   until the time the timeout would have expired if it hadn't
+   been cancelled by the driver. High-load environments might
+   want to consider disabling this functionality. true 5.0.6
    holdResultsOpenOverStatementClose Should the driver close
    result sets on Statement.close() as required by the JDBC
    specification? false 3.1.7
+   largeRowSizeThreshold What size result set row should the
+   JDBC driver consider "large", and thus use a more
+   memory-efficient way of representing the row internally? 2048
+   5.1.1
+   loadBalanceStrategy If using a load-balanced connection to
+   connect to SQL nodes in a MySQL Cluster/NDB configuration (by
+   using the URL prefix "jdbc:mysql:loadbalance://"), which load
+   balancing algorithm should the driver use: (1) "random" - the
+   driver will pick a random host for each request. This tends
+   to work better than round-robin, as the randomness will
+   somewhat account for spreading loads where requests vary in
+   response time, while round-robin can sometimes lead to
+   overloaded nodes if there are variations in response times
+   across the workload. (2) "bestResponseTime" - the driver will
+   route the request to the host that had the best response time
+   for the previous transaction. random 5.0.6
    locatorFetchBufferSize If 'emulateLocators' is configured to
    'true', what size buffer should be used when fetching BLOB
    data for getBinaryInputStream? 1048576 3.2.1
@@ -649,35 +782,55 @@
    correctly. Notice that for prepared statements, server-side
    prepared statements can not currently take advantage of this
    rewrite option, and that if you don't specify stream lengths
-   when using PreparedStatement.set*Stream(),the driver won't be
-   able to determine the optimium number of parameters per batch
-   and you might receive an error from the driver that the
+   when using PreparedStatement.set*Stream(), the driver won't
+   be able to determine the optimum number of parameters per
+   batch and you might receive an error from the driver that the
    resultant packet is too large. Statement.getGeneratedKeys()
    for these rewritten statements only works when the entire
    batch includes INSERT statements. false 3.1.13
+   useDirectRowUnpack Use newer result set row unpacking code
+   that skips a copy from network buffers to a MySQL packet
+   instance and instead reads directly into the result set row
+   data buffers. true 5.1.1
+   useDynamicCharsetInfo Should the driver use a per-connection
+   cache of character set information queried from the server
+   when necessary, or use a built-in static mapping that is more
+   efficient, but isn't aware of custom character sets or
+   character sets implemented after the release of the JDBC
+   driver? true 5.0.6
+   useFastDateParsing Use internal String->Date/Time/Timestamp
+   conversion routines to avoid excessive object creation? true
+   5.0.5
    useFastIntParsing Use internal String->Integer conversion
    routines to avoid excessive object creation? true 3.1.4
    useJvmCharsetConverters Always use the character encoding
    routines built into the JVM, rather than using lookup tables
-   for single-byte character sets? (The default of "true" for
-   this is appropriate for newer JVMs true 5.0.1
+   for single-byte character sets? false 5.0.1
    useLocalSessionState Should the driver refer to the internal
    values of autocommit and transaction isolation that are set
    by Connection.setAutoCommit() and
-   Connection.setTransactionIsolation(), rather than querying
-   the database? false 3.1.7
+   Connection.setTransactionIsolation() and transaction state as
+   maintained by the protocol, rather than querying the database
+   or blindly sending commands to the database for commit() or
+   rollback() method calls? false 3.1.7
    useReadAheadInput Use newer, optimized non-blocking, buffered
    input stream when reading from the server? true 3.1.5
 
-   Debuging/Profiling.
+   Debugging/Profiling.
    Property Name Definition Default Value Since Version
    logger The name of a class that implements
-   'com.mysql.jdbc.log.Log' that will be used to log messages
-   to.(default is 'com.mysql.jdbc.log.StandardLogger', which
+   "com.mysql.jdbc.log.Log" that will be used to log messages
+   to. (default is "com.mysql.jdbc.log.StandardLogger", which
    logs to STDERR) com.mysql.jdbc.log.StandardLogger 3.1.1
+   gatherPerfMetrics Should the driver gather performance
+   metrics, and report them via the configured logger every
+   'reportMetricsIntervalMillis' milliseconds? false 3.1.2
    profileSQL Trace queries and their execution/fetch times to
    the configured logger (true/false) defaults to 'false' false
    3.1.0
+   profileSql Deprecated, use 'profileSQL' instead. Trace
+   queries and their execution/fetch times on STDERR
+   (true/false) defaults to 'false'   2.0.14
    reportMetricsIntervalMillis If 'gatherPerfMetrics' is
    enabled, how often should they be logged (in ms)? 30000 3.1.2
    maxQuerySizeToLog Controls the maximum length/size of a query
@@ -687,6 +840,10 @@
    slowQueryThresholdMillis If 'logSlowQueries' is enabled, how
    long should a query (in ms) before it is logged as 'slow'?
    2000 3.1.2
+   slowQueryThresholdNanos If 'useNanosForElapsedTime' is set to
+   true, and this property is set to a non-zero value, the
+   driver will use this threshold (in nanosecond units) to
+   determine if a query was slow. 0 5.0.7
    useUsageAdvisor Should the driver issue 'usage' warnings
    advising proper and efficient usage of JDBC and MySQL
    Connector/J to the log (true/false, defaults to 'false')?
@@ -694,6 +851,14 @@
    autoGenerateTestcaseScript Should the driver dump the SQL it
    is executing, including server-side prepared statements to
    STDERR? false 3.1.9
+   autoSlowLog Instead of using slowQueryThreshold* to determine
+   if a query is slow enough to be logged, maintain statistics
+   that allow the driver to determine queries that are outside
+   the 99th percentile? true 5.1.4
+   clientInfoProvider The name of a class that implements the
+   com.mysql.jdbc.JDBC4ClientInfoProvider interface in order to
+   support JDBC-4.0's Connection.get/setClientInfo() methods
+   com.mysql.jdbc.JDBC4CommentClientInfoProvider 5.1.0
    dumpMetadataOnColumnNotFound Should the driver dump the
    field-level metadata of a result set into the exception
    message when ResultSet.findColumn() fails? false 3.1.13
@@ -708,10 +873,22 @@
    driver automatically issue an 'EXPLAIN' on the server and
    send the results to the configured log at a WARN level? false
    3.1.2
+   includeInnodbStatusInDeadlockExceptions Include the output of
+   "SHOW ENGINE INNODB STATUS" in exception messages when
+   deadlock exceptions are detected? false 5.0.7
    logSlowQueries Should queries that take longer than
    'slowQueryThresholdMillis' be logged? false 3.1.2
+   logXaCommands Should the driver log XA commands sent by
+   MysqlXaConnection to the server, at the DEBUG level of
+   logging? false 5.0.5
+   resultSetSizeThreshold If the usage advisor is enabled, how
+   many rows should a result set contain before the driver warns
+   that it is suspiciously large? 100 5.0.5
    traceProtocol Should trace-level network protocol be logged?
    false 3.1.2
+   useNanosForElapsedTime For profiling/debugging functionality
+   that measures elapsed time, should the driver try to use
+   nanoseconds resolution if available (JDK >= 1.5)? false 5.0.7
 
    Miscellaneous.
    Property Name Definition Default Value Since Version
@@ -723,14 +900,30 @@
    defaults to 'true' true 1.1g
    characterEncoding If 'useUnicode' is set to true, what
    character encoding should the driver use when dealing with
-   strings? (defaults is to 'autodetect') 1.1g
+   strings? (defaults is to 'autodetect')   1.1g
    characterSetResults Character set to tell the server to
-   return results as. 3.0.13
+   return results as.   3.0.13
    connectionCollation If set, tells the server to use this
-   collation via 'set collation_connection' 3.0.13
+   collation via 'set collation_connection'   3.0.13
+   useBlobToStoreUTF8OutsideBMP Tells the driver to treat
+   [MEDIUM/LONG]BLOB columns as [LONG]VARCHAR columns holding
+   text encoded in UTF-8 that has characters outside the BMP
+   (4-byte encodings), which MySQL server can't handle natively.
+   false 5.1.3
+   utf8OutsideBmpExcludedColumnNamePattern When
+   "useBlobToStoreUTF8OutsideBMP" is set to "true", column names
+   matching the given regex will still be treated as BLOBs
+   unless they match the regex specified for
+   "utf8OutsideBmpIncludedColumnNamePattern". The regex must
+   follow the patterns used for the java.util.regex package.
+   5.1.3
+   utf8OutsideBmpIncludedColumnNamePattern Used to specify
+   exclusion rules to "utf8OutsideBmpExcludedColumnNamePattern".
+   The regex must follow the patterns used for the
+   java.util.regex package.   5.1.3
    sessionVariables A comma-separated list of name/value pairs
    to be sent as SET SESSION ... to the server when the driver
-   connects. 3.1.8
+   connects.   3.1.8
    allowNanAndInf Should the driver allow NaN or +/- INF values
    in PreparedStatement.setDouble()? false 3.1.5
    autoClosePStmtStreams Should the driver automatically call
@@ -738,12 +931,16 @@
    methods? false 3.1.12
    autoDeserialize Should the driver automatically detect and
    de-serialize objects stored in BLOB fields? false 3.1.5
+   blobsAreStrings Should the driver always treat BLOBs as
+   Strings - specifically to work around dubious metadata
+   returned by the server for GROUP BY clauses? false 5.0.8
    capitalizeTypeNames Capitalize type names in
    DatabaseMetaData? (usually only useful when using WebObjects,
-   true/false, defaults to 'false') false 2.0.7
+   true/false, defaults to 'false') true 2.0.7
    clobCharacterEncoding The character encoding to use for
    sending and retrieving TEXT, MEDIUMTEXT and LONGTEXT values
-   instead of the configured connection characterEncoding 5.0.0
+   instead of the configured connection characterEncoding
+   5.0.0
    clobberStreamingResults This will cause a 'streaming'
    ResultSet to be automatically closed, and any outstanding
    data still streaming from the server to be discarded if
@@ -757,25 +954,52 @@
    permissions to create databases. false 3.1.9
    emptyStringsConvertToZero Should the driver allow conversions
    from empty string fields to numeric values of '0'? true 3.1.8
-   emulateLocators N/A false 3.1.0
+   emulateLocators Should the driver emulate java.sql.Blobs with
+   locators? With this feature enabled, the driver will delay
+   loading the actual Blob data until the one of the retrieval
+   methods (getInputStream(), getBytes(), and so forth) on the
+   blob data stream has been accessed. For this to work, you
+   must use a column alias with the value of the column to the
+   actual name of the Blob. The feature also has the following
+   restrictions: The SELECT that created the result set must
+   reference only one table, the table must have a primary key;
+   the SELECT must alias the original blob column name,
+   specified as a string, to an alternate name; the SELECT must
+   cover all columns that make up the primary key. false 3.1.0
    emulateUnsupportedPstmts Should the driver detect prepared
    statements that are not supported by the server, and replace
    them with client-side emulated versions? true 3.1.7
+   functionsNeverReturnBlobs Should the driver always treat data
+   from functions returning BLOBs as Strings - specifically to
+   work around dubious metadata returned by the server for GROUP
+   BY clauses? false 5.0.8
+   generateSimpleParameterMetadata Should the driver generate
+   simplified parameter metadata for PreparedStatements when no
+   metadata is available either because the server couldn't
+   support preparing the statement, or server-side prepared
+   statements are disabled? false 5.0.5
    ignoreNonTxTables Ignore non-transactional table warning for
    rollback? (defaults to 'false'). false 3.0.9
    jdbcCompliantTruncation Should the driver throw
    java.sql.DataTruncation exceptions when data is truncated as
    is required by the JDBC specification when connected to a
-   server that supports warnings(MySQL 4.1.0 and newer)? true
-   3.1.2
+   server that supports warnings (MySQL 4.1.0 and newer)? This
+   property has no effect if the server sql-mode includes
+   STRICT_TRANS_TABLES. true 3.1.2
    maxRows The maximum number of rows to return (0, the default
    means return all rows). -1 all versions
+   netTimeoutForStreamingResults What value should the driver
+   automatically set the server setting 'net_write_timeout' to
+   when the streaming result sets feature is in use? (value has
+   unit of seconds, the value '0' means the driver will not try
+   and adjust this value) 600 5.1.0
    noAccessToProcedureBodies When determining procedure
    parameter types for CallableStatements, and the connected
    user can't access procedure bodies through "SHOW CREATE
    PROCEDURE" or select on mysql.proc should the driver instead
-   create basic metadata (all parameters reported as INOUT
-   VARCHARs) instead of throwing an exception? false 5.0.3
+   create basic metadata (all parameters reported as IN
+   VARCHARs, but allowing registerOutParameter() to be called on
+   them anyway) instead of throwing an exception? false 5.0.3
    noDatetimeStringSync Don't ensure that
    ResultSet.getDatetimeType().toString().equals(ResultSet.getSt
    ring()) false 3.1.7
@@ -799,12 +1023,27 @@
    of foreign keys, even though the SQL specification states
    that this facility contains much more than just foreign key
    support (one such application being OpenOffice)? false 3.1.12
+   padCharsWithSpace If a result set column has the CHAR type
+   and the value does not fill the amount of characters
+   specified in the DDL for the column, should the driver pad
+   the remaining characters with space (for ANSI compliance)?
+   false 5.0.6
    pedantic Follow the JDBC spec to the letter. false 3.0.0
    pinGlobalTxToPhysicalConnection When using XAConnections,
    should the driver ensure that operations on a given XID are
    always routed to the same physical connection? This allows
    the XAConnection to support "XA START ... JOIN" after "XA
    END" has been called false 5.0.1
+   populateInsertRowWithDefaultValues When using ResultSets that
+   are CONCUR_UPDATABLE, should the driver pre-populate the
+   "insert" row with default values from the DDL for the table
+   used in the query so those values are immediately available
+   for ResultSet accessors? This functionality requires a call
+   to the database for metadata each time a result set of this
+   type is created. If disabled (the default), the default
+   values will be populated by the an internal call to
+   refreshRow() which pulls back default values and/or values
+   changed by triggers. false 5.0.5
    processEscapeCodesForPrepStmts Should the driver process
    escape codes in queries that are prepared? true 3.1.12
    relaxAutoCommit If the version of MySQL the driver connects
@@ -820,7 +1059,15 @@
    runningCTS13 Enables workarounds for bugs in Sun's JDBC
    compliance testsuite version 1.3 false 3.1.7
    serverTimezone Override detection/mapping of timezone. Used
-   when timezone from server doesn't map to Java timezone 3.0.2
+   when timezone from server doesn't map to Java timezone
+   3.0.2
+   statementInterceptors A comma-delimited list of classes that
+   implement "com.mysql.jdbc.StatementInterceptor" that should
+   be placed "in between" query execution to influence the
+   results. StatementInterceptors are "chainable", the results
+   returned by the "current" interceptor will be passed on to
+   the next in in the chain, from left-to-right order, as
+   specified in this property.   5.1.1
    strictFloatingPoint Used only in older versions of compliance
    test false 3.0.0
    strictUpdates Should the driver do strict checking (all
@@ -833,6 +1080,9 @@
    a different type, should it use BOOLEAN instead of BIT for
    future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT
    type? false 3.1.9
+   treatUtilDateAsTimestamp Should the driver treat
+   java.util.Date as a TIMESTAMP for the purposes of
+   PreparedStatement.setObject()? true 5.0.5
    ultraDevHack Create PreparedStatements for prepareCall() when
    required, because UltraDev is broken and issues a
    prepareCall() for _all_ statements? (true/false, defaults to
@@ -857,14 +1107,20 @@
    behavior for "AS" clauses on columns and tables, and only
    return aliases (if any) for ResultSetMetaData.getColumnName()
    or ResultSetMetaData.getTableName() rather than the original
-   column/table name? true 5.0.4
+   column/table name? false 5.0.4
    useOldUTF8Behavior Use the UTF-8 behavior the driver did when
    communicating with 4.0 and older servers false 3.1.6
    useOnlyServerErrorMessages Don't prepend 'standard' SQLState
    error messages to error messages returned by the server. true
    3.0.15
+   useSSPSCompatibleTimezoneShift If migrating from an
+   environment that was using server-side prepared statements,
+   and the configuration property
+   "useJDBCCompliantTimeZoneShift" set to "true", use compatible
+   behavior when not using server-side prepared statements when
+   sending TIMESTAMP values to the MySQL server. false 5.0.5
    useServerPrepStmts Use server-side prepared statements if the
-   server supports them? (defaults to 'true'). true 3.1.0
+   server supports them? false 3.1.0
    useSqlStateCodes Use SQL Standard state codes instead of
    'legacy' X/Open/SQL state codes (true/false), default is
    'true' true 3.1.3
@@ -880,7 +1136,7 @@
    zeroDateTimeBehavior What should happen when the driver
    encounters DATETIME values that are composed entirely of
    zeroes (used by MySQL to represent invalid dates)? Valid
-   values are 'exception', 'round' and 'convertToNull'.
+   values are "exception", "round" and "convertToNull".
    exception 3.1.4
 
    Connector/J also supports access to MySQL via named pipes on
@@ -915,6 +1171,25 @@
    about how certain implementation decisions may affect how you
    use MySQL Connector/J.
      * Blob
+       Starting with Connector/J version 3.1.0, you can emulate
+       Blobs with locators by adding the property
+       'emulateLocators=true' to your JDBC URL. Using this
+       method, the driver will delay loading the actual Blob
+       data until you retrieve the other data and then use
+       retrieval methods (getInputStream(), getBytes(), and so
+       forth) on the blob data stream.
+       For this to work, you must use a column alias with the
+       value of the column to the actual name of the Blob, for
+       example:
+SELECT id, 'data' as blob_data from blobtable
+       For this to work, you must also follow follow these
+       rules:
+          + The SELECT must also reference only one table, the
+            table must have a primary key.
+          + The SELECT must alias the original blob column name,
+            specified as a string, to an alternate name.
+          + The SELECT must cover all columns that make up the
+            primary key.
        The Blob implementation does not allow in-place
        modification (they are copies, as reported by the
        DatabaseMetaData.locatorsUpdateCopies() method). Because
@@ -922,18 +1197,12 @@
        PreparedStatement.setBlob() or ResultSet.updateBlob() (in
        the case of updatable result sets) methods to save
        changes back to the database.
-       Starting with Connector/J version 3.1.0, you can emulate
-       Blobs with locators by adding the property
-       'emulateLocators=true' to your JDBC URL. You must then
-       use a column alias with the value of the column set to
-       the actual name of the Blob column in the SELECT that you
-       write to retrieve the Blob. The SELECT must also
-       reference only one table, the table must have a primary
-       key, and the SELECT must cover all columns that make up
-       the primary key. The driver will then delay loading the
-       actual Blob data until you retrieve the Blob and call
-       retrieval methods (getInputStream(), getBytes(), and so
-       forth) on it.
+       MySQL Enterprise MySQL Enterprise subscribers will find
+       more information about type conversion in the Knowledge
+       Base article, Type Conversions Supported by MySQL
+       Connector/J (https://kb.mysql.com/view.php?id=4929). To
+       subscribe to MySQL Enterprise see
+       http://www.mysql.com/products/enterprise/advisors.html.
      * CallableStatement
        Starting with Connector/J 3.1.1, stored procedures are
        supported when connecting to MySQL version 5.0 or newer
@@ -981,10 +1250,9 @@
        any large parameter changed to a non-large parameter, it
        is necessary to call clearParameters() and set all
        parameters again. The reason for this is as follows:
-          + The driver streams the large data out-of-band to the
-            prepared statement on the server side when the
-            parameter is set (before execution of the prepared
-            statement).
+          + During both server-side prepared statements and
+            client-side emulation, large data is exchanged only
+            when PreparedStatement.execute() is called.
           + Once that has been done, the stream used to read the
             data on the client side is closed (as per the JDBC
             spec), and can't be read from again.
@@ -1044,10 +1312,46 @@
      * Statement
        When using versions of the JDBC driver earlier than
        3.2.1, and connected to server versions earlier than
-       5.0.3, the "setFetchSize()" method has no effect, other
+       5.0.3, the setFetchSize() method has no effect, other
        than to toggle result set streaming as described above.
+       Connector/J 5.0.0 and later include support for both
+       Statement.cancel() and Statement.setQueryTimeout(). Both
+       require MySQL 5.0.0 or newer server, and require a
+       separate connection to issue the KILL QUERY statement. In
+       the case of setQueryTimeout(), the implementation creates
+       an additional thread to handle the timeout functionality.
+
+Note
+       Failures to cancel the statement for setQueryTimeout()
+       may manifest themselves as RuntimeException rather than
+       failing silently, as there is currently no way to unblock
+       the thread that is executing the query being cancelled
+       due to timeout expiration and have it throw the exception
+       instead.
        MySQL does not support SQL cursors, and the JDBC driver
        doesn't emulate them, so "setCursorName()" has no effect.
+       Connector/J 5.1.3 and later include two additional
+       methods:
+          + setLocalInfileInputStream() sets an InputStream
+            instance that will be used to send data to the MySQL
+            server for a LOAD DATA LOCAL INFILE statement rather
+            than a FileInputStream or URLInputStream that
+            represents the path given as an argument to the
+            statement.
+            This stream will be read to completion upon
+            execution of a LOAD DATA LOCAL INFILE statement, and
+            will automatically be closed by the driver, so it
+            needs to be reset before each call to execute*()
+            that would cause the MySQL server to request data to
+            fulfill the request for LOAD DATA LOCAL INFILE.
+            If this value is set to NULL, the driver will revert
+            to using a FileInputStream or URLInputStream as
+            required.
+          + getLocalInfileInputStream() returns the InputStream
+            instance that will be used to send data in response
+            to a LOAD DATA LOCAL INFILE statement.
+            This method returns NULL if no such stream has been
+            set via setLocalInfileInputStream().
 
 1.4.3. Java, JDBC and MySQL Types
 
@@ -1081,11 +1385,12 @@
    DATE, TIME, DATETIME, TIMESTAMP java.lang.String,
    java.sql.Date, java.sql.Timestamp
 
-   Note: round-off, overflow or loss of precision may occur if
-   you choose a Java numeric data type that has less precision
-   or capacity than the MySQL data type you are converting
-   to/from.
+Note
 
+   Round-off, overflow or loss of precision may occur if you
+   choose a Java numeric data type that has less precision or
+   capacity than the MySQL data type you are converting to/from.
+
    The ResultSet.getObject() method uses the type conversions
    between MySQL and Java types, following the JDBC
    specification where appropriate. The value returned by
@@ -1158,7 +1463,7 @@
    character encoding per connection, which could either be
    automatically detected from the server configuration, or
    could be configured by the user through the useUnicode and
-   "characterEncoding" properties.
+   characterEncoding properties.
 
    Starting with MySQL Server 4.1, Connector/J supports a single
    character encoding between client and server, and any number
@@ -1170,8 +1475,8 @@
    the driver is specified on the server via the character_set
    system variable for server versions older than 4.1.0 and
    character_set_server for server versions 4.1.0 and newer. For
-   more information, see [WARNING: missing xref target
-   (id=charset-server)]
+   more information, see Server Character Set and Collation
+   (http://dev.mysql.com/doc/refman/5.0/en/charset-server.html).
 
    To override the automatically-detected encoding on the client
    side, use the characterEncoding property in the URL used to
@@ -1205,11 +1510,13 @@
    utf8 UTF-8
    ucs2 UnicodeBig
 
-   Warning.  Do not issue the query 'set names' with
-   Connector/J, as the driver will not detect that the character
-   set has changed, and will continue to use the character set
-   detected during the initial connection setup.
+Warning
 
+   Do not issue the query 'set names' with Connector/J, as the
+   driver will not detect that the character set has changed,
+   and will continue to use the character set detected during
+   the initial connection setup.
+
    To allow multiple character sets to be sent from the client,
    the UTF-8 encoding should be used, either by configuring utf8
    as the default server character set, or by configuring the
@@ -1233,8 +1540,9 @@
        73544.html
      * A MySQL server that supports SSL and has been compiled
        and configured to do so, which is MySQL-4.0.4 or later,
-       see [WARNING: missing xref target
-       (id=secure-connections)] for more information.
+       see Using Secure Connections
+       (http://dev.mysql.com/doc/refman/5.0/en/secure-connection
+       s.html), for more information.
      * A client certificate (covered later in this section)
 
    You will first need to import the MySQL server CA Certificate
@@ -1248,31 +1556,29 @@
    (cacert.pem), you can do the following (assuming that keytool
    is in your path. The keytool should be located in the bin
    subdirectory of your JDK or JRE):
-shell> keytool -import -alias mysqlServerCACert -file cacert.pem -keys
-tore truststore
+shell> keytool -import -alias mysqlServerCACert \
+                                  -file cacert.pem -keystore truststor
+e
 
    Keytool will respond with the following information:
 Enter keystore password:  *********
-Owner: EMAILADDRESS=walrus at example.com, CN=Walrus, O=MySQL AB, L=Orenb
-urg, ST=Some
--State, C=RU
-Issuer: EMAILADDRESS=walrus at example.com, CN=Walrus, O=MySQL AB, L=Oren
-burg, ST=Som
-e-State, C=RU
+Owner: EMAILADDRESS=walrus at example.com, CN=Walrus,
+       O=MySQL AB, L=Orenburg, ST=Some-State, C=RU
+Issuer: EMAILADDRESS=walrus at example.com, CN=Walrus,
+       O=MySQL AB, L=Orenburg, ST=Some-State, C=RU
 Serial number: 0
-Valid from: Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CD
-T 2003
+Valid from:
+   Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003
 Certificate fingerprints:
-         MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
-         SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4
-D:6C
+    MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
+    SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4D:6C
 Trust this certificate? [no]:  yes
 Certificate was added to keystore
 
    You will then need to generate a client certificate, so that
    the MySQL server knows that it is talking to a secure client:
- shell> keytool -genkey -keyalg rsa -alias mysqlClientCertificate -key
-store keystore
+ shell> keytool -genkey -keyalg rsa \
+     -alias mysqlClientCertificate -keystore keystore
 
    Keytool will prompt you for the following information, and
    create a keystore named keystore in the current directory.
@@ -1305,12 +1611,20 @@
    path_to_keystore_file with the full path to the keystore file
    you created, path_to_truststore_file with the path to the
    truststore file you created, and using the appropriate
-   password values for each property.
+   password values for each property. You can do this either on
+   the command line:
 -Djavax.net.ssl.keyStore=path_to_keystore_file
--Djavax.net.ssl.keyStorePassword=*********
+-Djavax.net.ssl.keyStorePassword=password
 -Djavax.net.ssl.trustStore=path_to_truststore_file
--Djavax.net.ssl.trustStorePassword=*********
+-Djavax.net.ssl.trustStorePassword=password
 
+   Or you can set the values directly within the application:
+ System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file");
+System.setProperty("javax.net.ssl.keyStorePassword","password");
+System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file
+");
+System.setProperty("javax.net.ssl.trustStorePassword","password");
+
    You will also need to set useSSL to true in your connection
    parameters for MySQL Connector/J, either by adding
    useSSL=true to your URL, or by setting the property useSSL to
@@ -1320,49 +1634,51 @@
    You can test that SSL is working by turning on JSSE debugging
    (as detailed below), and look for the following key events:
 ...
- *** ClientHello, v3.1
- RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12,
-54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, 217
-, 219, 239, 202, 19, 121, 78 }
- Session ID:  {}
- Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17
-}
- Compression Methods:  { 0 }
- ***
- [write] MD5 and SHA1 hashes:  len = 59
- 0000: 01 00 00 37 03 01 3D B6   90 FA C7 94 B4 D7 4A 0C  ...7..=.....
-..J.
- 0010: 36 F4 00 A8 37 67 D7 40   10 8A E1 BE 84 99 02 D9  6...7g. at ....
-....
- 0020: DB EF CA 13 79 4E 00 00   10 00 05 00 04 00 09 00  ....yN......
-....
- 0030: 0A 00 12 00 13 00 03 00   11 01 00                 ...........
- main, WRITE:  SSL v3.1 Handshake, length = 59
- main, READ:  SSL v3.1 Handshake, length = 74
- *** ServerHello, v3.1
- RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58
-, 202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3,
-132, 110, 82, 148, 160, 92 }
- Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63,
-182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177, 219
-, 158, 177, 187, 143}
- Cipher Suite:  { 0, 5 }
- Compression Method: 0
- ***
- %% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
- ** SSL_RSA_WITH_RC4_128_SHA
- [read] MD5 and SHA1 hashes:  len = 74
- 0000: 02 00 00 46 03 01 3D B6   43 98 74 32 04 67 19 64  ...F..=.C.t2
-.g.d
- 0010: 3A CA 4F B9 B2 64 D7 42   FE 15 53 BB BE 2A AA 03  :.O..d.B..S.
-.*..
- 0020: 84 6E 52 94 A0 5C 20 A3   E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q
-....
- 0030: B3 44 3F B6 9E 1E 0B 96   4F AA 4C FF 5C 0F E2 18  .D?.....O.L.
-\...
- 0040: 11 B1 DB 9E B1 BB 8F 00   05 00                    ..........
- main, READ:  SSL v3.1 Handshake, length = 1712
- ...
+*** ClientHello, v3.1
+RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12, �
+  54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, �
+  217, 219, 239, 202, 19, 121, 78 }
+Session ID:  {}
+Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17 }
+Compression Methods:  { 0 }
+***
+[write] MD5 and SHA1 hashes:  len = 59
+0000: 01 00 00 37 03 01 3D B6 90 FA C7 94 B4 D7 4A 0C  ...7..=.......J
+.
+0010: 36 F4 00 A8 37 67 D7 40 10 8A E1 BE 84 99 02 D9  6...7g. at .......
+.
+0020: DB EF CA 13 79 4E 00 00 10 00 05 00 04 00 09 00  ....yN.........
+.
+0030: 0A 00 12 00 13 00 03 00 11 01 00                 ...........
+main, WRITE:  SSL v3.1 Handshake, length = 59
+main, READ:  SSL v3.1 Handshake, length = 74
+*** ServerHello, v3.1
+RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58,
+ �
+   202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3,
+ �
+   132, 110, 82, 148, 160, 92 }
+Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63, �
+   182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177,
+�
+   219, 158, 177, 187, 143}
+Cipher Suite:  { 0, 5 }
+Compression Method: 0
+***
+%% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
+** SSL_RSA_WITH_RC4_128_SHA
+[read] MD5 and SHA1 hashes:  len = 74
+0000: 02 00 00 46 03 01 3D B6 43 98 74 32 04 67 19 64  ...F..=.C.t2.g.
+d
+0010: 3A CA 4F B9 B2 64 D7 42 FE 15 53 BB BE 2A AA 03  :.O..d.B..S..*.
+.
+0020: 84 6E 52 94 A0 5C 20 A3 E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q...
+.
+0030: B3 44 3F B6 9E 1E 0B 96 4F AA 4C FF 5C 0F E2 18  .D?.....O.L.\..
+.
+0040: 11 B1 DB 9E B1 BB 8F 00 05 00                    ..........
+main, READ:  SSL v3.1 Handshake, length = 1712
+...
 
    JSSE provides debugging (to STDOUT) when you set the
    following system property: -Djavax.net.debug=all This will
@@ -1416,59 +1732,200 @@
 
 public class ReplicationDriverDemo {
 
-    public static void main(String[] args) throws Exception {
-        ReplicationDriver driver = new ReplicationDriver();
+  public static void main(String[] args) throws Exception {
+    ReplicationDriver driver = new ReplicationDriver();
 
-        Properties props = new Properties();
+    Properties props = new Properties();
 
-        // We want this for failover on the slaves
-        props.put("autoReconnect", "true");
+    // We want this for failover on the slaves
+    props.put("autoReconnect", "true");
 
-        // We want to load balance between the slaves
-        props.put("roundRobinLoadBalance", "true");
+    // We want to load balance between the slaves
+    props.put("roundRobinLoadBalance", "true");
 
-        props.put("user", "foo");
-        props.put("password", "bar");
+    props.put("user", "foo");
+    props.put("password", "bar");
 
-        //
-        // Looks like a normal MySQL JDBC url, with a comma-separated
-list
-        // of hosts, the first being the 'master', the rest being any
-number
-        // of slaves that the driver will load balance against
-        //
+    //
+    // Looks like a normal MySQL JDBC url, with a
+    // comma-separated list of hosts, the first
+    // being the 'master', the rest being any number
+    // of slaves that the driver will load balance against
+    //
 
-        Connection conn =
-            driver.connect("jdbc:mysql://master,slave1,slave2,slave3/t
-est",
-                props);
+    Connection conn =
+        driver.connect("jdbc:mysql://master,slave1,slave2,slave3/test"
+,
+            props);
 
-        //
-        // Perform read/write work on the master
-        // by setting the read-only flag to "false"
-        //
+    //
+    // Perform read/write work on the master
+    // by setting the read-only flag to "false"
+    //
 
-        conn.setReadOnly(false);
-        conn.setAutoCommit(false);
-        conn.createStatement().executeUpdate("UPDATE some_table ....")
-;
-        conn.commit();
+    conn.setReadOnly(false);
+    conn.setAutoCommit(false);
+    conn.createStatement().executeUpdate("UPDATE some_table ....");
+    conn.commit();
 
-        //
-        // Now, do a query from a slave, the driver automatically pick
-s one
-        // from the list
-        //
+    //
+    // Now, do a query from a slave, the driver automatically picks on
+e
+    // from the list
+    //
 
-        conn.setReadOnly(true);
+    conn.setReadOnly(true);
 
-        ResultSet rs = conn.createStatement().executeQuery("SELECT a,b
-,c FROM some_other_table");
+    ResultSet rs =
+      conn.createStatement().executeQuery("SELECT a,b FROM alt_table")
+;
 
-         .......
-    }
+     .......
+  }
 }
 
+1.4.7. Mapping MySQL Error Numbers to SQLStates
+
+   The table below provides a mapping of the MySQL Error Numbers
+   to SQL States
+
+   Table 1. Mapping of MySQL Error Numbers to SQLStates
+   MySQL Error Number MySQL Error Name Legacy (X/Open) SQLState
+   SQL Standard SQLState
+   1022 ER_DUP_KEY S1000 23000
+   1037 ER_OUTOFMEMORY S1001 HY001
+   1038 ER_OUT_OF_SORTMEMORY S1001 HY001
+   1040 ER_CON_COUNT_ERROR 08004 08004
+   1042 ER_BAD_HOST_ERROR 08004 08S01
+   1043 ER_HANDSHAKE_ERROR 08004 08S01
+   1044 ER_DBACCESS_DENIED_ERROR S1000 42000
+   1045 ER_ACCESS_DENIED_ERROR 28000 28000
+   1047 ER_UNKNOWN_COM_ERROR 08S01 HY000
+   1050 ER_TABLE_EXISTS_ERROR S1000 42S01
+   1051 ER_BAD_TABLE_ERROR 42S02 42S02
+   1052 ER_NON_UNIQ_ERROR S1000 23000
+   1053 ER_SERVER_SHUTDOWN S1000 08S01
+   1054 ER_BAD_FIELD_ERROR S0022 42S22
+   1055 ER_WRONG_FIELD_WITH_GROUP S1009 42000
+   1056 ER_WRONG_GROUP_FIELD S1009 42000
+   1057 ER_WRONG_SUM_SELECT S1009 42000
+   1058 ER_WRONG_VALUE_COUNT 21S01 21S01
+   1059 ER_TOO_LONG_IDENT S1009 42000
+   1060 ER_DUP_FIELDNAME S1009 42S21
+   1061 ER_DUP_KEYNAME S1009 42000
+   1062 ER_DUP_ENTRY S1009 23000
+   1063 ER_WRONG_FIELD_SPEC S1009 42000
+   1064 ER_PARSE_ERROR 42000 42000
+   1065 ER_EMPTY_QUERY 42000 42000
+   1066 ER_NONUNIQ_TABLE S1009 42000
+   1067 ER_INVALID_DEFAULT S1009 42000
+   1068 ER_MULTIPLE_PRI_KEY S1009 42000
+   1069 ER_TOO_MANY_KEYS S1009 42000
+   1070 ER_TOO_MANY_KEY_PARTS S1009 42000
+   1071 ER_TOO_LONG_KEY S1009 42000
+   1072 ER_KEY_COLUMN_DOES_NOT_EXITS S1009 42000
+   1073 ER_BLOB_USED_AS_KEY S1009 42000
+   1074 ER_TOO_BIG_FIELDLENGTH S1009 42000
+   1075 ER_WRONG_AUTO_KEY S1009 42000
+   1080 ER_FORCING_CLOSE S1000 08S01
+   1081 ER_IPSOCK_ERROR 08S01 08S01
+   1082 ER_NO_SUCH_INDEX S1009 42S12
+   1083 ER_WRONG_FIELD_TERMINATORS S1009 42000
+   1084 ER_BLOBS_AND_NO_TERMINATED S1009 42000
+   1090 ER_CANT_REMOVE_ALL_FIELDS S1000 42000
+   1091 ER_CANT_DROP_FIELD_OR_KEY S1000 42000
+   1101 ER_BLOB_CANT_HAVE_DEFAULT S1000 42000
+   1102 ER_WRONG_DB_NAME S1000 42000
+   1103 ER_WRONG_TABLE_NAME S1000 42000
+   1104 ER_TOO_BIG_SELECT S1000 42000
+   1106 ER_UNKNOWN_PROCEDURE S1000 42000
+   1107 ER_WRONG_PARAMCOUNT_TO_PROCEDURE S1000 42000
+   1109 ER_UNKNOWN_TABLE S1000 42S02
+   1110 ER_FIELD_SPECIFIED_TWICE S1000 42000
+   1112 ER_UNSUPPORTED_EXTENSION S1000 42000
+   1113 ER_TABLE_MUST_HAVE_COLUMNS S1000 42000
+   1115 ER_UNKNOWN_CHARACTER_SET S1000 42000
+   1118 ER_TOO_BIG_ROWSIZE S1000 42000
+   1120 ER_WRONG_OUTER_JOIN S1000 42000
+   1121 ER_NULL_COLUMN_IN_INDEX S1000 42000
+   1129 ER_HOST_IS_BLOCKED 08004 HY000
+   1130 ER_HOST_NOT_PRIVILEGED 08004 HY000
+   1131 ER_PASSWORD_ANONYMOUS_USER S1000 42000
+   1132 ER_PASSWORD_NOT_ALLOWED S1000 42000
+   1133 ER_PASSWORD_NO_MATCH S1000 42000
+   1136 ER_WRONG_VALUE_COUNT_ON_ROW S1000 21S01
+   1138 ER_INVALID_USE_OF_NULL S1000 42000
+   1139 ER_REGEXP_ERROR S1000 42000
+   1140 ER_MIX_OF_GROUP_FUNC_AND_FIELDS S1000 42000
+   1141 ER_NONEXISTING_GRANT S1000 42000
+   1142 ER_TABLEACCESS_DENIED_ERROR S1000 42000
+   1143 ER_COLUMNACCESS_DENIED_ERROR S1000 42000
+   1144 ER_ILLEGAL_GRANT_FOR_TABLE S1000 42000
+   1145 ER_GRANT_WRONG_HOST_OR_USER S1000 42000
+   1146 ER_NO_SUCH_TABLE S1000 42S02
+   1147 ER_NONEXISTING_TABLE_GRANT S1000 42000
+   1148 ER_NOT_ALLOWED_COMMAND S1000 42000
+   1149 ER_SYNTAX_ERROR S1000 42000
+   1152 ER_ABORTING_CONNECTION S1000 08S01
+   1153 ER_NET_PACKET_TOO_LARGE S1000 08S01
+   1154 ER_NET_READ_ERROR_FROM_PIPE S1000 08S01
+   1155 ER_NET_FCNTL_ERROR S1000 08S01
+   1156 ER_NET_PACKETS_OUT_OF_ORDER S1000 08S01
+   1157 ER_NET_UNCOMPRESS_ERROR S1000 08S01
+   1158 ER_NET_READ_ERROR S1000 08S01
+   1159 ER_NET_READ_INTERRUPTED S1000 08S01
+   1160 ER_NET_ERROR_ON_WRITE S1000 08S01
+   1161 ER_NET_WRITE_INTERRUPTED S1000 08S01
+   1162 ER_TOO_LONG_STRING S1000 42000
+   1163 ER_TABLE_CANT_HANDLE_BLOB S1000 42000
+   1164 ER_TABLE_CANT_HANDLE_AUTO_INCREMENT S1000 42000
+   1166 ER_WRONG_COLUMN_NAME S1000 42000
+   1167 ER_WRONG_KEY_COLUMN S1000 42000
+   1169 ER_DUP_UNIQUE S1000 23000
+   1170 ER_BLOB_KEY_WITHOUT_LENGTH S1000 42000
+   1171 ER_PRIMARY_CANT_HAVE_NULL S1000 42000
+   1172 ER_TOO_MANY_ROWS S1000 42000
+   1173 ER_REQUIRES_PRIMARY_KEY S1000 42000
+   1177 ER_CHECK_NO_SUCH_TABLE S1000 42000
+   1178 ER_CHECK_NOT_IMPLEMENTED S1000 42000
+   1179 ER_CANT_DO_THIS_DURING_AN_TRANSACTION S1000 25000
+   1184 ER_NEW_ABORTING_CONNECTION S1000 08S01
+   1189 ER_MASTER_NET_READ S1000 08S01
+   1190 ER_MASTER_NET_WRITE S1000 08S01
+   1203 ER_TOO_MANY_USER_CONNECTIONS S1000 42000
+   1205 ER_LOCK_WAIT_TIMEOUT 41000 41000
+   1207 ER_READ_ONLY_TRANSACTION S1000 25000
+   1211 ER_NO_PERMISSION_TO_CREATE_USER S1000 42000
+   1213 ER_LOCK_DEADLOCK 41000 40001
+   1216 ER_NO_REFERENCED_ROW S1000 23000
+   1217 ER_ROW_IS_REFERENCED S1000 23000
+   1218 ER_CONNECT_TO_MASTER S1000 08S01
+   1222 ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT S1000 21000
+   1226 ER_USER_LIMIT_REACHED S1000 42000
+   1230 ER_NO_DEFAULT S1000 42000
+   1231 ER_WRONG_VALUE_FOR_VAR S1000 42000
+   1232 ER_WRONG_TYPE_FOR_VAR S1000 42000
+   1234 ER_CANT_USE_OPTION_HERE S1000 42000
+   1235 ER_NOT_SUPPORTED_YET S1000 42000
+   1239 ER_WRONG_FK_DEF S1000 42000
+   1241 ER_OPERAND_COLUMNS S1000 21000
+   1242 ER_SUBQUERY_NO_1_ROW S1000 21000
+   1247 ER_ILLEGAL_REFERENCE S1000 42S22
+   1248 ER_DERIVED_MUST_HAVE_ALIAS S1000 42000
+   1249 ER_SELECT_REDUCED S1000 01000
+   1250 ER_TABLENAME_NOT_ALLOWED_HERE S1000 42000
+   1251 ER_NOT_SUPPORTED_AUTH_MODE S1000 08004
+   1252 ER_SPATIAL_CANT_HAVE_NULL S1000 42000
+   1253 ER_COLLATION_CHARSET_MISMATCH S1000 42000
+   1261 ER_WARN_TOO_FEW_RECORDS S1000 01000
+   1262 ER_WARN_TOO_MANY_RECORDS S1000 01000
+   1263 ER_WARN_NULL_TO_NOTNULL S1000 01000
+   1264 ER_WARN_DATA_OUT_OF_RANGE S1000 01000
+   1265 ER_WARN_DATA_TRUNCATED S1000 01000
+   1280 ER_WRONG_NAME_FOR_INDEX S1000 42000
+   1281 ER_WRONG_NAME_FOR_CATALOG S1000 42000
+   1286 ER_UNKNOWN_STORAGE_ENGINE S1000 42000
+
 1.5. Connector/J Notes and Tips
 
 1.5.1. Basic JDBC Concepts
@@ -1526,19 +1983,21 @@
 import java.sql.DriverManager;
 import java.sql.SQLException;
 
-    ... try {
-            Connection conn = DriverManager.getConnection("jdbc:mysql:
-//localhost/test?user=monty&password=greatsqldb");
+...
+try {
+    Connection conn =
+       DriverManager.getConnection("jdbc:mysql://localhost/test?" +
+                                   "user=monty&password=greatsqldb");
 
-            // Do something with the Connection
+    // Do something with the Connection
 
-           ....
-        } catch (SQLException ex) {
-            // handle any errors
-            System.out.println("SQLException: " + ex.getMessage());
-            System.out.println("SQLState: " + ex.getSQLState());
-            System.out.println("VendorError: " + ex.getErrorCode());
-        }
+   ...
+} catch (SQLException ex) {
+    // handle any errors
+    System.out.println("SQLException: " + ex.getMessage());
+    System.out.println("SQLState: " + ex.getSQLState());
+    System.out.println("VendorError: " + ex.getErrorCode());
+}
 
    Once a Connection is established, it can be used to create
    Statement and PreparedStatement objects, as well as retrieve
@@ -1622,13 +2081,15 @@
    interface is fully implemented with the exception of the
    getParameterMetaData() method.
 
-   See [WARNING: missing xref target (id=stored-procedures)] for
-   more information on MySQL stored procedures.
+   For more information on MySQL stored procedures, please refer
+   to http://dev.mysql.com/doc/mysql/en/stored-procedures.html.
 
    Connector/J exposes stored procedure functionality through
    JDBC's CallableStatement interface.
 
-   Note.  Current versions of MySQL server do not return enough
+Note
+
+   Current versions of MySQL server do not return enough
    information for the JDBC driver to provide result set
    metadata for callable statements. This means that when using
    CallableStatement, ResultSetMetaData may return NULL.
@@ -1638,8 +2099,8 @@
    passed in via inputParam as a ResultSet:
 
    Example 3. Stored Procedures
-CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam I
-NT)
+CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), \
+                                        INOUT inOutParam INT)
 BEGIN
     DECLARE z INT;
     SET z = inOutParam + 1;
@@ -1674,8 +2135,10 @@
 
 
     cStmt.setString(1, "abcdefg");
-       Note.  Connection.prepareCall() is an expensive method,
-       due to the metadata retrieval that the driver performs to
+
+Note
+       Connection.prepareCall() is an expensive method, due to
+       the metadata retrieval that the driver performs to
        support output parameters. For performance reasons, you
        should try to minimize unnecessary calls to
        Connection.prepareCall() by reusing CallableStatement
@@ -1941,9 +2404,9 @@
 
     rs.close();
 
-    System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID(
-)': "
-        + autoIncKeyFromFunc);
+    System.out.println("Key returned from " +
+                       "'SELECT LAST_INSERT_ID()': " +
+                       autoIncKeyFromFunc);
 
 } finally {
 
@@ -2175,8 +2638,8 @@
           * variable, as JNDI lookups can be expensive as well.
           */
 
-        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MyS
-QLDB");
+        DataSource ds =
+          (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
 
         /*
          * The following code is what would actually be in your
@@ -2329,8 +2792,7 @@
     <!-- Don't use autoReconnect=true, it's going away eventually
          and it's a crutch for older connection pools that couldn't
          test connections. You need to decide whether your application
- is
-         supposed to deal with SQLExceptions (hint, it should), and
+         is supposed to deal with SQLExceptions (hint, it should), and
          how much of a performance penalty you're willing to pay
          to ensure 'freshness' of the connection -->
 
@@ -2470,15 +2932,360 @@
              our implementation of these to increase the robustness
              of the connection pool. -->
 
-        <exception-sorter-class-name>com.mysql.jdbc.integration.jboss.
-ExtendedMysqlExceptionSorter</exception-sorter-class-name>
-        <valid-connection-checker-class-name>com.mysql.jdbc.integratio
-n.jboss.MysqlValidConnectionChecker</valid-connection-checker-class-na
-me>
+        <exception-sorter-class-name>
+  com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
+        </exception-sorter-class-name>
+        <valid-connection-checker-class-name>
+  com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
+        </valid-connection-checker-class-name>
 
     </local-tx-datasource>
 </datasources>
 
+1.5.2.4. Using Connector/J with Spring
+
+   The Spring Framework is a Java-based application framework
+   designed for assisting in application design by providing a
+   way to configure components. The technique used by Spring is
+   a well known design pattern called Dependency Injection (see
+   Inversion of Control Containers and the Dependency Injection
+   pattern
+   (http://www.martinfowler.com/articles/injection.html)). This
+   article will focus on Java-oriented access to MySQL databases
+   with Spring 2.0. For those wondering, there is a .NET port of
+   Spring appropriately named Spring.NET.
+
+   Spring is not only a system for configuring components, but
+   also includes support for aspect oriented programming (AOP).
+   This is one of the main benefits and the foundation for
+   Spring's resource and transaction management. Spring also
+   provides utilities for integrating resource management with
+   JDBC and Hibernate.
+
+   For the examples in this section the MySQL world sample
+   database will be used. The first task is to setup a MySQL
+   data source through Spring. Components within Spring use the
+   "bean" terminology. For example, to configure a connection to
+   a MySQL server supporting the world sample database you might
+   use:
+<util:map id="dbProps">
+    <entry key="db.driver" value="com.mysql.jdbc.Driver"/>
+    <entry key="db.jdbcurl" value="jdbc:mysql://localhost/world"/>
+    <entry key="db.username" value="myuser"/>
+    <entry key="db.password" value="mypass"/>
+</util:map>
+
+
+   In the above example we are assigning values to properties
+   that will be used in the configuration. For the datasource
+   configuration:
+<bean id="dataSource"
+       class="org.springframework.jdbc.datasource.DriverManagerDataSou
+rce">
+    <property name="driverClassName" value="${db.driver}"/>
+    <property name="url" value="${db.jdbcurl}"/>
+    <property name="username" value="${db.username}"/>
+    <property name="password" value="${db.password}"/>
+</bean>
+
+   The placeholders are used to provide values for properties of
+   this bean. This means that you can specify all the properties
+   of the configuration in one place instead of entering the
+   values for each property on each bean. We do, however, need
+   one more bean to pull this all together. The last bean is
+   responsible for actually replacing the placeholders with the
+   property values.
+<bean
+ class="org.springframework.beans.factory.config.PropertyPlaceholderCo
+nfigurer">
+    <property name="properties" ref="dbProps"/>
+</bean>
+
+   Now that we have our MySQL data source configured and ready
+   to go, we write some Java code to access it. The example
+   below will retrieve three random cities and their
+   corresponding country using the data source we configured
+   with Spring.
+// Create a new application context. this processes the Spring config
+ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex1appContext.xml");
+// Retrieve the data source from the application context
+    DataSource ds = (DataSource) ctx.getBean("dataSource");
+// Open a database connection using Spring's DataSourceUtils
+Connection c = DataSourceUtils.getConnection(ds);
+try {
+    // retrieve a list of three random cities
+    PreparedStatement ps = c.prepareStatement(
+        "select City.Name as 'City', Country.Name as 'Country' " +
+        "from City inner join Country on City.CountryCode = Country.Co
+de " +
+        "order by rand() limit 3");
+    ResultSet rs = ps.executeQuery();
+    while(rs.next()) {
+        String city = rs.getString("City");
+        String country = rs.getString("Country");
+        System.out.printf("The city %s is in %s%n", city, country);
+    }
+} catch (SQLException ex) {
+    // something has failed and we print a stack trace to analyse the
+error
+    ex.printStackTrace();
+    // ignore failure closing connection
+    try { c.close(); } catch (SQLException e) { }
+} finally {
+    // properly release our connection
+    DataSourceUtils.releaseConnection(c, ds);
+}
+
+   This is very similar to normal JDBC access to MySQL with the
+   main difference being that we are using DataSourceUtils
+   instead of the DriverManager to create the connection.
+
+   While it may seem like a small difference, the implications
+   are somewhat far reaching. Spring manages this resource in a
+   way similar to a container managed data source in a J2EE
+   application server. When a connection is opened, it can be
+   subsequently accessed in other parts of the code if it is
+   synchronized with a transaction. This makes it possible to
+   treat different parts of your application as transactional
+   instead of passing around a database connection.
+
+1.5.2.4.1. Using JdbcTemplate
+
+   Spring makes extensive use of the Template method design
+   pattern (see Template Method Pattern
+   (http://en.wikipedia.org/wiki/Template_method_pattern)). Our
+   immediate focus will be on the JdbcTemplate and related
+   classes, specifically NamedParameterJdbcTemplate. The
+   template classes handle obtaining and releasing a connection
+   for data access when one is needed.
+
+   The next example shows how to use NamedParameterJdbcTemplate
+   inside of a DAO (Data Access Object) class to retrieve a
+   random city given a country code.
+public class Ex2JdbcDao {
+     /**
+     * Data source reference which will be provided by Spring.
+     */
+     private DataSource dataSource;
+
+     /**
+     * Our query to find a random city given a country code. Notice
+     * the ":country" parameter towards the end. This is called a
+     * named parameter.
+     */
+     private String queryString = "select Name from City " +
+        "where CountryCode = :country order by rand() limit 1";
+
+     /**
+     * Retrieve a random city using Spring JDBC access classes.
+     */
+     public String getRandomCityByCountryCode(String cntryCode) {
+         // A template that allows using queries with named parameters
+         NamedParameterJdbcTemplate template =
+         new NamedParameterJdbcTemplate(dataSource);
+         // A java.util.Map is used to provide values for the paramete
+rs
+         Map params = new HashMap();
+         params.put("country", cntryCode);
+         // We query for an Object and specify what class we are expec
+ting
+         return (String)template.queryForObject(queryString, params, S
+tring.class);
+     }
+
+    /**
+    * A JavaBean setter-style method to allow Spring to inject the dat
+a source.
+    * @param dataSource
+    */
+    public void setDataSource(DataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+}
+
+   The focus in the above code is on the
+   getRandomCityByCountryCode() method. We pass a country code
+   and use the NamedParameterJdbcTemplate to query for a city.
+   The country code is placed in a Map with the key "country",
+   which is the parameter is named in the SQL query.
+
+   To access this code, you need to configure it with Spring by
+   providing a reference to the data source.
+<bean id="dao" class="code.Ex2JdbcDao">
+    <property name="dataSource" ref="dataSource"/>
+</bean>
+
+   At this point, we can just grab a reference to the DAO from
+   Spring and call getRandomCityByCountryCode().
+// Create the application context
+    ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex2appContext.xml");
+    // Obtain a reference to our DAO
+    Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao");
+
+    String countryCode = "USA";
+
+    // Find a few random cities in the US
+    for(int i = 0; i < 4; ++i)
+        System.out.printf("A random city in %s is %s%n", countryCode,
+            dao.getRandomCityByCountryCode(countryCode));
+
+   This example shows how to use Spring's JDBC classes to
+   completely abstract away the use of traditional JDBC classes
+   including Connection and PreparedStatement.
+
+1.5.2.4.2. Transactional JDBC Access
+
+   You might be wondering how we can add transactions into our
+   code if we don't deal directly with the JDBC classes. Spring
+   provides a transaction management package that not only
+   replaces JDBC transaction management, but also allows
+   declarative transaction management (configuration instead of
+   code).
+
+   In order to use transactional database access, we will need
+   to change the storage engine of the tables in the world
+   database. The downloaded script explicitly creates MyISAM
+   tables which do not support transactional semantics. The
+   InnoDB storage engine does support transactions and this is
+   what we will be using. We can change the storage engine with
+   the following statements.
+ALTER TABLE City ENGINE=InnoDB;
+ALTER TABLE Country ENGINE=InnoDB;
+ALTER TABLE CountryLanguage ENGINE=InnoDB;
+
+   A good programming practice emphasized by Spring is
+   separating interfaces and implementations. What this means is
+   that we can create a Java interface and only use the
+   operations on this interface without any internal knowledge
+   of what the actual implementation is. We will let Spring
+   manage the implementation and with this it will manage the
+   transactions for our implementation.
+
+   First you create a simple interface:
+public interface Ex3Dao {
+    Integer createCity(String name, String countryCode,
+    String district, Integer population);
+}
+
+   This interface contains one method that will create a new
+   city record in the database and return the id of the new
+   record. Next you need to create an implementation of this
+   interface.
+public class Ex3DaoImpl implements Ex3Dao {
+    protected DataSource dataSource;
+    protected SqlUpdate updateQuery;
+    protected SqlFunction idQuery;
+
+    public Integer createCity(String name, String countryCode,
+        String district, Integer population) {
+            updateQuery.update(new Object[] { name, countryCode,
+                   district, population });
+            return getLastId();
+        }
+
+    protected Integer getLastId() {
+        return idQuery.run();
+    }
+}
+
+   You can see that we only operate on abstract query objects
+   here and don't deal directly with the JDBC API. Also, this is
+   the complete implementation. All of our transaction
+   management will be dealt with in the configuration. To get
+   the configuration started, we need to create the DAO.
+<bean id="dao" class="code.Ex3DaoImpl">
+    <property name="dataSource" ref="dataSource"/>
+    <property name="updateQuery">...</property>
+    <property name="idQuery">...</property>
+</bean>
+
+   Now you need to setup the transaction configuration. The
+   first thing you must do is create transaction manager to
+   manage the data source and a specification of what
+   transaction properties are required for for the dao methods.
+<bean id="transactionManager"
+  class="org.springframework.jdbc.datasource.DataSourceTransactionMana
+ger">
+    <property name="dataSource" ref="dataSource"/>
+</bean>
+
+<tx:advice id="txAdvice" transaction-manager="transactionManager">
+    <tx:attributes>
+        <tx:method name="*"/>
+    </tx:attributes>
+</tx:advice>
+
+   The preceding code creates a transaction manager that handles
+   transactions for the data source provided to it. The txAdvice
+   uses this transaction manager and the attributes specify to
+   create a transaction for all methods. Finally you need to
+   apply this advice with an AOP pointcut.
+<aop:config>
+    <aop:pointcut id="daoMethods"
+        expression="execution(* code.Ex3Dao.*(..))"/>
+     <aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/>
+</aop:config>
+
+   This basically says that all methods called on the Ex3Dao
+   interface will be wrapped in a transaction. To make use of
+   this, you only have to retrieve the dao from the application
+   context and call a method on the dao instance.
+Ex3Dao dao = (Ex3Dao) ctx.getBean("dao");
+Integer id = dao.createCity(name,  countryCode, district, pop);
+
+   We can verify from this that there is no transaction
+   management happening in our Java code and it's all configured
+   with Spring. This is a very powerful notion and regarded as
+   one of the most beneficial features of Spring.
+
+1.5.2.4.3. Connection Pooling
+
+   In many sitations, such as web applications, there will be a
+   large number of small database transactions. When this is the
+   case, it usually makes sense to create a pool of database
+   connections available for web requests as needed. Although
+   MySQL does not spawn an extra process when a connection is
+   made, there is still a small amount of overhead to create and
+   setup the connection. Pooling of connections also alleviates
+   problems such as collecting large amounts of sockets in the
+   TIME_WAIT state.
+
+   Setting up pooling of MySQL connections with Spring is as
+   simple as changing the data source configuration in the
+   application context. There are a number of configurations
+   that we can use. The first example is based on the Jakarta
+   Commons DBCP library
+   (http://jakarta.apache.org/commons/dbcp/). The example below
+   replaces the source configuration that was based on
+   DriverManagerDataSource with DBCP's BasicDataSource.
+<bean id="dataSource" destroy-method="close"
+  class="org.apache.commons.dbcp.BasicDataSource">
+    <property name="driverClassName" value="${db.driver}"/>
+    <property name="url" value="${db.jdbcurl}"/>
+    <property name="username" value="${db.username}"/>
+    <property name="password" value="${db.password}"/>
+    <property name="initialSize" value="3"/>
+</bean>
+
+   The configuration of the two solutions is very similar. The
+   difference is that DBCP will pool connections to the database
+   instead of creating a new connection every time one is
+   requested. We have also set a parameter here called
+   initialSize. This tells DBCP that we want three connections
+   in the pool when it is created.
+
+   Another way to configure connection pooling is to configure a
+   data source in our J2EE application server. Using JBoss as an
+   example, you can set up the MySQL connection pool by creating
+   a file called mysql-local-ds.xml and placing it in the
+   server/default/deploy directory in JBoss. Once we have this
+   setup, we can use JNDI to look it up. With Spring, this
+   lookup is very simple. The data source configuration looks
+   like this.
+<jee:jndi-lookup id="dataSource" jndi-name="java:MySQL_DS"/>
+
 1.5.3. Common Problems and Solutions
 
    There are a few issues that seem to be commonly encountered
@@ -2509,7 +3316,41 @@
      * [5]1.5.3.5: I'm trying to use JDBC-2.0 updatable result
        sets, and I get an exception saying my result set is not
        updatable.
+     * [6]1.5.3.6: I cannot connect to the MySQL server using
+       Connector/J, and I'm sure the connection paramters are
+       correct.
+     * [7]1.5.3.7: I am trying to connect to my MySQL server
+       within my application, but I get the following error and
+       stack trace:
+java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
 
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv faile
+d
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.<init>(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.ja
+va:411)
+     * [8]1.5.3.8: My application is deployed through JBoss and
+       I am using transactions to handle the statements on the
+       MySQL database. Under heavy loads I am getting a error
+       and stack trace, but these only occur after a fixed
+       period of heavy activity.
+     * [9]1.5.3.9: When using gcj an
+       java.io.CharConversionException is raised when working
+       with certain character sequences.
+     * [10]1.5.3.10: Updating a table that contains a primary
+       key that is either FLOAT or compound primary key that
+       uses FLOAT fails to update the table and raises an
+       exception.
+
    Questions and Answers
 
    1.5.3.1: When I try to connect to the database with MySQL
@@ -2529,27 +3370,32 @@
 
    You must add the necessary security credentials to the MySQL
    server for this to happen, using the GRANT statement to your
-   MySQL Server. See [WARNING: missing xref target (id=grant)]
-   for more information.
+   MySQL Server. See GRANT Syntax
+   (http://dev.mysql.com/doc/refman/5.0/en/grant.html), for more
+   information.
 
-   Note.  Testing your connectivity with the mysql command-line
-   client will not work unless you add the --host flag, and use
+Note
+
+   Testing your connectivity with the mysql command-line client
+   will not work unless you add the --host flag, and use
    something other than localhost for the host. The mysql
    command-line client will use Unix domain sockets if you use
    the special hostname localhost. If you are testing
    connectivity to localhost, use 127.0.0.1 as the hostname
    instead.
 
-   Warning.  Changing privileges and permissions improperly in
-   MySQL can potentially cause your server installation to not
-   have optimal security properties.
+Warning
 
+   Changing privileges and permissions improperly in MySQL can
+   potentially cause your server installation to not have
+   optimal security properties.
+
    1.5.3.2: My application throws an SQLException 'No Suitable
    Driver'. Why is this happening?
 
    There are three possible causes for this error:
      * The Connector/J driver is not in your CLASSPATH, see
-       Section A.2, "Installing Connector/J."
+       Section Section, "Connector/J Installation."
      * The format of your connection URL is incorrect, or you
        are referencing the wrong JDBC driver.
      * When using DriverManager, the jdbc.drivers system
@@ -2603,7 +3449,7 @@
    MySQL closes connections after 8 hours of inactivity. You
    either need to use a connection pool that handles stale
    connections or use the "autoReconnect" parameter (see Section
-   A.4.1, "Driver/Datasource Class Names, URL Syntax and
+   Section, "Driver/Datasource Class Names, URL Syntax and
    Configuration Properties for Connector/J").
 
    Also, you should be catching SQLExceptions in your
@@ -2621,146 +3467,138 @@
 
    Example 12. Example of transaction with retry logic
 public void doBusinessOp() throws SQLException {
-        Connection conn = null;
-        Statement stmt = null;
-        ResultSet rs = null;
+    Connection conn = null;
+    Statement stmt = null;
+    ResultSet rs = null;
 
-        //
-        // How many times do you want to retry the transaction
-        // (or at least _getting_ a connection)?
-        //
-        int retryCount = 5;
+    //
+    // How many times do you want to retry the transaction
+    // (or at least _getting_ a connection)?
+    //
+    int retryCount = 5;
 
-        boolean transactionCompleted = false;
+    boolean transactionCompleted = false;
 
-        do {
-            try {
-                conn = getConnection(); // assume getting this from a
-                                        // javax.sql.DataSource, or th
-e
-                                        // java.sql.DriverManager
+    do {
+        try {
+            conn = getConnection(); // assume getting this from a
+                                    // javax.sql.DataSource, or the
+                                    // java.sql.DriverManager
 
-                conn.setAutoCommit(false);
+            conn.setAutoCommit(false);
 
-                //
-                // Okay, at this point, the 'retry-ability' of the
-                // transaction really depends on your application logi
-c,
-                // whether or not you're using autocommit (in this cas
-e
-                // not), and whether you're using transacational stora
-ge
-                // engines
-                //
-                // For this example, we'll assume that it's _not_ safe
-                // to retry the entire transaction, so we set retry co
-unt
-                // to 0 at this point
-                //
-                // If you were using exclusively transaction-safe tabl
-es,
-                // or your application could recover from a connection
- going
-                // bad in the middle of an operation, then you would n
-ot
-                // touch 'retryCount' here, and just let the loop repe
-at
-                // until retryCount == 0.
-                //
-                retryCount = 0;
+            //
+            // Okay, at this point, the 'retry-ability' of the
+            // transaction really depends on your application logic,
+            // whether or not you're using autocommit (in this case
+            // not), and whether you're using transacational storage
+            // engines
+            //
+            // For this example, we'll assume that it's _not_ safe
+            // to retry the entire transaction, so we set retry
+            // count to 0 at this point
+            //
+            // If you were using exclusively transaction-safe tables,
+            // or your application could recover from a connection goi
+ng
+            // bad in the middle of an operation, then you would not
+            // touch 'retryCount' here, and just let the loop repeat
+            // until retryCount == 0.
+            //
+            retryCount = 0;
 
-                stmt = conn.createStatement();
+            stmt = conn.createStatement();
 
-                String query = "SELECT foo FROM bar ORDER BY baz";
+            String query = "SELECT foo FROM bar ORDER BY baz";
 
-                rs = stmt.executeQuery(query);
+            rs = stmt.executeQuery(query);
 
-                while (rs.next()) {
-                }
+            while (rs.next()) {
+            }
 
-                rs.close();
-                rs = null;
+            rs.close();
+            rs = null;
 
-                stmt.close();
-                stmt = null;
+            stmt.close();
+            stmt = null;
 
-                conn.commit();
-                conn.close();
-                conn = null;
+            conn.commit();
+            conn.close();
+            conn = null;
 
-                transactionCompleted = true;
-            } catch (SQLException sqlEx) {
+            transactionCompleted = true;
+        } catch (SQLException sqlEx) {
 
-                //
-                // The two SQL states that are 'retry-able' are 08S01
-                // for a communications error, and 40001 for deadlock.
-                //
-                // Only retry if the error was due to a stale connecti
-on,
-                // communications problem or deadlock
-                //
+            //
+            // The two SQL states that are 'retry-able' are 08S01
+            // for a communications error, and 40001 for deadlock.
+            //
+            // Only retry if the error was due to a stale connection,
+            // communications problem or deadlock
+            //
 
-                String sqlState = sqlEx.getSQLState();
+            String sqlState = sqlEx.getSQLState();
 
-                if ("08S01".equals(sqlState) || "40001".equals(sqlStat
-e)) {
-                    retryCount--;
-                } else {
-                    retryCount = 0;
+            if ("08S01".equals(sqlState) || "40001".equals(sqlState))
+{
+                retryCount--;
+            } else {
+                retryCount = 0;
+            }
+        } finally {
+            if (rs != null) {
+                try {
+                    rs.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this . . .
                 }
-            } finally {
-                if (rs != null) {
-                    try {
-                        rs.close();
-                    } catch (SQLException sqlEx) {
-                        // You'd probably want to log this . . .
-                    }
+            }
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this as well . . .
                 }
+            }
 
-                if (stmt != null) {
+            if (conn != null) {
+                try {
+                    //
+                    // If we got here, and conn is not null, the
+                    // transaction should be rolled back, as not
+                    // all work has been done
+
                     try {
-                        stmt.close();
-                    } catch (SQLException sqlEx) {
-                        // You'd probably want to log this as well . .
- .
+                        conn.rollback();
+                    } finally {
+                        conn.close();
                     }
-                }
+                } catch (SQLException sqlEx) {
+                    //
+                    // If we got an exception here, something
+                    // pretty serious is going on, so we better
+                    // pass it up the stack, rather than just
+                    // logging it. . .
 
-                if (conn != null) {
-                    try {
-                        //
-                        // If we got here, and conn is not null, the
-                        // transaction should be rolled back, as not
-                        // all work has been done
-
-                        try {
-                            conn.rollback();
-                        } finally {
-                            conn.close();
-                        }
-                    } catch (SQLException sqlEx) {
-                        //
-                        // If we got an exception here, something
-                        // pretty serious is going on, so we better
-                        // pass it up the stack, rather than just
-                        // logging it. . .
-
-                        throw sqlEx;
-                    }
+                    throw sqlEx;
                 }
             }
-        } while (!transactionCompleted && (retryCount > 0));
-    }
+        }
+    } while (!transactionCompleted && (retryCount > 0));
+}
 
-   Note.  Use of the autoReconnect option is not recommended
-   because there is no safe method of reconnecting to the MySQL
-   server without risking some corruption of the connection
-   state or database state information. Instead, you should use
-   a connection pool which will enable your application to
-   connect to the MySQL server using an available connection
-   from the pool. The autoReconnect facility is deprecated, and
-   may be removed in a future release.
+Note
 
+   Use of the autoReconnect option is not recommended because
+   there is no safe method of reconnecting to the MySQL server
+   without risking some corruption of the connection state or
+   database state information. Instead, you should use a
+   connection pool which will enable your application to connect
+   to the MySQL server using an available connection from the
+   pool. The autoReconnect facility is deprecated, and may be
+   removed in a future release.
+
    1.5.3.5: I'm trying to use JDBC-2.0 updatable result sets,
    and I get an exception saying my result set is not updatable.
 
@@ -2780,6 +3618,96 @@
    where you can individually specify the criteria to be matched
    using a WHERE clause.
 
+   1.5.3.6: I cannot connect to the MySQL server using
+   Connector/J, and I'm sure the connection paramters are
+   correct.
+
+   Make sure that the skip-networking option has not been
+   enabled on your server. Connector/J must be able to
+   communicate with your server over TCP/IP, named sockets are
+   not supported. Also ensure that you are not filtering
+   connections through a Firewall or other network security
+   system. For more informaiton, see Can't connect to [local]
+   MySQL server
+   (http://dev.mysql.com/doc/refman/5.0/en/can-not-connect-to-se
+   rver.html).
+
+   1.5.3.7: I am trying to connect to my MySQL server within my
+   application, but I get the following error and stack trace:
+java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
+
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv faile
+d
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.<init>(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.ja
+va:411)
+
+   The error probably indicates that you are using a older
+   version of the Connector/J JDBC driver (2.0.14 or 3.0.x) and
+   you are trying to connect to a MySQL server with version 4.1x
+   or newer. The older drivers are not compatible with 4.1 or
+   newer of MySQL as they do not support the newer
+   authentication mechanisms.
+
+   It is likely that the older version of the Connector/J driver
+   exists within your application directory or your CLASSPATH
+   includes the older Connector/J package.
+
+   1.5.3.8: My application is deployed through JBoss and I am
+   using transactions to handle the statements on the MySQL
+   database. Under heavy loads I am getting a error and stack
+   trace, but these only occur after a fixed period of heavy
+   activity.
+
+   This is a JBoss, not Connector/J, issue and is connected to
+   the use of transactions. Under heavy loads the time taken for
+   transactions to complete can increase, and the error is
+   caused because you have exceeded the predefined timeout.
+
+   You can increase the timeout value by setting the
+   TransactionTimeout attribute to the TransactionManagerService
+   within the /conf/jboss-service.xml file (pre-4.0.3) or
+   /deploy/jta-service.xml for JBoss 4.0.3 or later. See
+   TransactionTimeoute
+   (http://wiki.jboss.org/wiki/Wiki.jsp?page=TransactionTimeout)
+   within the JBoss wiki for more information.
+
+   1.5.3.9: When using gcj an java.io.CharConversionException is
+   raised when working with certain character sequences.
+
+   This is a known issue with gcj which raises an exception when
+   it reaches an unknown character or one it cannot convert. You
+   should add useJvmCharsetConverters=true to your connection
+   string to force character conversion outside of the gcj
+   libraries, or try a different JDK.
+
+   1.5.3.10: Updating a table that contains a primary key that
+   is either FLOAT or compound primary key that uses FLOAT fails
+   to update the table and raises an exception.
+
+   Connector/J adds conditions to the WHERE clause during an
+   UPDATE to check the old values of the primary key. If there
+   is no match then Connector/J considers this a failure
+   condition and raises an exception.
+
+   The problem is that rounding differences between supplied
+   values and the values stored in the database may mean that
+   the values never match, and hence the update fails. The issue
+   will affect all queries, not just those from Connector/J.
+
+   To prevent this issue, use a primary key that does not use
+   FLOAT. If you have to use a floating point column in your
+   primary key use DOUBLE or DECIMAL types in place of FLOAT.
+
 1.6. Connector/J Support
 
 1.6.1. Connector/J Community Support
@@ -2926,8 +3854,13 @@
 
 References
 
-   1. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-1
-   2. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-2
-   3. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-3
-   4. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-4
-   5. file://localhost/src/extern/MySQL/bk/mysqldoc/refman-5.0/connector-j-nolink.html#qandaitem-1-5-3-5
+   1. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-1
+   2. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-2
+   3. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-3
+   4. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-4
+   5. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-5
+   6. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-6
+   7. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-7
+   8. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-8
+   9. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-9
+  10. file://localhost/home/paul/bk-test/mysqldoc/refman-common/connector-j-nolink.html#qandaitem-1-5-3-10

Modified: trunk/mysql-connector-java/docs/connector-j.html
===================================================================
--- trunk/mysql-connector-java/docs/connector-j.html	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/docs/connector-j.html	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,4 +1,4 @@
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>1. MySQL Connector/J</title><meta name="generator" content="DocBook XSL Stylesheets V1.69.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="connector-j"></a>1. MySQL Connector/J</h2></div></div><hr></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-versions">1.1. Connector/J Versions</a></span></dt><dt><span class="section"><a href="#connector-j-installing">1.2. Installing Connector/J</a></span></dt><dt><span class="section"><a href="#connector-j-examples">1.3. Connector/J Examples</a></span></dt><dt><span class="section"><a href="#connector-j-reference">1.4. Connector/J (JDBC) Reference</a></span></dt><dt><span class="section"><a href="#connector-j-usagenotes">1.5. Connector/J Notes and Tips</a></span></dt><dt><span class="section"><a href="#connector-j-support">1.6. Connector/J Support</a></span></dt></dl></div><p>
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>1. MySQL Connector/J</title><meta name="generator" content="DocBook XSL Stylesheets V1.69.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="connector-j"></a>1. MySQL Connector/J</h2></div></div><hr></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-versions">1.1. Connector/J Versions</a></span></dt><dt><span class="section"><a href="#connector-j-installing">1.2. Connector/J Installation</a></span></dt><dt><span class="section"><a href="#connector-j-examples">1.3. Connector/J Examples</a></span></dt><dt><span class="section"><a href="#connector-j-reference">1.4. Connector/J (JDBC) Reference</a></span></dt><dt><span class="section"><a href="#connector-j-usagenotes">1.5. Connector/J Notes and Tips</a></span></dt><dt><span class="section"><a href="#connector-j-support">1.6. Connector/J Support</a></span></dt></dl></div><p>
     MySQL provides connectivity for client applications developed in the
     Java programming language via a JDBC driver, which is called MySQL
     Connector/J.
@@ -31,31 +31,73 @@
         <a href="http://java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html" target="_top">JDBC
         Short Course</a> — A more in-depth tutorial from Sun
         and JGuru
-      </p></li></ul></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-versions"></a>1.1. Connector/J Versions</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-versions-java">1.1.1. Java Versions Supported</a></span></dt></dl></div><p>
-      There are currently three version of MySQL Connector/J available:
+      </p></li></ul></div><p>
+    <span class="bold"><strong>Key topics:</strong></span>
+  </p><div class="itemizedlist"><ul type="disc"><li><p>
+        For help with connection strings, connection options setting up
+        your connection through JDBC, see
+        <a href="#connector-j-reference-configuration-properties" title="1.4.1. Driver/Datasource Class Names, URL Syntax and Configuration Properties
+        for Connector/J">Section 1.4.1, “Driver/Datasource Class Names, URL Syntax and Configuration Properties
+        for Connector/J”</a>.
+      </p></li><li><p>
+        For tips on using Connector/J and JDBC with generic J2EE
+        toolkits, see <a href="#connector-j-usagenotes-j2ee" title="1.5.2. Using Connector/J with J2EE and Other Java Frameworks">Section 1.5.2, “Using Connector/J with J2EE and Other Java Frameworks”</a>.
+      </p></li><li><p>
+        Developers using the Tomcat server platform, see
+        <a href="#connector-j-usagenotes-tomcat" title="1.5.2.2. Using Connector/J with Tomcat">Section 1.5.2.2, “Using Connector/J with Tomcat”</a>.
+      </p></li><li><p>
+        Developers using JBoss, see
+        <a href="#connector-j-usagenotes-jboss" title="1.5.2.3. Using Connector/J with JBoss">Section 1.5.2.3, “Using Connector/J with JBoss”</a>.
+      </p></li><li><p>
+        Developers using Spring, see
+        <a href="#connector-j-usagenotes-spring-config" title="1.5.2.4. Using Connector/J with Spring">Section 1.5.2.4, “Using Connector/J with Spring”</a>.
+      </p></li></ul></div><p class="mnmas-kb"><b>MySQL Enterprise</b>
+      MySQL Enterprise subscribers will find more information about
+      using JDBC with MySQL in the Knowledge Base articles about
+      <a href="https://kb.mysql.com/search.php?cat=search&amp;category=10" target="_top">
+      JDBC</a>. Access to the MySQL Knowledge Base collection of
+      articles is one of the advantages of subscribing to MySQL
+      Enterprise. For more information see
+      <a href="http://www.mysql.com/products/enterprise/advisors.html" target="_top">http://www.mysql.com/products/enterprise/advisors.html</a>.
+    </p><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-versions"></a>1.1. Connector/J Versions</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-versions-java">1.1.1. Java Versions Supported</a></span></dt></dl></div><p>
+      There are currently four versions of MySQL Connector/J available:
     </p><div class="itemizedlist"><ul type="disc"><li><p>
+          Connector/J 5.1 is current in alpha status. It provides
+          compatibility with all the functionality of MySQL, including
+          4.1, 5.0, 5.1 and the 6.0 alpha release featuring the new
+          Falcon storage engine. Connector/J 5.1 provides ease of
+          development features, including auto-registration with the
+          Driver Manager, standardized validity checks, categorized
+          SQLExceptions, support for the JDBC-4.0 XML processing, per
+          connection client information, <code class="literal">NCHAR</code>,
+          <code class="literal">NVARCHAR</code> and <code class="literal">NCLOB</code>
+          types. This release also includes all bug fixes up to and
+          including Connector/J 5.0.6.
+        </p></li><li><p>
+          Connector/J 5.0 provides support for all the functionality
+          offered by Connector/J 3.1 and includes distributed
+          transaction (XA) support.
+        </p></li><li><p>
+          Connector/J 3.1 was designed for connectivity to MySQL 4.1 and
+          MySQL 5.0 servers and provides support for all the
+          functionality in MySQL 5.0 except distributed transaction (XA)
+          support.
+        </p></li><li><p>
           Connector/J 3.0 provides core functionality and was designed
           with connectivity to MySQL 3.x or MySQL 4.1 servers, although
           it will provide basic compatibility with later versions of
           MySQL. Connector/J 3.0 does not support server-side prepared
           statements, and does not support any of the features in
           versions of MySQL later than 4.1.
-        </p></li><li><p>
-          Connector/J 3.1 was designed for connectivity to MySQL 4.1 and
-          MySQL 5.0 servers and provides support for all the
-          functionality in MySQL 5.0 except distributed transaction (XA)
-          support.
-        </p></li><li><p>
-          Connector/J 5.0 provides support for all the functionality
-          offered by Connector/J 3.1 and includes distributed
-          transaction (XA) support.
         </p></li></ul></div><p>
       The current recommended version for Connector/J is 5.0. This guide
       covers all three connector versions, with specific notes given
       where a setting applies to a specific option.
     </p><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-versions-java"></a>1.1.1. Java Versions Supported</h4></div></div></div><p>
         MySQL Connector/J supports Java-2 JVMs, including:
-      </p><div class="itemizedlist"><ul type="disc"><li><p>JDK 1.2.x (only for Connector/J 3.1.x or earlier)</p></li><li><p>
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            JDK 1.2.x (only for Connector/J 3.1.x or earlier)
+          </p></li><li><p>
             JDK 1.3.x
           </p></li><li><p>
             JDK 1.4.x
@@ -67,7 +109,7 @@
         <a href="#connector-j-installing-source" title="1.2.4. Installing from the Development Source Tree">Section 1.2.4, “Installing from the Development Source Tree”</a>) then you must
         use JDK 1.4.x or newer to compiler the Connector package.
       </p><p>
-        MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x
+        MySQL Connector/J does not support JDK-1.1.x or JDK-1.0.x.
       </p><p>
         Because of the implementation of
         <code class="classname">java.sql.Savepoint</code>, Connector/J 3.1.0 and
@@ -83,12 +125,13 @@
         also not available on JVMs older than 1.4.x, as it relies on
         <code class="classname">java.util.LinkedHashMap</code> which was first
         available in JDK-1.4.0.
-      </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-installing"></a>1.2. Installing Connector/J</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-installing-binary">1.2.1. Installing Connector/J from a Binary Distribution</a></span></dt><dt><span class="section"><a href="#connector-j-installing-classpath">1.2.2. Installing the Driver and Configuring the <code class="literal">CLASSPATH</code></a></span></dt><dt><span class="section"><a href="#connector-j-installing-upgrading">1.2.3. Upgrading from an Older Version</a></span></dt><dt><span class="section"><a href="#connector-j-installing-source">1.2.4. Installing from the Development Source Tree</a></span></dt></dl></div><p>
+      </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-installing"></a>1.2. Connector/J Installation</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-installing-binary">1.2.1. Installing Connector/J from a Binary Distribution</a></span></dt><dt><span class="section"><a href="#connector-j-installing-classpath">1.2.2. Installing the Driver and Configuring the <code class="literal">CLASSPATH</code></a></span></dt><dt><span class="section"><a href="#connector-j-installing-upgrading">1.2.3. Upgrading from an Older Version</a></span></dt><dt><span class="section"><a href="#connector-j-installing-source">1.2.4. Installing from the Development Source Tree</a></span></dt></dl></div><p>
       You can install the Connector/J package using two methods, using
       either the binary or source distribution. The binary distribution
       provides the easiest methods for installation; the source
       distribution enables you to customize your installation further.
-      With with either solution, you must
+      With either solution, you must manually add the Connector/J
+      location to your Java <code class="literal">CLASSPATH</code>.
     </p><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-installing-binary"></a>1.2.1. Installing Connector/J from a Binary Distribution</h4></div></div></div><p>
         The easiest method of installation is to use the binary
         distribution of the Connector/J package. The binary distribution
@@ -111,7 +154,7 @@
         the driver JAR file.
       </p><p>
         You should not use the debug build of the driver unless
-        instructed to do so when reporting a problem ors bug to MySQL
+        instructed to do so when reporting a problem or a bug to MySQL
         AB, as it is not designed to be run in production environments,
         and will have adverse performance impact when used. The debug
         binary also depends on the Aspect/J runtime library, which is
@@ -119,8 +162,8 @@
         that comes with the Connector/J distribution.
       </p><p>
         You will need to use the appropriate graphical or command-line
-        utility to un-archive the distribution (for example, WinZip for
-        the .zip archive, and <span><strong class="command">tar</strong></span> for the .tar.gz
+        utility to extract the distribution (for example, WinZip for the
+        .zip archive, and <span><strong class="command">tar</strong></span> for the .tar.gz
         archive). Because there are potentially long filenames in the
         distribution, we use the GNU tar archive format. You will need
         to use GNU tar (or an application that understands the GNU tar
@@ -140,7 +183,7 @@
         class that implements java.sql.Driver.
       </p><p>
         You can set the <code class="literal">CLASSPATH</code> environment
-        variableunder UNIX, Linux or Mac OS X either locally for a user
+        variable under UNIX, Linux or Mac OS X either locally for a user
         within their <code class="literal">.profile</code>,
         <code class="literal">.login</code> or other login file. You can also set
         it globally by editing the global
@@ -149,9 +192,9 @@
         For example, under a C shell (csh, tcsh) you would add the
         Connector/J driver to your <code class="literal">CLASSPATH</code> using
         the following:
-      </p><pre class="programlisting">shell&gt; setenv CLASSPATH /path/to/mysql-connector-java-[version]-bin.jar:$CLASSPATH</pre><p>
+      </p><pre class="programlisting">shell&gt; setenv CLASSPATH /path/mysql-connector-java-[ver]-bin.jar:$CLASSPATH</pre><p>
         Or with a Bourne-compatible shell (sh, ksh, bash):
-      </p><pre class="programlisting">export set CLASSPATH=/path/to/mysql-connector-java-[version]-bin.jar:$CLASSPATH</pre><p>
+      </p><pre class="programlisting">export set CLASSPATH=/path/mysql-connector-java-[ver]-bin.jar:$CLASSPATH</pre><p>
         Within Windows 2000, Windows XP and Windows Server 2003, you
         must set the environment variable through the System control
         panel.
@@ -217,7 +260,7 @@
         </p><div class="itemizedlist"><ul type="disc"><li><p>
               <span class="bold"><strong>Unicode Character Sets</strong></span>
               — See the next section, as well as
-              ???, for information on this new
+              <a href="http://dev.mysql.com/doc/refman/5.0/en/charset.html" target="_top">Character Set Support</a>, for information on this new
               feature of MySQL. If you have something misconfigured, it
               will usually show up as an error with a message similar to
               <code class="literal">Illegal mix of collations</code>.
@@ -351,12 +394,12 @@
               connection property:
             </p><p>
               <code class="computeroutput">useServerPrepStmts=false</code>
-            </p></li></ul></div></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-installing-source"></a>1.2.4. Installing from the Development Source Tree</h4></div></div></div><p><b>Caution. </b>
+            </p></li></ul></div></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-installing-source"></a>1.2.4. Installing from the Development Source Tree</h4></div></div></div><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Caution</h3><p>
           You should read this section only if you are interested in
           helping us test our new code. If you just want to get MySQL
           Connector/J up and running on your system, you should use a
           standard release distribution.
-        </p><p>
+        </p></div><p>
         To install MySQL Connector/J from the development source tree,
         make sure that you have the following prerequisites:
       </p><div class="itemizedlist"><ul type="disc"><li><p>
@@ -454,7 +497,7 @@
         </p></li><li><p>
           <a href="#connector-j-examples-transaction-retry" title="Example 12. Example of transaction with retry logic">Example 12, “Example of transaction with retry logic”</a>
         </p></li></ul></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-reference"></a>1.4. Connector/J (JDBC) Reference</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-reference-configuration-properties">1.4.1. Driver/Datasource Class Names, URL Syntax and Configuration Properties
-        for Connector/J</a></span></dt><dt><span class="section"><a href="#connector-j-reference-implementation-notes">1.4.2. JDBC API Implementation Notes</a></span></dt><dt><span class="section"><a href="#connector-j-reference-type-conversions">1.4.3. Java, JDBC and MySQL Types</a></span></dt><dt><span class="section"><a href="#connector-j-reference-charsets">1.4.4. Using Character Sets and Unicode</a></span></dt><dt><span class="section"><a href="#connector-j-reference-using-ssl">1.4.5. Connecting Securely Using SSL</a></span></dt><dt><span class="section"><a href="#connector-j-reference-replication-connection">1.4.6. Using Master/Slave Replication with ReplicationConnection</a></span></dt></dl></div><p>
+        for Connector/J</a></span></dt><dt><span class="section"><a href="#connector-j-reference-implementation-notes">1.4.2. JDBC API Implementation Notes</a></span></dt><dt><span class="section"><a href="#connector-j-reference-type-conversions">1.4.3. Java, JDBC and MySQL Types</a></span></dt><dt><span class="section"><a href="#connector-j-reference-charsets">1.4.4. Using Character Sets and Unicode</a></span></dt><dt><span class="section"><a href="#connector-j-reference-using-ssl">1.4.5. Connecting Securely Using SSL</a></span></dt><dt><span class="section"><a href="#connector-j-reference-replication-connection">1.4.6. Using Master/Slave Replication with ReplicationConnection</a></span></dt><dt><span class="section"><a href="#connector-j-reference-error-sqlstates">1.4.7. Mapping MySQL Error Numbers to SQLStates</a></span></dt></dl></div><p>
       This section of the manual contains reference material for MySQL
       Connector/J, some of which is automatically generated during the
       Connector/J build process.
@@ -541,85 +584,370 @@
             implementations of the
             <code class="literal">javax.sql.DataSource</code>
             <code class="literal">setURL()</code> method.
-          </p><p><b>Note. </b>
+          </p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
               If the mechanism you use to configure a JDBC URL is
               XML-based, you will need to use the XML character literal
               &amp;amp; to separate configuration parameters, as the
               ampersand is a reserved character for XML.
-            </p></li></ul></div><p>
+            </p></div></li></ul></div><p>
         The properties are listed in the following tables.
       </p><p><b>Connection/Authentication. </b>
-      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td>
-                     <span class="bold"><strong>Property Name</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Definition</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Default Value</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Since Version</strong></span>
-                  </td></tr><tr><td>user</td><td>The user to connect as</td><td>
-                  </td><td>all</td></tr><tr><td>password</td><td>The password to use when connecting</td><td>
-                  </td><td>all</td></tr><tr><td>socketFactory</td><td>The name of the class that the driver should use for creating socket connections to the server. This class must implement the interface 'com.mysql.jdbc.SocketFactory' and have public no-args constructor.</td><td>com.mysql.jdbc.StandardSocketFactory</td><td>3.0.3</td></tr><tr><td>connectTimeout</td><td>Timeout for socket connect (in milliseconds), with 0 being no timeout. Only works on JDK-1.4 or newer. Defaults to '0'.</td><td>0</td><td>3.0.1</td></tr><tr><td>socketTimeout</td><td>Timeout on network socket operations (0, the default means no timeout).</td><td>0</td><td>3.0.1</td></tr><tr><td>useConfigs</td><td>Load the comma-delimited list of configuration properties before parsing the URL or applying user-specified properties. These configurations are explained in the 'Configurations' of the documentation.</td><td>
-                  </td><td>3.1.5</td></tr><tr><td>interactiveClient</td><td>Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout connections based on INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT</td><td>false</td><td>3.1.0</td></tr><tr><td>propertiesTransform</td><td>An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that the driver will use to modify URL properties passed to the driver before attempting a connection</td><td>
-                  </td><td>3.1.4</td></tr><tr><td>useCompression</td><td>Use zlib compression when communicating with the server (true/false)? Defaults to 'false'.</td><td>false</td><td>3.0.17</td></tr></tbody></table></div><p>
-   </p><p><b>High Availability and Clustering. </b>
-      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td>
-                     <span class="bold"><strong>Property Name</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Definition</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Default Value</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Since Version</strong></span>
-                  </td></tr><tr><td>autoReconnect</td><td>Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don'thandle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead andstale connections properly. Alternatively, investigate setting the MySQL server variable "wait_timeout"to some high value rather than the default of 8 hours.</td><td>false</td><td>1.1</td></tr><tr><td>autoReconnectForPools</td><td>Use a reconnection strategy appropriate for connection pools (defaults to 'false')</td><td>false</td><td>3.1.3</td></tr><tr><td>failOverReadOnly</td><td>When failing over in autoReconnect mode, should the connection be set to 'read-only'?</td><td>true</td><td>3.0.12</td></tr><tr><td>reconnectAtTxEnd</td><td>If autoReconnect is set to true, should the driver attempt reconnectionsat the end of every transaction?</td><td>false</td><td>3.0.10</td></tr><tr><td>roundRobinLoadBalance</td><td>When autoReconnect is enabled, and failoverReadonly is false, should we pick hosts to connect to on a round-robin basis?</td><td>false</td><td>3.1.2</td></tr><tr><td>queriesBeforeRetryMaster</td><td>Number of queries to issue before falling back to master when failed over (when using multi-host failover). Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the master. Defaults to 50.</td><td>50</td><td>3.0.2</td></tr><tr><td>secondsBeforeRetryMaster</td><td>How long should the driver wait, when failed over, before attempting to reconnect to the master server? Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the master. Time in seconds, defaults to 30</td><td>30</td><td>3.0.2</td></tr><tr><td>enableDeprecatedAutoreconnect</td><td>Auto-reconnect functionality is deprecated starting with version 3.2, and will be removed in version 3.3. Set this property to 'true' to disable the check for the feature being configured.</td><td>false</td><td>3.2.1</td></tr><tr><td>resourceId</td><td>A globally unique name that identifies the resource that this datasource or connection is connected to, used for XAResource.isSameRM() when the driver can't determine this value based on hostnames used in the URL</td><td>
-                  </td><td>5.0.1</td></tr></tbody></table></div><p>
-   </p><p><b>Security. </b>
-      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td>
-                     <span class="bold"><strong>Property Name</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Definition</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Default Value</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Since Version</strong></span>
-                  </td></tr><tr><td>allowMultiQueries</td><td>Allow the use of ';' to delimit multiple queries during one statement (true/false, defaults to 'false'</td><td>false</td><td>3.1.1</td></tr><tr><td>useSSL</td><td>Use SSL when communicating with the server (true/false), defaults to 'false'</td><td>false</td><td>3.0.2</td></tr><tr><td>requireSSL</td><td>Require SSL connection if useSSL=true? (defaults to 'false').</td><td>false</td><td>3.1.0</td></tr><tr><td>allowUrlInLocalInfile</td><td>Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements?</td><td>false</td><td>3.1.4</td></tr><tr><td>paranoid</td><td>Take measures to prevent exposure sensitive information in error messages and clear data structures holding sensitive data when possible? (defaults to 'false')</td><td>false</td><td>3.0.1</td></tr></tbody></table></div><p>
-   </p><p><b>Performance Extensions. </b>
-      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td>
-                     <span class="bold"><strong>Property Name</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Definition</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Default Value</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Since Version</strong></span>
-                  </td></tr><tr><td>metadataCacheSize</td><td>The number of queries to cacheResultSetMetadata for if cacheResultSetMetaData is set to 'true' (default 50)</td><td>50</td><td>3.1.1</td></tr><tr><td>prepStmtCacheSize</td><td>If prepared statement caching is enabled, how many prepared statements should be cached?</td><td>25</td><td>3.0.10</td></tr><tr><td>prepStmtCacheSqlLimit</td><td>If prepared statement caching is enabled, what's the largest SQL the driver will cache the parsing for?</td><td>256</td><td>3.0.10</td></tr><tr><td>useCursorFetch</td><td>If connected to MySQL &gt; 5.0.2, and setFetchSize() &gt; 0 on a statement, should that statement use cursor-based fetching to retrieve rows?</td><td>false</td><td>5.0.0</td></tr><tr><td>blobSendChunkSize</td><td>Chunk to use when sending BLOB/CLOBs via ServerPreparedStatements</td><td>1048576</td><td>3.1.9</td></tr><tr><td>cacheCallableStmts</td><td>Should the driver cache the parsing stage of CallableStatements</td><td>false</td><td>3.1.2</td></tr><tr><td>cachePrepStmts</td><td>Should the driver cache the parsing stage of PreparedStatements of client-side prepared statements, the "check" for suitability of server-side prepared and server-side prepared statements themselves?</td><td>false</td><td>3.0.10</td></tr><tr><td>cacheResultSetMetadata</td><td>Should the driver cache ResultSetMetaData for Statements and PreparedStatements? (Req. JDK-1.4+, true/false, default 'false')</td><td>false</td><td>3.1.1</td></tr><tr><td>cacheServerConfiguration</td><td>Should the driver cache the results of 'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis?</td><td>false</td><td>3.1.5</td></tr><tr><td>defaultFetchSize</td><td>The driver will call setFetchSize(n) with this value on all newly-created Statements</td><td>0</td><td>3.1.9</td></tr><tr><td>dontTrackOpenResources</td><td>The JDBC specification requires the driver to automatically track and close resources, however if your application doesn't do a good job of explicitly calling close() on statements or result sets, this can cause memory leakage. Setting this property to true relaxes this constraint, and can be more memory efficient for some applications.</td><td>false</td><td>3.1.7</td></tr><tr><td>dynamicCalendars</td><td>Should the driver retrieve the default calendar when required, or cache it per connection/session?</td><td>false</td><td>3.1.5</td></tr><tr><td>elideSetAutoCommits</td><td>If using MySQL-4.1 or newer, should the driver only issue 'set autocommit=n' queries when the server's state doesn't match the requested state by Connection.setAutoCommit(boolean)?</td><td>false</td><td>3.1.3</td></tr><tr><td>holdResultsOpenOverStatementClose</td><td>Should the driver close result sets on Statement.close() as required by the JDBC specification?</td><td>false</td><td>3.1.7</td></tr><tr><td>locatorFetchBufferSize</td><td>If 'emulateLocators' is configured to 'true', what size buffer should be used when fetching BLOB data for getBinaryInputStream?</td><td>1048576</td><td>3.2.1</td></tr><tr><td>rewriteBatchedStatements</td><td>Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream(),the driver won't be able to determine the optimium number of parameters per batch and you might receive an error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements.</td><td>false</td><td>3.1.13</td></tr><tr><td>useFastIntParsing</td><td>Use internal String-&gt;Integer conversion routines to avoid excessive object creation?</td><td>true</td><td>3.1.4</td></tr><tr><td>useJvmCharsetConverters</td><td>Always use the character encoding routines built into the JVM, rather than using lookup tables for single-byte character sets? (The default of "true" for this is appropriate for newer JVMs</td><td>true</td><td>5.0.1</td></tr><tr><td>useLocalSessionState</td><td>Should the driver refer to the internal values of autocommit and transaction isolation that are set by Connection.setAutoCommit() and Connection.setTransactionIsolation(), rather than querying the database?</td><td>false</td><td>3.1.7</td></tr><tr><td>useReadAheadInput</td><td>Use newer, optimized non-blocking, buffered input stream when reading from the server?</td><td>true</td><td>3.1.5</td></tr></tbody></table></div><p>
-   </p><p><b>Debuging/Profiling. </b>
-      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td>
-                     <span class="bold"><strong>Property Name</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Definition</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Default Value</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Since Version</strong></span>
-                  </td></tr><tr><td>logger</td><td>The name of a class that implements 'com.mysql.jdbc.log.Log' that will be used to log messages to.(default is 'com.mysql.jdbc.log.StandardLogger', which logs to STDERR)</td><td>com.mysql.jdbc.log.StandardLogger</td><td>3.1.1</td></tr><tr><td>profileSQL</td><td>Trace queries and their execution/fetch times to the configured logger (true/false) defaults to 'false'</td><td>false</td><td>3.1.0</td></tr><tr><td>reportMetricsIntervalMillis</td><td>If 'gatherPerfMetrics' is enabled, how often should they be logged (in ms)?</td><td>30000</td><td>3.1.2</td></tr><tr><td>maxQuerySizeToLog</td><td>Controls the maximum length/size of a query that will get logged when profiling or tracing</td><td>2048</td><td>3.1.3</td></tr><tr><td>packetDebugBufferSize</td><td>The maximum number of packets to retain when 'enablePacketDebug' is true</td><td>20</td><td>3.1.3</td></tr><tr><td>slowQueryThresholdMillis</td><td>If 'logSlowQueries' is enabled, how long should a query (in ms) before it is logged as 'slow'?</td><td>2000</td><td>3.1.2</td></tr><tr><td>useUsageAdvisor</td><td>Should the driver issue 'usage' warnings advising proper and efficient usage of JDBC and MySQL Connector/J to the log (true/false, defaults to 'false')?</td><td>false</td><td>3.1.1</td></tr><tr><td>autoGenerateTestcaseScript</td><td>Should the driver dump the SQL it is executing, including server-side prepared statements to STDERR?</td><td>false</td><td>3.1.9</td></tr><tr><td>dumpMetadataOnColumnNotFound</td><td>Should the driver dump the field-level metadata of a result set into the exception message when ResultSet.findColumn() fails?</td><td>false</td><td>3.1.13</td></tr><tr><td>dumpQueriesOnException</td><td>Should the driver dump the contents of the query sent to the server in the message for SQLExceptions?</td><td>false</td><td>3.1.3</td></tr><tr><td>enablePacketDebug</td><td>When enabled, a ring-buffer of 'packetDebugBufferSize' packets will be kept, and dumped when exceptions are thrown in key areas in the driver's code</td><td>false</td><td>3.1.3</td></tr><tr><td>explainSlowQueries</td><td>If 'logSlowQueries' is enabled, should the driver automatically issue an 'EXPLAIN' on the server and send the results to the configured log at a WARN level?</td><td>false</td><td>3.1.2</td></tr><tr><td>logSlowQueries</td><td>Should queries that take longer than 'slowQueryThresholdMillis' be logged?</td><td>false</td><td>3.1.2</td></tr><tr><td>traceProtocol</td><td>Should trace-level network protocol be logged?</td><td>false</td><td>3.1.2</td></tr></tbody></table></div><p>
-   </p><p><b>Miscellaneous. </b>
-      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td>
-                     <span class="bold"><strong>Property Name</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Definition</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Default Value</strong></span>
-                  </td><td>
-                     <span class="bold"><strong>Since Version</strong></span>
-                  </td></tr><tr><td>useUnicode</td><td>Should the driver use Unicode character encodings when handling strings? Should only be used when the driver can't determine the character set mapping, or you are trying to 'force' the driver to use a character set that MySQL either doesn't natively support (such as UTF-8), true/false, defaults to 'true'</td><td>true</td><td>1.1g</td></tr><tr><td>characterEncoding</td><td>If 'useUnicode' is set to true, what character encoding should the driver use when dealing with strings? (defaults is to 'autodetect')</td><td>
-                  </td><td>1.1g</td></tr><tr><td>characterSetResults</td><td>Character set to tell the server to return results as.</td><td>
-                  </td><td>3.0.13</td></tr><tr><td>connectionCollation</td><td>If set, tells the server to use this collation via 'set collation_connection'</td><td>
-                  </td><td>3.0.13</td></tr><tr><td>sessionVariables</td><td>A comma-separated list of name/value pairs to be sent as SET SESSION ... to the server when the driver connects.</td><td>
-                  </td><td>3.1.8</td></tr><tr><td>allowNanAndInf</td><td>Should the driver allow NaN or +/- INF values in PreparedStatement.setDouble()?</td><td>false</td><td>3.1.5</td></tr><tr><td>autoClosePStmtStreams</td><td>Should the driver automatically call .close() on streams/readers passed as arguments via set*() methods?</td><td>false</td><td>3.1.12</td></tr><tr><td>autoDeserialize</td><td>Should the driver automatically detect and de-serialize objects stored in BLOB fields?</td><td>false</td><td>3.1.5</td></tr><tr><td>capitalizeTypeNames</td><td>Capitalize type names in DatabaseMetaData? (usually only useful when using WebObjects, true/false, defaults to 'false')</td><td>false</td><td>2.0.7</td></tr><tr><td>clobCharacterEncoding</td><td>The character encoding to use for sending and retrieving TEXT, MEDIUMTEXT and LONGTEXT values instead of the configured connection characterEncoding</td><td>
-                  </td><td>5.0.0</td></tr><tr><td>clobberStreamingResults</td><td>This will cause a 'streaming' ResultSet to be automatically closed, and any outstanding data still streaming from the server to be discarded if another query is executed before all the data has been read from the server.</td><td>false</td><td>3.0.9</td></tr><tr><td>continueBatchOnError</td><td>Should the driver continue processing batch commands if one statement fails. The JDBC spec allows either way (defaults to 'true').</td><td>true</td><td>3.0.3</td></tr><tr><td>createDatabaseIfNotExist</td><td>Creates the database given in the URL if it doesn't yet exist. Assumes the configured user has permissions to create databases.</td><td>false</td><td>3.1.9</td></tr><tr><td>emptyStringsConvertToZero</td><td>Should the driver allow conversions from empty string fields to numeric values of '0'?</td><td>true</td><td>3.1.8</td></tr><tr><td>emulateLocators</td><td>N/A</td><td>false</td><td>3.1.0</td></tr><tr><td>emulateUnsupportedPstmts</td><td>Should the driver detect prepared statements that are not supported by the server, and replace them with client-side emulated versions?</td><td>true</td><td>3.1.7</td></tr><tr><td>ignoreNonTxTables</td><td>Ignore non-transactional table warning for rollback? (defaults to 'false').</td><td>false</td><td>3.0.9</td></tr><tr><td>jdbcCompliantTruncation</td><td>Should the driver throw java.sql.DataTruncation exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings(MySQL 4.1.0 and newer)?</td><td>true</td><td>3.1.2</td></tr><tr><td>maxRows</td><td>The maximum number of rows to return (0, the default means return all rows).</td><td>-1</td><td>all versions</td></tr><tr><td>noAccessToProcedureBodies</td><td>When determining procedure parameter types for CallableStatements, and the connected user can't access procedure bodies through "SHOW CREATE PROCEDURE" or select on mysql.proc should the driver instead create basic metadata (all parameters reported as INOUT VARCHARs) instead of throwing an exception?</td><td>false</td><td>5.0.3</td></tr><tr><td>noDatetimeStringSync</td><td>Don't ensure that ResultSet.getDatetimeType().toString().equals(ResultSet.getString())</td><td>false</td><td>3.1.7</td></tr><tr><td>noTimezoneConversionForTimeType</td><td>Don't convert TIME values using the server timezone if 'useTimezone'='true'</td><td>false</td><td>5.0.0</td></tr><tr><td>nullCatalogMeansCurrent</td><td>When DatabaseMetadataMethods ask for a 'catalog' parameter, does the value null mean use the current catalog? (this is not JDBC-compliant, but follows legacy behavior from earlier versions of the driver)</td><td>true</td><td>3.1.8</td></tr><tr><td>nullNamePatternMatchesAll</td><td>Should DatabaseMetaData methods that accept *pattern parameters treat null the same as '%' (this is not JDBC-compliant, however older versions of the driver accepted this departure from the specification)</td><td>true</td><td>3.1.8</td></tr><tr><td>overrideSupportsIntegrityEnhancementFacility</td><td>Should the driver return "true" for DatabaseMetaData.supportsIntegrityEnhancementFacility() even if the database doesn't support it to workaround applications that require this method to return "true" to signal support of foreign keys, even though the SQL specification states that this facility contains much more than just foreign key support (one such application being OpenOffice)?</td><td>false</td><td>3.1.12</td></tr><tr><td>pedantic</td><td>Follow the JDBC spec to the letter.</td><td>false</td><td>3.0.0</td></tr><tr><td>pinGlobalTxToPhysicalConnection</td><td>When using XAConnections, should the driver ensure that operations on a given XID are always routed to the same physical connection? This allows the XAConnection to support "XA START ... JOIN" after "XA END" has been called</td><td>false</td><td>5.0.1</td></tr><tr><td>processEscapeCodesForPrepStmts</td><td>Should the driver process escape codes in queries that are prepared?</td><td>true</td><td>3.1.12</td></tr><tr><td>relaxAutoCommit</td><td>If the version of MySQL the driver connects to does not support transactions, still allow calls to commit(), rollback() and setAutoCommit() (true/false, defaults to 'false')?</td><td>false</td><td>2.0.13</td></tr><tr><td>retainStatementAfterResultSetClose</td><td>Should the driver retain the Statement reference in a ResultSet after ResultSet.close() has been called. This is not JDBC-compliant after JDBC-4.0.</td><td>false</td><td>3.1.11</td></tr><tr><td>rollbackOnPooledClose</td><td>Should the driver issue a rollback() when the logical connection in a pool is closed?</td><td>true</td><td>3.0.15</td></tr><tr><td>runningCTS13</td><td>Enables workarounds for bugs in Sun's JDBC compliance testsuite version 1.3</td><td>false</td><td>3.1.7</td></tr><tr><td>serverTimezone</td><td>Override detection/mapping of timezone. Used when timezone from server doesn't map to Java timezone</td><td>
-                  </td><td>3.0.2</td></tr><tr><td>strictFloatingPoint</td><td>Used only in older versions of compliance test</td><td>false</td><td>3.0.0</td></tr><tr><td>strictUpdates</td><td>Should the driver do strict checking (all primary keys selected) of updatable result sets (true, false, defaults to 'true')?</td><td>true</td><td>3.0.4</td></tr><tr><td>tinyInt1isBit</td><td>Should the driver treat the datatype TINYINT(1) as the BIT type (because the server silently converts BIT -&gt; TINYINT(1) when creating tables)?</td><td>true</td><td>3.0.16</td></tr><tr><td>transformedBitIsBoolean</td><td>If the driver converts TINYINT(1) to a different type, should it use BOOLEAN instead of BIT for future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT type?</td><td>false</td><td>3.1.9</td></tr><tr><td>ultraDevHack</td><td>Create PreparedStatements for prepareCall() when required, because UltraDev is broken and issues a prepareCall() for _all_ statements? (true/false, defaults to 'false')</td><td>false</td><td>2.0.3</td></tr><tr><td>useGmtMillisForDatetimes</td><td>Convert between session timezone and GMT before creating Date and Timestamp instances (value of "false" is legacy behavior, "true" leads to more JDBC-compliant behavior.</td><td>false</td><td>3.1.12</td></tr><tr><td>useHostsInPrivileges</td><td>Add '@hostname' to users in DatabaseMetaData.getColumn/TablePrivileges() (true/false), defaults to 'true'.</td><td>true</td><td>3.0.2</td></tr><tr><td>useInformationSchema</td><td>When connected to MySQL-5.0.7 or newer, should the driver use the INFORMATION_SCHEMA to derive information used by DatabaseMetaData?</td><td>false</td><td>5.0.0</td></tr><tr><td>useJDBCCompliantTimezoneShift</td><td>Should the driver use JDBC-compliant rules when converting TIME/TIMESTAMP/DATETIME values' timezone information for those JDBC arguments which take a java.util.Calendar argument? (Notice that this option is exclusive of the "useTimezone=true" configuration option.)</td><td>false</td><td>5.0.0</td></tr><tr><td>useOldAliasMetadataBehavior</td><td>Should the driver use the legacy behavior for "AS" clauses on columns and tables, and only return aliases (if any) for ResultSetMetaData.getColumnName() or ResultSetMetaData.getTableName() rather than the original column/table name?</td><td>true</td><td>5.0.4</td></tr><tr><td>useOldUTF8Behavior</td><td>Use the UTF-8 behavior the driver did when communicating with 4.0 and older servers</td><td>false</td><td>3.1.6</td></tr><tr><td>useOnlyServerErrorMessages</td><td>Don't prepend 'standard' SQLState error messages to error messages returned by the server.</td><td>true</td><td>3.0.15</td></tr><tr><td>useServerPrepStmts</td><td>Use server-side prepared statements if the server supports them? (defaults to 'true').</td><td>true</td><td>3.1.0</td></tr><tr><td>useSqlStateCodes</td><td>Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes (true/false), default is 'true'</td><td>true</td><td>3.1.3</td></tr><tr><td>useStreamLengthsInPrepStmts</td><td>Honor stream length parameter in PreparedStatement/ResultSet.setXXXStream() method calls (true/false, defaults to 'true')?</td><td>true</td><td>3.0.2</td></tr><tr><td>useTimezone</td><td>Convert time/date types between client and server timezones (true/false, defaults to 'false')?</td><td>false</td><td>3.0.2</td></tr><tr><td>useUnbufferedInput</td><td>Don't use BufferedInputStream for reading data from the server</td><td>true</td><td>3.0.11</td></tr><tr><td>yearIsDateType</td><td>Should the JDBC driver treat the MySQL type "YEAR" as a java.sql.Date, or as a SHORT?</td><td>true</td><td>3.1.9</td></tr><tr><td>zeroDateTimeBehavior</td><td>What should happen when the driver encounters DATETIME values that are composed entirely of zeroes (used by MySQL to represent invalid dates)? Valid values are 'exception', 'round' and 'convertToNull'.</td><td>exception</td><td>3.1.4</td></tr></tbody></table></div><p>
-   </p><p>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>user</td><td>The user to connect as</td><td> </td><td>all versions</td></tr><tr><td>password</td><td>The password to use when connecting</td><td> </td><td>all versions</td></tr><tr><td>socketFactory</td><td>The name of the class that the driver should use for creating socket
+                connections to the server. This class must implement the
+                interface 'com.mysql.jdbc.SocketFactory' and have public
+                no-args constructor.</td><td>com.mysql.jdbc.StandardSocketFactory</td><td>3.0.3</td></tr><tr><td>connectTimeout</td><td>Timeout for socket connect (in milliseconds), with 0 being no timeout.
+                Only works on JDK-1.4 or newer. Defaults to '0'.</td><td>0</td><td>3.0.1</td></tr><tr><td>socketTimeout</td><td>Timeout on network socket operations (0, the default means no timeout).</td><td>0</td><td>3.0.1</td></tr><tr><td>connectionLifecycleInterceptors</td><td>A comma-delimited list of classes that implement
+                "com.mysql.jdbc.ConnectionLifecycleInterceptor" that
+                should notified of connection lifecycle events
+                (creation, destruction, commit, rollback, setCatalog and
+                setAutoCommit) and potentially alter the execution of
+                these commands. ConnectionLifecycleInterceptors are
+                "stackable", more than one interceptor may be specified
+                via the configuration property as a comma-delimited
+                list, with the interceptors executed in order from left
+                to right.</td><td> </td><td>5.1.4</td></tr><tr><td>useConfigs</td><td>Load the comma-delimited list of configuration properties before parsing
+                the URL or applying user-specified properties. These
+                configurations are explained in the 'Configurations' of
+                the documentation.</td><td> </td><td>3.1.5</td></tr><tr><td>interactiveClient</td><td>Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout
+                connections based on INTERACTIVE_TIMEOUT instead of
+                WAIT_TIMEOUT</td><td>false</td><td>3.1.0</td></tr><tr><td>localSocketAddress</td><td>Hostname or IP address given to explicitly configure the interface that
+                the driver will bind the client side of the TCP/IP
+                connection to when connecting.</td><td> </td><td>5.0.5</td></tr><tr><td>propertiesTransform</td><td>An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that
+                the driver will use to modify URL properties passed to
+                the driver before attempting a connection</td><td> </td><td>3.1.4</td></tr><tr><td>useCompression</td><td>Use zlib compression when communicating with the server (true/false)?
+                Defaults to 'false'.</td><td>false</td><td>3.0.17</td></tr></tbody></table></div><p>
+    </p><p><b>Networking. </b>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>tcpKeepAlive</td><td>If connecting using TCP/IP, should the driver set SO_KEEPALIVE?</td><td>true</td><td>5.0.7</td></tr><tr><td>tcpNoDelay</td><td>If connecting using TCP/IP, should the driver set SO_TCP_NODELAY
+                (disabling the Nagle Algorithm)?</td><td>true</td><td>5.0.7</td></tr><tr><td>tcpRcvBuf</td><td>If connecting using TCP/IP, should the driver set SO_RCV_BUF to the
+                given value? The default value of '0', means use the
+                platform default value for this property)</td><td>0</td><td>5.0.7</td></tr><tr><td>tcpSndBuf</td><td>If connecting using TCP/IP, shuold the driver set SO_SND_BUF to the
+                given value? The default value of '0', means use the
+                platform default value for this property)</td><td>0</td><td>5.0.7</td></tr><tr><td>tcpTrafficClass</td><td>If connecting using TCP/IP, should the driver set traffic class or
+                type-of-service fields ?See the documentation for
+                java.net.Socket.setTrafficClass() for more information.</td><td>0</td><td>5.0.7</td></tr></tbody></table></div><p>
+    </p><p><b>High Availability and Clustering. </b>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>autoReconnect</td><td>Should the driver try to re-establish stale and/or dead connections? If
+                enabled the driver will throw an exception for a queries
+                issued on a stale or dead connection, which belong to
+                the current transaction, but will attempt reconnect
+                before the next query issued on the connection in a new
+                transaction. The use of this feature is not recommended,
+                because it has side effects related to session state and
+                data consistency when applications don't handle
+                SQLExceptions properly, and is only designed to be used
+                when you are unable to configure your application to
+                handle SQLExceptions resulting from dead and stale
+                connections properly. Alternatively, investigate setting
+                the MySQL server variable "wait_timeout" to some high
+                value rather than the default of 8 hours.</td><td>false</td><td>1.1</td></tr><tr><td>autoReconnectForPools</td><td>Use a reconnection strategy appropriate for connection pools (defaults
+                to 'false')</td><td>false</td><td>3.1.3</td></tr><tr><td>failOverReadOnly</td><td>When failing over in autoReconnect mode, should the connection be set to
+                'read-only'?</td><td>true</td><td>3.0.12</td></tr><tr><td>maxReconnects</td><td>Maximum number of reconnects to attempt if autoReconnect is true,
+                default is '3'.</td><td>3</td><td>1.1</td></tr><tr><td>reconnectAtTxEnd</td><td>If autoReconnect is set to true, should the driver attempt reconnections
+                at the end of every transaction?</td><td>false</td><td>3.0.10</td></tr><tr><td>initialTimeout</td><td>If autoReconnect is enabled, the initial time to wait between re-connect
+                attempts (in seconds, defaults to '2').</td><td>2</td><td>1.1</td></tr><tr><td>roundRobinLoadBalance</td><td>When autoReconnect is enabled, and failoverReadonly is false, should we
+                pick hosts to connect to on a round-robin basis?</td><td>false</td><td>3.1.2</td></tr><tr><td>queriesBeforeRetryMaster</td><td>Number of queries to issue before falling back to master when failed
+                over (when using multi-host failover). Whichever
+                condition is met first, 'queriesBeforeRetryMaster' or
+                'secondsBeforeRetryMaster' will cause an attempt to be
+                made to reconnect to the master. Defaults to 50.</td><td>50</td><td>3.0.2</td></tr><tr><td>secondsBeforeRetryMaster</td><td>How long should the driver wait, when failed over, before attempting</td><td>30</td><td>3.0.2</td></tr><tr><td>resourceId</td><td>A globally unique name that identifies the resource that this datasource
+                or connection is connected to, used for
+                XAResource.isSameRM() when the driver can't determine
+                this value based on hostnames used in the URL</td><td> </td><td>5.0.1</td></tr></tbody></table></div><p>
+    </p><p><b>Security. </b>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>allowMultiQueries</td><td>Allow the use of ';' to delimit multiple queries during one statement
+                (true/false), defaults to 'false'</td><td>false</td><td>3.1.1</td></tr><tr><td>useSSL</td><td>Use SSL when communicating with the server (true/false), defaults to
+                'false'</td><td>false</td><td>3.0.2</td></tr><tr><td>requireSSL</td><td>Require SSL connection if useSSL=true? (defaults to 'false').</td><td>false</td><td>3.1.0</td></tr><tr><td>allowLoadLocalInfile</td><td>Should the driver allow use of 'LOAD DATA LOCAL INFILE...' (defaults to
+                'true').</td><td>true</td><td>3.0.3</td></tr><tr><td>allowUrlInLocalInfile</td><td>Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements?</td><td>false</td><td>3.1.4</td></tr><tr><td>clientCertificateKeyStorePassword</td><td>Password for the client certificates KeyStore</td><td> </td><td>5.1.0</td></tr><tr><td>clientCertificateKeyStoreType</td><td>KeyStore type for client certificates (NULL or empty means use default,
+                standard keystore types supported by the JVM are "JKS"
+                and "PKCS12", your environment may have more available
+                depending on what security products are installed and
+                available to the JVM.</td><td> </td><td>5.1.0</td></tr><tr><td>clientCertificateKeyStoreUrl</td><td>URL to the client certificate KeyStore (if not specified, use defaults)</td><td> </td><td>5.1.0</td></tr><tr><td>trustCertificateKeyStorePassword</td><td>Password for the trusted root certificates KeyStore</td><td> </td><td>5.1.0</td></tr><tr><td>trustCertificateKeyStoreType</td><td>KeyStore type for trusted root certificates (NULL or empty means use
+                default, standard keystore types supported by the JVM
+                are "JKS" and "PKCS12", your environment may have more
+                available depending on what security products are
+                installed and available to the JVM.</td><td> </td><td>5.1.0</td></tr><tr><td>trustCertificateKeyStoreUrl</td><td>URL to the trusted root certificate KeyStore (if not specified, use
+                defaults)</td><td> </td><td>5.1.0</td></tr><tr><td>paranoid</td><td>Take measures to prevent exposure sensitive information in error
+                messages and clear data structures holding sensitive
+                data when possible? (defaults to 'false')</td><td>false</td><td>3.0.1</td></tr></tbody></table></div><p>
+    </p><p><b>Performance Extensions. </b>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>callableStmtCacheSize</td><td>If 'cacheCallableStmts' is enabled, how many callable statements should
+                be cached?</td><td>100</td><td>3.1.2</td></tr><tr><td>metadataCacheSize</td><td>The number of queries to cache ResultSetMetadata for if
+                cacheResultSetMetaData is set to 'true' (default 50)</td><td>50</td><td>3.1.1</td></tr><tr><td>prepStmtCacheSize</td><td>If prepared statement caching is enabled, how many prepared statements
+                should be cached?</td><td>25</td><td>3.0.10</td></tr><tr><td>prepStmtCacheSqlLimit</td><td>If prepared statement caching is enabled, what's the largest SQL the
+                driver will cache the parsing for?</td><td>256</td><td>3.0.10</td></tr><tr><td>alwaysSendSetIsolation</td><td>Should the driver always communicate with the database when
+                Connection.setTransactionIsolation() is called? If set
+                to false, the driver will only communicate with the
+                database when the requested transaction isolation is
+                different than the whichever is newer, the last value
+                that was set via Connection.setTransactionIsolation(),
+                or the value that was read from the server when the
+                connection was established.</td><td>true</td><td>3.1.7</td></tr><tr><td>maintainTimeStats</td><td>Should the driver maintain various internal timers to enable idle time
+                calculations as well as more verbose error messages when
+                the connection to the server fails? Setting this
+                property to false removes at least two calls to
+                System.getCurrentTimeMillis() per query.</td><td>true</td><td>3.1.9</td></tr><tr><td>useCursorFetch</td><td>If connected to MySQL &gt; 5.0.2, and setFetchSize() &gt; 0 on a
+                statement, should that statement use cursor-based
+                fetching to retrieve rows?</td><td>false</td><td>5.0.0</td></tr><tr><td>blobSendChunkSize</td><td>Chunk to use when sending BLOB/CLOBs via ServerPreparedStatements</td><td>1048576</td><td>3.1.9</td></tr><tr><td>cacheCallableStmts</td><td>Should the driver cache the parsing stage of CallableStatements</td><td>false</td><td>3.1.2</td></tr><tr><td>cachePrepStmts</td><td>Should the driver cache the parsing stage of PreparedStatements of
+                client-side prepared statements, the "check" for
+                suitability of server-side prepared and server-side
+                prepared statements themselves?</td><td>false</td><td>3.0.10</td></tr><tr><td>cacheResultSetMetadata</td><td>Should the driver cache ResultSetMetaData for Statements and
+                PreparedStatements? (Req. JDK-1.4+, true/false, default
+                'false')</td><td>false</td><td>3.1.1</td></tr><tr><td>cacheServerConfiguration</td><td>Should the driver cache the results of 'SHOW VARIABLES' and 'SHOW
+                COLLATION' on a per-URL basis?</td><td>false</td><td>3.1.5</td></tr><tr><td>defaultFetchSize</td><td>The driver will call setFetchSize(n) with this value on all
+                newly-created Statements</td><td>0</td><td>3.1.9</td></tr><tr><td>dontTrackOpenResources</td><td>The JDBC specification requires the driver to automatically track and
+                close resources, however if your application doesn't do
+                a good job of explicitly calling close() on statements
+                or result sets, this can cause memory leakage. Setting
+                this property to true relaxes this constraint, and can
+                be more memory efficient for some applications.</td><td>false</td><td>3.1.7</td></tr><tr><td>dynamicCalendars</td><td>Should the driver retrieve the default calendar when required, or cache
+                it per connection/session?</td><td>false</td><td>3.1.5</td></tr><tr><td>elideSetAutoCommits</td><td>If using MySQL-4.1 or newer, should the driver only issue 'set
+                autocommit=n' queries when the server's state doesn't
+                match the requested state by
+                Connection.setAutoCommit(boolean)?</td><td>false</td><td>3.1.3</td></tr><tr><td>enableQueryTimeouts</td><td>When enabled, query timeouts set via Statement.setQueryTimeout() use a
+                shared java.util.Timer instance for scheduling. Even if
+                the timeout doesn't expire before the query is
+                processed, there will be memory used by the TimerTask
+                for the given timeout which won't be reclaimed until the
+                time the timeout would have expired if it hadn't been
+                cancelled by the driver. High-load environments might
+                want to consider disabling this functionality.</td><td>true</td><td>5.0.6</td></tr><tr><td>holdResultsOpenOverStatementClose</td><td>Should the driver close result sets on Statement.close() as required by
+                the JDBC specification?</td><td>false</td><td>3.1.7</td></tr><tr><td>largeRowSizeThreshold</td><td>What size result set row should the JDBC driver consider "large", and
+                thus use a more memory-efficient way of representing the
+                row internally?</td><td>2048</td><td>5.1.1</td></tr><tr><td>loadBalanceStrategy</td><td>If using a load-balanced connection to connect to SQL nodes in a MySQL
+                Cluster/NDB configuration (by using the URL prefix
+                "jdbc:mysql:loadbalance://"), which load balancing
+                algorithm should the driver use: (1) "random" - the
+                driver will pick a random host for each request. This
+                tends to work better than round-robin, as the randomness
+                will somewhat account for spreading loads where requests
+                vary in response time, while round-robin can sometimes
+                lead to overloaded nodes if there are variations in
+                response times across the workload. (2)
+                "bestResponseTime" - the driver will route the request
+                to the host that had the best response time for the
+                previous transaction.</td><td>random</td><td>5.0.6</td></tr><tr><td>locatorFetchBufferSize</td><td>If 'emulateLocators' is configured to 'true', what size buffer should be
+                used when fetching BLOB data for getBinaryInputStream?</td><td>1048576</td><td>3.2.1</td></tr><tr><td>rewriteBatchedStatements</td><td>Should the driver use multiqueries (irregardless of the setting of
+                "allowMultiQueries") as well as rewriting of prepared
+                statements for INSERT into multi-value inserts when
+                executeBatch() is called? Notice that this has the
+                potential for SQL injection if using plain
+                java.sql.Statements and your code doesn't sanitize input
+                correctly. Notice that for prepared statements,
+                server-side prepared statements can not currently take
+                advantage of this rewrite option, and that if you don't
+                specify stream lengths when using
+                PreparedStatement.set*Stream(), the driver won't be able
+                to determine the optimum number of parameters per batch
+                and you might receive an error from the driver that the
+                resultant packet is too large.
+                Statement.getGeneratedKeys() for these rewritten
+                statements only works when the entire batch includes
+                INSERT statements.</td><td>false</td><td>3.1.13</td></tr><tr><td>useDirectRowUnpack</td><td>Use newer result set row unpacking code that skips a copy from network
+                buffers to a MySQL packet instance and instead reads
+                directly into the result set row data buffers.</td><td>true</td><td>5.1.1</td></tr><tr><td>useDynamicCharsetInfo</td><td>Should the driver use a per-connection cache of character set
+                information queried from the server when necessary, or
+                use a built-in static mapping that is more efficient,
+                but isn't aware of custom character sets or character
+                sets implemented after the release of the JDBC driver?</td><td>true</td><td>5.0.6</td></tr><tr><td>useFastDateParsing</td><td>Use internal String-&gt;Date/Time/Timestamp conversion routines to avoid
+                excessive object creation?</td><td>true</td><td>5.0.5</td></tr><tr><td>useFastIntParsing</td><td>Use internal String-&gt;Integer conversion routines to avoid excessive
+                object creation?</td><td>true</td><td>3.1.4</td></tr><tr><td>useJvmCharsetConverters</td><td>Always use the character encoding routines built into the JVM, rather
+                than using lookup tables for single-byte character sets?</td><td>false</td><td>5.0.1</td></tr><tr><td>useLocalSessionState</td><td>Should the driver refer to the internal values of autocommit and
+                transaction isolation that are set by
+                Connection.setAutoCommit() and
+                Connection.setTransactionIsolation() and transaction
+                state as maintained by the protocol, rather than
+                querying the database or blindly sending commands to the
+                database for commit() or rollback() method calls?</td><td>false</td><td>3.1.7</td></tr><tr><td>useReadAheadInput</td><td>Use newer, optimized non-blocking, buffered input stream when reading
+                from the server?</td><td>true</td><td>3.1.5</td></tr></tbody></table></div><p>
+    </p><p><b>Debugging/Profiling. </b>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>logger</td><td>The name of a class that implements "com.mysql.jdbc.log.Log" that will
+                be used to log messages to. (default is
+                "com.mysql.jdbc.log.StandardLogger", which logs to
+                STDERR)</td><td>com.mysql.jdbc.log.StandardLogger</td><td>3.1.1</td></tr><tr><td>gatherPerfMetrics</td><td>Should the driver gather performance metrics, and report them via the
+                configured logger every 'reportMetricsIntervalMillis'
+                milliseconds?</td><td>false</td><td>3.1.2</td></tr><tr><td>profileSQL</td><td>Trace queries and their execution/fetch times to the configured logger
+                (true/false) defaults to 'false'</td><td>false</td><td>3.1.0</td></tr><tr><td>profileSql</td><td>Deprecated, use 'profileSQL' instead. Trace queries and their
+                execution/fetch times on STDERR (true/false) defaults to
+                'false'</td><td> </td><td>2.0.14</td></tr><tr><td>reportMetricsIntervalMillis</td><td>If 'gatherPerfMetrics' is enabled, how often should they be logged (in
+                ms)?</td><td>30000</td><td>3.1.2</td></tr><tr><td>maxQuerySizeToLog</td><td>Controls the maximum length/size of a query that will get logged when
+                profiling or tracing</td><td>2048</td><td>3.1.3</td></tr><tr><td>packetDebugBufferSize</td><td>The maximum number of packets to retain when 'enablePacketDebug' is true</td><td>20</td><td>3.1.3</td></tr><tr><td>slowQueryThresholdMillis</td><td>If 'logSlowQueries' is enabled, how long should a query (in ms) before
+                it is logged as 'slow'?</td><td>2000</td><td>3.1.2</td></tr><tr><td>slowQueryThresholdNanos</td><td>If 'useNanosForElapsedTime' is set to true, and this property is set to
+                a non-zero value, the driver will use this threshold (in
+                nanosecond units) to determine if a query was slow.</td><td>0</td><td>5.0.7</td></tr><tr><td>useUsageAdvisor</td><td>Should the driver issue 'usage' warnings advising proper and efficient
+                usage of JDBC and MySQL Connector/J to the log
+                (true/false, defaults to 'false')?</td><td>false</td><td>3.1.1</td></tr><tr><td>autoGenerateTestcaseScript</td><td>Should the driver dump the SQL it is executing, including server-side
+                prepared statements to STDERR?</td><td>false</td><td>3.1.9</td></tr><tr><td>autoSlowLog</td><td>Instead of using slowQueryThreshold* to determine if a query is slow
+                enough to be logged, maintain statistics that allow the
+                driver to determine queries that are outside the 99th
+                percentile?</td><td>true</td><td>5.1.4</td></tr><tr><td>clientInfoProvider</td><td>The name of a class that implements the
+                com.mysql.jdbc.JDBC4ClientInfoProvider interface in
+                order to support JDBC-4.0's
+                Connection.get/setClientInfo() methods</td><td>com.mysql.jdbc.JDBC4CommentClientInfoProvider</td><td>5.1.0</td></tr><tr><td>dumpMetadataOnColumnNotFound</td><td>Should the driver dump the field-level metadata of a result set into the
+                exception message when ResultSet.findColumn() fails?</td><td>false</td><td>3.1.13</td></tr><tr><td>dumpQueriesOnException</td><td>Should the driver dump the contents of the query sent to the server in
+                the message for SQLExceptions?</td><td>false</td><td>3.1.3</td></tr><tr><td>enablePacketDebug</td><td>When enabled, a ring-buffer of 'packetDebugBufferSize' packets will be
+                kept, and dumped when exceptions are thrown in key areas
+                in the driver's code</td><td>false</td><td>3.1.3</td></tr><tr><td>explainSlowQueries</td><td>If 'logSlowQueries' is enabled, should the driver automatically issue an
+                'EXPLAIN' on the server and send the results to the
+                configured log at a WARN level?</td><td>false</td><td>3.1.2</td></tr><tr><td>includeInnodbStatusInDeadlockExceptions</td><td>Include the output of "SHOW ENGINE INNODB STATUS" in exception messages
+                when deadlock exceptions are detected?</td><td>false</td><td>5.0.7</td></tr><tr><td>logSlowQueries</td><td>Should queries that take longer than 'slowQueryThresholdMillis' be
+                logged?</td><td>false</td><td>3.1.2</td></tr><tr><td>logXaCommands</td><td>Should the driver log XA commands sent by MysqlXaConnection to the
+                server, at the DEBUG level of logging?</td><td>false</td><td>5.0.5</td></tr><tr><td>resultSetSizeThreshold</td><td>If the usage advisor is enabled, how many rows should a result set
+                contain before the driver warns that it is suspiciously
+                large?</td><td>100</td><td>5.0.5</td></tr><tr><td>traceProtocol</td><td>Should trace-level network protocol be logged?</td><td>false</td><td>3.1.2</td></tr><tr><td>useNanosForElapsedTime</td><td>For profiling/debugging functionality that measures elapsed time, should
+                the driver try to use nanoseconds resolution if
+                available (JDK &gt;= 1.5)?</td><td>false</td><td>5.0.7</td></tr></tbody></table></div><p>
+    </p><p><b>Miscellaneous. </b>
+      </p><div class="informaltable"><table border="1"><colgroup><col><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong> Property Name </strong></span></td><td><span class="bold"><strong> Definition </strong></span></td><td><span class="bold"><strong> Default Value </strong></span></td><td><span class="bold"><strong> Since Version </strong></span></td></tr><tr><td>useUnicode</td><td>Should the driver use Unicode character encodings when handling strings?
+                Should only be used when the driver can't determine the
+                character set mapping, or you are trying to 'force' the
+                driver to use a character set that MySQL either doesn't
+                natively support (such as UTF-8), true/false, defaults
+                to 'true'</td><td>true</td><td>1.1g</td></tr><tr><td>characterEncoding</td><td>If 'useUnicode' is set to true, what character encoding should the
+                driver use when dealing with strings? (defaults is to
+                'autodetect')</td><td> </td><td>1.1g</td></tr><tr><td>characterSetResults</td><td>Character set to tell the server to return results as.</td><td> </td><td>3.0.13</td></tr><tr><td>connectionCollation</td><td>If set, tells the server to use this collation via 'set
+                collation_connection'</td><td> </td><td>3.0.13</td></tr><tr><td>useBlobToStoreUTF8OutsideBMP</td><td>Tells the driver to treat [MEDIUM/LONG]BLOB columns as [LONG]VARCHAR
+                columns holding text encoded in UTF-8 that has
+                characters outside the BMP (4-byte encodings), which
+                MySQL server can't handle natively.</td><td>false</td><td>5.1.3</td></tr><tr><td>utf8OutsideBmpExcludedColumnNamePattern</td><td>When "useBlobToStoreUTF8OutsideBMP" is set to "true", column names
+                matching the given regex will still be treated as BLOBs
+                unless they match the regex specified for
+                "utf8OutsideBmpIncludedColumnNamePattern". The regex
+                must follow the patterns used for the java.util.regex
+                package.</td><td> </td><td>5.1.3</td></tr><tr><td>utf8OutsideBmpIncludedColumnNamePattern</td><td>Used to specify exclusion rules to
+                "utf8OutsideBmpExcludedColumnNamePattern". The regex
+                must follow the patterns used for the java.util.regex
+                package.</td><td> </td><td>5.1.3</td></tr><tr><td>sessionVariables</td><td>A comma-separated list of name/value pairs to be sent as SET SESSION ...
+                to the server when the driver connects.</td><td> </td><td>3.1.8</td></tr><tr><td>allowNanAndInf</td><td>Should the driver allow NaN or +/- INF values in
+                PreparedStatement.setDouble()?</td><td>false</td><td>3.1.5</td></tr><tr><td>autoClosePStmtStreams</td><td>Should the driver automatically call .close() on streams/readers passed
+                as arguments via set*() methods?</td><td>false</td><td>3.1.12</td></tr><tr><td>autoDeserialize</td><td>Should the driver automatically detect and de-serialize objects stored
+                in BLOB fields?</td><td>false</td><td>3.1.5</td></tr><tr><td>blobsAreStrings</td><td>Should the driver always treat BLOBs as Strings - specifically to work
+                around dubious metadata returned by the server for GROUP
+                BY clauses?</td><td>false</td><td>5.0.8</td></tr><tr><td>capitalizeTypeNames</td><td>Capitalize type names in DatabaseMetaData? (usually only useful when
+                using WebObjects, true/false, defaults to 'false')</td><td>true</td><td>2.0.7</td></tr><tr><td>clobCharacterEncoding</td><td>The character encoding to use for sending and retrieving TEXT,
+                MEDIUMTEXT and LONGTEXT values instead of the configured
+                connection characterEncoding</td><td> </td><td>5.0.0</td></tr><tr><td>clobberStreamingResults</td><td>This will cause a 'streaming' ResultSet to be automatically closed, and
+                any outstanding data still streaming from the server to
+                be discarded if another query is executed before all the
+                data has been read from the server.</td><td>false</td><td>3.0.9</td></tr><tr><td>continueBatchOnError</td><td>Should the driver continue processing batch commands if one statement
+                fails. The JDBC spec allows either way (defaults to
+                'true').</td><td>true</td><td>3.0.3</td></tr><tr><td>createDatabaseIfNotExist</td><td>Creates the database given in the URL if it doesn't yet exist. Assumes
+                the configured user has permissions to create databases.</td><td>false</td><td>3.1.9</td></tr><tr><td>emptyStringsConvertToZero</td><td>Should the driver allow conversions from empty string fields to numeric
+                values of '0'?</td><td>true</td><td>3.1.8</td></tr><tr><td>emulateLocators</td><td>Should the driver emulate java.sql.Blobs with locators? With this
+                feature enabled, the driver will delay loading the
+                actual Blob data until the one of the retrieval methods
+                (getInputStream(), getBytes(), and so forth) on the blob
+                data stream has been accessed. For this to work, you
+                must use a column alias with the value of the column to
+                the actual name of the Blob. The feature also has the
+                following restrictions: The SELECT that created the
+                result set must reference only one table, the table must
+                have a primary key; the SELECT must alias the original
+                blob column name, specified as a string, to an alternate
+                name; the SELECT must cover all columns that make up the
+                primary key.</td><td>false</td><td>3.1.0</td></tr><tr><td>emulateUnsupportedPstmts</td><td>Should the driver detect prepared statements that are not supported by
+                the server, and replace them with client-side emulated
+                versions?</td><td>true</td><td>3.1.7</td></tr><tr><td>functionsNeverReturnBlobs</td><td>Should the driver always treat data from functions returning BLOBs as
+                Strings - specifically to work around dubious metadata
+                returned by the server for GROUP BY clauses?</td><td>false</td><td>5.0.8</td></tr><tr><td>generateSimpleParameterMetadata</td><td>Should the driver generate simplified parameter metadata for
+                PreparedStatements when no metadata is available either
+                because the server couldn't support preparing the
+                statement, or server-side prepared statements are
+                disabled?</td><td>false</td><td>5.0.5</td></tr><tr><td>ignoreNonTxTables</td><td>Ignore non-transactional table warning for rollback? (defaults to
+                'false').</td><td>false</td><td>3.0.9</td></tr><tr><td>jdbcCompliantTruncation</td><td>Should the driver throw java.sql.DataTruncation exceptions when data is
+                truncated as is required by the JDBC specification when
+                connected to a server that supports warnings (MySQL
+                4.1.0 and newer)? This property has no effect if the
+                server sql-mode includes STRICT_TRANS_TABLES.</td><td>true</td><td>3.1.2</td></tr><tr><td>maxRows</td><td>The maximum number of rows to return (0, the default means return all
+                rows).</td><td>-1</td><td>all versions</td></tr><tr><td>netTimeoutForStreamingResults</td><td>What value should the driver automatically set the server setting
+                'net_write_timeout' to when the streaming result sets
+                feature is in use? (value has unit of seconds, the value
+                '0' means the driver will not try and adjust this value)</td><td>600</td><td>5.1.0</td></tr><tr><td>noAccessToProcedureBodies</td><td>When determining procedure parameter types for CallableStatements, and
+                the connected user can't access procedure bodies through
+                "SHOW CREATE PROCEDURE" or select on mysql.proc should
+                the driver instead create basic metadata (all parameters
+                reported as IN VARCHARs, but allowing
+                registerOutParameter() to be called on them anyway)
+                instead of throwing an exception?</td><td>false</td><td>5.0.3</td></tr><tr><td>noDatetimeStringSync</td><td>Don't ensure that
+                ResultSet.getDatetimeType().toString().equals(ResultSet.getString())</td><td>false</td><td>3.1.7</td></tr><tr><td>noTimezoneConversionForTimeType</td><td>Don't convert TIME values using the server timezone if
+                'useTimezone'='true'</td><td>false</td><td>5.0.0</td></tr><tr><td>nullCatalogMeansCurrent</td><td>When DatabaseMetadataMethods ask for a 'catalog' parameter, does the
+                value null mean use the current catalog? (this is not
+                JDBC-compliant, but follows legacy behavior from earlier
+                versions of the driver)</td><td>true</td><td>3.1.8</td></tr><tr><td>nullNamePatternMatchesAll</td><td>Should DatabaseMetaData methods that accept *pattern parameters treat
+                null the same as '%' (this is not JDBC-compliant,
+                however older versions of the driver accepted this
+                departure from the specification)</td><td>true</td><td>3.1.8</td></tr><tr><td>overrideSupportsIntegrityEnhancementFacility</td><td>Should the driver return "true" for
+                DatabaseMetaData.supportsIntegrityEnhancementFacility()
+                even if the database doesn't support it to workaround
+                applications that require this method to return "true"
+                to signal support of foreign keys, even though the SQL
+                specification states that this facility contains much
+                more than just foreign key support (one such application
+                being OpenOffice)?</td><td>false</td><td>3.1.12</td></tr><tr><td>padCharsWithSpace</td><td>If a result set column has the CHAR type and the value does not fill the
+                amount of characters specified in the DDL for the
+                column, should the driver pad the remaining characters
+                with space (for ANSI compliance)?</td><td>false</td><td>5.0.6</td></tr><tr><td>pedantic</td><td>Follow the JDBC spec to the letter.</td><td>false</td><td>3.0.0</td></tr><tr><td>pinGlobalTxToPhysicalConnection</td><td>When using XAConnections, should the driver ensure that operations on a
+                given XID are always routed to the same physical
+                connection? This allows the XAConnection to support "XA
+                START ... JOIN" after "XA END" has been called</td><td>false</td><td>5.0.1</td></tr><tr><td>populateInsertRowWithDefaultValues</td><td>When using ResultSets that are CONCUR_UPDATABLE, should the driver
+                pre-populate the "insert" row with default values from
+                the DDL for the table used in the query so those values
+                are immediately available for ResultSet accessors? This
+                functionality requires a call to the database for
+                metadata each time a result set of this type is created.
+                If disabled (the default), the default values will be
+                populated by the an internal call to refreshRow() which
+                pulls back default values and/or values changed by
+                triggers.</td><td>false</td><td>5.0.5</td></tr><tr><td>processEscapeCodesForPrepStmts</td><td>Should the driver process escape codes in queries that are prepared?</td><td>true</td><td>3.1.12</td></tr><tr><td>relaxAutoCommit</td><td>If the version of MySQL the driver connects to does not support
+                transactions, still allow calls to commit(), rollback()
+                and setAutoCommit() (true/false, defaults to 'false')?</td><td>false</td><td>2.0.13</td></tr><tr><td>retainStatementAfterResultSetClose</td><td>Should the driver retain the Statement reference in a ResultSet after
+                ResultSet.close() has been called. This is not
+                JDBC-compliant after JDBC-4.0.</td><td>false</td><td>3.1.11</td></tr><tr><td>rollbackOnPooledClose</td><td>Should the driver issue a rollback() when the logical connection in a
+                pool is closed?</td><td>true</td><td>3.0.15</td></tr><tr><td>runningCTS13</td><td>Enables workarounds for bugs in Sun's JDBC compliance testsuite version
+                1.3</td><td>false</td><td>3.1.7</td></tr><tr><td>serverTimezone</td><td>Override detection/mapping of timezone. Used when timezone from server
+                doesn't map to Java timezone</td><td> </td><td>3.0.2</td></tr><tr><td>statementInterceptors</td><td>A comma-delimited list of classes that implement
+                "com.mysql.jdbc.StatementInterceptor" that should be
+                placed "in between" query execution to influence the
+                results. StatementInterceptors are "chainable", the
+                results returned by the "current" interceptor will be
+                passed on to the next in in the chain, from
+                left-to-right order, as specified in this property.</td><td> </td><td>5.1.1</td></tr><tr><td>strictFloatingPoint</td><td>Used only in older versions of compliance test</td><td>false</td><td>3.0.0</td></tr><tr><td>strictUpdates</td><td>Should the driver do strict checking (all primary keys selected) of
+                updatable result sets (true, false, defaults to 'true')?</td><td>true</td><td>3.0.4</td></tr><tr><td>tinyInt1isBit</td><td>Should the driver treat the datatype TINYINT(1) as the BIT type (because
+                the server silently converts BIT -&gt; TINYINT(1) when
+                creating tables)?</td><td>true</td><td>3.0.16</td></tr><tr><td>transformedBitIsBoolean</td><td>If the driver converts TINYINT(1) to a different type, should it use
+                BOOLEAN instead of BIT for future compatibility with
+                MySQL-5.0, as MySQL-5.0 has a BIT type?</td><td>false</td><td>3.1.9</td></tr><tr><td>treatUtilDateAsTimestamp</td><td>Should the driver treat java.util.Date as a TIMESTAMP for the purposes
+                of PreparedStatement.setObject()?</td><td>true</td><td>5.0.5</td></tr><tr><td>ultraDevHack</td><td>Create PreparedStatements for prepareCall() when required, because
+                UltraDev is broken and issues a prepareCall() for _all_
+                statements? (true/false, defaults to 'false')</td><td>false</td><td>2.0.3</td></tr><tr><td>useGmtMillisForDatetimes</td><td>Convert between session timezone and GMT before creating Date and
+                Timestamp instances (value of "false" is legacy
+                behavior, "true" leads to more JDBC-compliant behavior.</td><td>false</td><td>3.1.12</td></tr><tr><td>useHostsInPrivileges</td><td>Add '@hostname' to users in DatabaseMetaData.getColumn/TablePrivileges()
+                (true/false), defaults to 'true'.</td><td>true</td><td>3.0.2</td></tr><tr><td>useInformationSchema</td><td>When connected to MySQL-5.0.7 or newer, should the driver use the
+                INFORMATION_SCHEMA to derive information used by
+                DatabaseMetaData?</td><td>false</td><td>5.0.0</td></tr><tr><td>useJDBCCompliantTimezoneShift</td><td>Should the driver use JDBC-compliant rules when converting
+                TIME/TIMESTAMP/DATETIME values' timezone information for
+                those JDBC arguments which take a java.util.Calendar
+                argument? (Notice that this option is exclusive of the
+                "useTimezone=true" configuration option.)</td><td>false</td><td>5.0.0</td></tr><tr><td>useOldAliasMetadataBehavior</td><td>Should the driver use the legacy behavior for "AS" clauses on columns
+                and tables, and only return aliases (if any) for
+                ResultSetMetaData.getColumnName() or
+                ResultSetMetaData.getTableName() rather than the
+                original column/table name?</td><td>false</td><td>5.0.4</td></tr><tr><td>useOldUTF8Behavior</td><td>Use the UTF-8 behavior the driver did when communicating with 4.0 and
+                older servers</td><td>false</td><td>3.1.6</td></tr><tr><td>useOnlyServerErrorMessages</td><td>Don't prepend 'standard' SQLState error messages to error messages
+                returned by the server.</td><td>true</td><td>3.0.15</td></tr><tr><td>useSSPSCompatibleTimezoneShift</td><td>If migrating from an environment that was using server-side prepared
+                statements, and the configuration property
+                "useJDBCCompliantTimeZoneShift" set to "true", use
+                compatible behavior when not using server-side prepared
+                statements when sending TIMESTAMP values to the MySQL
+                server.</td><td>false</td><td>5.0.5</td></tr><tr><td>useServerPrepStmts</td><td>Use server-side prepared statements if the server supports them?</td><td>false</td><td>3.1.0</td></tr><tr><td>useSqlStateCodes</td><td>Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes
+                (true/false), default is 'true'</td><td>true</td><td>3.1.3</td></tr><tr><td>useStreamLengthsInPrepStmts</td><td>Honor stream length parameter in
+                PreparedStatement/ResultSet.setXXXStream() method calls
+                (true/false, defaults to 'true')?</td><td>true</td><td>3.0.2</td></tr><tr><td>useTimezone</td><td>Convert time/date types between client and server timezones (true/false,
+                defaults to 'false')?</td><td>false</td><td>3.0.2</td></tr><tr><td>useUnbufferedInput</td><td>Don't use BufferedInputStream for reading data from the server</td><td>true</td><td>3.0.11</td></tr><tr><td>yearIsDateType</td><td>Should the JDBC driver treat the MySQL type "YEAR" as a java.sql.Date,
+                or as a SHORT?</td><td>true</td><td>3.1.9</td></tr><tr><td>zeroDateTimeBehavior</td><td>What should happen when the driver encounters DATETIME values that are
+                composed entirely of zeroes (used by MySQL to represent
+                invalid dates)? Valid values are "exception", "round"
+                and "convertToNull".</td><td>exception</td><td>3.1.4</td></tr></tbody></table></div><p>
+    </p><p>
         Connector/J also supports access to MySQL via named pipes on
         Windows NT/2000/XP using the
         <span class="property">NamedPipeSocketFactory</span> as a plugin-socket
@@ -652,8 +980,32 @@
         about how certain implementation decisions may affect how you
         use MySQL Connector/J.
       </p><div class="itemizedlist"><ul type="disc"><li><p>
-            Blob
+            <span class="bold"><strong>Blob</strong></span>
           </p><p>
+            Starting with Connector/J version 3.1.0, you can emulate
+            Blobs with locators by adding the property
+            'emulateLocators=true' to your JDBC URL. Using this method,
+            the driver will delay loading the actual Blob data until you
+            retrieve the other data and then use retrieval methods
+            (<code class="literal">getInputStream()</code>,
+            <code class="literal">getBytes()</code>, and so forth) on the blob
+            data stream.
+          </p><p>
+            For this to work, you must use a column alias with the value
+            of the column to the actual name of the Blob, for example:
+          </p><pre class="programlisting">SELECT id, 'data' as blob_data from blobtable</pre><p>
+            For this to work, you must also follow follow these rules:
+          </p><div class="itemizedlist"><ul type="circle"><li><p>
+                The <code class="literal">SELECT</code> must also reference only
+                one table, the table must have a primary key.
+              </p></li><li><p>
+                The <code class="literal">SELECT</code> must alias the original
+                blob column name, specified as a string, to an alternate
+                name.
+              </p></li><li><p>
+                The <code class="literal">SELECT</code> must cover all columns
+                that make up the primary key.
+              </p></li></ul></div><p>
             The Blob implementation does not allow in-place modification
             (they are copies, as reported by the
             <code class="literal">DatabaseMetaData.locatorsUpdateCopies()</code>
@@ -662,23 +1014,16 @@
             <code class="literal">ResultSet.updateBlob()</code> (in the case of
             updatable result sets) methods to save changes back to the
             database.
+          </p><p class="mnmas-kb"><b>MySQL Enterprise</b>
+              MySQL Enterprise subscribers will find more information
+              about type conversion in the Knowledge Base article,
+              <a href="https://kb.mysql.com/view.php?id=4929" target="_top"> Type
+              Conversions Supported by MySQL Connector/J</a>. To
+              subscribe to MySQL Enterprise see
+              <a href="http://www.mysql.com/products/enterprise/advisors.html" target="_top">http://www.mysql.com/products/enterprise/advisors.html</a>.
+            </p></li><li><p>
+            <span class="bold"><strong>CallableStatement</strong></span>
           </p><p>
-            Starting with Connector/J version 3.1.0, you can emulate
-            Blobs with locators by adding the property
-            'emulateLocators=true' to your JDBC URL. You must then use a
-            column alias with the value of the column set to the actual
-            name of the Blob column in the <code class="literal">SELECT</code>
-            that you write to retrieve the Blob. The
-            <code class="literal">SELECT</code> must also reference only one
-            table, the table must have a primary key, and the
-            <code class="literal">SELECT</code> must cover all columns that make
-            up the primary key. The driver will then delay loading the
-            actual Blob data until you retrieve the Blob and call
-            retrieval methods (<code class="literal">getInputStream()</code>,
-            <code class="literal">getBytes()</code>, and so forth) on it.
-          </p></li><li><p>
-            CallableStatement
-          </p><p>
             Starting with Connector/J 3.1.1, stored procedures are
             supported when connecting to MySQL version 5.0 or newer via
             the <code class="classname">CallableStatement</code> interface.
@@ -686,7 +1031,7 @@
             method of <code class="classname">CallableStatement</code> is not
             supported.
           </p></li><li><p>
-            Clob
+            <span class="bold"><strong>Clob</strong></span>
           </p><p>
             The Clob implementation does not allow in-place modification
             (they are copies, as reported by the
@@ -696,7 +1041,7 @@
             save changes back to the database. The JDBC API does not
             have a <code class="literal">ResultSet.updateClob()</code> method.
           </p></li><li><p>
-            Connection
+            <span class="bold"><strong>Connection</strong></span>
           </p><p>
             Unlike older versions of MM.MySQL the
             <code class="literal">isClosed()</code> method does not ping the
@@ -708,7 +1053,7 @@
             <code class="literal">SELECT 1</code>. The driver will throw an
             exception if the connection is no longer valid.
           </p></li><li><p>
-            DatabaseMetaData
+            <span class="bold"><strong>DatabaseMetaData</strong></span>
           </p><p>
             Foreign Key information
             (<code class="literal">getImportedKeys()</code>/<code class="literal">getExportedKeys()</code>
@@ -718,7 +1063,7 @@
             information, so when other storage engines support foreign
             keys, the driver will transparently support them as well.
           </p></li><li><p>
-            PreparedStatement
+            <span class="bold"><strong>PreparedStatement</strong></span>
           </p><p>
             PreparedStatements are implemented by the driver, as MySQL
             does not have a prepared statement feature. Because of this,
@@ -743,9 +1088,10 @@
             <code class="literal">clearParameters()</code> and set all parameters
             again. The reason for this is as follows:
           </p><div class="itemizedlist"><ul type="circle"><li><p>
-                The driver streams the large data out-of-band to the
-                prepared statement on the server side when the parameter
-                is set (before execution of the prepared statement).
+                During both server-side prepared statements and
+                client-side emulation, large data is exchanged only when
+                <code class="literal">PreparedStatement.execute()</code> is
+                called.
               </p></li></ul></div><div class="itemizedlist"><ul type="circle"><li><p>
                 Once that has been done, the stream used to read the
                 data on the client side is closed (as per the JDBC
@@ -769,7 +1115,7 @@
             of the prepared statement again before it can be
             re-executed.
           </p></li><li><p>
-            ResultSet
+            <span class="bold"><strong>ResultSet</strong></span>
           </p><p>
             By default, ResultSets are completely retrieved and stored
             in memory. In most cases this is the most efficient way to
@@ -813,21 +1159,72 @@
             concurrent access to the tables referenced by the statement
             producing the result set.
           </p></li><li><p>
-            ResultSetMetaData
+            <span class="bold"><strong>ResultSetMetaData</strong></span>
           </p><p>
             The <code class="literal">isAutoIncrement()</code> method only works
             when using MySQL servers 4.0 and newer.
           </p></li><li><p>
-            Statement
+            <span class="bold"><strong>Statement</strong></span>
           </p><p>
             When using versions of the JDBC driver earlier than 3.2.1,
             and connected to server versions earlier than 5.0.3, the
-            "setFetchSize()" method has no effect, other than to toggle
-            result set streaming as described above.
+            <code class="literal">setFetchSize()</code> method has no effect,
+            other than to toggle result set streaming as described
+            above.
           </p><p>
+            Connector/J 5.0.0 and later include support for both
+            <code class="literal">Statement.cancel()</code> and
+            <code class="literal">Statement.setQueryTimeout()</code>. Both require
+            MySQL 5.0.0 or newer server, and require a separate
+            connection to issue the <code class="literal">KILL QUERY</code>
+            statement. In the case of
+            <code class="literal">setQueryTimeout()</code>, the implementation
+            creates an additional thread to handle the timeout
+            functionality.
+          </p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+              Failures to cancel the statement for
+              <code class="literal">setQueryTimeout()</code> may manifest
+              themselves as <code class="literal">RuntimeException</code> rather
+              than failing silently, as there is currently no way to
+              unblock the thread that is executing the query being
+              cancelled due to timeout expiration and have it throw the
+              exception instead.
+            </p></div><p>
             MySQL does not support SQL cursors, and the JDBC driver
             doesn't emulate them, so "setCursorName()" has no effect.
-          </p></li></ul></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-type-conversions"></a>1.4.3. Java, JDBC and MySQL Types</h4></div></div></div><p>
+          </p><p>
+            Connector/J 5.1.3 and later include two additional methods:
+          </p><div class="itemizedlist"><ul type="circle"><li><p>
+                <code class="literal">setLocalInfileInputStream()</code> sets an
+                <code class="literal">InputStream</code> instance that will be
+                used to send data to the MySQL server for a
+                <code class="literal">LOAD DATA LOCAL INFILE</code> statement
+                rather than a <code class="literal">FileInputStream</code> or
+                <code class="literal">URLInputStream</code> that represents the
+                path given as an argument to the statement.
+              </p><p>
+                This stream will be read to completion upon execution of
+                a <code class="literal">LOAD DATA LOCAL INFILE</code> statement,
+                and will automatically be closed by the driver, so it
+                needs to be reset before each call to
+                <code class="literal">execute*()</code> that would cause the MySQL
+                server to request data to fulfill the request for
+                <code class="literal">LOAD DATA LOCAL INFILE</code>.
+              </p><p>
+                If this value is set to <code class="literal">NULL</code>, the
+                driver will revert to using a
+                <code class="literal">FileInputStream</code> or
+                <code class="literal">URLInputStream</code> as required.
+              </p></li><li><p>
+                <code class="literal">getLocalInfileInputStream()</code> returns
+                the <code class="literal">InputStream</code> instance that will be
+                used to send data in response to a <code class="literal">LOAD DATA
+                LOCAL INFILE</code> statement.
+              </p><p>
+                This method returns <code class="literal">NULL</code> if no such
+                stream has been set via
+                <code class="literal">setLocalInfileInputStream()</code>.
+              </p></li></ul></div></li></ul></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-type-conversions"></a>1.4.3. Java, JDBC and MySQL Types</h4></div></div></div><p>
         MySQL Connector/J is flexible in the way it handles conversions
         between MySQL data types and Java data types.
       </p><p>
@@ -852,12 +1249,11 @@
                     SMALLINT, MEDIUMINT, INTEGER, BIGINT</code></td><td><code class="literal">java.lang.String, java.lang.Short, java.lang.Integer,
                     java.lang.Long, java.lang.Double,
                     java.math.BigDecimal</code></td></tr><tr><td><code class="literal">DATE, TIME, DATETIME, TIMESTAMP</code></td><td><code class="literal">java.lang.String, java.sql.Date, java.sql.Timestamp</code></td></tr></tbody></table></div><p>
-        </p><p>
-        <span class="bold"><strong>Note:</strong></span> round-off, overflow or
-        loss of precision may occur if you choose a Java numeric data
-        type that has less precision or capacity than the MySQL data
-        type you are converting to/from.
-      </p><p>
+        </p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
+          Round-off, overflow or loss of precision may occur if you
+          choose a Java numeric data type that has less precision or
+          capacity than the MySQL data type you are converting to/from.
+        </p></div><p>
         The <code class="classname">ResultSet.getObject()</code> method uses the
         type conversions between MySQL and Java types, following the
         JDBC specification where appropriate. The value returned by
@@ -867,7 +1263,7 @@
         <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Types.html" target="_top">Java
         2 Platform Types</a>.
       </p><p><b>MySQL Types to Java Types for ResultSet.getObject(). </b>
-          </p><div class="informaltable"><table border="1"><colgroup><col><col></colgroup><tbody><tr><td><span class="bold"><strong>MySQL Type Name</strong></span></td><td><span class="bold"><strong>Return value of
+          </p><div class="informaltable"><table border="1"><colgroup><col><col><col></colgroup><tbody><tr><td><span class="bold"><strong>MySQL Type Name</strong></span></td><td><span class="bold"><strong>Return value of
                     <code class="literal">GetColumnClassName</code></strong></span></td><td><span class="bold"><strong>Returned as Java Class</strong></span></td></tr><tr><td><span class="type">BIT(1)</span> (new in MySQL-5.0)</td><td><span class="type">BIT</span></td><td><code class="classname">java.lang.Boolean</code></td></tr><tr><td><span class="type">BIT( &gt; 1)</span> (new in MySQL-5.0)</td><td><span class="type">BIT</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">TINYINT</span></td><td><span class="type">TINYINT</span></td><td><code class="classname">java.lang.Boolean</code> if the configuration property
                     <code class="literal">tinyInt1isBit</code> is set to
                     <code class="literal">true</code> (the default) and the
@@ -912,7 +1308,7 @@
         automatically detected from the server configuration, or could
         be configured by the user through the
         <em class="parameter"><code>useUnicode</code></em> and
-        "<em class="parameter"><code>characterEncoding</code></em>" properties.
+        <em class="parameter"><code>characterEncoding</code></em> properties.
       </p><p>
         Starting with MySQL Server 4.1, Connector/J supports a single
         character encoding between client and server, and any number of
@@ -926,7 +1322,7 @@
         versions older than 4.1.0 and
         <code class="literal">character_set_server</code> for server versions
         4.1.0 and newer. For more information, see
-        ???.
+        <a href="http://dev.mysql.com/doc/refman/5.0/en/charset-server.html" target="_top">Server Character Set and Collation</a>.
       </p><p>
         To override the automatically-detected encoding on the client
         side, use the <em class="parameter"><code>characterEncoding</code></em> property
@@ -937,12 +1333,12 @@
         Java-style names for MySQL character sets:
       </p><p><b>MySQL to Java Encoding Name Translations. </b>
           </p><div class="informaltable"><table border="1"><colgroup><col><col></colgroup><tbody><tr><td><span class="bold"><strong>MySQL Character Set Name</strong></span></td><td><span class="bold"><strong>Java-Style Character Encoding Name</strong></span></td></tr><tr><td>ascii</td><td>US-ASCII</td></tr><tr><td>big5</td><td>Big5</td></tr><tr><td>gbk</td><td>GBK</td></tr><tr><td>sjis</td><td>SJIS (or Cp932 or MS932 for MySQL Server &lt; 4.1.11)</td></tr><tr><td>cp932</td><td>Cp932 or MS932 (MySQL Server &gt; 4.1.11)</td></tr><tr><td>gb2312</td><td>EUC_CN</td></tr><tr><td>ujis</td><td>EUC_JP</td></tr><tr><td>euckr</td><td>EUC_KR</td></tr><tr><td>latin1</td><td>ISO8859_1</td></tr><tr><td>latin2</td><td>ISO8859_2</td></tr><tr><td>greek</td><td>ISO8859_7</td></tr><tr><td>hebrew</td><td>ISO8859_8</td></tr><tr><td>cp866</td><td>Cp866</td></tr><tr><td>tis620</td><td>TIS620</td></tr><tr><td>cp1250</td><td>Cp1250</td></tr><tr><td>cp1251</td><td>Cp1251</td></tr><tr><td>cp1257</td><td>Cp1257</td></tr><tr><td>macroman</td><td>MacRoman</td></tr><tr><td>macce</td><td>MacCentralEurope</td></tr><tr><td>utf8</td><td>UTF-8</td></tr><tr><td>ucs2</td><td>UnicodeBig</td></tr></tbody></table></div><p>
-        </p><p><b>Warning. </b>
+        </p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Warning</h3><p>
           Do not issue the query 'set names' with Connector/J, as the
           driver will not detect that the character set has changed, and
           will continue to use the character set detected during the
           initial connection setup.
-        </p><p>
+        </p></div><p>
         To allow multiple character sets to be sent from the client, the
         UTF-8 encoding should be used, either by configuring
         <code class="literal">utf8</code> as the default server character set, or
@@ -965,7 +1361,7 @@
           </p></li><li><p>
             A MySQL server that supports SSL and has been compiled and
             configured to do so, which is MySQL-4.0.4 or later, see
-            ???, for more information.
+            <a href="http://dev.mysql.com/doc/refman/5.0/en/secure-connections.html" target="_top">Using Secure Connections</a>, for more information.
           </p></li><li><p>
             A client certificate (covered later in this section)
           </p></li></ul></div><p>
@@ -981,24 +1377,26 @@
         following (assuming that <span><strong class="command">keytool</strong></span> is in your
         path. The <span><strong class="command">keytool</strong></span> should be located in the
         <code class="filename">bin</code> subdirectory of your JDK or JRE):
-      </p><pre class="programlisting">shell&gt; keytool -import -alias mysqlServerCACert -file cacert.pem -keystore truststore
-        </pre><p>
+      </p><pre class="programlisting">shell&gt; keytool -import -alias mysqlServerCACert \
+                                  -file cacert.pem -keystore truststore</pre><p>
         Keytool will respond with the following information:
       </p><pre class="programlisting">Enter keystore password:  *********
-Owner: EMAILADDRESS=walrus at example.com, CN=Walrus, O=MySQL AB, L=Orenburg, ST=Some
--State, C=RU
-Issuer: EMAILADDRESS=walrus at example.com, CN=Walrus, O=MySQL AB, L=Orenburg, ST=Som
-e-State, C=RU
+Owner: EMAILADDRESS=walrus at example.com, CN=Walrus, 
+       O=MySQL AB, L=Orenburg, ST=Some-State, C=RU
+Issuer: EMAILADDRESS=walrus at example.com, CN=Walrus, 
+       O=MySQL AB, L=Orenburg, ST=Some-State, C=RU
 Serial number: 0
-Valid from: Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003
+Valid from: 
+   Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003
 Certificate fingerprints:
-         MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
-         SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4D:6C
+    MD5:  61:91:A0:F2:03:07:61:7A:81:38:66:DA:19:C4:8D:AB
+    SHA1: 25:77:41:05:D5:AD:99:8C:14:8C:CA:68:9C:2F:B8:89:C3:34:4D:6C
 Trust this certificate? [no]:  yes
 Certificate was added to keystore</pre><p>
         You will then need to generate a client certificate, so that the
         MySQL server knows that it is talking to a secure client:
-      </p><pre class="programlisting"> shell&gt; keytool -genkey -keyalg rsa -alias mysqlClientCertificate -keystore keystore </pre><p>
+      </p><pre class="programlisting"> shell&gt; keytool -genkey -keyalg rsa \
+     -alias mysqlClientCertificate -keystore keystore </pre><p>
         Keytool will prompt you for the following information, and
         create a keystore named <code class="filename">keystore</code> in the
         current directory.
@@ -1031,11 +1429,17 @@
         the keystore file you created,
         <span class="property">path_to_truststore_file</span> with the path to
         the truststore file you created, and using the appropriate
-        password values for each property.
+        password values for each property. You can do this either on the
+        command line:
       </p><pre class="programlisting">-Djavax.net.ssl.keyStore=path_to_keystore_file
--Djavax.net.ssl.keyStorePassword=*********
+-Djavax.net.ssl.keyStorePassword=password
 -Djavax.net.ssl.trustStore=path_to_truststore_file
--Djavax.net.ssl.trustStorePassword=********* </pre><p>
+-Djavax.net.ssl.trustStorePassword=password </pre><p>
+        Or you can set the values directly within the application:
+      </p><pre class="programlisting"> System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file");
+System.setProperty("javax.net.ssl.keyStorePassword","password");
+System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file");
+System.setProperty("javax.net.ssl.trustStorePassword","password");</pre><p>
         You will also need to set <span class="property">useSSL</span> to
         <code class="literal">true</code> in your connection parameters for MySQL
         Connector/J, either by adding <code class="literal">useSSL=true</code> to
@@ -1047,35 +1451,41 @@
         You can test that SSL is working by turning on JSSE debugging
         (as detailed below), and look for the following key events:
       </p><pre class="programlisting">...
- *** ClientHello, v3.1
- RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12, 54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, 217, 219, 239, 202, 19, 121, 78 }
- Session ID:  {}
- Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17 }
- Compression Methods:  { 0 }
- ***
- [write] MD5 and SHA1 hashes:  len = 59
- 0000: 01 00 00 37 03 01 3D B6   90 FA C7 94 B4 D7 4A 0C  ...7..=.......J.
- 0010: 36 F4 00 A8 37 67 D7 40   10 8A E1 BE 84 99 02 D9  6...7g. at ........
- 0020: DB EF CA 13 79 4E 00 00   10 00 05 00 04 00 09 00  ....yN..........
- 0030: 0A 00 12 00 13 00 03 00   11 01 00                 ...........
- main, WRITE:  SSL v3.1 Handshake, length = 59
- main, READ:  SSL v3.1 Handshake, length = 74
- *** ServerHello, v3.1
- RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58, 202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3, 132, 110, 82, 148, 160, 92 }
- Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63, 182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177, 219, 158, 177, 187, 143}
- Cipher Suite:  { 0, 5 }
- Compression Method: 0
- ***
- %% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
- ** SSL_RSA_WITH_RC4_128_SHA
- [read] MD5 and SHA1 hashes:  len = 74
- 0000: 02 00 00 46 03 01 3D B6   43 98 74 32 04 67 19 64  ...F..=.C.t2.g.d
- 0010: 3A CA 4F B9 B2 64 D7 42   FE 15 53 BB BE 2A AA 03  :.O..d.B..S..*..
- 0020: 84 6E 52 94 A0 5C 20 A3   E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q....
- 0030: B3 44 3F B6 9E 1E 0B 96   4F AA 4C FF 5C 0F E2 18  .D?.....O.L.\...
- 0040: 11 B1 DB 9E B1 BB 8F 00   05 00                    ..........
- main, READ:  SSL v3.1 Handshake, length = 1712
- ...</pre><p>
+*** ClientHello, v3.1
+RandomCookie:  GMT: 1018531834 bytes = { 199, 148, 180, 215, 74, 12, »
+  54, 244, 0, 168, 55, 103, 215, 64, 16, 138, 225, 190, 132, 153, 2, »
+  217, 219, 239, 202, 19, 121, 78 }
+Session ID:  {}
+Cipher Suites:  { 0, 5, 0, 4, 0, 9, 0, 10, 0, 18, 0, 19, 0, 3, 0, 17 }
+Compression Methods:  { 0 }
+***
+[write] MD5 and SHA1 hashes:  len = 59
+0000: 01 00 00 37 03 01 3D B6 90 FA C7 94 B4 D7 4A 0C  ...7..=.......J.
+0010: 36 F4 00 A8 37 67 D7 40 10 8A E1 BE 84 99 02 D9  6...7g. at ........
+0020: DB EF CA 13 79 4E 00 00 10 00 05 00 04 00 09 00  ....yN..........
+0030: 0A 00 12 00 13 00 03 00 11 01 00                 ...........
+main, WRITE:  SSL v3.1 Handshake, length = 59
+main, READ:  SSL v3.1 Handshake, length = 74
+*** ServerHello, v3.1
+RandomCookie:  GMT: 1018577560 bytes = { 116, 50, 4, 103, 25, 100, 58, »
+   202, 79, 185, 178, 100, 215, 66, 254, 21, 83, 187, 190, 42, 170, 3, »
+   132, 110, 82, 148, 160, 92 }
+Session ID:  {163, 227, 84, 53, 81, 127, 252, 254, 178, 179, 68, 63, »
+   182, 158, 30, 11, 150, 79, 170, 76, 255, 92, 15, 226, 24, 17, 177, »
+   219, 158, 177, 187, 143}
+Cipher Suite:  { 0, 5 }
+Compression Method: 0
+***
+%% Created:  [Session-1, SSL_RSA_WITH_RC4_128_SHA]
+** SSL_RSA_WITH_RC4_128_SHA
+[read] MD5 and SHA1 hashes:  len = 74
+0000: 02 00 00 46 03 01 3D B6 43 98 74 32 04 67 19 64  ...F..=.C.t2.g.d
+0010: 3A CA 4F B9 B2 64 D7 42 FE 15 53 BB BE 2A AA 03  :.O..d.B..S..*..
+0020: 84 6E 52 94 A0 5C 20 A3 E3 54 35 51 7F FC FE B2  .nR..\ ..T5Q....
+0030: B3 44 3F B6 9E 1E 0B 96 4F AA 4C FF 5C 0F E2 18  .D?.....O.L.\...
+0040: 11 B1 DB 9E B1 BB 8F 00 05 00                    ..........
+main, READ:  SSL v3.1 Handshake, length = 1712
+...</pre><p>
         JSSE provides debugging (to STDOUT) when you set the following
         system property: <code class="literal">-Djavax.net.debug=all</code> This
         will tell you what keystores and truststores are being used, as
@@ -1128,53 +1538,58 @@
 
 public class ReplicationDriverDemo {
 
-    public static void main(String[] args) throws Exception {
-        ReplicationDriver driver = new ReplicationDriver();
+  public static void main(String[] args) throws Exception {
+    ReplicationDriver driver = new ReplicationDriver();
 
-        Properties props = new Properties();
+    Properties props = new Properties();
 
-        // We want this for failover on the slaves
-        props.put("autoReconnect", "true");
+    // We want this for failover on the slaves
+    props.put("autoReconnect", "true");
 
-        // We want to load balance between the slaves
-        props.put("roundRobinLoadBalance", "true");
+    // We want to load balance between the slaves
+    props.put("roundRobinLoadBalance", "true");
 
-        props.put("user", "foo");
-        props.put("password", "bar");
+    props.put("user", "foo");
+    props.put("password", "bar");
 
-        //
-        // Looks like a normal MySQL JDBC url, with a comma-separated list
-        // of hosts, the first being the 'master', the rest being any number
-        // of slaves that the driver will load balance against
-        //
+    //
+    // Looks like a normal MySQL JDBC url, with a
+    // comma-separated list of hosts, the first 
+    // being the 'master', the rest being any number
+    // of slaves that the driver will load balance against
+    //
 
-        Connection conn =
-            driver.connect("jdbc:mysql://master,slave1,slave2,slave3/test",
-                props);
+    Connection conn =
+        driver.connect("jdbc:mysql://master,slave1,slave2,slave3/test",
+            props);
 
-        //
-        // Perform read/write work on the master
-        // by setting the read-only flag to "false"
-        //
+    //
+    // Perform read/write work on the master
+    // by setting the read-only flag to "false"
+    //
 
-        conn.setReadOnly(false);
-        conn.setAutoCommit(false);
-        conn.createStatement().executeUpdate("UPDATE some_table ....");
-        conn.commit();
+    conn.setReadOnly(false);
+    conn.setAutoCommit(false);
+    conn.createStatement().executeUpdate("UPDATE some_table ....");
+    conn.commit();
 
-        //
-        // Now, do a query from a slave, the driver automatically picks one
-        // from the list
-        //
+    //
+    // Now, do a query from a slave, the driver automatically picks one
+    // from the list
+    //
 
-        conn.setReadOnly(true);
+    conn.setReadOnly(true);
 
-        ResultSet rs = conn.createStatement().executeQuery("SELECT a,b,c FROM some_other_table");
+    ResultSet rs = 
+      conn.createStatement().executeQuery("SELECT a,b FROM alt_table");
 
-         .......
-    }
+     .......
+  }
 }
-</pre></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-usagenotes"></a>1.5. Connector/J Notes and Tips</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-usagenotes-basic">1.5.1. Basic JDBC Concepts</a></span></dt><dt><span class="section"><a href="#connector-j-usagenotes-j2ee">1.5.2. Using Connector/J with J2EE and Other Java Frameworks</a></span></dt><dt><span class="section"><a href="#connector-j-usagenotes-troubleshooting">1.5.3. Common Problems and Solutions</a></span></dt></dl></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-usagenotes-basic"></a>1.5.1. Basic JDBC Concepts</h4></div></div></div><p>
+</pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-error-sqlstates"></a>1.4.7. Mapping MySQL Error Numbers to SQLStates</h4></div></div></div><p>
+        The table below provides a mapping of the MySQL Error Numbers to
+        <code class="literal">SQL States</code>
+      </p><div class="table"><a name="id2473006"></a><p class="title"><b>Table 1. Mapping of MySQL Error Numbers to SQLStates</b></p><table summary="Mapping of MySQL Error Numbers to SQLStates" border="1"><colgroup><col><col><col><col></colgroup><thead><tr><th>MySQL Error Number</th><th>MySQL Error Name</th><th>Legacy (X/Open) SQLState</th><th>SQL Standard SQLState</th></tr></thead><tbody><tr><td>1022</td><td>ER_DUP_KEY</td><td>S1000</td><td>23000</td></tr><tr><td>1037</td><td>ER_OUTOFMEMORY</td><td>S1001</td><td>HY001</td></tr><tr><td>1038</td><td>ER_OUT_OF_SORTMEMORY</td><td>S1001</td><td>HY001</td></tr><tr><td>1040</td><td>ER_CON_COUNT_ERROR</td><td>08004</td><td>08004</td></tr><tr><td>1042</td><td>ER_BAD_HOST_ERROR</td><td>08004</td><td>08S01</td></tr><tr><td>1043</td><td>ER_HANDSHAKE_ERROR</td><td>08004</td><td>08S01</td></tr><tr><td>1044</td><td>ER_DBACCESS_DENIED_ERROR</td><td>S1000</td><td>42000</td></tr><tr><td>1045</td><td>ER_ACCESS_DENIED_ERROR</td><td>28000</td><td>28000</td></tr><tr><td>1047</td><td>ER_UNKNOWN_COM_ERROR</td><td>08S01</td><td>HY000</td></tr><tr><td>1050</td><td>ER_TABLE_EXISTS_ERROR</td><td>S1000</td><td>42S01</td></tr><tr><td>1051</td><td>ER_BAD_TABLE_ERROR</td><td>42S02</td><td>42S02</td></tr><tr><td>1052</td><td>ER_NON_UNIQ_ERROR</td><td>S1000</td><td>23000</td></tr><tr><td>1053</td><td>ER_SERVER_SHUTDOWN</td><td>S1000</td><td>08S01</td></tr><tr><td>1054</td><td>ER_BAD_FIELD_ERROR</td><td>S0022</td><td>42S22</td></tr><tr><td>1055</td><td>ER_WRONG_FIELD_WITH_GROUP</td><td>S1009</td><td>42000</td></tr><tr><td>1056</td><td>ER_WRONG_GROUP_FIELD</td><td>S1009</td><td>42000</td></tr><tr><td>1057</td><td>ER_WRONG_SUM_SELECT</td><td>S1009</td><td>42000</td></tr><tr><td>1058</td><td>ER_WRONG_VALUE_COUNT</td><td>21S01</td><td>21S01</td></tr><tr><td>1059</td><td>ER_TOO_LONG_IDENT</td><td>S1009</td><td>42000</td></tr><tr><td>1060</td><td>ER_DUP_FIELDNAME</td><td>S1009</td><td>42S21</td></tr><tr><td>1061</td><td>ER_DUP_KEYNAME</td><td>S1009</td><td>42000</td></tr><tr><td>1062</td><td>ER_DUP_ENTRY</td><td>S1009</td><td>23000</td></tr><tr><td>1063</td><td>ER_WRONG_FIELD_SPEC</td><td>S1009</td><td>42000</td></tr><tr><td>1064</td><td>ER_PARSE_ERROR</td><td>42000</td><td>42000</td></tr><tr><td>1065</td><td>ER_EMPTY_QUERY</td><td>42000</td><td>42000</td></tr><tr><td>1066</td><td>ER_NONUNIQ_TABLE</td><td>S1009</td><td>42000</td></tr><tr><td>1067</td><td>ER_INVALID_DEFAULT</td><td>S1009</td><td>42000</td></tr><tr><td>1068</td><td>ER_MULTIPLE_PRI_KEY</td><td>S1009</td><td>42000</td></tr><tr><td>1069</td><td>ER_TOO_MANY_KEYS</td><td>S1009</td><td>42000</td></tr><tr><td>1070</td><td>ER_TOO_MANY_KEY_PARTS</td><td>S1009</td><td>42000</td></tr><tr><td>1071</td><td>ER_TOO_LONG_KEY</td><td>S1009</td><td>42000</td></tr><tr><td>1072</td><td>ER_KEY_COLUMN_DOES_NOT_EXITS</td><td>S1009</td><td>42000</td></tr><tr><td>1073</td><td>ER_BLOB_USED_AS_KEY</td><td>S1009</td><td>42000</td></tr><tr><td>1074</td><td>ER_TOO_BIG_FIELDLENGTH</td><td>S1009</td><td>42000</td></tr><tr><td>1075</td><td>ER_WRONG_AUTO_KEY</td><td>S1009</td><td>42000</td></tr><tr><td>1080</td><td>ER_FORCING_CLOSE</td><td>S1000</td><td>08S01</td></tr><tr><td>1081</td><td>ER_IPSOCK_ERROR</td><td>08S01</td><td>08S01</td></tr><tr><td>1082</td><td>ER_NO_SUCH_INDEX</td><td>S1009</td><td>42S12</td></tr><tr><td>1083</td><td>ER_WRONG_FIELD_TERMINATORS</td><td>S1009</td><td>42000</td></tr><tr><td>1084</td><td>ER_BLOBS_AND_NO_TERMINATED</td><td>S1009</td><td>42000</td></tr><tr><td>1090</td><td>ER_CANT_REMOVE_ALL_FIELDS</td><td>S1000</td><td>42000</td></tr><tr><td>1091</td><td>ER_CANT_DROP_FIELD_OR_KEY</td><td>S1000</td><td>42000</td></tr><tr><td>1101</td><td>ER_BLOB_CANT_HAVE_DEFAULT</td><td>S1000</td><td>42000</td></tr><tr><td>1102</td><td>ER_WRONG_DB_NAME</td><td>S1000</td><td>42000</td></tr><tr><td>1103</td><td>ER_WRONG_TABLE_NAME</td><td>S1000</td><td>42000</td></tr><tr><td>1104</td><td>ER_TOO_BIG_SELECT</td><td>S1000</td><td>42000</td></tr><tr><td>1106</td><td>ER_UNKNOWN_PROCEDURE</td><td>S1000</td><td>42000</td></tr><tr><td>1107</td><td>ER_WRONG_PARAMCOUNT_TO_PROCEDURE</td><td>S1000</td><td>42000</td></tr><tr><td>1109</td><td>ER_UNKNOWN_TABLE</td><td>S1000</td><td>42S02</td></tr><tr><td>1110</td><td>ER_FIELD_SPECIFIED_TWICE</td><td>S1000</td><td>42000</td></tr><tr><td>1112</td><td>ER_UNSUPPORTED_EXTENSION</td><td>S1000</td><td>42000</td></tr><tr><td>1113</td><td>ER_TABLE_MUST_HAVE_COLUMNS</td><td>S1000</td><td>42000</td></tr><tr><td>1115</td><td>ER_UNKNOWN_CHARACTER_SET</td><td>S1000</td><td>42000</td></tr><tr><td>1118</td><td>ER_TOO_BIG_ROWSIZE</td><td>S1000</td><td>42000</td></tr><tr><td>1120</td><td>ER_WRONG_OUTER_JOIN</td><td>S1000</td><td>42000</td></tr><tr><td>1121</td><td>ER_NULL_COLUMN_IN_INDEX</td><td>S1000</td><td>42000</td></tr><tr><td>1129</td><td>ER_HOST_IS_BLOCKED</td><td>08004</td><td>HY000</td></tr><tr><td>1130</td><td>ER_HOST_NOT_PRIVILEGED</td><td>08004</td><td>HY000</td></tr><tr><td>1131</td><td>ER_PASSWORD_ANONYMOUS_USER</td><td>S1000</td><td>42000</td></tr><tr><td>1132</td><td>ER_PASSWORD_NOT_ALLOWED</td><td>S1000</td><td>42000</td></tr><tr><td>1133</td><td>ER_PASSWORD_NO_MATCH</td><td>S1000</td><td>42000</td></tr><tr><td>1136</td><td>ER_WRONG_VALUE_COUNT_ON_ROW</td><td>S1000</td><td>21S01</td></tr><tr><td>1138</td><td>ER_INVALID_USE_OF_NULL</td><td>S1000</td><td>42000</td></tr><tr><td>1139</td><td>ER_REGEXP_ERROR</td><td>S1000</td><td>42000</td></tr><tr><td>1140</td><td>ER_MIX_OF_GROUP_FUNC_AND_FIELDS</td><td>S1000</td><td>42000</td></tr><tr><td>1141</td><td>ER_NONEXISTING_GRANT</td><td>S1000</td><td>42000</td></tr><tr><td>1142</td><td>ER_TABLEACCESS_DENIED_ERROR</td><td>S1000</td><td>42000</td></tr><tr><td>1143</td><td>ER_COLUMNACCESS_DENIED_ERROR</td><td>S1000</td><td>42000</td></tr><tr><td>1144</td><td>ER_ILLEGAL_GRANT_FOR_TABLE</td><td>S1000</td><td>42000</td></tr><tr><td>1145</td><td>ER_GRANT_WRONG_HOST_OR_USER</td><td>S1000</td><td>42000</td></tr><tr><td>1146</td><td>ER_NO_SUCH_TABLE</td><td>S1000</td><td>42S02</td></tr><tr><td>1147</td><td>ER_NONEXISTING_TABLE_GRANT</td><td>S1000</td><td>42000</td></tr><tr><td>1148</td><td>ER_NOT_ALLOWED_COMMAND</td><td>S1000</td><td>42000</td></tr><tr><td>1149</td><td>ER_SYNTAX_ERROR</td><td>S1000</td><td>42000</td></tr><tr><td>1152</td><td>ER_ABORTING_CONNECTION</td><td>S1000</td><td>08S01</td></tr><tr><td>1153</td><td>ER_NET_PACKET_TOO_LARGE</td><td>S1000</td><td>08S01</td></tr><tr><td>1154</td><td>ER_NET_READ_ERROR_FROM_PIPE</td><td>S1000</td><td>08S01</td></tr><tr><td>1155</td><td>ER_NET_FCNTL_ERROR</td><td>S1000</td><td>08S01</td></tr><tr><td>1156</td><td>ER_NET_PACKETS_OUT_OF_ORDER</td><td>S1000</td><td>08S01</td></tr><tr><td>1157</td><td>ER_NET_UNCOMPRESS_ERROR</td><td>S1000</td><td>08S01</td></tr><tr><td>1158</td><td>ER_NET_READ_ERROR</td><td>S1000</td><td>08S01</td></tr><tr><td>1159</td><td>ER_NET_READ_INTERRUPTED</td><td>S1000</td><td>08S01</td></tr><tr><td>1160</td><td>ER_NET_ERROR_ON_WRITE</td><td>S1000</td><td>08S01</td></tr><tr><td>1161</td><td>ER_NET_WRITE_INTERRUPTED</td><td>S1000</td><td>08S01</td></tr><tr><td>1162</td><td>ER_TOO_LONG_STRING</td><td>S1000</td><td>42000</td></tr><tr><td>1163</td><td>ER_TABLE_CANT_HANDLE_BLOB</td><td>S1000</td><td>42000</td></tr><tr><td>1164</td><td>ER_TABLE_CANT_HANDLE_AUTO_INCREMENT</td><td>S1000</td><td>42000</td></tr><tr><td>1166</td><td>ER_WRONG_COLUMN_NAME</td><td>S1000</td><td>42000</td></tr><tr><td>1167</td><td>ER_WRONG_KEY_COLUMN</td><td>S1000</td><td>42000</td></tr><tr><td>1169</td><td>ER_DUP_UNIQUE</td><td>S1000</td><td>23000</td></tr><tr><td>1170</td><td>ER_BLOB_KEY_WITHOUT_LENGTH</td><td>S1000</td><td>42000</td></tr><tr><td>1171</td><td>ER_PRIMARY_CANT_HAVE_NULL</td><td>S1000</td><td>42000</td></tr><tr><td>1172</td><td>ER_TOO_MANY_ROWS</td><td>S1000</td><td>42000</td></tr><tr><td>1173</td><td>ER_REQUIRES_PRIMARY_KEY</td><td>S1000</td><td>42000</td></tr><tr><td>1177</td><td>ER_CHECK_NO_SUCH_TABLE</td><td>S1000</td><td>42000</td></tr><tr><td>1178</td><td>ER_CHECK_NOT_IMPLEMENTED</td><td>S1000</td><td>42000</td></tr><tr><td>1179</td><td>ER_CANT_DO_THIS_DURING_AN_TRANSACTION</td><td>S1000</td><td>25000</td></tr><tr><td>1184</td><td>ER_NEW_ABORTING_CONNECTION</td><td>S1000</td><td>08S01</td></tr><tr><td>1189</td><td>ER_MASTER_NET_READ</td><td>S1000</td><td>08S01</td></tr><tr><td>1190</td><td>ER_MASTER_NET_WRITE</td><td>S1000</td><td>08S01</td></tr><tr><td>1203</td><td>ER_TOO_MANY_USER_CONNECTIONS</td><td>S1000</td><td>42000</td></tr><tr><td>1205</td><td>ER_LOCK_WAIT_TIMEOUT</td><td>41000</td><td>41000</td></tr><tr><td>1207</td><td>ER_READ_ONLY_TRANSACTION</td><td>S1000</td><td>25000</td></tr><tr><td>1211</td><td>ER_NO_PERMISSION_TO_CREATE_USER</td><td>S1000</td><td>42000</td></tr><tr><td>1213</td><td>ER_LOCK_DEADLOCK</td><td>41000</td><td>40001</td></tr><tr><td>1216</td><td>ER_NO_REFERENCED_ROW</td><td>S1000</td><td>23000</td></tr><tr><td>1217</td><td>ER_ROW_IS_REFERENCED</td><td>S1000</td><td>23000</td></tr><tr><td>1218</td><td>ER_CONNECT_TO_MASTER</td><td>S1000</td><td>08S01</td></tr><tr><td>1222</td><td>ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT</td><td>S1000</td><td>21000</td></tr><tr><td>1226</td><td>ER_USER_LIMIT_REACHED</td><td>S1000</td><td>42000</td></tr><tr><td>1230</td><td>ER_NO_DEFAULT</td><td>S1000</td><td>42000</td></tr><tr><td>1231</td><td>ER_WRONG_VALUE_FOR_VAR</td><td>S1000</td><td>42000</td></tr><tr><td>1232</td><td>ER_WRONG_TYPE_FOR_VAR</td><td>S1000</td><td>42000</td></tr><tr><td>1234</td><td>ER_CANT_USE_OPTION_HERE</td><td>S1000</td><td>42000</td></tr><tr><td>1235</td><td>ER_NOT_SUPPORTED_YET</td><td>S1000</td><td>42000</td></tr><tr><td>1239</td><td>ER_WRONG_FK_DEF</td><td>S1000</td><td>42000</td></tr><tr><td>1241</td><td>ER_OPERAND_COLUMNS</td><td>S1000</td><td>21000</td></tr><tr><td>1242</td><td>ER_SUBQUERY_NO_1_ROW</td><td>S1000</td><td>21000</td></tr><tr><td>1247</td><td>ER_ILLEGAL_REFERENCE</td><td>S1000</td><td>42S22</td></tr><tr><td>1248</td><td>ER_DERIVED_MUST_HAVE_ALIAS</td><td>S1000</td><td>42000</td></tr><tr><td>1249</td><td>ER_SELECT_REDUCED</td><td>S1000</td><td>01000</td></tr><tr><td>1250</td><td>ER_TABLENAME_NOT_ALLOWED_HERE</td><td>S1000</td><td>42000</td></tr><tr><td>1251</td><td>ER_NOT_SUPPORTED_AUTH_MODE</td><td>S1000</td><td>08004</td></tr><tr><td>1252</td><td>ER_SPATIAL_CANT_HAVE_NULL</td><td>S1000</td><td>42000</td></tr><tr><td>1253</td><td>ER_COLLATION_CHARSET_MISMATCH</td><td>S1000</td><td>42000</td></tr><tr><td>1261</td><td>ER_WARN_TOO_FEW_RECORDS</td><td>S1000</td><td>01000</td></tr><tr><td>1262</td><td>ER_WARN_TOO_MANY_RECORDS</td><td>S1000</td><td>01000</td></tr><tr><td>1263</td><td>ER_WARN_NULL_TO_NOTNULL</td><td>S1000</td><td>01000</td></tr><tr><td>1264</td><td>ER_WARN_DATA_OUT_OF_RANGE</td><td>S1000</td><td>01000</td></tr><tr><td>1265</td><td>ER_WARN_DATA_TRUNCATED</td><td>S1000</td><td>01000</td></tr><tr><td>1280</td><td>ER_WRONG_NAME_FOR_INDEX</td><td>S1000</td><td>42000</td></tr><tr><td>1281</td><td>ER_WRONG_NAME_FOR_CATALOG</td><td>S1000</td><td>42000</td></tr><tr><td>1286</td><td>ER_UNKNOWN_STORAGE_ENGINE</td><td>S1000</td><td>42000</td></tr></tbody></table></div></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-usagenotes"></a>1.5. Connector/J Notes and Tips</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-usagenotes-basic">1.5.1. Basic JDBC Concepts</a></span></dt><dt><span class="section"><a href="#connector-j-usagenotes-j2ee">1.5.2. Using Connector/J with J2EE and Other Java Frameworks</a></span></dt><dt><span class="section"><a href="#connector-j-usagenotes-troubleshooting">1.5.3. Common Problems and Solutions</a></span></dt></dl></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-usagenotes-basic"></a>1.5.1. Basic JDBC Concepts</h4></div></div></div><p>
         This section provides some general JDBC background.
       </p><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-connect-drivermanager"></a>1.5.1.1. Connecting to MySQL Using the <code class="literal">DriverManager</code> Interface</h5></div></div></div><p>
           When you are using JDBC outside of an application server, the
@@ -1229,18 +1644,21 @@
 import java.sql.DriverManager;
 import java.sql.SQLException;
 
-    ... try {
-            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&amp;password=greatsqldb");
+...
+try {
+    Connection conn = 
+       DriverManager.getConnection("jdbc:mysql://localhost/test?" + 
+                                   "user=monty&amp;password=greatsqldb");
 
-            // Do something with the Connection
+    // Do something with the Connection
 
-           ....
-        } catch (SQLException ex) {
-            // handle any errors
-            System.out.println("SQLException: " + ex.getMessage());
-            System.out.println("SQLState: " + ex.getSQLState());
-            System.out.println("VendorError: " + ex.getErrorCode());
-        }
+   ...
+} catch (SQLException ex) {
+    // handle any errors
+    System.out.println("SQLException: " + ex.getMessage());
+    System.out.println("SQLState: " + ex.getSQLState());
+    System.out.println("VendorError: " + ex.getErrorCode());
+}
 </pre><p>
             Once a <code class="classname">Connection</code> is established, it
             can be used to create <code class="classname">Statement</code> and
@@ -1328,25 +1746,27 @@
           fully implemented with the exception of the
           <code class="literal">getParameterMetaData()</code> method.
         </p><p>
-          See ???, for more information
-          on MySQL stored procedures.
+          For more information on MySQL stored procedures, please refer
+          to
+          <a href="http://dev.mysql.com/doc/mysql/en/stored-procedures.html" target="_top">http://dev.mysql.com/doc/mysql/en/stored-procedures.html</a>.
         </p><p>
           Connector/J exposes stored procedure functionality through
           JDBC's <code class="classname">CallableStatement</code> interface.
-        </p><p><b>Note. </b>
+        </p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
             Current versions of MySQL server do not return enough
             information for the JDBC driver to provide result set
             metadata for callable statements. This means that when using
             <code class="literal">CallableStatement</code>,
             <code class="literal">ResultSetMetaData</code> may return
             <code class="literal">NULL</code>.
-          </p><p>
+          </p></div><p>
           The following example shows a stored procedure that returns
           the value of <code class="varname">inOutParam</code> incremented by 1,
           and the string passed in via <code class="varname">inputParam</code> as
           a <code class="classname">ResultSet</code>:
 
-          </p><div class="example"><a name="connector-j-examples-stored-procedure"></a><p class="title"><b>Example 3. Stored Procedures</b></p><pre class="programlisting">CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam INT)
+          </p><div class="example"><a name="connector-j-examples-stored-procedure"></a><p class="title"><b>Example 3. Stored Procedures</b></p><pre class="programlisting">CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), \ 
+                                        INOUT inOutParam INT)
 BEGIN
     DECLARE z INT;
     SET z = inOutParam + 1;
@@ -1381,7 +1801,7 @@
 
 
 
-    cStmt.setString(1, "abcdefg");</pre></div><p><b>Note. </b>
+    cStmt.setString(1, "abcdefg");</pre></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
                 <code class="literal">Connection.prepareCall()</code> is an
                 expensive method, due to the metadata retrieval that the
                 driver performs to support output parameters. For
@@ -1390,7 +1810,7 @@
                 <code class="literal">Connection.prepareCall()</code> by reusing
                 <code class="classname">CallableStatement</code> instances in
                 your code.
-              </p></li><li><p>
+              </p></div></li><li><p>
               Register the output parameters (if any exist)
             </p><p>
               To retrieve the values of output parameters (parameters
@@ -1664,8 +2084,9 @@
 
     rs.close();
 
-    System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID()': "
-        + autoIncKeyFromFunc);
+    System.out.println("Key returned from " + 
+                       "'SELECT LAST_INSERT_ID()': " +
+                       autoIncKeyFromFunc);
 
 } finally {
 
@@ -1898,7 +2319,8 @@
           * variable, as JNDI lookups can be expensive as well.
           */
 
-        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
+        DataSource ds = 
+          (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
 
         /*
          * The following code is what would actually be in your
@@ -2050,8 +2472,8 @@
 
     &lt;!-- Don't use autoReconnect=true, it's going away eventually
          and it's a crutch for older connection pools that couldn't
-         test connections. You need to decide whether your application is
-         supposed to deal with SQLExceptions (hint, it should), and
+         test connections. You need to decide whether your application
+         is supposed to deal with SQLExceptions (hint, it should), and
          how much of a performance penalty you're willing to pay
          to ensure 'freshness' of the connection --&gt;
 
@@ -2184,11 +2606,345 @@
              our implementation of these to increase the robustness
              of the connection pool. --&gt;
 
-        &lt;exception-sorter-class-name&gt;com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter&lt;/exception-sorter-class-name&gt;
-        &lt;valid-connection-checker-class-name&gt;com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker&lt;/valid-connection-checker-class-name&gt;
+        &lt;exception-sorter-class-name&gt; 
+  com.mysql.jdbc.integration.jboss.ExtendedMysqlExceptionSorter
+        &lt;/exception-sorter-class-name&gt;
+        &lt;valid-connection-checker-class-name&gt;
+  com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker
+        &lt;/valid-connection-checker-class-name&gt;
 
     &lt;/local-tx-datasource&gt;
-&lt;/datasources&gt; </pre></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-usagenotes-troubleshooting"></a>1.5.3. Common Problems and Solutions</h4></div></div></div><p>
+&lt;/datasources&gt; </pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-spring-config"></a>1.5.2.4. Using Connector/J with Spring</h5></div></div></div><p>
+          The Spring Framework is a Java-based application framework
+          designed for assisting in application design by providing a
+          way to configure components. The technique used by Spring is a
+          well known design pattern called Dependency Injection (see
+          <a href="http://www.martinfowler.com/articles/injection.html" target="_top">Inversion
+          of Control Containers and the Dependency Injection
+          pattern</a>). This article will focus on Java-oriented
+          access to MySQL databases with Spring 2.0. For those
+          wondering, there is a .NET port of Spring appropriately named
+          Spring.NET.
+        </p><p>
+          Spring is not only a system for configuring components, but
+          also includes support for aspect oriented programming (AOP).
+          This is one of the main benefits and the foundation for
+          Spring's resource and transaction management. Spring also
+          provides utilities for integrating resource management with
+          JDBC and Hibernate.
+        </p><p>
+          For the examples in this section the MySQL world sample
+          database will be used. The first task is to setup a MySQL data
+          source through Spring. Components within Spring use the "bean"
+          terminology. For example, to configure a connection to a MySQL
+          server supporting the world sample database you might use:
+        </p><pre class="programlisting">&lt;util:map id="dbProps"&gt;
+    &lt;entry key="db.driver" value="com.mysql.jdbc.Driver"/&gt;
+    &lt;entry key="db.jdbcurl" value="jdbc:mysql://localhost/world"/&gt;
+    &lt;entry key="db.username" value="myuser"/&gt;
+    &lt;entry key="db.password" value="mypass"/&gt;
+&lt;/util:map&gt;
+
+        </pre><p>
+          In the above example we are assigning values to properties
+          that will be used in the configuration. For the datasource
+          configuration:
+        </p><pre class="programlisting">&lt;bean id="dataSource" 
+       class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
+    &lt;property name="driverClassName" value="${db.driver}"/&gt;
+    &lt;property name="url" value="${db.jdbcurl}"/&gt;
+    &lt;property name="username" value="${db.username}"/&gt;
+    &lt;property name="password" value="${db.password}"/&gt;
+&lt;/bean&gt;
+        </pre><p>
+          The placeholders are used to provide values for properties of
+          this bean. This means that you can specify all the properties
+          of the configuration in one place instead of entering the
+          values for each property on each bean. We do, however, need
+          one more bean to pull this all together. The last bean is
+          responsible for actually replacing the placeholders with the
+          property values.
+        </p><pre class="programlisting">&lt;bean 
+ class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"&gt;
+    &lt;property name="properties" ref="dbProps"/&gt;
+&lt;/bean&gt;
+        </pre><p>
+          Now that we have our MySQL data source configured and ready to
+          go, we write some Java code to access it. The example below
+          will retrieve three random cities and their corresponding
+          country using the data source we configured with Spring.
+        </p><pre class="programlisting">// Create a new application context. this processes the Spring config
+ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex1appContext.xml");
+// Retrieve the data source from the application context
+    DataSource ds = (DataSource) ctx.getBean("dataSource");
+// Open a database connection using Spring's DataSourceUtils
+Connection c = DataSourceUtils.getConnection(ds);
+try {
+    // retrieve a list of three random cities
+    PreparedStatement ps = c.prepareStatement(
+        "select City.Name as 'City', Country.Name as 'Country' " +
+        "from City inner join Country on City.CountryCode = Country.Code " +
+        "order by rand() limit 3");
+    ResultSet rs = ps.executeQuery();
+    while(rs.next()) {
+        String city = rs.getString("City");
+        String country = rs.getString("Country");
+        System.out.printf("The city %s is in %s%n", city, country);
+    }
+} catch (SQLException ex) {
+    // something has failed and we print a stack trace to analyse the error
+    ex.printStackTrace();
+    // ignore failure closing connection
+    try { c.close(); } catch (SQLException e) { }
+} finally {
+    // properly release our connection
+    DataSourceUtils.releaseConnection(c, ds);
+}</pre><p>
+          This is very similar to normal JDBC access to MySQL with the
+          main difference being that we are using DataSourceUtils
+          instead of the DriverManager to create the connection.
+        </p><p>
+          While it may seem like a small difference, the implications
+          are somewhat far reaching. Spring manages this resource in a
+          way similar to a container managed data source in a J2EE
+          application server. When a connection is opened, it can be
+          subsequently accessed in other parts of the code if it is
+          synchronized with a transaction. This makes it possible to
+          treat different parts of your application as transactional
+          instead of passing around a database connection.
+        </p><div class="section" lang="en"><div class="titlepage"><div><div><h6 class="title"><a name="connector-j-usagenotes-spring-config-jdbctemplate"></a>1.5.2.4.1. Using <code class="classname">JdbcTemplate</code></h6></div></div></div><p>
+            Spring makes extensive use of the Template method design
+            pattern (see
+            <a href="http://en.wikipedia.org/wiki/Template_method_pattern" target="_top">Template
+            Method Pattern</a>). Our immediate focus will be on the
+            <code class="literal">JdbcTemplate</code> and related classes,
+            specifically <code class="literal">NamedParameterJdbcTemplate</code>.
+            The template classes handle obtaining and releasing a
+            connection for data access when one is needed.
+          </p><p>
+            The next example shows how to use
+            <code class="literal">NamedParameterJdbcTemplate</code> inside of a
+            DAO (Data Access Object) class to retrieve a random city
+            given a country code.
+          </p><pre class="programlisting">public class Ex2JdbcDao {
+     /**
+     * Data source reference which will be provided by Spring.
+     */
+     private DataSource dataSource;
+        
+     /**
+     * Our query to find a random city given a country code. Notice
+     * the ":country" parameter towards the end. This is called a
+     * named parameter.
+     */
+     private String queryString = "select Name from City " +
+        "where CountryCode = :country order by rand() limit 1";
+        
+     /**
+     * Retrieve a random city using Spring JDBC access classes.
+     */
+     public String getRandomCityByCountryCode(String cntryCode) {
+         // A template that allows using queries with named parameters
+         NamedParameterJdbcTemplate template =
+         new NamedParameterJdbcTemplate(dataSource);
+         // A java.util.Map is used to provide values for the parameters
+         Map params = new HashMap();
+         params.put("country", cntryCode);
+         // We query for an Object and specify what class we are expecting
+         return (String)template.queryForObject(queryString, params, String.class);
+     }
+        
+    /**
+    * A JavaBean setter-style method to allow Spring to inject the data source.
+    * @param dataSource
+    */
+    public void setDataSource(DataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+}
+   </pre><p>
+            The focus in the above code is on the
+            <code class="literal">getRandomCityByCountryCode()</code> method. We
+            pass a country code and use the
+            <code class="literal">NamedParameterJdbcTemplate</code> to query for a
+            city. The country code is placed in a Map with the key
+            "country", which is the parameter is named in the SQL query.
+          </p><p>
+            To access this code, you need to configure it with Spring by
+            providing a reference to the data source.
+          </p><pre class="programlisting">&lt;bean id="dao" class="code.Ex2JdbcDao"&gt;
+    &lt;property name="dataSource" ref="dataSource"/&gt;
+&lt;/bean&gt;</pre><p>
+            At this point, we can just grab a reference to the DAO from
+            Spring and call
+            <code class="literal">getRandomCityByCountryCode()</code>.
+          </p><pre class="programlisting">// Create the application context
+    ApplicationContext ctx =
+    new ClassPathXmlApplicationContext("ex2appContext.xml");
+    // Obtain a reference to our DAO
+    Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao");
+        
+    String countryCode = "USA";
+        
+    // Find a few random cities in the US
+    for(int i = 0; i &lt; 4; ++i)
+        System.out.printf("A random city in %s is %s%n", countryCode,
+            dao.getRandomCityByCountryCode(countryCode));
+</pre><p>
+            This example shows how to use Spring's JDBC classes to
+            completely abstract away the use of traditional JDBC classes
+            including <code class="literal">Connection</code> and
+            <code class="literal">PreparedStatement</code>.
+          </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h6 class="title"><a name="connector-j-usagenotes-spring-config-transactional"></a>1.5.2.4.2. Transactional JDBC Access</h6></div></div></div><p>
+            You might be wondering how we can add transactions into our
+            code if we don't deal directly with the JDBC classes. Spring
+            provides a transaction management package that not only
+            replaces JDBC transaction management, but also allows
+            declarative transaction management (configuration instead of
+            code).
+          </p><p>
+            In order to use transactional database access, we will need
+            to change the storage engine of the tables in the world
+            database. The downloaded script explicitly creates MyISAM
+            tables which do not support transactional semantics. The
+            InnoDB storage engine does support transactions and this is
+            what we will be using. We can change the storage engine with
+            the following statements.
+          </p><pre class="programlisting">ALTER TABLE City ENGINE=InnoDB;
+ALTER TABLE Country ENGINE=InnoDB;
+ALTER TABLE CountryLanguage ENGINE=InnoDB;
+</pre><p>
+            A good programming practice emphasized by Spring is
+            separating interfaces and implementations. What this means
+            is that we can create a Java interface and only use the
+            operations on this interface without any internal knowledge
+            of what the actual implementation is. We will let Spring
+            manage the implementation and with this it will manage the
+            transactions for our implementation.
+          </p><p>
+            First you create a simple interface:
+          </p><pre class="programlisting">public interface Ex3Dao {
+    Integer createCity(String name, String countryCode,
+    String district, Integer population);
+}
+</pre><p>
+            This interface contains one method that will create a new
+            city record in the database and return the id of the new
+            record. Next you need to create an implementation of this
+            interface.
+          </p><pre class="programlisting">public class Ex3DaoImpl implements Ex3Dao {
+    protected DataSource dataSource;
+    protected SqlUpdate updateQuery;
+    protected SqlFunction idQuery;
+        
+    public Integer createCity(String name, String countryCode,
+        String district, Integer population) {
+            updateQuery.update(new Object[] { name, countryCode,
+                   district, population });
+            return getLastId();
+        }
+        
+    protected Integer getLastId() {
+        return idQuery.run();
+    }
+}
+</pre><p>
+            You can see that we only operate on abstract query objects
+            here and don't deal directly with the JDBC API. Also, this
+            is the complete implementation. All of our transaction
+            management will be dealt with in the configuration. To get
+            the configuration started, we need to create the DAO.
+          </p><pre class="programlisting">&lt;bean id="dao" class="code.Ex3DaoImpl"&gt;
+    &lt;property name="dataSource" ref="dataSource"/&gt;
+    &lt;property name="updateQuery"&gt;...&lt;/property&gt;
+    &lt;property name="idQuery"&gt;...&lt;/property&gt;
+&lt;/bean&gt;
+</pre><p>
+            Now you need to setup the transaction configuration. The
+            first thing you must do is create transaction manager to
+            manage the data source and a specification of what
+            transaction properties are required for for the
+            <code class="literal">dao</code> methods.
+          </p><pre class="programlisting">&lt;bean id="transactionManager" 
+  class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
+    &lt;property name="dataSource" ref="dataSource"/&gt;
+&lt;/bean&gt;
+        
+&lt;tx:advice id="txAdvice" transaction-manager="transactionManager"&gt;
+    &lt;tx:attributes&gt;
+        &lt;tx:method name="*"/&gt;
+    &lt;/tx:attributes&gt;
+&lt;/tx:advice&gt;</pre><p>
+            The preceding code creates a transaction manager that
+            handles transactions for the data source provided to it. The
+            <code class="literal">txAdvice</code> uses this transaction manager
+            and the attributes specify to create a transaction for all
+            methods. Finally you need to apply this advice with an AOP
+            pointcut.
+          </p><pre class="programlisting">&lt;aop:config&gt;
+    &lt;aop:pointcut id="daoMethods"
+        expression="execution(* code.Ex3Dao.*(..))"/&gt;
+     &lt;aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/&gt;
+&lt;/aop:config&gt;</pre><p>
+            This basically says that all methods called on the
+            <code class="literal">Ex3Dao</code> interface will be wrapped in a
+            transaction. To make use of this, you only have to retrieve
+            the <code class="literal">dao</code> from the application context and
+            call a method on the <code class="literal">dao</code> instance.
+          </p><pre class="programlisting">Ex3Dao dao = (Ex3Dao) ctx.getBean("dao");
+Integer id = dao.createCity(name,  countryCode, district, pop);
+</pre><p>
+            We can verify from this that there is no transaction
+            management happening in our Java code and it's all
+            configured with Spring. This is a very powerful notion and
+            regarded as one of the most beneficial features of Spring.
+          </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h6 class="title"><a name="connector-j-usagenotes-spring-config-connpooling"></a>1.5.2.4.3. Connection Pooling</h6></div></div></div><p>
+            In many sitations, such as web applications, there will be a
+            large number of small database transactions. When this is
+            the case, it usually makes sense to create a pool of
+            database connections available for web requests as needed.
+            Although MySQL does not spawn an extra process when a
+            connection is made, there is still a small amount of
+            overhead to create and setup the connection. Pooling of
+            connections also alleviates problems such as collecting
+            large amounts of sockets in the <code class="literal">TIME_WAIT</code>
+            state.
+          </p><p>
+            Setting up pooling of MySQL connections with Spring is as
+            simple as changing the data source configuration in the
+            application context. There are a number of configurations
+            that we can use. The first example is based on the
+            <a href="http://jakarta.apache.org/commons/dbcp/" target="_top">Jakarta
+            Commons DBCP library</a>. The example below replaces the
+            source configuration that was based on
+            <code class="literal">DriverManagerDataSource</code> with DBCP's
+            BasicDataSource.
+          </p><pre class="programlisting">&lt;bean id="dataSource" destroy-method="close"
+  class="org.apache.commons.dbcp.BasicDataSource"&gt;
+    &lt;property name="driverClassName" value="${db.driver}"/&gt;
+    &lt;property name="url" value="${db.jdbcurl}"/&gt;
+    &lt;property name="username" value="${db.username}"/&gt;
+    &lt;property name="password" value="${db.password}"/&gt;
+    &lt;property name="initialSize" value="3"/&gt;
+&lt;/bean&gt;</pre><p>
+            The configuration of the two solutions is very similar. The
+            difference is that DBCP will pool connections to the
+            database instead of creating a new connection every time one
+            is requested. We have also set a parameter here called
+            <code class="literal">initialSize</code>. This tells DBCP that we want
+            three connections in the pool when it is created.
+          </p><p>
+            Another way to configure connection pooling is to configure
+            a data source in our J2EE application server. Using JBoss as
+            an example, you can set up the MySQL connection pool by
+            creating a file called
+            <code class="filename">mysql-local-ds.xml</code> and placing it in
+            the server/default/deploy directory in JBoss. Once we have
+            this setup, we can use JNDI to look it up. With Spring, this
+            lookup is very simple. The data source configuration looks
+            like this.
+          </p><pre class="programlisting">&lt;jee:jndi-lookup id="dataSource" jndi-name="java:MySQL_DS"/&gt;</pre></div></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-usagenotes-troubleshooting"></a>1.5.3. Common Problems and Solutions</h4></div></div></div><p>
         There are a few issues that seem to be commonly encountered
         often by users of MySQL Connector/J. This section deals with
         their symptoms, and their resolutions.
@@ -2218,6 +2974,41 @@
             </p></li><li><p><a href="#qandaitem-1-5-3-5">1.5.3.5: </a>
               I'm trying to use JDBC-2.0 updatable result sets, and I
               get an exception saying my result set is not updatable.
+            </p></li><li><p><a href="#qandaitem-1-5-3-6">1.5.3.6: </a>
+              I cannot connect to the MySQL server using Connector/J,
+              and I'm sure the connection paramters are correct.
+            </p></li><li><p><a href="#qandaitem-1-5-3-7">1.5.3.7: </a>
+              I am trying to connect to my MySQL server within my
+              application, but I get the following error and stack
+              trace:
+            </p><pre class="programlisting">java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
+
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv failed
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.&lt;init&gt;(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)</pre></li><li><p><a href="#qandaitem-1-5-3-8">1.5.3.8: </a>
+              My application is deployed through JBoss and I am using
+              transactions to handle the statements on the MySQL
+              database. Under heavy loads I am getting a error and stack
+              trace, but these only occur after a fixed period of heavy
+              activity.
+            </p></li><li><p><a href="#qandaitem-1-5-3-9">1.5.3.9: </a>
+              When using <span><strong class="command">gcj</strong></span> an
+              <code class="literal">java.io.CharConversionException</code> is
+              raised when working with certain character sequences.
+            </p></li><li><p><a href="#qandaitem-1-5-3-10">1.5.3.10: </a>
+              Updating a table that contains a primary key that is
+              either <code class="literal">FLOAT</code> or compound primary key
+              that uses <code class="literal">FLOAT</code> fails to update the
+              table and raises an exception.
             </p></li></ul></div><p><span class="bold"><strong>Questions and Answers</strong></span></p><p><a name="qandaitem-1-5-3-1"></a><span class="bold"><strong>1.5.3.1: </strong></span><span class="bold"><strong>
               When I try to connect to the database with MySQL
               Connector/J, I get the following exception:
@@ -2236,8 +3027,8 @@
               You must add the necessary security credentials to the
               MySQL server for this to happen, using the
               <code class="literal">GRANT</code> statement to your MySQL Server.
-              See ???, for more information.
-            </p><p><b>Note. </b>
+              See <a href="http://dev.mysql.com/doc/refman/5.0/en/grant.html" target="_top"><code class="literal">GRANT</code> Syntax</a>, for more information.
+            </p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
                 Testing your connectivity with the
                 <span><strong class="command">mysql</strong></span> command-line client will not
                 work unless you add the <code class="option">--host</code> flag,
@@ -2248,11 +3039,11 @@
                 <code class="literal">localhost</code>. If you are testing
                 connectivity to <code class="literal">localhost</code>, use
                 <code class="literal">127.0.0.1</code> as the hostname instead.
-              </p><p><b>Warning. </b>
+              </p></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Warning</h3><p>
                 Changing privileges and permissions improperly in MySQL
                 can potentially cause your server installation to not
                 have optimal security properties.
-              </p><p><a name="qandaitem-1-5-3-2"></a><span class="bold"><strong>1.5.3.2: </strong></span><span class="bold"><strong>
+              </p></div><p><a name="qandaitem-1-5-3-2"></a><span class="bold"><strong>1.5.3.2: </strong></span><span class="bold"><strong>
               My application throws an SQLException 'No Suitable
               Driver'. Why is this happening?
             </strong></span></p><p>
@@ -2260,7 +3051,7 @@
             </p><div class="itemizedlist"><ul type="disc"><li><p>
                   The Connector/J driver is not in your
                   <code class="literal">CLASSPATH</code>, see
-                  <a href="#connector-j-installing" title="1.2. Installing Connector/J">Section 1.2, “Installing Connector/J”</a>.
+                  <a href="#connector-j-installing" title="1.2. Connector/J Installation">Section 1.2, “Connector/J Installation”</a>.
                 </p></li><li><p>
                   The format of your connection URL is incorrect, or you
                   are referencing the wrong JDBC driver.
@@ -2336,125 +3127,125 @@
               can handle these exceptions might look like:
             </p><p>
               </p><div class="example"><a name="connector-j-examples-transaction-retry"></a><p class="title"><b>Example 12. Example of transaction with retry logic</b></p><pre class="programlisting">public void doBusinessOp() throws SQLException {
-        Connection conn = null;
-        Statement stmt = null;
-        ResultSet rs = null;
+    Connection conn = null;
+    Statement stmt = null;
+    ResultSet rs = null;
 
-        //
-        // How many times do you want to retry the transaction
-        // (or at least _getting_ a connection)?
-        //
-        int retryCount = 5;
+    //
+    // How many times do you want to retry the transaction
+    // (or at least _getting_ a connection)?
+    //
+    int retryCount = 5;
 
-        boolean transactionCompleted = false;
+    boolean transactionCompleted = false;
 
-        do {
-            try {
-                conn = getConnection(); // assume getting this from a
-                                        // javax.sql.DataSource, or the
-                                        // java.sql.DriverManager
+    do {
+        try {
+            conn = getConnection(); // assume getting this from a
+                                    // javax.sql.DataSource, or the
+                                    // java.sql.DriverManager
 
-                conn.setAutoCommit(false);
+            conn.setAutoCommit(false);
 
-                //
-                // Okay, at this point, the 'retry-ability' of the
-                // transaction really depends on your application logic,
-                // whether or not you're using autocommit (in this case
-                // not), and whether you're using transacational storage
-                // engines
-                //
-                // For this example, we'll assume that it's _not_ safe
-                // to retry the entire transaction, so we set retry count
-                // to 0 at this point
-                //
-                // If you were using exclusively transaction-safe tables,
-                // or your application could recover from a connection going
-                // bad in the middle of an operation, then you would not
-                // touch 'retryCount' here, and just let the loop repeat
-                // until retryCount == 0.
-                //
-                retryCount = 0;
+            //
+            // Okay, at this point, the 'retry-ability' of the
+            // transaction really depends on your application logic,
+            // whether or not you're using autocommit (in this case
+            // not), and whether you're using transacational storage
+            // engines
+            //
+            // For this example, we'll assume that it's _not_ safe
+            // to retry the entire transaction, so we set retry
+            // count to 0 at this point
+            //
+            // If you were using exclusively transaction-safe tables,
+            // or your application could recover from a connection going
+            // bad in the middle of an operation, then you would not
+            // touch 'retryCount' here, and just let the loop repeat
+            // until retryCount == 0.
+            //
+            retryCount = 0;
 
-                stmt = conn.createStatement();
+            stmt = conn.createStatement();
 
-                String query = "SELECT foo FROM bar ORDER BY baz";
+            String query = "SELECT foo FROM bar ORDER BY baz";
 
-                rs = stmt.executeQuery(query);
+            rs = stmt.executeQuery(query);
 
-                while (rs.next()) {
-                }
+            while (rs.next()) {
+            }
 
-                rs.close();
-                rs = null;
+            rs.close();
+            rs = null;
 
-                stmt.close();
-                stmt = null;
+            stmt.close();
+            stmt = null;
 
-                conn.commit();
-                conn.close();
-                conn = null;
+            conn.commit();
+            conn.close();
+            conn = null;
 
-                transactionCompleted = true;
-            } catch (SQLException sqlEx) {
+            transactionCompleted = true;
+        } catch (SQLException sqlEx) {
 
-                //
-                // The two SQL states that are 'retry-able' are 08S01
-                // for a communications error, and 40001 for deadlock.
-                //
-                // Only retry if the error was due to a stale connection,
-                // communications problem or deadlock
-                //
+            //
+            // The two SQL states that are 'retry-able' are 08S01
+            // for a communications error, and 40001 for deadlock.
+            //
+            // Only retry if the error was due to a stale connection,
+            // communications problem or deadlock
+            //
 
-                String sqlState = sqlEx.getSQLState();
+            String sqlState = sqlEx.getSQLState();
 
-                if ("08S01".equals(sqlState) || "40001".equals(sqlState)) {
-                    retryCount--;
-                } else {
-                    retryCount = 0;
+            if ("08S01".equals(sqlState) || "40001".equals(sqlState)) {
+                retryCount--;
+            } else {
+                retryCount = 0;
+            }
+        } finally {
+            if (rs != null) {
+                try {
+                    rs.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this . . .
                 }
-            } finally {
-                if (rs != null) {
-                    try {
-                        rs.close();
-                    } catch (SQLException sqlEx) {
-                        // You'd probably want to log this . . .
-                    }
+            }
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (SQLException sqlEx) {
+                    // You'd probably want to log this as well . . .
                 }
+            }
 
-                if (stmt != null) {
+            if (conn != null) {
+                try {
+                    //
+                    // If we got here, and conn is not null, the
+                    // transaction should be rolled back, as not
+                    // all work has been done
+
                     try {
-                        stmt.close();
-                    } catch (SQLException sqlEx) {
-                        // You'd probably want to log this as well . . .
+                        conn.rollback();
+                    } finally {
+                        conn.close();
                     }
-                }
+                } catch (SQLException sqlEx) {
+                    //
+                    // If we got an exception here, something
+                    // pretty serious is going on, so we better
+                    // pass it up the stack, rather than just
+                    // logging it. . .
 
-                if (conn != null) {
-                    try {
-                        //
-                        // If we got here, and conn is not null, the
-                        // transaction should be rolled back, as not
-                        // all work has been done
-
-                        try {
-                            conn.rollback();
-                        } finally {
-                            conn.close();
-                        }
-                    } catch (SQLException sqlEx) {
-                        //
-                        // If we got an exception here, something
-                        // pretty serious is going on, so we better
-                        // pass it up the stack, rather than just
-                        // logging it. . .
-
-                        throw sqlEx;
-                    }
+                    throw sqlEx;
                 }
             }
-        } while (!transactionCompleted &amp;&amp; (retryCount &gt; 0));
-    }</pre></div><p>
-            </p><p><b>Note. </b>
+        }
+    } while (!transactionCompleted &amp;&amp; (retryCount &gt; 0));
+}</pre></div><p>
+            </p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>
                 Use of the <code class="option">autoReconnect</code> option is not
                 recommended because there is no safe method of
                 reconnecting to the MySQL server without risking some
@@ -2464,7 +3255,7 @@
                 MySQL server using an available connection from the
                 pool. The <code class="option">autoReconnect</code> facility is
                 deprecated, and may be removed in a future release.
-              </p><p><a name="qandaitem-1-5-3-5"></a><span class="bold"><strong>1.5.3.5: </strong></span><span class="bold"><strong>
+              </p></div><p><a name="qandaitem-1-5-3-5"></a><span class="bold"><strong>1.5.3.5: </strong></span><span class="bold"><strong>
               I'm trying to use JDBC-2.0 updatable result sets, and I
               get an exception saying my result set is not updatable.
             </strong></span></p><p>
@@ -2485,6 +3276,104 @@
               statements on a table where you can individually specify
               the criteria to be matched using a
               <code class="literal">WHERE</code> clause.
+            </p><p><a name="qandaitem-1-5-3-6"></a><span class="bold"><strong>1.5.3.6: </strong></span><span class="bold"><strong>
+              I cannot connect to the MySQL server using Connector/J,
+              and I'm sure the connection paramters are correct.
+            </strong></span></p><p>
+              Make sure that the <code class="literal">skip-networking</code>
+              option has not been enabled on your server. Connector/J
+              must be able to communicate with your server over TCP/IP,
+              named sockets are not supported. Also ensure that you are
+              not filtering connections through a Firewall or other
+              network security system. For more informaiton, see
+              <a href="http://dev.mysql.com/doc/refman/5.0/en/can-not-connect-to-server.html" target="_top"><code class="literal">Can't connect to [local] MySQL server</code></a>.
+            </p><p><a name="qandaitem-1-5-3-7"></a><span class="bold"><strong>1.5.3.7: </strong></span><span class="bold"><strong>
+              I am trying to connect to my MySQL server within my
+              application, but I get the following error and stack
+              trace:
+            </strong></span></p><pre class="programlisting">java.net.SocketException
+MESSAGE: Software caused connection abort: recv failed
+
+STACKTRACE:
+
+java.net.SocketException: Software caused connection abort: recv failed
+at java.net.SocketInputStream.socketRead0(Native Method)
+at java.net.SocketInputStream.read(Unknown Source)
+at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392)
+at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414)
+at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625)
+at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926)
+at com.mysql.jdbc.Connection.&lt;init&gt;(Connection.java:452)
+at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)</pre><p>
+              The error probably indicates that you are using a older
+              version of the Connector/J JDBC driver (2.0.14 or 3.0.x)
+              and you are trying to connect to a MySQL server with
+              version 4.1x or newer. The older drivers are not
+              compatible with 4.1 or newer of MySQL as they do not
+              support the newer authentication mechanisms.
+            </p><p>
+              It is likely that the older version of the Connector/J
+              driver exists within your application directory or your
+              <code class="literal">CLASSPATH</code> includes the older
+              Connector/J package.
+            </p><p><a name="qandaitem-1-5-3-8"></a><span class="bold"><strong>1.5.3.8: </strong></span><span class="bold"><strong>
+              My application is deployed through JBoss and I am using
+              transactions to handle the statements on the MySQL
+              database. Under heavy loads I am getting a error and stack
+              trace, but these only occur after a fixed period of heavy
+              activity.
+            </strong></span></p><p>
+              This is a JBoss, not Connector/J, issue and is connected
+              to the use of transactions. Under heavy loads the time
+              taken for transactions to complete can increase, and the
+              error is caused because you have exceeded the predefined
+              timeout.
+            </p><p>
+              You can increase the timeout value by setting the
+              <code class="literal">TransactionTimeout</code> attribute to the
+              <code class="literal">TransactionManagerService</code> within the
+              <code class="filename">/conf/jboss-service.xml</code> file
+              (pre-4.0.3) or
+              <code class="filename">/deploy/jta-service.xml</code> for JBoss
+              4.0.3 or later. See
+              <a href="http://wiki.jboss.org/wiki/Wiki.jsp?page=TransactionTimeout" target="_top">TransactionTimeoute</a>
+              within the JBoss wiki for more information.
+            </p><p><a name="qandaitem-1-5-3-9"></a><span class="bold"><strong>1.5.3.9: </strong></span><span class="bold"><strong>
+              When using <span><strong class="command">gcj</strong></span> an
+              <code class="literal">java.io.CharConversionException</code> is
+              raised when working with certain character sequences.
+            </strong></span></p><p>
+              This is a known issue with <span><strong class="command">gcj</strong></span> which
+              raises an exception when it reaches an unknown character
+              or one it cannot convert. You should add
+              <code class="literal">useJvmCharsetConverters=true</code> to your
+              connection string to force character conversion outside of
+              the <span><strong class="command">gcj</strong></span> libraries, or try a different
+              JDK.
+            </p><p><a name="qandaitem-1-5-3-10"></a><span class="bold"><strong>1.5.3.10: </strong></span><span class="bold"><strong>
+              Updating a table that contains a primary key that is
+              either <code class="literal">FLOAT</code> or compound primary key
+              that uses <code class="literal">FLOAT</code> fails to update the
+              table and raises an exception.
+            </strong></span></p><p>
+              Connector/J adds conditions to the
+              <code class="literal">WHERE</code> clause during an
+              <code class="literal">UPDATE</code> to check the old values of the
+              primary key. If there is no match then Connector/J
+              considers this a failure condition and raises an
+              exception.
+            </p><p>
+              The problem is that rounding differences between supplied
+              values and the values stored in the database may mean that
+              the values never match, and hence the update fails. The
+              issue will affect all queries, not just those from
+              Connector/J.
+            </p><p>
+              To prevent this issue, use a primary key that does not use
+              <code class="literal">FLOAT</code>. If you have to use a floating
+              point column in your primary key use
+              <code class="literal">DOUBLE</code> or <code class="literal">DECIMAL</code>
+              types in place of <code class="literal">FLOAT</code>.
             </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-support"></a>1.6. Connector/J Support</h3></div></div></div><div class="toc"><dl><dt><span class="section"><a href="#connector-j-support-community">1.6.1. Connector/J Community Support</a></span></dt><dt><span class="section"><a href="#connector-j-support-bug-report">1.6.2. How to Report Connector/J Bugs or Problems</a></span></dt><dt><span class="section"><a href="#connector-j-support-changelog">1.6.3. Connector/J Change History</a></span></dt></dl></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-support-community"></a>1.6.1. Connector/J Community Support</h4></div></div></div><p>
         MySQL AB provides assistance to the user community by means of
         its mailing lists. For Connector/J related issues, you can get

Modified: trunk/mysql-connector-java/docs/connector-j.pdf
===================================================================
--- trunk/mysql-connector-java/docs/connector-j.pdf	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/docs/connector-j.pdf	2007-11-30 10:46:51 UTC (rev 4921)
@@ -5,10 +5,10 @@
 /Producer (FOP 0.20.5) >>
 endobj
 5 0 obj
-<< /Length 2186 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3208 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0E9lJcG&A at sBkdCeZd+5,^hO8W&ZJ._YUKF2RJX.aCN?kM<AN0X<.ERlf,Vi,F?/uQT(<Mt'_`)Mt]?P0kS+!j;Z>__DCsi0=B+&?:F.J$aF.K/*a.d]I]Og/?Ep)bR9<OgiDqRgpqXj^Tq=!;4K.%'B.j=*Mf?Q<fF0AXU*I#o5IU:IqRXo/:P-rmnfe4!shcuFE*;GOo_arQ7bR[rA&#4uCi0B8-64M8J/2J>G`C0eCg9Cb'7*0Vi&2/da.V6N84ZuVPe6^QckJ_leb\48bkoNMZE%m'=:`+I!/UGb35Mgegek(<unn&^uVE at V)W=T]2A^HDKL*.2KCk]X4iK(ihg)IQS3,n2I`A"TC/cd$0lb1IZKqVBn<T`\l>)pH&(?Kh0Z!=1.R%UpjF<6fhn5_[^a:FEcncHe3.TsuZ;l0RWBE<\uM`e2$js?DF4"`D7)9D"d=/fc_;2ctO[9(7e?(o1n9f4s"/plc(Jh%Fkba^<E3pPUMT;bf>qpYfVN)a/gUs=_X/b#(,MId)f#[m:%e/:M*'el[X\H*oI/8>^uQm[Rf;\GPjN)OGC*]o%aUPXTD91F-7B$=:aO-r-l$'uC6N#c>kit6k$XFs.-[&@\^A$['S1BNma"Q.%_+_qJ^"Gm=MK/ct3&r:Y3/0<uKES(EH1?\a;ROpK&lfn?&lB;Zk#Tp=GdRD'&,XfIb0S1rM;:ZW/MmD<%9!KRp1"2D<*JJkBel4LP1Kp>3m/1qchW^R?,(8b-aP/rSQH;JB6U,?N`[:9R\%ee4Q1*l,j7,[go1U at hIfA`gBt(j^n*]up#m7Qk6Rall^JqWa/#GnGj0"5]^:tl[b%b850&>Q12R8BrHi^+P:R3V:q9Q1?=^\+df>>lRMAn-AG.U%*Q6(ADCc?9V+ge%bb/P2I!=2r_17We0&nr)JE0?J[ENNgah8q$;AuCS/3)&[t-YgRI[Sg7bS6<o^)"a0S3NPl:rMXjQ![^U_d8D:'6/$,d4.Vb_CaGff/c>CXJ at e1i[fA3,6Zdn0ARAT@[&UO(d<%^0Tg*BoUL8'4>ehQ#$Vbk+`NIRiJ6[<X*>:Pn[70m7"*2+OQ.]_Tkh<p2*PFqX-6gXOEC>BrLHKh$`N$OH#p!Mq]43-&/C\:T#rluY#4!#F70mb0S^go(U#R9K47b5TTOo6.0=4Z9X#Ua+O2Gs-.ioX4A%a!nKP$fYj1Em9UoFfsF)eo-EAcV\nPmcZ&C8,[[deb"XutmdNr:6&atl:7g?.MdoR7])=F`e'`h;oP*-Bn%%?QO&er(pm[VX9tcj.L`7+o.gb,c'q'C(&A2UoS?)"iY`8<A->6nKp!61!1mJdX:mom1Z8Ac5(r?>'K(+/ttq%W(!<heq.bou<qPVnj&Lf*Am*E_%atlqr:16\XItpKH-rA$+MZLnMc!mFD0sP!kFVmaYaT53hIF_(T)4<*a=mr at e6,c6*cCP)*m&ooXrFNCkLL4n?-(1DRs^oV+Ch4JQ'hRX)^Cr)#r?]']sLTsi9hq#K5DlCtCk1dgpV"mSY+Q,C,'iG*,AgOM/""Gop')?^aL6/V?Oh0hOn=I,F7Z@[MjUq,LmR\"((X8o(Rm"VRHY*E4OZ,6K!_GX-l_p\+_^(?MMPEO>pYScHGKo#1#ZY>Q%/ka,S*F?2inlcj/Os7S=.IC)]$":FXGfutkYR!g*mFekX[SV576'S?P1(1q5!dJM-E8_=2ehJ.<03Z-\@"M at kE2`8;i*4MA3YWu=UDH:b3N(>9R5lsX.4F-tGpWR$2q9nIAh&BuIE?g/1Y.ft@&hsda`Z?u['ui"77Z)1n`lb at O4TSS[05;-X0S(E*kok<s"RV"8JjM"n:KqG;PJ^5,0jq=^qE%$rD,trlO[+\8P's-,\q9CR?fqMJ4Wm5aBlLnekQ9U`nfOd7YKH0dE3 at b,SoD2^/Lo?0r(rkF:,0-kGqe)].g_f\g7U\+%Idc*diZ'nFkH<\.n!BqN;/ZCa)WIX:**Kf"'1[GjEm&l_(IDs&(RPFQ\dA/o-n`?P%L#F.9L`rN?:#I"XFKg6PqR4Fd/0MdU`u1j3Kqjt;turZe_Ys7k3MN(Z:3Jf^E$f(R:Km1bUm<6pJ%l<)3kfGQ5F7B\PPQ"fUe^M[XJ\d#$;@2ZR#eEop#=a-'iA54oW4M0@,?]5'G)rKuMhA3gH%t,(\/96LdrrqhDaO:~>
+Gau0FD0+Gi')q<+Tl6:AfJMJmo$)?@CYbl$->o_;Qhpb[OG"!50H_g`rUmW67<'+FA?"rjAn$ZFJDFiS]?fp1ror_AI5(8%a.5 at FYMB+IIc\jU=5M/TJ)2a?rtK+&PBm/oWLWqaQ05fOh=sPKcX2*Frq+Bpe&M\r<BP2r\p\rhqp4Wn&U'4Ifs(:^@iG&&-F&b'_6Ds.Q`(_Y<NY=O/9is6abP;`7^ULje"p%-PsXu?SNaBk/k!ug+Y0qIJo>lWCVH/WM/)n#?1[<u,MOmre4R!O'\-rE=KlPjYn^HL&SMXMp$qGJ!tmtX%9M01KC)k'CEsSESJ=RHmg`*M@@T<["[cf,a1+$$*Ui*tAs"p`D?@kLSif0f#`nJ&V+]+<WS04Fc,\G,d`dA&'kL\?&mPGhJ7BPfKBWCK=>"G#5]#$$4;Z2S93iJk%8t4g0kD*jZ!<T+=7p at 6="f4.Qj`!&>LBT*_ItiB\,re0cWrmXo at -\-jAtEK0`-Qj3PhZ3?.#fd[p%N/_+*Z-^?gaAo,O_Ug4gD:Cj@;^<d"XuC+Z^fBMO(/odi2cOOt^+M3]\q.13nU%:cCL4cV*u%'ef. at 0_',<.iNY>AUBGMaQ"WH5mBkE&/j]9i83rA&YiM<.]jZ.,.:t^kO%*<9t!QL(k.2 at 3H+(o6.5>Lf.\%>.F3VTnVEQF1 at l&Ra at O%2%]ZPSh2I1K+\'UQ??8:X4Mb*BNea(gk9g7APP'k]nAAk[o.W?68fho<GXMEYf0;<Ar$>t-ufkaV-ABrk$1o*r=A4k%-qJUj*MADE.-^G8B-Mcrhip#9`JdBQ>AS"`?G.!$/fIc9'4M1Hct.hnDV>CcrBpl%Zq?o:L_TS<guhoikUCK9pJbR&1MI&`r7J'@0>-u'X)d4J1m!DUV9j#DU$j(@%SsfZJBes_,Je+M%?=aF&XAWOJ'PN?Lb3>7!s+EE@<T^j=##=A3BEs9o^)8Mcg5s#_>d.<J=5h36r15.(_k'Jo'.EF(3l)97Z?q9TQos`TW3>_b5hR[\t=sP`2WZjq7n:/IkTCa at W41X]r,LU4SKP+V&/gciACY3pC0f*9h$3aK65B$A%6cLl7PDOG'H?EO4R\jT3TF;2rBATHOjh:-;eO2b#Y,J)]FZI6Y$pH3cSEI at o6OSi%]Nh/Y0%Q#:hg<\gej42qa2Y%3CK8r$Bj-*SM$ME,#MTWdQ7\$Fl4L$_UJa8h,7:=T+[=Rc2%Q;;:qOQ at QpV]'%`83C:@dF$?RP[=!M92HnMnF<-Al42?K@]\G'1nL_&+8E/Pp6uku^G%uJH%>Y/o7e:BA'V at A7;[sL at o8%f^M/%5BWb.G-#8L#28!d_(Nnq at Lb7oaIdL;Ze)Y;W4h(;a#^ddhI>Z,or<U^U*D`k"G-2WJ,o7[TK=AHRlh2oG=TbD]aA3iaef_AM!hP%LqXl(*fq4k:Q at 6)!$'^6=Z0IPhH2aXj]rP2lZq!)$.P3s9cu9H2+EFba;p1OD6QbjY;bki")H1Q"YEi+GKeL+ZjD%VC>]C$2,"F<`n)!#m+cA!M$(^Au6'OBYQr#XLIM"S(g9PpQ?dI(kUE2g$U at HQkDj8Z<4N,odYN5Y55[036`M&IuF:5F7?$Ba^RZ64mHTA+2)O0=*-X#Rk#O<[Xc]@s>HEMhqSlZ](<(=3FrWX#Fdb?A`OHhe`W$k,#K at HC#jQ1nUhTP%t&,'9g$d&TmV?NXNYPW8lRK]=Vml%$tQ,JVWlqb"WEdWTsm!O?[CCnl\/HCKS)5'8R)aYumZ]n';!qbpFlKa`$J9/9jZK(D'VRrIQ6>k4](OAg\H-D!B?,rE`5$M at lEcqD(edan1i+JYA-GY1Y$3o8[d7'NmO at ljs-G9\7 at Zs*EE>+NoIE-`]C/44S7QY""r[\>JeqMjD40Cs.r*<ZqJ9_XCnFjPn';td]I^&,j]X5lWXQekAY57QR1HjNNTp7E]>)FK[&"c=#3IK9?MrEWg0H31sY/'OH9Te at 4K*rU<"TiPP&(SRQ+"FfQpgU)oCA&KhJWl*25h1E$1*@Za_4)ZXmELm2nbX/WpI&#t=U+T5)")B12aLB*]0,6cP%gl-%=2gb+;i*g<8ANdd4&</c\=W>%1[r<hm_$oQP#Y<@`,5>`bYr?JGZ`UR'#jDGe_W4M4\/eXH&6)gf(ej^%=.l8g?$cVSLo[V4U%c(gh%D2n-9NmFE_YS$.O0bOW)BEm`ucF3>)7hZ at PeOfEEPD-:-'9**DQ^'l9B3espNbkgmL(OG]sSY&1<pOdHVLW=L?Fr2;"2G?Q.(M^?AHbbNDqo!L18_P#,V\C7MO9lp[SMl6*%pU)3Mp<2,!Qu/\Rht3nL at 55*(a[^Q[rK%$nYQh-ju0/ireRW:U0](MgU+'L)Vb?\lV+(tZ3F*qMh6%(BrWM^G1q?/Wni)9Ksg`e!tnDJWk at QZjC5&6-Q(WS$RiVbi0aE=*Y=JHBd-oC-YjVuMALX^^@o]gJ54c8H\I5'mobe%;I:n/HKE[L<8;c8Knt&qk+Mic=SJTNP5*"h!@a`Tg]l\ek!UG5XD?pWi[`E`H6I]G&3,pKaP<iu)":cF4L/-E[1IiG(>oC%AjMq6?h=1'l'8K=YG at oY^mX&-bPBL?_5DBt9fcLScp&GEhLrRT*$aHS^(ZQ`r;M8m.JpJDq/jkgkf)cC"$jEUq at Mns"+Qg55rm#6p_a5A:dpi*dffU(VaCCTnD2p at j.K!'0a-a9FdF0lGYhfi*WOmMa"nuk$9RmfV:0:q5_^P*i],I3q!mPEdfP1TaR3d.g?4pb:e?E[8+D-FNRTTs^9$$V&quIEpi[Ks;0RXR-Q0n>>s:@[+UnuMpp:/>8AE05dYiYl?C$p_EXfAmFo(8!M;5AsA1WF)pI=t1UD..Gd at u\>@c!5e"N7pP at G9!)r9F$gc``1o3Zf;7(]e7giTu+LK,t#MQ[hhqg+r5q+anH%^NjAfEYA'nNS9%dpp:io*t7NdgYD`,js:60IE8*,ZBuK"P+M,?aPZ%oG,)\>`)Y,>qT54mP:;FOrX_0(i&Kj*`n?ps32e!]c$@ILp5X3RNN[i88$jU9BCWp<a.<<_$H7sQpidn)mD"^$=aBKL\Kg6A88LmR$pV<B"%G3YdA^0ZAgB/*>^uNhR;iq)`Y0jL??(fMXKq>AH;""N'8PG&TX:/=:7K.,*JrL#OQ(Ic/IB+>#a"^r4!5#'/[k14i$![A[0+6q'"i/Y"k,leRnil'<(ehR~>
 endstream
 endobj
 6 0 obj
@@ -28,12 +28,21 @@
 11 0 R
 12 0 R
 13 0 R
+15 0 R
+16 0 R
+18 0 R
+19 0 R
+21 0 R
+23 0 R
+25 0 R
+26 0 R
+27 0 R
 ]
 endobj
 8 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 467.99 581.75 507.42 571.75 ]
+/Rect [ 157.728 605.7 193.215 596.7 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://www.hibernate.org/)
@@ -44,7 +53,7 @@
 9 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 235.54 570.75 335.68 560.75 ]
+/Rect [ 197.715 605.7 287.841 596.7 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://www.springframework.org/)
@@ -55,7 +64,7 @@
 10 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 493.71 570.75 537.32 560.75 ]
+/Rect [ 299.838 605.7 361.335 596.7 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://ibatis.apache.org/)
@@ -66,10 +75,10 @@
 11 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 559.75 142.22 549.75 ]
+/Rect [ 97.5 539.1 145.254 530.1 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://ibatis.apache.org/)
+/A << /URI (http://java.sun.com/docs/books/tutorial/jdbc/basics/index.html)
 /S /URI >>
 /H /I
 >>
@@ -77,10 +86,10 @@
 12 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 474.75 188.06 464.75 ]
+/Rect [ 97.5 520.2 169.005 511.2 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://java.sun.com/docs/books/tutorial/jdbc/basics/index.html)
+/A << /URI (http://java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html)
 /S /URI >>
 /H /I
 >>
@@ -88,114 +97,111 @@
 13 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 442.75 214.45 432.75 ]
+/Rect [ 457.482 464.4 506.982 455.4 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html)
-/S /URI >>
+/A 14 0 R
 /H /I
 >>
 endobj
-14 0 obj
-<< /Length 2873 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gatm>>Bci%&q801n7-K69Xl"nI7h(p0"p)I3tq)./b:*;Co\KW,J`R9YFbmN:aJam:=%$L!2G_Lea9CM&+cj6Dh<gU>P;UA]Wroc.q`>/T/1KBp0hXHQ4m$US&t-N:TbYl9W!g`[(fk\Hi%`kF*BR'j]e?q-@\_Omh?)7a;HEM0M-d2%MHK_%O5;;B"294%l]D9A?!XO_im#JT(Aa1jP at H,iDLG\iN=XKR33YI73pAuDLOMZ<@u1aWC;p[:S4d?cM(PsD.k+pPC at _ik?2[r[eC&C,U+5O[JX5SdWN7lo;ZD*eu_[PQP]DekRP]MhXW8EMK6W+H?f4`lt\46LFN1N'8(_CZ^GB[A at ABT108?ieY]<\$o=>JB2aT?64VU-J*,t'/r&\ffT[R)^Z',)L7lT4NV_Iq/G[4bgsWrNL]*,Q?64TDJ&^!dJlcX]"Tk?3b-I*d!FZ=!A(phJBA;>q5r at mDp:q>2i%:6!<5bZXG\[YFRU8.LS)j",(e"bcnng%'iIBd>DEuJ#%787!a?i(r/[MHA1[C0?H!,sSjS!'U_8i#KWf5[g#^_)hNT*6q)U7AHA=5_$ZH;dDL-`Qe$kOfhl*\T1C3Y]IZB)(NcZiG-[#nl-s0h#e/-Qe?nAn)]4N/d;8q+?X]HBl-FaY7Y0%I/o6;!%>,1Bo1288u5062M2[/jg&)'*%JeEU4]f'-T)pHo3mn%cSl$>Zp*:H'&H at quJj^0[a%Eg\8Q.Fn!Cmc&2V'eC at a7EUr@(DMpA+0 at .ldiPj2HbC6,*_7V%+D5(FXc$f!7_%#qN/<=p,QZ*/(5knO5a+gF`F,`A6O?"U2inc:/>C]ffs:aD5L>p3PBTTcAYOY$8,p at F36`D.XGKl<n\aBb(0coip,sauSOe8JD3e1U$2Z:f)-^W2-/`2)rt":ER19X<i%JBY7Zj->HL$YT'0%-Nq()#p%?CTHI:qM(_D:Gja?T4e4'&IJYju&)nH!5Ok]n`mAD#4Q+%mcXe!LK2+Jp_?[ahRuRlIb1.Lms7?+BVRc\`b;!2SA'Y:s^F@)dD87P*\8IoJl9Cp;MuZ-ssX6&FDKkD0e[cojlSPNHLgMAm$+,HYjCDfYq8RY]]bj:$fFljY:&51LRQNsKsqW$G0/Qr-ZMpO6u%jd]=Yl1GVq8p"-Vd*](I)FTT95'Yp&AYhlsO`h@*$'>F\?>ROr1_[N;==Xl2ZIL-#ok.)OcC"B%<I%STh*,_uU^4Z:Fr24 at f/XI+%=gV8NoXI)8r&1WdV3pGL<GA//9^c+$2eEu&Yb]TmsTRI_G9aY?&q*VKh.><H8Ej`NE*lqODr(9oNpcN:63VT7?NOQ*.h2&_>tWr`As-+hgQ065-4[:Iom%/?V(eVl<F7uQG?5eW^0:W)H"sHA4=@s;(6I<p&J/haX,MD\YC at 9(N%ePP<k!EU<uH8/%-jpg+_G5!GHS+GtnDl at RYnJ/uDlYKS3/03!=pon*_>?s*I!1mS(4LitoZ4a>85_a,@#:)5(iq\0oue>3t)qoTlEfHtt03NVrX!c'sEQ'9i*]L?c9Ge^@_Q_9`neh8#J)oZ\=<L\:_u_`M?h.+mMUOFZT&_8S"$HWr at kf'3V]-%X3WEG\b;21or99HOgERT$V")aa&+S\fNr\7#mW/TgH23jID/D0Yma*o4-LQqY):JMM56*7QZZH1eP2m+=hMc%GjaOmtNcFp1Y3LLs!RYkVns.3o2b,Elc1f_9f:U^CqA,>^15HVb%Nen2uo!6da=^,V%X(74S\K4aG<d3Grr>4(<!Jl*chC7T^#BSAY'iPdkP6Vit%7d#>L1pm at 546YR8=i5!-=ZE+YN78(S2tD`3Ibm9\g5f7C^Y&-iJ[-&,+#-OBX0uXEA69XMn7#KoJll$2)M4g%n46MU7$1uE,0)N,j at RpuV?gE^WGf2S?oEd_O7n:&,oY8O%.'G4\^e9)YS32&Lm]@b9qJH[X]kQRRtP<*bR.9jNI*\3K\B#S*ZG[.7:?8?;BTE3n>DBE>0upIhG&0O=/7,*qhGbH*b,EdMIj'89q,K4+M2O(/fqmtdO%g!hQ;l.hO\0r-/$, at EIVKCE;F*Z)nia&,]DV/-'oWDc>a:G2C0OB<)m=7WMq"<D+lD#BK%U+--oMdEMg.[ql5=BKgXG*095<*Z&gZk/RER$e`[!P#"F at _gXR]SPsSd'`I7&#B\RiX_Fc/!Yh2%gmHLCI7q;\)D%0;r1ioZ_EeD7NblHpb?<j8j6^&0RqTu#X7WFW!R%k?G_&Z,s(ln)9a\'o,6aAH at L&HT,Vc,!>E;-LNZtW5MOACoc-OkEW6h$r2YgnR7o#<o#p';;aim/gHoljj0$rVhp?_`p!?]pnRfSX<PNV*RP".=8XM.IXK%ruW3FG/DW26cN=9j,sWr5>blf)Zp&S1?,2R0C)i9-m/m)PLa_489XsrUu$HTjO0Lg-&=.L=HQfYBLrWlqc4[,Fo9K&i\"Mkq\ALSIui>RQQ:W;DSri^CC_DKa%"k[Lk=Hd'a at 7"odl+b[aI,d[ad=TK)K[40sqd>@EIOZf_fYp5 at T_>'jG\n&auQHf)`[r=Usb:r,-iRa)X<&[4efU8tu/]B%V<2;:(Bs$MSDknI"sZL8KJdn-f.^)k>,QWKdJW3d<6mE1A6_Zr\u2!Vl+_N4c'X#.4"(!l`cPY/;9/Oq])4=?loE'0oY(gYBdZ?hKa<;X]Q$^R[PNmP)JppO)n*\3J8YQbLY6F5X4ju&b2ppLo_=O(S1N^Lu7,8%Og!m-%imD8j,.O8[@f8hMO'?_0n,t?u!#MZR$9P#r<WF..Bc;0YA&b*Qt=+BWr])0&NC7ennGLu5[%['VR=$L"SbYa5b>>?0RS'e2']l^T2#k-h"]7N4s\p-Gl3sbZjb5^dN~>
-endstream
-endobj
 15 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 14 0 R
-/Annots 16 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 454.5 435.945 445.5 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 14 0 R
+/H /I
 >>
 endobj
 16 0 obj
-[
-17 0 R
-19 0 R
-20 0 R
-]
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 359.247 435.6 559.731 426.6 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 17 0 R
+/H /I
+>>
 endobj
-17 0 obj
+18 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 433.29 657.0 533.01 647.0 ]
+/Rect [ 97.5 425.7 164.73 416.7 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 18 0 R
+/A 17 0 R
 /H /I
 >>
 endobj
 19 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 646.0 266.07 636.0 ]
+/Rect [ 278.958 406.8 460.695 397.8 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 18 0 R
+/A 20 0 R
 /H /I
 >>
 endobj
-20 0 obj
+21 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 138.6 366.25 425.63 356.25 ]
+/Rect [ 201.738 387.9 376.986 378.9 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 21 0 R
+/A 22 0 R
 /H /I
 >>
 endobj
-22 0 obj
-<< /Length 2631 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gau0F>Ar7U(4OT5nA>=^!jSK3jT0OUUmIVV:"t6TBa0cHTOlhh.u>`%fu_"$6V4]rC!q,bfG8-gM3l-KgqIjXS,(%RgM#ZYN]W]50&4kN_Ag&Tf,K"LHo^_Ea.&%Mq<6-@]+$>uNlHMn5J3q0,&1/[LGr#A&]<i+pH.R"kc`D3<nfC$HA=*0Xd%qbJ-\1Eem(55k/27=Tm?;8%@2T_.?\>alA]B?mH^6C_ru6Tn;\2n9uWqt^)_-tV'\ZYf;lAZ"K1R=DD:bBB,"&;BJ^@fOUan@*oKS7mc70opWWHE>bo[2rX/VDAj,rui^CpAUpAc3bt+[Y,P<a"H(!'M(,_QNQ#):%`T\YQkVJYKNrB,jPS.db[+;%!2I&i;KRn1!X7t5Pfr+03Y<TtHFf+G%>&9VbKtQmtf9Q\3()^tH,rV=?+5Yfu6[6q7k&D5Y2"Qa'_r0QkGeP7 at .2X8c4l\!5ZDZ[#$L^O0R"Ni]2Z_[^ba]CTe4fcKfSSWXauN3GpkiYT7p$#$^@LjjkPLrsFFM6]nn+dqT1keH?&-hg3qR3HQ+WYD%.2&.ZAMj[S"]"f`YYXi([.8>R2S9pYL&LS9FdER[epG[L(bFNcq[ZL:9VrBWUFV3oLPC'p#<lj[hsGf87[B>I\_6,)'2X3Xu8,[L>g%.[+_09C)B.I4>06hj\c3XE#pY>Rat_Kh:`MJ:-2a]H`e!u3-Jb<J`tkp(AWOAWCr^qCt=+U*Te?rf+;JuN:5^%0E!59jqlo.>UPBe%ii63qVR"i9jp=E%H63K[=n)"8WmU0k\+u+gVqFM`?=Du<b7+#YaIK8KP=H^?P"B(pO33AGShYVp91Vk;K&^QIar&LEOscN=D"H#T:hg6n`$bBJecR4,(B@>.6KK8U;ZR8^D$"0PpdcU]E+iF'SOOn.^mF^#qK#7oU at U>&8AH5_ at D%\J`<:24CgRha.5D[>gs,K#13DAs2<X_S7!7YidB-/e/).:q$sY>hf[bu'Ar7C+nZPm'/O9#-nYoi#<#&<2\c\Ua)pDSa%d3OUI.M$o\oN:7dt#,A"p<-Whqf!3kVBp8![/<JnF/d)a`gO*+L;G4+cS&;X(fkfl->/&0Ni#_>1>e1R<WVE.n=mb,CaC01TcHo/t"A'W7)J)dZNF<mJ"pN(ofNfPJWpRNW6,/=!?b_-78d-2rnB>k57aAt[sYTl2s3Ka]KiA$4(scPsSS9*U-T at d?K3eIE89E=#V^+r*8[2aJ!L"pcum&7DdX,De:>F6j;_e;;b\Fhrc<NBK3h"qW6=6l#FN*#Z]1EO<,$]bPNMKrXLUmVcnC1Q9^!*OD^r-;a4Pf:)].2s2?2M;RZnm"E_.K+sHH,0do+Ps%8XEdY7SKC9&;.to%7iT$Br3;.gfR'd7$+;tBpKj94+(F5n?0"t=.:O'AnC-hpdR&E(hV>\P\FK<pT8h_Pc^#=Q7s25CSJQo&%U+E\ed-!Xtacb\/XI4t.k[oD^qB<udp=Q(Y+6hu?D)*<\1*7d"XW;@Ur).kE-dK'Z,W@\--Rm<3-)`Z\V.,naLlC?=KVGg+-RFcfeenC&]VNBQKJ-hs2f38!W=@%jf]Z.OJbV1^dM3Z[ZUfP!'Z)UOLKN(%jiu'Fh1rI@&DM2f>qDCWh&9i2^9SohUC.(DOqf,"<+_Y^:KKD%J-_qQg#+i(@SG'*Tp-\RBMNZ)1\M."%$V[?i0$R!W6@^N`/)1!gD$kN-LW4nISFQ4dfPZ[8<T0C*Q$,M#Weh//IJpZs'1nm]5i_;Ut1fqs7d'WV-Kr6@;-`U at jb0`.nTSsXDoJ8=j?L'Cl'aXPi[oO<X<H#a[(F_iqEX^7ZEis8isAScF95gNJTK4i.mo'T$NF+#D?'0Q.=PtgSDNt`[5_(/Rd^#TsbO`C+<6eS!W.&P2fZ[o+C#G)20:'h_Oau9i?^s6p9Y-9ffEN49pm+K#1\Q6+Rh0fP\TG]`MeX!Q.8e9XD\7CR"!a<=?6K'X2=<MMebn?f at dM04#d,/BnY8TKmM"q1i^S>$\$f:LE1`Ao#&V5=M)YNoQU>f=\*PR?iAI'YShM[04cBb=WpH^`[Eh%XC%:Z&`sh;W$XHE$-d5Tduno2+E6.Qtcas4n#$Q1[XkQ^q-ItHNWXmB0Fpb[4Ub;omu%Yih7k5N0'<VDH17I3hI]+NZ$#tY\_MuUY%gd=NMnj8QaTN$G-G+01\MnE=Ii>`3ht^g-P28_8m!)V6;_<m4^2PSB7RFiHYpKejen\m"i at M>cn65p=@[*=p^c2UFpb7cQBL-(#9=Khn-]8qHBI43-!SX5]a'n)sl,_1VneD2bH`$ns]7VMDf`P1$c[:N0<JJr2&IS-/4]O=J]lrA=d]k(fGrWNSaY#VcS7GS"lbAq#b(f+TAmnKgN=^3CYLSOCF2V'N>C*#fm/^/QZ>t+gRc>_9th8Me8!$Og9.!8_jZKJH?]eE*R,*7u6];"e(MQcT*,QC5TCA?*&KA>H1s8aM:S0r"\2an]jI7_<h^[cP93)'HK)h@@5bM>5[#%"f%$:8g6Xt.TKi.]sUA?pXUCBJfZ?NI74Rm8ADB<8[5bLY5'],5kglk64_.PfKKBNOaZq'f4R=p=i^a&1kM.5mh\d-8kl5)A9.7&!/foP'Eq7aT+DDMr=+d&,Z+~>
-endstream
-endobj
 23 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 22 0 R
-/Annots 24 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 204.735 369.0 382.98 360.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 24 0 R
+/H /I
 >>
 endobj
-24 0 obj
-[
-25 0 R
-27 0 R
-]
-endobj
 25 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 317.18 453.224 539.94 443.224 ]
+/Rect [ 193.238 319.975 216.737 310.975 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 26 0 R
+/A << /URI (https://kb.mysql.com/search.php?cat=search&category=10)
+/S /URI >>
 /H /I
 >>
 endobj
+26 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 385.802 310.075 396.305 301.075 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://www.mysql.com/products/enterprise/advisors.html)
+/S /URI >>
+/H /I
+>>
+endobj
 27 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 442.224 194.7 432.224 ]
+/Rect [ 114.2 300.175 329.255 291.175 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 26 0 R
+/A << /URI (http://www.mysql.com/products/enterprise/advisors.html)
+/S /URI >>
 /H /I
 >>
 endobj
 28 0 obj
-<< /Length 3146 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3177 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm>>E at OI&q8_FiO?-$A//B%]0Hs9di>#H:3s/IoUsqD>,'X8OX69gG1Z]c,pfZQ,E_OX3D"agZ:3#cF8KRJ]C"/'Ij^/ip\`!RJ)r=)Y(DA2rW\9'e/)%JoR_1i4YD2uY2k0)S&K63k\mWmY:Hi?Q2++ta-&WS`?XJr<Md6&mD/u3C)6h0D1;:kQ at a."mi-?,M91BQE at P^l8\6YuJ%0iD<r8c<Ulp9C9V1Zj^`s=h;rhQ("4E8Y_C:`']35':fR?fP71,q&],07#]j at RO03H16DLiUMOV`7gLNKA:pej=qP-a_q>h\f7BNBcbh=>toQ:euN^\qgu7\4N6S]3<i"G;*A[b.b+,k[J:>t;MN9"(5%/=8,`F"5g08TQuI&@NV$rhkk$DmS_2'?PD$F]m'7Je)Yt6o[*X_>[/7$)+!O!EUIpC-QS*(h!t/#7Rm?.3Btj$!EdTWZ]`d1\,PH at -04m<hdD>(^PcFg:&e)S<Q$[!;?BoK)i,2/".Fbs.s7/PuG<G;$+/f,a0p^"dS'V>jBDB'eBT/e=>(s+!@8=$D&IodCi/ONP9;5BVBsDSB?SQ/lX7^0Yna.&-SD7#Ok18UD)@aI2ELK,E*]NB2o%>M=Q)6O/1"8cEo^t$a$;qi<1EuMuY/Ik[pPo'0Hr.QSUAmp5orC>e+JX%4;c7d$e$.:<BI(XQh.uBMeFe%$MhfUsluLquYb0GgR)!;g6nk%>6OjK[Iq8emgF4GX$<fZ)#\YR^A&(i*A&'T4h*S^NoSA*Oo_m&SGZj?OS@\/.XH'i@(Tl\aC`Y3XAgGTI_X5D3T[koHX/!<&J9-+HOm;X!:W-FL at hR"X[RRJ`]3\"os!Z&<]L>UaSL at W,`-mBkf_U`#XjtIfiL6j4"Q5)@XsuG%:ao0o4\KeprnX?L12J;#R"SBp^Fer*a$B9bT0_U4%MB$Mm#pmRt;ble*gmKCkeBW[M'BgH6<NTdZEn`=Zu[PKUfPgp"&[g7KalFu,rJ\[^*^+<8Q,ipM3=`stKU=m9&1YT)\',3";SC*P&c\_#-h)UWjBFfrHo.S*F@;BQdUhW0eI:IFL/hTdffW2clCLlP0ooDQqP-T]4uPqfNh.TsYc`aL]LWhtd?)l+b[T/.b!:mD:DKChM;#,0LsNJ'W`LHmfF<K\ITk^^6$C1H at ri<gaWq;\Rt6/QW]*Ni`;+%%=/?Y^uQ)oA:D'R6M_].lOU&lLoTSRGiY,lsYaR_#X]VMM.=@[ne%D!."qHFPA+E/E2$^KUHZYZ='a`o4P5dBfq8XJTWdR%m!Q;FjFNM;H,KF(,A</!pBYPVq^1l1qn"[T2me9EQd;IpqghHIoJ](M=RuJdn]JW*TX^>qbl;Ee*pt+cfNK^F'1YV9Im.WM-aMgk#ED$iK,<:\IHs-\eIWa9gLXe9.J-!2`Jtbc7O>5m3/p6j&F*;r+o>(+6"C3)uG/_Q*9&S(]UTCIJ^8kDXJq6>WkBU=-P_J3G2'._KM7U%X%+[m?!!ij@::-L>J;JP0i+S at e?#1SSj73`9DP:pB^8e^</a"!-16W8c`%7Km`*5oDto,Fr=:_92Mh-+[q]D2[EIDosT/k-;']9LeHR0/%t:.eV4,4k`teDVKYqVL"hXNh7e13nak"[P7=-*[j]/O/,$<MS]ZGIb;dmbB89uaKa.HYW5GdVNN4o!8-C_<9t$h>1Xn<H\N;]+t%rs:.III]a&kjB7\B95(N^M*-+PH53Ae)]aX*O($J^KRR5`-9=+c]O%OcUNL#pTioGYl:Tbc+3MSHl^E$eLH_NoNWA!Et"&Y4)iBu*$RY`^t$QLVU=:&[H,ltooMK at 9Zm&933),,E';a8Eeq'NZ0?4IPd_V%kfmg.5CnBW[aCFY*\piENW[V$;^7p'"dH&?&u=*,M38StMkg>2DSZSS<hdK6jI78Z3V(1T)tGBVr\3";_If6?`O'Au'iM<?>$^JpF=8MKFj3d8?REK<lXJk>dTLM8l1M?EkVB\`E+]ob'oK[e]Y^:ui67cI3]F!M^^0bM(J at UPfl!H5$bNM\?"<XW!(]A;'&k/61/.dL7Qo`..ObPUmB1g#&Q^2)/l3)?VrAU-As+d`L`$fZ+IY**^P7NK`in6Ih at lht'K3_)s49WKJibo:3+74$<72Q\rM?sd at +3"3GJ5tWU2(#67tcJTA>-MKP0\26nTg]YYt^Y:3_`GW=^oUH%J^EX\d8j#q\Oa:p:MWmX<Nk?'Ej=F7j<L(6e-P;aB84Nun+dX$8MM=`7otns8*B'fNqd+=,*VO%3FJc83Sg[q1_egeJlpTWNHZC&d8O`^^gH9];Wf2e_*bY,-3o[4im9DFM'3%kgWS+)*)<'`qYB8oXV\X4c1im93=pIaB+V<:[']aN82*T;*0dT`7H'@eBZhh9`EBi`=.Q"NFT=G:kA`ZnaeLh5g#HrAONgci2>WFnHB%$*`E'?N,S5o72RLaSAgQW. at H\%.V-ZoR/W1Riii<[6CnQ#NI]\tY/6CZE"pYG)<qbSMBHbj_R_F94u$7e4^s"7EWf5j9L4gK5 at 5PnTB`?G>M5`TKbpLka49EOBJ/8EC.Qa'WK$R\a1oF68OO305N^i+. at WaSOKB#28Km='i."p#QAC83c67Jhq)lcfem6$j/j"RGE-CO#j\cFHF;7rNpfbkS+ at b11bDE[uJ\$C2:"((rK1n7;_(Vj^?n3]a%dk!G)L5o)hCG6$f3_[:Ys-TFUof=4;[kN-@>ZN+l)@#8<-rIaL2B^;gP=3WVF#_^H07;J8K3H]sH7qMnq<0S8Qi70s);_e,L\ecj"9#^qK4K5>YMpB:pa\TTmnpc`TNg="12hI=qrc\&#V`SS&U,eNLT=+JdBZO<_/+>MTO^jJ8="TAU7AT<GP5bFcbe]gB69l*Ul_gTCBF>l>c*`rQ2J1Wer"dIXC`>^TTo0h"%q?);`UBRdE"[&:Igj%TL>s3qUsd,p*5c at B4;[AUVk+_A6tMkuNmSG;eUVL:lUn84Y71M3>q33 at b+G1_mJP#](oE$iD1R4(,3gSr;GLQs_[;bGErD=o$_WDu1h+0o^YOA6D%"'=ePsaea3L!/(6oEbX1u78mbShA"-n>pp9R at 9$VhN8X'`$t[NIl8@]L,5)R"n)Map;\2IUrig*2u6Ln<WPM^7lQrX#j_kT>0t_L)~>
+Gatm>>F1gK&q6H[Th"u"X,!a+m#s)lMq?hAA1#UiB(F<#QIIeoV#cRr^O?QA6rh\f+Zma-,QiY`lFMB9<AguJDf3<#B*iWkpT#6G[_?`o%fCB\E<,_Wa(J!-cgJ9Khk/_C`:M[;$Q]$$=kY8MML)e+6s=K;U<#_\gtW9;ag)b$X at b?T]Io+/UiiYSh[VSV2>eIMLCI>Z)uSI%OVu`JCtBT.H(GN_ogT8YCqoC/dH\\ilVZ6&iMtUg<3BamgaUBL@"%*B^de?kh7odE>U8grUCA;(66T&_>!UTF/Cs$oh(G%=Kt9+U37=n`eZ]k at .s(U$5BFN`_+2KB7B9PO0g?./`E_!nCLonR/)r5=oASt%Dk>q5?*i:NWc"B`YB#LE])0iaMrX6_5Mc;9mXkJ>:,(CGX303bf,rjZ5KP+oRg`!bS^I:?^nTlc&"XTSMlteBHLm1`N6=?WH+86H<YkM[21b(GrK%$W]\j-%W?.WM20;/#:Xd[/(DodNj2I+Vk9U6H$&a%adIRu;MhJlSmSUhsX5T&9Z9_oWK7DRq4<l"f[-L>L>8/,iUJ`A5WJH3GA!Ht/-EC=lAQ'75H at XKYrKb#(c.%&o;C,kq_PJ3a""f at k2L5E=P[HlYVkU0lL5:;#1ITtV at P@:m[j at u!f5JK5E8P*\$(:nP@*.?6N<.(*-XZsc3I/>u7&34ZES%XuC2 at X\XXUDHTjNUGJM&[36+tpmSG\93nTp/B4.^o\CVbu'E$7HK!Lg+.i!O-]_B?/=\SKO[mUq<'2JO!Q&0"-a?AXm[6^B7GroJ<iM`)J5%CPn`*cdI<E'#Uir5=6fLOS<<1kn.@@Zg+V9g/W[ZKfc+h=822Q*Li\*Qi at Eq%-)R0''tT,Ia?0T_pGuCM5h&>dB2N<.jpP&Ao_7Q?brpM3A>W:ad-jW%+RK at ZQ5`h%>.43Npe=^_ia0iq4ueW>eiF,#k_,Rie4R;.UU[rkkbfH8l15jLJ7gq0rlH5(-LsKt-&G2eHs'^#/>cF7Ksm*6H(>>IE7DLZ__+LAtTF$#uN`C"1YtM5k?1E.eKg?Lhpdb*cAR6IoX%H_mKB:a5HTWlDHGk^a:(eqAiB`pn at R2J^6<ASj=iaYTNcO;9fnEM*s9A.&)ao#!2Wb[j#@)lSE\(@cXGm@*lei=,p-eJ*eUbB$fNRDCfN#<)7DV]/fgnb]8fW,aOIgV0QcgR43P8QSfT2V[]2bkX$7V#_e]OP]4ma$(_Uk*7q8Pb at tD=^2ulS]Q<0,T]8i(fnr=al8N*IcT&A4l9P$B,=STWM%:k9Mt^Oia&X]Wm7)/dNUDaD;pY;-t)(q-icCG6%&$T4OamU6."HS).$[Cch9YRi\99Sp\%sa*%CTPlm,o36oYg+;Il]DO!s(t!e;H3m?@7NJ?IKrad06j(h%F6T-N*d&=ekQ$2QWlrV-q`SItapXXq$j?9r\bUTJ>$XVq,rb.h(I:?s8qd>"A)$4-_;H<kFM(I=Y#."sr\[6?oHrJcacF;IthhTUuAN$,*\k%rXo at 8WKe!F&lDB[/D<'fAHs?DcN'/>rk9C2kONq-F=i:J<_BqVB?,]77T.s7$.(MSM(F],,*/ILrj<%gM+1ZP;+!T#$V#GM$2pq9,g;U3,"*`ee..*\Us)@iF</1'Efl9;*acj!d'`4.l)@Pp[g7f:e/.9TKS(0rYRaj<i)4)AlO)7#CBC])CTGgmOl-J,"ea`62mqn4Zl?ca<hGAbH?M(H/Rr>8sIbC")9nT^)ntqJZ0WS%E54.(e$K0?^6$]Psl_#"(Jo:dPUD_ON1]7^OLrXb2n>ffCgMfVM5:";9#k!"uY_0+=>_ct-X]]ji$?GnYp,FM+C]G2d"&X-HpKlC7[t+H/R*mg=],C_/WN96:c3iDp61`(@$nqqTgAGR5Q);[,W,&Pe^ERflBi1M`_kRObiGK&0DPT^:pj7*VFQ]J.gQ"R]OHg4BW[<_mO2B4(XY7]h8\dA6fBm_F`X*SinFQTIF&^WbGM@=FC*3dLsNPDBBA`:*Y%bo^G<L1/.i?[*&LJ:6MD;I9l0:1(@G&$epWYmiiLZ8R]I0kN2GClka9BRE-,e at T\Tp2CoSIuQ78'J7&HR97CLBB$C(B?o+qh<@"]ri-hR!-P%Mg4$/6'0R_H*T9:W)H'jCT?_(Z2?83(]m!"oX:;?>`)O.G!Qq9)JL66oi6W.CmMsN,O#rBbl&u3qUl3oW^;j\mF'WY*#%nPd\_('M?^-TI:oktjW_#bo_)*`2J)`iB,GAL;["f=eX;DgYQ,&:(+VQIn*:ITY<^J<r04.Ej,t[,Jh=Eb-jLncRje6OXlb[7g,r-nerF:))I:c3R\))J2b+_$UYuX"K0]$bRk'5J:&S at e)*VaE$gO?]h&Y`"5"$r4*3Y=NV[)Rbo#;]e)g-+&#c=mJ,MG0Mt)$BJAqiO.'&Z[#%UFnea'ki4aJ=sR\VF--)!P;.m.pq&93t%?B+l-G#dT]Q6Q63L6W9VQf+$dn!4A?;;Il\Kc0tpMd^YF,:0/S$tnV=7dcb[V^[O2-J&\PIa0k&6A4-&hOUrcA'Nkp1Hp^UQPO_fsgm:SPC0luP)n5-BUFCpOJ)#'UIl7L'EAH!o>oCfg1L#6gSTT*r*)bY at -gL/\_5Rh5DBm!Of[NTH_d=%XEXtN5?)oQP)CN#csO/5Pn-YfE/jBDGf#[cr@)DG%:[QQ:cd0M!hb7$tQRc%$O(h-*2rp\6 at d4\aa[V9jF+rHcXDPVf!na0Ih=6b>P:e\+J&%"fi9S+lkO[2:43C2nFXaq)b[S8sA^tU<ldE0mog2d7J6=XO1Z\JRM&DKP0icN[eq&,caH5EHRrDNTuo>I[*\S,'i:NKJt\Nm>?q[iLte&<4.cRf3umDNF/B.XpNrkA_O+G2aJ3dU90[a*Y:T49j-qT;/OS[@B2g+MRQ_6V[Yo- at jai_tbsIaM7TiR+>5*]qMCO#XD"pAcY@,`H7*FQ(SRm<H<1G1;RKQLpif5+5V3fZ($mF/DRQOS5_%PX4633*sr@:CPr"JY(9 at hF/DfK8\ib4HI1Apd7n//V_HE!iP)Dds\\$7*LK>F,#PE$jC]R9N<Qe'bf2EMHC!-lf0FSG5Cjir=RiaIXgtP_0Xpmd=D\MHM]-jl^r-n`,'Sfo?F?5+V#;Co7>:Y;#?G*hk_2kJYM>">m0N>@#`MT5#(YR8cK#OcLZ_~>
 endstream
 endobj
 29 0 obj
@@ -204,254 +210,268 @@
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
 /Contents 28 0 R
+/Annots 30 0 R
 >>
 endobj
 30 0 obj
-<< /Length 2530 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gatm=gN)%,&:O:SkcN*g7>j*%b"NjmVjU,&YN>"jP!2,V,U"I""p"&\hfj>X\Ous\j*!P)"H!2&cgIAfJ8[=^=7>.:^[N#Fc0f$qgL'`^"oS-O5E+R[EZKCR1><MLO5CnqR<^ZZ#BBA3pIq at .j2]L4JU.)bmbR$eepB:OHa``(/fHS8//*M8oMZ=>rC^5\P/3<<'kkHg^9..iG2$3]R3#Cb:KLYRhV+UiR>`ZO)5`@SB['%U%ccc]P`hrA<*(0]LS<mX/$&]<`P-=om`*T8^sOtb*k3-/6<[IJM&]6QXPhdiq\mFniC/ZD*Ph\pQiuqqVY7>FW6</18FZfpS#8bPk=lNN_nuE4"P$oE:R:b]k%di:^Ff.!B8->,j7u`sq;2"mPU1uB*Q'/m at PUrVr6kmJ<?0uAnl"n`aLD=i=fR)8-qlr_oeaqE%]<u'm+L*,O+fUPikWRI0M6o2p(F!6s34sZUNjAiZf8>Uft%L>1Vshpa`QYO#BZmKX5T$S=5`of9u4eJb3ob`%S%RbC!O)_0h&u`DJP[%4M)m1T2;72Zs"o"r7(l_3SWO=Br]kkL7aDUMp;-<r<M1/4TJ&e=3aZ%.>/J>Z,\.s0*bGT.Vl\C=^reB-/,r]Y!W#Xkcg?a(QF!q7b+KS2/c*W!Q]<Rq=264Yps(FPKZC!>H&6e=4gqb`</f1_Z&ri#6"IAp\RbHnG3?#'^hd673WT:N,,\b0&O,D7o`1j)K at A3r.#'mB3[V+<Q]ndUTo(WD=f5s7%4GTJ2Uojg^[?t%:D'[K;MUQm2 at I?.B\i.@]=>N%<=&l`o6&b[e6^cFp4BD)6^Qo&B5OpP)\))!rg%&"1T7HTN?0=FZ@,E#Nlh(K.6+/dXs^<H'pFNggY$Q2n+uaji)"EN`PBZfR:_BpHC,!bW`\4bFImNBj"i\3^ZM?P0\f)TGh<G.^jJ`akgEP#YL;h[%nS[cr0gBe4oPJ^^K+.ZWj_Mg%OgXmJIHu&3H#7O5F_YG><8?5PoQh7ebG>Z)m,MmSR&Eh<1L"]AXc34g0!<.#`.BU+^uSO5(KYWhcCii[o">HKe8SGf%- at bX\CI^Nhb.qY'Wu'U0kTj<%o9G&BBY%+Ye>8B at CoEIm#3^s%(Y/G0!H2sqF)p]+.'I%DSIW?Y[%;_1P,K at rarLHK0$C=[OXoFtQAgF%bnSR9J/6i"f[Vc4'iHFc1K9%u/daMM=Gch$^iGb^ttfH0 at GSK3i(4<RPO\IfQ/Hjd<M)q)&)^G0E65'"$X8^F*i%Pt_?M5"[fmq\At9sOLNEA.RbC>&;NQ4&<hLgP]CU3K>\W$/iERcXo,W(F=sWa*>EfYOQN'ZYN_ at Nr0"K.n9!+<J$2HHOU?<7J>"7YP,V03G^_.sJe*!["i<V1<U_9&aqmAC$g<*Gj"+_;P648->9:hNSKB?93ZtQ,,#p*1lWAP<oB!"bHtl'*]'B+naXa6XoWb+$[Gfl#Apkp]EDcY$^c/W,Zst+Y/,!hgh0X?YTIEb6_7.Deh at X6J)5!oO.Y;aDa+.+WR2#%<#hA\aHbVAfd)r;0R#N)-ok#eYXmj)H3;!'VC='Nhn-Q*Kp*Ljr2U!]t&f(r6aedo,9W-=28HDTU%CsVjFbhDrL2r/WNW1gjFkmmO?G45eJ!HQl\VfQV$*;gcG]LAg;@5p.f.(eP4+]28'jG*nMn'\3)",3#AehDiN$5B[fBhqC!*TB?\BVO?"UG\JI%Ia^K#qlehna:/dOdO<<&\d8%0eMUNQN\K?jClHFOh6D$/.4AtI&H*OoD7(h/,<'s6$VD.kk%aL"QT.?X7 at Vj@+_$V0B<o_jW677]c&qZM6jHQlIAMK3khn-^/aLo0eN\U!`%T$@YH>0/LBUIoD]OgC/WXSfDNOi\b;j-6AZ1P_P9P0j/"B(bPp.'.A)Cg%p-<r<_n<ms7J8==FpqZKYj at G9L'MLpLf<iTIC-!a#/:YMBTSAL5IBN)uO%_SoA'`O=PN&mVWu>:,&JA-PPf.>G1Z`$C"Q1$@<AUGGTrTlg?C<N:,+iqR:C+**#:fWW%\2HQcKCD#T`C-lDb;#3[V!5_Z=?a7#UQ67eN5Z'<BgSC$@u3]IrL=LQ'6@:.")o6fs"RH!u=/QMLVUZ(6HS?XfqUn35"*^Z+9>t&usg^IJqTcNTl"ls5</r%Z<E)ZjUB,D9[bafN[?.[nenZd]Lp.e2_&JAq([,hAqQS?=."&_(3if]b9$Lec';cBTI&C!#`(/X''6U7_T8Jl\_P")A$Q,M/!*;NDQ%7-iOc#@0l8M-`6Tf!82Uu2:I7IeD,=H03q,aD[-mshYoM/Tq_d7%iYhM&;+<P#)HhT4`5bGG)?"JOs\O2!U]uaY>?5@[VAmhQUO=q/+II<$Gdtu([D*g.:81B01[=T*;@i>%<EFsE!NO//BQP&a^+XCiGL]O3mh!!k7jOG8nW(A)_YJg*!MF[!I!X6N^n^D2A]`3XR08IClMjio,4 at Y9_$g(iqJA#%B].H<!B4c,T?]IGg6EG:9[-"h.S4M70q6"d#7`@ic1ZA!L4Gs=9~>
-endstream
-endobj
-31 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 30 0 R
-/Annots 32 0 R
->>
-endobj
-32 0 obj
 [
+31 0 R
 33 0 R
 34 0 R
 ]
 endobj
+31 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 365.961 546.3 566.688 537.3 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 32 0 R
+/H /I
+>>
+endobj
 33 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 428.28 180.75 537.18 170.75 ]
+/Rect [ 84.0 536.4 104.484 527.4 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://subversion.tigris.org/)
-/S /URI >>
+/A 32 0 R
 /H /I
 >>
 endobj
 34 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 334.11 148.75 419.1 138.75 ]
+/Rect [ 251.571 315.45 509.898 306.45 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://ant.apache.org/)
-/S /URI >>
+/A 35 0 R
 /H /I
 >>
 endobj
-35 0 obj
-<< /Length 2560 /Filter [ /ASCII85Decode /FlateDecode ]
+36 0 obj
+<< /Length 3276 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0Fh/h%)&:a.UiC>rXWjX1&nSg8qi!otRi0o"e%-'<>@d;UK58sCUT4C%RY&#I\iHTl$OnO-O].<DtIW=u)2Ct&Z`dDo;)Q$p\7_Uc5/>H?M<;kr_PZY(Ch\oj/PhF6LS)Q.GgSa$S-=OJcX/oLC;0WbO8Ku#,c*)V%)W-ML#>&ub+@\ML3%gpp/raX].ipA#-f*sn844N;=5>f2C"4em9j5NZDOmS7[g4o:]^M/#"b%s\U0EGLN,l/J7@"^O'`P*/DBZ/e75Tpiej5'FW!Yd:K!M1D)+7$sQp(jF57`ore#e6*W)X&fDQuh*DXluUp<1Ko3SU&%k/.J(#`.tH74"$S%e@"MK3C=-H-gT':,=V6/#+>I at _aMp:<Z*CXeKdi)!Pdm``LMVELcHY]:ikp;Imbi at 84:^d*trZBshS[NC?V\D8i`XjT\n8QUbVOXKN0Q(j5pbPabpnhnh-p'Y8ud1!CIOAZt4?4!1=Yi?n/qr7>O5[;i3fjq=")9/Q"LZg[%-*(5];ZansTHZd%*(9_#[]Y4Bs#m$trM2dPHEHnVheFEq/6R(/1D*Zq6=b(CrWMgL9V'ln./A\Nj4Pc#;e?c(."FpotcWuW_T6=)Bd?C<p"EcfYLTHq,P>R;pI>_#':>on,K_;M!E0`N;&Y;Eb+%bsE)uN7uD^W35W(h)U&4XNY#oK!@`A at mqA:arU92'`;Z1&p9ZRenY4:32<bHar4.4Ba=[9^lBmOn75r-!PZE+'C*OaeXgrMMk5/ESHq=cWELb<.I)HHC8N*?uB?io8/#lZo$'?6cN5 at oK8+j#(im)\L,?FN1W[B20bUbD;pUH;qI8AfD`Gk<%t7iFnE$Q+!SLjjZ3KAe0acL[Uf+<7dL97O[8ka&Ju,iJd.8&"P`'_6I`B5Robc"uuCd-Ik@#2&:"fl"l\!k4?#XU[f)\63.Fn^h;]57!J'-0%37bN_OogFAXWUZM+]GZP=c2ck]Z-/g+[6*6)sLDnM*^ClqbDW2qfeQaZPN6#b>-+d0B\W$G*Be.Hu9[B_+G"'#[iNnGaMekjpS$q^X3AZEjkG*ui>iWGKT\jMkm<UKF?6\(EUi-qIhQ?=W at ioj[:hkN5R?o/H2D<_d,q&UB$id'Ap>dUI;=8G!t&WmEe&/HXkGo\]rB>c^!L(Jtlo=cV&0bTjPMDb$+Y at gATQFUg&J]D`A?\?TV at 1?.;@q/N\O!$[;J4aML[::__73SS#?kFX$!'MT.PX-;@R5Qe,\0q6tS?kS@]<Xj[q<nS at 7&jtdg=TRu$K!2[%aO04DL)8'm6Fnl<>7)^<=PA*^38r=PRK7&mBfG9NWFA`6 at sqTX&.7R2m60,lG"7=;Zi;=1V6SgDtrE+2:"a>;[*TL*%/9_:,AbjmUYjIOta&)@]]?b[(9bb1sFp,R3%l5clcUEf?GA*i at 46F;'QhF*_q7e>u.gCXc-?N5Rj[<(e5J<M!\Vj8Jh\$0kCR:%OsGk at Njf]7V6_]LntJ<!NBq.3!5mL7d=Rb9!306`B&lZS3Ce$rWC3TK1&U[$.CK4Zm[-:Z;e;B-+$_eeYnM*#nHN6'HdT0j$C4*N^7o$6$OKQ at Ibosc_Eq*9%2=plEUHl+F-^80X9aph]c=aq3a+$H-4sgn#1Mf'WQH1f5e+p+nJ^^cR*d6bdNAO)H%P\qZ1dO46W:=2P1MrT"5_g/175$AR&M.^f*3s=u\i476^b at k4NQ'J&>^+!9_knc-[Fd<ohQB&C6Xo'[j,gm[M$HB:L'Pe^jlSTS!^qKBk.c\ndmT%!=B>?e[nhqFX=lhAb\tnRN0AhsnEp-V#^G(/iUd(5s!D*I\_((pf$]i&Ue9F3R(@9n at Q4;O%9S>%nYhkge`Lh'I%Z!&^2qcQRX>oKZc1*`tNF`B<2iebSB.W6hrfdG@&pocD)>aU$p_s/cRsN%RV26:(:SUQi/P0eUV<A&HhJ9ifZQb595OGL*G3pq?I/O0<G>5JNZe2<``mX.[cr!`D0FnD\*VT"5Sj43<$W1[6J`OJAs..dFa(]6+&g;pW#(oi`$EGHU)O[m,L^(5nlP#_P;%S\Ma'6VV2n+<qC]YN>U$%F!aNPGcrWUL!Jdc8#hZeY-6-D(,%<P/ZfC>&*Y2f/1T)hOcSB`E?i&'N-\#mpQ<Nn!2h[R:ttFI$mE7r<8<kO/J!a#78&Xo`W>U/Sk>lo]dCB]<)(S:]lb(B*4bPa%mKXg*GjZ+HbL50EUs!64UaKQ*E`Vm:a'0=a#JqHZmodk?f4YWaHH^[=2?&d+tF!)$Y]f.Dk at O06LB69doGsgQ-8co8.(DM9bY]LJ$W'a),O;kLgL7n(Ek<[_.l5!7UmfT#tB=b!g;T-M><'N_Ei1:F89lht*I&R;CjpX4\Yo8=#0-,2GqYXh,BnQP9=/]/n'oAs%5o/@0m*4gWe0=oK"K3BmgT2YGfEWFN.IV0[&A-E(jT,q%@%'@Y>R8sJEnrTC-q)XpYM;Y)\8lq(NYP^&!\PajX01G:4N2?,=a>JuiF^%s;!<MSNQ)%H,"&1<c^bMCuYq6BtJ1;lbI@"XDGQY%0p;p\B_E&p>c!AL[>&H~>
+Gatm>=`<=[&q801nA>;h"!ih#>8glckk),9NP5TlDtd]r&%#$bm$:&IN]?k/>L[(]ae[r*Ad1eaZ:Gbsq!+oCDJh,1hh8>'3O at JbKeqIi\2+BhG at sbQpD/e8im==RB!X^NqJGiqm)f\V\Gt$QC;p9-`AlL"R;^,SXiB\(6I41D1g$+8?BjJp0C,1jdC6;s?B1d^mDS`]k?Cei>3+G=-='EcpiCWU(60Gs7sA(npot)(LAo$tB<uV?4(uN="J?$].O7k/b9!]+.JpP[-1"W]Ljj'5<eK%5F(_cgY:O<\;I5LCGWh't<i"`Thi:fU*lHlDnP&"$>2pR:k=gfh8^!(a(EmjTq>\@$9R&`2.T'"&I\p$!^h%MmT_lEMpL:]2o]#Srdu&6WO%'%YY/p'CWH*FJ;6kOT9"mo[>+9o!SaH$(hnC;dGpi4G at 2pchjEmJWK9+4GqWiAT^if24WChE(5)qQ[GXYB_EY8C.-i0+iZS`_K+U^GoC5E\%g;m6`<cC$ZfO?o7o%=Xt%Q+cbGM#)Z>CEf at eo`2q&*Z3X2Ds%T(@+R7Nf?E<#P5<#(4VY6HCLoL[G_HuMm]W8jP^[<jusl*k!&BZBOD9SM`SC4bBSRKX8A+F]ikj7*mC$:4S8pE=ZgY_17-SE62ld=\_;ls*J\i'-.NL-;8_nkkOZKXYLaQ7^/TA1P$4.&Ds`AaV]XRRq%;].+TQXQF`\s""Y_<E-?G[!mOT<BVJ6XVs3`RuqY&phKu9WFr!*T?=9#geHu1eaeqK?_D0Q6]a3gt&Zdm%`(H8hj&Jr[?1/VP6CH.Et>%(T%r>Pm,4$s8RL5obcd,EPu_<]p"S+a"J_$!h#X^(f*WrM[*-$/d>QbWIYKU/Q)D(Z5YM*>*G!-5IdY[f at oq[A.?"P\uBO*KN*10S\bLZdn_N'U!*gs=Jn(bGa/Jj)=\'WS6k[LK89fAEdjE:1"c>]`kkR:MU:11.(FU'R at N30u"As8!$>-"!9hE[aTnNNf^,,AC:Z+ilh/('dAk0?;\V;DhYtM-N5F[E)0F:jH?He-(e?k^itbh%`$gW=_Kbhd_T'0e62ZF"F_a)o$l-rr2<lUmhJlc>#e1aohc5&[Fl#;)JZONf8.^Z39bmS.FK5W2d/OCl'BTa7"`XW28EfZ38n>?@WEKs$YkJ<RM^e0(9bp%3!jd0KClH=dd,aMTk&"D"9^&hPn1kG&W\*gbpJK1g-ub$=;;>'MOm at .T[q+W5-!^3mSL`rY#K(F6#5\jg:\,/jFFn/GA7+QY0p>0EctE:$.>gCb+*>M!prr&\j.0&KW^+IA2psSl?K#=";+AU\FWXGri[XG6XmeVjVLVR*B-5So+js8eqToAKG%GcL^t[PQ[Kgi19KEbsLuCL:mm3J#"([/`[G<h3)D[YKH;KL_CbgL_t at N1XD'FqT(e6WkGni,rYj,hUFVe8fa*4"tBoP6s.Z%/,MO%&QKT=V,'.+0t>=p'QS<V8diWAOS.GgoI2H>nfK-7Xg$EMp(V&bCB)5$`dAIO!au"'jWi3a=bB"=<=mL+4/;p:&;\;kT*V1Q<a:-_]!T5]">?%%1MnRr<-(K<is]3nJ?t-q:Isq0-/$,?ba6p?AhTPW_!_M6Is?#DE/ugBC+[Ue]MX8W)k3l$@KkJk,RR(#Q!%ND\>pN!_+S;Ih1RcPC(`aZAhGlTN5 at +c`omPjfkH)#"bS#F/_fYu`V5ff^Z%+X:1/kLFS%pR%q+u8+N?77<J'V^IjOalO>I`uJt at 89;:L(XV@"mlC*M=-[=KE[7).[%GJ;&8]<jWTfuXj'FeQtr$T++-7g!'n!AHkIA6E^'`E5J at ShVUI%/u[WSA3L7$G:=got%MVBUb`'?XVW\;Rbh>/0S:hk-ZTr*4lZ:<m=W$l3e;ApQGOsa)T#%qDd_-XS)>Zcfk[0HVUktNWisIrD)?]in-!"^-GdOX1FOrL1m85H!^#!!%Y^ba at Vo*C%rPG"KW4Z4[>i+nRCcoP<s'MigL:=g"QI"qte]fA:V-+K/Bo79ng'`JC5%HoQmlpgS%\:E#Vnb9r`lYj%B)+4cjD?COh_LZ5PeLoVc1\&jd_%+j>&aK8E4\*,m+R18fY$!tefXkBZJd at OC\->CGFsinnm+h5INO'_g)ieWaD2Q,45E_e".jLdUO%:9/L&dNGUNN?hmUlPe!\ig&`'4eCt.rg3ebqIs6.%oIf\=bH?jr4/C at 68"+hDEb"Xq(9rG,/(eT at U&DGmjR"o9rYF<e>:aaqIA6gGIug`LB'<e?LiJC,>:Gh`=7iJ_A[B!rEY5r@^r;#T3+u;!](H_O9Da.c+asmWcYH6&t?3Z at a*/&aa:lZj-]OKd-d1Qe9dk@\=SA:[$DS16aGbJ(>m1:JOX5C(_7?..jRkZ</!n9/l5?=7lH8_Nf]%?h$@=G-0\u+C+C#OoY^jI49;?SP:'WCP4^&g9$;TQOcMEV&g7Gj1D<@+6.O:5iQOeh^EZ3,'os#5fTX0G at ii`p8<9RZC2b0d3pG\/f0h6O%"s'ugs*p"YQ9#5GscaFoLp;hm3-'B7\W6!m`)1:1?R.\RrQ9:$942O7bS_&SqPuG<BL'F#fd,+'4[sM_k?955h008/9L"k<8H`QlJ_ZsE+60A-K+#GkY=8<i'%1QD?t`er]Q:h+keeIBXS\Agu_Os0kkJ.,m5W6D1'lS#%-iFG7:XB[J`5'1b'6so5=aXVLV5h.k?nUg31!W`=7hH"O%mN8c/r`)Mg;JTS,jB"^'f\)Zon7kGOPIk"4:.nX2T(1/Sn2iRB/-A5[u8ReA4RKO%T at -;+BH&;q"==gqrJSdWt"omNTUHiVV]3EdgVAgqKn.aFb';3n!a%p>A8bMi3Z,f`ZJ3)SfMV_4Y/d]f9P4tZ>r^+oH_Km)!;>@BrIlZP,=';#A!R)uP-MOX5a%1;b.-3o5kiWSde?q/.0(=-,;4PDO-*YfGh</;>A,`a%Xd*#o8>.2+-)pl+"_\TsJ'k2aWg\>qE*m"5k2P_m0XZ^rBbH!upqRGVsF_EK'Ld^B8Tu"NVNgeaPXM=$EM]iBuMsSPe7^Osa,#%,ug*4SeoW9^=V at 3^7X;tRBR]WEQ42N^-=WLgPXYFD)N/#!UCT<,<m8B(u at 81/g,. at fkogO=j#\Y`V8^_69d,/JXf#!MoDF!l]VIHT1BZ'009KnDfefOd4!d-Am[Wu)(/EUb4XOhYG:4C(`;,/JJ<pm[YJ4I4i/-?][$Y!Do_0PZC"e4qTJjZ_U*\i-FCB`,\dGV!J`.hQ>Mu)K]^>h$%,P9$[^&2lHrr^ph2bF~>
 endstream
 endobj
-36 0 obj
+37 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 35 0 R
-/Annots 37 0 R
+/Contents 36 0 R
+/Annots 38 0 R
 >>
 endobj
-37 0 obj
+38 0 obj
 [
-38 0 R
 39 0 R
 40 0 R
 41 0 R
-43 0 R
-45 0 R
-47 0 R
-49 0 R
-51 0 R
 ]
 endobj
-38 0 obj
-<< /Type /Annot
-/Subtype /Link
-/Rect [ 120.0 719.0 286.67 709.0 ]
-/C [ 0 0 0 ]
-/Border [ 0 0 0 ]
-/A << /URI (http://svn.mysql.com/svnpublic/connector-j)
-/S /URI >>
-/H /I
->>
-endobj
 39 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 303.34 319.56 529.43 309.56 ]
+/Rect [ 399.693 519.104 562.188 510.104 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 21 0 R
+/A 17 0 R
 /H /I
 >>
 endobj
 40 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 140.0 308.56 198.44 298.56 ]
+/Rect [ 84.0 509.204 189.219 500.204 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 21 0 R
+/A 17 0 R
 /H /I
 >>
 endobj
 41 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 207.56 402.41 197.56 ]
+/Rect [ 315.462 102.629 394.95 93.629 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 42 0 R
+/A << /URI (http://dev.mysql.com/doc/refman/5.0/en/charset.html)
+/S /URI >>
 /H /I
 >>
 endobj
+42 0 obj
+<< /Length 3712 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gatm?>Ar9+&q801nCqZ.>1*:H?ltiE"^:Q*h:f^5U?Mb3dBO$];CO%tT3m(;P&8H?Uhj\Qm:%;E,_pI:k*mN8Zb3kun'*&oS1W>ok3V'LR[NZsRY<AA(7:*oH-U6:SUH!-Dqj-lfoOhW,E)p=@h.rtJ`5#N(L0(C0sB#Y^RWPcc\)<P^'\1%_3<iA7EoS]j`t,P-hl]DD;.!c.icYE%+EuIH-YH]&D&"Z%irP+*hLe2)kL/1(L'q<1!?lYn8Z<\Qj(0h,>jp;mA*WsY8hD_6JFRoZJkWK<,_PL^NH8E56;K<2 at G]mTcR@*YuQNB\ps8-h;\0)Z?X8C4B-$GQ?<Y[0Jsfoq%FS=0+Fa[JS/(T)qRd5C&WXj>]1ri"cF56/$aGrI$#&0X-i!>D.J..U-ZYSOU[+CO"!FL at lJXc2]4>FYs/"m1^&PI\>Bqmn:/ajCi;3F&$r$;>gkW:c_d_q4!a1LfR(Tbe*gZ!^Do<sN]te`%*+TEX:(jfi)T[D9'a3#S*!6^ko0MZ4?GuY7bXi1Z7JD'?+AaVSEabJh!!j/'Fe_lY-GY+X_h%NdQ$DM)p4(bE_EIhU`cit/*'^T=[G#+=5(Rpo,VYqWh<6caOE,(ZP7L25muR.)'bW<!#oGCln9>,&5Xe1dY`WI]tla7F&k6PH<=l-X""`kHh$OpUUIp<osV#P>I+aUAst4bk'@S)H\Fb!,?O[8p?$OC])21mNCo9U]YajoOGD3)=BT59q),u6T#X9`6f;?&<E4[R7Ub[nm!hG,db$p+QrF_k`(>k<_81/s]-u0Aq1aAqT-C;q+sDEP1iZUI,g!od(!M:D/EBVQe%.gA:hHq(]5<WrT?aCKM"]gQ0OmD,&p-q2lj6A/=;J;*bQ7HjK at Z-fP;bQIjb86t?9k=-qes:jFh6ZJ"Nb[N_ff9GNp6VmhY_k.'Nbt%ZH+IP\USU=p4ILIaS4rQ-N'9Us'Ta%m,g27 at lip9(+^TSe>CB`FG03:2]O'c,KiX!P1Urb@>.1%FaGLTDCKgr!]2*F8C:ZaJU\hn--UGs,[M#jSCK>fj)8at-ap(3CUOd>P4+-DeWB:rmLtHnasgD^J?pA$CFolM--A=,Gh$?tK%EVNot4trUSm@"]b/3sc>OP>s,M at r,B-(BpmQaV]$8dSFsi7/M!\<B3:5.aQ]c:64PQSSE$ti<'39Lu*`EJZ/;BK(@KeW=o at sUK4M3#cI"VT!TWI*6-B\]rjcXFq/05+X("m8`Y#A)q*kD_1 at LFS94=pT*DT7SKq)Wq;"B0q)&Jpcm=]RX6!S;CeJa65%U@<cdash.JTOn=`cc%uhZ<BZ:o,rOS_DfZI!7rJ.U_9VMrVMq;0g'=$Qm=J9@"D[t)on8/#jD.k<J$D#F35RqEIL1I[BN=\RT>40=Q=C9kV92j*rZhJiHb-)].?-)/SCJ.>\G!5*Ncf3i'<e'l5=k(0WZt&bOcoA-(>?="d at ACUIk\kD:IO[qF[<jTfk*pJ"Uj<NrUk/:?WUpN&599fBmrHbYEt(CY<gTgB%hZHWF,,K+;+m18L8+g4B+I[NljqA$Y7Q^Ct$c7cV="'Dg"il;+g&F;_c-f3IU`Q4(X`V?.1mido^>]'[k7DM0uE]9"k52L-SgZ@/-\/_R+!Ud`][b)6Anm\Np\,RCrS#$C6_ppXF7B2R^$f!h709K'+ea7Q>V\.[0i#N3j?*+mq?M&9Lgl"aBT/O;^c6;aci]I%9ZbfiOFMPM=ga.tX*+qu,<s(>7e=ebU,W!b$.DRZULZKho6$$@Fg1i7NW=d*5?,4R%Hj5(G/(Lg<3:@$DXqm<(,;o,/DegXVeU!g)&.>'[QV(jGh9:f(^--&I?ob(&#B[B&IHRQ%$`^oi^*K9^Q(fjPHY)IO0NU9p+:nO7TA!?XOF/4XI/%R<qampCDfQQ)5#T'%-J5U'eqm@/-d?IUM5od!S*bVl@:E1/*P:#V#UGp.a];eM4FqZa;Ts at dQpYs2U%*qJaN'FN`D;`b+0-r4QX%?l!^sZoRUtWlaZ,SSDK*b;Gn6fn0Fg?SKNLfOm=CXc\&SgppO8FK]YK3Uo=PVb+`bpJR%K0Neglg1881k%Ir";c;jT:AbVeeo7X!Z*V3GucO=]nYTf/L;pBs&rY+/5)#IURAl:"@E7.Uo-*`NQ@!9SYcZCJfq8Npgb@,Ki-36M*@Xo/qr4fbORV2\HCk+QT%f4-st-YFYLHOO<Sc,;c6bYUtsWh0f'>K5Hr4gK`pL3DQDkN"I.>8HSQS2%`dpn+4or`bJTHmlrMu$:]Q;&Iu8<Diu=nI)%U>.ZPpC at UdHG[1L`#e"Dam-=Ghd at N`@&Qs]p/piqn\*Di?MOX^>3rhJ`Tp?YCCkoCQ\L1k#qBA3U8(r/qN)<nB(B[FUmM'>Y\H&8Y!?rMlYCYgK*3QDU6VS5I(<&GhPPq5`]k8>c8n3e8q:`^c['5T#^9l'Ia]0DYGX'NiJWSZ4ZBqcB5SN][5XAo/@N8(F9WSu;7005OFn%[tb#,_Jm;99MJK\&=:)Qei-o%@T#W8)ls&F^+'af8.e!4bir>lZuM\JX$^V:nd+<!V.u(i1682EGbn3$4<P3P$46"`Ql-pqEXk5K;[k.4(,nT!-Rira>Y7im]uR#B<;TP#K_%>*cOs/Kc+`6s$bWL"J0AgE6_747'pQVf_l?m.8H[6(R-BTunQ/8?tU).!32FX<<`4T49Xl5DtP:9m55uG$l3u*N0+np:[g$2JFBj_YAk<WZ]unob*6K(idQ7NbD.I3ste`achZ2P[G_kO`'"5P>&sWYuF4n5/-iIkDV?M$JHDVm\e[%6H-Z!e1h<S;;9't#4bJJ^>.Hd%1\Q'Uc.#C!fKdE&LQ+M.FI?P`<>@?oJ8Yf"D1Y)Sqf!9C'?:4.[i)C+M5K at 93M;>%DH,E\Xdj/78d>Lka[4&)p4E;+kQ+P*4e'h<Z9Xrd_t_LSB"SC\U9HmS;1!W5e/.5>%GGcFgtP_J=FS=#.>h9[7AO0XVKZ3V<ghfG16FuU:r<ngXa1/92<bh[$Tq3<$gH57N]u-m3lT28q8aE_M&Xm<lFXMcj1F?4m'!-lTj00-/r\Ol^DtErtP-?pi[\8Y\\$FWAGG.>Q*Y8U;IY22f-YU8qA7)M=mmbr;?E)1[C(3:mqH"c0iY,2!oafA_;_>s0_.s@>Cq&Df4_Gcp50;j5K#/a(o>do,i<4m%<^g#Jgmo$Moh[#jZt(`d%Nn3-LqjB=De;p(%?'icBJ$=&3!]Q4-pDE.A^_=gG5AalNE4M2o[5o)0P*f at t;MpF_Mb"?-e0f>F/pY[*#:3Zhu/.``dZ">69eCKf??P'3QJ>PW7d8'f-+Hh!r_Z+jI[ceeLhPOGepMC8Bo4-81^-??,EBq?WMQ1Pa$;['JK[Q7t1RTa^cA9D)8:fc3FRVF`jO=9Y$UR'T'".m2HP\e7C?,J`PnIu?n[V^k*OF+?/hISO0V7=Eu?V/sX8dE)!(,S%@Oj`!'V,^15"NLq4l9m/&0%]CWeMX0Y\QP56p]l5U#Y%sbk'EMg]G54tbS8Z%GBlf!W2b6D%k+h)4Tbo<"K_LF$/KH*RB3\>d;3f.N2!9<&ZaGt\jgX80C9rnIF=&#W_9R?h`e`&?MPXg7X1!pm.M_u!"?c_Ks:N4GW50)jjgOe\+dXE"MET\\WLa9X*?G:BBuVUYAU_^Q"9b8+M7p];0=?+Dk,8?U:q#CTPD_]r\iL5nQFIahCmgt1]8D<[(9*~>
+endstream
+endobj
 43 0 obj
-<< /Type /Annot
-/Subtype /Link
-/Rect [ 135.0 186.56 405.68 176.56 ]
-/C [ 0 0 0 ]
-/Border [ 0 0 0 ]
-/A 44 0 R
-/H /I
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 42 0 R
 >>
 endobj
+44 0 obj
+<< /Length 3153 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gau0F=`<%c&q9SYcsm,ZD0 at k-7t=aXEFsBXSHWV5^C9*fd%VqV#t6U3XS'?ci[%W&9o3nr)'C.ZXQo(3^,ij_5@!%BVA;A%((ZlbCU(S4$Ocso$dXQOXsKL>jPjUWq#'8O#k`B2pYt-Fco=0_s1RkV7DHSJoB.f7(=>0j]3#DO!Pt6inhAO<\/sM^Qb!TL at 8[jGk%`+FE/t=a;#\Xha'G,Z at 8Nisc)9NsA:/XS7r^XRl)IF$\r6\2a"P?rF]\\]oN?e,X9u=o_R-f=BQ"(.gHh"#0H$J)V[s.7e=@5.=re]Dd;N5RlR:>b(bP[\k!(NnJ,VVIDg7"l,"g7g2WN#MfOtoc_u0$V)r>tt%)6/t$8L+7(5!Aq_E:MDZq<qmF*JK97298`7-2l.W:QG`ACMJV0^j-"BI$n*_HP%'AP10$4J_Kc.kui)=F)6'\>s'MC->4+`%/f#asUJ(<FJeb\%oTU51jMa/^[Ic'FAStMILUU+N:U@(_l;i9_=4=AgH*bUiDpmUL]:,h1R4sQ7Pff0ZVAK6hL6X**LLoUbC[+9"I>@(AW,.P"$lIHYG_]/a1\4F'n at Xig5XbMt"Mc0"<lq3pFFA8(\5tVId]6@!31`Y1EeA!]H"tDDU[l9qLd-Vf(hf_[#\836cc5[kI1nE!#[Z6QYrQrVCL+]p6[=`H*Sg\S at hr-O35m+qGKZX$<fE5I=Y!-oc5W"p#pi4OK%9#Pcjufu.1,*&+bY/j/8.k"!5Anl?;p!50uU^Zp&*ps`5G`G6GtHB%lSq"!)0Bn@?cU*)5n`PYb7Y(Qt8\-VVP$KI*?>_OFOJIjDoJIFf)7cuW+%1cerb\s\K#uoT4kT at C7,_=LF)'[8ieSIon<AoE]H'KXcgK;c9ZATOGhJNap;[+ON:D6DpeiG1UO$eoA?:RGP8#sWEfkA8tFeKq$rI2Kl.5kiQDNdV'e4h$G"t3[(GjKTmLrYMWiXom9Vn_2<?fdJr:%\1UT>Lsa%"JV.Sql4(W//5Hh\gP>0ksje!aB^%<DM)E9C,2em!$9E(js42?VZF.lCsYD-Li1.eeI1>Etl6IVQ-HRd;5f<&4(7!E+UUd2u$@<ft3`Ciik.<7b^$T=IO/H%_N#,[[#0TB>gSp>n!1/I)[NjH(4$ulf/aBb#.]IR=fTK5m/Rb=(k%4%Wd#i$JT7ui.^Dbcma*&,%Q'h4J<5=\!WQ0`hs at I?@m0a0^l<MGB*hI[rD_IYsifNS8]c#fnpo"'Dr^*8^a8#$clLFH%3EK,-:X>S9"*R(6FX7&X90oNcC_H^X?(9fq-/nYNpg]c"9hI/pTKK`%F%Qqm>"_W\anEWVXtT-Chm31n^\J/l0Hq!!i,ZNr&O":>#!2p`;5q;>l_Z.qg/FJmO&j=+b]ImPD[*jbVABd`aUC_Hb.57NL68WLj*"nk2kcZHi\Qa>-VA`X]%'cm6"gUcCt<%:OchK'>"4Y*/k))^tq_^&7tY'3<c\OJPPpDliPIBQdf-R:FD)V\D[f4UPG!G at Y+nD5(EA)<kbYNj#^JlF]fL8Qu'JbfmV#qJ%F:H0pI$LVtY!.&1'](7!Inq`H@%;Re&iYZ#7>O#4PgiSU<R`ZWfH#cDmZ0N-'T4d^>=k1^qW7D%Cmq8GCdk`G-?SoR5HIBa2=,_(/=#(B#GYb^<?L`:9F<!pCkKPcr?S^W6insFYA?#BHc4b]>X\X6 at Zlsb#7E2%Fq8(WUGQhEY1:'Pl'JcUoiCrSML^?S\<Xgtk?*fXZ<?%(SoYA+"1)0^V:aS/2hfH"SITIbkTg8%]&-oif1Z at UFG;7>aHq1FTo.%SoKPW[A2WfP;WUtVs/h;d76A4"'o#QB5rrNC]WEJrFA3$8Kk0lB-%=`Oa^9W&pNXST2Q^Molp`FFO$1oS\?X.)Zpd$_rS)"bmrRPT9N'']aL`9Xtj9g'h;>\Ej1-sUK;P,`T;I;'F:dmIdBj3&R[)"1TWLNsqHC$]oYXq+L'bCCD&3k:+Y`M5S&KmM;>.^jl!AL*b=g7!!%:5-Oc41TcRMR-<ELGcf-KRG"kE^2lk$G+Ar#mFC(H6[1g,1fYI"gmiXOpP(pW]VX2!Wt/G^Z.BB,j$.,fkEW9=q:d+6a"u at N>7eeTZcPC/T;_NpS=!)(+C9MlAp\WQeDm6b4[-H at D:Q6n:#^PD:1M/Oq)(JRZ6kOp=uSA2j/7m$dHUU!Ss9JllHF at 5JB&]#6i&IYah>;K7m=1`\9ANkDdXjJ95])/K9OPk$rg9o)jjHA-Yl-Ed:%_WFugh5g#-1MNiW>QD.Sp?Zgg$4]6h]WY*p=m%T#2BJe5TOk^C"RR!A.=\:DA3L>)Ym6QTer>a/#]\68dF00bM4"!B$+d`Y\rdQ^,=_1T%nbS2\Sm]8kUAHs-!OZS;;1"%qf:A#%9$\tb.H?Ju.3>U^bXTV[Si at F0%BqAN0uoOqX'5V#qa-b$]el$eJd^`6+.gQ^;%ggt3h4@$'34I"Z[HT`4am1Vm%<3Ch)Z^3;6Mq2lqG[Na`<@I)@X<l+ba!VHOeRZ$Y!BY/m9*D)?0>VZ5WbI:^!e*&^%P8U4Fq:*gsS!12.g<2B/%8cr$YnodLHXfPgPC[%=Q3O',X#&`HgB at kVm$TQSH*fc0T^4W3U@/9d2g,$.\u*IQ=?*8pecRQ7CX0:C at +WME$PdAp1CbZ*0/Xn")>AK8F.q+o+CQk`2'b9aO9?Qg]\;rSZp;LKVGn?07/aaao\9Xr,t3WS]H1#YYMKM0pe&JWSV>4.3*7R[PB`2:K8T0Bp&RkDm_a5W;OH9QC*jVVt+;Q[=?@9[,9M/ps;e$q&.dAro4Epso]I$(,j1d&qEF-eZ-N+7MO4XHp]:ZaVnfj=^#a70a";95!Jbd3+sb#Ne7I"BXT'd`\klBdUN+b.k at c)@=QLj`o([Ma2hPB4Zb\kh<"h]nidajEq7ol:\YLu.IG4Kj>CaQ+<RM]<kGTWUG#d`!Nj*]1'RrdK7JV7ohQ&-tK-+mHBYK=i:$[7GLUZAH"iIS>31VZ_5n4h?3Vhf%uPs1!LPQc[atVgY.GEm+TnBU_br4WhDGl*p#`N.b<.Z.n'[EpNOnf:THAF?+,YNn(lAotPg>`F2&q('r;oCUJG9/F1ng#E>7f![ShI8QPqT2r$PZFd-K?d'8uqY'8Nm-(?DlEk&HS##>1#S6A_i~>
+endstream
+endobj
 45 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 44 0 R
+/Annots 46 0 R
+>>
+endobj
+46 0 obj
+[
+47 0 R
+48 0 R
+49 0 R
+50 0 R
+]
+endobj
+47 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 165.56 264.97 155.56 ]
+/Rect [ 361.452 534.15 459.462 525.15 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 46 0 R
+/A << /URI (http://subversion.tigris.org/)
+/S /URI >>
 /H /I
 >>
 endobj
-47 0 obj
+48 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 144.56 362.32 134.56 ]
+/Rect [ 276.699 515.25 353.19 506.25 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 48 0 R
+/A << /URI (http://ant.apache.org/)
+/S /URI >>
 /H /I
 >>
 endobj
 49 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 123.56 312.47 113.56 ]
+/Rect [ 362.208 458.55 519.213 449.55 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 50 0 R
+/A << /URI (http://svn.mysql.com/svnpublic/connector-j)
+/S /URI >>
 /H /I
 >>
 endobj
-51 0 obj
+50 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 102.56 394.2 92.56 ]
+/Rect [ 131.007 118.06 389.334 109.06 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 52 0 R
+/A 35 0 R
 /H /I
 >>
 endobj
-53 0 obj
-<< /Length 2648 /Filter [ /ASCII85Decode /FlateDecode ]
+51 0 obj
+<< /Length 3015 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GauHND/\/u')o%@ThbmuYZG(m+t3VLU2f6:Bq/LHq1+*7'J!oED+\gd^=_!7qO8c[m4:[uQJ5XH1aKK!SXJZ/[q4FB#,CMSOBTR at cB\QJ\/N_f"b5`tg?PAMfnB4>=Zh'kHaN21]A_4Xp3BIf).oqC?LNhWl_f6<30!^FiIsEqZHiAhI1=&`>@(il5Ds'KT4^8>UE\;#=L-:4Z7OG0d.]!4=Nh/Ekss&N.X;N`h4:]cUV),KA/&@bB2iicW(QF?H at d,Q&_D%M2Z,^]I!][IB0/#VWNk@,pTn4Vs7H!LG'YtC3noM0R-eVTd"(ngQr$7`S@#u*n&tW'$H#ZXFc;mZ$<HYS7T at Ql^9q/\o;!KZ?@:f?cesf%078sJSisf7SVafDZihF>gmssi3*O(PpuDZ&NqW?#La+W=(icq3h=Sn_C=\BS'C;$5q>"n4G25l2iNEb>5jcpaUr-#-s"p\%qE:dVos\Dt='24,hh8E)<*1A"a06?$l.S03Q],3[S,7<D$9#(-jB7c/gF?LdEL!e9U\pMI8NhmlGiG9I-=4HJ->fH%T,Y1nl,umM#nICeO]PI1,CT;1MKS*_TC(kWrK*n=#am+jkLhZ;%NFhs:GWumh`'>.)8<RH0(j at LYp<s6j%XSN#_S,NNT<]baILYc1SilV:Z:&#;i'K@[;/bkEJ]k=#>39Am<+hV53mubB:hE$^J``oqrF%rH,c&0GAs_H7B5T+>DjIgcn`u;^`'a1AkC)gK,On,8WLKhdmq.U9o6Zh/L>.0DDl^RIF5OFD?NokiuWk[efmLq@(TNDaM$kCqngbi!VLu-;1pW.D%!UtZm[^T4q+p-Yc8ej9+`<JBdQ3=k608N20#WEi<GBqgd"ei,#Wg0"9cp3"/MMnQ<EV:L+a.^F+*p#HGTMahr!=?d5Q7gJF--\ht30qi[r-LbaLLK]N8n4\\t*s)ZD7N.F/@;dG>>F*BZMP;:i44\"b?lE5MJ02fK&351okS:k(#qPcIgmXn]o-)9KW`UirhB-&rYgUtr"MV6Ln1]!n_G0o7h]T5TX5\5TE&R-6YA]3\,;d:uZV4Lm at s'4Qq1OYU.'e[L.K>Q%m)RVPX=fG)h%aMcVbh'eW"6#0*[(0U96[bkNFNeRL[],8S=c<9q_$d0;%i9q at nDMU37qooSLi($!0O"=.7>0 at OKH'r.oN@]G'oTqF2YXp at -X/*:mEOtK>05p5_<3W%.M%d#%LBMnUMno1D:#m8Jk&OUTZ[/-[f\7JrUf2=^#eIoZ'fg]oUon"fLiHD/ZBY!C/B<@:8A=0JI7t1UO'?J^c:-V`_a3l&#f^kUGs$F=JtM<0hCRg#JSr$=pN6J5.N>_XZ<92Y%DE?(&pWmf7,7M$j+,R7HI_Tm"J2aDo`auS^@)Le5+E.4)n_eC\KL+Y^Wm[pj>E?2V4c!]g+PskLUG?aVhaji'9NETWEV^1e&,`HZf5]k:j'Z,k>/Z2IY>PEgTWCYI^@58:MF*#2QC66Vh]b>n,KL.GBiTf]YJD<Yb+1?UP6Yf:BV??hXXTPq+sQRB;@p>g>sao`DZ3E3G&P3(\j/]B/_ebW9k8.BM*XMXB-q6i#f+aj&k/`2cu;Zf,9H'')OL[b'8q>lCgbNLO`ZOFH#%k0AgqKkI3To^_XPCh_gbHd5>n/1htbG<+F,MIra9b_u*&X,M_,onO=jnD-"\Bp`^p3i6RDVJ'hJ,'7#+'>Es2i,Zl at f]#$e0mm\&V/ps<rg,[@+3T7L*a2,TGWg.f^r*Joj*m=852`B4.H#X!7_Z8A:+Ja?7a.e6q"N7rT970'roDj-d!?b'U"Z,:!*3[Nu#,I0G)/)i]:-cB1%?pQ^0pP`4O5"!_;_oX_SdE9U>FJ_$beR1nHW[b:lG[#q74P(4X&):1m`gXTgL"mgqX2WBL4 at Zq,dK5!NIJT6C5*#L0&XGG8<+fJ]@=%]&\U3Dlb-#!R:=`hV#hX&od!fPf3OE2Nj_h4GQ`dbJTH7aCGf=ck"$B_Nd`W:qkGeJ`K5.oEW3:e#4;Zd&g&me^@C7^VaGol=i+4<[$_Ds'3dQV2;RTPBN_#H]9(%fR[:!'>fI%_N`E+$m:/Ft#f[kJfF.`'&qjQ&o&G!YO4\Mt`h;V7ajq9."S[?>apTG46bQGdJUW]#[^\$EoY#odMW`&4fDG9/YH1lqEdY0(1 at SsC'Ks>8Hc,Zc3sg]`QlbNWgnO(,d!"*-N+5^ISeYDMNtrJ1cFM3oF7fhI"i?dbbfC#0`!@6df4ZaNVdcSK!NC(fk at Jb"gj'Oek at A8$<Z*r8CH2[?*$'1;1pg]k&To09qi8^V"4lH at +!#/ii&=#+"aZ0lZ1];ZM;*>Z`TpaBI/#9J^nbHF5G3=,o:dUU\FE94AI8o2hsB=Z6[:UU$3Cp:/l]u`pCP22/[TL1dD>6^R=t5e/d`h1oVL+7AAP3FF1u846=uRTl:b#M=+B\KTX8E`qtb["OCUFA+N=J&DL(9,meBqj.-Hq/L--nN at Yd*%@Nl-[?MeXU?K\G0lM+g,TO(=nU!E%e2IT^"YKP8Z_R<;@9Hk5Yn*Lrc!(DW``7i9L#4 at QnN=Qcj):UR*0%l,=`#Nl!jOF4H`22"hNOjj`<$Ue9*bfa9$[puE$nLuui'Vo[f.J2_3OjoU3UhF_&@^?/f7Nu,KaV]l~>
+GauHN>>O$H&q9"FbgVG?d.i&U>-1O*M38O8F'puU$IGfndkg%&G!!E'fO)DD at s_JBb:mNeRlghCG'DL2e\1QtIWY;?T(u'Lbu[EhCji,*2#+,t4:C#VR`Ts3SB^*Ih=9T&*iG.Ahonh)>D(fSb>nF\R>8nqICtY5,(`-nB:mnE4;7*L,QhA/Ac&4SA?oHYA6Ii7+ZaNjK/+36EAFsFLEf0SZnKj$5))]]1InLh39JmMqH\]kM8J=8qQ>qT:luts6k'QO1OGM65>_\,f3qhbiOr[PoqH3C/6gGDl[>,QFF=UDH^'OiLX"&HMh6]j5oqHl<$W#C<geo'-FGsKn,:fG1hLI!Dns,rko)Gpdr<)N>a%eV/diaiG/Y-/(>3RuA6D'4:"*QtG8noNg&]\X7`T\?4p=Nj^5&N361U>qml16e=oKXT2[*<_1:gmm&3iT[(4s=d'i.W/nAOnS>^jE"QlcO.mP)]FI/frUg%/N,9og/jd)>6sBUTquDnttLDuYsA5V1)PjEkIdI:c6:$*Wg>A.:=WOWE9JQj(5MB3jjDMkuh4+g,,?.(VXWIrc02L at 6k!]B<L.1lu\a0R/!V(qTmo[9/30+Yr$W&;F+'M*X]S$Wsg'5oXXnSffFah\4N(>*ULn=)mFB^nFCs;iL68,`mX#6Cm-jgZiSi-Ei!C0pja#._NJ#Q.t?+a?oDf-jB&iUEUf%GGBi#Y9%sgme:,D17:3m.D3BmPda/^W?qW)q4:@mr9\\,oj8fU>[1WB?%+dbfG_PWB6NtTA5pQ?(T1SV1f7f4KQWkZO4m_e>DqKn849>4W8GR8IW4qaI#T$"IJ8n>=Nm7$.UM!Wec,fKiWK3";XlrcQrJH4PO)(FgkMpqs)p1''lGY-iLb=L'+9M^dG9.&Z'sL7pP0<<fCs&Zm=36ZcRd$d54MXs]$)umqn0j`R(S#O*GBTZ3Oe79(FmD2b[,3T0]]M:0LiP&iq/$FA^p;+8_jSRhe0=NDKLcs(Kuetb,B`7$B*0eVk8pk9D"-!NC)h;a\KCfTY&jW30\,URKKk0YB[N<kcMY&DisQ8k8iZ`\f&!ZC, at 0G%KiNPfm at e;>Q#o(FHgO;brj1M7+I^Ee3:#oct-93muK]HH[DGq7e>bGX at +Cu5kL%rJuIGJenS%Dq`AQp^]kqqo;(FZYmf0Xjqs&J3:059rktG2,UtpZSo=W+4?-S24QS=>+m<VR#)E\h(G?.&LP*Q7&L(oEA6(O7-u"7f[a#RSE)igPE,Z/i.)Q>XS#Y$fh[L+)VI35@[UroncutmZCBcgTKW=u7HO''fQXs2`>MD]eo=TML!_$CDdQZPpIr6?GSq3r7m;lPUmYkutY01YES(2:fm8B&]Tg4C_j:NV"m@[H.j[*'dPcA-FCS<(_k-c%_peLT$qntOJ#*%bh*0n11no7Z7TbY1*5Qrme&hhue[CW>?`O2,q>#afQ`[lR>&q*t:Jt6$BCsn%MbO_+Qh1rD4c!N1/Wp$ZDju"+#CsCPg$5PSk*?prGX%npteDZ3=9:Rr`d3tm9lWcM*VTD6M0jtG2I"1//neBGE_4\o*1j!)1Yq<WpYCF\-Kq42SYs4S"1g#_bqbb3e2<H!Vn'a`O=HA#6SJ_-B$T\WQ30)G3,^btBKim3)p2RBf/m$"*RGAX!7,MuS(`\R).aed;ZV"Zn;]VZWR3BRVHXN]f0qfS*'LhRA58ge\&3@!iRCIn57nE\aA7/dHa-$8Z41+NFQ(I<\JkfWM'ju</?6aVR5Pf@&mbtr'n=8X=%nD]m!('D8dL_j.L$,H'0\pqbR@#Rdr]GKgh`A-fN*[VaHlGD';'E+EanaM#cd0o)2DQp,mS6eFN$OdRH=Vstgmt>u.j!"([RDdo`C!k\hA`UX`302ma3j2AIeSo18eoZ%#O)bCIrCqY;?]%mp`!OIF;Gc)+N=fb6$74Vk#M_9*(p?*V=CLI87YtN.Y;USMWG/Q"6=?/`T^2&qhAuRGn#7G'Kej];Xj[+M'2:I-R/]B?]I^BPjnf&^ocK'E,HW1GtgTF*NM5M=6o4^Cr6<,D/6^s6Y-:%NgY'%`[Bt_0WSngD>;?%]`4a,CF7MQr<96>1jGi+d5#F/SU1\g<1bg7otDD?T8D?5@#`7[4VB:Yef]Ks8D\!LKW/dE+SUcg\gCB:&+>@iHd-Dp%A-h86TN:XYM at qHWV\"uRRJ$c4>_Y"j.3VJdS2UFTc_=LfLS-<9h(M_(gmufZ%A2=$`D?O$*DQgAKn'!.VNt$ib*`ZG+`9li-Tqn1;P:^Dn1CRhnc-N8m<p&M1>kA2W#osfN,]Q:P"I>q<C-"?[oc+4*K0c\M%[V9p^.t7%F3q)$)XTLnI3"QbEPd:VhFcAh-er)jTRsK*Hj1N=$\a6DdTPcD7"s$%@KT_8aKpdVX>)'pCBkmFe(Ap3eLc`"6`O1:Cn)_*q[:io'ki#X@*ngkTQJh0'=dqs1]0lisOYWmV>*7A9K;Q)*<]b>`%'d^IP88/8=<k0RgKYqZZWKn%>\6s[8+^jUAIn]+Ce20RT9(F3<S2H3+nq]Z]$H!2*1!4%p0S-E,=cQ-p'lUCF4qn>, at r&-Z3"eJ4]qg1%.L*Jh]J&U&BZ^;-BK;bS8;AV*K_rC[*a at bu#H8$NAR8.CY>hp&:,@?7W'<`Kc4lAjM;D2"2@=Fp^inlFQVUDFZK0kKN^4S5j0k?tc>%cBEXEepleqH"'*u^3.i7Pi6bWLgJN-[+`@kL3DAb:[:%5;I/SWB$h:<e_U"DZ][liX-W_XXc:KlcE5*,CCH#]b3Lid_$YT6j6BB,#JeKepm)&1qHGL-r&R'ktB/dt7\]?Bn)Yp!mJ:K#(R1XJQ"D&M:qU5DCpT+-K)&4*ULKHNA,dlZGo`<jIq"7ai+qK*Alt(,[ATDR/8!0$\2'RhYcS^Osod$&]?[qtJ[R:.Dlf!Np$Lkh%"+ZbqC]'$glbSe/!/!nG`LAW<ktiaD&Oqcd(V?[K]R1"3O=V:T%9mE;s at V`5GJpE!dYlDf49DRI)4En[)`".;m<8,~>
 endstream
 endobj
-54 0 obj
+52 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 53 0 R
-/Annots 55 0 R
+/Contents 51 0 R
+/Annots 53 0 R
 >>
 endobj
-55 0 obj
+53 0 obj
 [
+54 0 R
 56 0 R
 58 0 R
 60 0 R
-61 0 R
-63 0 R
+62 0 R
 64 0 R
 66 0 R
 68 0 R
+70 0 R
+72 0 R
+74 0 R
+76 0 R
 ]
 endobj
-56 0 obj
+54 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 709.0 378.29 699.0 ]
+/Rect [ 97.5 682.2 338.169 673.2 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 57 0 R
+/A 55 0 R
 /H /I
 >>
 endobj
-58 0 obj
+56 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 688.0 439.43 678.0 ]
+/Rect [ 97.5 663.3 341.112 654.3 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 59 0 R
+/A 57 0 R
 /H /I
 >>
 endobj
-60 0 obj
+58 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 677.0 277.44 667.0 ]
+/Rect [ 97.5 644.4 214.473 635.4 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 59 0 R
 /H /I
 >>
 endobj
-61 0 obj
+60 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 656.0 439.43 646.0 ]
+/Rect [ 97.5 625.5 302.088 616.5 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 62 0 R
+/A 61 0 R
 /H /I
 >>
 endobj
-63 0 obj
+62 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 645.0 235.44 635.0 ]
+/Rect [ 97.5 606.6 257.223 597.6 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 62 0 R
+/A 63 0 R
 /H /I
 >>
 endobj
 64 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 624.0 518.98 614.0 ]
+/Rect [ 97.5 587.7 330.78 578.7 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 65 0 R
@@ -461,7 +481,7 @@
 66 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 603.0 419.12 593.0 ]
+/Rect [ 97.5 568.8 316.461 559.8 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 67 0 R
@@ -471,7 +491,7 @@
 68 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 582.0 353.85 572.0 ]
+/Rect [ 97.5 549.9 494.283 540.9 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 69 0 R
@@ -479,70 +499,50 @@
 >>
 endobj
 70 0 obj
-<< /Length 2776 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gatm>=``=U&:XAWd.F$:4#Y>^PhN#^DJ#?WD47Doc;]4B-qH\K6fAVGs1TTV&\B;R12\HqN_(jH(VAQG=SV"ClMbo[F)9$kX`de>Ck(QjD#VbK3H"dNZsCi]B<,S:p\!6_K:CPbiro%Al^rD!_%j_O#HAjEGlLf-*:rMfoCB!>OK-O]djRjf)O9SF.fd2oU>N#lf7_,rB:>k[#BDrT'Wa87BPL at VRkqEp91t^$/>Z2$JWu_es3+M^PVKh$J!_m[eeh;7QDID.^iIJ\VH``+^>@4BQ<uW/^RhoH^IW>HcWUHB%-=L]gEf.88\!t4p8R%%ei1(Ri9;#W'urS_%>g[l`\W5VG:YCtJIWQF_R=+=s'k8Ih<kPA6UopO*$mjGnsN^QCh!0S:KRt[5^$]J#AMU4!5ECH[^UASs+(ut_/,G+B?JXqrn9YqeYZrcGhMlb4Y?9s,:OaVI:W.[B\?QaE0m1Gga9=GEMp;!I+^8+._8pC=C&KbVWZg1V>D/"V2Ta'"A2&hHAgTQ;'_^=$J7?D$HqX`pkRKGlITrBSD;ZbrJc,/d%%k\28E'*Hm]]Mcsi)<WE1=BAD8o:0Td<_-HaaRA?V^Q-+$8Fq";:UZ$siq(`4O?[VQ1.Db3MogVIj%?QsftGE5/Rfa6UW=ff^P?1&?%@5"\V4KHtg#ZsC(hO'*#40Vs#/V-+DA\_\fB<H8)/IDcOc.V%Ta7G)DhfchFJpX^1=p_C<FN=)T:,$j%O[?e:dN)+g0^#"V_^bc'/AH[EE`k`#?ksXbY9>m4qm8Vb9k/&rrN6sN6?!ss-WU4qdYnFomB?a<!]2_<'[M22h&eVH5 at db8JVDe>,HCQYWJSqONO]06K=g='R(M6=HaBr>A.TcLW>XE$EL`S%\d(2<kMgj%m[g]<qP-;<O:8;OGO+Sf%0.J%&)"CAar4-rP%.0!R[0b5;W+-1o#b\^T41IVehsW?/34Vo\H>G+F at Yr8%WY8E$XD6%L`MkON!`eL\Q`1no_]gGZ#,[HGe[Z5kF0Z9m3ZRs\5q0k at hN",obV9c%<Fa=rWhR8*8Y8[J>`ZJI]tM_q at Qg97,&A<hgZtm"arjBY)`SDg;^d*a^RmZ?7b2l1Q;rMSF#PUppE7&s'UOEg?(cOm6?b5h]qs2W_[0OPNe[)b+p5;#+IP1#d"*3ae$sTc52f0+Z;Z,93-fSRRe7,o<39+Rtm?65RB`323O/VQ?8#!dDA&u4oK<5Pl>Z%YuUmPn`dABk4+%R);dU[C%K<JG$j4<J3,AK]NPS`N+&b*51KU+8q6-%oee_j\LM]`,5&[b(eXl_73!-XN8OqYQa96Yej*Rlbi13RL&[G41"JTolHEPW?_Sjd\OAi["%cXX(f!%*CnP1V(<JI%hsV#m)m#6Q4heVan.QW)b)gl'?Dc4o\ADu!cI^k%X[0I-Ya5I<!>.p)1#n;taSqJN`ujrj$X2_TP4jmaD7M=!VLXj;;r>@oE_810@/B5Ue##dh2]1Bk$ul%^74fpsMS?q!4OkFQE-'L3Hg"EKZN;/(NO`'<1m[f6UDOCLTq/lMmB93IpHM\`5[(IlEPmLT:TLNl+tB(i/jCfT`74]kVl6-2G#uFc]j(S)B?<C-iuu__22UXP<iDi'r0kkE8i^U%-jPI<ISd at ik=\CaCYo+^60Gc&,0Ws<bcM0fJMicPHe4`gr^mgW[?ht/eAV%CXX5ZENDUA@?Z\,MW"5Op6qp2J17T]I&!V1n0ak0(W1Is+`FUCs,[KWCFFT0SZ)cbKM041E<$MLW,P;.3H_%76JhhU$crfC]Nki8Ce-Q?<+UGLNd$>;qQ!e>%Y#'dag>%@?E at iFm"Q:&2!6HYX#W3$E#nP]\@D^I&^(O+k:ktpNs!?bT!5Q<>4-o0BeQZ.>]\:_,$$os$*q`[sPiZ,51Il"L?T*")>R=s$'%/6b>1rFspbGgK98;<>UO/+%D<Rk,<tLC<(MqXg?&/0BZ;(PG+nnUG.K4H+el'AbaJ(%c^*`NkmVHk5";QTV-/fOs!u6oe;j'hB*CV\3L2Tg>&)B=Midb[d#W]NREC6bL/+c9d.&Wk/%>_5s+)uN4D7W!tN at n9;efA!f/aU$r!ZN8]i'gIhl7n**.<"WtUfUBG(l.iH(GRR(F-dCK/h]K`K`*8>aWIQXbTa`rL#5KpZTF?=$Y75Nbc=oFY:5Zt3rt]mF=#n!64\#a\#qCV!"c8LoT[.*,rZLZHP\2a&=Qmlfg#e$Ar>=i%:(kTRYS\M4j18r;^Q6>H/W-TC?R#*+c!NmQd/%>b_d]'X%H;7)V%+8Tn[GO)M/m7U:"VT6]AoS00HQdr'\\bqC6u!TH!&-.hDJYi6K$7 at Oq9a7B%Vf=[XYH1<4=s5)r^f-c;JOc,22YNobnSqRl"d at B3@":FJrZ51ktV?J$oi;*4&3)70k/Gg&K%g4iE8XMuFbEiLA#0h$]C at XIm?,mgKfK'0SET"s&N.#b2L at mLZ$#mR%h0,PgX;HhZ(1'^<t:@&JGf%7 at pc?`-qR8,6m3ita>bbYMA">\S^Kt?mBiG[Ln&qGRQ!Wmr79F-lq7Ni%>%7)\\oW5s at 31SXl41:TCo8ZAs%e`[d_)0gHH/Z]-+3K&%Of\X1]Gr<[DG+K*0Jk;[B1MfaYqoe:5R^nJlXj+_^InX'\Uhpn1r)M'[`;m*<X02;!k=5mO/mKDO[a0-i!u#eW(j(ABpdNnZYAH-e;YXZ_MP1^E71Eb3npt\/'uVs67u)!nl#3Mghod!$X__/Fb]q[:D%\H3"'Y$LhD'WDs:E=Pd!uCrWOsA=]k~>
-endstream
-endobj
-71 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 70 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 531.0 467.283 522.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 71 0 R
+/H /I
 >>
 endobj
 72 0 obj
-<< /Length 2956 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gau0F968iI'#+6EW93*LC.sg6.8$Kea6V6".Z]Fg9FdPo'I6!\G"G$Z_iEm200hcjK*"I- at 4s69C3DPJ^])U9jP/5(rT`C_76#Y.LenqAZRf>ckq)'`L\t(FB%r6i(N6!T,Xg(+(ne7&M3;G3=[ge-kj]:Nk*>.5q^>$iNqjl"12-)p[+?M`,1Ku\:mn,N.T%rC<2+>WWq,9m^I_jW[?Wr"\ADB*6chZ"\6K70Cpkg+s*R1_mL)]=P$cJ:;(=2D6S&,Gh%BV3jW1D1oXV#B93KeMl<i2pECnG6>tM%3?XM(W2-^#6p11EF4:sUV-)Eu7bksejUcKbmF=tYXKbd)*q2LT51 at F\C"A;$rL6"86+^o>]`Bf+::"-*g2;Y_4L"mE-#O3Q7WNR6J'N3;8MG`_k)c*8P,k17#_u7X+pP4g1qo at I'e\bbgqR:<mT7>Dk^@(2s#Lnj0B^AloUVZMV_pdfCi$&@hduppF34<6q75d=&oTs at K5k$N/hVMrOnBtuh6/L2ee\d1>:L@/,%[r^AgdJ\I(T3UpY-5;F/$6pRjlo_o0M=\NRZ at q(kr=l2>VtNkncJ#:"oV;()MTJZE=`<4=Igq$F<PC=\h^(O:LS4)8?p"FAp[FbPVfEZ=3[&]V5#lEh2mmFja7C;\8_o77*C!EH86-FS;'Is$u.]%%*Cs/UBpjiY;a;R!9?A8[lI>%`e1R?LL&H!f:5BH:m/16oRC.6m$1VWS8Jl6s"BG+,]tH8Su#)UPD1$O(l`8okNA<'3sWO\[O(%LV:@Bsc:.qs)Ona"/28U:EF('0ajL4:&Z(]$8r#>Bas7UK\1/]Ik.sr\,1#*d/,(o8)V^7LSr-P!JDr3n(4R-B%0I%JC7Zf<+]<Iuh-`$CYI*-=nM3lFb6B:3;B)"up84rREmk]U\#\EY&-!bVg_F[9a4Y$R_542rQk#5=<#?asWRiPY6rJ8A6_7k#i9"<'Zs#M94n$AdR/<F<K;7u1"k*OVBgGs"FgqLLF'!J]oVm+0:[UVbNu!o'-[Xo*48-?Z%q'%*lli=]o?=[,:,k>.e>#6/Jf^gX)D6cg./F'4r.rPFj"L)u7 at k2cd,V'T^2R3&MOkt;&,VNTPi22M5;Fml at T7DH:,m3Wq[442UHplnSROTmp5)fh1,:>U%&9W,Od6'*/?=4R>I]dMK1$&M0`i^D'/.H/%5S4V>4tM:n_o"TLGohm;U>Lld"A5:l\aKh=D[;US`FO+9puj$$ebSaiSP?fi0,*\>=gfrU;<V[k]&nL%4LrOb]flBG)>Z>QlS`0X4aQ0,L4J"QGE#WIW!6`m'jD3kQbD0O,ifX!QP7YRa*c0?t\,bFr;6g]87T&Fi`@Ud$bXW*fnUlB[bLS56bj2n at fHPlsi^%S+AFT?k"J21CL4pJoDW#_UXbd:0-H=QtV^K-l`'rkrAGa_i+S/mVFB[%/kGD%9,/+0,fl&Xe9[/75hUn'Hh0aE8/4u^`P'b$>mn?L7I>>g-#hm_6Z#P@)E'R2!m!$dEikhOmSH)XFXC[gq#Q:B&(c'!bfUo/tIoe$)gIh0?:3JO#N<4/(FhLgh[YoIUD_A at R%N[9J'^J8Z]Ma;biUq\pXAPrr,Fmi)\ra;B,]/%8UDE=B- at -`\RbmYbBC..sPhC/@k(q&$,jC6)\lgJ(h=%5hb`IcS]/*.l_"*Wi^i\gR*etc$68!R83ptb:m,uD9^3XAV*m?kgd8_3 at 6JbgQ+5FhA3aSY-;#q)*+VF$U56fF,sjko[\-7+I^^C8^&9:i$6LPn8*Hf2)TINb:BIWGI=sW`@*cJ!kFjV%_R=XCh,QSHs1%J4_tUOfL;M0Ynsc%f`J\"Ojgfe-'(5\[]<;F\m'l*d$ZbV;E*-Z6>]R23Ef(_8AVf[VR9BT*qMlAlESXu-f;3*:WA-[kI at _fE(0Jc9$j?<KWZ.j4Q,A\C`cM<LD+qs(qd\CO96C:OO7f'aU'Ql1A/T6?A:jDosrEDrp at jZ8rkAm<%>^u=Mc2PJ`Tbd'J?(?Yl]ega at qqO(GL1r%pYiF54"lLQkl<=+9'@'pldo6:*;R]K0j*%k5e-;_cPk\'HS,kb`"9mB^j4hmJ(hGEWQN];1k)BO7O&:9.*od[<Z(<:g*BT?AnO9ni%@=-Y',pfum+-&r':8_AoM=g87+AQ1`l^j?b.2Fqeq>(7*$c?\A-PFPHs,A9?S/Ibn/:$^]=K-t"7V9b$^r:9tG7_621B$BZ)3,/*,JgcDr%f"2[KPU:](k9Bt)NLj5]5J.j)f\(6N+hbK"k+&*::6\(.nm#nF4IotljL;. at l?_C#*bIj2hEqh/90"*H8%Lpa=]a^GCe^u)%-MPKJb at H-N;[H\,kRq'&+Y8r-7=.J?S-V0_j+VP60&LiKmIDE-]oC`ChEN;T.-U<!:W/-r!0[7NN8<uf6jH2d8H#^n/B?)W))>o/M[T,%a9]7=(P(';&4nNo;ISgX*Ib2-HcI('2=AM3/F<f=`nn21TLXAl6=C!Tg)Eo]=7aAl&i at Gi<'ik4^=g<m>\-*aS-tdkfq/N>XOC'jE\%8KV7R2>OL#Z:RBc2X>*,Ict#N-4;g73<E/=c##U&nAo7/Wc'7W7_j=7po at K^ols?LWeH/n?hlacgm&Sc['#Pos]:=oTG9CoP4ANAmDH]m3QQje"2Mi(&=PH(C?Uk1S>W.2mnr^T&7'$V2;kplg*Gt'!*.G1tFY/YHS3*$%2*&ib8AB$$g`fD,J2EgHB$1.bBPb9@'TY\S0<;mG92A;m`F=teRjq7$38IjsQ=YVC:YUg':fs[Z$S/gD'K534,d:V!LVA9d^GE)&2kN#+:S"Ie at 2TS2VRXX[f3[aO?[U_uE"mi1m63tMcPCGmhk2,MGdd;F@'H5pXrTAWM#gaV]pM;Q!=6W(HBP9TWsNOH<KjEqmZ[V.rt'*tF at AiOUHIs7Kqi4[H39&T2h0J^E#%nG,B4Rln&=gm/1e3'rrNn=1GS~>
-endstream
-endobj
-73 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 72 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 512.1 443.082 503.1 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 73 0 R
+/H /I
 >>
 endobj
 74 0 obj
-<< /Length 3062 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-GauI:>Ar7S'S,*4/,\NF80$Gs at G$HB=s-2<[]qn&=SBPOF=^CW*(iiWrVDX#,ot.(a9(+&3[HdI4=%[_,IP_fantbQo8,8M3cR;qiQPaY=bO,SI<Off_6`ONkEAOekVA8S$L#5u`!o&@?l1RL(4ppI/iYku7lA6D/;cS6j2Y(:\X_un054AiTqt at 7LjG=-?)8d2k9&n%fs'co[p_`@cJ4g(/D5qQZA at MnH9_;uc*enm*nTKM*NEjJk&4mF?HCuN;j-*b?K&7B2Us*;QN(a/rcL?=EnLumRcX@<-q-jaLEB at oT'I<`&,#FM\f@:`_+T-^_.kF9X=S<O;PLHNNdD0"SYVcMCGG`rSaF7c1LRFhPLJJL)n\U6?<:n-Hq0PZCj6TID0QU%)]lLf#URY[YgYX!YNL,Pkd!n at b6=1/Ft;m)CqqIi(4jM-()Q0ha4L)O2[7fWF4&KK]16M/9bFR`DZ;3D`R/^[[RLW0iIXZ4);tRSAk&@@8D5sj>%_DtnuVg#LoB82G;BDee;2UWnYF/9bkO&rGJe2LQrJ/]5gR;/KoQ7iOBi:+$GlW1"^2$(m>"LU7cS&mR(r-A5HHR2XrX&3JWpC5i8R$"k;?uC>lD-+<\[LF at dlar_s(71R9n=2\V1&WH at LiMh%1!]W83PAIp$To-RuB(kWI at RS8_=XW:J?]nfSnc[m`:bMW9hc5i8gsGNqN8)6'C,/DRKkiUC7i$;Fqq+1+f0"q+Lc7tReI+Xt.YB`-8V;sY7GGqglL;dMY^49s4i7J>Meh($cX+p.iGZOJPPCn'BUZhL$`C4%#92bDRNK=sQ.lI;c\UPKYJ^V]`ahk;:Gjf-\(0_'IQBW;2R8::GWEA&ZIaJot\T_l?V2\0gm9\A;jZQpKQ;:em1^3'8";cLuWMJOukMu0Tk\j^p23S_#4,ZtYEAli"jgJmYtT;p&.f]b!1F>Nc6J#dQo,FcPu!1WV7hL3[6`-iX`GXu$^qGCTbXDll)RM:dVLCg:Gm+BgmIR%1?e>Grp4_cKM.oALC&@Es!K-V*&Ynp,7"'j-G(^Ss6_*/lNLYl:U<lPi[,s9.f3K":V/\/mUXN9ZMpPl.,O3XL`QSSmDdGV)q*5O8gSAG#NkO,-<$=[+9:4T+BZJm8:$7Rs3<"U+t&N_"S#!BDgOq635aK,NfBT\)b7MA,aH:O;NqqPT-7X$^:As3?F>gNNdBZ:336PFO:5,R+e3p.krOb<7AT7N*8B+mDCZKJcfD?!?u`jN*+1CeafV.-<T&XjK-[&KcKEqn2&)ch`b[l&q`lS<Ctk;mfAmt1$F/nfDF\-Wi!;5_b$#uDZ8$#Of!"G+p1!5+T'Ljq_OoN?bIZW\C4WR,Zr_/Uu<K/"5=$UhD16!Ye4jaL!`0A=347*1FFj9-2E\l'^XL-l0dgr%lrM`Tm]$0YPQ$g,6^9ej]=)3G$TB_l(\3oo)+1X*WS(USb=&,KU_i9)F3;rF(a(Z]R%)t^r at pO%>tI`f6I/&bU?s"f;j*K4A.2Ce30\^_.mT]tF;Y0""P$:0mqgGO7q-)Qb:qM\)KFY&IlSL4`@r5>umj1(Tg;9lJN&_bb4L#b23D4)UET5Bn[W]7=.S1Q at +K,-.O[4 at _oUBsV"O40_faI/PV$m=]J^1)Qgn`"linbQ^CTqoo1b]K0+I_<5l<8[WIiTBk4>`j`@G8g+t$cU6JEYdSaFd`;+W&&H!Zn^tM*HqNH-./>;ppA62dd[>M<0Wg=Cr*2NC^3eI7UP,*/k/i.YU7=/#_%oY,Q-q*<mkDRKo:gH$STpWYdu7SBg%Ufa&.1?$>n]='*A&Vg'h'H$!JdKn>/-75an665eKXOC(-hBDeDluYJRHd2c?)GbYoQQ/f*Y+<S:lBhl-jXs)nK/fVNk:HD,Kl,6Jp3aFJg?bk#+0>ap>i9tg"MI=V9L8\9Z[4S3q!X_+V5(6tcKpDoTUEauEt-%4&)qpMQ8XhKL^3298cqg=G_%p#6k&@=t??8=)2%s@^a[dYOc">-n^[)L\`>E$m<Y7NMqhpLrIDVnY.d:/j)n&@4RBfN3.h3S>:D"g)B\ZiWtjDmK;m!DU^QcFGX,8P!r:qFQFlu+I)7Y:glXdfhJ^kakL*f=S0#ce3_hB0/h0U/DM$MV')>J at uISX>d"6@*&?04EB7n?K]C.3uh&f*Z<]::lE#Pn>j[%SbZSK=-%^FL4?e]G\S%r2!Ef"ns3?>a-iG>t.<aWi!@UcZMtT^*&pRg-e5Ffd@>7=r^T9^,BG;2FDH."`;$ZB&!BVQq<*mDC<I07#X.*0S#8T)r8afX<V'%B[N]SZ[l`9moEISS4D[(][goT@\YHN/J_<iaTX9S"RG<+)E<F+,Fq)$>F"E`3&g7h<geu_>g<OKE7-8Sq4- at 5P=['nAj\PM>g<OL@*!hWkN`W.F7hhMS%.DM&)`]t6;);Pk3=a9mj at qUn^fBB'UIUO1Tbbk"gBKtGK:4Sd*3NI3TkDuR"KhW_FVq^:hIWckaKM]g?r*`c35dbGGCh7ELI5A25D_D#@+/g*T:TL"QE\5JoPH+A6aBp4?VMLQWX+gjC3:gW;?XLk5KOQ/C![2?kl\8hjX`_46fIBV!u6"I<_']cP:mZh3XpZ9 at Ou_9?HIX/BQT/(4"(M.=&]+q,^9`1YBL+nl:&!@^517ICPs'J)HnoS at Wh=9J-IuZ/+#X&C!<q[h`5o`dbY[j1*.BHsGW>F1#e at Y7jopfa-P&Xdk at Q!@-=7eU&oW-(e"5e80u,bM/)Ek?%9tpsh^.0$df>hX at dTN_^i\X['.c#0XLjJu_qt!\:JfXqNA(=;RF:66fPs+01G9%9L`gXutie2I&8[f5>G?]L$nh&iQdYFB?+e.l^X9c3>h-`=JL_>@hje!M;gt`!XYMi at DNK/IZll(PLhfjXfm&lS0!ZKK:>_5T>>KGOH7mU3(B&5K%HY/C98AnD+`5VGEr#ePTlt?@5&-fN#:dq#;).m[6iE9S87CVW,M4`2Z3.':nu70sN/>JM))!./l/UJ6u1o+)CRs5SKu`KN*!%V\g.H=1mlLan`.*;5ahOEY*E"b<Gf,"FV[>2#RMsn`]W~>
-endstream
-endobj
-75 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 74 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 493.2 353.208 484.2 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 75 0 R
+/H /I
 >>
 endobj
 76 0 obj
-<< /Length 3265 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gatm>6$q at r&\\'CkfZCi)`&a.;UV8`(H^=$?74SthX.$)!MU\E0h!K5BLhsc$P3k(O8tI>g/BP63-Z:Q)8$jPo)A"9rRpqOE:eAj`F0=<VVsX.7nN;r./m\#Zp+A-FpSU&BeiDj75g9X3UG1iES,')(RG*U\Sdq5_,U3O,&NOf49-<RatAR at LHI2QFJ-f:a]b%9#gnLP\%e`681o8(<Z"<Ci@#V`_P;?p4qFs`k^GVip\>:R]NT9mnJ7J(H9Jiq;u,?3?(8V#Jr'H>W5o=806[g`&"@Jel/q!ukWbj>S)Yf-ZZaH&Tf;*g^&_PIjVs^S]eWrr.0D*MKVKhB#iE7V<gZ$[VNJH/9W)Lg-Q>>4b2]L7dlMZT"gb:9:i(5XiSk_Vn$K2WqY>6cd3knRqBY>MdsPa9j*i&iPCp3`[$/SRd)k at detZ>"R&*ZA`+nTu''K*+=*;=qR")smXUmuM;+QIOd2AScSLJN"$inI[6gJ4CiSTM)EU&)N'"IaND;djg"cK<3#)L.F0'u1dMfc^JIMp%Ik!'s+o%sQ at q<#SuSns>Jm?1r9Itc"aCA`77M at +th=*T_C[`fQJ`a\l8g;h\A'@K9KL+]>E+=HFD!B#e-Xulgc:'l>fQ\OI]]1EP;dBPc-qfJ,-6WgPYL6+SkJnPK^^M(d2'eS%uZ"Ae*>'al>O%$<[faT)f_",7%hQg]e#'_bXn`$0bs)M$nS?uDtGlRI5rT3Fe`Rp at Xg<JU:e_TW[qto]kcbNjVZ$k36 at QKS)'mHC6s8M]B4L.?inQ+WJK2CW0Fp#5`cComFg0HA>JN$0fe48mW$t9Akc8rM<$jVkUSS#]CB9fV;^L3XO(?Efj4f(MCBN?Y$!ORS0m^m016Abi-)Le]jJc9Upb"?:fiu:O<!m(A"MV>0`dO]64=lb7:QM@</?fVWRl43U'37\WgN9lV"VPH!6k9LIFki&<1eON0o8eQ14+_3@!CJV:8MiZ"u#TZA+%bbYd97G2`rcGWeV7<@9c#S1Mc?(0A`CXW"KTZ=l7EM#0QWJ]Z4.MKlan75n9'Z/>0O,'gcC=rZeXX3.5WUVDIk);!oEV+Yb?0&4DdLf5Y4sK85V[*=$ar<_[2VPNFT5Zl!A!S]`CV=U'NaC`__8)HJ,&Vt;LnK\?+R3ar'RVQ\D4S%>\'9()'2[>*X1N]*#8a7ZuWQJbR0f8W'n>(HQH9hE`BUNL)Btn_aE,:/S3j&$]L\(,6mF0LfA9ZaBir.W43RN+!l3o!0PK(e%r9B,"@-4fX/&07E\<MN0O7"O(\eeF:!.]RCgTgZNTo_FpRS at Um>grIEa"m at OM2U^f,FRl\):Lb&0Jl"_FDq#\\FqFVf:dFa*,O]4lWmeh%$3iIK(?A>[1Fm7Xf4q%YAu\O+MEF8JhuU8n\-<*b=>Vt;RYS.SW5I-/K::p0QTVPH3*",`ES5-=.O2sq;uUp:;/E+c.i>*_.77"&:uWY5 at .K^4.*CVhR=I`B59G;3.ql_?"Qf;2c:0[U6?H`Et0L(H6tm0>8b9!i1-]sp/-k]TT=Z7?q$6W:Fu)o>\Wiu20%F_+S<:]&+tgP\ICXY/T='o<eN<AfI,0'5o&LI=e6%S0U969c6pQ`W:=->n;`5tCX^`ZhBIrO7ua!Ta at p7kG!(EZ5*&br+7L%tq712kHS`oYVD.):j?ha/o`4-u:&4[E04aO>7 at 091m1".lmoR2YOFH-Y$XF+;Y'(\oLZYlV0h+9r?pgPDYQ?H>#IQ7jQf-5Vc*s8_Yq8BX(H&[7VqC4)t6-f,.XT3LdBO9(@&#Pc?Nb#67iYWso<00*M8q"-jNgI3K0W1=I*K%H]&SJO?]:b"`1NNNSUp%t%1)ik[@/>9(<9EZ/m<.Nc$+ImUs2kX^M^^V5YWm^i&?A.iWQ4UKNg%EXCToH=AQRZt*i2K;m'f-275_j,ZrRc07^&:t!0n9u;9D-[DohC<Wt(g[rC;JE"uOCYS"*R"Fjr!$I>Lq?6DR*MUE!mm+=$&\cRK5H;*Qr^ruBItHFh7Ar^GF*2$'@Qua:1sQC;i:As.1t<AQb/\>35]Fq9nqMge%*iX%7gYAE)E"HdV7i8#3E'J(P'7l&:<U'd:@EN(<nq=3neUF3lL07$,\ETE-s+<H2Bb$1EhLg;(81jLZASgo2A*8majnD-RTPkiLDTV*t_]l6J:*\$l9LC,G2*7X5dM.:-i3[U7a03,_.NT^an:_nUf`,d_7m"eN]XXmRJ7^(Ie(f;eJF&IpRO#H4NVBko!@:ZY9fA$1rqCe!_Y<[,bP1MLXQseh"-\:jYf5'R9;rNF83_#X20^EiZZ;*.EK'2omrNgJKRi6Vu_JNp%&4rk9hDl3<TO/NDbY3d(ZN+=Q-o1CC/N7F:q[dN7c:eFW)k:(oE?3#;'".Kt+s[*D?E;<V%k9$W>X(ia[gg'ZHW944HG&#d,DL!A3Kj_"V!BYDX%E=hf+_EHL[cR$'_[ce#-Yu]2LjQI2'"`quOR]`L&FQWs%CgN]*;Ye=VG.8^%"=%='AtO/i-hf;ontn79$X>mdN@/U at kS^fKOnn',\np:'U+U#Fibs5CBJ]shXlC3ip.h:15g'3A&W!"nk*U!Wl'jTY*@G&hDr#)=VS&5fFpZPueJ0gGPmbD70/R"hNUh)gJr>LKDE&o$Rj6>1Ph[5l3(nmG`f._b>VitaK]1uDa&1ALBqJ5dQB"h.JV;=@i916>"/k(=$H2;iGtT*MlMc?-biB_T?h(j.pOd8;e5TSOoEaf,o-`"Kj1^idej/AOZDX<9X[J!p\FM7\fZOLtk!ct8dcC2o2&Lbjnfd!cWEO.!5h6#(e^<=rr:R'ni3)lULi;Jh7<("l@]o&%B\Fl+\'l+d$\(GW^Fo>#LoIE9(:1[s[$5Fo"t3U[iqLU,C.754U!sX-;?4?(])[[0O+u%+9e6,>-*;mhZX*dFF]oL(=odcS&l=5MO'Ku=@3)%7.;HZHo!Nb`J>#26RXL0$5P\]se;Gt-pQW-Nl9T'GHARE&dra6L:"]ta?>.>7JpB<$gT-=,*TgGJ8ZF;YahN9QMLWcd#lamhpVdNo"3+HfP#^mj!>ji;Oq<sHA6RU`.0Z%q.GP_mW(dL2WhXMcHh59*TR0*&h]1HIIdL!/qGcpVAeX1^\B/^jRtWFJl*.r>b<><SKUa#c^C\'HI$&!JKp'og4Lrskp*uDnh1/`38+4bMmB&=6J?q'<:PTTE7?T'<s*20cH^4f^>jO@'FfBZ\@\T[]s-141EN^Qk9KEuXK^pM#WTZ)Io,3)Q^E)G&m/~>
-endstream
-endobj
-77 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 76 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 474.3 294.465 465.3 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 77 0 R
+/H /I
 >>
 endobj
 78 0 obj
-<< /Length 3241 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3327 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm?=``=W&q9SYd$p2u1!NY;e!B#>>>@2H`6oW*j*cG,8A^eu'HXMB^OAHK,b#K5@()4'QXi#oNQ1_c"m4Y7iktKLqh76r>Qgbhq6Qp4p6&Q055Y7VJS4k<D\ql8@*j]\r"]7bL]i1"(JNgXMh@[-=pe"W*QErFjD1r/,nhPf)MME-MMDTij2=f+(b[q8+nsEQb,^TJ(AV=HHYLBV/"?dL0OBS at fA9ssgZGiWhX"D8G'4`MNUWG&8BUoNr/=CKJt37#.bt&En1;Q)(ju;Sqe,p'IqIG2@"7L=g]$3.hd7?sH#11Z"[i/q7<, at Z=\kWT'\"1*lcUDe(0mqM0+Og3lOr*['P/4gQA!X[-+l,+6/lX93b0\<4lt(j!_YFA<bFL=Yi+/W"o/1[Wk`uV[:4mR#d&&3;:bd0#Kgci:r;p@]-GLb;.gcH/!dJ'-PZUtKr,WAQ7/lF"!J@]L5*ICNX2X5J>"9loQDe&cAors?N,[VMT(GoJ+T16GU"beGk:FU@&883;SPm<FN_$`\,EIIgV#H/If[T?\@2]R6t!XaCL"LbHI/k:UKb4ei2BM,*`7doAYPn>L5Lme:e/OVPW0PUE0%<qS,e_X9&DXA>BPekO46T>?[]#YAN&_,e.ah')VN7>*@s:\hIEq)54Tf)oMem=WnLFQ1B=G)/+G#AJobP%MWEi:%+%B[]mS%+qSWm]83YUOc:fWWJ=WIsK=G."X9spWri,'*VNC4Z-\0^#nUfj,b*TWrTu)U,RqEE\J7kmV(?u_6fXUqC8/%iulJo1n=oG<W^IVCSQ`o9+a83Tr^\V-<4j!IHDettjBX&7C_-/uQ=%XZC)(6Vc#J6KU>?81T'kSDm8aF(sKi62>(F at 2/F#8*e#o=>4EaMX\b!pPIA29<g%_2jKnfI6+,r0^\@:W_N`ZQ^C%mcu0;4W[qUZD3A>.NO[9Vr>#C0=c:Ju`bp:%;mk,mDo=7B,g76:\Zq][3KU;1_u!^cHFfQ,r^N.^3OOE+h"f()ma,88otC(Nb28[kQT6!DurnFNM#lQtE9J9m$HX:DqG4X\*Gp9K;(eQ05dgnab<&Hfk9NpR$9^$f2mcbY>+08HqeZ7pmak,d;e"ob5rV[tFDkXW9F*(=</K;&/[DhX"G,_K&nVWZfd>Q?X-:GGs0MM*mm7!n.3BNrrSq/$'NB$?1'UmJ at FgpJ$L?V6pDE,d3K+"0hlEe5#\T#,g7e68&YjU%O[O&:.J?F'SSk3T)D.Vq*B&@;AC7qe\*"?j[W?dc]Sg80R%6&kT_E\qGDCGnt>4h3e=tF`eE at KUbDSJ?/:Q3Q8lcQp at Y[>DFFMTRb$833A[_F1'%'6oqNdL3+A75<99*q at Y&bFAED>TJ1.KB`%`o<'"*T!esF&@^TY;\WEGQ#\A.ciffJ1dg>krU/Y4ZCg_tN)R0&bY_K400FM'\TF5#8k-di0n;.n1B3Z/s/T7,=PrG\7H0?U<j+tY(+9c.%k^m2Mcn3=Jo7Q8s^>`V*/=[72HO!i6*](G2_>;6E@:lTK7:3'4?\dAaL7R;(5(O%4mN?f^:nO'bDbVg6KC]d*V4[o?JaXeTs"g<Gl*J5>WoL;l9sF7WVd`oR\U]hjTsiCmdEB"rJ)Bb(Dp.R[U>QH;<[/:kKq&M^JIsi"<X$[h;6]@])82-E#*!#Fl(L.EQ/#te9dZ_Nj2+ at m8"OX'H\V4C!EWkq%NF&!g\ClVi7s!`N.+TD#$@pa'jcTa"'gD46U+lpcmlG+_$ZDa@#YS&NL#L2[$jIb&p/Y>Zjs]Oj+Pi.>_u5QSJXY/"9u5pFYH6n<XCg;(7MP"KIEto8!nEB3oq[sM7127Jg!kj9EWq'QYKfK/YhmghE-JU/Vb<Lpc5iWi_Km7!=6 at R)M2P$b_A?\CD)+g45I-V:WU)>DmAM`LGEAc,$JlF"m2d9gJ+P`].FA>6I#a`^m7:Wk##ifCu[D:s-L8P#/aJ%)Ufoo#5L_mK1:Fc4Aj!!^LqL8:+Errj'[e\hr`0lbY?4:mWsTK`-3\%U$"dQk#^9<=9cpF&qAtdG/^LR0M=\J1kTdq?/jb1pATC4K9XS:A8s:iTi_Z5^_dpYO04++=k;NWo=?XuT`#`CPil-#KJe*4h+^;r]!LEBL6@;Z%Ia$:-("b$MP^")rR:Xf2[2^C?i+GXA4s/lCf-N_XLS,fE;(X_3P+>_&7+2Jl-/X`SJeHh1KW3=cql9WJdsU>aI)2g>('GG!Hh=>MJs0.at$XjnP%(ccn+82Wi_SUCW1d5M at 3DB/M=(<Q$AK.jCGjPZ!/FZ-fl?C#-I\kI,!3IaWN'PCl at 1cbV?Uf,#j%L)=hgF2)<GAiB<:8m("[h%[_4p%5f&_3m-Sg>E)`:5S'31mh7E-1ni]R*"q=1cAlmh)A<rhl>.#eg)>dN9b7i%D09L-\.j#cGnP<I=bDsb]Q.k,Fap5H/?AC40rp)hW-a>L at F[kKGB;"b,1pa59TXiY;ueiV/1Tumc?>NlrddAaQn)eX;hPX:^`bt3f9_+%HO60<6B0Xu^SLW]HlYLInP)144Y at 2&N;A^,gTnY\[jQ8F%[RZ45hI<EPsti.O&7W&$9CuhSDk6:PWgUQJ3&f>9[-#1DQCoo%S8Ys$3eVU&o8,V$M[umL]/-ue?E.$R3CZsL]K(&=i<iKXbOY/Q:V0UeNE%lk_&kM:kBQF+L;6IMeTCPB#`c:0qRCC1$I:&XS^SH3 at H-Mm7/JM0BF1h%o-j at r(<Lpq`rn8?QI(F]nd;C!.V6<X0]/.KaIKs1DK=gf5:_[n])b-Si=kI5k>'=;SE at 1a7js@X"p>]dC165RK`PUZU'`LUA'M"$YUsT3Z<mIcRa4i,^g?S,nej)_5=n,(@7IS=nW!=q>;\o\/X*L,Kj>Adi65RA!4-gBo(l$&&l!0YpFtjR%dgOeri:@;g_e>SdX;+Qj_N?^.cHuV[O.E#"Ag&=`'7"'4NR^"#g(9l.(#HV+jlL.##L13Qk8g;NtarE8eX+!P[q\s7#4']Be3)^eA?,FIS).7Ke/>0d&!mEMbn at RW.P<(h?t/4YYM#HpLbJi4>QTs)EJe"mBY->uS&86:S7VLcKE-Eb^l82CnBn at AdY)\h.!#[6"9?]TdKqhC=,\1YHJT2aHq/auL"D(bdT0cJD_A^0"@.Vno0c*mCegcbX`$dJsgTeg*GTS/i_[B+t&:g2$`ib-^q?`uYJWrr6>I:?S00Y7A*RJ"rqHJ$#mY'0b5%>X^K[82C<_?Gl;B[6al[r?')\DF4~>
+Gatm>h/h=1%Y)f3iQ9!6\OfnO>1VP/RpKa]D>1VYG3<HipV=jlXB#s>I3eK8J"S2mJ4s-=pSK``D1_for:!"6YA_`n^'tl5Eu)6S1M?5sjt7YKj"B>IKo=%B=WI9rgO/O`(Mcm+1/A9T>qW:nd"+&-l7K40-S^#$Uh,7/gIh$n,:<./OZ?Ue+*j=Fq[@uI^sdm at 0)iJmI=G(5'u3Vg<QL\:`B2BR@?Km@<UVg]h>[3I at JI&Wj\D'5*_C-^q&,DH$BZI-;nVY+&\e_rhh&l=;3Z<\]:C at sMPmR)/F'=[PcT;1V)q+E=]c*F!U[gOpj<;nhoJ];p3c[Zfd!.KD&A#O%B4=NkdWi.$-,\B^ap_<Y$:D/JH^7a=3.Vb)JbUL(^3%tL,LH0#$06nC&QL-rBHqiS"">2jkn@*nITUK$QMD)i8;nB]Pt,(*u5*>"qa7qB/"'MWn66V'FidqbE.!4WH.NA;4%0@]Ya6!na<P"(6'/QZEPJn"h"Q=G#^D/I<TCc;EnW>ch8A_,uUs/JptQeECClt88RX(K+)$,LB.T?7M-aAH`dJ1>".fRn9$$Lpu9Et#ud'OV-/Q?JU,dX2+k\NE06i9;q8P#S$!=uOUERNT4F.fN<],$(A%gWjZ;3?JSSS5gErkW`K*FRe=ML$V0jKV-?a.!Ia#Jm?8PCqhJ1"Rc_p&O*k*F6ht:*U%-\[HHV!@WW%&4/qn9tL+89,ks"K1X%E-PLKtY8@=_uNAVW8F'>qmKT>Diq9*h6K,L(?3]<jI,KhWBukJ&BCi1aFZ72sum'MHFCsZJi,h at JQ-g88o^q[]WAD2;q)kHXX3h(pDSt[iu+laB(jehZ-oraJMXm1 at kDpWQf[eNR*U#>ag`$h-h!b7Qg)+:NL>[bR(dE8;n$mCK`VObN)T*4<U0`IJ&LqpX!J%93:aU7<f@]T]4kt?JJg1n;2HAf,,BZ'iet^b=!t?RZC>NhU>OSR&p.U0p>i1+QKBAf$\VV8W\ji-qbQ@>#eccd*QX=\f,?t(q;r^q\-TP],\FNZh8]]UC&"FbP-n=fCaR?roGlf-=3:KO6RNt/H^&A(g;UU1ERh$PE^5f/[U*El4?FZ?6Lhm_##JP0->Jj@/'2u1^=-QEbjO/:,?L>['YA3=X8YPc2/t05-sSG]bno8P4%p9gS',X"b2'0q&nt=@QIah[8m;B3StisPgAe'>>GLN%/NpTf%#=c9sb#?j\Ro!3Um!:f/KX/OOFjrGqNhda.$Vu"/oQ(,u8fKa5W at S2`(Zu?ZK;V at d]WVePCKB=:T2c8A4i%oDClHkCa_UF/QU<=a at PhnD%-&5cMQ'`?3Y>%i#:V0YQAj#K^K,3lC^BqOb60ht7o.;n.Pb$Obn2\o:p\ZpcDZS$V"u?E4s!^Mk4I7_3KQ^qh(rpGDX%TYW at hp>Z.!QBnqf at Oo=kW'BYd4-?gRGN,mXoNWYG?DLS=3T)a(!Q2$Fde+jI`pcC/ie%$qNutOniP:/Z3[10hTnT_AXf.\5K-14E]nL!ZOBdpEn<"q=J)g at m+/?EB/aBBMl7Imi=$e4,=r^aZgmn,LWkpOu>DE6:!$rLcUuq;OMRK-`TA1m[9#3'uT$q&dCt\:IQM*D\ooC)BZa:oh?Y^E8oT?PM?W>6u:=I>dET at N;EL0C,^1r1J;5[!$m\B!qCON.4&C%He-cdS^KEabMN?":"n>%JFV8jbn>mN:4*5Ya^VE66Zo3J2_%kr&cQ3$TZ*'62?:c"(Cc\'&uMQ2s2H\6`-)DhB4o:p[H7+"\.Pg[aHcjN0?*R)*>^98ri12k[)1tBu>'aRf+S>Ca6oq4RSJtCX>5CeC9Z$bD=odBr(68or at kF]>&4P"LpO;p(&+ujD&2%fa4-2lhqU)]d+\j]_Xb$_+W1L;'-\.oP."BNTW1*^6:'h!\p6/<_1f[9hdBalms1u0.E@)4)nBe:mtLsF.C<]'qo>$[+<9kT(.#R2=.,RX%D3%f!nL/kf:14+7gYh3Y+CV&c3i5I<iHYBA\bEFL/2WmgCn%>\-B=O:aV^:Jub at HU18*bU6V+QWul-nDN]WMS(\@nZ-niPOmZi$uG!%mL:cIR0J[q%;-%X$th<N'I!:"4*lReuJhY+Ls9H+p,Ej*7X+EI>Uo3<Q3*j41g.POX?-[FK>5,F1l1Pa$dOUc6]0)C=Wc8'>Q_W=6\kapd4UTPH at a^"q]1DR at s,_:Lu.j-57A>!V,WiCDpl:?aGN7Pu!WiQN)HN!kkFE>M5;oXj6L9,$A[H-ONfMF;)WQVCA=.p]%`5Sm- at NnUs+KI>a27;tmYh$suQ+tFL7!oiFp:,Q.,QY']Ci3g#WGj?@rhRY!O]546d248:MVL7M[hj.O&Lok9rRN:4CoE)`hUq&)lDUVk3VRGTQBbe2^UC$*o(p(YA\hVM-W4"SqGN8af_a-,]e<TsC+udY?BoT"/cLB_<6*a$)c/A>DjrMa3>%<d2r^H8>TJh8*%)Uo:q=WHQ!W!Dq[1H+ZeTB'>,u%Z)(Y0FnG$,W7RW(_1 at ZNMbm!$Z#_2ijjn at KJ*]<Mn-PM"Jt1SF3T6ZUP`3fX&Oq&9$sD at Z*>#"Q2D3%77t:h5UhF[d?Hl0_k;[S,k-IuYEO8fTLl^tsd:q2cd^fFO=?A(XN19;>82blAbBQN_IOY=ZrH<[TWb3^&F&reY>T7lQDg=$;O_Nd?`skscrq,STWG';-u(AU2d6p,JSnW<8kAS:O)&eLR.ro:00k7@*'bR1cKuOh(.#>9Ngd"4RIA!2c?hoQm$gR[fk2eiL93*&j#,ka5q^-S.V[hkYM3A;>p:i2kODpe!Zc/kJ%f-<1g_3"U4n7DFP9>BC:k at 3u"-6#0)\Aa1i]ILm^g)d`,0U4Piq66LO'eFEgVSC[K/Q;HsZXi>I%#f:N9ZJfNE84S3b at 0Ap&Nqac\Aos)/%h4d@@1VW]WdpuY?nn#j%`@0^Ul7[fo;W#DWG1KTZ<o4W18!^;]D$Dt+^3-Q\]B3u:*_'/4f7#9^)%9#h=7hG'CSJrUWg(-PEB^!PXagNMp:'sR.oTV7WDfT3m2P<k23"'[iCggfH*S[&)5fAMPa<a=,SooN'Td+3E9fbC5)#O\ijQ&JMTR<Eq7b_`JLjA0W3,SmVr+.a(G8[`E#h_7I,9JP0KsQ=SN-Cf+nAb9Qcd'I#>R:V$b61n<@V,b7bCja;l+C+10d6Ha6C at 3Nims4m5AT#,>F9M5?J!]HnISDG+L!a"HCd1$^m`Jilc-0$O$d?)()^p,`!&&n[=edQuHIbm6$u2]6!?D/b_IU%'HYJtojO53H>Djbij6mAO$e at lV%4-dV2h;O4?34XC62[gJbS*uaBWi:K6VO"CM~>
 endstream
 endobj
 79 0 obj
@@ -554,10 +554,10 @@
 >>
 endobj
 80 0 obj
-<< /Length 3226 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3500 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm>gN)>a&UjCTkY:*UVKioUVlB2t;P"f]].;,QPrP&'.*,ja&R'dnfDG4A#`rk!!.gR!`?ir,i1P'bUV"Y6p"A0tm'ecpp2]<kY2df:GNhN%Yl3t;?PT$sc at .shL5$a*=i?.18$,MD3PNos$J@*kQ@*^]3B=\n7a,JL%#'ZWBjgKT><C/l3X?)90X;\H%i>2+5'>AsOB8(=_ee'"NanI(!GC95&!Q_'V6btjlbi'2+"B\P\Ca"+7?Nf0q<ZOjfQ/,mB_YlkQP[%-GWu#fD#FOQ?,h&,_e=Q>H;b;U-A/"1p3jSagF/qBJRu;scY'"(P0aM`^>?Fn1\=V"CRjACA4;XB\AF;7gsmH)`HL`R43qJh?;WH4/5cA#X8`SeN]iIGn at K9D(l_D\dXnh>"<MX.LVTTM&E@,GNdK%[rbK&;i at MqR%lqa.;5>MT%<9D7KRFCfA/=0a'4DJA/W7$j[4i;7,AF\u+^DTZ_*ic-WEa>>PjX??Ib]:=;XB4>qL.#@a7On(KJR*MP37Qlc&]@@YhYsY?KQBj9-';Ao*Z"K6^<HtUm%]PS"^[>O6jMJDte$N1oDDS+[O>!)AB^@,1di0]?8;*N@#B at E)KXIKjt6W,+l-)c'W`S*8u'5MSN%NU5S=tSd<4C6/TgI_cn-MbXa]\\=F'p_!EGbCCd/d[`VrS2-`'Cr3HXah8o:=NI3U2!0u%Z042e61H:D.l1R*np8At&gSa/AT[+'k86rK?4q;eVaDk at qX+MjT%IEj`.QB8bG&SeaOJV'O#6./7c%ir")D<T<64eD at O<-(*J/1]ekX\d)!>Z%IHcOBMO]E#_oe&80`l%9@%TaO:>OFV$;IVql7NTN3EmsUFI1!+uhH)pk"U)ss/F_^9,9f._9P]NMmCWc1eO9^q1rgiD^m!+:<ji5`+b8>5c^2cFq#tkOK<EoaoE+H.)r?D;-P_^cl at I14:if>j2J\jR at r>okN.KL3]]p=a7Pfd$0Gsc]U5>E!ddOITRLMrD:17gU]LF_hkgGQa:Nn"mBYpVM$m%?]Tgdmm0^a-M91'*mK$&q1nQ=og at +:Yk#$jXIKF9a'dE/ZbLL[t:Q+&0,KCTs]V\0>HEsLe`E^)(-P7t?Rk%Rb((1FOqrd:kGNQ"q>fa's'8u/dMB`_1A8eZ2\W0Dr\>!!*\KSeS&E]m,tW;Zr4mT?kDN%fo1>!!*\5SuGW5S%R)$OI7c$*(@&:PJ)2.cE7IBp>2)-65U89,A:Vm;*WV%8?tV*XWmUn,te07$^i00XN"F@`XB^>aA>W1uZ-a["qYH"Hb8I_32mJ,_83gi2$:\O at _`eJ/(u'WqS33AA_T_>H`;Z_Fr'K<<S3q^ia+JIgo0&*T.^Y'0M2[p!f at BO^/+nSNP.R06"#<kLRq5BAnph&4BW#okRaml)$`"87]X]37cg::^gb,":9$,UmD3)Bl'eX),TAs`$Pm`=jh'`lBPGHICCQJ/'SC.)-<7.:Z);HPVnCsTSU-__i*(1ronF4iAciEHqm7#5."Nm'g?Q7G"rAa:31h72/]#DG\^qZEGfH#37q`l^Ig<c&!FHG#Gskf0XRO`)t`<@=qpc!0eGVTg\Z#h+h.[[c>[3*6ZIY8]B])*0Pj5[Nkn?YcW?IN`Vnh0&2]dp@(N3fT`^`@/c.FC'-5f0C+?ju<IdGHm4E3h:jE!/I'2dP>54)@9,O+5+Qt`2"]Z:'h0;C%K&IOQ,97)cXqNf9A\"?8"Tt34FI<9TMJihqcqg_=:p9-kV$C;)2$4FYD!`cu#_@@F%k6cG4\7G%8C4DGQQYL'CgDuDYd^C)gLAP4+l!<(o^<R3=FEl"eGk*Y2m$C)7SRhp8e3=[s2:%uAuG(aAj;r7O1.MMC?$'8:.MH6q33<;G at 6<VHoMe7iFn1tT!+or+5!Eg_U+%eU"^qHN#oHa*bRL81]Ph06S1P/5''(163 at f!%mol6'pt7jpeQ&.*r*o""Rn]1J^(s_OUO=G\:VB=_4cAWp$$e4g?#9#i252"Jt1WCJt9]J;sY4Y)<hH^RgQZSZEQr:\Xa0g]l<1EVT6>e[q,Gd7-oVf0&2n(E-cOd)g29"2a%Sb:^Tb=?AY*c[qVCN;M#rdS[.,peNBYj>;t at oXfWM0gN&dSQ<q0-"E-<pCl$RnqCC8^E>FTSX%FKu9dVt>jG]Y]Tsr:jp&-*+(3.?83t&oX*L\+`(s^3H0H.?A at NF?YUs#"$^SV:_%4rFca,?Y;>2[YbAZsVcMBG&@ZsZ%MZ8T?j5s78=HXMW*p/EJ*-3`[,'<(jue0D#p*5+s3XRH.s#"3mL]=n7<^i!n!q>0aY(]J@'<)M:DFddoGRcZZr)HM:fL!*d$dR^:Td)hA4X''HKWh2jH#_AE>+;Dg)/P<t,d\Ir*Ru")beYpNHQXZb(3@,_aB;6J3)*5'&3u_/h]UZQI\gqQN_SsF[CpX;;XJLBu>7OU!/02LK)u="aFRVVLY@=?ol,$N)/2>RLaW$C;<@u"Ls6YM0/85DdU8B8[3`>I5Sng#MC=fM<iJpP9Bp`1[%mSEtKJ(p>,YP2?PbRR';K0<N]?"1dKa""2[g#i?rGZ,kmjMtIrmh;$:^;24gMiKb[YOP'#&2qZHV"LW,D/k7kW)pIafGOb1:WnN:kg0]30_C$`ioM6A'NQ#0!#JsVR'*-BG"@Z`=d at alb%&\F]rGTVt.!jfQj?S.V9qMXDgOb\,qe?=.RjW#$#r&1Y+"0U>)n at 8Zc3V53Vuh0Ffo>&@KA/r$$c8>q!M<1bT,JYY&'<QeUgFnFR<(:eDX2iR!*.n/IVTkVIt&'^/Zl3`O76gCFL[caCVEDmW=N2`+/4m^)nnJSmH2FPph+3kEoUSf:k`DqrW-35JEVA2Hp/W2kI:GE98Jqn7C]TGES#MpP47B-SFIjeg"kHdfA;(l(1+<(nE/bK&H;mc)tS?e%ciiR^]X(GmTCqa5l3*?Og.\I(mh'-YUd])l!H8J;QKJ*E%TZEL0^7T(:PMaEV-NC<R0T)):\#/e*g68G[6Kjk, at V?.IL)&36C at iZ*9 at 1t#3Y;p(HqJSBY)sD7BhcKa1N>l-NE*fa<hbf(bMD8W"MMZ0X5_.sGH.e=ffLO_ZPE^6i9)9jMkVJNE;b^!,P)?$W_20nLh-d3?GNZQgSCsg1V6RIbP$&,sWXnF#?(D/=;SJ#%bR6R(rAf,kkXF^^KU5jP+$5KH!kdL".^#qgBYHM<SR?K$_"A]@'qQ(^T<Ef5keHIoAk2~>
+Gb!SolYkN;'*"Q<_UpMUJOZp$d\jrV;f$X3UeTVs(7RZV99d>"=`PfKO8J7(&h.n#O=#=0Hf._nYjBc]E;5n-B:*ndU\!TppS7P#I?Xl[Hks>8-_(@>7mHa?)E]O1^)Xh=F?]XeN96-b:q5K>M,eY+*)S$uKS*Z%G>PO,]8duYU,6mq`\DE6/ON"i#''['L1:e8FrYkV&Z>5Pd#"YO:HsN=PY,3s"$Pa8s%utWVshTGf*Ocn[bT++Gh%qfEaI)k2I$.^Cun)\BVS,%nYf!?32q(84i_(Y9HDGieAu]9`lnrA[FV@::2/:!d7M+A;*l_oI*JJPE.bHV4Q*Q/i2W9LJ(t=9(47JHTlMYogIlkJ?n1ujUHk?q??Q!E4Igi4358_T?!K#.Y8:ipMf7e<\"Z:V2raAtSn.SG%5,h)4FVUjq=5-q>4\Rc[%cjqk'KL4E4l at BAT_#3X9T)?]L"3q`FE21cTpg.ji+.hX051d,H9B,UKjoj'Kb9P7U:h:ITp&."Y2H2c$RFGap;!\*OQ45FLkHU.U/teSa76.7.nVSoTGdUo(*mRR8<X<i2>Y-"ni0V$g<@0V;QC;3^`"'Sc+8S7 at lHJD0=Ji.`%fCd+KN`Wpt/dFqr^2`7GFuctDkdNEV?LXJsfP18;h8ph<rqN-+0f"Q"a3G7E,Jg(4W!P[QEn"!<3=H_g8b]V/M['g.Yb"MR4&>k;FEj3RMUOrPC:d(HeLqg8aQ,[Ajclh(3r>ghXq[,oB*>&,pG&Ja*C#!/64^^,q6"2PX3PZ6[)+a"7^%RE@$NAeO-3XbCr;\9qW9teM?7rZ(V:t6>(ZdH:DKc\Hf32)<65tr,&gW3u2iXLSROo`q)7g4Eg.\^`[Dk,'nqn7+NbZC3R[`?d.ePZPdNLldS*2YQoNe1gQfsOrLd[N9F"`WY.f-$Q>m/Z%;<_?&Ifg1s1 at Ceqt46fSjX%6,9hWIMDg%PCH?;iq^d[MdQ_VBt]>56Q;Blka/VEPk$3esG`E&+ZgI/6.h-".dNTL(U/XPRdjYcV%)kTD4dgtIk[=,<$SQXg8:%0VR:8<.SWc)r6.OuM`iFS?]0[(+Lm3=;<jeEcg.fBi"k:a=c0IOc8J`s(1^?)r_?naOE#0%Lpm`6&e96G,uPn\[>+,$IdekiU/22s)kj5K)Tm@)%1Y3:oSY)jssjm]g[/;PaC`m!2?@E at CE-?6b2+2\,]RVEL5JOUf5gV9R+581>uD+E\#C7n.+9"JJJt at C-UWO!$2`%crW^lXiRf/mrc:WBa at 8JrE[i5aX9aB2OE2R1&&SoH*!*-fK`"TCUuphb^l4<7"mS.:u.lDb-,o=8dlCBt3:Y5oiqpBES]s>o6b<L!_ at .!`%PaEWV[bP'tKo[_H:a?'I2EkjW'4K`d&A=s-:4iM'0!HnZL4O0$t:8'^hBhNb[0G+)K:49YuALdEBCT7jE9IQ]pq]_G%DN,Nhm2HtmqQk>d</FQ6,KdZ%r_Pb>T02c:#f#"#<)/:=1]pJk"&G=d2i,r2;a!H&_J]fI3 at k2(HAnXL#ru9;l6P(ZKm\lA^bH(C#*uD\MQZ$6mUd_=^dROpoM:Ntt>a4QcTuFl\8F^GN)K!Y03m_"0<8(J<5T)X.4s at CfpiN:YdggOD31U4>!uKJ2g%IIRGdf2F!&[F4#\AZI&;B]To+2oa-krl2^>6L"qEYQp\ge;PFQ\r]s5TeKX.\NY9%o6G=e\sk(QR\!@$)Ce@(/oqT0X(D&p<+P4B[g0MAU80VLU7km[h3]*Nunh?gA]XN#D&/N6oH7+Cnf#lF>_kAk'gn]FHrnT6S"H46UmIbeic0Z'6)CWa,"9B%bDo(nHZ860\E\e*ZFMg?EK-heSe.:LfM;r\'ee:L[2kr]?[rN#:\uN6]<%+CI#Ya*AOc%^J.ZEKo!52XmPgT]A[nlq8)kZ9^oFZEk0X)sCnZI(LNes'BU.fSX'L)Srk/XmUi:OY7o+pUnHmF`Gk(9aEkP'a0j`@.s9ul_'g#fhTr</9LGM)A#F7J"d2/<4I#%JA\&gKH#(2We[kmq&?=+8#kFVd%SjcH$:.'V;7u84pfXcl8pf\9Z!X/1#mVpkaDEDet?$NGX/&m.oI#4L-T_-E)uD\%QE*:NpjQ>B/<nUpSl1(25=R1aKZjuK72Sn0m at V"l@>kQBo1<,Z_B\Y*,d[u&+son<+.eM'3ZYQ8Ju"ca$KeBE]KgRk"b#RHT</'Wr;S\5VHc7Cl6T&@,#:RUr7grNn[&MekMt>UW3e<qk7>V(FR`&\I*$%e^p/nC(4i-FCChNKGKr;<4MadLu1&ikci]Rn-O!Er\WD^#Xc at X#@JHR:1;MnLH5 at WBh/&$)61 at u&Ns`nk\rroA(rl^K;7kKW?7gkJF-9i[u)`2R+-N at d3&an8>L*H=FEcW$PP7PKZoS0E&L27hrn2$ZBfeN\fs#d22sFdmeY$Gb1(J`Ns5jR((ei#"g<Es#"RK'o7G?Bj'QINO4#:*/,[o=Q at F?tpV#[g#oD,Yr'UrY<.f.ahP:KC^+^9tq9$9T.`^Ym4=ls0N&p47!W94&--L"G`_46dWLmAQ.`XR)*h4BF)ZmP+,9Tdr:4S7Sg89QBd"@!h>1u`lBb]*g*"LK`]8<K"F4A;Fd?`.:UrpKEb>UI/mI,"ea_dfcY)]>9<]P9+RkGl07rN=r9$J#hb[W#io(gO at 0rc7p.sRO/a":Fq3fC+N[,eeU51_k=).I/9KL2*=@Pr%lLa/MNL'naC-r$_mgL*d-QBkPV_Z<0%;aSspILb&\)e_)Go- at 71,,Ok1fsk>'0c),fCFdoli01U3<"CgHOgWoJgc,JJ)e,5,'KUXeG]JXY*".ZLF[P5,qrn]TmZ(YMqaogSogTtJgaEn_nm3gA-\qF#;)P3A4.=/^Ym$=T!"<6AJ-&/7X#orUPVT?<A;'+1;lX@<>d<>e9JrZbc?3AtFm,_[(77l3SBcMVE5U%BkGk!?6f!-mlVc=ZYgA'TL5aKR30\NI/kdDOaf7JVdNmK(os5>mW:*T*6XPt5-l&@+=Red>WrKfs^E#Ucpj.V%rFB>!N^cKOL8/:k6ZD)p9mDotop9KB*?s<Y]]sJJO9:mS*[t/&M at h;@BY:pR6251cAni_4<T$Cfk\PbTQ2"(rJ%\HP[&p0L.b/>*$#BHb;s2fAmIfHWejN<['gZ=Uk6)5J)f!NZ^+_Ot%0Ff*jNnn1/<C0tW?27`G",_'DSuDq:BShj*Okhe0H$8^0^#_)enrd_IV3d(dO_#aP0qsU&o-nB4d!u!'IqXNZ[t`$8[8B.W_i6F?pk>HN/9ShVQgCGrFWf)ON1e3HL,E#PF2m%!6OFU!"&&f,-3LUEaD3l6<<ra4fE#k1E5IV7Na%LW1Wf']BGPVkPIWk'T,BU%#M(aAd0I/i5,sQ`kXFIaq"p`[-.9!%AJgT@";]b=!O<03=]S+IS\4=.*-31D_Hi:$nHnpW-cngXbmb3V]-KYi4Lp-%IbmsF`[D;;G%X:rfCq1im7QE/>4`F:a.mt"!NVZR/~>
 endstream
 endobj
 81 0 obj
@@ -569,10 +569,10 @@
 >>
 endobj
 82 0 obj
-<< /Length 3450 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3468 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=/=``=W&q9SY:o.o"cISBAWT.E%8s,YbVoHd0b"ShdqZ'KT`IoEL*g+.NIR+#\)AGHPqiF41)N at 5\f-crHX0\kWfoIq5oR,fsY2bP>mbEXg at Ir0KT5m'JT)Es.&JBCIaY[u7W$.Q3'5+>@N5C=G"XW'=TcDkC&#5T4A0,b00 at EDEE+*j[h^jpnIeZ;If/0N[>;2c[7iiuMRtYor1**LjUOSorI?gN>s$:*nP.Z$(qTijJ=mQhlQ6Q.`U_CkJH9Mq?'u:Dn4V;Qr/5EXE:[PS",I)+Ll$!T3riTIi(AN"/R:,3M=K:Hu\SdI4i-:::kLS?CR^@YMdm8UA6D[+t(7^(RkXu]hOqlO+5k5P+mYc*mC/&E.L>.k'g)(Joo=-#F\p6NOn>#hNkN*23LJ=B<`&W(^-&tXgI)tgYjVTO%8p[j-cKaEqQDil"lcDG at XHeFD&M#U<2X/^:^089d">4Xg"o^r<JMOHg8t2Ol95F9:aX2HafeFq"@S*(:oQ1m<Z2o?_8\nG,MBX3HnXIg&bZ<i._?_`@"W4Yk/"N_[G",g>ghL2al?jnV\^Z-,ae5[kF>f^':*#BWC%@dV*8coqos-pM'Ph!m-j9:+L<de)J.U=l&.k>a7 at BpP5]K at IN)BYR>"J1.7!Y)/cf0j7!PXe^1qc_<O(P84pGIiCs!ALE;&A#^.[AeILsP*.8rfP#4$).7<Q/Cucrm"OBPJq^1Nl7;!H*JK3cbbqU7'o9EN68EfPs4sgemo.#Q2'U3aS!J+\bd6Y4=';\X&[fK0BZO)#Q at e%K:l3D[OkQ/gj0Zp^R0i:@TQO/l at I#%as*qMUh-O?XeD].07YQ4.Lk].7<Wi5WH&d?nb!&RP`&LW[.$<G(j`E"]6e>FT0UQ+.",H+)m`dPP1)Qa^^KF1l9%STa--IZ"0REBJp"qU!mgXS:lIQl29]:n]sG<%j at u>Pc6.M8";fWf;/ucbV;?EKGKRV+K&!W/N]^P")`8Xn9'cH#\d$UN6];"UT*/$G^K)R5`WFO at G[H"D*qspZuG=G"=]T\fiNN.ghOTY?;%^\/K?XQ4i_-!"!Bh!]>*RH!`is"B]=ShR\gZ>,Xtq1h[H?\/>GDJQkdf<g&,\V-Ih at 8Vnj\>AV)4=-(TkgH!b'/"jgAf!hp=KiA>As1%U-]bR&-Z9]=Th?l<+c0eS=u\^*j+2cYl&P"h[QLb-JQkiROVn-7nM/a!2h($,6[Y&t5VSDYo^UO>iE;#>RS!-M"eUr$s[FaGd at iT`JiZ0O5gjhBdgMQgG&:m;f. at s+7Rq4\WoN>Vq]\`SWt3OsG?/]>9Ahs^O[O3<;/NOO5.4^+qi`/=GI]B.T)Q\VVc+GPEA'KDB31me^GYB<!)Mlkjb'oc#ZQHCGoM at q.!*Tre.#cInA8IXWRM<#l?M7KEKW,V0I6?OE6JlTQOKGDQ#mG4+oGl2 at IYl&q>5Q_i;?>Y74jpGtHa+H+YWOaAI,G=S0R*m:%$YO#_P7q,Q9]2rR<I)d6(cDE,LmI.R/dOA;XEe^#kU]&.3/P4q+="7L`FZ?QStAqm5JCa70)?IIi5)Vmfcp,U3A*l:-T^R*L#i2Q2>D\0RUnA7MSbD<,;,O">dc"bdBNCf:/bt#fO:6S=tNZW5&mIm&iJQ#kkJ&IJjGB at 2#rX4iqR;<2T$'*CC#BJSI'_+gX%>moXqUZ*\Jd"Tah!K;<DM%74n+<P2R_/pits83 at PpB6>t:(>]GD]^hOXSUQlGF^_M[]K598`%_c%NhgirA';ZGf>u`&%28uXk%7+t2P%tLp>7`j91^UX;*WJ1'4--WVV.N/9Qngu9S,th_iKWpi!Yqr?_k_1a*(2BVBKh)J7V;g%<tqW:Q)73C=&\[;;j[.".aMgkU]I)j'S\b?B*1:>MHhIY,7%HTi#A9_l+]<c.:CI#Xr3LU,%407H_c\"_;+FdVPbLd$;XJIDMVPmiLU^uD at LarFe9qEO?Fp?_5?5K+`>OJb0J"-dYtBb-(9[I1`3EO*uAoYVZLL8=6`+1R*7tq8((@O2,jF3 at PCkjiuRua5Vm4t!h*5Mnhbn4?p!qFCF#/^dBjQS;,_5YPSpRHru%A]qZEqb7in1idJ3gL3_R^ScL9*X<j#m"kCaW=+,$A#YJGNX_1%G;MYHHAQ15>#Mls;P#65-O=9RQ8(*N[8diVo5+E/c0s8.7)O:/&!mlG,Y*Jj.AT*KOWL$qYZ8dp!g%PHp(\lRWeN;kUrF]juO^1\\Hm`@P2`)<'nd`qgTF4>*$C4b1.j-ZR;l)5;U2YE&:.@%Pd>`eW&cqL0]l<CAQ at V2Z`I1)DthrfZ)kD(J94+GH!jSb1pT_H(coa>=njSZ1BFdYH-`BKuMb``Kbj<NmSXMiG1;SHJ.['tGQeF+1A:_l"MPYW]&;S#C80T7C;n\V]HCP+!Zdp5In7/<uD,FF9X,t&\s1h[#LBL at t^S>u*I8Te76juDllNmr]4R)rBQ4t`R7UI`os(C;btTAcmH8Fl5<UJc\E0U.b.%a<:N"sE&bg-EmNCinWeKlPoK>9d$"mmJ'P.HFMTcdLq at M/M7)L110^HL\sur!MVAbX<r6r0`X`-dV<.lUa#sRZ"-,jTqu#AOUKPKPC)?JLg:(P\d_;.o?@b\$'6`n=K8!'gT3G<Da7l92`/e10<79bJncfQWh0kOpg1Xet5k+H$@'Wh3R=?lYjbdZ&'h7fC!YuL8KP$OL$b)#fs-n&YADgbaV%<UHAB?)(2d^'Ad!L`uXZ(()_sHG\43BMt!7`,/hCT5;dkE`edHoF4IuX/P:"go'(%e/<7V5_$SAbJ>X8M7Ea![$cjq;Ql:3uH`]3]2_[939MD4a at lYgf^J_@,l0;hKmWYi<FiSSq1j+F0$,sOX`rkd"R"a[)nguLtU!W at XTf9"J\lc6WT/rBk8H5P[%BR)VJ-m,3s,mK0qrng737.[f-W66!p%f;__&Z?u7'Gl^_i'+A/=P;#ml1`_3!X>J.B.8DJf2O"b#LurW4^I^':GV5h?$EJan>VfrpK(RUu'Y]!F5+RNd<>G"enBaI!9uHl3an`DC$Us=1=NHEX-s,23qQ"6"sM<>TJQXNkhEODg*W?rEq#Zq,[7LOBXlY4Lo%d0kfG>kO=.qdFlG\:Y[KY=/eW.<Q+*OQXk`,:F*^$nO]k!Qb)0Opi56j=gQ='">Ms,&-QNm;o?H_#f95U<9S'9=<8dh>X'^.MsLA65X6!rWJP`qc<o/Q<DR3q0o.SG$]"jIkrW_,c%o33Cp8/Q#*'-Z)_qeOc:,4,itT0XWfepTI\5gNlGic?g);^SY^ubVFc at Uf!i\8RhE,A&!9Kogp\p9VEDt8ha`DRSMq`4=f"EslV;sC_"#qPBLilg"8^rO!;=P(.%@J*H5jng6Pqb('(\%"]i-KElc64'ONqtu1FNULmLGu,$%7O4LZS0Y#,ld@:]aJB`$uQ]fSl<CDj+Mdb0,0rU#KLQ1D#~>
+Gb!;gD/\/g')nJ0 at J#HsTMbA7$\uVDmC'O58[hFBmK/aP^t<a&<#/b2:=fGoq'KljP9SI.mVjQcpuV$baEcp9+iV1,3I,[eo)0i(gn2g=+Ij-C(hAi;N7i"GWkjn\alejh&=9WIZT&JY9L'fq_q^'lMO^+)Jr7AZEcgq5M)Zh8o,s2Sdic&%PcuPfH4LG"Zmbi'ntC,YMpj4*%SaZbRnZL5lHN5bR5O]!@?ZBCXpCdFc'$2nHLgOf*MIV&3]W,)OPuh,DKt!>nF-Y6Mr>+NJhJ0aZNEF,:u.j+2uCe_j?JitpE0fZ<AU9CnV-2iQd132\mjISX9Y%Wm#"fZi[Kl+Bopu"-#pq<okQSJN65cA&ZVZQ2<bCHDTEaFD0Iu85.GJH<-0G5(g]@sRM5-TB[F2_=*RL+)UC]S8INFnmOGko@>VRa')7be%_SRSlUO at Y(jEK:`4-jPB^hEnOTXe"ZD>2*8$8Sh_s:#%*55uJ%I[Ia]1!+_?f36a<q$pPa_<lN4Ul5?FGE<t;Z]\;lmJ,8j^D\/e$-0lZ"*M$[^(.tER7`#7#0?>MDMG&%u(IZ#Jp!WLjQ7;kdFf&d5t65:pbJ$.TFa!NTX&$'[6g,LZQmHhcGnD5K%UG"`9)e7 at B6C@C2\M[5/b)U8l0cbV[TX2J_D[];"$e=FUH&ZQ\pY_n-Y#D,`Ze%fDL,fn1R1!BP"@KKq#]QF'[EG(rRc?Jmp_&#1E$bJJR>cUiFr/d\91.#AZ*ZSH)W at 4UC9[MF%Co,rM!I-]8Kq\D*n\$,:a2Y[W'c0c4P*H!F`pM`Tb?NGT-&)I&k?/P1g:$#KfC8MGSgF\.(c0(kl]-?o2U^eY#['Wm*hc!c*Ua\%XEf_lEAf14R&(&=uZI6MF[[K!f-L9`OB?YAP$;p7Vr-_#r/$,0$<J'_3`9^u5%Q+M=r#3JX8CM`I5c-G?9Ygj_%oZK0!8^Td__T;ZGE,?\Op]_!3SLnaJdCXnXI\MKh-h/DKr(Y*a.`pUeli6s#kN/DOpOK?*B"]FWF&/R2>_/d/WU$uZDE'm\cq[U$dGEr>B?e[1mS9<?+#PdR2R[.''@cHCmGNirA!'H29Y6AOt;1U:b9kZJ'l:[XqcY)I98$X;Crf]jY:[/5mln'@8`F.ORg!:WViu^[`B<5p5T$aNJPk[;`kaR2\!/`95HGL``t(ED2kf^>EsZ6I-rn9jjB`a%;tF=RH&K!)kWWieNuX(k<j at Z.sf7Y&a70A2>I`_=,jSha9lfjaTC'7=mC\P'!(caAtWAE3j:jiZLB,t#-C'G8bEG/Kp9Bu%1])GL]$Nf?n[^hfWRWjJ"0kK2HO$"%FQc?$V5Wh(@LJYpA+*T/X&YFE/s34=K)I9U9Z+'[Op><Wj^RH*ogVT]>rW)/U;n/]]Sg!\$kB at 2W)8S?.^paa\S0jOImE6U42.qnnlbrZ0]q-LijOD%F6o'D]@-lNXD2)VDBA$mPD!oa]U)$#hrmW$_BK?nj//<(!u,s/'Z%9RBoR9S at _O9XXj8pqUEk$DE<mC+%9WX<Q2ij=f)n[UJ>OKY_GOcH0cKh6eZcE+;J5ah3*7I5&Z5eZD at f3e`hWl5uJ_fLONc8IF&24-8ec*YIHSlPl=1gM:Sn+4_N3)\a62PJ/K4,N\GVHA/S$FjT2Cno^S)_$pOF_*]%`)mO6cpS.BD.(['Q]MZRV[3S^Q-s"T4*%gB?bCNu-l2EmT;fK%[H/XX\rVM[]#DEH?N$:N$K>5$EIZ^M2E at pQF[0?#"3p=0+q:&JC69-7dd<;$Tf8+OGLN#Xc)Hd7k+k4iqKm?B2^`,sf4Q8fCHA9MU6?89`1?QU#cT$$Jp'pg5nLWj`,09EET%<IRaVs?3Ual]PNR.j._PpI/Kg`5\TXUMP?23%m[q?[eG=&?.u!^13&(N>=fb+OrRd,%mjSq_6H\fX+-Qj4#:JoPAZid]jKArMSl_%dOccA.0opa\A;Ko<roHE#"=_uGo6lkd`&0Cc>4,&g=\0h:.aL]_9P7N?-gaW"oRldsMo;Xl(_Aj$kuoh49!bXorffP-/AbT at F%coJ,_eOrfn)EJC!,`*5^K124Zf2"D45^FH[QgTdhc at 6"q=Q_VNj*L7MlZ5>@G9>_V]6Ftl_:0lI$1%'S>9G:V3Lr[ml*6V=SdO]DVm_n=F>H(,HS0b^k.%Q?2L]8nIlBLob_)I33Lo5f.">qJ00SO`oL#l^i-`TP4o#t9ZCNRT$/>MFSXgO2ArHcfdmW%[ncmM<,g!`!ML,Ef5s;_U,LWOW3N38.EG8*`e75UiPdtS0_Kh6(Z!r2N1p70h`7JhC9:!57_B8:8\t]`6K\H-^2R_X&&$HZ\-.%F)d'$l&it7Kp9K<@II[CS;A3Vdf?/-)kI$$[TCEPdg8Hh*DCns\C/:OSqRA,@[&4'E@&`I?2Z)Nl?+3=P?o:Z0VBF*6SlG=8\+oF<=qu#K@$QqCT!^D#:LH]L4lLpnERRAS/GF)::"?Qi8;tU2X)LoZ:p39VGUisB'cpg*A^"Z\Ws8M=1&7fJHg%u<3ccdG#&Z at k^d_W7c+'r;EnMB,'Nc*egeISIL[D";$,N.Ab4hsR,qX][O6n3ZO_7d*G_J#&-%B^KPLC%o1#Z&QHFhB_^Z>&N34,49([S3K/&ZW491tn*0I-(m"_<&1cL0"%&8 at A`ESNp(o,gJbt'[Wg=0[E3'-]bt;EeSRM:<_n]/3^-SZ:U)2+f.13kTN!U9B(KYeEcs3*)jZkj9KQ/CC6KA_/oU'BV at Y"EMlF!(IQ(mRs"]M`Cr5bi[MT5TF]uqd`"1_O[g;0#*%HpNeD]I/P?;U at iL0r#C7E"98[`kkJK:Ce5JCK0``\'%Ac;Dppat73$E6)P`S-X,N'c)0d8*rrXmRlgS8^/S4LHg)B1%ng)^c[,U!lKLCqn0-T+(P7#@i95pk]M_6op<Xnn[a5ph(+E:6<i8&h;'hs5ng?A at R7k6q4fdJQCD3LgWW2Er6s,aCk#4f"uG3jf3igSjf[Tk_qDgE-`C.o/"FL;)694SIpXm>]O7Jb+sDboa^Oa'WcamS:jA]-82_mf'<9gA\UK:[q0;I-;o.0",Jp4Nb)Xn&Jtf/$(;E_(D)bC/@W]X`ncS<%A8lhOnd`]eF31NU7)AVSNe3%cT]3PiJGkeLg%,K-ia_Loo(@o+LO22[sWJ1!dW]SMb:hF:WfK'Pq)a"E^,?Rt<Ms at H+63Cdfj[D at O>G<id*sUD,gH at nY5_VPHHIm)R+XB4U,7V&!3VDL%OY`@T+%]U4LbDq?#(-j#`q52es6:V3:ik0K'k'WkU2eKS$:6D'`972i*69#c?g]iX:7aoA`g at 6Smm(pNM^k=V+Gkf[qTQ1C^Y7;b>c`(QVU`'[=j[u&n898/TipR1ini7=qs1IaZkRm_!nQeoSVII at b6%ntT2<:hCCSICj;id;Le"^fa3E2DZ:GPrCYg"5eo0l:722\)4u7g[5ron+Mu_O#pmV4rEpEj50PZ,fQq~>
 endstream
 endobj
 83 0 obj
@@ -584,10 +584,10 @@
 >>
 endobj
 84 0 obj
-<< /Length 3370 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3985 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=/gN)>a&UjCTW;L+#eX at 7l\i_5O=n_2qC44d#>cC\mRXuj, at SDnf'"$\HI;gFaTE#&=bEOP8)i'DoRb\Fue`u4-Fn4_;E:@o-#=EDh(_<NN4G`dq+oPBu_=lsaP:L>11QtCI0acS]NEmO/+7+/[@;a]/D at jeDR/35(dO#eK!-0a0c4B\=#+&3RkV at r`NQZj,kfDlI[s6i;AQN)e at 6bhCr':lgfS.eR2]<3-qnYMCfD0lfOJh]J0>DlP>Qcdnke,!coGOc<g"5W#.,2+YPaKZEP0]W@^C:*!=T.Hd1mp0N4e7e2_O"JkQ5H^2_-9m8UhmdhH2IDrS"Dilq'u?P_4$D?Jec7AYTgh8r6n5Q(ta$rCA'D&pMRYueF%`*/jE_ONlIb/"X71T-7I!%";/;e)4>=J0)iX4%FmlpeFM<jcs43$A=hp05)NM0MHho1(htMNfk>n`\]_#f:ke6if^9r/_Zc\O^J0?#F8+%1M[Q&WJJGe%a#stFe67bIdKfk_Ze0"D]JU(k.Y8ZB9RXnt5[U9C`>_S`-MAB4X'GdS^s?-n;#Ro<RU8m:*mt1&B!g>EnqN`-+SO'G$5&"RM:D84`SJ5iniU6(!rO&hWHK'gLE1T4kBjr#oiDN]B6o>4bO&Qq(61LaAu@]Oh.I7D::IK05?8n)52=FP4TNR$e<?,."b&qL'`Pha-EX-?9=!u_(($%VZA+-45kmIbn<tVJVM\4Qk<O["k<.AK`gGVbTpFd$Fq3:2O_0Y!"*K;V.s$7tqU"a0fYH9e)L?rBFetc>G:BSioqS8(Z6XdbYEjWNTWA4s_-(L96<\ot-:?*.#.s`'(U'dYX)%mPgua-ucdb[fVp2Nic:Tt1Bk\I6]rD+_GnN:W#VJ\O,m8Ta`D\7.otBCA$0F699MeM'3cGT4X=%r;riGGgmTA<cS&iWIg<11=k<Q_J<_. at 1=5nhOFHu4_J+LmK0C9#jpUHarocSj5\pfcX@$8GrA>RI43ZP(Pg7RpfTSE[;[9l)F9VeT?3+s)ojO!&?)jV<9*nJGeP-Hr+0!dA'32[VdV=+reL at ekCIKP=l+tlN(NP+=+s4<r*QA.;8_ at 9tB0edU22J$o#5OKhq0Q^_NV,CSoPb^k$jBJ&t6>>qN-`i?V;q):FE`<meO;%[,`+iDAV'#Q`Q_tcn(7D<t4b6<fcbp"a/P7c(6[M.PGM&8/Ud$ADJ3-5hTntVDcu\H`KUiK^6GIf+$d(RQ2RN'(9k:L1OBgaf/lViO5#qW"5e[fN!BkUNrMjDC-X+37.dTtWHW5CVEt"%9NT_+,PVAf-1qV`V/1Vko.i(0H%D\Qp2,q`bTS07G+U07pon5R8MoN&WAJlIAV`f)'q]8?aE'":VJ1WSoQI;7&$, at EW6=`nZ1`Fn%9"u(9Tn=+)>(+n#"\kF-Mi at F&H'Oa0VZR&F#&3:o"pj0dC*3[L`+2M48NmXBS(6Nd+,db]e%'B6?:C'N_#_/s`UD>d"b';aGWL>oCK-SOE+ud<.$G?X"?m`;K;J%XU9WT at UHY=V'L3Ia2;PIM,@%P\.QN,S\MWWn at K2%CUnM'c$3Q8[Wnl@("MO=HD.M(X5EW*^K_qR`m9GlDDn at II$C$g_rRp3?Q-uD>@P\4QR5AK/+sk4q>SI at 7/@LRRU?(W:9@"`JF[='_8[a"E8+Zq]6#V?B<<L[!fo^X`,cumelMgdlS(Sg`Ue64N/@;hi?1:Tg#:k`O=PJ^4"buR"+/h/jP;FFJU[JNB[=@"]/:[]e,5-cLL1'E\^['J]g6t$6`K,3'L^\LGJnWZ4Ptt;Pgs/3ZJ5/7MT6+^G..G7QfVq00iMb^\^I"cjMaW>aVW+DJ7"MN2gNU#R2<`HhJIn\8FAXAM0PAKD&FQ7-#?9VU1Bf^s;@^Q_:9-J4\51#MO:n<OB_\o+kV@[n8'e;-)'&S\n%$MHiDW-).,lIDQ,#FGPIC0DhMuX*5O$KY at J*h0l"dc8e/UL5'c;s at d*FZk8f at 9`^_7kNC$;3L5a*8.ec)Y5"7E,TnI2Lb>i9s(MW8jiJdY(c><rShl^llYBpt:Do&?,++.Hl0RP$Z;WBG.^+?bNl/tR2^!T[NHgaR6B:(E<i576e-]j3pB#/WfG4%%+!+#ZWCfX6k]PdT0=NUhQ0#"e/Ya<h!]TCmreVOZoO#q$/nps;.bA38>b5/f0gr;lA-LYe3,!(o=aqE'CtTT)6:H[buUCr*WtAi^U+QSok9fuYHBamJG at K\Yh at UMsk0,P8uO^eEdo?kU$CHd^80Oac%V:doiZ92[CJ#?hM`DWJd87L(2*Nmc9CNr[:<"6b1,[iIo&TfkTg;hs?b$tfmuC_hRoPQ#7jlQpI(6-S&0_.9.8eNUTMZ/2AfL%-aM at +%g0i?Lj<Vo!"M at A9qmZitrUT`]j3Yg#mCmES=3i2pqQ)an2^jX<IJ3NtFZ\+Mdn_'3AP(1OWMja)^VaM_C8%S9uEkJ1AZj6p_Q/^?oGp_W at Fdk@rcS#1$'APS!;EM;WWF>OuF%7G,IB-'Vqm_7NWEB at AVmdGTWkML2qB.@`RWc<ABZGC?'W5N:4Pgr&RSj/7S>"d8mce,^&We&pX`;k+c-_!bQ2DII8ER,KV+-1/EA(/&,>Rf-.71Na1f$c=ZVrbR?=R+c^$A,_SY#"SR]4>[qpGma>dZK8:g&[j22==CVCoRo$8j/M!F[RnpEq5og?`V05bpE7"\XM><>[G`W2sda!J/25_LJ6X8>m9VKi%s_uK[^>uBm[oRn(ot$$B(7n!+Hd&+?HXcq^u$(#mN]!qDV%GNS]auOX0U5B83`%.b'0?gTT>MrFu.`cdV\q:(l[0C8VPY@>UBo3cC at 8hsc-HBiQYY-9'2X:+K6Li(nqL!W8cm[8K2)PF&<5m#(l,);@C]Xb](c%36&rO at i<CWr6'V'l_coWJ2YD.8b(HLHO[Z`ioob:H('ZgQLZH2Q+=Le\"(Sq/Q;W9f'^Lb#29U1.cIILE$Hus8P)OIG`2o4W/?k3a83@#n-m@:>+8(l:7gA=A]GR0sHEIF8fMT^T&[4"X[4Mk<8(WAk?eNG]BfaS'baSfD72HH()2s%82 at OZc$hiIn,t4Fb]N+f0Q?Xc.dh[cSRfOPgcQO1;.Ii(Wf=5Yug13bPFN:TqW.`S>WYnaAa,3W at r<&\$MtPaF&'(+MHG@%WiK\S81lSDQh4i4,8JF*Pj(hk*sEA\&hXF3,K;5K+LUBfJt2h!92Cn(+?DF8RGN^Iifd6U?rrfja0/8-ZK6(?Y.?j1NuaC2^3K#Tr&+geJ3)6Gs"8%/@EN!YZ09.=UILMo3 at O:Cb,b8fJMaS^ek$^PXs#AL.6*tLo.$u'k5Z&f6G'^7VOKd_n<MWTjNMb#^[?oLVCf8)"8`[!dk=IIgi<Sn,~>
+Gau`W968k/&\dR4W:hFI at _m;Pi#tZp[("Um3EhnEp(6BeP!]gs&>8P)=8DOR_$kj3E!A, at 3gJFi>+h=DSt<.#Ia%TLro2c4`O?6Lo&1c&*VXUB\P at 5a/q!G^s&?;Ih8Q:M)_;f at kOXDf[0!\MEu$71BKkJQN,mpc>\ru55(#Qp2=\o\>8]YI!j+X0k(RmU*NJZ&O4!%j7.,9$3*6FcH'`MdXm7uU'O9K:0)t`VP at Rh%cIf-\eB;C/B<1[^m,d?D0(2"5D[Y`L40uHq1F_LBOY\*6eJ[?O!=0<2hX=cW?Mfim(;@<4E)W(D[d%W&D- at B5Z^a[)?O`XoMpq078Fi\;WE\`Y8Q@,q40ZB#jI3'!Z\@R1SUo>*BX7$ph)3*=_;=Qda,Z)U)1:>YESSI&j20GOkkl1tL.6X`>Z^UP\C7mJY<H#'q([[;AV&l%r`EH]b*h^COTd:iS._.kLbi.TkfPh+=Ij8l3\c65CkY9N&@?s2Z/^:o[P_t5CK:qk"iBK4L6X2T:ooQe*eq_]XD5%e`TiM)Q,BeK"k'%2e1dE;S]L1C(/ts$Di$e#*>PX*p0H3[_b`mPH*Lqm"B5jQ7IHM/3]ro+5QY*pRLr9&+OU9oC,7$%.-$-WFGLE98_hdFDT+0r*!IH.hg6p-d>Cn_2Fog:E].UN<a\he(*t9].bKd`2pEGY"5+0f;^"11#1C=5gme#pPHE6+&t?"n"Z3JKrC@#bU!/<dX=J6pa#ep"#>G_jB1'*oc2sO8_?jHn359%6kom0O!&[t)&Lmp#+Gt%3?UOIm:[0H(e!QN/UjQPb;SN!LWs5Z7(c[5JfTtbHc4Qs2bI!+VHkVfLObH%Qd%]7,$PM#WciVgD'DuO@!Obs?6oNUSM<lYOHMF+X!+44V>DQG?g5R)e$`PC5,u<'Kj]b98n#IX*lEnVi^c!,UE%U-e@#[l`N<j>51W"$_Rc8:"noOuda2"_L3djg*XHNu!mocdrEjUW,$<e3jc8OHJ%[7=pAWg+dqq1f6b6qV[W$73;dk(rO+5fE8(*:(5X4XK#1/ql*d?#KJ6`eA8Vsj(8V/XPLlDq)<(=/bL=/[j/eUq56.slB<]Xpm^4'!!NX7qIrY1#Q<RNXl3b&VZ7jX[ahZcJP'.UL:2b_qNRedrlqcnA1OY3(+*;.V6(VX\!2_lL,]!(_bb>Wiblm0t[NJi7CG('b4l!ZT(+rPD/6:Ha4dIDBQUJReli/?PS\EAXZ1bZc8)#W[f2J8'r9"mSc(eJT#$f#EX:\L%i?5i7`$,>S$F6m1U8R32REo=gbhd_(<!pZ[72TSBC%8\i1MOThPR72d:l4=^k_7Um.!C!e&NJ,h+k"OUQc.kEPpW2PHt\$qb0cbAS\I<bo5$.!N.&UW^n(pbTEknsH_:m<pb=h-.=e$?9tnU"YDPs!uj=qO=Uj#t^ko^/n-_QZ\S#)jdNQ_[c<;ArLH26"sf4m(:)=WCRI5H3PiVE.C,)Wl*ha6d;>4Q?$DcH6tIjSD9a%9]QZ_!7nN!nqp-os-#l6K&OH&JAb\dG033N_nBK#F8\/+!5ekl%mG*Q39!85r1B\:sr9 at VpE:U6lkHfKoVWH+2k^i+9\6=C21`3bf!SrkW#iH)!jXjPWuI"Ze*Tfd/_9\XdfPD&JK,[-KBg0.gS<\TA#=(Hi+8?%mc?HUJ.3=\8>_u]0&:PTJ/_O>[5/i7AK9H*B)k-L;p#2*p,=8ANW)Ja^<Fn2E4MlmI"6NU(Go[Ho[E!$67iDR6gqO2Dp at 4Y;<R;:6i!(1[ho6T2s5W5%N+g*@^#=kVIo';9-.QfA_K,5.;A883)S.$H%E>f3,B@/`(O*jBj8]CDKsl+;08a;2"=Mhlhc%U[KQ[YaiMmk4\6*ob.7Z#b7.(X#<QJ,"ElHBE5j?,eY*<C7O8,lkp*d+_ZE`lkn%Xj(PS5.!-qu@)Em1\_7G-!&6$D69Wn'0ajq*MZMo%a&"gu)3'_SCZTAoL<XsD&c&Mb'6)n4qE7%"(m]#*PB#t&9WafgQLN>#=A.j6!XYos3o:9R(Kt6gesCCm:kD^r-HA$ZaLS>]7M[<V*(`F8+CQMo00nfiCS_m>([;++?kK)i5"jj-f2]E$#3[GReTeaV4o><GfmN`!j`K:+<)!_%GFGA<ShJ/YX*dmQDnrio1RF at r;b*YsVtXgTO0%bGYmae?>t&0&-kVtIcltM*SgL2<;eQS8XD`,u*(2U)1Qt7]&2Qgh0_^;6cM=0632KVFWR27pUog.UN"T(g8;p:`$PTVB6Ep]j$J[FUi6V!s7c!VOGlK^8GlI4+kA/BMs+KDEHFVSe)=Z>,o2R;\V*0;dDh%t5.qcjYnc'<#Kg$0GM7T\Xk909iEm0U\EYb#H$(>j%n[L.@?2Dp4;o^$s`^j2>5oX'=F1tlH.9hm8C6H;7rH.f"nG&&'TARDo\UCruZ2f2)+-Oo?cgpMtp<ER5]>#LPR\'0XAH)>LW+UiS1=[&Fg$99o6I5eXf]c=BQ3rUb`qhZ`c]5e&1qU#&"6NY.!m?$HS-VZCk[t5`$l_p&&8pTG&<)UYJ'P;PI\P;Q*03J,-W>9&3'+o!Gf/ffG06C5Q&-<M,Zh:)%!ml/^"K"jS?8:b2R,>$[sXu(,:p+"WW(8"7P^"bour9IR8V<=9Rj;aVMNtNkJpPkYBNL&6i&C'El3"\]nIm:T2O#;TYqZKcjt&5Q3`(4[Rk]@=HlM!q13-]1>VVZ1!&mD,AHH&!9uPnnH>@D6]"5r?494g9&NEEcq1ci,J at bI3o<?$rrh?&Yi at H">NiM'<IRs.&!ZGZ:_`3]hueN,m-n5a<i7&54+DHXB;8\/UfV;t9;eN:<)ocUM`6du#eSHL-WOMcp#kbiV/5X(ng5L^WrkEt-!ERRP-<6gTuud6SVr:arihhBq424NT"2a<MnWY!j:sp3-$*E#._J)qI>f4>N>Kc]&1&cn%b!>@^kZD$CMN;!,(>UIpA at U.Eah<CJnV!RR:us\9N2%`65//fI8e1t^@02^p1+m\`%8tfQjmR)m8KSrd`jPh=M at L%/^52.O=]//foRaCi`UEjE%[e!+*Tmf\MW.7=Ru-.F9=j`51j2&['tiQd2S.Bm-YB[?WX>/i,;oWCA+n8Cb'.L=7"oq]F''V\70SZ7B6YAM_0[4;U,?J*]hSX=h9;&XF:8^E`j at lWHRiF@>=ib'XU.X[KI]J20<rl*q<Nk]Lu8$S*oiD:Opd-5J<PO<Y_M2X7iQT$G.oOEoqMaVY/c;F4>W/?,*#AFX'@lp_%80 at je8Y-u:g%)e04B]nJ[I]XSq^T+10a(.,a.=76?DI!MtL$HTa(-0Sga!\5JiUnC0K at Aljf?`26fITR[&^H`TaDW$2MWYim(IIspVk2>jnkYPAdIegp#/FY_37ACAWUD5QX=uhMf,BiuWJ9cT0\\1pmYh%Qd$9ZfPP+t>Aq7&@NLX*Q$j=nKsTEQ_m!:^BnSrTLo9-+bEWRK)MC2c<GoiRAqOHuFa1O2h[g^pO]<W7a)a/HXupP:te.t&&]dSgff%-;_kMnM"&OlDG#)sr9TMY80]cp.(LTM;E,G(NKe$9Au*:K3np\Sm#/Jsb6frB at 3pN]_1>WZW@#e39\H`FkcY09i3OK",qtlM"#=^t4_59b-rcfJD!"Wf.0dkqCY[BEHm8\@rS[^/`&U(I%_,4'E=C/J2BY?Z<I+&Wu\sIXas3p6qrPio.Ic<oAn6/:U@:+s/[k#>diAY3)12e\!&W at KQX>Ro]G:WP2*]20k[A1o at kS0ORKIZ'_\@1"3$o8$/:A)\MenhtiN$9Y#smj0r(7/geYE"RcgOk*JEYS;S/V1[@rs^<!bBCo635U-!lu5C6UliJ00CR8"<N8]Y$[->_3'#4cCOR^3lbHCE6bdnu&ekV[2(g*jcCT's9eD&c;E]QC!7ftda$6I$!Fm,*X\8ua[5(MctCg]ES2kG\D<Hga>#"Tgs/]Np%e34dcjld;GSrB^7F)>DJ,9*1HL&HtPPJPU=d"#KYL<W~>
 endstream
 endobj
 85 0 obj
@@ -599,10 +599,10 @@
 >>
 endobj
 86 0 obj
-<< /Length 3138 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 4063 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=.?#SK-&q/*00G[d#!G^<aZKqs(/=Ip!9sJQA[<jjOomNKE3nCGbWhL32pXekqqAMu#V6gbejU?p"__Yq[P],,SGO)<u=0p<'Q"eL%fuTX7%<pmScSP?\d*_jX\#UHaBM&rY0e?D[=EE*`3_`mMp_dV2:LBJqSDS at _O86XFCok/OW:Z8-R?ehg\t$KU8#ELnn+-Q)E&]VhfFe:Q94RCLf#Q2<?=fXUNl(CF-I4NZ9+mP<Un!Vrb-+>8+0 at cWP</+sFI"_dcrgqQ(:k-<jX_<`DHSekY=B8L(#WH<$&rZ+X1oMCPXITrm)J^BX5AA3V'Mo12cM?'IQXV<C<XCo<3rK=J<08jKVhlb;$:c`Y!8fhKSRb/)QeNB]jUqagcD*gWK3t;2WL2P)/W0]W4c=c2O;YDfrX-@"rO5lFB=V at 0RE-uf&h^XZu+1>ZOOsTBUVll!@Y3FO/\G:rAY.5k(Z5/MK!t)XaQc0?q45BOp(B.$[*S*PTof(qhn_DVWus:E:>E==b8egQfBalj6"mPmR\0D-A^Ph6&kEs>0G(f3#8mF%0uA8L%,3t$[9;&7,Dat1hJdT8kg+nD!rQ"V;2\`3hBd2ijnn,9(dJQG'_9s`DBB/g'qI9_OrRqZk,A7>1m:mrTNF3.o&PrV$*2B7+-'GT87t<G;I^>=*PdaT0]f]$BiU(DBp7"HP1A1\UIiaTjTQ at W6oS:[;u,6f/ch(DFD>*]FjA*1tm]mPg6u81)`.dN4(U&Fr+34FBk\b at 5rqh5e/1=g,,Mapa_>qdK at 7FEljk/6D'<\@>3f6]dODAS7b)q3$\Y=jS(4B[@0b`olDMtJeXY!j at E4<@-Z at 9*sr%8C#*$:5.Rt6&GG?)b(6KARMq]nO,o?I"?3MBf=6N*%h*$9:Vs?*cgJZNguII/Vac`Ws.[Vf!W/!,6=Dhr'@:pd1SJpBRgR6+kW18#.S,["4\'BK+;%l)_O^3X37>QEe6\+rE;G1:dV]&anapa&"H<Bo^D+IWGD_KZk84TkQDQ$%&XE;K!-Zr>"S^=\3Xp:Neh(DE"-$\!kG0rL7*=A"(7^WhpJV\0;pMN(AF?U]P1\MX8m9DHI;iUSKTn_Ge-CJ_[b6+*6)!_A3(nDLm)Mtb1H-;j:5#B^:s3ekEeE>Q)IgA"8e$:("jn!X&4oGP,AjoYH_3"Q:u=FF#hC)\Jml4K!/dJ+>Yr,N>qa0Y2gNt0FW?n(`ZU.1]-.Rbo<S$K;j,r->L8cqW/H[A]a+sc8\6/$63pTsQA%EF%Y)"%H&MH\"OFT<$aQUtOWVnA:R]k8CHqp--'Y%9h7`XT',"+eDP[:,#C.X#ruN(&Ce,-mf/5osmlu;)CF-5EbQVFOh#P0`-stnJm]DhYaI26nQQ!;iS$)m[]7b/b&ir[t.f+YDKLlM;n[Y=unf4lW<kAIikM(mMs*ha7i=3LA80$Y>H90t3Ki:_FdE#s'"Kc6ZA*6,W;e!7bZbBZ$J%Vfe5Eddrb)%17KU*=ETT1beplY[CI#u\d>sX=r>8ILi<5P^Pp5J<T[$p638inlR$0=dW&O`jl at +UBG5-#6NIYQ%;TH*dbI%2f;8f5S?0;$6u!6#4Th)KsK^c-qPmHF]3e8=@#chjh%qilfn=Ec at FcaGsb0\[kok!>^q6\H#oBGfifL4+nHF'9qGBk>lG"+&"DqC,(7+&EY<NqTQ&JOFmXE_Z/Vf_jn4.9H_..C<N'T_,#gV9\NqZstKUa%u$#24MjfNt8ac:c\ZGDI(GVeIB>BJ`$Rk>qu at TR_C"Ul%lnhl&EUI.>T<U%RM1.F1AMZ[GC&<02>UX<&tuk=81o.jifKU+;YrqAh0+Lgg3%lb+j"=%n&G:('%ZVE47H#Pae6Rr%g5MZ&tN8gj,,WV]ZE<M*lj.)mWpaM=pR;h_!5+Mj`PJC`kkdRdIqREKiSF;!oloq*GC at Ud4g76H3e<h\:6qUmSglI>K7#'DA(,l%/A(m"XZ,-#sHk9Qo4NZI'As5Z_mRmO(ZE+WD(W74_=0k*t"aY#'E:%C8hA#TAhBW,,[gi9m<'aqBC8OMh)r,h,r?i?*`]r:D*,\ddeTZ_kAK&<WQOTX*EZaQg3]PI2A^nr7?kF^b$h7<'?.p[m?>*KB)^oOEC;[_AH!LXsa0-3,T<a$KX7+EG!]SlkQ]Y']YZAQC)e#q]&DL?=dlRkA,L77gorHoU`5-\r[2iZEaX':!>qeMeS0aNWdhA7!WR#Kg>+&d,Q1?kF.eZqqe`A'@;:3B0hV8`XB%;P%onVZH4[TX?#$[`Rg5AblJ]\rG"1 at kHLt3isnMHhR'LSc+Mj%9K\V!K_J;>8R/%[h$sbLF,Ea)jfa;\@uB?<`7`i7jp2CBK'PZ$1C>rjU at A(.?kWq(5D2*Y3qRrr'f,k(%2H.;1*)S-rrO at l]qLK38h)MbJTJ%[W1U06(kBW"^8XbGnEeuhXkP^6gTs"W4M06<EUHR<`'!mfeb7JMr"NuGQ.6siP'cN(OIM^_b&_+QJ[1$C%Yq/0=DY'F,OU\Z(ZQ9h/#bE=3^uZqD$g\<@J at Zl>2u-_nZf/'0+VLTj%=YdWg;m(XKP4U+r*%eO+=k$\M]c3YKqqI\r!R5`32H+JhUBhm3t"*3Zu@!d][7d?+-&-h/ZO*`G;U6b,aQ"_-;:-;PN1`%B.Dc3$/%j['>I0Wog>q8+qXnBuB%#b]YH6I7i].m6VK2S.Am\#@I&]eCW5#q[d*oL.!\5n+NWG>#"t[qb\RV+9Op=6G$ag5,aBUCaJ&F)W<?&l]5M=ut?.-e/<*_D;XNE.<I?$CBEC5Z#2,hO1NL0D^LB8+q:3[:XU\R6[DDR]+.('nD9T\M2Sko>*@/gGT;i0)T:k`&iZkYN:SsO=9iM&PF(=_AbCq)2Q_)m5hbe&WF]F;t#'B.'&0g\7\I53*fee-.7^pOc7[s;9.mdUh!+Xr<PM#T8erK$<Y0[`]O"f#6?N.iH_sZH)BOMT(D]Nns:cT*`/IWPY_NH^l_H<e<GoN\"m'-bk$qE*gAo;$0<n"o(NbMNLRHBG&5.2niBo-8\?5_,T at FKgK:KDI\aNY(QJNO.dX$]@@bO=)[ON=,FbNUg=2*bo,!aRWVOME's>)WQqJ5H(Y)OVV;gAi$b3Do:1DOC6bGZ>S72\D?@2MR?C0QT~>
+GatU79:+*g'&N41Ed at i3+n#+?4"TNcgMiS0>[X5:@2?a3'O+-;[MA`(&:XSB/3C"TUtBa$5p)V]9;U'<V6<M6c,5`0*hN:SZC#L$KV#T="noh(BAUd^s3(EWI("=kl,aC#F5_2p&$9CfiVSYsl/_bM#tcNM2?PJ?MZZ&DD7TX^$$tDs`q:n'W)/?(?6J.!)R&&nZPA9YC`7;?#"-]&=X`YH(:N?#md)*drVKa?MClRVL\p-KJ(PLKgkdk:55W[miGCfJ(Bi42P4gP53QL`n?t`VSLuZ&h81aSgN`FD<F2qX<NR(5$Vgec[l4e`kd6f&ekaS-AjTN0XC5:<OI=:^eIu90FOVdt;,k<^InL%CtGHP8Zrh.7a=1747`j;1o=dT(c>p9YYs5-Kak_kD;o5W&7Rj:UNNi%#.a8SA at YZ(8R(Q_A^X^T)+CNR"?YYLbV8RCp2B5)\iD7K)LZ\1>6=Eo[aDW/:U>+*st$Wb/fG8=M`)`R-5X^F]52W"g,N+hf7gTlV"g9C.7Y%9-,CtlW7WNDgOZ:HAqQ\"6O\kj"j^0rJ6,CWu7XQ&!0+crQ<U-biES.J?3PhQYe+2J1KTJUo-<Yi_$%WWUjM9$:/`7an]0CM-=lQ:-ZEtepP:<_)DdK^V3PB_R&b$Loe#sc3'nG7mlZ:VJ^Gak)tp"B1ToDj`0cZa%.Lo3u<'?1!\GcnLhO:cSXY+q?:Z2B`uPl>LTq==CYlKe$X.#N4?[%jemT@?MURF82cP%p>SV`()/DIA0h4_0#Z,R<#d-2*Q$of8IZ!I7 at L=8aLfKA2,q%nW365:4d%/`N#orcom68j=IHYZBS52?JlV)>7RVJ(2-4XT"E=?%O2#nN=gP#cS&++=#LNWpk\A*CG'$7%MBi6EH at jXXPb*hU,'9%]r/Y&N0NWQ_hfFV9&F_CkO?s?uY`=nO<hHAMHK2KGN.Ris"+-%>\>DQJL6Ip_;t;A-E;f*6,2`m:"a;"&qk3.IL-;FX.<'ltYbcWM at t3l*!Fs3 at .pjK*\Z_!,tOpi3YC97N8XXfLH?7^tb,#],20Kh?*uJ_pEEclWI2CEekhH:,r=Xm'"_T'!#eMUUt^=#8m"b8$1(`+LJcpC.2EJK>ohKoW(5&<N)\^6;K[T at VfhS>jlGqBUC!l?\\MK$/tKC#Y`iXkKZ`OCk%b%%SbWj'd7<<`u,KB]t5hpj$]HX0iCCRqUWMeRRiRQZKt&GirRmi;PHWsS.YQ\BK6eD,%?fchXE#Kh:.o'0CBc_Y9uc6L:!2DOdj.BLae]'4loh;!^D%0bU]<[a at 8_a\-4f:RAX%^o;<kcR4FYI1]9n7\aOf,T3WZ^Y]n-41lg#27f&M="#o]JM25'R.V8sN/['&T*)c]O^99&[Y+0(#8mm%/>2V>[j1$IPA4%XC-+*dK9&gcVM*fg7N5^=V=It*]ghs:#>JUWmG8G[N0DGS3)o#gc/3LYM!,`[=C8Ct/;lijcD^D=>+5:jdW at lb`6r]R at jdeZ&0+tpGIXRLr"lHU0:'mI')oELhZI;-?9'Xdl/GcL3$E@!DGV:ZDUb'$mHQXYC#EF'Dr^JKYg";,QmhlH&Ek#9T$Y9E3b=un>h#DBgJ9_4A(QB[&O"@Cn(+FN+Q):eJ5C$5;0`(VScTO!"[oi8;<!;XLnSeA!+%2A^A1qS+;o\'?G=HLH*YXh*3j at +q:e99*jPP^2A9)"IC1cJ-(4D1M:DkKbhq00[T]ECt@%GjipL9/W-7tf+_d;gfIu7/_b5j$+aRVSg`:"LgqWroo^&TZk_WfI:l?;1!95$^t%=eX,FtWYl\p&94#)g3i.Q))g/3F_nehr,,%T>\M@,E\INZVD1Yq>g.k(2SE*3n^6_F*3ko9OQ,4q3rsfS:;AYiL%gkmHm)P\cB4]5kMNeJ$9<e]o"mK3#k0f&Hs2pi^"CSWISP-N'@eAS"9E^f.h_e`fIU048R#@c<ObF!te0,TkHYS["dB=&s?5Z5U8"YHp*pL*TSrYY5O[]XGktCRMl[nVK#3ZJ?,(+)u7OhH[ULpV9$3ZRBcTD$o':\EMm[1-Fke1L,\'=nE_YTh3j%TJ^D/a=%o_5VN2XZ2;5<_$Y.fJ+/uODIWMk5F-C%(tLbp0j066Ku'gJHBG*q]e-\KmrG]R29#dhPh[*pq)hdV,`gBBeD\;.ZT$<3ISJcnMM8L%'5/ek8fO(^B17\%Tq0%t(5hdqZ6elVBW)k0XfdRP4\BO\K=\+VVnYLW?70bL\eBkF\HK!bI?#8?\>PLZ5WsrAT.qm<I_[ZsrEuO at -:"jih9B[/QJ[lhjokI]4j*dS5A9QTL-]1#jh)M=$_)1^cribh%=6Z>8L"JMkEm0Lj=%bi4YGFS6\EC8JW at 7EY,AV*L5hQkW@#ka=UeP0[b`\Alo;XW\15&ZBI2l*7CYN`>BOG\9!hkb5'$$%&>9S[AP="P<QRr at etq-!HPrp*S7P:aC<$\Nr,'.[VKa,jU^^EP3$i%=/UtH97%aMe0\7<k74d.1LggKCB+aCGQS_qUE;*X9O_'1*+B?+p_l20^:GHY6?3dffEgIM27,,W_B<:DEXUN3rDI9XpKC(^V&3F at urSSfV)$gL2><X&W^VC&9<sc?H#LWW#.o_k#.:L*N;n]&8a\qT7;;ieTV[Y#hCWJDd!umEE;U.mE3MNF9Y2f45SF3D-W at 9_K*8:Uo"=I_>*/tai>cVd"`F3nUR_d:lZb5g?YS*Fei"iBmRK8)])cg"CJ_kg."P9"=_sB:!KM$,SDG"faAe6lK?%'NT]E$.;J!XbRb@?h"%tnE#`tT^&30Y]/aAW^"Zj)a["#FSt!e\,-j\[9/e,6+;029]46LO[h3Gab,"3Bh@!opfb'Gcth"=RaBRA&95)att%-B[M]O;KN0E*\3mOrMH9'Dd-GD6\>T=!LN]\"^j9&tAqk@`LkjJt79N\>h at tJ>[4_".F[+EWX!OjSp;;a5,@!;$i8XOa-<r)I%W"E$X^ZS`Z?uk]JhW/u-fMhT2I">b\i?!62L/=j-YJ2(P_r.=+OF>1+VCRs57#f^aB8?q8]-26t292Jo$Q77J-:3 at 5[h1oV=;qrPN*-g:?hnliljiB?-fk&t#[`:k23nOduMOE'o-"h^;pq>+3!T!b at D`W-i]X;:im[#9!9XY05p>(*18a6I1n2O-ILk^^hq>sK]:jt!T,DF+?5k]e"2-BDWbHb&m-]P3HFcp,>X"ohVB$N@:Zs&8#XE,qai7.PV4D[q=,Ve3$56mT&h_2LM.2t\1=_!1?1Erf3dOUeB7=uHql:T?!oL$mKjOH](thnd64NJXYWWO'f<2rmM.K;s7]NP=dRV+j_=p".Hn<.I!BTKa<8(?gc``aD[%+"eu2I-GoX+lnf^W-l1HG%A:31uC<Xc'("::C=5Gp'TW->Gk%6j$leco6AUmb;*\Bml"VVaaFb)>[><LLc!YG#<P?Jh:=.R:6fA&g'N/YPbk?6HVOiiPs/Eg$2G7TqM"k#.3)'T=J+XbJLQ0HVgB(RA=Bfu*Pe"t(>NmFs1X+M\*=f.T+]1D[gRh2T=8?N5=6Lk,erR!\U;+d7FQ`49a'/Dj.p0Vc:`qjCT<esK8j-oe"/b9dQ#s`0d:;T2[U>=2i:V\h3AHHaDr0BRYC.oPj:7opVVm;dPX*.9]NLF^?4a-]7h\=Ik8MbJ\^*5og[J9Nnq&ja*rpZ3:/@m4?4MD02Y3Xe.5rWM*F at 1/Ks=I3H$-"^>mtVA(dE*^b'n@\L<ko@$t`T+#QFQpl3pZrLOksRVrgajtC`WJjouH)5n,dcTf=P=jq79n^%Uq*6[RL^SiY-\jWC7*D9=Af_^=A3.0[tB*dltfS!/_Tb\pSVf4trY/J>/X at +0EU+=?bfJXJK(*V]b.#9:^#oLK'5N$XIZcpcr`$\5-UDJ8KS$qWo,)Ii&Dlr.\"P1&,*Eb(!l^c[7BiASnDEq.NoI at EIjre7>0GNB7bE?^V$\@(uN]Y>AVR&0u`g9'sNfk?B*5!K:>MA#O*pVO+?SQs3[0DSD&0B`Z*>0f_Ha_nBKr6e5L6`52$^rL`Id*RDZ7)m5C.DEQ9lXee`*GniI('W4)qk*qO4IWn~>
 endstream
 endobj
 87 0 obj
@@ -614,10 +614,10 @@
 >>
 endobj
 88 0 obj
-<< /Length 2950 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 4148 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0F>Ar7W&q801n66R!;0B:J"nnWb;Oho/S=[a>@^4Sk7]($sZKRHN>lX[g8WGVpPE,!;d>(3"3?6=lF8Mp#m,Gp\A/+g15FO/Vr6b'Z)#=#80FFOBJ!g)+(SmhZHM$)f[A$`tlS,;fI$kWST(o&[]@tJc,#8C;>#8?:Wdg=6aiCI]GtlhHcXm--io%l<)=G*\jcY4f5*>c at I3I%Jd7NZj?&ehBRXHoN3M$B00cq)AMNuA6m?X_Os)c0%P3.X[%q#/DT;dp2Qi./O(O5rVrcuO7BKYcW\ulC7J?%(M5=sJp"j`%^)]E,9s%VWsUcC3H7dpj%C1k"@l?="g[o_gC^Xk]8W9?=TOnH7Qs7GDrHIG+WpjV]):]Gt]=BABD21\%CLb&V/`f"R at IRa`7S9 at 4HIjRVG5\osQRluq9Z\Q)rL at 5,\O^(n^katMR5,JGS^AH-U[&O&`;[-^`pL6A06N)BHV-u+tM<1Zt>r3&(EIs5FE-HeU1[@H^dm<>9I,K-^72,e)n:b6]dEk at 7.`!MAl%A"X at PjZu>L1STHB:OdT--.HNZM_^Qf*!'^8-kSJ.!i?R6tVR`=>)J_q1=I]u([V)TB=k:gO9EZ4UnI"PE2kh16S;3I`9.&jL(aKFE-CG?U5S^DUKqNQqYY_Iq0)::/C;Kc)AEL`h)Ja\Q<IC?&!NLW0EfCkc\^?q?h?KDON%2Q$]Z;@?5NRG)8gE89-`bU>GNe.iH5YZ)&/'Lh28&9P]67i("*JLV[>o#Jr&nJ):sQ]9D?QW/Ac2jJ\cJs,feA_fKh78aaDVMt7^Ul<k0=r'qF\l2Q<:a*p(`NCVBl!qP`>/B>,Fqm[kjMnG',0Ah^PK, at NoSOdD[k$0DFW/YENM:2l.P&`+1jW at .<;/HT31n^XdlE@;T$qs2 at -^8b5b4u7)guh<Ar at i]Dn!\_!t=pHp[HctlH&<@.2hk3/ah<a\%q>QYY2Q]EW41<&NZiL.ZDARPTj+4IWD,4MLBDp7BfZeYol%1$1"H$\eqDUB@`H6K//m?);7i at _D`Z\=/2:_:AVEq.okY2Vl2n4PNjUt(Un)^G>BU@]J`lF`1Cl<`/Ci*#4Pl<A\k\t at O'bJYtb5*RFBK$#LkaOASeg3'[:?J6L^Rfl;C[;JT"t`H+bX4lsSClc5%ri,X0dUg,T3qk8Ql:9R1#i6-WemA'A'UD/Bu"I8b0uTD;fdkIh:"5O&iGhD>QL^giV[>mZbNiplOG"PT+&UiO_uC;lua(mHOW'u1B?&<$;C8W$%.P784=?5b"Jhm=K4e)jp9G^?ftd58ck60s;-S/J4(f.l.9P'smKB;.BQqZ&UMIf/nb:k0NBgS\]^aI>kb*o at o;jFJeaC at O:1mc0-?@7dTWm>3/b&ndrl=4B:,D)R=KVW(*K0\AV]@ZDpI97KJoqS.LPN8f6Akj$2p)BcGTe4,XD50.Q>(ZNsl>`\Z0j/,g%'kdr)fU^X%EUA9e(kKg=">X at G#'>BlL(nt+!($)lnfiA)`LNS-oE[I/3?,N&H'ddQgD.QbXI]JCJ`\R^)5fL*f;M8u&6UD'lK9G>*=?,bW[j.T"$thg4AjUDl at K;"'/sVAZROu^F9Z\,9(aL,NpPC"`<cXeSp"p at L#u2pT<\4=ikqPoXFB'taNDdiaJSLkT79j?^p[0B3P]%cPET1[VJV(1]J)=sA%rTp]E4-j:J- at tF2rV.>:.;Prm'cJnB\Yj/@j:Q;:`4;"kTSM(_4"kK4pdcMLjG3fs_-LcR1m6YpE6D?a(8UV&j1!#Q<YMD0q(^Y/u3:gHXsd#Bb!*BF=QS7KLs)s4Gi^me9;A`7bh)P0kraVe=*#o4(^c)o8?qJ0Vk/I=GZR#O!,l5f'KE2Z]%uWrdPnXMFJi6LZk6==(_QJuGVt/3tid_7*CaT8OXGUJnYHB4R6/ca-K-cM-hKTE"?Naj'RlO0^p[8HH:Wq`):(Hc;5c\/oGEWn2K8BO26H0W+sYQnI at O>/36X+%)>Z3\?]0gq^5C%]mHQAq9p2&:_qF2ELpDbfHK0Pd2>qC4bH$- at .%i*-CF4Mo)(N68p360_JPu%KRl!Yen at gFgLhid2!\G0d7Pgo-qRKf<t%sL+kT5AAIUa.+*G`4`eJ')QH;fdb4)Jm%!;%+ at 0Xj%+P[^;lkoC"A at b6FG1?u3l_n4+a\MFhnkVo`=P>>BA_]4:XEG`'6Knb@%MCj/a'P1i3aZDY6L_S1(S7X4#mJFM$U_]!,M9H9l7Y.GK(G,[:+N$L:_6Ck&aF%_7Tacr[1_ZZ+Hm0_u0VKD4X\eWP`p-*6C\UVis8.3jS/e0"O->CV'rU5_I>.SQ_&<fc#)2aA_/I$_-S at a%+k&8'fnKCP!Pk>7e'8QB:Ab\MZ\tZt/ck5F`OJp_EtG)UR7QS=VT908kr&dZlZk^qEG]KFPX;WtC*jS8g\-L9VmiX85]UN"+>_C5V['onYr`)Hr1D-*'CIYU4?(]j6iuQK1;pkqHR"cGDpP\lsGM!SW//&;A$Th-cB`CPNH@>>dLgN=kfOrhat;>W'P>i-deCpZHB=KAt<K.EFsf_sF%#5lN4C,:<`V`(V-UgGoNp1r.S18r6k'J=<i(l6chnAE&:\;DBgQ&RDk%BIMGV9<f%U.o#OCOlcMF&hOD?9e++oethcXF_"*HTbm4HeJNeo<Y@$aYHHqNm!D0`CBcc at oZ/@l8-`$%0];[XGbqS=iq`trA`LAI(f][gjPrND7l at Y*^t:-]2aQYPVpc8ILf]7SGeAEuomk\EdY9^G\=>4_*BA=bWFk`J at -e;/re[Rk>b+q-/=>Lho@!Hs<JpiYCP<E at 1#JmL+QI^alZ+6B5YH\r-c"2J:s4;)Ia&[Z2`rAb+0SpfW8sb%Vk>2=#/DM192]F;\XQn&C(jEBo@(4[<Gh`d8T#g;BQ<Ro`H9[D\ZnmE=e^E!pHn;Nh7ed_&KT[QC'^sfo&BTX6/#!.)mc3(<W~>
+Gau0HgN1h-(4P^ZOl7Uj6f%MQa%neh!a>lO-"11nfa$,0&hf7[Cq6CW^OD4q3Bg%\9C/rH0QhGWF<l:l\Ol5NlKi,*p at E)YfpjVqB/YV%R1i>IfRogNT.2U!T7&c4`,\'>I_H?po>$-R'OkK-LC_ULh6<I&kbt*A3ds0Yh!mEC06$h$IBAkU7.#1T)M9HDe(>-n(NNgn3mk4TMq?o5L/D%<:>VPW/10f'o6pC?1io31oI$]qjIabjV0(neT'Dr%oC)$0FUG^"Q7OJkUrqQAc%]3D?G;esHf5C_4b)8Ll#q:SiHf`XFSL-l]"\"<q>6'dPCH;-Dp]VNF!n>_ at _3ES'SD)Q+4%0`;@pgf;sJ3a(PBo*o at O3SKIBMiA`9@*3#5PTCk(Y/*]M at T5[(f">YYTUhS;R8:RHA\lg7?tQ;eqt[=1nRQ)F;[[68'E<X?og5p^2TPXosK\$RS>[+/A94ChIkd&pmYb`*[8pL$1eG"J*r0CAO^&@MR[_a?ldZsqI6]A_ut:QG-LfK>4bYd[c at 0r<tmh_Z(i,kPuYSHNQp1d"OD%:\:791Gq:Uj/LJ7:&d;7+tuf6VWp]!eu&CC'$;(p"snN0^k[-";`n96JB4t;Ue):FeS:=lb1$Z>I8EQTC9:MGHA!G"GkfW`(nO.&E'Gsgtl+SSJPi4l(*h@`k_fj3]S-#i8Ia;JjK=@+]V9P%>Fn63JQ-=Ou=khji*ZBlpY'k,8rM@`NT\dnGg>0*Fo!DF&k0ZU4YbtPn2Xrl_GtsB)$'f5Li'N\C:SbSX]jknOeiCQKp?d'ru-?3n*&1(-$2F!E^seJQOiPR*m5QB;%\oH$B?m/nfl?Cqqp]=!UKWacNiGC':X&^aeEto!2jQadh<Ks!. at t:3%*\521r;ZolVirm*P_Diar[(K;RV(rAVfLgdrYOGc/;TE0&BJ.KmAk$hp.LN*S4 at c3A6W55jo)@gI6B7Xa<T%c-V\s!g76Q#2L.?jWqikUjD,#GBZ6+Epj'-<AVg`mSVSlY6 at _aQP0T^P,oPVptJ?g(AgoAXc"EB1 at 4Q.F6Q_*>>7(&CUkPgtcS!FZ7)ZLFn]c2s4Bkj@'-97D9j#P>CIrurs#l+RXbAh+!"\Y'BKYoQoQA/-\2:!a)#o3Huhf7K5+[_8WUClCR=7:d#,KG>j_-%5suU84844&!rU_4s,11$3`qaNG$u$[H[qhS8qa%9i?Y$e02_$eXH=p1D--K3Ftp#[=C5KrqP2Xksrnj/D@`/3jRUhB@;AdN=-JPP\s5frWf$>';Sr"4q4A^hkH3"']T`C)!ITZb+9B,K;+'K-la^KI,9saU"AV-i?iCJiY%\rFk[RYYZ6fhSsg](6'glT\. at h+;$8sVt6KbR1Y0Uf-h&?;/b&G%_8)5&3\q@%b55Y+$95-g/\?.?'1,eF0dS&N*O^6NS#4"U4`2D$M,CRr9qLJ6*V at jNokX(HQ&a2(j5enRG<u`XUptBVDc!Zap0HDB at D#kVV7!E]_hiY>gNQ#GkF4J8g*aD;2s+cjTIj`#\oBCbIEoWdm4s@*j*ER>uFQ at Us53E6ut?r5YJOVJ;.O6ZfF!GDMht3PACC;N<\#m%cgM@!$%T5?,j<T"U+BZWS#\@!")C)Mg4_bG[SIUP*Y9\OU?LBQ%3DrBP1.:=els8ScS/$:rgcmel*SQ>'qF/#B.2I+j6D+\1Y_kbQq0+]=c'][`Zc70p>fU3Z9'/0un"Pjif9A+K(p;ct-[jUah$i1X\!;R.Z<m3/+o5"c):"i&^oP?g;/h?=aXm9i%5L4a5./#kE"%)tm_kI]M:qf6'G+[Y+NWN/jok]G^H8g;up32J9+Gggn+L1minL*5YO at PEK?tJJm_]md9]/Q"I,l:L.bDItZgY,UoM=`ES6)AT9-8Nm*1<5L^[_6h:qSc_<_#YF-sR+fN?`/\$K81")'J'cIC5osGskeIg#rgXI<Mb^s/op#+DbL=%rU,ZV_&MUX1OC9gO6I*Zn.1E%Kof#cV7%%Him4Nj2L?6^q.<gS8lrd21AW6<AkBJPF%25stc%Gs8a`KiV2R)*=*8B4@\K14Vt-V'4g[=C.f]YJS9B)uYXmUL4uqM]+-l<6$b)Dq=!cRN0*3=@r:8p]DmMs%NLd'nKU`odUCJi41[(>SI5\_>FhZBmdM;#g6E1Qb$*-X5rXd1@[/.rZLtS'=uN`bCnU;.g,V^0L!5JBr#^SPlLm<_4HoXe`SOquo-,1p#0JTu<&s\58q0P=[;C'ZUWa$\[[W\==@'a&i_FEQSVS$dN4Sl1uEYK0CMmZ+:K+X#iuDh1ZO34#4M`lRCSFR2P<\rno<TU.nR(b;K6#d0Ed%?&iPU6L_FIZ_4F2L=H2PPF]6#RheStKiPkA(FQqG9 at RX0q&G"-dI!D]1CL?"9/Hc&5l=eHL;O_$PpG1+7:ec@:.FP]042,#D)d=LSYEZN4><\0ZQ\Z:9$"iin'>u>[3KK\Z]h,qkD4+V"[tI41IG\d%;VrUq'.Kh at C=X[G_N<a:48DK&($/[]DG[-2aN+-]C*AnYdkZ\cNc!/@3n>Fn+e/qBJ6Jb3-#Ms`Bhi2dEGP1=V-&u&,0Gbo^+eM8X?UA6[;+YAlmhdp6mmNVJuVP#&t_-"DsY/2X+#6TBi!]fm,k)80JS=S#arA5UlbfO/;p\HrkCAK6f[Y??II,;R15?MJ]?SC/AfkJ*nji''?:e1?2R`Z[/2'L%aep`AV^:$UG/(j'<Qp<dTNi$=J9fmA9-[HdN76@&?1:AnHS&#R:mf,"OPu5En+E/uc*o1djUDPI!NgABLN2IS(b)8Pp"N>S93B<,8I9Tku7JC.um&-ldUa%YF9IlS8%`pe2>.a2:C1nSR%g!Uie/N,E>W`mE!'RD:DR)`ppfr-8h)d8EZZ=eL-0%Tdl8UV6Oo=--Qe+csqPD4lmUpaWh_dpjTgH(d8\^[KHVeGu0jcYHBrmr0&B_Ir'B:.O`"j21'dTMO1jDjcAb$F8DM3-s@`XJBm6U6"LGQm=ocI2;TYU7"F-%Uhh"1@?l9_O/bFG<0L?!5U$8BL+&Zs'p]p$B:"]qF2>[o0bW$:8gq3Jko]00U_jm(JnG5P>E9nN(+(:F`KB^_Ii_&P?Gk9.Xe[<><9BLj_7U,;J/AQP4RIBL]UI!5/A$GDSCOHpZhG^R7qa2mR*"tOV"sC'B+5,j[EM#A=u7RAH&V+lGtI1e/3Z4qWdYmqF6s;C;ZJP2hHdTS-99TCrTSuobg>WeVtRu]@"KqC_:]")S31<3,[Rr-F[PXZhl%A=B"+Cf-JHFRf;MkLZ>Mt3YjnOcaf*;ZIcs>:1#/X_-b=`>Ah1[M6p^g"sF9X^Lt`>I/#`bhV>fT]t5/RDt!*X>[QK6p8c`1jYJpN-nm9UN4r=Y4HcIG-J&/TiaCLf9Gtro9X*.O:'DeSk(9rD,@'FIR`HW9)`sU5_n`-uiF3R%R)dW:"m0K/2F,'gPI!P22W;/`0m%bFnj@/#QdRr-+H&U?mRku.%HB,`im)85>RNFLnd)%6EY^pXX"sr:Y0Vn[XDZPPqQp0DV/`/J3=DV`&e6o<XPpYLMXGX!HaQ*p>9K$I"`9oeD>:4G=UNF2?K"`<:I8@^hP1eJIG;AMkM>l9e\Ldo_%dgPAmACkNTBcWjd.nr.>'Z([)duZ8IKD,L'VU:5mqYlr>5XG:hW6Uf%55q:l"J,eN:HLbQ?N?H98P"Rr.!#p#2L+)WX at T"NG*.=+QFZK2&o,okKNTN[4R7*4p<"\qg[V7hCH%'N7;I/Q:=:<O'A/DtCEn.-X.jph&[`jg:g5o`mUJ9*KH:n+/B69oY'r,,mKSF$fJi$C]3*hL1]ueCEC9'\\K[_90bG&g%TFh#PlK*CXG\;uIQ*n@#NgIB4il.Fl*gB-*WX,ITbbO2p+#D at JYpn!A\jdL)hEqh)kf2U?VO@%IkK6u7-h*8A1')quAQa0mZ#/f`jMIJB+?V\ePR8prEppR\0H45WQ0r\aCPLUUJ?ZF6=5O`"d4S$Is^8Wbgr$TC/1e529;hoC^G?NB\).NMNBc5qJ9+2Xg4J5EnFY0XQN at Ds2JOqXZ\,F;K[4d(RG03[hHr at 8?;/'bDS='T/#g^g*_REt'D6sZqa^[)P_moKFINPj1q"[3JVl4>Vg(qZN@(hLu+?i;,HX>M6pL[2,r!r.6F;_++t~>
 endstream
 endobj
 89 0 obj
@@ -629,10 +629,10 @@
 >>
 endobj
 90 0 obj
-<< /Length 2820 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3933 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm>D/\/g')nJ0 at I0=RJOJ%h/aq[_,\_TrdBpNB]8PZh-to$WH'Lrk0D9^Ar=(&CR\@jU=^2?EQ7S1Tcb)CslLj6)m>#3`pms^;bg64'i[brAn2D0e;$&\3j3S0'K?7ltn+4RR9\p11K-8R&TARrQ,&G$#rp-!fCrIo2oXD+PP7(^j2s,ftoiF.((;.D`<<$5RH.i+CJUUt#LM)=ni1j=q=nPi"06uY]=n2Vp?C#&TDcWr+QYLChrm,!CYt(bg@@.6>6L05UqKumk\Djg"lc8R$D&i[]J?5%FD'T[e;_I.J7!rT2Zsl80lcbl10Bq at W<sVgYi>DA2*/7l\/?cV9?VctiK7^;V#!3-!F+P[W))G;J(LBa(FtYhEm4a,s at de9(bi2%/3GYA<V&c at Bgk#)sMh9TAqDZqp@<osT>$rVr7PJ>m7Z=eu`[KJf0D;/\#lc7!-qjht at PR'l%EBT$9LOSB\e^GD]8$a/><SQB^?P@!-^!$lbY9]r01tRA/YZL8R1L`=P5+#96'B/.=X<h.Y]?7W:J$\J)*_9X0T(i!re at Xc1c@8>#0bWi'EQ41Q]SU\]Y?db#RhM<V?f,YM%C+`0;XRM*A5mDoLudL;AG5'#B,hXq,7_WOmc+G#a at c$WpVOKbSBP_-?V^h[9G_uji[giPQ4 at a6[MF1qM<A&e7D at cI'%tna9BHk\LcpFq"7eNng%Q_CKEfK0Sq;&Y.u<\GJKGd%#p-c6>:!=-6u?KF&ekr"Mm_lF/e!/r1m8Qs2hUYij/_W%.tB-)S7uX4YQ-6HI.aUN_"n%\*t<$VAZ!^=G at fhgBUe:XD4V5lQ!.RG?D2O&:B*bUmA"p!>n27K'HV-;";9=5:YrT,qe3mWUcd*fXK\S#"m<":d1M09G!D+):B):Q!3b\*9=u1m&YA,:0N*l.2GHQVgI:f*e+!>,+2M:<W'Z#X"'C6gl;MMV/YN$Tab^[aWq?mC6TGkO"8Zk0@;Nfm]dMWnmsI1.FeW>LJjP&`alcO at KeKHL"bMh:cLCJpg`PDBG8`NOJ8>.8k!f`ap?g1EjeHrYU3Tl7fslk`;KE-?%4S1KWekrDaF_bUS`Kfk0!-<6U<!\Nd].f#Wki8&.TWm1dDCo6;o^Aa1VZ"O=*Ca,.9jh<`W;#`/<;"G!,1cqZKL8-(F%mk2Kd:f.sC[Ot7F"c39g#Y3mY>6n5gt4rdT;+ed+NQQHc#ba!).83!`=Pe?d:YPe:>g!cAXV*k"[p%/"6^WqFcmD\<\J,9!T2XlI=GYmnGo^'&nlUb%)e$f#i+K")S_fX5-Wg+?KkVpQ^Zi?IC.D8dT:<U]Ls0<+*b,=%\,2J`9qt9p^%q"c)\@R1arQFA"h_#>dKg;HO5s0bFrLoN_DSCM4q0N)>rr#j/alAjtllIj>4\\[4iB<hA+*a&&+2hOa&ZSOD(VK8*DEiXNrT_k`p\dRKNYn8&BAsHrI^PtOE=*/6p7A%d%0CguMV4hhKL4t,Z+5X.Bse9-5mq?u`<U'6d4.t[$ME[B$-GOJ;Y=t)]l_2$gco=WI#RLpcVE[.9cK%j886UVf2 at ph_t%`D1^j/i"'=7#l0]2`6,iA/DG4&LH=:i,7kBIBKjR$NNO,%r#Nu<L0^!6UcLmEk*\]RJTLE\d((q%cr"C\ZWYLO..-qB.A?%m;>6DQIfb-UKNHA5i+j(\,Bm"6SqKrA7U?o^aS1Os20-8FMLKQ3RC!_L!jf0TNK^OeF/(%D8?-,N<@d;XV>GidgN#WKL=>2Z\7kXIVHKS"&7%"99&M*t_1ZN8>"5PS2/HK;En?&DQ8 at 6`2jrh3Mp'Sb(#AjT9>=bDWQ7=i(Sio?3I4]Gd4J$JXSAGV741b at _R2-/'R-K11]7%3#4#RUB+W?>oPpSSZ3J;Nm7l_Xb%i`Su'C/>rho1.&NXcuqL?%MnOC at G!j[*A3=4Xm'l#csjd81a,IR'11Pu><-4pE0,<HB=D7%BX[oWF"TjBZ-up0nn.Bn'14<]`cI;(t>j\5JXSBOPFT@]<%l0P.6k-;.sJJ;O>7\-#>&LQV00Gj)A1i:D^NHo+"jFsc&6$L[7?O!1KZ5 at 1FIJBd>7Jr-4T1lePdZfKLWLCl4d9L%'KNse<74!Mpkobe>ddh/XBL at nCB>#k]Y4*1+O#<gn3b8G"oS>F1_AttB=Mmfthe_[Sl<fh/pV2/cqS*M:Re^X$)EF!q.0JXK.0'/+mEEs4,=u&]fm at e\\VWC_+\<I#8b'6L9+_%pJ8O(`Lqgf[sr8UtGV''(8.u)k<<"d+g=%p\mN)kLKA9CaG\&\tP>kGb0GiUa8*)MDC)rWR.SSi/WE)pW>QY7.AZVPJV#k:(0kk:$<ls93dc3 at K\;PI6Rj:`r#$/J.:=*:?W,r?BSj*lA(T0O\XG%oj*i/l")Uh>8(dV65)1$rf7Jtg6"N\L,oB1fml<mqD)OH,miUWJGuoO*L?o,fUlY/Q'gFik)IO705i:kCVed[&h>=7bY.a:#\s3N6i:\naR]UA9trh;L7C&k#$a;"1e-\c*hm:NZ*spuWr'q@(Cf0 at D-fgs,#T(iDW5k"THV3'X@\,!T*L?HB][U7%eA"u5*\AQFU9Gni)7)onI(#SYGRM=8b)-X^=:lh+ErkQY/ql*F?CF.E19?dp:@J"UlXWm8_F;qD`-9GFo39d>MtZiJY6cYR1h`XuSI\i^].rZ7KYNC(,rNP.0.g[,(rp7d;#NRC:5[UC!9EfWP6[7@:2k`!qr:3LFPglo*D=.L8!/2N09ApU;g6Z1Bef(nojol-Q@%V%./-a9VC<#Z<ddmgYnhL;i(W(3B/"8fRgJZ%C(Gk(cd+ROCL+*[&L-N~>
+Gb!#_gN)>a&UjCTW;PXp18]=3abOEM\(u9a^?eC^9etQt;&!P\=!G$T3?IALK,_c6%1mK%+&Z#p--D"Y!H>WtNJ>aA#?UMLh[\G`dm4U-mNhL2-^o^f:Hm6'24CTVIQTTF\:Ark9rF#l&74Rr.TdB\8K27]l^#FX]Cc9P$$aKhhI*!o0T>$hi9C!`,UNE5<t%jVOal6+%g%?D?-`hP?o$V7^:\f?pYk]3a-.0$EXDa$?iSQ(p71@:pq,S.c]e/00P5h"7&+&.?.WAkI/'u0\6Z9s"jYM-PEi(]Yqt`uj.jPL/QO=o.34;%&jf.rjQg?/j8raTX-F#Y-Y+&YF9;Eh1RJua7?^s2cOj)RQkE_7Npc4JL[+#u?]a#YMhZ<'.D\Q.17eiC+%B#,A&:YY;7T)qU+Db-.WoJ[G#*C,h1piNl?X!$<7-'DDAppq!#VMB,Qda$gU+.2Lb_Z8N`4!MoGdT4Ku&4N7 at Bp"p-5>KAACLD"?Bl/,RL_DM+^jh"@uOd;iAF?jT5ip<fS7^;cdpO0mY7+]NXUM at _m`@70%2s!X[*%<P)E-;qJ+e4>,sZ/#rJZ_R=sHoBs?6f3+[+%=A2A"B(I[\Gti>NQV67SX%A-!=W"%&G=+H3hj-`Bg^eNmgc<ujT\iF!_L%k3X2>]cEm)?!ImRLFPENXK at a,Yh8i/KmY[FK#dh4gFuqIhpL#NPqGOa"6WV+MlPDI'qrb!ZI'fp=*$&/l'Y_[i=YZ^#NL[?p;Du,Sdcd-B#+f^=Aj:_I$7Q66nE.#C=ISh:CC!qMZFJ`XWZ",Da]J9VbRmnIVrlV$J!b_8Ousf#LQgDcdoEimkWN0jk0U>ENp/Vq)FnRO\_PX at TS9G#L.f-Z44(,nUBcLtH&hWpF,!O.5E,$#*+n%f,8+9-dEjYOKW!2-5pHWn"W>V&#tcbO=("aLQOJhN^cHI:Z@[UMSgE3RGXp,far at CW4S=M4"T)%L/N^Qjau3.Qcc#`HpTJu2UV['>SMaqp;c+@!2b/qL3iaqeUbE.1;q])cm2TdP]<pF)@Lml"EH`iuW)l($=id-V?>fs&e9jTMc`:7=Afa;/*,$RBaQ15![%XWor3+dD0WMPX8<'(H0&\UPKp99r-T[N[7k\DiJMoP$8>B#>/-V("G_&U>?=K*qE0u>UkT]Em at TA`4N_"hL;3Bp4(!h(j0sV?O at 5o3o5T\9.&q.EZ7=kID<CkuG=[QhbpI">^qHAD7j%*&r6P%(^0a'`)_gL1r^\diVDV)mR'T-B[)o6Ne;/^Eb8+dY;@@%^aU#Thj-ZBiYF&OM$3T!eoGr9B;)NjM;\SE-h/CF:<]gd;`KXg^YcpiDEdX4Gl at f^CE,Im>o\[f;aI^hE:G^'&&cfZD=c6Wq-gJW&`8jS/Fg1MU:=@8_,5$e*Rm*eL0lY7[5mLB!sBX-A8js:2X4UY&@+gd,/P4maISO$cN*'F#!i[_$s;RXsN`ZLd'?5bZY9>^i\C49J*0_?..]o"6F at JAHmcp8*2/=%$&Y.GZ=[p<)AOW1J(RR:p`LFalNa4agO7Os8]71QS8SWWe%JK#+ at 36Z3uiABg_@G at mW7_RV`IEOQ$&`<iAm;99P//s('MYaO`h.I0/P\qj(;]";@i at LLM7/eP_O?FS)=,1^QgCJ#VIe[5$>;uZ=`1\fm#^!;qRX?\k4LD:>U1#JjH:$Ls,S#GXgdmJ6.Hr5:%JgO)cQX%8cOm,1"&.HiK<X]G?O#Q;$t@!jmnd>l$:neucRr1EJn]_7Hmie#\f>CgPM2_Z2`r2 at Z#-bc5nZ!H\mAG`A>l(W,MV`0i at Nqhj&3Ve9D15a>-_V_JCQ9qJ'FRHenl967X-M>ZNrPod,^-2],G`[!V$0smkY5 at UH3<=E#.l_oD9aLP6 at -LaRpRWCLGp<=chhG8-!KH&u>OMW<;u1DA9mZ&9YB_$>f$OK*HV.9"f2RLBdeWLY5 at _qMt%DJ(fk#WcFjH5RXXWO at HR-X$:>W;d\2MBO]%kAto[lUj&>rph_4NcWOOa$RR%)pl,u at p)2.Bq@\W@)"hdUfa1,0&oC7Qd\aff]<+=>8U&G.YIjkbYBj&]Ng1AX at Te:P at CSD2R]EhY9mZh(]:.!Imr&$3*61p#3h"@.V1?AQ:@1?&H(BVW)9t^q*,*d+[%%0EEh_$'OEAnf(Gj*7W1!'rq^M[?/[nl.<\CT!PtoM7$:;rVBVT01i_:`'9_O4M),LugG(2\=ko['H^B[7I<;H#C'kljGVM9biZ\B<\qJ7CL),<\M9C0J<EU@>f&ng_/:4ESc at M4CC37+0>8TQkS,'=>5,mi>$P)OLGoKjq6:aCZr#guNG311gr?Yh3NmUseQRF,,`X7`_e*^Z/$B;8)?jDCnBZq8bRj@#R^m\#WVg#[<`<$X`75^\r[p6>KiAB+m(ooTN$ml,D-`kt2Ts1*SnWB.jsdTIV'm at Ro=>L]aT-.WFU386d7$tg_j38%g4jm<r9$_*pVomDaHrH)B_*7_7.=U<Z6F]kGl913s),23h3dG6:Vi4u`NJ2ZD@/H,qFnk'OFPtUnm'/7G>RMW`cbuUfK56]SE9FhRh7rRr)+j"R[H;Rki,?L]A>0%=Oj+o7u#*$3Im>ZBk5FMrV)GTlabUc at N!VNn?VHVoa)U3MT;("s\"//;@Z]M!$iJkJIF7o[q;>EV*E1.uiU"p9l00g?s%SI!ef;*kE$qe1Y<W+H?X]6kh6b=bH(/(ee>@n=`\%EI]-,4]Pbf)9iQ#aAV52ABWX8.poF2'N$rtB38RuAVHH4r':]!*<31E2F_k[Oh8gG at NDe6M(Eg-F=%Hong_E>B<X&:;UFZX(s4D`<1!Tj*!6W4!m+%q.pIBOaPoai0hXck#*24V`[boob,OO^W?!"A1mO2[rsGb*F2XkTV3S[2ZOI8;OhNm;dC$@d-/O-a^DDS&Fd.q8;.eCCV,\+Cr`J(:o6YTYLER[:duHClVW#'5M^WV2<I2^gS"Yp&f9*:A0ALb8W]Y*aI89B;94_6ol^%\uMO-BhSF<UJcP8MIMBCXpal%!mIkI0is\cI!U3fIk@/?1auPU2D%hLIt!r5ieE-_l9cJ(9?=/3SW*D_fK9%3n^L0.<F9iLbcPcnK`RU.S(eIo=WEA+T[>Bb.]C++F`p(!e\n1#05tOHn.JP?V3\AO2,?KZ/<+!PZJZpcK**^,6Xhj<Y%btBW]ZKp04!2nfRo*(Hlt4N.1REml&o5=`@f[fi[$3:>_;+"Q#L5I)r+s+bEN++5lkjf'O^*H[C$F:F<b$+=D_,Gm.Q2sGui,_"BKJDQ$e)T[hdM%Ui>/:2jCf")=7J;B*DpQHd>?V*9j36!hiLTLoK-q&SfG:)!=Y<?>?0Ui>4K#]WXpo+,LfG##,RqC6.678TbN]-\&JT=;f?"I>"9+8j%#jPV"qoQ/'"BWil(bfiPo*840lsL9pCiON<osY)[Z)c*"3i>4@&c[udZ>X8l*MZ._CA;LO,I$U at i3e'lmM<*EC8`<PYnBHJkQ0BB3PE'=\iDK*@]jHVF]<C`H,_F0'+8!r0qIlu:cM/c7DOYE;ZNPHjja.B"cUP6M7W.jI#B^*7\2k!U?QGmL_"3&c[h4j-(OmCP7eQdMQp'&j;Q'eOi.nOSNaV(!L7RhaT9p"_V);HJdFOd=F:ES,!;8=9]%"E9e!]Oa[-Nt@(R;U&c6-U7U>O?*[OhV5;8H]&f7+tpioER=VjJg8>pd2]d/T&VS]Th22;JlAW?lX,37lHIYY0&:Q7h=s.qaYI&A<-%lbR#[&U@#+/&a*74<CpG;1U$e3l:PP*7O$VE@`qA"V"/k)F#g!Z:lZN?Y`VODSfH^oZX9JCN^8B0,g*:uYTF:;/sp,h\OX501#ndENQ/Q0YUiX2V0_kV>?-[s;rO+e0(^.VmrJ/D2bffm80MU;]<#'4ES(Fj+KPA2KDLADFE5A^$!^(I$MjpX\#]N:nNB+]~>
 endstream
 endobj
 91 0 obj
@@ -644,10 +644,10 @@
 >>
 endobj
 92 0 obj
-<< /Length 2568 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 4091 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm>92jk1&AI`dEeGo]r/BYrC__62Ar;6Z@^^Ff3/F4QAo0&.bELoBYPa44E[7`&:$lA+Vq'V\VcH%#N;U$]NRK\*:%.oD1VL7X1O;+Zcfc6YrmB7jc#A#Ye9a6KIk8 at l?1b9n-i`:U9]?nm-qX8BRu]kh4Kh-M.sMLXpbcr\kna3$7it!R2U>SA^P*LQ?'4n0:==[2i8Z>bH]3OHQE+q[,%14V#;n7Ec-'2\P=`L\r3%[r#+e[*+],(n(V+k*BEm8Hi#!gi]S'%-SDZQ'"3$@V2GY"hCm4pHc/Um<XG&(ZBM4csn`E_9%Hh2j75tTS-rb@`7$$BYoQ9m:NK^aqN69uBn1fUs6b4SsB%q])8/AsGOnBV`&a%b-,^];8Bq@\TVIJ_3$!md\X>Z[p)!PRR,ppVQ2T0-_,e-OW(0XV#:ptT+6j49t&tW)r1Go&.7ce/;"sK?:N!$"A"KVfARW4*CM4$#rQn>mDaM)qjYi,5=/7'LBnUuCon4pi\0rTY%4qXG%H3JI,s2]cp#1r0r'(C"`G&SJ/]uslNPZ!?m<Co*X@#t(&[LP25k>+c:eq9+b#+ea;#*>YoVKA_JMMhtihd&Qlm-"Iumr,D10o:'^J7:"^*A0(BR. at Ku[%J,)okurhB6;DL/^Iu-G:_?fHG*k<X.?_iJMn?ahQKeDi`_PU at TgUDA`Ap6%..68h]=)g<cOg;"Rr&eaJ4h3/g.aI:r4'eZ7NF3ahJHg@?>NO:Kf7",\3m1hbD%hm$F.][q;NAHXPC.a+>0,<lRE*MrPPN8jU&*+Y+7oA9GSTqI`%89di`gT'#\3igFY#0>Y?pg%Vh8%g7kY`%X$VmCA0a%5h38lI`Wg-.1.g-2OThbOffqNb)9IL*L]F6r_6qHEi9-fs#6-Jt=l6Jgpq.euImHY+GN%2o;n8=hqIcN2@)1X)3.klY\dJ:M::f%tUpc<9SBpX?CY8<3[qHfk-M$6>B:e()?_DXk@;T_4T<iH!)*MKsQ875IA.`md<U'eXuj.K at fAIEIN1t2[Uda<?!AC@'sN*ou0";^t?>S^jeX;`]6u"!6u9/!i!s!>t3KkN$?@ff$:`@4^do:'0nhQ6i&%W7Nm$ll`jSObj!6ga%T_R'UGdK`C(lIE8#-;klG\im]YRR5"r0K_!C(\EHF-VhS23Ia0XmoIK<spO2hF20JdBWaaEq=)ah;4c()-$cPHQ)(=8<m'=]PWM6o?&cp!j\cM?#WD0e;*,'S4R5"]:djK:;g+EaF8+%AQle!;7Y&dNrZB#0Fk^s-ji'u/Q:=[5>4)Yj]]=E0P+YUt;mK]lGU"e,g6g7l7K(C)1c&lj1E"/\O+A$!N?WZ[p=qbqC1kO:A/[Iktu?2E1EDgBp at O!kaKSKB%V*+W_cgZu8nS0(-YV't%(WrMTGD[i2+YojbQ%*ik=?p!)hjHb0O)u]c`L^cP@*<Jku8-[5hQ4R/s5a;sWK:oLKA?=p?^+Ds2I3^=42)kFq+@$0G=eQj>"dBrlOK at D&iAQ3rL2/HCC<sAKUq]Yq,jpdHVoeEsO*k[UTD**3_s)H^Bbo,CVV27'qq$&CJ3KSi?Z`[Jk$XiB]V5\<YoPYR=c-e.=R0#Is4eY%/r86U#pKFRo6Tn-e?bc0F7-N)Rltrjgm/'o?YH+%KPH"2&C0ADVHFP at LYu,*eRk?ZS$F0LH=+WiBV'AXU2s"*hZ%?"R#Xb*7I`?*Bt?PA<XAqjK`\._%f'p/KX:68m("Vp[KD&Y)gEDK!gsMW2+aMXqkSX&:Hf:8(WPsSEIoEc'Y7U9#Kp1/EA6fZHuQ,k_O3>'<^U[u!?]H2(^*2oEsfiQMP.'7DQWPF9Bkk;^L)T9 at VnQ.rk"TX5!-KIfmb6I[#]Ar+jalcqh8p)9;D"<U/tIG3_*>:NV[cgnYaq#Dg1/6_ZOc_Y$jRd16lD82-Cr=Q,[.>*]cG;T2_XSYC^/)6#RR1asWLUY/bY>HAqiFiiN=uRW*=UefBTT(Rhi2G`/'BK<`_)(#!s^&t(T&j_[I%5DGl%eoX88#+V+A'I at HA:"qesf&t6mEftuTp/dVjM=&jrrb1FYT,W'U4Q(WOO7=9gY>us]E\tI)X_n&1Qb)GE4cg8lqIs"[`_&O^(pfYPX+9&WqkJH7I=,"SFVDXh,pS)&)G5AkE[l/Jc)#nIDG#GD)S9u;-nMV?XS=7kO at i=Q%6JZedl+eP^B8OGr]7`!Lqt)_FJApB3m\<FV))B!.:=t&gZZPIB<#`tVs![Io>O at To'(/!SD>fpP8=PPdTS"nOYJ.4(&3?TG&Afr(&2[qY1Ik:VrJjIhOb,!S\foP.im,;:'JD]gp6"M3n7N]kmWd\]*[E_jgIp8D`N\f]+e3fJFtYife0G0:<-ZYlNViLlP/].o@*fSbg[!@9rC16`SG&7r,P\#(W*7G5UX^_?5XmA<]nX'TF)s>RJ+f#e,n)iNi>q4%I-Kj"P(jt3hV1o![=hY\?%<'F$rfjP8>[F;KX@]A'GWSXE at T*F/>B.-J.;G/&`ndD4N at APT=!b,_,8uDJs at 9n8ba#V12#]ZGKRCT,P!,]M(APAlf`VYL2ta*<K^^~>
+Gat=/D/\/g')nJ0 at 2(]23Gc(RRpU&Q=<pYQS"aW'Z$XIKE[YUh[^DAH=/+^NIf5BQA^8AmV(DHhZH(H'q91X5m*5IfDLV-S at aJ=dN!!_>)n?%ulie&`s"45mBlA.aa2JLPoD.REU#T2U6U,NZ-F&Yj$p)JOf*\dhDtSM0n\\Zm)*)R\j1;r<=;R3BDD2\f_Fsn+0'l<BaF%!Eo@<V#[l:J>*OniUqRu^5pRep:>:,$N%p;;&Q5>n$s7YUDom;;<W1ugKpRr5lK$YAm<N:32I%Lcj-"`XJ9U;$Y.k_l]"lHTpeIThcI\F<$<Z:97rZj/8.D at 1T9p.(8rV8Wh)uZ!L$\-\KI6dpN'A)AV<JM7>$#<OBPcunJDl5$e,]ri\8+$%jP(7Ib64Qg^ZAd8?ALS7sP(4V^F2:b-&[V=DIPA2+A]j#NZ at 8+VLQt:XV_!nn$WLY0fd9pTSUZ+#VX-M&Q#+H\9"*&aT.e"bnLq)iWfC>E)l;iEJ^L4^99K@@'UerIQ/uiRHjPe;keAh<g2Y)(>Q)2X^o.F5.6NQF('Ru)Aj_<U1Th14QfUr+*M7igq^haO9Kco4f\s,DqPc"g.eK?1QBYUP at hKHU5G1a0a'"uqI$9D\lV)%,Rr=%)S],P?[Tu-GT^M$a3W/<`,=qs!J^2VW9Yag,ki,a.A:*Bm3T\VD,8Op+nILO!Zb$`ldlDdl!G<0fA2.iZ350j0!9o(jZ_"no\@%nF'KKIHS>Hm=ki;`9^fO3kQAr5N05]Y%O;B_LG'U_H3I'LAHMGHqL$(I&D$<#1L\TC/4Cds)j)pkYBOODL,#eOQfO*uY'X(tc5UM1jO%2L40N8'o#DW`C;?0%8]TUZ)71i3Ao8CSlo32i=.(Ts%>YG>14<8;#<hPG*8DTGIi:Z3WU6k,SSS%^0H:&`7];,r]dAYNL@*G%XP!&rC8Mb]qEJLHh2im:t-B;_A0(d.g$n^X(Xts`D=(c,4riK.ZVg=pt1Quk!e-tGJiHX_a".Y9t:5a/%irP,l*t,MgetfBQ)3pFX_%'Tc;Wh%_foe*/0aJ<$,<7$^JoTPR1hB_K&]isG#5PL[&0nd(dF\b?XVV5#`WaVt`jTE0X.'YkT)?&?'>Uk/8n)\Y<=plm+t-4tXN=g^c?$k]fEl>I$P??e[<YOE)(X$qK'f^nNgSq)c#_B\D6AMW'<X-s\W0`EFl%0hYmud-#+C]VAL_3]8':T(M=Q$:<Q0LAF'<]`N`4&BnJeFOX8I\tHZFWX+n>b?I_*ZXJB=S:-opIf=/h)aG!Vd8mcMEb)]6]4hm4esJIenY!bK:p%<SsH*XdBW/"rReb=.qd?u944&8UYGo(+l-?ICgs?nK*QaohV3gXE(HO!(q02frlQPA at ch[c0b:@jTaC(D2JLKJC#DX"V?O6>,58/\W'\_qMd`,[@B+._-&GkRB?NYA$UMc at iNK]1_?4aa&'I;k=5AcqU/!TTaIi-.p0eTiSAfb1ZuK-t/(W&<AQK2U-ne,P3$uC5E4j,uq!L"jWMZ>jaPT"AQ4DmtG="Gp.0ZPBt at R)H;)M,D,2Q>H&;H#0u]T],H,6M*4W2VieTZ(tcG?a#!fbC:JugQ&XG.ZgDN^))rkJYNHQ3P_(IHU?_-ukb&#g,p+PO%YOfoH\N*s"^;iFXsY&0Zn]GE<BX$:F,6 at u.^=.\#Y%2+g[R>LEr%L`k<J*<5Aa9.>mEduQ!=Ro7p=\L>!VOJY?4QJJlG#M<fW19G>D1/27dn$--:a,h`tQ=R:jDf_ at -30VoOJ:iD;JrqULGsT.b&(<;R.S]N'">E'kRd%W6=#2RK1>I#bXl%;g`[0Lr2Z$<UHi-KR;H/bClJrjc9%dnJ+G8_H5h=V;;oCdVL?/dcc"YRBll2Qr!"Cm/LYrHR[Fhf<IeQ5j'*7mqQ\*)HhG'@m4\1UfML0_ld2PV>+V;7cS:N:Td^g&'NnW?I2<k1E].4\as6QDV=G7*RR at CU'dg\D1&WWAsEieB*/!bl7C156teD!7d/XKg!a"f*\Et:\3aB^^tS:"lA at oNogad*GScp3)<U+<+a1ZNbQsaNhWRk#%M887S1.dk*\`tHHtRWiVCt$FT-F^FT$4QFaec1iGN7=W-3)W'I<oEE"mC at HbEUdf?-s03Dlu+(67sd&R]BBP[a"gj\NmB[':oAT!H$]%^s,T&sALF^dmVEW+X?iM74e0.FW=LJ?XSFq7KVYjDqO]K3g@<cGO1'KUF@,'s'EXo>b?(;8I/qE2"\DZj4tOXd/+7Ep_dQ4+$(XTrAfC6RGtEfu+oAO;r1.Wm=]0:sCH"%Tj_TRVc!Gk4+aL-$u!?Eh(0!nu3O0=:s>kOb0eOm"/ZEdhfs_NT!n-WqK*-Z7V,(NIF>SJ*FtRPVHCW`?,:F#k[p^Ibj0AUL0GEcb?6*Jr731D5=T^qTI"A#^Fh?C^c&hfGn[T:>AA)k9V9DC`@_\i`)>c5S;'W"m%Ic=kG^-Z"SX6\'dX!ft,9NNj$82d'0e!huj->KZWd\1hjdYgIc$3D`6'<48ou at 8APd("5uHaSBN>pe#;A3W<TL at SsaeI]KD=/Ruu$HB4D]+eq$s$JL9nD]<5c0oB\3X,.chp]ucSF[6/9W7<49MlU!$sRkMkK`&&_%NeUeU=0$VT"2k[2$9\sOMGqeUU:..L4-hkH/,&"*]kU<p-5OZ;$op%d?.bPgW%B6t0:]Fe!=ts!V!I;c\YrZR240E:RJm-L$S_GAmWs<eH=0O at VK]MATC1Rg)E\<uEAIts%9tRo5DUd\)"AF&5b&UK&g/dhojB^L&VJ%uqea[1iN"nWosjmT(mOXA__"g'FoP>=rNGObl0m5"&/0s79up+K0!&Q#>5kM.gpuspY&5."S$h84NB'*Q+R8b!T\>Au2^#:Ll![?tE6XRea-T$]L)n3k!cJ51ZIRL0X^TgV+d/.f>R+;a%!&4pU=O+VA[n8of(h4-Y-+h4(FMhXSX@*0Uo/HOG0?R4Oq]n^RN9ht`l[V*-rQ at _*EX2YEgn6'U1o0,$J4l)-afV;Dh/r5;!+i.X0%n5`YNfC==+K[*pE?]+6^%7<W,=8DiO]9[R>[?$FZ]<mDq;PW<M$aL=SFFZM!"X1['@p8ur-9pdo]kqMVB5Ip#CA=fMfK?=;"-H._W;Wf_m;-.`<;:o=NOU.R00_>O95`3CcqK^!DpAuO*n6n-m8NLW0Ch%giS^*`/l"8i,bMnVa>#0>@Y;g>D)3ku>Fff;%7oif`KQP60-b=,^.[(7:#>U at u&NruQ>On?dARIYm"AEsItE_OY=kLl1*LE1K!5ZE>@O9i81fr4-6o)+-k'@_g"I9TU"M*&j>6%[OGUj8WC3[Q(9):@SA-CQ?K)7A"'NVZ at c,qe+P;JOB_lODIf7AVUd5B.8NUF%Gbl2+\kqmb[CNWM/gX/MU&f^^J:'bn;-Tkb9lP&WcHj<]OBNpgXXOs`!Vr'i?;Tc\K_ at rp(E)-5n&ec=2#S'p8\?jDaJd'U8ilh+FE*\Ji"O6rBfbOZmlK>Uc"mJcM`6VX-RU`VVl*siV40cp[Z2RKtXVXPrShl;TTpe%]A^E[Ta6R(aa\#@M1\SL*465PIm=?lh6-ga"cH&I<.+\8M>NRG>'KRFi\3cq2;<\jWgp&C12><I%lm:d;89nr->30#2hA8-3QF6R\I?!(qs^;^Ts?])H)\Dr>>!:SF_''<Iae5Kd=.'pO5.l`g7=*![<O0EoLiT5)srf1#@kO6sr\:#hX`B*e+k5mR'kEs:_3`AD\qioKN*j`ai\fkd0EY*6pf@:O5d>W?@U,\"KUs;LDm06+E(_&Q%P*$'SRNsg^12k)cJrU5]&o,"_TFpTYbt(T<fgrVI\;S.n'e"/Z\MV!Fi9EMf;CMogjseFJb=8]Zh7TN8]U;<6S7/4g=m8oq]T('/>i"beX<9OXX at _/,6.mGgUO+I<k!C-L#>D\@rnn_Y^qPpn%>,[2=1A4?hPdi;s(;D'Md%P)eK$1+G2d at B*M!R;@#W%n34<D*Z&iA*o19Q7%D*(@Omej?F4>N</-qRU/Ei[!SgCuPY2qmG*fk"pG'm%)UWS>F0C^oRWU'k&q9SMhD]&bFs,1'q7'p%k:AVQBl3;R2rrY9ch[o~>
 endstream
 endobj
 93 0 obj
@@ -656,30 +656,28 @@
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
 /Contents 92 0 R
-/Annots 94 0 R
 >>
 endobj
 94 0 obj
-[
-95 0 R
-]
+<< /Length 4296 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+GatU8gN)%.&q/)-W:Z7KC<VKuW6<@Zag*__<+9KLGpch^XXTWh/0kqO&>U>mf.V"U)`!n5CYg>;eSG5`:LDK>l2&s+qULX.UVZo0q'6ddB&P.o#EqS%?HZCr*Gf*]n`gh2NQ'dfP@,F^M<T7'KUm_bCaq;g8Er\q_;7[Pj"M;X7It4). at Fi^N)>:4/=arFG7]atg!!6eI.Yq07?]]u*NOS^4ru8n;qG5"*tH4=#2p[CO2^j*UG/(Rj2$%+q!X]#?IH<8P:.IYnMf!YAF7!I26Y_X>9V,V_8kHt;u;=$7?Np%r:WjV;>>FBCYt8K86J8/Q32PZP\'T-#QGaE59*fg?%bN!=F=,qltX.D$,(caWtB923LU:HO"HTCh/)%\*\>Z*OW*DBi500pJ\raJ:3\N#66$M>XqMAkio^r[;dnm#ACb(Kc"]a at r[4aif*+b0nbO11\ZIP&dXn#43][@Va2JgaAc&d/]M8J33!F5`4K\A9\9ETnhuTp\IU at O)+$C at s1QlU:D`s2h#&cF\"o>R<l:0?H(<"It[Ob,bj^b"8)-BQB2BWSZ6B[4li-Du.$/73BN+Em$qs<[Ej^j78hS3F/QOsT^2p:-k]M>_;g6CrTJk$)7+d+/-':()6ls&_LJ0J7-!bG"+ep<XK-f*nm0:4WZ?XLT+mfcRMXOl([=?"O."hVF\0kW7G*(,-I;Z=nb$n3$IcWQsh(IrNq#PH'P.$u(ACWJl!CK9==R,Hbn`&stcD!lj4$b at Ka`=e+aS<#R]U#^Ur!^?O1Su2<.!jiJNV^pMCJ7+Cb.A`<F@=Ve'\JbPV-0Kgb\<oHJBRNW at 5]pQ;L_0]E=Icir1bj"lO9cXFR]u.f1qt:"qWBp>]R/W^,c@\*b at mUpF!jjJCsl-7Jj*:f<rKS(J158O1!nN`CENQ[T]RPj7l+HnANc./$X+0rVacS&=st'"f7;6k,i4GLK293fS,k?X"A']W'@9hCFToYfl3X!I_7RQ(,NR4QHSt>&d0:&dVn][t>ONDajp;:4!]E0+DMt-`LVp==p<E8Vq.8Br%>(8-i;')V'-FA0/r.6VD$?!Q;La.@"sg=`j,V"*&c7m:4n%GG&):qu0nLIJ!b(!(.ZHbBX[%:ic at d%:N(1WMW=nmQ$tg;$jsH@<(5"T1(Ni>"HBE2N<aYDfRB)q,/J4E0^L`;B0j\i<AS=NSH*E0;<\LM$;0hIbXZ/cWQE.0k/lsu=rjqoQ[TkI]5G!)g:`"GUEA27Rl-IIDHI?USic.>_[W`#Q!lS/%.GO(B5>%3W>.8(Wn$Y6B]A-u(B at 9tG]9L0\pWA;Ae?jn:V+#em5>M&8E_^GcV[2r%oZWeID4&u='KhF=&=2%)>:VhO"ahUf.-Glt;$,S)RGp`"Z3i^p`%2nKJj,-I5AF5&X0a>!,=lX,3!mI,(%#^(U^2d="P2lRK/(BTifcf/f?sf$NET/p9sSOtpdn@]ECu^nr<o\m2iPrO!nb(I>/,63gUFq*)s$RP7o^OP/LQdR,dX"&(lF:\"0:j]ON58I$d1pdbH)88*cai]iWs^ZAoHS^^'3*U(d(#k8BSQH+&.CO?p.)q^L0]&/cH6SkkK3r/nXkAnk4.U0.KOEKm/:S'5a#)44m`aTJ+r4IBH[fWKXSF7Ukaq2Jmc#]a[:QkDm!V.2a1)pbJ2SF3YFscrIfRr-_n#/no<2m40a9HC1KqXV(2][?a\A05<c9'`dAnCOu<I#(d0d%2gN1oaBIW`jCqT'LZejXeZo;e)7KL)O!Y;%8(EjWj5)Jg$!L.dS`"6_B=dk<)+Xt?/pc<MLn`_H!kYRD^iAq.J^NUX*g3$pT24skd8-5:s&Mu&D@,:KA at gfP[d]BY`nhGl2$I%T"%L:`P]<5\YK&sX;'s:@Q\R=8QCq4PNSc[A[NtDh-#l6!jO1Oa!'IGcrT:C.V[cM+'+_.`,p9nM"_1:+6(?+n,t6Q$XCGFN!l4mJdZ&QCF/ruDu.#l at m!W1GrQuN:JZ'dqOEQOeO?bV)e4mf3nW$PM-A3B'nr(Y#F$F9A(buNZB.5oWhh80&)$7uc(?YqnP6Pr`]^`&O+0L6?5+UU.M1RViiJr3Q8F5RB\)"1!L\<O[KXhr,/[91iUG)7OkXPZhUu?<BrkSd0T7l"POq7f0]Ie[Le!4;-9L?D%,.!t9`e3pioP1P`j`=JLNC:qmrL+Vd-4h4T*fXAULPj&BAq.L[$ZVeM;7SccOFr%AY8R!S8"TW\a at c#Uucuo$!QmDB4 at C_81iF2$6H=hM;<g1n"7!+T610;eqr&L at arC,?pl13&ublmrh?rkH\XIl[@@J6i<J>1R8N-8/CZ!crZP;pN2Io(El#i-0fp/]@iGsD9]#!8f%84M;?Kq.!=#T.ci^A8$0R_Xe6udVQetB&f@^@O942%N=5P5+^ic3]ju0VMq2W!J"Seh+i\dcM2^kTQQLLMXMW0";n at A;;TMGs,iFd"1%qRX+>l_8(8]mQ3p_ul6DYPY8&+mh<Rg=";MG7B_4oJU0L32J%Bn1c,P4Ls]1uV%D:pQ)2po-)]^"\aFYhHsu1oQA`+EH at Ac-.E(Zkn!uO[pZ,CnQ-YD6@/JCb=t/lno(2_T;/0q#bmbkjtDIf"&m,Y=d)M5/mjh at 0k1GnfUu*7^K?>cdJ"\JmgAR)EA<NMW^$1',"$bGTg:.#q`0i`0qtnQ=rN%5p&K]>F$%5mE*J)VG'rd&6gUDf^)1%36+*'9JE,2Nk96Ic<F0`MPg^V-iP>"^2<;$i4<EZL=gR-Y@"CiBM&oIQm%b-M!XRq^3?r1'R;KZi6e(qrCN1k^MdZ92Qc?Zi+TVhVXGLLZb>5`1cnmbgGJd-S'H3+0\M;O7>AAseoEZ,=ERAEAmYGq at 8`Gd`.8^iXN0c)]Y,03Fp$a0g'Q?P8SeECYg'U-bNM_-^o[F at 4kV*i$LEpMM(.7]aKfE!E#kRCPjEi(rM85q&u^LQR<(P"AtDF?B*,(\XCCan#Yh3dd">+58LL2u.Z%^cKFqrMk<YZ679[o0VpSIM8K[0'A4cGXbt05 at kNA@H=OYT5L#GoUk<Yr1*B-K%Km.k4FRsSVA#r8oi%,+D=<rgCYi&c[JV'&+-\BnNHP/W>c\30.?/O^?,\l\"7ic("4:iaG/fIlK\LBFgF4%eh[_RD^$i6M"g at 1i:Glq\^PjsjLB_'tO*,j_:(Nr6hp&kT'mhAOnE12XmP\smZNEi#DP>N9Ll9a=lVU%Sde[jkh/6U%\U4$0C<dqZY-gPb[#5_TN"8qZ$n05Nmk6+"K-c&$`^cr0c'DM32[?q3k,B-"$..Ts*(a7q;khV'lq$-Xec3O-kJenI&P5"C3XFQpkA&#afr6N at DqY!Wsh'QNNA#C,Nl:YpX!dt?WW,G+ZP)u,BM;<.P`E=\=,T$iN4;/>=jH^Z2s)K\]=*e/,Ni"D=Wdt>3'pPom*rg5O\oAf<kM\Q>lu;F at lC?-%[AaoW2T6Y2B;3Ig]8r(qqbO_X+:XEkDtA!F2o:+upo/)D&o:\#_\VW37aT&>/8NUZ`fBB%,Lgjg$PG(jk;R4XmPHEJ?[=&,)%`oq[qCEdW!t,P='$KiQ^9C;iY@/3%tJAVjYKrHDHg2nFKJ60KI>P0Hgo,u;,]EWh-#1dMl`mI,brs0@[YS"J$Q4c#\(F$7a*)?d%amp#eGH4!`)TQf-k`dV56u]obNon:<&#nHIFl+k2DIX\FroWihg2LR7QM,>W2A%7J6oIP>5&89.DqDO+bONPN__"4HQ3O$OP+[d7XU4l\eR%fB_Orc\NsI7s?+[i8bFsMRk?ZX+^Z39TJ\bOd&4W+qQ3Ze_N0H0Zn/L'Y)F?2Rlb-*jE&5)[dr]SJ/A,>MndH3*Q)G&hW8IgG-4c'q&r2cKN17C?ho?WbqnI<jeS2=J6$O2:^?#gt7l_T`nMlcKMmC/#aF-h#Pa56cPe9#[AMpAQUm'Va,'K(YiU$*Bqsk*`:OKh<oou_-6)1io at d]nKi>_=b3ZaRh_mH"2hYKC^^Jg1H.FTT4pS%7t^$hM[r:Ne((*T3J`:3U!2L=F?>qSno1oI\D.fiqJBSf at VO!GE2$lDGrpSBrD#_e_^R;^GCucoF:neK6+*+hq7uAj^\/kJ'AgKP>!m.2ldsY49)ou53j?45[1U6$!!oGU09c>_X0OtdodbtE6\V:D\+?KhSg7*N%6O-ro!8.:VXm[^bOM9JW$0YY2M`I.5;I.H=cVZERR^k)<[TZ0Y:21::P6IHnMIAnl"<gspZ/1H^B.ijrI4t\)a+qX_+ai,K6d=rJ>9qKo4tD>I.s?+.C.(IqiL>=RLtV8M at L+Ar#]^Fr]1~>
+endstream
 endobj
 95 0 obj
-<< /Type /Annot
-/Subtype /Link
-/Rect [ 252.04 325.25 341.75 315.25 ]
-/C [ 0 0 0 ]
-/Border [ 0 0 0 ]
-/A << /URI (http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Types.html)
-/S /URI >>
-/H /I
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 94 0 R
 >>
 endobj
 96 0 obj
-<< /Length 2460 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3931 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0FgMYb*&:Ml+)#M_q85t__mn7)emV[nAD. at 4&K;L%2RuRZn;Q<Af"8hU]M6o7,7>XUJa at dcfcchdrLQX$CUZpe%qiNnM5=4!Zb`QNBL,6.$s%W>s7Rl^=cUSWsLh[OF2E[n'_4G/p:/T'7E3r$$H7mpPhnhZ*is%E.6&rh/)MQ:A1YCG),i?84M^t47ROo at R^_%J.N9c1KiHh9<G4ankh0:Y[SBn(97pecE&&8qgW'H,-mh%-E)j.Jur4TcGoU'#6^D7pK2JgUE=.8<1na+_kgZ-uSl`UY%*.+&T(UT'fCDg<oC,m;@DI=S_-s(@rE5PY0co;EfY7auj"r#MFiE/RbSTlq2?d[6YIr0=I3:JVs(9WOWAa.`D/Z6M,nsaG[qf at -bB:=D[_]5Q%JlGT5Sn at LH5oh`5C,5^jnJ0u'MOb+>prharEdW#)MAFf#9^trAMPZ1&$E9??iFr'^l8(2D+_.IgHk&CVF<'2eX!fU!6-'@OPPN^pK:MYVK;:PZ<P1Gq<EDjhLR%$[7Bebd]U<#PS1+.daC.+jLn'IblBN,$eD]FsY^'L1/>iFu'RVn&0HgGL?UTp6[#0[]l*.EUOmhSV0XtO86YMtnFu=&j$0q#a:m9m%&`IuP6ic"-6kIeIQWd.>$$)O%Sk\+5?dF&X_E`n at k[?59G2VT7$l at FPA>1EeR=9:;p8Gm)!,-k at Cu<aB)CDAua7e=9cS%Igrh5>`LE%S7KuhrWNaDBt5oi.\fq`tk]dGi\F-mMh*/KVbC/kh/dp_8r9t!hNY at k>bY9(Tk,j<bbqZjks0apFWX`?SB%"Xj2H56pUWOA6:554;(T.YGK5",`=eI`:kIIl;6+e'J^pV?HNC7CN?".oVCMP5OM'M-PB'];$5P'_1o[L'9Z=cc>4RZep3=(/B9Mj%hBY"3&Q31UCgeIB\l9l?$P=NmF`O\td3\3Z9E>ck>S/$=a=&JlPG:!U]-.Z5 at 9c!3)*/B^U3$&0=B\4dS4YTPIc]I_KJ=@C=dNcutj&h<AVD$qFQECHF?b!"8F$8%@Q=-spGpb^`?OBKScjK9d;*kOiRMG.<',&Tik1+lWjlC9/ZanLs/&Q-R/0aaCp#N0G%c!AemohNuknn>UqZMRKdfV[DfAP%jD79GX'Kf7!_Z7HB(Hed,Gl90oTSMn^6o8V_cG650(f+4oYb9H;\Z!Lg7ooa%)T[,GC3t6OQ>g6so.Kkl]+u%C58Y#n.N[hHkng/.F1 at d\GC8K!rgHVJn^^,4'`<`QH>L9^/*meDs/`E3sdc"J*F=k=[B*9smjVi"CXZ5P*W7WL*QMdDPQUba6DP_.=Ihu9`5jai<=[/7b6an(h-,V15,"*1`i-, at H23JH[\@(lR<U"f]&8L5N9#>C'0/=/XFfNguIbkY&fnY<@o48VMgL5AE=2)H[Mff3V%_gt<>@RK[6j5k_<'?7ulfsUl;.L4RKBsjYmrT2`nLH-+6DGLRF?la\;T4Y\gW_riLs('ZcaO>d[_oF.8]\2.ZSrskeWgXUBkn=]:MCQ`6u1Z``=3nBR8V`FJ0e!\BY!BgN<:+n$=sXi,.-)ij?$b9 at X)&'(:078oupS+JVdRENpb(AC`/j;16RK<jD[-/I^`8>0(V\VQTokunOKk+ddk-.EI#,=;nr;+eS"uGWI"5f/%$CDC at L>mHq\,frUp;>Y_*AAOYE7]Rl8C=hh<&N%IkCYa"4&9=jTaD]@JZQl<pfmOt`Be:%">_>lO'Sm>_lVKRfW%4Y;R]fHT`+d,ZZ'-`ohs!b?1HDXX5eE+A>k(bf:6)PZgNn3BI#1/#!3VHpF[VhZ[G_#tV5l3%N$G"(sW<br5t#4m=mXR+g[CqI)GOfA\7!o*W;M5#$/"B/@1hj+dl";I>V+`9n7C82Lr"eR:j>-rY at UeCUG%/JrpA/VKoL]d&49tU7hf[Z/Xk8tW\VC)sliq]\tMhb(po(%0pdm5).(hQWf!)Sp<Sl1?q;'6&d.T;?!^i*r"Ot(Da!jC!`BUE(2kW'lfjVt'K=Wt?<Rq0*NIrHjHN7,h_ERt6=/hTj_,YoZf$2m%f\.ZIKgnsR`9U&NF6/$r\:4UF?[r8l_!cl8U\,[O'V'N>P^2&k:=K:<._nnS'*m0(JGEJD1"Z0n<rp7^(rTM1^Qp,^\i4a6MFNLG4*K5T%i:YWS6jL4.mQ%qlBl$<kh\>Uf`";P7#'Oe5UioX4o_.%+L9>j9GFB=0fJjLiE/1!*UE4t:=/d,:3fW1l8Hc3X">!$J6>g%JPf)HO_aLKR>6A,cs(GKUIrEcW;nV<_VAKG>Qn[mt$e"9*;-d'uJVUNbalBrC1p^r_bCK'Gr<UV7c%dm[pEs>eK)G at U8GDjcR\/,Q^T)o^i%7`s*CarWh]afDG('/4r;7%(f82DP'<OX:.`>Vto]f5s>orD0^jo_E]E7A(n8bSs%s[$rdRYO_:5'-oZhhg:O&IsgJ5(Ue+%Xd%[/~>
+GatU7D/\/g')nJ0 at 0B]."WCP4Q4I*aV.k9NYt;:0HgT\OE+!KCelJ)A]d1l%qAYT\25>s?Z^22RAWMVaiA2!WL at jfndG*d1pEP$*G80k=4de=='hnP9$a7OMS52%5^E(#-EZVm^L;"B,;T/;sbNkE%Ag+*-&s\+$P(TOASg.3fT\>SbSo;/CNCqFbBAu!Pj7HV2R=iS$,K62[$or;SOYD&m4-gt5PN?]Qmh]!?he,A\>botsHlja6NYa>rs-YE&["J1\R at .'lr32K.rP4kEc"cTa4V%[P+_U-6bK6_&fO3ZrPJ;%PW0H]E8c*+f[BIF5]P&DI[!hn-Q0aeOr3W%"*Aa3I<mZeNa\*u!XZKj\B1]L(Vb;3&nCA=505b+)Wc)4.H,Z[#Q5'8ZjfA*-2;bVf.19m,hs_5&W#>S&BKNK1mLJ"`9Jg^t=I7%`IUp]GhY(URY.)BkIH*eV\iC2PY:X]<&0omkbI63/\d$?lgV at kIbY##Sblkah33r[uL$qIAampH&j"+K;[X?,;n/XH``ig"3!&-.hbEa at eY-KnGX-0[`nU()Aq!H\^!;n`jhotZbFikeNNY#6-ZRWf6[N**onB,eb*W>10!&0DG7)R!o]8rR:c^#@hYPu-nJ,f65XP`G'(IR/s4#ii at O"@tmm'd-6HIg`s`,$*K^Z]-9Hk#D7:kI_)6'b=O[O*P?Kk#*goSN7K2/s3r&I^<l"Tn\7YY\d5;#sE$&?1/(oD8AIg[12lV<'JhnNX at E0=LDJe:blP4XrLWS4R[-M\aWnDt>(uEk6!td_;>T#tKC?d#C69=N9T25PA?cA`;aijkHd0[of5-?;KIK/`2G`<nTW/.UM%mW-5'c%/L0M4E+AS1*#I'Cil6$[SJHhX*-TWaZSV6(4Da%6f2MCb<OWBZ`5cUXE0]sFNnj+<jOmASeX,+MNND)S)74H`[d'dB-i"aOe(0sEWZ6!s&^RI6$O[0BWuJ]l#uW*mQkeNMc_XScXLBG-:$5%G?*L.-tSqqp at _ga)%Qp?1J5rCAT01/YY;0;P[q#06';87Muj*I;0(\R'>S]6[/#/Een5P]Kcoe@&GKtSAbWG0GX'</gJbG;&i?7!@aSP\)'TBd+'Un&/aP0ljrG)Pa(Gbg\V,NG*")Y7&;5+?hAEtifdHV/9CXi,;Hg`]I%$l<Z8LfVPp<4`Yrp[/RhtT!CO3o:/t(<=?9hE#i2%b#6]eU]WUpp9mkfHANc'r!qK0pYr2FE:n1?#pb#6LDApJb?V_#Y0+]EmTjaLumaDW#56rfu):CQ_u'8I2?B+>"3+m,^<N_p&/-`Fr7;$YUnO]:H=:_7<.6])`m#?Y[FKK).KhaM%&0ks*N(K/P3/[-+b[0-K,SOQ!:5=d?k(e&'r1"kro1gVo%RiI+ at EncX;-a]D`?=32L56W<JC]G0ipTd!N#O/0cCQ(kG",LEY%RF(Y)au/Uk?5%%)MIqeXEqD^+^3&, at +9@(65(1>@U2N+^<Q7#^Z7i"o^*4mEth_l5ngV7KAL/'#th]#3T"n:JWlg=,0sJg9GNH/7EJ/T<A%n354/o%lDsWM$MUm7k\2'i\t`'g8)8d$2i\SgFo;jRs!lgTJd0$0maZML)L\Br`Tfia`!LP,"g?V>=d]mAa/ta&*-(:REP2pk+-6otBI:^U\3VuoV47R,M+JL3hq5nlH&);UH+(W'L at CPda*Eu.6:=mt4>;FG-n;H1Kr<BD??6`<)A%[$b<(':A&oE0Lh%g/9>Usr2-%[\ch:2#,5<7D8mPq.&@G_-?-N>L=jLS?gTj&$O(D&X$G2/Ig&P\t^JhaHT61]WDIZt at Q-WbFXf$;GBE>34/*q[;!]Zsl?Inc);SM$_A-HOocBt"hUSfFc.6#OF,^9MOe6:`H0+i=NjjVY@'/eIDj'06hQ^"!@0`b.`BL>dhkqt$gU>2$fNHEs7RZ3Q)#p!GA8D<ff^bXL6/<ip%>j_8g:/2&&I&F"g%&RCL8I6<n$."&+:US&:5[f'b6drFb]X_rY6RXYk3'dV&CP]t(6tSC at fijl1NLQTFUuY4BKT.*N,)ie*PHbUN0^l/)3Pd1;c>ZC:cul'$&h($^3rK?;hc(*=a+LhVmMWnEO.C"X\+%npPm5(b5$%_bEX)<X0%]9#[CT6- at Vl1[SdrFigShibji6iVM>rqQbFGopY<9KK\+0NBQkp4uWNiN-%DYXgHCq$Yb:fZ6\1&ck6mTlp[c8Y\3d_?_?$#5(@Etieq=H$phCNInL]oF`aC`/8rMhtJ><O\&2FE&$@S*<1oB6K4L%EP^*@Df9^4s!-*60o+quEU0I:Josl at t=/4WfP!"&)?B"%\1W.6WuPUQL!2O,dNk=N[$3(ls0L:4:J%K/M+8 at DDAQO4iufQOfTCBTe`^)^NPoDX>V6:sk?g^hnqjYJF+o&)hQYT/A0rS,k+E3sRNp>2L_XZ+<5#HENX\B]`I@);rr4[EWj0jlc9Eg2&b<d7(XMFCh\-))XUCTF!lg+e9AYWP`%Pq\C4?-#Ir&BMD)LEU]\QVgB*+IOpIS^'9c61dD_GqZ+[eqF8kce]IfJUP"aN=XG)s'Jf`QMb\143Qj(52aAicTFr9,7Lc;9,iq^-KM<:Gje=-Dh:AsK<N5e+VrfSmoF=TKhqt#R6JVk$G%(@AkFf.lGoU+;:FtHa`.&WU*L2CRqYeH19.^1)3kE>4PV[h at p@A8pQ+Bi2 at Vm=>S8/,jL&pB$l5$)_SHao<I,<_Jq31!0KHY.u,KD4mlID%0rEi*fXaC(]bP$:qDpf]Oi:.(K=':fSj8r]WXc*0Xd>dW-2!O-6M]qnl?*9,k97gG5c8e'K`>+_Y0!1;.?>@p-%GJT@#M)RXL(8]S?BC[%:1<_g$q[6KL!0UeGQ(/;\pZ.cpLF24`T;dYg]Tab at 1h'MU.(s.CT?3fQH/[CRUpYB--<Ep,;p#k'lF.n#;a1C-^[>'(l#ARN#TNARCZ5\c.guhkN]ceQ;9`<.4#.$N2LM0[T`2fPC%FQofbj0,`[D;p\43lHf!YmrXu-pliDNZ[@Fh:D,7p at Y%4G/8qKPQFg%uWJ46pC`liR"bRoD%6^mo5aUK?R1D`(L/l#1MW>9%Uh#YU8Bf=@W;:FX8g:-PP^ggCg?k4MN?%350>Vf9/eg%lV)#0nZ6$b">n7US4LrI!:OfOK<.h?WO/dU6UY6?=]3qP&Kg183!0:bYQds(EW^:%nS[PsOWM`5^0aX"Wj*/8Wgd.3rnNik`@Xh4<[\2t'L\#W'_b2tanVD&X&KpJdnh."7iZ;[jZ.;8-IjiPl$*44Ou=\bLe4*LCVFUp+Sin9-<H-45(#JJ9gJX!.NKIZ$TDhRtg#fnIuml%BK+auu_UB/3QQDten<#k&H,ob]!fC\6i)'ffu1rMFf%]jBVFoMrTl-HSt=h at 1d9)Z$/?!4$T:Xt4%@P`\.!A_Psj?o5_e*Zu(NFtjpLepr2pR\P['. at E3K#%?YZ%uKV,0u:O[P>L_aU4"SEdRBqq/]5U#@ste6$S8D#<iV9B4iY?$%/`fn+ITM5]$!`BhC!_nq*As`]2e*I#R1 at CoD!iddMhY:kuuqIl\&*TCA.DP_OCcMC2^"jn\TAijro_ea#b'1,Y)>)WQSX;<"6'JUT>^*jF+?2nt_:L_0DA=d!;+qRr?t?uuuLX.@\q1WeJeB><H)%8ZpU1'$J9+FmA$X1)#n4k!B<7'6XV7DGD<M9*$3W6"P/;X<f]%mHZAk(t"uZ!diCUnf/TZs4JgTEte2H9/[PBHJkFdYU0\iQtoEJ!H:Srgo-G,MVc-"$>;hS!WPG6*=`R]XMUY6HeROfL>PbD%a%8a&AqIj+Qi:i`[24$&qDY<9X]-J21PS"i&5N.%70GjG;[LB+Op3r.kNG%+P"'XWGuQoR'1#,J=11_T;:[!D#[sHZ at -q4em\Er0<Ip&C2,("C?G8k+IWGrr\]q*97~>
 endstream
 endobj
 97 0 obj
@@ -691,10 +689,10 @@
 >>
 endobj
 98 0 obj
-<< /Length 2395 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3325 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gasas>B?8n'RnB3i2+n7VBLK"G':=C\B.L/G.4TEfn[gI#D6jA?uWZpp&=eN+9=tj$@4Mjk``_Vk9ek`MmM`gg!;HA>G%=LTc9G5"M1A]m:QMb'i\D.I6G3ZL1Xp^4J/)S at _nrS6cX(@'F`[Ebj`TNM&#6GUUbs#D+mI*`YO9]=[kJDKY4sK_2Z]K`<KuUW0s)g=ma!iG<KfJec'h-Q;Ng8C]A.-41#QNnBKDsGArKrL3o:pI$i5tDbgp)2%Y<lG(LYh*$oes`j^*F@^@_O[3OYInYXDN\>q>D:;eHi&s(U=Kn^Q1-5U[8I$1^AB[\4<fH42h1(M7O/[d.PI?=Xg<+4_s4lGUj5E\o2KB#\t5]H>,g\JC'@6)T*2ihnJ at X+@/!b)V"psnQ<n7j8^43YHg)BS(KGc']tDg_4JCU1'k9t"s)VYPHh'=cR\_+ at fl'mVC7O0j^_*n?V9:!8IQe0TL4dd?efj-2f`X-aBaTo)78e]7-)Bb=:@&P&?,#m"?sbeDF&P`Tkd;^j"H3s@*/i:TrP97r)=:%H)5%?J+S1*3%AV1f4"bYX"MDtYnQlfb=0:uQrR>S(5Zjo1a.Meae2+L>!TijsQZUg]O!VM#9YS7[BBDWcNF^(YlYL#cc\7kIoSTaDg\m%(bb,Dh]rELIC>m.d)[E^%E<$^fL?Kai$W)HoreWM1ANRL=`5\9Kb]5X?u8R%Xj+Yj\*06^Iu49B^?5Bg)g,j5\>X(ku5P[!!p8$jW%MhfU5U(u@(c"?.9R at m"R at kEKG1)_*PH"/C)1.OeZH(oPe*:3+1$Mqe%E+=H1R8ccir<2b%:n);[>KVC&l_>pL%+M?Ae*bXKbC>m#=<Ob"Sn(fQ38S7B%Vo^'sff1*%6NX)d@<O,"C`C>#b^>k%#QgN.K(*C4GasUZ'Vg@"Wc/""cFLDm<92dfPHa#5drZ/^k:l`j$HWSl^08I^Ft.T-]D$9;'2]&PL,Vg1_#]f=c)Ms]YNmCiSK]j^,Tpiq_[R>OOhgEK]rkD621]/J8Q$Dn@<$?3X"hkY85I0cYKeN/pXW3pS4(T:FM6ganW5%o*`G%so/SFA]`-o#J8(>\-^%6i^V_6gZF/O"E_ZkMNpbZ=W/panYfZHNGY#u_VScoO5n?1q=r`:;f-NifZZ&R\f>I2DWA`s>RqEUuWk>jSHTQToQBIZq+q]iDq]nl^$]\p)^_eH#N_kFn03=5D[gLM0/Ae@&o*NW!GaVs0X_E_cA?fsE;4^&q.d2_2Y=k;1;_)=I. at N2u$ZH.R>`pC5&rQ",h7+;'`q"IfW[,oD-.G#)I#p'&a;0S at R&9\.GhgkaQ0cpjVWG<q98]i,,fg=5YtW=t!RnN@[lgP"8")^L$::&J]M at MW28BE*8"ef1R)nhPWg+;-XZ1.8.TuAVj7+(]gJ2FZ$j-50&e"4P@;3\a6X^Eflj at tcj%6FK"6_Y^;=ZE:Wt;]JX"I_>N,Kfd,X9]ZH/Lr8FRHc%=#5+gON%l-dRUnG6UOplMS*86Ni(sMR>1PJpc9Q"Ej*Q,mn,6NLZM[M01,#oMHNo6+f,js[)J9t[C9TtXNL7i-oe9mNnuI)0B]s+b6;M8c"U,n1d)9)4HuSVr6n;]K]u&*^1D$GLGU6hN^W,=#e&`s_ndDRj;E_Q-sVpmd04eZPISn0^&uiogk8J1\\/q+\_9t3_dMmkk!kZgdm*OuTbIcO/9(7?MZnP;gV(`;B`0.6b:H=;'JmUS0>(Pe6M-(\JQ9t!1f6QVP)f"b;1SsBN+c at O0q)q!/#m0l_f$@3dLt`.6m]luL!iC`J[LkqF4h%gaa`M'?bOsraNCnu)/f$a(DR?-3b*cC$E/hN<fYeN-J<R:gu.Q52JZXfG'SYN(6mYKH1?QRJ&lD1:J7BWH'U<%X1Tn"T^05[A\,bi7?<br1i9567Xi,rEA)+6^oc$GVTA#ik4<@.]m&Ek&s+C`oUJEI6J9-Q<RIn$p>bZ_O\_/g+[kcPR]L.^Q^`s%KnpBfO)qtd[\4CX+\`no3i(@2(0j*LOWMHC_6"esGXEe5K\_T+TS at G;'=5h/,dHuKj-$Xu"M]t.h<BaqS2?0u,^];`6&8cce]YOJ4uQTGU]KhW048XsCA7/e18`ASD$m$:"'eAMlV*B1mI9cp!.TQg)q3qJH"gJ(EGSk:<84dPDgh!AQDrg37n)dSq%:*AA3jHm*)9$d_MM_NA4kdg92aR!$R<9t#ohf.Mp^]#qoO;N7*%Og?XSofdN#FY>4rcZGNeb12l.\)3H)/daCF)AJ,^;]%iVq%$_dR,V6;moN9=#%:tR8KjeHI%TTW:f_%L`'MK7?8XBj"X]GOZg>XZc4QYOsrnHApm1PTa=rf#5`HB;X1*.[-i^3ZbO)OgmQaGZ5nU\Umda]a&r$XNE'%hW!34T~>
+Gb"/)968iI'#+6Eka&$eLA?:oC(IT/lm1W at An%V1Yhn)c-qIg$+qtTqid\_fG;i5gU*m:FbuBZ3)JCu(q/lcuMi6m3mOVngCpC].At'0LB#&oY/9'O:DqHU_mFbr-qq(.^_BY2VR9`su!IoHu\"XghH at Ia>7bM*9nN)]?Nb-pmNm[,^^<+cK^6H1rL0Jre#*D`Na,&#%:SWLs:La9UG2:;'=`raNU*&jtM:dYaMq)D5<N;2AN#[?qM*.Fu,XNJ1ih0]Mhm:9L.Rc"Ds#3hl)(A.`_>1ggb\$i`Mku_rLiTkZ+0eRA![O&(%to;RW"s+WqRfuq?*sc^YXahg:cn<&R("Khi!\O^LP<e#CH1.k/l+7k#'kr*rVUd;a5*C*4dT<2)!<YKJ\"h4T8$opqoiq)5?O4U:V-P`2r;s+%t3Tgq)=.8[e&>6q. at F4B)]L+9#p1n42`;ngMV\371LPA7c\8>P8T]3,)S at K.<^!R23M+YE&P.O,/NWB6 at W?O/Zi',F5f*)-21dWB_ncUn%5%PhmCPF/$rQE"/qf/.bP[?EuWf[5kFC/*CHU'`u9,lS^:StSY'l8;W((QM^1lukpG%\'?Q:@JO_ca#o,?B`AR5(4OnF,U-^05MSTfn[ZN7A at s!"-,0Q'&&EcpMC.R?%(bi.UGf^+Q:aJ[Yg5":^onQg*0plJ'T`mYdgUGtk9PnD/2Ppl]&DD6(NR/crIBN%H&q'HCqVf\?YJN<BEZ>keM-Ng<`H":W&+FW2Ud]Hq$QG8iZfRMu]#]uHYGK\%K)/<J-G!M)0qkZ_<^3N_C at jmU2]R7=aUYoeG/Al2hoC>k0$j0G+X)RaRH-Gm1h/]fCre?IatP0(l]HJk*AIL`>qtHQF6FD]HV*j_ at d-ZpjL;V+]PeT&5;!?[Gjl5[UA=3F(SOnY^]ObaS,gcNFp4`V"u at Q;bDSS;DAK("qTO8O.U:;S<+4=ViqDe`^4>T6UIq\#"KMd2$;5WoNCu"X^DA6gTA!3.:MV8#FFrYu7%D>'DOKbE^W;4U69V%C=Q<o<&rBq?,-n\1Q=$Ln(T6jTe#ZS%[UOQmZ9V>`O)C_^A&6"R9F+M/KuZTS`*+RO at PB.Xj9^ld7OSs1QIBU9%L[a/+,AZrYMpR(BZ';uBe[+X at 0gTCf?9Q.SK_RLYV0K!K4lH/nKq"NR%WEH+)QlXJj`q3$3CBV]G6FUJ/YZ#OPW!SQiP3+K%PrTLU@,u`VV0mJE]hW!a\F*&4qJY3/1P#P_kD[>.WbOOYlY--icE^G_')6iH!<VrnV7pR$"oF8gU`"'!pTiWiV`<=la=j[X9JuJq:?S)/gUTluLasPh6)$^]oSq]7,;C33L.=-_sXd&'8A(ZXLf9\=ie4k%CVZMqP0\-,/ai'8_![LbiG0k<o2 at 8QM5Mi=/-N2^75aK@"ueB4DgXh^9O7H9ahHOmQ^`_>U?2KZ_a:*I<f#TIj"-$>Mgh,"fmd@<_-=ZOF&d=QO8=RD''>RSo5^H41'a^a_OEo2T=HU at 3eJH)]'[33j!SR8/gm_s;OUksg%oUZJX8UT'D&Ufa<HN2N9_.QH?u,QUA=So1c*07!3g#f7QXRr'mT3T7"3'h2W+A&(<j\Q/Y<b+Fsc%3LBfQGH84!g>?@#SYD`0<Qh<GRb0j@:fW,2gp2<(A(:4J9Ih%EEsZpDMSuX]RWOqRYJo1Bs'\QXh>^NeC^S]!*48:#Y7]2AZEbrQ/r7)>&K6N(<$6S$[62)d+1SnYXSF7Olfl60=+3g92W)bWfKl(TGjrmWlD)/4/pTt3/.3;f*;-;A(sh\LAGVF<^\]3c(9XsR_;.RIo#/L&ZZB;FMfj17B7je6"2:.8e;*!KkS8\VWgYHRCE):!Sll80'cK/WiT.thNKJSBN9GMUNG8]Qkd,a2qCi3a8mk93QY-I#5'Oc\;Zq#@($^[::@_U^M?CF(,MHBao:]#p#0jt];P!c_BQP&hCH.Z,Fr+uQAKCkHAjXNP\dCEaYX<Xb^udGpLM<3EnTogTu](:Dg14*Er\0g*%j$LOe5A_OL>6Xq(nSfU=![a`!q4:BEnYYM,g]tpa52R_mW`e96t&E[4Ue1Toe/8:Yhr4[pgN1id(Y,6ZKntpG#/!^e92\,u>P"`VIY!!$O1e9. at 7(Ifun93T-K.-Vs]hEb]6hBL"ib_#%u%rk]D71U(]u'Mm`pE!7"ZPZ!e'.9`)GdHhFc3e'VpY&1M\V^19,g&N[D&G+Qj!:K!@XYeSE>70,(fn<!*4FM\TNku,ck1Kh_dJX"j;-OZA+W[\@/DjsOr at 87"9_!U:%o)aI"(Xe;5$dnbT#Uo%Q9FggAIg(PA&GK;riN_T&AS%J(r$Z4pZ:F<T(8s72q^l1k(b+B7DEk):T0OR>d&k(Pt2/?P%=kM.QSe,iTYL)0E(lpF>C`LJp^4T)#&YnZ6EuuU"/>/nG+m+jms:b2HX_I+^lnH5dZq]SQ?Rg!F=M?fcsP'+2<6JKHa,KCqm"pkO)g75`Ut7@^H6_?p'8fZ'g*[Xi`&TC;nAFomf[S at 13$oh"r(:4E<I+*c6`aTlWYKLFVb`EN40api@"."%;>;eoJT>kh7U`:.mmW-R;3PW(&E at UXiAJ/s-'':euc^$%F>(@&1JEL1(I.,36't#*WbAHi=9TZoU/A`*&DT#h[ERJO_ZuOUHjDC6hB2^^>Wg<4WN0/qdnnO4q?u1Jd5kLJ6MK0pWrB5<7(pKcq+TcLE\'qk!H#Y=a0#Pq"Ws>M)M%C\N0"+[)is2@$R=ko3RJ.A#B=W<OcAa9AGV[NOo='SGo[al7M1?=]2d97nam[mZqXpVEj!cGajY?%k(CcZ=%FLro#r\UHh\nsSJ/3P#)KKbHO%R1U&MjYMUi*YWj-k$F[H\WU+h[CO@@UUU[6LmXD8mc.d*Ulh96ni/q%f[NM5Z\>%Eh05@);Yg:r\P,:56]V#44:#oOF'f:U@\+WY$A`;^%Unu.[t"=^$T at U[\*k*Ah0AJXk3hqr at Qf5f*[Ri.p%%]E79I4D?Z9\tGPe1WdX-[IZtB(;;&AI$1/0[**ntKSC>GTJeuegUr>k8$AWO=-ib;HC*^rGKPi?dX+GidF4BW7f1*#gXX+eUW6p*;se3dkjd>BlY at nXl=?Kff&O[>F-EMT8q+m(3g9FG$>[^!6IB5dGq3++c:_Qn=f]7@%&OPMj\,6qqZpOZr7P`OSG^+\eufquqtLCedJF^>)>Xg#jj"=?Bg^4Vb^_n`5K5bX\\5OqqLHO41WMkee#%7RR!MJfe+>sn^seSB;`H,V:qiY&>;ERe>QFWi\O[dV)WTR5;I1[X@,'m(8<@J8)CCr7fr9m(Ba9u!Iq?h72TeG~>
 endstream
 endobj
 99 0 obj
@@ -709,39 +707,48 @@
 100 0 obj
 [
 101 0 R
+102 0 R
+103 0 R
 ]
 endobj
 101 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 194.18 149.25 480.27 139.25 ]
+/Rect [ 114.2 398.177 326.285 389.177 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://developer.java.sun.com/developer/bugParade/bugs/4273544.html)
+/A << /URI (https://kb.mysql.com/view.php?id=4929)
 /S /URI >>
 /H /I
 >>
 endobj
 102 0 obj
-<< /Length 2652 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gb!#^lZ:f='*%C76Jq(``!AX1Hqflt,2%0`mA@;PG&H;7#*pC$(Q)2fYc at Td];54(8>%#lB',U$RfEKUHhY!W5"s8Y?1AsnLs_^9c]$`P)=8=2O7oMT9KIo"W7.4eHCrL_'Ba:[(FCftcW<CL[\J,0Uffd<Un"if/8h)4`A138b3VR*K=ggOQeQ\Nb2cPLm<CTUcSEUoGq]sNg)i_MP\;.W`%)a;8Y0O.(+59PPN_ at 4/=,6I>JKnZ2d>"@4kaT4FDfS$3K/l?1G$\7N_8U*Xup5FK.t469_cd_a5jp?o%\!t1`_Kn=5e*\^2;0CgYDE<1"8p3r\Ouq3<aQ->F+:EOX?%YA;X^>Ji$,PV`Y at H#Ut8oJsCnZ="bcHX55^5_=WbUVc)u>1RZ?q&Ob7o8>nE>Fci=sds/.=UV`<_oSgn(03[7+jYTaC3B>L1aLt'_8_bIgiS37dV`P0,3+3/N063W&EHoPpW(/s]+'.\aOc#?<_etO)2s96n,,5QncAiRP7:[S^48X'B7c3)H(W1eb/HYT3D[bbd&oWQrj7ud?+3l[0EpQa>oZ9u`%b\YMg&(R,D27j\"]FnBma0->%`<Of\Fegl8CMs[b[kWRSfk)qJBp1^^W/>R1DsWd7O:u$MTdLG/k,K.,abc+Tp=Fck<u2Hbm7DEV(Zg0+qK)gMZ3m[hkn?`'F(.d6he3WCD*L^3h6%HS_oBT7*G(Xqod#=I`9+ajQqp`K=Ei;!<)cY?Y:&c$`MgeiAWPepjGFKi])*7OhI[!?d:i/8hIOlk-qDpS#Pf+lBYlR*pD?J8\Zua4`SlFLjDC at JOnPc@\*K1NltBV*#-pB/Y*+m32hQkAt()3YYK;j'Ma*=G#1Wd?jY"d)c;'#=P&kJ7&J-Ek<?9^*K'>/R`EN:'\2tLo5KHtdq#k;4Ib"$-j+GpMS`jrW at jsIKM&@p[a_^<bd31j)/s^!qocO"e3D[ZbdgHX;/,FX.[#YG*d;lg\@ruB7e`80EIT9$0ZH?=T-_]+:s\%-.u-@^Ik0EO\W?^#AlfXu&*WCts!(LCV`]5oPN,5a*&"+Z;b1LY[5XU^N&-9=bWu4-TjsQX*4,5*CIl\L%he.f[7oR\QB3I"#eAgPQ%qWk"Th,/PV8UBJIVPqO>.0UUAZSForQt(:Z;O=i$o%p]_O?Eg2*C\m;q]:$h6HMZib8AZoF,P+ at IKp,5YS8=F+,0!eOF$UimT_`";8j=XMK3V>g0+[REkL"2``nSEb"1EP0uSpS^;1r%$5T/DkDoQuN>E97>"VD;--AI-UMpk.;jfn3MCbo.3dBA,Pcr7Ck4aml3M+^;55j3uIdbkIX*RZJtUti7kF'%=Rijs5<`+OKj1kq/#-H6`i86b6b)3pIpH\Mj!P+]r3`1X/[8qfhYKC4-k=I;`u/A+h#MdBRPRBM3uO5o"bqgmps*6"bAtVE%L`k'+W\:6&4kLr)l at 0lpBsc0Ml^IdM;nOW<+<`=#T\XU3MrH*>IFE]Lmia<(O%_r&U;JW+csUh2cCU5-k)@5UROZ7U?"L$c-nWqCr';@CKb"a2/Lo&59*S6A-b:,cNa'?A>:Y=pNTnPG<QRnj"N!-u?2BI(Md9<18t1`ePqWPcgTr:Z%#)X[>H,MV'm'=#9&c4g(_kO5;S#jtOp,SU-O8V/0h7lLisBZ2X1,ibl)(aVijHa8lR'FPdt8-p#<[bRP at X0ns.R[:`$_/3a;f=q&ht'7Z6Fi]?3VpsT<>j>-J!iD0hHO&lM[D_JWij4[E<hpgflQ`5s3=+a?-=,"]oYX@"eG0_p(0q>93I<p^,S/NiAlqV*L?n6XcZN)],)Q\)Q]noS(9KOa$6*5BOngpNnJ#.EFQHQ$GlOX7CNEL$o/'KuEB.JO-23P-uN0fGX3-5c7`*nDtbpCk4A-)bjH`aU`]X:(nJ8T2Hr$F`=K5?N96GPgW6>=Qm*MNQIEZ!OUgJo)gkoqa?eB)5;g$N1gI$IB:e"ITCk9PKcU1m+WQ$4VK4b?_5C]1.2PF#A6V[/KgiepWF8h-:LcpT-p4>Fa0*_W6Eg?tmpR8'WhcNt]5HC6=N=ERDfNG*_N9k9eqE#\W"h]&0m43fE!$KP`$:cdo[q6o1jPhM"$h^TZ/_tZCG?rBtH%c'AniH858df\gA at bAWXSMqFu7ARhKhs5:9qe`'(aB=RM:sSCHp at 8([Eq42L'Cmjl$)nO^(`7GJ\b:?P)u<FD\AYLKFVR/6&(_)IW>u1p#<1]5V1+j/E<_'2?-K1=4l*"='pC%Ro0p;abhT)oR<;f&GBZf]m-8Zm[H]sq']s2<[LI%OOKuMYCa&DUn-#_<!L$@cUM2%f<G$XCqWf.70TjOT9GQR+(&@#Dr`ndcF*4a5`(a^Di-_4LN:gocgt&F_J>lO4e8H3Nbf?$MV.a/oGWBq-f.PNtgIXMn^8k1iA[b:Re#a-4f_KZg)Prd8k#I'pFr^a19//lP<h at l!%gQK$"dJ6i%IUY)eF95 at 2l26*f9`&'s,'nJ-])A'dPN4WqJi4[IYTH=<(8=$=a0mk5?F<<mt^8Z.'BMU-^?#[H/!<mc&!Jh(-QnQ-[QdW'Rf$(s'@BO32jK7L5*'3pUjQ$DNZnp#$%84Y4c)Qf)<cGNhWglI!;?`=PV;>#rWmr!)+1F]Z!COP(IDV-iF3ejYbT~>
-endstream
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 488.348 398.177 498.851 389.177 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://www.mysql.com/products/enterprise/advisors.html)
+/S /URI >>
+/H /I
+>>
 endobj
 103 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 102 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 114.2 388.277 329.255 379.277 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://www.mysql.com/products/enterprise/advisors.html)
+/S /URI >>
+/H /I
 >>
 endobj
 104 0 obj
-<< /Length 3816 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3686 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GauHOCN%re(B'h3 at 14iW[V/d*U0\"\h)LI0Mfu]G],3(GK0m"\>?b7Q8?t%k@/p+%P&6bjOA=5pfb?tGM7gLqg]n?YAN2l!Fla(B_jnPfIc:bO-[ak9Isf%#I5>gFj2#-9)Z90m?hsQoi)FsorF^=GRWbem/b?-1.5gEp'g*pFeMYpS)N?r-NkZ(Dn$Pb"W8oYs\<5/<HYst=_4Xm=%U'4slY0K:CNY_K2GHT0H"9a7F_YaaFj[UoH'YFDQ/p<P2D4!SW`2i=pZ=IEqR at RE"GWJV1 at jGTqj0Cp.*B8)BWm:cWhfc"P:aR4RjHbIkpZ'U?nrqT[,A]E$sK7C,!$QH9&]VorS_>>at7f#0W5mW%^'.gJ at EU<<DdW5gdmQ6`#M at nGh@6YHH`.Z0NK+*TBqQgkFlr.q<rPAZZVP/m%:EIM(0V<MA/++;+Qk8<LcbuE=E+h=oN=F]#%r$+ELj'a[R!ej"+N8Jh9rEIRofpB/e-Y!]Q#5!gu&5[-rrRBH!X%<*>o1 at VCnn8H=RY]_G)c&`^6%(E%LS<T*ghFP4'6C9h.s8EZITEJY^93(^A/:/m\>h)pI?M)0CHk4r(OIHeVOLQ`,W36h%oM^Jj[<FD<MW2qIUn)R`6:OYBF=Qo&mm at jL/O!gGf[9tr?^SMM<pSi+!k0$&a#*Y7C:57dUL;MNT4agT&=[1]PQJHbfADt>U;T5fbM\e%RL;)-&br!ft^/`mIQegJWbIC[bQ`Q5'7aSPt11-Sf#SX%O4Os9bUOW2$Ibi-*%M(^hLT^"39t"&mI/E&qmd^l*(e()1QQ`e(#?l_+cjD%A'[Z@=M3- at VE>hOkKX//K>3GS#]KS5Y'IX*H(m1e'e<bMm2DT>=>)8D^8]r7i$)E.tCghPd+1(PZT6\#\2pNMEZ>:?Dh3`C&(^#8T7<jq)C[SYUcBI"+(#DM!XV2]j7#KY`T#G\-LIO0l415Bfm=@urCE!gIKW5hK/D0.JH+pjAqT++beM<b"JU)3d:p<b_m[O%UP""4[YCF)5Zm1#'"YRHjdFQXWbMJi\Y-j53-$<Uq^^`nV0/BTelOrFMFER3de.Dp4EZF\Y$YVW)H)uA?>n?m;f5MNP+R&)!8gF\%TSO'9q?$La)!d[o(qM[3N4iZec[0`0p6oRFha>0k!nRYdV&=lkcjY[O=L<2N!Y0fK0]Nf8)dYiQ:a1c^+T[cq!.eKeY[Dt<@:4_<lbt@/6QcqlM]lbV"\o,93I!^fXlQ;r1<0S"l/Z"8>Q!DIr<^%`@,$/bFrUSJZ9oP?,Rr&Co5S3LmooALh`h*kH.L;%mXQ$B%<+2&kIZ>OMN\H`im66F`g9dR>2i)_a1h4IQ^`"5IoS1/(R7FsHr=5^!V;"%C3+fKG4F04'1fGbRdC9]D]iO7<G890BJS^iXp]UOA24o'Se$tQ3&3r/NFuI<Hi/[t-aJD\<fRe(NY2sB6"lP9,@`pbK+L7X4eYLq$clLIX<]VPYc[je!_4(s801VllP$u6\SPEt*LB4oj-U\XM,+9.(a:YeD2*iZ-\I("XPie77!qR>[82 at OZkkg8KME,]8/TdZ+V"im0R*_8,*2dn7StC%_bS1M-+%;<fVE<V[#BCp"QB\sILKY6.%XtJ2mn!qG_Cb06=^\h0G4-$fqo/\PKp=MIK)I3ie<7?2+*:NfnK/LH=#4e0 at uu*00d2j^\r2$0#)?;IaMEm=7^OJA:&2eb,n*i=bu5shq^)!Xlo%(--2C[6ctJodR'$I$imIW0ZYUTA-i at 4OQq-/N(Um6Un8X*OY<=?C33GhjE"?.[U:m!":bf&$UI89%bD>\iXf]DMOl0,CI4*d5]?l at P(V(DJ7]'$(!3`]L!ItiIb7$WRXJX]`eKG3(jf[7=4.hpqO7dH;K<jkpV9b``LK59GiuR`6Q[*e\-,_noK9tki=l:c="jYAg%aM;"kbbC2`KPk`l?k2<i.="a.1MtQ%u$46rbr]+<r*&/oDH?UCkV(/$hkpg3I66fR`pdi#Ed^()MI93;+`n5so+8J-V&%ho[s>6k#1&&)eEc;J8#__+5&%?*[hI[kXn*hl$(N/V8'gjuWN\;U0o at Vdrb^1V%ume`i%]e`hun=0H))g\.Q6WGd]BPKX_d0l;/VN at -i`n\qEPj8*WliMH?Jd0$_X5u.3rZ&[dsFL`tR[3EaN-U4dF#!W$27k^Z88.7^?^*@iMIctcK+2%Ns10(R`Kf=""W;26j(DJW2P4Jfs/m9]?&YSR5q8?@2Tu[W6$TSWoMKN(XlVRYp'1JR"QYM7&$`G7H.PAt"H-X5mW[_2aaK<K-ZZT\@iZlJubC,[YCnK8`@e4-1kuW(-rBAcl(5d7DhGlHm9OQdVV76&=MN5J[Y)4f24#b(;7L+0Mohk3.act&\Lfft<QMn_!LNPU"O*QJX?j'Z^kqu(9`j`E,lfs<gi0)ZTTK_pdp"a;rOQfZ]r!)kf54j-o\_JXe2F#2BVi+WO1u>2!m^9W#CCfhiBlDiS9d.1,jW)d!T[JZfY[FB^W/:t/Y.UCchDot-;\s6c5c3>9+#`'!<BS1/gr)c&:K"jU?#JXGM)4Wo5.AK1X>r5YLTr^]j)pWj1b;R2SB)RoG+XFGLkP\VaDYio,cW[_+#L1q%'Q+$lY(S28BY^2N1F`TEafK$/FmpA4"^"0*h0'Q4Ia9'I9Jd$M_*5?FY&(UPISPNH2g//0Wo._L'aWj3FGh4o].6E<tg"KM!8BW4pSjRJ:>Yhm3>BB71_/>atfT@$Q0t2S:SpGLG?8][O6ta:.nfI,oCM\AkSn7X!5j3pdkA!_HP",BqAali`URJ^>JSQMA2RLS64ZXD-l,ce*2:q`Z$4MU]0?7@?5Z--g2l9Lul;5MUlKh7i#W6mA65Qf4^+PAWD3Xdeg>-F'*r(!<Jq5,.C7C:`,&GF`O)1cV at 3Y:,a\&SlAlE-Dm"\Vb=FL=`/2s6<D(k3-#:NWJf`eXZ7S81D\b1@*sUGSY`?nk"2a4Fo=q!4[<[ejFC%*MG5kV_1i@%ge,ZGP0=!KoP--,Unoam:S+<+>;KrQf*.8$crA+ZSa+1m9WDVj*390=WsMoq$=tVc(acpLkA[0*gh,#HO_=L$,[\gK[i>q++"Ub.0f,K<,C)Q5:uYmV5GcBM:j\AL"mZJo?uoW(+"gu>.-e"l:a"Ri*+L5#8nln[Ys-N2R'2GAHUSTiTmN0?L;pZEdEtm\*CSCn$&LN?b3"?aZ>FuREU!EkM4Q4KjgX*1`f'W5pPRFB;%O+Y]Yc"s-?J<ic%D%t:@m!cW?B9F1/E7"PH'U7EJI&m9Q:fF3I]MUW]Pnl2F\KeQ^]UY*lTDsJnF&3;f.`DrGma80YQd``sdpan_&(g,-I<:ZUVbj9rb=`3-!VKX"c$!1.pZ-6:C<9@\a10n%Kt8lf/2DTZ2hj[cMEVqtFn^A[!\rfS/hbD^p1-77?'Xi6IR7"A3m-g=MY3'HQe6-]?iqSpp]Tk+YR^r_T'C$5+r:0dI.-T(PAXjW7qel$ik0d]^91Ika'pQ\X^E9+r)R,DFY<HJ4c^3i$bG#1TKCR`6V/0SOVCW8">Z6s(<OL\cDsJS;^5]SrsJ)FY%4(;3&g,%ECbknmt>fZS3c3m!J;+d)HI(`/de>Uj3K/)*^_?$XG<8c at KLQb3)?_3;Wg%QKZ(.+*F^?"Yec/G]BW4MCAW<6j`GK55Elj?oWNT0mI5i13'F9l)Y(8bO?aQ8(L<M,U?A;D6KiN9OQ6Du+'P8tWXXRf=XlL!N(@HG<:`h`]RsgHTnEoj<Hpk$%(JJ)`Y0^N&]@i']=AH+IY_WO;]N4j:6G&an%<>_hWX3m-Q%#2s`grX8$lC at h~>
+Gatm?>BAQ1&UsJXcs)\O$fV'4:*4:"kr=ZDZ'BAW;g0u."\EMY6K,nDqWka--#j_(,Va$(&5aVeMnOSOMd0%1?G6#%0sa?5q1U\0>hC8/rn=,,AH"!a>Sd5)BX/QTlc$d)pFf$Ha1(!g(Md?AU?J2t]_bqc7JDmSWQ6U`EqJhJj;k'<6p at pee14j)Btie35,Zr:H[6;nR7aiEbA:d5O*;P[hi5j$O]8W2p$T-_(0Cc%j"d/K%bHDbdtelfXl>UJ!S_2[*RE+ at dkp7,Ah<L at cLA\;P]^$cr<S5]m95j$iU]sG!n>:AoU3CZ=*kXg<P;_^!cJ<C<dM=R!H[sV-N#sV(?(Br<L6*:r7Aofl?LM3X1.'E%enbi2B4\s.Dm#V(?2R<n#^R3(-<[F",u`7W5+(jG^*fH%B'^R?HNej`LWo/bd6*WJU`),FSsOb=H!_`GV3*r(JlH/lLi_G*i_Qre`2S.Qi1E%(\A`/`@@.r_9D[LbWH=j=63P"*F?P:rG+unT$+5FjC>cI33RL51QN.L:;>k(j(RUriqO)nI[:]q!#>2e\^':.1]smtZ`qKchOnC6Ql4NG_%OU+4#c?-'7.l(=0mp\gNiO=,AZA2*;aIsEM+8/T-C6bTgZ#H^>?iX:%s0dRMmDqZe+eX'X*fb?f2cRR7%W:Qhbm8$-HX-25HZTZ(%Ii!NAJO-+L+,UP*:5E1OQ`"KHtn1!'PO4rpBJC#+*<c/MQn6?3eIAm7`O'#6MB_:'CW`WRe,gs6NfS:LG4h/SULb5rUgQ7;gW9=uOnjE(,um64RlK)IloNoS#4gMYWVXp^DbV at oBQs"!]ME(6NG<cuD45UVm]PMSOLGfB>g(I*79/Fs9Q>+:5$At[dd5tD1QBm8?fk)cSaD2;?Ga9OGTqtD$iYaq8U&c\V^76[Puce1:7QtZSX+#ZdbCAjMI,f\c<]u5(M'c=$rA1fM)3YIpN4CWnO1sGKXJI#8S4:ZkghSaAhHnn:gGU-V#(561PGH\2g&H0"-*abY*;sWQ@?RdT^or'p8#bnI+QkpAADBCDd)Jre7BG@?`,XQ!_a0C23YIGEMg`bODCg^;>"?s$SZGOI$;3P1E/s:&2"u:tE at I[KnR,kIu(`o^dG"V>,Xu+KrP57mFgkJ5AUC"=P[/rKn`fousOQ)LtW:5XV[34--ZW`0MI)>#2ZC,^UUQP=B5iGl+',rL&6QfF*mogQ2RrkVa,@[6%3U;_eV$ZN[pqaR=V]Ie`TO#"V9]):n^_WP&<hu=4?tnX]+gY,t_a/U$IXDS\=64tL`O>l(rB;mKj1ruh?G773X^cK]Hrtf,7fh*uKYT6?>-Z=g9'#@8`pj%NqK26\<&K\SZQKa)9Uo89=O+ at T+fcdQ/`j(`k9S7=+S(N995lV)Nbj=UJDm>$)_he>>&DiFW-CGrSR9B5S3qOu3UN/j'G$'8c]+pj"rA^P\>^8n<)+,VIJnG!Z$n at HQ$Vs1cam=7K>T+e/f:T#;<GlC]>MfTTGr1GJepI_rF5&?[7A7*hf#!B%umAYc*);Qmrf!7)We/jQuepNbiM+<e>5k"C7TpCEBR3UW^t)B(+7XD-&J_.<*,0uM[f);i#a8Qf2@]RSZ-iZWhqVkI%DZ!J3S*B&MF/oK2JSo=_!5YgJoc1g42G&[MEGc5USEmWm\l37J/3in(<e7,nl^2,m!+QC5O3LUW?HiME_C('^]QM(cXM'R+L#QMQ36Z3>Bc':mT]`D0M]<VB.4K.6\l?@j.Ia/3!Ycb9J;GNjghrab7J-V;Q-"pM0h]Z.XbK&O5a`\Eb3#%LG&TXfn[b:s;VlSo>/lRi1FiV%:"tI;,c[K&^tg>t)@lN<.t#Jg\WM+[H\d+b=+],(@t*cmE_X6M$6Ei`bDjRZS;C]jb=28^j3g=^#Z9_TRY4kXe#`Q/6s3qH5)g+gpAX4!8QZ)dUCg<]n\sZ#>'(R9]["YZMUT+3:>V:]Iq\"-A6VK/?&("F1s;]?H,F;'AEXMpE3kPAQ0fRF=e=m^>dKm''NuC02"JmH/oe4si&2=aTYU)'<5`LHfcZrlEa.jk8_\V`3MiI_>^PlMgIcO!!5.]i;hN&)]L#D/I?c?G!=ocHXB?2dc!fh7pL8aZR<8S'D)(:3PP#*VTtm_cotabD5`)iXrIf,o34_GIJ[.rGhN0bF=NP.9jAi"K3O$):ao4^1PYlOf>)V_LWLfo3sUq3d6qC20f"hl7E/7TERrK*,X%B'!6cG;Po27EM:SB.&+cNM=3hE(P%4M$B8n.CM<+pOWidQT'8C5)m(s:i'q)_]5;[Vm"i`&V(J=[e'/a/6(.7XY-^tq[lQaV:YaISqU!H^i6rna*5sATm"$j2&<43d(gO:$FI_ at ToM_p67/.(K:mt0F,dR]*_NQ:/jB#5JOn2CS at .(XufB/e&h\^JnqW]+C$#\C=A.cq`WH1+3d/(PJMUSl4[DOTJMBNbUqf2r[Pm!Bb,>b:iZ8&pF[[Z#e%s/M:c\*/L581<!iQZ1XeIi"dDtZ5s"o(b-6D7/uJ8nL<(%B^s$KOjN5G2)D1,E>rl5`]q.c'a[V4^_?V[Zoq3/78\LhqD:]?T`\e_Q(tBj8f(Q%`4j at E'2[FCpaKen!h/a5=m#coSR(GEA2o4U[/#22*$jaB at S^'Vi4E`]:#ToKt]*QHc'0VHhp=\U8gRU&tQF6(fjKmYR^%k&S_/R(G"soh?\ZDubOY3Anc"%G=\eTgUnt4JPVM'g+.[lJ*+7S72LEY4nLHLg\SA)UVBr9!K*fF_3+^[:!?8g60eP*Tl_-9Ej)lF\H-8q*VA8/P6<'33sQ&)/UBB^<lN,VbK\@;PRfe`(BdWB<KO\'R;>j<c#GVCG#)p.3+9QiJ<:QLUh^C.qXETZ*@JLCusp:4DQQq:O#Vr,h75ge3G(k2bel^oQHM3Cb9*A?gVBAWY`jTXDo]kgs]L?+fE2@;Y1>rn]>u[&s,,W/Ba7lZbOkM?L]kU82h3$5uJL&eT.C'/@Arph%&5h&CDaS1:!@$d%U0P(F;$'lPW)MWtY*G94LiQ-EGs-I5p;@dnNm6rh1UN',?#1*O6\;+)Sdn+i-?0k:e%9cr14Hj],U.gDn at k[e)aDOg.4g`X7uS(kRAC\Q0o4n&?WPpkR:`ChK)JZ%m8WP'1"]StVh,WlYc4oQuUenGB0UEa1cP-*.!=5E?h)GJEpCE<<gnTsiERbY)U;jOu?h#\3J2L$tm>P^;N%ch!"<I0RJc]b3&Ac3`FZ%O*CV6Q8LaC2>9_:ti?TF)o4`"CT8lTk_k`=J+Vsi-Z#$`!)50r(3e#G95gSOe%u[1e at dj1_XFuUf0#qRE?n*CX\#9n,Oudh0#_YPETRjU3H-kX<tDTg:Q<ur7bPB)iEDqT>b5,)*`?l9:?o8G/b(0aEW_3VOP1+(fXZS$UW1X%k-j%'[`CA\e;;4,G=m>^c[l'Js"ZUd"in+\APG\L:o=#.OrCufnN5OnP)lU[h$$<hb6WslKh[WH6:eI.t/j6^eYLc66_A<hS*og^2#EPrpo4Nl\Qh=O-sWZ+pZV*Qlsg`#VVI;C>^hT$B=$T,nIF.]3.j!R-p^i`cFbe\SWIPjs0^bS#,L_"&i2P/L&_3gm_lTD@[9FThpidF-D%%C1e/u`,,tp4aT8XG??(^^R7=1g\F3\NBUf(7.S\l[g:7Y=%Bkqi.2H#D;n_.)nt'$L[:t7oKOk"SG*~>
 endstream
 endobj
 105 0 obj
@@ -753,10 +760,10 @@
 >>
 endobj
 106 0 obj
-<< /Length 2205 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3518 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=,=`<%S&:XAW&G"6P883pl]BDVGVRp()9O.4`.2K9bgY,2m9 at 7a3`W#TPl7b^<AT-i6Yt0doHuj<\-DU+#d.(B#3g7P(Fd=8uET,5b4K:>MQP(rp-AD'YoFESMRIcr'qM3P$1=+0,<\#?D3Eg(73_.6c:Y+D7$hM"D>I1j%lTCXkQ&5(>bjN"u58DjlV9Et;aDECj7m`Mb+VcNj//KTnaOWFrjNRGf9\tf4-DMY-QR!/j!1E9>pQ=X8E$S%=p`E<>s.9NQ->?!kadq\[oB8LCRlX,nCEM"R$&BBD%B'7;!!Zn#DCrlcm%0@)7nt[*=n7srkI4K^4XtG>kV4:5]\;H3L8t=n;*t2F*[%ac=\_.PZ0"qq]G_n+;A+8;S]jbAUj18'r&p[ta<R_qORsc\":`?CAdV$>`LKqn=[&STRMWj=r6IQT5:+g9.+g-j?i]VgnG6I%F0d>u<o0G).%- at DDr8f'PKW7bbcK>eMCk"_lK=,0[?Lo,$PO.4Olo*U[;aioTKPHq>JE"U-/TaDkk=ZBlk/SAlS+;i$]6[MTsTQfl8V.PoDkf-,8"d<NQ:2N=)OV=XAg%`$o))pV##[S\`eTq at 9+mHZc^t7f#]7;m$$(?JZj2Bl]&J=4^f9UY5,/UaHL[4lpSr)58hs*KU`T:8O_^M\R<MEVCZBA$ltjY[H7<f[++n(VN8-L at T1RU`AB+&!UGYui=iTk"%#ooHB=;$O3BqH at 4F)XJX\d!ZA,%0$9 at QPRPHls$*%1i;Zjf%*@PiG at .D*X6JN_sbHj85/jk(@AAN;mIfFBYq41[&I=6*L<Z at GOiO>4Y=_U4B/D0QOYF/pYc`n1LZDnM4!D,V9Vq&boI9qb&<TaJ`ZH-RQY]E*N)gM$Y_(*FcZQW50+/$Qp#6`n\R-L'HBp;!s3ltfi3[Zjq0Jt!&I]hI<3O7A$:h*-hq*WNLcjD$'SLfSEik)=VRBhh^?D>unI3NZ)+t!X`&p-">d>#"7$%bO2ee`IoI5)d%p,T>(nX;,bI/.7U`mGCJkE&$8Fb1__+iTR&'Np"UOZ5HC=TS2h?*ZlL-S?Qb(YE*II(Y`4_HBaW]Dn#:)bo_t]o]%p,p]A/!@t>&YbQ!MSr5?o#8T>;Dkh,sZ=%ss:[GBscj(nQSHRY#id38Sg9:^5]*8R*JcN9X2>&,>ps;hA>qPHh^4Mer$(3&;a%euH)0T#`5!9"m'u,;PL_m^'\qh%!PKu.EIp(*+(Hh-/Vq8+!<sVDqn at o[,jp43RT>C0d6gklr$"hpa4*Uh\bToC=G((!a1pORH#I-](CQ9s$1 at L85Qe:"i'\<$D?lI1;ql*%D%u"mL=cQd%S]u,-WUD#hL&@=J[O._%>Yfsf3oO$$I?.nh+1fe6+*9p,!Xos&6MD=N]>h7lHD#eopQ!:Am^'>/\D5?Z];s*]!qq"fNk4jAf,ro=q4dplAE/q55D6[C*=@d>(Rq5r=DnT^R_uME/,a7s[*U3F?C0lf=2o0+i7,LH`qmhK+cCMuoghXG<A`8C`i_c:3IYa%56t9@\onAr=L4B*lO's+D!]r=KUABe3*rhh^@8MQ[V[bYkaRLh?j6g`rOi>=V^pJb'qPt9]:.>h5_6JljrD,;(,N!;0=e/GX*18%WP.l/g"irO=:&#u=qp]TQNRW6ML1E#DTY5V="!_8\a/J5jd3"Ik)/X&HeqKt?k6PE^08.1>@SR4Na+V,\U8'P>pu@/d at .]#HWIH6Y,5X(n8QBC'1d19T>7'F'Z1*Dn`,Ap'@U68?#aDA#I,uk-@:rA<ca`n`(!a7::,Sl3Xeemba8Q\LBQ/Te-Ns\U at H`A<9$4K<)5M9W:l'Lh.#cBmq*$bi2J&&f`^+u=HA(q,1(Bs$HfB6c>7Z[9NtU^8j<bN(H'-p?\(`5jbA1M:P":I#mp at OQ^lAV)?N>Ua87k8b&(55Cc19E:%^n21PAl<&fXM9e^`Bj]O5s=$8.JCY$Z at Ndr5i^h<Yc[A95<GBWqO:F+IU"-c0["0"</kh4umuLnF;B^=3WcTYpcE$EgGn&Zbc%YKJ"7JKn*%rV(o;]l%tMl*7C7E,0PX`D:7o,_6P at 2pUb:?q:-ZP-B-olOkEsU4$lrhsG0FjEhB3$h/UCl4tZr#-qOlhKtW$>nq3gb?F7Ec5o5^JkM;V##t43H-qS5NNE<BV'3L>lmD;@@b\En$3)+]hpirgE/C7L5QaC<5B+op4T~>
+GauHN>BAO[%Xua-n=5h^G?WRlDUs)+D!.l]:=:KWcL,NCJ[&oZ4gO;:Ou34<J<DJb$t[IsGGd6r.?OT9JA;Cd(ULI<m-OUP^UH7FqOI5*6.BP at ht,g)j"Toq1>,m'I6P91SUJ"iHVPYhba.pR?6B/,0[F5K(:n=tHa at k`5B!])+lE"mgREV=6/IC320jD]6:0Ms75](;E9OR2n(*^V7@]U1bhr-NF'm,]VGQ[K\Obm=615k*43.e,aL+^"p<ccL>;8?I7Q^%RRsBE at ipa_OL7j-)[d#_:EK at F+1do:)Ab!<qg%-b$92ua7hgBk,&GB>FXj101@%&pGZ51cgCho0Q9:6T,2sX]qO*LCC5C*F')mRaEoQj`="irn,0pe at R<p>(tI6M%V@$&G4#e8?]@D]fiKAF3"6);R)@*gl=+Z%(DfI2#o7lQ9hO<LC=kgd>iZ>++N-q2GOKXH`OVG-Fl2?1##D2O*BM?P&Z_TlNfORob#Rj"$1CCb-7pQA4PI5d:EeY8:-@[h+\O%#%CC\P^jD.9_BA^Eo9.:Vfi*jIV[[q4j(@jeR)P at eNt(SE-f#T8=)S/e'4^Q\3Qq<mhgm87;9:)/p70/ji8Ta,*O=?BSeriTa at 7,F!"M]lPt`K/_MYmBUOYh%B:+q!<3EfCptaE`5eUHbhJ\JAt[&,pKL]4>i9_f at 3hKj@hVaIJg,L+5o+U<5ZIC_uum@;a,o[G2Y.auh*hGUH9X$F*^Og55rcbdAuCe0=qK-CQ"c(uL2DQ#luhf],4lku0X9^T!Z[A<JS(jA42u=kja2jOT*=./0JdR2G"(GYF0ZmfT3X?9kJ6Yn"]p&$-QbX7b/mVn4I84N.W4^ce,fju=F?TTr/dp4XoJI3(BR`nAOH7LhbS%Lmao+PcED,>Kq=aLKeL-8W=q*s(Pt?Z at -Xcd(:moO`>3D24>@bK4jR^_HiDnAsNranWInKa`N"j3`3p3G2UW--Z+H3q at aA]fu312^k2H90C6"8N<R<j`^KH5le\\g`Sj&23Sup?D,[#E%md1KEEfD9kFKBDjROJraMuK#IaH!X\*?A:<.6'D2-QB/.a6-SLh)PS@%miAe(G%!sW/cWRdP,dX5/7n7W1=n0>@=3PY.d5B=?A?[S_5%kqa3n7XZ7Q$B8079QQH!"]G*)OK,nj$bNO;9/O)-]1=,qr\ILfD3'(5@[6J9"F/a*2oPSM;VGH(GY[sF6(Mj at ht/LKI&dRH(ICr;#?1Kqr&OYoc4f9`c<<;W%qL`M'8piQ6<EO1'r2+QR=\\l4RSS9QtAXQH>;7$^%8on5IjUJ7K25;BjLA4t!N!12c[H#Xl>_(.+32hDl'<O!6#7MB(WE-b,WJn0X#Z.`q at Ml1@5im/m>FQbD.#+M0s,-K/N+:bQlf':fmT#;-b_aE.h$#F*"qfm(::gsFYmff3A*BFqtI;BLrZPP0mWLStd_$D`^HXF;4i at rdtgat0qEM'L;i"M%)o:JCTWBK^;7XW+>@he5/JYu,SX%CriE3<a_E.s_eGlgJ0j]W6c.@;u:,H(itn\.jqQeL/qgejiJkEhVYp<j.@#i(]?=/4O%dfC4'BEI`lj8a1^X1#=3"r%UAYaFVY8d%OqJ6lkXq<-M&_XBFTW*6q)b<qgY8*cgDR?9R%O*cmb+(=:JM(\&p1U#EE\;ieA3YoY``UE7^P2_ok3mZ-nSe"!,qC(dWL=f`VWlse.+reU"VBg7+nO-,GG#*Me>/eZsKR]@-c=q1P)=M8r'L,W=D'H#@>[pj*Sc+WGeK3#%PminuejB0N'Ag/B(rqQgS0pYN'ISGM1?1AI<r!%LXM+C\Vg_I,E='\*[9VF/aFA_SO"iFRZ!GJd^*bkH,VLB?HkpEB3`?idP"<q3`64kMBA\*,iQ:j&-JaCQJ4lJm:BL0V%gVnrZI3.RI!%#)`C-F:,Wu&=?ha[=O6=dI at r=n;7hKo#@W3C^e<nuT$V7AM0o9(*>/-T0s].T'Aoa&3TQBg^ik_;$uCu\F4(-6W#$[GcS9j2JYU<UJD+aX;\=iR=0AI>]]`'qas[>)eT-UC,I11$569[Zt^m_6L49\K/O?udI9-;tjC9ka70`Vk1_q]W48$_,#TTn]@3h'r?6lGiQ"`Z:DGGq"n]ma$DIAA/lGm)MD5BYhlMaC=31$69<<,DZKn$_"XV70Yf:?(pLSAUelb9&c@/[*FW=<!br'C/4lPnFoC8Tf(lA4D]@r6]2>$WZ<8&.I92i.<2LD[mV]Gfd*W\C:IjVQ$Te at P1mG0C:Khu_K-3`Q!_dd&h1-3o0Ws`?88_E>gM0Jf]A70'mBZpq_^t$=6QHiS]<m#YG*im]a!"T^WdAK9mLq9Ajk'$L.SM3DBCYF?L.n!8;$`"FC#F.4<f=lF;$K0(07DMh/]`&XXalXZJ/L31ok4q+!rJno>gqN]C#XYr)T_oD<:B8o3:7s`Ur9o%;4^Nc-UEWrT+ at qlPR)Ogl5ri6g_=&aA,-")dM)PWa<Z%+mf6S#[pqh",@<k6!/7[`M2dp at WM;=KO#kghY)=&cb#,B[DDR0\GDN&e!fsB_Ztl#+o2HIC07S9g[Y(R%7`?a/p,dX)h5F>%juRbRqjhclnuMJ$r-]M]_o"Qbp->;7EEe%fDhFIA,^[+ at 7hVINCT0c=7k2,q?9WqfQeWt!TQDa`9en$!0IckfdZB_"(C:3EhN9ZK?B&]kJ>kd=%j`XX^:j"okpcZ2E##hn*P^h.Np7/[+:sr?l/?J"K6%:Z*`?[S7nr5%cpKa5IkZYVFKaT0er/4*pU5S,<`]/SR6es7tV at D\PNMQe>thi&B/49.^Z_H;!RlQK;-!il](ja.VB=WDmYQ)>0DJ5k8sP)b6];Mf&ZXW.'rM\/Oq%bk,Y4>--ZCbJjBqR/U^YGb$PZOi=(FgP?HI:72l<OHO=2=o18J.JpGNtnG_T[,KqR@<G^U%&8WC(V",(&JFA:tSnNtrbAGD"YYfgNAnl2%aH#Gk=22'T]"/BA)TW5Ld&kO8UpuTl>KPmerYn,L=#?KhT+)usP1aCtF4jQ2)-qqpg,s^C3G&r?#1'o>jedF4[N?J[]ZmR\;(p at 9keorWEEN^DM'7P/)&jAiF^*EJKTC!+q[N`FNsaSXq+;Dh6XKJA/;>W'fBFt$H+s0sl9I_c+$a!a8E3;`.RID95,]jW/cm7?9jHZ[\utIQ/a<Pig>'H_ku)).U[V:G)*R[iphoaB%H5e`$@CZp[d"maNsl)2X]7qF(jD_)X/O908.,Mp8Pl&AW$k>-TbUuh>KB6lce3h1PV,$oPWDXUFL?[4[$uq''Ip7?EJ\Pg&TR11CBWVl2<AVH'57VGI!p"_=hPlJ=0\m)mH)LfJ]:,EFTZil^8e(k]#25uOMbUQO7(bb5-Z,)mBB.XgA+`(l'iVAe85qq3^]9h(0g6JSC^63&29F5&N3 at .14sNt;2EXq'>WsR^>>[qc(bA+^H:B)4la&2JI$Dr8fF`XLRuuB&$_ck*pC3\*u7/`p$E5T]##5;J[W:P?aJ4qQ3cKBZfBt3/Z$2^A!9l?~>
 endstream
 endobj
 107 0 obj
@@ -765,28 +772,30 @@
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
 /Contents 106 0 R
+/Annots 108 0 R
 >>
 endobj
 108 0 obj
-<< /Length 2671 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gatm>>BAOW(4Q"]i;0/-Zjf]j!(hG`ZdU7=VqIl4[CSF]FWW"<P*hDc!`HbV:B124n:h%&eBQ6j6dWWZ?bQX=gZc]MG8#bC`-%s#$eKA)F8^q#s)UEs\DsNQF$UiAKgQmGl$X`J#S!>=k59B[QPmo:MpEjJ[$]+[4-_o6oud*dRD&pC*"cM"<'1rgW/t*+CQIe`=8tk7[98d\UbJiCP\[i)g:VZr>I0^?D7VFED'@+IcHW\?.YZ%*'VEU9a_Im916H811.`(7;OZcX]@j"+NLE#j\?o28>bgnK`\\^b1bOl-H(8%.0#g\)@Ki5eM.0RJ9Cp33a=U,-0e\`<21.5M>iRT2`J#FeC'KgTWB%q4/nLKo7uK[,3F]nKV0RM35K$[<"eo"ujWi1c<[4cnP1 at -#9-mV1;.>`;I(oL>K3>pjbokR0HR-.b)3-i,L%1#q&Hn<C2HcR2pK.*m%\s6h&`NH".b9Al#.c&8 at Yi!6?H)>,_rc,gnbL$J;#pWH9DeA["f2ka+E2BBk'?If2.\t>7*JNFR9f`2nSMPCU0>R. at N8i=LhZsSCa53'HJhh5HiceN>mMA/JiJ6%%NSs@*?nAtP)jT(YCmgfj)^qXZ(DU&rUsqX2LFKcRfkRHl!.R7)F)A"R>\8nMhO"6\O>6Lgj<&kb7VIQKWQCO?`D7j*TkW+0hG<M>/q/jX>W\r;NV]D)s"scS4n0;X3Gel!J at 1b.T*PM+QGmpa4kh<lVmI(`,g/+ at 1oeQW^R4;M:N1m=dW3[(O)^-9(C[)k'a=3L!Gu]`Xo]i)!gJ9\?&ZUHhN4<#ok"4&97lMY"Mj^;XsNL%V"UB;5\5CTGZEH6+2Z5F/8X7lQN_tdW^P=>0'AGb8E"NG,:]Xq+"H(RrqXc:%3_o7:'"@TGT$"+qf,0l*oC("=3gUg;s6Cj^8V6]RN at r#2)S?g)%sZU#5Y):MdCY^,F13X"Fk+9C]C at f<shV7j9kL]/;Fe'Y1Ec^(a122(YrOaM`lrPUnX:DWobXE94O;1WTV92&*f.>(($:!a*LA\]k9Fdqo='Vuo0UOO4sEadY\+93Yl+dN3(#IMhFA+,+2AF3.Pk$#ed];e9T\&ur=XgU*COJo^p]618cU^E.J]K<XZ"e^9H&*[@>/_.a"RYfB4p(#V$LN8$0YZ-]3s'G/UCQ>'MgW`+o>Es=eU<loAA=c(R/nHbr-]kE2\9m=T!b7QWU;-P;`T42*3[!JFc)-<&ikF\bFC%,X2.Q3?/jRDkKMqdM3c3m7*[ZdnAYq>)eM:#uCLhb?ZT3A"J0KPh^D?&l`ng;]kD,H?h,99OQI5]4E.In;5B]]IDn81Xm\:_D0k<,1fU$JbM#$cKd0NiTD<amb4QNHttOXuha/Org7861"@DO!KdXo&-VC3L6^"["Ydr;\8#,jI)dg#g%/p)lo.RCWs(Va\5mk3!hJ:#p]&!Qdl\Kaqs9Ceoe/Gm.T-D.kjnHO+rtm[k`Dc6RRCYn?S<=lIf;L?Gd(lcF^MXje>]Zb[P_4";<h^\hb36J[Wi!=UHXk=3K#@"+mRR_<Y?o0Z8X(*&uf`!#3-cij-;;/Yh*<m;4?ba"i)PGa'V9Qq?.q_1dN$/;RmAf/_=lA^)EgB]Gi(;?bUn]g`L.\R$LAPld#U+$$BSl]hcR?u6,C]I*1oO#76.1qoNHSN,PJBG9u?:[CD!+k&]_,:dKjgo>[h:u'U,]2MqS^M at 7!c3%-Sa4L%G3:WYK#Bj;9:*!&&0AIS1_fCQ!^LZ+6ZsQja#W^8L#Rk8)AqWC[/?BNh(sTCN6#j-2DA!oWHKC"p;YIl1ll!P*HV:/'L`&=TM+O:%m:am!WVLtHO$=530IjbI4nCc-$R$c9`H^8KiGP8>Y"@ep:cfM0ud4$GVG0_PT;glGs4/G)F%Ati!c8Fi?SQ&7J(8hi_^e4S:0&, at t7`pBi;#9^/%LCo>.qPL/``b+2R_n>lr;bm=)hWVm`g0:h1<DfW/]97!`LHgZ+JPQQM=r[gE#7":Qei;$f&6brloA]DY7 at 93D63`&]2WT>7?.ncKql+%;V.s$hiC-/ns9"tF8e-RlEP#)P)nO..)+K+)a]fDpAa!qaL8:\#+7_ft#`p4GWt/5]^2ONrG39mLoRKmHIe"GL\iVh>^U4>]A&>$06P(J"]JP,*B;I1l-gM5IQCVYn)rDp"cET,PO&e.H+0D'B%l;&aUf5f+BeN&",e`WZQnelRCJ>648H\_sYD]?t3c(sr4>`?LU`-+BqAhU$5okPM\Fr[!H\fV'iO]0MfHER<:eG6[A<mlT%Z3gm1,;d^c+\OpHn"]B<E)ZE&k!UOo%V?9ih-*8<r#lMtWEGW.1*oP#ofli/l^WXJOkUadNg9KFF>?dg8r.kZ>;+ at 9$Q(9QsrN^Nc-O:UKU!;Z^`nNJF#5r?Z0l?LsUR-'nSVJK1\tRCDPLt.*F[V'*g;:SjG)Ysq17b8U3l;,PL0qAsd&fd[JBLe.^fW]8-O]?^V@*L'<6*Pgr"lV1PHbH+0gj/SK6ng#S8&&q1h8FTGB>QI>Se$#oPcCdrg'5/DpApOf.k0[nS^YI^7?=Vc%2#"[En)tL9?L at YE3*n)PD-S(/OJM7V.#8Td?f?&qWu/gJg?%HRC,0AUD0HiXH_`-t(C5qG[83I,C:f+g+)I+(X)nc%Vs;3&H2 at K?"FtrrYr"7^<~>
-endstream
+[
+109 0 R
+]
 endobj
 109 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 108 0 R
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 272.082 130.75 352.821 121.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Types.html)
+/S /URI >>
+/H /I
 >>
 endobj
 110 0 obj
-<< /Length 2617 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 3537 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gb!"sa_oj&o^8njHYEQ"A'Vq#71hA:;D`@@,Z7B3%qB<aZ)(!HM\d*+pV,V_L)NO>HY<p=T?+M11Oc_k]6FuLe0V9(=J*-a98(J5#64FB^c]ZZDk&WK^^GoYPj.W<ap8HH`Rak`R9EXfqH0`pn]S7aRE+R:Qfl\c5Ff\sT^*c42a%bmJ[RjTX3+/FHL@:Z:OiE24t\br<,U1cbWfDC^ggCg,ajr(.3"kZ6BP:q;\;6g]aP:-E4_8Z at LA2:TK&L(9n.=<$h_AE4o2ZGc+t`>JPRJ)LoMM2X+.:Km*#rUI((oZ?69Fg&ReRSS3R9u(RqoOTWsPuF`+[&kk6:F.Sks/A#7`L>3,jVXV'se*hg^K=tNC$G[E[1&03j8-ir,'[%-X#(\P*5\t4_Y<L3W7!pd$H]19/DP[)n,.,unBjF.4)_tm+uFY>RHLa5:O,><B17.DZN;.q at BT3]\o(].Zr#=Or(),a#&a,;-hQb[O,jQ@&WIpU at mM%]Vo]SdH57hKaO)$TKlZVUH,iGidppVItY+rkW3UXFu)>KZS&XVe2`e2*K$(#uZ)_*JmpUj$@O0Z,hN\h5*Oo*VH:Y7Ja;YI`cHD%9FsM0Ub4-Y3W"Jjb_ent8[M:]j44W$gN!=j*V.:d2npD^]WmU+p#:co342.+(#-aU%@S#o1MR``*F?1bP_\41=1sP'@#t:!^HVO7'IZ'HClIiSN">o]oKGg1pI_K<kl.jrYJ1?3o!r7]UUp`?DVq1,k9Eh7,]ZYDXkZO]Jtf04g;'V<9;8NI.0$h[ofWQ(Mg2F&d[7[X[l,&W'0g44W[/2qgk7od3;W#oQB)<7_[Lo:V&=Tf9tQ+_,6INi="_YmejfbP`k-b60%51FO</Yq2](Rs;X at rek1ASP%/+FdZ5hgT at Go@`n$94p6AA#$bnd#D>=:Fo!<qfm'&JhEr=jQpGdWb*2)`_?_qpitU:50)LXqiXP?R9fmU()*qf_=L/7C]hN89"JOD"Ol?+Wd2&;Xr);HD;LD3[*#I`!?*cFIcgB(9r%Jh!8e3RW4pcU(ekqjt'+Mu,ShT['bVNInI6H&])?'`>mcg&o8gA0k(ek)C[QFc03HlnYKQ(tn>b?F,He#4qU"-"]K5ED\8*Z"@:m=CGeb`#JQQ'';cn\8C!NJZ+%M/LjP8q$-7t_(cP="XIDdU8j1oWg%`E?q-\"9P=n8Z6dfL<>>cqhPh=s_:OG)ElEMq6[3;].:\i`Ld at M@fQZf`6&gqVU?%Te/">UX7kLYU`cg$>@&5`cf&P3q[]-BP>9Pj[BX##mE.5dY<*qQLfM at V;4)r//s*uT72,G5h at dkIYt`C,8*nZ)c62Ag]p<Jnl1qFSP&0=U^O!knD"`C(?fr4(2S3cN`s&YGDm)qFsf4XfIA^(&bC4#DE:#j[/&@*m?5rO"FV]G^/\C&OIc!!L9L(3c)mst8F/!aLJDiLmL,5P#8<eM"T6Gj[QdO,o at ATPTqUtKp%puI:Ukqj[+(Q=fUFCe=cqrGbueB.UI(H"/.#!$QELs%^t]RL?J=8=E=0C*DqYP3WZblaR`(#k;&+l,Rk-qC)Qsj$=!HIAPpfe6$2uW\ED)J27%RES;BaV2C)*MooP^Q'Fcn?VZL`,tEJ5]'0_$,pVn>jh=h[c^f8Xn-W#34J9:D^2f0"1CKLiN?1^P@=aW*`:R[kg/H at nXXQ!3G?A)e=mKQiF-9LYHjhi54_r\WX9,pEtWFG'Wp<JUIApo`MN-!;QtTr_t[OS<,ulhWR2j\hDf%G#"fnS7d%<4 at k#qZQebbT95GFH;1$Dpf%5G:13G at J$QG^0OX<]sD\ZLZ-5LAd./C-`h*M]*7dtIW at fmUVkmj00PX2NI.:m=N$"k`W*hQqMl`[qo4Y1="uobOZ26u]"Tql'"@0Zs5;RsR:Bp'@0S]u2*N=JpOr!qo4ShN#E@^WAm8=R`%RhHam3ISUC)omk#Np%"S4`&Jp2t&G\<D1@/naWJ&nsm>#i*O3-c27%?3^6#C\nqk,!'H at U>52HSk1&<.9U@)g!NhnYQPd1EpD#9im%;]SjC<7e/+FGWYiE_*R_(Z/t_h4R-87B\bjd_/. at .#(nr4*H&oMqtAo4n%T(/3/-U</XQ5P(+o+CWgNju=*i3BN2DSFS.d<8]XD%=;[hYOs+TYm[",F*IN_3)\biQoaBZRjV=Ji1PcnXI%llE'^,?iUe"<#\pd#9+_H%gY]R9MfKu+bKagS0Hr=(9igrbc0dckH6PH9=Rq]qXB&LPh16u,>oLKc1a`;qN/TVV7d.[.A!/h$=m\j>;KQeW[?j1W__!SqW=r/oqkTV_i:)L_Mo7kVVW8V6oJa*XGD#fT]\6ia!BM/uh]\T%u5(8es?*IFg?$JjC5)591+ at T'2i!c*<(!GUV6HS!Qac!s*J?2nb-k- at 9k#a[W?mBM=j3Ph^=!7FN/Irf#=[VVLpUU08nhmel6k$>X<G7gSSE74!H5$=f-eD#dTH[Uc"Xt+iD=Pq5g8>dGGMeGMS?P^8j\?@<,*/^I6]6rBC^n]4RUBm_g9_SGJX/ioM[![PVV.F^AW'>.uE(@]#"hUpdepi=9.qdZX#i^1t$DUjV/pg)[B[fZo(EgQ/a[DaklGGM(=q8au./aPPgfCN~>
+Gau`XgMYb,(4FM1poQ!,ZB at c7?3Z3!\)S[!e&E8!dI]_EPXGbbPU"B=ah`B&:aBUh9&`9&)@VK)Z*Uk4i`_6VO](>BUW)N^O!A<2F;,>JbX0oIrf"c:rOMp,9P9DVT9&+g%b'n&+@*3X6@;*Q*/sO-J>TRYA]7*6[^tW)..h.<*$CTe\-*CT7NU]<?@@g'nbi]B(=SAq,7CEkkWa8,eW</=7E!6QeTUZRGeRG=*P6N6hp1)(^-dBsI!;*Y5MM6%hYqouHLpslSWB:%rqgO)W<IsgfFGITKR2hk<mQU-_"R&&kE*t66''ao+6&Rl#8%3]?6+Vbc_&J)VqIab`a6.2n&^UnD3Ff'c&6%p-@*(HUK?M7-I<.?j$kO76m9DW<34X2+75+&b%P[Scr&79=ibJ"'LMA<;^a(0]RR'3Dr4RcO[rHh"F4bT=P.lg6B'^>;6/g/fB$#InUbp/&Abpr8V_DWg73DREY at 5;9iV^%&4X=**^Z\;fjj@'l8`]GXGd!2HeOfXPnh*j04j[:QIY;pOtD&'f3c.1od\-lhhb='Fp8e/:"V5)gh\pq.Xh25jIsY]0?nYlp0CjQ`YP>=5[4g'%Zq,ujZYQKNWV?+FY::ifmZI6PPqA16VNY-.&>Nr7@?9S0=&@.-eMu(A=-XE,MYO5b0`mjihDNfEh:jA<3PpC/PWVI.#^5JTH3Wu6E2cZ_p&JP&VS\e$BN[0ANl(2(jUo,oD8=CUr?2\>b#-"7MR]n0s"Jb&`X3$UE<Q^cCofc,</ABP6/4Lr1LaFIIRCB,Wj,&48>ihIb82Q/d9(P[BVc(fP1]A>45*Qs/53)k*IjJX3I]f%"91kf^&b_AmkkpD[351bL;@WMYl/an`AT]miA>hhXT?<rU'=DB)1t3ESd/oKjDo1\Q^W3$F:/WD$ebn at LhC?-CL8EOKi;HR$h?>4"C,+;EepF*AA3J+I]18ha#60iM4-hCFg.qoh8ff%$*36`fFEd2Klc=S1ee6ordiL;XDaRc)re]'t1 at Ib^D:+G_C']V(%oZ60UDm`&*J'[hNkC@?ZY at mHFtEi)k^$U<&s+mdaWt=dY,$NF)/O!>YI8186!PrP/-;pnBR6+1D-jCc3MciAELlNn$1HCU>=F`l_!71PfQ5#_a]7.Dq!V<#TU5H<V$$.[W97H"!J!hJkYq]]BDg:4%3\&IrXn1(%6R5mgt5:j.fJa#1_8]Z_Y at JiErL;n7cD305ecg/S1t`goD3RoI^ISit3`mlAS1'VKI3KnkZ^PoJ-4X$H@:iqenmlK#Su`d/seiXm50"6s!e#=7;aQ>dJC^l#6s5&?+P;Q)kE,)o^kJ;Lf+3]I72NB>FhDs%oE-H'. at KEWX(KFUpNMEZo+/O[`Q`.0^L1!Y^Ur?bOD&c$PD15iuc7Vb;!#6g)Mc51+d\tlOKpi^]W>pG+:P>FUU8etnBL-c?8EXFOsRaN=$T!#D:Z;Co at dM83FpY^&O,ma\7+,kKVlJ#3m@)a?hciE=O?nBP[i!CbC!EN_Tm/GY1o;#9-#<rnkp#NH`n_=@U]/rt*YZb?_q)9K0oW+[-fXE?U'js$L&HES0#dDI[l)mXe0.I]mfA%P7A`.RgFW_:OPIb#SXA1EuPOCMXHC-Dn5r>5qM@@?u8A_].U'WAdYDF'hcpA?\2I6thg=la9Y:u[^,,epl1b]^-oWrq/YR_+6lmmn4PZM9U6BJ6Y?Gqi$hMkCff\u/U[(c;.r^50u_#b/a6&2D665RPD$6l0hLKM^.I/J[C!=*n'aE_+ at Ug3?Qa^b#?@QGj0lXD70,VYfV%39cec?Tm]d`H*fSk.Vhc&l.q%&^c`PKmLW[+^jl$BA_Uqqoj[/BD!%EK/cZW,G?aQ9cL,J+Hp0L3#%0I3[NAJ6GXi7glKp\Q4(6bubgWb&Q,sbCO.Sc>e30V'`?im#j>o3',u]HGkGtL6aMChWOqWH_'KkNdkZkZf37uU!5$mK'?2,0OZ at irflL@U]`;nG835r!LIZ0LHKp0VUVW+6#bc7loO\ko8Y`3+2DRph6VU&IUQ0gC4%hg(;lWP3$6t&eJ4hub;[B[,8G$5"E1:q1N)2sokfVWiU1r_b6JjSiVIUn-L3-5'cg.43pRd/Dt_q5@!q2q-OAEHhtcj.oZPkFEGktU&Y77)0;k,-r6T8Nija%Q*3Yhd<U"<>=D_u63"'[%?99k?lgtRhpL3fXPGXksc&dV=/MILS%tIQfT')W^p9<bafcFc#^Y>@*!3Yq.YrtcDb4b>A;7<BoZ.OEb at 2>`<bDKCC'!t$;Z+;X!\\.EU^V7SA/E8#DL*a5<WF70[g0k_9U[K&(Mq`YLg:8g12^8q:BHVF3(^-NDJS1NA"^"g+Jeo)iSQ-^h=p$F<g[2uVH'J-Doj#>I)$qZt"%DA/XuN2JkF at -LWMlEaaKaGc'li9EO8O0&Z#9PUnScs!RMK!.q&1q-p0i76ga[>>_EKNNQs4nu"AN18$nQ5-$DH3l/aD,06m2\0_s$T./Bl4Kq'P&A(=YF\I_<J9q)iOWkC*\[/2aRhit/#%J94nKV=h"S3 at A5V(:A9J%W+*rH.[?FlJqQW$iERDSbJ+GKFDTL*>L-KF!4Z:8T]Joof[gX<.15W^uNH+mmZVu*[a5)&'S^?!#dg;"X!en5N"OLi?&iCm0==XYn2M at NRr_.%3%fB>[Mp/o`??N+bCV)7g#gO>Qg at 6&e/5GD'*`WqB90$oce99pC=l>G!PXV4hq+,-XR+U_`fWTm/\Eo,j`FCQ9E$C>aos/Gh?[EgaHiC5?hCK?JZ^;q2oLeWVbh2s5:RN5mSoXb09 at .12;3B0G5Rh.P0HkU.>H)71$iKXXHZD/_QiL5/D`T=H2HH=g^U?X90jjbm_gkg^:*#pA,6<XMHkc/g`]3Ml8qoR7gYsqK71t at p,7]B5f5WIs$rU]CqG5qaXn]BC"Pci:Q"o39q8r>domJ5M^/6$U]%UU_C$RDS)1Q<jD,el$RpaN]YDM#MctUcASJWPgWG:P[5&,(HMG*"jbPYI8t*I.U+9ic)V1R:'9F4.A*P.huUrS,I!TG[=GjFA.fbfO&CFgRSca!GIXJBF'=[r$D5tpf:2_k:qCp("pjb>4tX*?.E%8u)/W^+U[)d`bKt.,Ue8*Q2bO*DfD@&24$N9me]W$@f1"LX-bn#p#f9%j[kIErR2H\FidS?+TLan9D.B6M0-;p6lWo]$$DRMneU'0+G,)"MR=\T"4&eFCkQpr&AsKXldi/Kb4K7%QAbn`?gr"\dKCYDd/&uZ;2Z/M_!['`/NhedMZq\[@-FJLn#[K2REXLNbA=M!W#rts,aejR90"@+:(+:5Z<0$/nUT5Q/9^HtB'!Prcok=m#4'7B;S^WkfcKld::4(fta*Z]b,;q`-0k>Q-^=(+T9stgi6iA:HhhHaIU<2jk3SggDbSEcu2BVs9,fK3bkp$d]Lb0D9n8KXcq3@!#HOa=@\35[,`OuG2O:i2J(\^[hptt6W/fO]s)$d>J&]/YS'b^"l3r=7.D,3,AniU2Z7JZr3@$RDA][dh6:(>)GDh371S*@]+=j7<~>
 endstream
 endobj
 111 0 obj
@@ -798,10 +807,10 @@
 >>
 endobj
 112 0 obj
-<< /Length 2186 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 2872 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=-=``=U&:W67i8hlJ1kl>+:8cRGAL1%t[2hp)9 at X8NJh2'jP2R!Wmc;-,_/hno;XA"m+X$-&4hf`@0'B#$pQe9kU20An8CnnP<7Be:eL2*#Nm7oQ[/!JoHT^3T;sfNH at Xl%N[bL"##TqbProEP-/NT)5U;sWHqWO7s9),Dla%<Z!X#r\dfuD at l<Pl()#ugaX3r at ajk%GX7:UP\Zq'W\AL[M#Q;`?fCIp7ZF/58bY[-"qHFU5COqZc"RT&'7"^MDK$9k?@,Z+IBdK^X/QM-Cb6Z<_=.2J;LQXm5O1Z<+OS\8Rf^P3$1<9sH^2&D8E"?Z$<b)8qi*%)Vf&N+#PP:l3\gA)O2p(kt=]8)ed6dsRY0G]lasfBJI4CIjD=UuN at 4jK\:JZjg`i(&AlFrLPl4>cr#2+AOF)e+r4^811PLs++5Df7ss7Se,Gd`3D._0j'sVn?]/A>e;)+)-tMq$b8n.%$,Y_/JXf29eYX-d)"9 at qqg2c9Vtlun'KXCn"Nst(k929pTQUB]tZ2f-@]:U/]Q#?(8l**+$O#gI%=d$Ri^t;lhTO>*U!^_c7"skS:2#AaJ[rS9XC-YW*(#:C-)0<JT($/$2RZH!Q'k"B$"I>SL^[h4+)rng.saP7<aRh0'l5.nO)<-ebgtZ&^.6.I at a3r.N],H3]:uWW@"4l>Yd!7Mt+p\rort6XbuH([)]ZFQt<nJA(;Ttf2mhLlOXp&s%8>teE2(Knd^,^2"dhb$;e_Db%Nu%pC*/9QK](ujtXq8m:m8f+^[Q.T790E>a at D7)Q<kX4AOc_c"ME0,Ff+jTG>^g_IGgpR\jCBrU&1NQ`=`]B147`[rB=-e"RPh(u[Uoi(2;%Z9StT,VF50J23%&PM(T]ca4fF,[R9^'=4p"!6XTFFd*'Q_0WkWnoh;.^Z0r6+nk0cEe:lbFQpiuN;G=#T8(N>Q7dGN(a:O/Np9`8-V9PX/#`r$ao!1*cI at TQS]24'5t"q,aZ%Uj`=lK5EO:B2;Da8b]DV'4W`D^pbcnd3Wb/!*p/Qtt8q/mpQmJC6b;gJ;_ZeI""]A;hRq\a+'M-AO_XrBl.<VfTKrIK\[jmTX/rl*r7DXKPmlj=XD98SG[oo844\RDp#r5]7-UN.,D$`Hc>a$k:!m^CYobdaH_fXWh:#TtdU"]"j:Ori>Weg]OH\3`8VRfr at aK)aE9)(MGlNY.Cg3qL$S?,.C,5G6#(X]8D#bg7^_\TMmafa;5gAlolY%?To$dnFphZ<r+RbY(u;riD>?B"oJD$hood;NDbrCiD7h at ZqH#hS at 1]nf"(S7Odh7.%f'1.BKN2]W at n0(*p_bC<lhrck1dj+U7PF248FU_T%ab-P5*3 at r<5ULNi7^L+/=q>mi^J+!g(#d[cFea5*$Lm-GTF\p>H=hVEA(t9[7LKT/D'?rlA.HY%Wi+`>@]89Re/?JDn=sK_&OSLCk6R+0VW(Fd5&leu=1S%R["3"l;J9JFFn73Oc]2Yn1R`?/Geb('2TY9Y++imI-DSD2.!rNV]ie72C'W/r$;E]cF52YAZRI"M`<!G&Cec<<)%!KHCP*cXGo/-+p/!0k3K(DXh*1IP\QDM at 1cd<o at 3m;OnQLIs6 at j[/h:.Ika,O-VMW47gY$`#e]5.,sV9GN6-?%VC)Q1<T0GTDL\10X3SjXYX_VOK/5L7@!\rX_>YS.()JE4iW+MfGu at l&6%rT7D2/[gVLuf4]<D at nDra6Oe;1QA!1Hl^+WPg.c(k%+YH.V")o8":B[M9BEc#_ti9$2,%J$W!!=;=??*M0Q9X3WlKZ8mpcj44*6N&*Y-!/1)U at BlEHS*TCLn'Z%:NGnl\=?/gU@!*[G!:@I0XD$G:_7d'`Tf0_Oo at a.N,^(Drs>j>n*d.BX^iZmJ)'^ESi>CPrd-ig`uiH;fLmD&pV\;hUiOV]BYdAK=%%=$6eE0&3aC.>i<7TVe#m&\Z(,#iV'O+U at 9jkLM>W*M9\e1LLV.H9ejaW)\I[2+3b"/]O%@?Aj5BBVN'IY%X[Z"*2[r8)=sg]jZ4GN$IB2HlqrZ4X,09R-*mk<1FO#@$sBaFE,7(Li"Ys&G/);qYIMt8_]g5#gXtD=:Pd.ji$%=KWQ@%=-?ELS<X'?@7#4Sr$f?'!/11![JB[F?q`[]Gns5ebrbNhqq7=^D7iO,X!@`S6Rf620qAsIejdkU!cqO?rr^Ll#-n~>
+GauHND3*F0')oV[6=5Ts5aE!N;(m\lJZcUFlo++9Yo!Ac'S.ga[P##Ll6QhqYJ8K^:aRbHg+*4MYr$bdcZ!JecL>^ooCa366=O0"m>YuMDQ%-a+)1s34S*DZ6I)[_dJE7\cZt]8YH;#ag[OK.R>q7HE*iP7If-_jq,`,8>sdG-^D9t at MR^n$[u]ReU,njN]G-k*;2!dQl0]\6hsinQUU\6PLc!C8+>uDDiR`'2BTf2_PNR?,n$,Q/5i[cuggc-t;BW!^lkl!Z*$#OPnaT*(^:&L"<faO(]b(22bufQ9R-$<QNl\5I1eKL-,EUP)]./jg4L/3b!\<)VN^mHK2.K\k*kC(h=gd![A/hCahbe`B+Ifl!$Q]#/1Z./WQ1(fj(bG._%@CrB)!EDC at .HcPV*hgH,;,I5*2T-kX5WW"G^_%c%5)cf379[$3-U2I9JB4eG#CC%r(VMeOdCnQLf2OY`9HBLAH\1KUS#@IrsqCsr,ja'(?Mk6.Ji=kLpTPPUmn?5^X9]R;WO<]F^3t#Z`U<sg?b=C2RnWJG=6n<j(K*ka;DH9X#r6HS8$@04=LS39p;B*bCueoAbuVi672P+'\/m^ML&U4\M3X=ji;,FA0,`;M:MUoE!g+km%1eq4&)a*.Dt0Tae+M&,p&CF6Wu75m7j0c[4P.BjhUBl+7`14:%1\Un#Lp122NZC06A6R:_NWm7%Jcn58U',mQV2*M<djL0@!!8d!GCCb*-Luo8")l(SjH*^bd2ZAquAY&4OZ*OQ2-[nY6n(_;u`3bdh2:cb5LnN6tBOon_40]TB%Kb)lP>ZcJ9p^;J=</4\8c;Af-fh&mE+GE5d/3Jl*D%g933e(%uK)<a_e$fONt3&E)nK;)osE_5J>nn?1I_aC'k6e4P+1]u"nA0Wp^+si*#bM7%Sle7_q-E7,:7:q5nnRm&6FUW8Xl3he[0QgoX;"$bZQZ:#_Tjd`gn\)jGfa0<:O?o/2N5LZ.k_H9]XMML"`e&NZ]jNs at r49pa"sd,<Q"OgfY,jh+T:ar[k\G9EP\6N!dc`)uL>V<@gU/bYbKUe(,^YieP]E'P,R*%k)<fN0)hgk/3,VP?>=O>F+Q!q6(`^ii*DV?2m;Y'HJ]+m$.m<mC^BhHDJ,h?$*i2;p2MTKHrT`O+`nB*d*H3p[%38>:'t83lD/Xm6\Q^4;#1";\/J)U'#brNDPiTO`0Lhe]i18rZd?tY7&6bU at 8YqQC&D^O.<m5Q/rJcaqR+lE;JhaV[k+55hXbgUn11m</]aHkgDIUn5oYq?,Z5Y8mcqX1$O4IKP)"pmIeQeZ8dU"=Zb,2LNin=82TSSe"D2\LBef#CpBmKUSEq/Xl,8P5b%<sa>o5<K at p:(E9-TeK)d_X0_Ol5Y,E#f>DL6QL>UQmXn!J/:;'COFY1c0Ng=WSFJ?["a"BYZMZ0OW:a)3F?X0a/?m8I>ob%W7R8L0Hi*/B>j-,ZT%(ViWoPBg]'f/>ZFrp2d*1n5"<K9'N8*3]">Wm+Mm7fZG'K&p>pO5CIOp:2.fkE7U6jd+hluk%s[\\W*,*6T/kW7a/Y7N/_&Ch:dF&8n4S*E7R>mnG[.lH1@)Y#nUJk<*D9a/ei:%af4o at e)S\N_0 at i&a.IKRCn!Bg9H#Vf+uUN=MkZ*Dj;K?chCK7t<tkE"UP9&Ta#BBL:2ZX1$Yb#UBnrkc?R\ur)%buP8;If"Ig5ub5 at 9"cI!eOQ"$2F/!?9o</#Wmr9sjPCZXbat:Z`:r,K'Id^[#]>3<M;`,BR$=F!3B`jT%85#6X[AHlX5o&^Pu%,A'3\7Amc2=Qe[H_kmO+-%c5F14bGor8X6i9e0jALUeZIQ`e-q1i.KR=ZRC=T`j_\:emAYP=EcHLguLBQng*Qa4;s/!,%-r^t$>#"kYQ7 at 4=(%PZH([MsV*?:'u\$>Jm:-S0`e"(M^mSN"bt,el=?)fK9lRNg`=@^7h8mbWXl('+&WO#4.`P%Vu^cW3gRL>l9+3d^39AS<cg/3(jH#0%>[t)%J#r""oS#!>Q,fr/OXEB?)FNOQdm#\R<Jh;>a8tfs,O&fI7ZaC#ocu\021GO\`&-(^tr$CbR!9=bsH/UVhL[=!`!j?jMCJ8LnDgM=Iia;3fnZ25-kr_9FaPE)3NY\%4Go/e[jC5D/?W:-FAm<J2hSg2dOT]co!.euPqo%YsW>0\!8UThW2kqL>k)>MqOJZ`#Gl67>scYp^UdD8.U&7X7%\Q)EQuZVCT4a,/`#NMV8FNp7XS+&Mnhc#@m5-n8NArf9\4W*'"8JP0*>RIf2'\(h*VI;Rb0/i4eIgNCiaiLj&XD&c*;Vc&/IY+q"Ap/iCJ:3U/'m<VEjgWm#E*RfVABq2$([?V/X%"Hrs0\,g$>I(5K\PBi)\kPba7'^-)Vc=Y.3[r8Ne4!pZgu4+Tni1B(<JN=ic!3^d">6'OF0)5#"0VE+\U2d`#Ms.NG"FsV;OZn[+Q/0)7VBi7c'9=)"Z#I]o&armQ*$".U$cM#K;9,T519b$]#@AeD[r\BD$9GhmJkDTV<3)fei6:h*e1\(k_!4]16^DiM_75J*XPstS*$Q\502_l0YutbI:W*g('-qNeBD]/$W&BngTTOk:3b6[JrS9I83:%ac^#cXEm`aNBV.C%ZG?&0V8AB?JAO[UWKC5(,c=?GeQjI,Z2B!4mOI1K-stU%oI?&79"m3&4bnUPVV"CG6s\ajj*,O!jTU)[ou>Oa:h>t)B!lO=.g%=fCMoBVdT:.)^qF*S&e$GF#:X'A=i'f+Z'"$N7"nI`hdlZiLQ"Z!aC1Lp?JgGo)?30905c8l;Sbg&4i?E1Oo-$o_AC3E2]p2m=PeE$/g]f^hf'aY$ODCPT9%/.f6gdW$Ch7OeNrJA.u,B5k%4jhir00lP3SD~>
 endstream
 endobj
 113 0 obj
@@ -810,150 +819,477 @@
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
 /Contents 112 0 R
+/Annots 114 0 R
 >>
 endobj
 114 0 obj
-<< /Length 1681 /Filter [ /ASCII85Decode /FlateDecode ]
+[
+115 0 R
+]
+endobj
+115 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 311.448 622.8 436.926 613.8 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.0/en/charset-server.html)
+/S /URI >>
+/H /I
+>>
+endobj
+116 0 obj
+<< /Length 3070 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gb!#\99Z,/&AJ$C:qkR\2(PF,!g6K*CN_L<8bQS";K33!Q6.Oa%Z$PC^JFG:+>?PCRAk40=R,n^;g7O at moP^Ne`?1UM&["T`hE8&BnE=B9Z&apI'W,1*?bQ7Ak"&)c/dX?rG&Y5kSas/[+\;L=H*b'riq$B#nOR^+j:s:FZ05d0,>)<4)UqAk@#PfM6+`60CmCTMC:se_r&A-*c/N_*^o",MIC\[i1J$SEuTdB,GN"NqlHD9nL6HI6ZJ8ZeI,&8L:bM&JZ#St'u$jV@(V6+Ul.KD*=3US`/>JMd7q8W&/\Osl[n?="QXmVc53bF-ep4l*^/1_Y5F^mV=W"^ig=rg-a?L6bA_-]+hBJXS$sqZX1PI&_eeCI(g%dnf(Ym,LTaEQhn8Oapo'h'a7-JX2$Rdq&=OXG'UENK&jiTh?G,J9-?5O?Sm<eeb)gCe632.%oOH<c4hn#)#iR01:6/m(-;e.&KHY0t;a<&QpfDS`g+c'g4G?B`\>U9CTjL%V,RW\\%;mEufWm1KS7B)45sX76^)%_#pM=Ur[UHDh+P-:@I!aA/P:kDeEYu*]T%uR]np4/&Zfg4H:S45mcdZ]ce:dH-d;2rW'WBQoO2A4ESB[c"1em`fjHn+u at Op(%on.]BGEC"'pVNX1Q^,6ib-OKI3IFEZf.&*/7,`+hZ,`dKH2[6-hD2JN1P`<U+th:Yo;tCd!7c$0bYCH\Il8VU#Y%YQs3hCl'\Q/Y0-`6f[#ec,VZ:ese%)"Me!e_L]ue&0JoH!q,?4B'`CJ+eAhJY7"XI<f.&(;SS>OF5'.`[`'PYT>#5NufK2[kUj'p[)`l5:Wr3WXt??!"OiXKAbTojnLX3`sfh..s76Wf#N0R/_33EDpFPZ_/['I:4(8H9*T.TJm`AuSKU_h\dlKbc?ddr*&$)@8YPG"_'kE>F,TE3ID["qRi%NA/P]9OJY*IL]^NECGr_ at k8J9\2OB'o6kQ;Mm3g"A&rDRnGG7F=A:E70TpF.B:196JXtIBl&`?@+"A8;S#0=*e=e!T$4K_ at fL]r4WNMk7<.A8@!"+W$O!T`]N9mF>I_r%L0M4?W,G\bCWg4Wsb"$(0`ncAi30kOjh/90JChVqagJ*S^%\FMPpO[.CjW\/1fO at +sf=:B3&/B?"D)(JjA]GlT-8p`f3X%V8VKYEJ\,c/YRX>SlTVB!N7Npr\10'V*3E@'\eD"e`@Xjb[RUp)g\/_qkcl6t4&%Lc7qT4Nf!g%[\_S7Qqj7/WR(F-%`$n>4t&2ha%NQJPpoG29NP-Xh0b3)kb8BMu_Vcap>Vpt>kSf&4XA])eB"ne^SL5dHo`';EUJ_:m'CL&\B%gfK`0ntU<5lG2J[-2fGA5gA(5#SW.TG\4-#^/2oLdmPfb.dCVl!-TbijA#S/`bc)__s>#2I+;T@\B.DFe&!R,Cjl8ArSE[Ph7"[GqdmGja.3/C7_=^Yt_#<)_qol5'`i90\UVipX/<dNfD(#0r2IKd0ZsH`/1[r+.uasM_ja*IP:0CS[2OM;,(C9Q/Ec2iQ?R.rYrGmo];ESlu'8[Me1@*=.3sABLA4F=:4g&ES%/t9"KjfZkR%'SFMT8U7I.kRh\@KUaEC+[\:Tj:s6QT\5XL8F*1;6`#7'\"Z%/Y>&k_"?C0PuWBGV7K)jD6(bIr;]mpDMk_,Ba]9"4;GT]aukoU]^'F4~>
+GauHND,]K'(B2U_aLB$W79G`1Veg9Bi81<WD+SF(cI1B#YlGT\9)V(7rUi=6j7p]Xo<QiY0E\b"8a#b,cV\^;5!$<]H6+eej/4,spO#AU#5lo at CVlHKE8k$52kK_,*To*8Uj's4"!/Ee;ctAkF"a/SlS-D4jt8u=V'7S@,U4\Mhg=C!FYDLWLf*V`-.?JWBQSlEMV1\#B['d?f5m at o^2%U&Z=n-Q7>>Ef"E-gM7C=K\a)W4Icb<FRb9=5.o78V(-;n$llo0i-%tDO@#AEEORD_NB1A(-NFP5G4*+&qhaFGKi.8hGK85<J[Ni$04a[M@]13kiFb/_o1:ujX5`$?&-%%0'eF-2UqPD9bYRn0=_8=#,YNu+f,cs;mNTW$OY+N<!)6`ckr3eZ&'<kLF!'!uHlFr<*&4&!N4(VND?`uI;R$QLr8bHHhr/t!c=F_Fddp<r-\>&it5`:R$Z?h.-tH+66:3^%-Tfi]`[6nE!/b<I,(86.!H,,`]T)=7<CO9VC,YHfsJ3SZAanrcf/[7Dk\Yc1:>h%>.K)#]&<-55X58D>b'5HV4Nl\N%n5n8;Lcte\/+NK+PZR?Sta39B6St]jgSl"ma&7&p.;!'FY"GL]W[F@;B]+ at m_2fXq;oGoiCk]XDR.Bu$?[IJ[B"OBX&SSu$VWi)d$0p"sN-9328VK")n%DoWQ0(9 at k0@FG^n\S-)&#V_(Z51>0f-C"W:mo7a^r9e-dQDi5-=AhBK+eP'KRL<d/X?Tr(Nr\WC&`.H("/ttWJ16l#*jHJa2e`fMCV.?]##ka[OMnZK,nY\5\:ntbRc(F.h&(COiUHLDcmS,67 at lFc@Y:3r[5ZOerF>;/C`D-U;X0NXno*DMR+h:i?+.]#58u4\%'=,2-Hkd6ej1b/l?,dN0_I[AcdT^-Y:8,(o7cf1.?<n1RP9cPXRW!O2EBhe*+&=,t0sZ<WWn at cP7^)E)-qY,3pX9Tn#7m7LAbnP$TT%!R>&fbA!*'Y#Gt0ZUe^Jcr55?U^\nTTgp&'HE:MeiHo(fTlq=8hdqRu$Ko at 8eNI6X,*t(A&MFVJ?E-c=VOsYNaTHAeG!Qi96q*Vb(i;Yd_ITLED8i!einpGS_n"QRZj5@;FNSpK<'*g+Ed9DRe*T'%389t)E7[gaVhlM<)^6=E5fOk*K.JOMaqu=qq\t)(#'!=<Q"7'H at j]Fl=u-o+8/,PVA=`u#MZP\ij!][gRE*'/DBPF%S#r+3M_&<OSr^tjldF.J&;LH25`almWW\A[Z<_ at snmJ`RXPIGQ[CjqmPT37.PL(td8c/2:jhjYaoIn6TIi]JX8alh6eQ;c2EZk[6V?c_NDn3k:[0["/R#9b\d4\!+j/$mk)(:bJFu*DC@$oJN&97Uh"LO/bJ89Yc"/fqeW!@o6>#O\:rH+2#)3:*Q1$Frm^NoJ at d*e5"d(ff%n=ZkOPm"!\d?7q>k$U#JD[B=l#e:kjW7c?fW\)/\@l`(G5H_EC<mFZT1Pm8RX`1l`!n#ddrF^^uL08j7r\DOLa+MaSR?h.CFI?+p[W,]u<&;<AkcR:fAC[b"qcE`k$V(lC`%AH%ro6*QWSae#H/&@<diT(g/k;].Gn>TMr[ki8Bk#aO^2)RFr,hqmo(foK-Bs+VTJbXe>:K+4]Oh/Eao)USd`qSXW-XLnqMYVKj7AD'MUPE4:k?)=^Ur*4<@3O8$Mt>6jkiZ^"2`DCs*o>4^oHad*?brg!+g(H9HP!4%G_X:,1!Yu%f[:74)?6+V/k9RdII9&llL?kiiEdY.ZKOM`A?502:p at O/^(U_;JJ1/DBLjbb=.Ra0p6nujT!::>\aAl+L\T`%^gAWm/X+<dTHp:<S"S`"W&l%a+4 at scH>af6OP939mr*!ID`g/^;568nR9B#q_%QPleAj4m/,<(#H$r"rVf7+#. at 6"Vfq`_q\5HR=h`qk-M7hkLTUG at ht)5>i)?u[46ZRMl[&`+?25;>n%qiTkGE/-I=%VE#a at O@X?pBd<em>*p/X=K%G6ZJIV;28-1Z8]s$C8mD-3c>\(TrSe-'2.:hVHfp'0l#XTk9r#e7L9G^t+Y34aJ3B*%5K`U(((=5\]PKnBS:Gp)UFaEc at E&3`b-F>4<c=-_+scO@,1;0&McIPm.PYfdP$.5'S\`rSY8GAicKNdsE\1'B<5M)5UK&//e\";&gc`,TR!U>n7dV<6K&eW?"YK9A5QZ/,@,apd3QL>8c84N\8k!GnU5A),M?,%^'c$)[%+S-,2ef)mdCWA`tKS&3I]"Y7'ZIRWG$4_A(;'XrBc"Xr)Qb7;sVbhSVX-maK`GSmH>@X#epC3Pe/H#WiEA1"7of8Po6)kOB!-msWO$6"\<76)5E$$TI)i3sM-l>`q/l+Sdl.5"7nkcSg_!'_rCo7o5\@0B[NA"]r*mJH-3.:G&1$J0-V6QF at YeVc-ai/I&d\r&j#8oqIX)n!i1oJoI:^QuUQN35^G]L*Ft;@Ut-U<?(kjmrI6C05o$1L4UI7R;Pnca1BsJpN-Lk7ZBf[lL?\Y'!?5O-6&roi3IMU3qM,6Hib2TR>gY%`L[ca'`*c]8C0c9r7j!J3"1F%J-'f[-TjG3rPSk[j[Yd,SG5"h[Opj:*4A\b1I\1r%S03F*S[^>QLNiq8Ir-JHnM8hm"LXJaL`2C>"9h/LoS;ZmqIb@<C=>2EQ<t]jm$LW3cAGJ%dcrnZ1Am;AEV3T;]j;1o-BF2s9LO;B*jk2bJ^J5C/CXQ^.f\__P&8%9e+jc1SJ6!N`Begh-N;/0,b6qd*(9hJ`&G0!T6fa,.]7]074l?&nCBIR4'idNDI$";AQ<o%'>[F\t56O_paR<KA+#ON._s[a\%R0nT+c&XmM._];Ei2Aqq"j`DS#W&-o9P3Nihl8C$V3c+9QO>>56d:bLR"tD/7rYi4,/"J5UcbY;.?lqY]WD0M9QK:E4WejMaJ%\93V.(2F'1J_fi;@XK5M,&r]K34Nj+OX5LjPf-f_*(uVAIe]e;lSTr#@epQF?>WDKuh2"N"9o'%icZ,a"4P^0;mJI&cu#qBXYsQuO[%dqq:tC\=X,mh<e7lrq#Z%@hA&-JU#gYgD>jA'IVq"3 at Si-i~>
 endstream
 endobj
-115 0 obj
+117 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 114 0 R
+/Contents 116 0 R
+/Annots 118 0 R
 >>
 endobj
-116 0 obj
-<< /Length 2575 /Filter [ /ASCII85Decode /FlateDecode ]
+118 0 obj
+[
+119 0 R
+120 0 R
+121 0 R
+122 0 R
+]
+endobj
+119 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 395.49 653.4 405.489 644.4 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://developer.java.sun.com/developer/bugParade/bugs/4273544.html)
+/S /URI >>
+/H /I
+>>
+endobj
+120 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 643.5 347.979 634.5 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://developer.java.sun.com/developer/bugParade/bugs/4273544.html)
+/S /URI >>
+/H /I
+>>
+endobj
+121 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 531.696 624.6 567.444 615.6 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.0/en/secure-connections.html)
+/S /URI >>
+/H /I
+>>
+endobj
+122 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 614.7 160.239 605.7 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.0/en/secure-connections.html)
+/S /URI >>
+/H /I
+>>
+endobj
+123 0 obj
+<< /Length 3873 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GatU5=`<(R&:XAW&GhOmL`)&FXqlFV*%+:Ge(u>Ul6;<9Y#h`+g/*qk7a_7mU!B?t>'h:)GU\brHuj?#CiT+.7;+\>Z)u"9aL5p%D&-%EmkUNHQ6i`9,KGBPQ=Dp[mIP0e*8ar$XZmnb+!Rj<nS+1TQ+2'`n`Z,.QaM&I6sW4QC7miFPeuJ>`1=l?bVb=KYnlom%T5"G&6C)QaMYu)N"X-(Enj*:^mcCZGQ_+g;ci at 8=KTNO=;PO+Y*PLNO^;Yp]r4t#JdrUWml8IqEKuiFle,It$_;XCl%FG)(?l[$j6rJ3*Z#-G5;/,`\I^Q3rf\.&7!U(&['<`-6g5L4QS)nn0<7EK?5m%c,,lmB,"5MT]Yk?'3^_>]U+1X5!+H$q'4<sm!nr6IqBK0^>0Hbn,2naUpR.sj.^%7uC4+U</B7umSrAT%Xu:D[XuDK%^fQ1/+'hB>/UF<S>o\8WZ:Wuj*uM9_p%$14TotXe`-3dC5&16$#\DniQ5%p3dDr"pD'fRJ.`1`&$!J>U+W\1'P8b-LH'AttG<fCK74+`KjRhdCb>jds%D$`/INO'ZkLI-fa=i$!o<8>65bj(SP:&/5+<(/cgsbliTNn-Xr7f<X(9KMP/i,WSgn at q&G;+&Z6V?-QY>DCjOhfrCT'\"EI&*NaJ\Y+`fg#L\8dR&o/_:A$837+2;s+3R.JEjj-*#p[&`o=4bD>3H!k=X"BL'X4OLp0lFOkrq9f++XkS1YJ?"Bp6GmbePj#63YY,W4Fk77N[\(E<$0\pM0*r7-mHl&#"Wf]H"W9n$ZUXncp4G7!f^JFG$b)C-*l'!=;71?`pa_PFAHF#%GL at 2@G<oY[S'Ck,X1+KQ<2i5Sng?4.c]:<rhBhCb$=@ARFOp<9h\-6Kc]Umr1Olk1.e^#`$m<A&$3OgtagnB4QjjoYT8\d:l=P$Om`:K<PIpZ8!PdQ"/AWL9K\MoqB4-6CH5)kf(qG9=4qeF at u)`U!Xi98*&I&>tHKH+WtFBdY\qd;([o6C+Gnc%:DG:.%$Dbo7B%,u!W at K7TLS\RgZea%SsnM;e*\.'=oT]uQ1g?!Tc"?[@@!a"r.Lon&*`s7RU1Yi0$&RI\OFu$4lIX%^`Dj\EXpGt>">KR9S-^"emH=Q(h[JJdg4T]KgC\c=*b$p@!Wi1f>j6U<=(*Db>#CNOOK\MkP*T1f`Wc at Kmk-5V9OeX^d7'@8KPu^H&\?TI6`[>-A-Gf>PZUDkpS.h;s2n<m('/Ou^F8k\=Z at g_0`^QG,/[AkB(=7mH=F`CCYI[+kN@&J&bDh>,AK&m=k1Ico?TRuM'1+-iFV2qM\9t^Zn%WqRNp>N/Z]HDc>XkuC4Cc%0XMmTiIdp]L[glJjHZ4D&Wc7gsoOMC=L;V#ZDk<OR+FTcPT#&2)GI_ZR!F5:S)UYi''+M@:;JkU5Al/_4B at BIulV/)YQ$s35(@j-bhFo^.%ac$*mG(3\DL9`Xl[m?VC9o*(H<>)P!0S!&nPA7K7OAn*$O+[9ipa1@??5WB#TT,B'R9E8R4%kOi#]3)n7eh[RO4J!cf/O=@$?5q"0P&bP8T!oVBu`jLP&:T)Mu<O*aA==b)4eVLGKXEmr^+"-&UUr^VW8aM`OXoTq6,Ff6PUKqT=L##Zm[D2/@2:CrI#L-e4(fI_ at 2Q;@QQdUFn7hAW^QCG0Y/C@#pu!7=kaMH+M[b$F*4A]#`&e6F*1[Zs$%PohLiHkT[7pH8WQqal+:\\F2bAFELk!Mg`'+kSf#\(I>"687bq4$OS\a?8rS.*AOZ;cSfOK5dMWa9cRpX?Csq%"qIejn4jW7iq9.hKb?EA6emoM-rHL3_C&l;r#9j at gDMk-;LlO'h5`i"X;d,jpIu&R9_!X,l_W`6EVK2q"bAuA96u[I1 at W5$63)%kQAL^QX6:J2F)a.00^."Jbqr.^E9@/@]G,@:3Jqglf;4=(YsgcVfXQ7r)W`<%c,7^1cT!<*\Lsf!DYGc(E/e9aToQ;PSsGBln#'ANNCMBRcDL(fO0X,$T\_NE-e#*MmZdH"1EAT.DaC/d4emTqF6DiO-U+e at M1!lAmWhT:`-6=#BjM-LSJBnkHlqllj&,T!S$]Pj7X%a#apA0eqJl,P83OJ2^2ZXXWM`_F\BU'n9KGMHbX7pd4;i'rGPZJgQhM0%6RM;Z]lS$9H]$DP-NQ)=Z5gs<@2N+:2W"X(Le62SIhL+39#_0m_i-SR1`fq3LH/nQ7Op;SbNg_6RF\elT`EGgG^oJPp)\f:,d,tJSgnm=SK4mi2?%YFpcY1=`jU46QNYMhNKp5TFo%NkpsT3RKG,Wt)"$cEL#$L%hSf1n8mC5c`H(bG3>'^58SAt#5KF5CNj8lq;r`(^5mOo*XkS6O4_`*nn6P3\=4eo.]_&-'YBG6JNp_5hT]"s'\u(ARin<*<a(9b>5^P:@7WMuQ-r_:*%C>ur3_H`e=r:$1*b##&KNZo-dZ_32K>HUb&,ujVV#6gH_5595l\Er-0A`=LH$]2^g\Z25^-:*l<Wu\@nig.4\p<ld14tJ/AKRDR_c#c>idf^1*XlClH`4MrTRARLE<HouaDf4I[KGu'8:DQa)NL']#O%20-N~>
+Gau0GrGUFK)ZDmLKi)6f[V/d*'(sX>EL5"Z7=3kM%)\bV!l\bf?"GIKjCH*2s8?9W<qO$Skr<lk$P]=/^>C"C7J?4un+EY$-,LUl?gi98fq'1/oN.(4JDN_d0,!Z5c536/cYq6DraN7<Zq%knDb>8G)]2Pt\uD1)Jp5gL%\roAfafEp8K0I6e\=\6Bg5_N=YXu:8oJUIntsQCIK/jbXm<U)!YVtV^,.\Q;c`k4U_?S)V.Zgj?M(H#c%JfLIP>=KaFJob85<Pj((Ld#j^S8/ka0]m#B'8iTmo/90:5&rX>"QNCJN1LIbjT`*7>pL?2+!i?L5-h<ht>5e7db6KV,2?AO]aJ5OKQ#DGN`a:p&J4lR3C.<bp:`a8\_Le)*Q%)H#+sphK6;X_6P"Z#JB\Qh4bKlH?Sj/Xf)!#^*ABg^b?r^$VA'TbR>ild/C@'m*]f4q?DckthLDIZ1'q4NJL;[P`4Vf47*IY/&([W_MLZO^[lWZcYt1r4[Z%8(!X5,)gOfcR*@iQ,Sb\UI]V_IT"t>_H3L/dXmI*`Y9/"+\8XU2ApZ-r0cC8cX&c@#1Ko:(OrpjbR6iPW[IE&S at IRfH$$:;8O><AG%T,l$7I\95;t5AU2Y=_I2eGpKj:m^_bc"pZKB2BgKg-.&3"kDjfL3 at 8e@;;(:_QAN5m%9`/QTFhd\b\G\p-"6MlnnUJWlf4UA#8MDUZ_<&WWKKtU+S'S1m:Qk>R,'c,W"TG5PQ.c7%%\hK,C>^bV\rV/n0ltB+gXBpC$jf\J^OJV2`]'<g%["kLglqrUK\ukAtp2S"60G'@*=T..m*Z.iaUM^DtfM?-%6-7QK+ at SWWmaYTUrH!T[7j:KO0)H.;&kh0gK4Wq>cmQuqPG at +\d5H3fZH7-l8a*6-jkF;Rg+#-_.4a[1/LE(UeY`BHA]K<N\;ir%oXN%]:%]qeX9_6"+k=ZYe9#:P0f.)%6Y9Ud^Nd"h;+ZI[5XninWJKiQX^JC<g6e=OckJr#bRg[Lc2>^N(?;r.<X&@4lc"YLp]PoK\Q[9jD">R:'UuOF;JH806a'0ihHdd5_;;gU^H*hV9<7A6-T[2B2*@g.\#*j(h)`<:A8)p)S\IlOXl:?DUR%(Cb6CEB@<\J:FZio(H27"W8Jld>8t'B<i]BMR]^"D2qEioW":8Xe[- at R&A?p5q7&J+18A$V+HL^a%cJS8iUaK[>bLd@%G?=O`$<(erDu7T4@:>=L6o'R<'RMN3;SsmiE2%s#Ko5eZ<g3c at 1kLBg<jY.4)V;"i8'"Ya.$-n)Uush9$^l)[%X]P#'Vh$Leimgp2<2-/^(bN9g7TK5,:5!G?28<t0ttjp8tl^QrqR(?*(?Q_d3`DEVQSNa@'eqm0o"0GFp]B-[1.\^lt5)J2s\N-N8fcCYkq6YI-^?KiedocogA*B_*uLj/VkH(BdcC_Dc62*>hi:GZ[O[S]UV%MneccqLc>m;KNR@>[!@WiEV8Fr+ufE$r/Ytb=I7df at IF\:5FSMCS@^F5TE\@'S_0d34`@ui%?LKR2=c9YkLlJU0X^)ufD<Pdo3hn:!d%0JTZdJ^"=i at R4<MJ2!7lHd?j`q&OUuHMqTfC^Yn)69W[FA:'(tubf$hF;s$:J172S$qKs_PbA'j0;Cj1+*F$PI*!I5CK8jNE\28Do`F(53^r[K0uW]C511r/W)#^$Ia!b!B74T_=/'>)(CLB6$Im<J)=JsQo&6NDr>IgNC1!&>.8&p+?I=U#j*"BUWX^-b0+H\iUubVn^:(0_MM67$(7!A5mq')m:Prqlo"_\ul;jO0%E;44EHkuRuQWL1n1?W)*N-_;)/M64XbSK&XL26oW$9bosMKs\Y*Z)MEGYUb\('.G=_SdaYR8;H-gh-_3)3SN"q#FL at c.Fti1V:lJUqgE5?'>d>R9a5Rl&i&/S1JUg6j?%m3+d83X7RTSJc^7-0!\dh4BGJ'#JQlPL<,q[2DZC=d4Gh`e**%b&lp*k at +p+AZ=QJiTiag..X3_J6RZF/q(i:_T,,LRR"TptEO#,E0mUk48;"H%",e"kR7\9]>-9@'3*eDPe&W\Her4rXoCU-sq&o8-HUor989@`D>&uWI6hB9B3h]=U?/-6]*!UdSXp;\@5OM[@CUKAAfU6U`\#USN5R+GE%k`#2jG5s&B#A^9_QVEY0 at q\]Z\3(]P>lO[#r8$Jf;/dsaHqAk`(HXEVUJrVfj&E*JW4=6ij+8p0k4C;1l&J5&9f5tF*Mbci#X/InA=j4W".5_!>geD8-j.+P&1djWKu".Od8!o-IO#gXB#8kcVPo\K.^l*ogc</k!.a_RQli>Z+Id"udUCEM!*KX-emOk4!6U,M(!(H80Q9?kJ2cJo&6!Qs`)qh^G)W=)KS7&SUuSU#"Pt&=^g5Fj"GT)A=`ILF\t-XC7]3"5+HReOqrs7!N[l26"@-rR`5P1FWt=Ij7i0lZoWRtR's8X1Vu_d5Tn"(YF=lo^^mteo_Pu@^!NLE[2m3JT:V\hC7T1<, at g2HgMiVB\QNa*S"TH;n,sVjrL>ic9E at 6NFiuAKe(o6!!1M\<*dWiqV77Eg;IJY)IYM0i6W84CTU^;ICr,4b#]?VRb:L,L'?lK#%rMD&_ZRJ!-[XDN';onM)n`QOigEVK/:]#4N%flKXqlJWE28*<:aYMgUk?)XDo\91l-j`9Q/$:m=3`p+l2Jl5u\";hU:Ct8&FO&S<'7Q2#(`]YO@<!B_#h;eaA&ftQYrj^JB,1A:2pT5)5?ETmFBS544t&FTQ2SR16>^A3o9a.$XP?=/lr[rXAV!4j8UG]+h;4rX)<SC7Xd^3<0(ckt\`(\W"\$O(pmp7(-GR)tk,:++EDt\7D'/qXmG[^^/'KU].nZ%HGR1`>dhua1T#NM6g<SH8aumAJ0h#G'r/jsk at e_++AYMWG*W8M at Li?r!-uNbse6N_l%mQnT[FOS_T&,q8m?r70$e)k,DpMO`*.caH9::aG83.QIS$_1qi#RGg/e7M,W?-s'1?-UWm1p\P8>d;j[qV/+=*UDkm2p8q.SC.g96on1s68fr(Oo,LZ(!A8E^<`o:j$5;rg4nE/kqV!g?rJ_Ym('/=5W.;`R\J$C`+EanpY4X49u+Rf4:&-BnI)"E`l*1X7?VDq"$83?DVpoX?K(4*2]dk:]fIUdMg^tDtqilWu*G1VtLQoRppsrbRT&?[cd'kAC8^lIfK2BH[F0_`<0RMF<La"0*e-kgaE7^ZWWFQ[4@\$N%>=^g?QYS8rGad;CSaWO.uTY^i76 at q_h@PQhr].[tdL$>^`i"NRi!NRQP2%h;23P&q#Cf_TH-VouDCG6Mf'+dlZ,lD0;/B0f<8G=^qtb*ktte*V-/6?Hk:=7E_KAo5+H]6mE8NJ%Itn=OG'- at E[MR1,fU!Ee,`Q<7m)%b]\%Wke>hmk`qSdhq*4%9VtQh/W8E6cBK/>k!?5E at Ksi`Y"CBJD5u?,6 at jor"8p3Tm[S7t at 0ZrQEXs.J<A]%XM`+"p'`ec-/WALK"HJ)Z4k+<5XN>@Jf?%!(EOG'OQ4sCDn3"5'LQ\J+I5.-kAPk'qFudUVB6Xj@@*N/jl_/h0Rm3[""`Ys-,ils,g6)PE,W+W`YAr/nefQJaMY8<Hk+qbn at 2ndq7>fm5_udg9k'C2#4-m":>l,!<RE"Y0-I3TX3!5s_*Gn*JSSs3Z["kSsG"s4nG.*em0sUM^qoa)IDRi>!=]Q9db5sC0[nY#a,&8=BZF\6bl&hH>PN.sfD10hZbtG\qB::o7'fJM\3Elg^Na6T*2(KK/S<-L1jRq;?k;X+%6TDF7g9r1nE,AB[G.q8u/, at UU>Q#WT\26^l^A]3/JJ<DEX=c/(qR*fmY1L/C*c`E_Kcb#t)>F/T`dN9fas at IL^QjZE[:3h'3-6.)hc'A)Tb4'-~>
 endstream
 endobj
-117 0 obj
+124 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 116 0 R
+/Contents 123 0 R
 >>
 endobj
-118 0 obj
-<< /Length 1778 /Filter [ /ASCII85Decode /FlateDecode ]
+125 0 obj
+<< /Length 2677 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0D>Ar7S'Roe[d..eSCDcRoD&<K/;)5V[e(VsK at NGU$@1m^gS4[gk at -E*tOY)uc:"]i-[=CTdLPP'`SXWr at SKR9Hl+"eJnB\XLYKf3HqoEn*-LbQAHlb*$6U#!TVOUGQ&"V<\kN'j24SUA<mC7*R4"gHO%ZqG38%K[KiAol at q6TrYqAQ(*97fV_?R3Tm'rZUBlsGaTYMPoF_VXU:?uko2On4nQ^01'De47FhSr]gG<d[Y;>%fVL0%2Yt_KA/KmNBgl8oKW7 at rlsBTf6<o.Z7<0?eir5Q?,Le.8d4#Z,&dj7^Il5eg>EO,V4,>`?k()Xn#q%+SD\1MaC]4gdHjbF:V,:QgaTi9/@KtTBULfa']mIofcOno0GDHiD14Za[)utR?)_jg-sXV,pg'-0E@'1"@)+2JmK;$MRamfrW2YtF/KFT=]t/_9n2Yo`Ga:j@=Vdb2oA:0F*1pd^3:;u?4Vc5kIY+=^+q\%mjA/KceH/U//"Mm._3sqId`9m^\('[Y$dG<KCeufi*C9n;87>pHo*]fWA6K&*4<B_]85K1ET%NULb17->\/WGcVjqng&.=rkfBFo(+4ilmSp2AJ:Z\'ZtWRi)3oWPJo[J,8]A-2J"$q-;c>=/g;B2h%ZRDlq8%6>-QoA#STa$+,?MSnp0:FIlZbQ0%*-14CnU=h=f>]FBQCHjMO\`UHqPl8NKT!`bpM8a^?M,<?Cn3l`Mu'4c at jCu;_s[We4V6?4Tpk\ZAZaK at W#cBE<2 at Uc_F>K?B']kkJ8hh6"W*i#4R].@*U2jK'n at R-PT-aJU('>i&/euom2b at l(-[Gq<h4%?6T1M:mFhfV'g*R at ZEQg6*4k19GLB,^:%%8V5NY]RLUk<EZAj2fVEL5nqFP)9m)0_#S?>4"m.O-ld?]@hQJ%1(-9Y$Zee'NJ,2SeO0RNE9R)AWlL$44Sm=gWbo85c3C0r'<q3;Kr[^R=H7[4FAlS`8abP5!3K,]VK.<.YV^:g;3<q"gJ6U!f!r6upqJLB]8ZqcE6nff#SD?,E[0[]C3)pJX4?$U&Jc^7BY;/0f(oE,?L#oq3K_c>O3aIC+dq#&NG/mEmc_T4Xl?&.6/VaL"I[sKZ:H8P,RP,!N.6fDI'Y8red>)[FA?!.S;qhMST=2&kdc_C:XsL\t6,K?T.4H]i&ZOg6nG$jj7S30X_s&\rU![;U"j_Mh_hK''".V[__s;\QA at htIk&H8A,kuH9]j_MCM%n]Z:SVBFDe_$+E"Qp(<'B[!?mQCA2P)5F`HUQo)qIZ]#dbG_^D#2:P6fdNY([!>"'VV)VGG?Noj>?uTRD'9;[Cm%hIt1.\:m1*(4uLWDBWC4.4k^CQX?E$H_.^qk_,4"VNkO,BES]r2cJ;L)0N0q)Gcj`=Mg_8MlD%+,0OE'lQa(aXbukY;5fjk]]KP`Tt`%3;eX.L,i!CF4%IW5&7uP6DemW:(.lb:RR/?5IZqTiSh+KQQ>k[E<VresH*OR^H5XA8f/U8GqHFF+b(SQ*aeGNAk`*LG<PNlD3&TEd4eENEb_[>19(d`9&q(XGHbV5l1,$M.,?`>NlfP>&4M7?:/6NUQ\*L?.r_d0*q3Nif^Rp>FK_,o?IQ:LKD6/H0o+:RH!S5RrEtGZ7[cS]t(K!51oV at I$:RUI`8-4]so?2n`7KAfk8^<*Q"(kD>9PN2.`eMf=OeW,(9"-GS-Q,AnOA`64Iu0?C)C9b]cA>qZAe(_$K]PgjG>F=Qb!=o9Mp9^M9k!r\H"4T$=A\=;lgT")8q[3KZ0DPYC/4d1s57i6~>
+Gatm=>ArQ1&q801&G!,0+JC*EU8/k&Tt0MM=Cc?,(Ztcm;:%5]W%1MI?'5YSrqA43.!$Y2G#Hfj at Z_Q=B>1p>5+`qAhYBo7>\AM8j>uTOcZ.piF$^@XJ]gUF1M=FAIQa*3Dk/]L].e`/;Ro>_)4(kR at 7Kuk\h)kmPt>4ET:5SUkUf)Qj1!<PcMTg2',b4`_HdpSq"@iUIE0I]c;$Pq4lV=4KZe1!2UBA:K`&)Y/BNn*-Lk!bP$I/>UWReM=>a]Q+Oo^u4>nB9'3QFuT,psh?=rGM]WUfb?(8t!XNJdYDKjNra[5)3f:,*"ecT8InDC11VJ=dk]Zmb#N3[h/\6QkY=>G`i`bA;mFGYs=h'5P`q#9j^_MhM6d at JPPrdE%,r[XCIWNr6abj;lDb+=g8-!ITZWbkm4GYfPBB_:u[g'T;,pGti2if1nS-WrD!k:8@[iVE2jomUFH?glB)+ApdXn[1!n.</L_`@=eWYr"fd0<73:Bph(.ccP`rBG?dDYL\?tOAI+s;Z%[[-)i,q<TA^*jnb&s^5*'d#)pj/Fq$F)*Q:>\$JEQ;3Gh1BR4:oF%WC]YnW[No.>I6o:1;/Q>/s(2E\(p_G&O;16LVd,&bPU<"l_Y;,k;0*M%t$f-`j*<&*RGW$`/pD+j3MX%#T;9`?dqNOSoKUL*!WJ1U%!NpM(AF+->S"LgAR&ME&!OGq`5U:gD8+^)sm2'^u.u\u1$"Kri:K"#^Xme$jD<U58:mFE6IoNkQL(cPpp0K\"fM+_>$nT=0g_2uja#+l#PC?2Rsoh"b;X].abq(I,^r7MSp$hQ##LK5qV$@LnO"N"bbBcaG;V at o_0lGV0RA9F_eP.mkED1Da6&D7+m?Lnh,uH8so/WA^/irTA]\6jYR at 6OJZ#Zu5r9;<%+U(9bqq,NJFajL2-/J7!d^o.jYhGAka9I)rWA,j3^=\u)m'BL4W'o`u!I5O\tIk#>@E<j)_fT?UlGT(W/+GB3G$#^a%gE(LaWi/Q7rZRsB9L\EVupMInR#m+ssg!$PsY;4F"1+b5Z1"s<X7(Rr.PH4upL:U+ASUhIMW.p:6-PV6rc7s'FagN\f:6&mcAiE=`R,_,_m't7iFZccOocYdR="0!]2p!?9]nK.%s40oCnk#gqV5G+B(5fAkGugcBRqr\7f<G!p]AmZ at G+jRZA%n%ho,aCu/=Ye=UO]id*OFEcn3cBElEOO6Hk.``oe7#u!`gM$\KJ3QicUQ86S=_Dh[mR9\UWFE->)&@#^WFR"?idU"Gs=O!Q3A9TEn+C((\jZ7\1?0.s"jn'c+/A0%CY<rVV`lAT9ci1A>cpYHpm[&`M0C$k(UW3,JNLCh2B!]U at J,='3ZI-^d.CJ?-q(f at 13to/Qid)iKIDm\7P=+j11kL8jhp:690#`2$Y]qOf\h.<PRE4lhQ\e]6-njoY:a4/8]eo^e.N6oL9O:'L/MD6?n_U=0p3;-f-1C?k at cZWV(=dfpqurM&ZqS!@BSj3us&bU8M$\-Hl0a&.pn#9dW3cjTU6bol3PMh?FYH$t:^?6`OXV`ZSO%W.7_LN7mFldYp\a&&7\,<m_Y=\ej@/_QOU/9E&#85,#+qsh?n$?\HS$oF>8jTc?tcV65Q(C()]])`d5JI$HI%KB"h0EFlZl`^6jb1m0]Pdiic)NFtBUM+poe5Ed&4fJm\2=&C\e.8sg9(d>=Jm%TTYFdItR9q*oZ]4gpe2L'INkf9gSMN6H\r>Zt[Cjs]+J(oCaS?rb!_WmrQp%/([6j"Cg&HDqEbt;/gK<^/TUO#W_lQeG*1OMSPmR/^D7me%WB%ar'nYW`gu?S,l$=P\<Q!+A&t^7b7iFZl_'e'DDB(!_7%9r9mFklpC-dt^lq)J?7%d\U!Ak6LJ!irYWD*R<H$a[^FYQCbT.\@Gh9Di.kZ(Nh^2!=9T*U3=8;4b^J@$Ea!JEf3"M,$>ANKrjE-!19T47MS3FE(.+"3PUWPR.OfAh+/PG#I$$c8bUJRbgr'@[]OogLsTb4G<rY8r(.$>j2uRZsY@@:=e#Z'/jF]?hA/7SNUR3Z6hNPl9q/KhCic+0*Cl]MeHMKoJr?HuIBDXr1KklniO684.lnP[A_9_H9&IYYS'B?HY:C#qY6Pqq\k#_#H$Pc9$DW^qG/ND"[hKNS<67J<[0XK#;#eK(ubO!(MCQESk)A(eQ2WNe$]F)@@nR'GRhaHMSEG0I7pu]=$fW')2rYq-M5WT%G*M/R]YRrda+s>qg<]3k at EZ<o+ir5<KIo[3=K?cTLLJ^e.KpW]'G3b.8aH$:H,ppKcmUld(=*'@>NK/rbQ:1p2 at LV[NCBb!lH2HX4D!=kG.>J?V;Nor_XWR.D,(%f>TXB,8ig!jt;L\F^oiNo8KkDBhN1Ir'Qk(oSTaXp3eP41gLJ8<%Dm*_OIC9$$, at Y;?hhiU5cVUVb2ET(?Z3Vnnt"de]ud7YQ8'jMrh]Frj0o<Z:<gZEsUMd%o^ShXM4'0ZPr*&XFZ6&ORfeh:ZKrOQgKsJslrig&+r_,ME[(GQEoc,s\o\:Gfg6eKB1"XS6:)+L at u[E?26l%i>G&f"uLm6ln>#I at CCD]#.YNms9-JqrQGU'9W0DNVS%Bqeh<N^@baCgU><9\h3&5fn``,X#p'+"-;Xk0.OiY!<VHiGJ@,gg)GH1OQEhCem1OU[W`DIemeEur:WUjiUhUI>_OR~>
 endstream
 endobj
-119 0 obj
+126 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 118 0 R
+/Contents 125 0 R
 >>
 endobj
-120 0 obj
-<< /Length 1881 /Filter [ /ASCII85Decode /FlateDecode ]
+127 0 obj
+<< /Length 2216 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm<=`<%a&:W67@/V8UM:^O at 7uO*<Cro]:,E_2u7TC:a]XH?gUr1(TMZ!2Y.8_'W9A0T=!&BmGcHOR]1\2&?Gb(P[q:79WA&1CiG&qf]U!tBc at 8dBFK&scE_Noj87]_QPSM-NE?KY_W)@en"DE9,W9FV[7;hK+W0Z.quI(-RArp1rq1 at 6%XQ>+5(hTsa64:4G2O-Q?;BaR't'rJ=oXT)5Vpri.jDMrTbOeu7&4?1^VYrT(f<UD58NKYVdeM=g*Mkd&2C at NlSnNnPPNLM>6GVAa)#-)\'!m+87rpi81X#&Fl`%G?GF=cA`=0fu>6p7MpQE#l?C[g)[[s'Yhs'4g["s6TpkjYA4HMGO*l:n=eT;BppS9s`Q?MM?CU\=qb^_4m=CqjQ9`%ikr#dr:SfK5T3#Mu]4bUlbn#B2l?FCkQpeuj@$mGGO:(SB25Ekn)E/^DZ3io1(EIrOA;?]ECIcI:a?pDuoUl_b4)]@t(Crb/!Wlg!>FK?<)UK?:3-M-1qBrB(si%OROmNDIGbI.#NNB)PlI3A;d)(2H\RkmCm@?!;o<MG\C`+Tca*4^p\RB]%ho5k2BbTo&jR.$S/rWAkT-n*1@)/E-qJf?f"d3#<aH$`WNrr7+5-\Vc#7Q0f.7C3Q/d$$G*^Gq#F`QBM!^B at GhXX$Q:R<KQ+2B\%Y>LJlbmR$p-^n:M/$Z<=AXLdNu$/rs#di7%cELCjUl4"o+6Xhr4f\h+baS.^adG%WU+Ca!shIuuQ&maL@`(R2T\K;j(&o(U]0-uUAW78smiC6IO&$g6au_Vd`c^Q'568Gm&D^dWL+!hA1*8WVJkEXaEkROp at 5J7a.$&2#3W?+Y30_m.2qBGe_9EWh0G2W&q_O-OYeiFSL<'CECk(VW/PI"htk*M*ZM]s_T2_F%E8O_"o*ggIe7#mo-\_$Y-&r4=ONk*9%N-7N0K/%krR+Y;ZDGL/hN,U7KcID;P[*aiM:Qm;fr.LiE,]39L!=fJ"TlFmbH)`U3m\l5l*9*fDI[qrP2oii7ZX:-_L#>q4a. at 2_7&S5mK4srVC8<fqRb&!FL+[j#pAn.*<EN$%0m,"cqEL#h'%%ZgI/N'bO1^ClHb80iT/K6O%Abf@:r"Ah.l6)cB"OAZN/mQAH`joZafZ%m<q>k"Xb_"ba:ilChbBB&gD2&bLJ4l*1Z5L$(GU4DU*T+j0ge[*Wp^g89LV(&q:TXk()$CpUi8VLcj0rI($fmRQ_!GUb%.lgI5e\f&MKKEYaO-<=qFL2NTJ.KfDHVBp&JXB[KUS9"]FYij_ZAsR)ZRjf0UkDi2-d1./drRoHWV,h+,PlO-IE[W(!G<oE\QJW2-8gKI6.U>bOt:33CrNn4.eZ_n4L*UW"`jQLS=gQKi5LE$8R/YEJU%iQ,7.]0]H;HUPs1^QsJ0A*sXWWijkE=,I=Jb5ECD4)-6HJkh(3Po at Q"V.0.k8h9LV4eq_Y09!TS1W\dkZG[]+J+X-2med&,hl1^'D+*&<,L;(j_8D6c.Fh475^3R$OG+M$rQ1s(!`6DG(gO?1cO`s:bRY38#9S'HH\"/+J@'#mdk0IK+70Brjl;HZ47 at 9m"33Q3#g8Liep7iVRP3n;@3ZDei_)93@$TYrn"#MLHKptrh$Lm88m%_GH++Ah/lXhFo]6_rM]OP`U&IbB%Z1oSKei<>DPT_[?5NlC$(kn[;iq)"(M\A-lcL3WV\Lu6"c%(>?DiGgW6%'^a56)C&,FnQ?rZYGjWD4aY&(qFO4!tm/)iur[oUjY4#3 at -+(6D6?&b#;^n7m&/q0aqW\iN&;\U7a7'R>]Te^kS$8>lpl2:SD6,i"V161.]gAdM!I$#_hWg+-B#=##Ql'#b(tobp+!f.,QT>*HfZan(*aB]-7\oH3=ua,'~>
+Gat%f9iDuk&;KZO$6PI!KYHU7#e!l,^b-J18SYTn at M=o'@+W@=p>9d-[:U[[\(/$bW79#'h5t<n/MZFXPNCrfT+aBbERK00B8u.T2)AFRcPh8d4jLd6gn(%63X>[l\'!:hX/)nWC7M,rk4P,tD(7^6DmWFP4N]P(i+#ulgp*cO'QSh5bTD1XdEFYuCqb[[cbOa:Ao7KM%+(Gh[eVr\j3>huDeFRF2X_/%&8BTO6.4XgP.EGA=)F%(PD[p>B=jg=mbX(p`[HdVpUh^c[8CdNJnF[/.B0C(h?M*J^IX3jar;-BO3qVl^AioH)kC$e&_2rn7MGI_D\&n&ZF&c;h6\.fQIUEVnFLX_9DpBOp_$/N]:\/)rV5D,6pJfSiThXq6'UL$jis]%>*UkMrgVJt$p?e&GtVpNMC(T`-H52[J"]:[8.R at Ap!\NHkmK^<Y+F-a)HT'NkCfCP;U#.%JJP)iI/8$]5,)^Ak/6U+/NI.\>#NOI<(h%aegYG;ai5jW2aeA.%C(d"0lIK/<A#V.\\92B!4r:^b?Z8IhL8RUk2'U)[kFS-!-.;+28_+3Y/kSHBjhaW(<V^:aEbaCCJf=-NNdM$%VsH.>faViX5]kN@?st->$0X['J+/@h"#?p^>I<)4:Q7XEO3;$AQ8t.5KN7P=@AR%BpB9r1m?d0\h0dnBL<FJ4j-6iQC"6.k)hY:QAD*4Pf)+Ng'>H'LVb6Y<NDC%Nal-JH at .#B%QcsiL`ItgfO_l>_(H+7]=uqkCKaeTjeM6]1YFR9W.L,*#RFbod5%n**G$e<P+m3cJd"-?U2U6J'k%D<cQ^2n,suY>a:kX`gD2YA'UB'6[Aq6dkt-J$Xm4m<iWp<QL'48L5]@V]&3O='BU[?L6$9qO8;D\a6+=M=)sEfb-csA$.udN)g$"_f0<;KncpQ,7H3MJq at N!PaFTV`7!50d3U?=U"[L*4%&''sH1m1Es$g:#j)8.R\5k"Ve5pDQt\@l/ARk:aXXD5c<n`5Oin^9`/W3Tqno!1qKntKcZ]E??K,VQXfR\#;nZbgnu&iVpu-nLQ`[3P2]H+k;GL#^5fKSJQ];F"P,*0>Ai7Q8"3^dW`up48O?<jfj*?CN"<K?:fb3YkI^^0\AdaBb5f$YPnZ$R^7diQNkk3&=2;:=ha_7K@'8QJmLJ[je^R?tqZE:TNHnT`mEN;?1>J*5K*E5GMpb".[](I8@=@#A9GWl5%RneWOgVY-!q+,WiI`CG/s*UbBu])]H&I\V#"r/0gTeH;!_^,tgW?#1Zc&5GPH=W.Sfl/+cV=)l9=,iXciXL5;S0N+`tH+F5r<dn$;9mK$TEBjG;7M7PjC>=Rf#GYgO-W$H"/?&ud-QEt.KBU1=[.!cX0/QpX;b5eJp=[>],BG?a5[E[>V3'^359[n%56'BRrTM[-<1^sop]jK/KG8B+OrGff`:u.)NbPD!h_:*SPGeuRpV^Q%.>n&&4c'[.%r?P\:clG9mGc$eNTf[JZnUs3D8GZs5(@ec695c=2$;]EdjA(WN;\0l,M97TF=`AoSk3STB.Lkkc-Hf1dPD'H4!,K*Jh":L"S(:SW4KS+$8lGB2jTt'].<GB5^*$,HN1NI%1J;ZN&N$uX7Sp_3+dJSAUThri:u[oFT=,\W"/hi<EG3E at .Kt3s_&YP(B*%EL^1J;Drd+XmBIV*D9.Q!$V:cD"pdlr]H4<cMSn7O$QF4XdTP*t"S!*Zi-nM-kX0Ge]kaARl)[1F7,RH&q(R5'odbk,='ltIb"UP#&eLeIW)`Z at n7t$sk;)2H]BS7j0L22*<-=##XSn7Ij9C(f0Z"/^P\U(b441Zk2@?E[>"HnMZO9bCDcmuV!+\??<*\, at oA\eCmrK?#)ZN,L4$SQ37*,:4t[sR7U3<i58_\mOs""6[;,[i?3$j84XR&)U`QDG6XMf`QsgoXCja_f-h>NJ;%&VXdW@@jSoI1bVpiPpbe'p=6)FJ;YuB,]Qf/NF2XHZ?I+?8.?Nc8;Z%0hSbAdRk`aBf<Y:cq@<Bba'53bNPdggm23N`%PY)<Cqp2e.ogcgt4"lmU28"g&uWK,7"[lRL-hdFD5-ugdeq!VQm7)^B&GP$jQ+X,&L-;a"%?R8KM2?h`t).#M81f/9FD"L5&.J;EsSY1%Un,eaOG.C_#"]T6WCX(^XTqX?bI=Y/3uHn=.IB[bAdtB)_`#o_A+ at H$B+VFFhZk;%ol%@DL"46jK2(pck4%oj7~>
 endstream
 endobj
-121 0 obj
+128 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 120 0 R
+/Contents 127 0 R
 >>
 endobj
-122 0 obj
+129 0 obj
+<< /Length 2238 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat%f9lo&I&;KZQ'fm[s^r-IDngN8"X$i7jIC/Z(*KHUK3Q5\im\0Zp:q.Vaii]MrGqs=5^:SX""9i+Jce at d>5A.t%lWNUS0roDkb*al>n95KDg-<otS3<BJ@%@c,?<PhBqM3!NYDV%1;Vb\YUrCcGrjo>6$CA&`d_<J4D3VI+<MMD#(B.fig%4NEeca7k97QJ7n=AfHm=-r;BXOIL^Wlb&F8OkLfq5V<q]e['>1HV+ffW(Dhe6#SFY?m\MHVNGRU4D0hiX@".nHuG^*aY%X1H`mB([f+.?2d$oTF-BOV2;ke$bT*8#*?bmO+I,$f$/'F?`spK';skiOE)7_`_r4drtAhV!k@^DpOVNr6NmPE[2+^DGn[Kq<N#g2lYm&dkP:E.&l7eS);P+'kZ[tDd"qPo_e:M/Zli'9n3EW`b;o.V*u5sVcTVi\=mM\_[i+*PC?8Jq+3Xqm.a..2m6#a#l!oOoC;bbU9U+^qp2Y1F>(>'QX807(l?rr%?o@&Abgl_642bpXY.+3gutE,DY3Pd161%_]@>?Q`ZAuHl#,'ro\HTaje?X:ceZd-YYO$VO/2uSa"(!_7N%R>_+(a]&;@"d8O&8'CTW0!F\u$m]54!X;<+(,/W&&^@oVRqFqUr`GJC9%[7g)HX_t]h=r'.t7bRWUGMc]T]#lHDFc1Eg`P<UrfL]-HVAcU^UC2,7-1r#_4o(gbG4HrFc:fefe?WHd6tsdt7jN>20T0T.Mt+7:n1;H\U8Hr>'LW$c6(,bL$$QGHSF;!kAk2/lTmYAGG+3[.dQ/<g6W&'<jlhpCm%fO?i8K8j3cV\]S`#p41U1KT9Ir1b?$P_%P2R:4Gri>T-O!"Q=L0JlC5,JT[%>RA'[V]SSY-YZ44*dR.<CJMFd1.;]=,>IFeS#2\D1u at i@1eZ8QQ#8_\F-(U%-(o(7N6!HKObA=F,fMi9;`QiJ+a8nZSEZR)RNSAfMKnT$EgF%lq=+SN%PG$-GB%8[s<6p5\!s,ltX>JP]Md2S)W4/Zr9K4 at q8&g?9TXU_mu'-C&P&0m[HR9.[WiQs-eJ0l0+gQ]MHh4F+ at VVaX]0P.\"T+""r`RM/0nnj"WH%u1PR>n,Qe,979H?)*tKOY4#9Lq;.7[cP;geDd9P#,Z>k/9jaa3Ujg?%j&0$L)tVsV;Xn8X[7T%OV`^sn,`u?FW*@]p9MT3Qpu/VBK^T;.0'm63rrY#Ykud2Y%UsDWZoT1lpR*uBpXB!>R?0k;?hn^oZUg#/\-.n>&s5[QK<'1T)9TR$FAC<WYfa=Yk*/\'8FD"&.PiGAKJH/gaVT/^b.`7f8gnLP*A_q6(eCZA<F_@=erhhMc=/"/;$o2aWdRhW[2St;4'j''V(_)erR=F%Oj\#5RN_WUi8<mh'0sbfXY9 at BUu-B8JWARh1d(P=%X"799)b=_+qQY!$3b[V%]3=dKuLj3jBtmn.JBS:/b/mQ>H&b0Mc>kE02#_VQi`()[1sk8[P)7W"dU8_Z`*ik;8c%^eNRkCIVB0AVPE_21qHu&h!'%&;;VX(iYZo#R3Ebb[[!EoFag4cAa3XVm->4(-CQGEDUW,Q-D968e*pK1r'TcH0g]5!PA&kSmk:,m:5mpUOVG_Y#-P2fOqIf&h!'%&;;WCZ,:.WfR[ZN]:>^a=9<Y7UDhOe"(<64>E647fU6b"'&4Xe,T:%q8ca)p>ek6"8&$Z%!I(T`6arn7\fB#ujqg\Y]g>\VPP,l]_R4?Dfq]2I0cN:Ii*f7o]Yos8n"A3o9je[t_(5-`49M0moJe2-it"_g*k\EcJ;;#Gn.mPNVrK&\HoX4TmR;menaYeSm]*)okCVZ;*\cJ'\R5/#"ZtsX!-l"]Nm=G(Nm?jVV\<9TFJb?TS]8arNh\:$V_0KIG"gBLWq##/48t=0"$/`9V_\Vqke*`&h3*0/mG`TJd_ at GN>E,Q$pZk9BJUVA"n1HMe#]L;k;8H#=(]YU0R&(I_bL-]g=ZR3hQHmF3UU.+gQ#%1$QY06$HuMcc)6AU'6gj;.&?JA8KE<oKZkFf/8\FJ7^3V&9 at 4OO3)BXDZgb?rD$?<ou"&-39Q8i+Q7iYXnJ]!L!E/>eE>ig^Do!D>Us('Vj^W>`Ue83.pTF:0J/9'uj!hnPI7c^&SFM.ScCF?2#4tf=Tj749D/R0n-!?Si)\?a.jTaq731$l<bfF$Ah6a=%69),+i54s;9b!UtDQeu.VSj0o;hk.Y2?2WXtbu5st35d5_.r]SaBZ'j7<u<96~>
+endstream
+endobj
+130 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 129 0 R
+>>
+endobj
+131 0 obj
 << /Length 2093 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0DD0)1+&H;*)_<&iddoJa9,i"lY)`Mb_C%Kd,jEdBg9o%AA9T-^j&LVYKs*fk,CU(]IVJSETYX2O\oB^49`@J. at pGuYC-?2'D1N(c>Y<.tlM.m7<i7DT:mgFp)6nfjJ`c^HmkEXXZ4R!b:]^_j>',FNaK`BP0_BRbHVF)bU%<$.^ZiA^noh#!;,QjHHTc-NlN\2uFXp1N$hAu@'hQ+!^khroPj'T]NZdqLG/n"s.]>X!?6I4V!b8a+oe(/SHE74s-p.s-oP?LpA,TInuC#NhSP<c!k?lCCEl]cl>b1;CMdNS)Wq')HcA9(EXjh"q]K"*,,<6Z/?GB03&H*s%78riW>NX(p>oQD`'&bZn:h#I7nkRFrdB.?*)`ZG%L!^GIU-/\-RY/AsffN]^*\=VFd0>',hmqC):H2Uc"2b_UP!+3'Ub9g2QbWXGHpD-?i<o,&&/j(^j>[l[W%/IV]j^!<r]`:.?q\S+1Up52p`X-_BmJVSJs!3]b7$%;s)]\h0)iXL$4ZYish[N?,H\7B*i,/K=7UA=t*K1K^>sh?Gcj.jMaTuMgkFu<PAi1,qolD;lS;#]?\j9Y[l?m$+9B,TjQeVrXju?X;+;WimB:?d1k.S&_cKYWe=euZ]Vc#D at k&NFEmS8?R6&@$_&efA/In0?4fY<1uE=[U\m%<s3&E:Q6aMA=jYSBl-SgGIq&R$2q*).f5bO_VR=N;2q2Ve]ORE)ebK9`E0e0&(Xp0/>:P]1E_"Bc<iLCad>bm;;^*^1YV7`%hGnrIs(R72dGYP/GH at AYJEQTsEXHuF3jb%b(,Op&6,ASuuQ61.P.-'EZ+U-qJ77LSQ8,P6PUAID9"WW3%ocqdY'DO[,KgUe1]N!P2Uj8OQ[:1nR$K//=N>.DQY+_ni`i"\oWicCD0SdJiI(4L'=)N>![0PE_O1`JhtlR]i&4+Y%!(cIsh at .Zpgdg.ob$U\\Hg=.;^eY,2;j&EJ!_WhKf=F=c,LpA^N=UhK*<P4(\WKM<8B8V4rOU<<e[T:6f'k^)`Oi(r$L*dd`kN/k2itC+NT_39)V"^s]f*2:Y51'E`B",_J%@Iu"IMa9ps(c3H\q;K["K:[GC1R=L_<0rhLY`(JoEBE7==(TG=OhC!lF;JP:V5?hp!b;Cfg\-ueTcgG%,[]Mn&BCA!doa`h+e*,78VD9Doc2"9b\sZI:Qb%PkaO.Hu%f46.P@'af&d.JDVgmeU<T^^[L\"K*lBKpX*;U:_.Euo02(JjuC;!f["XIK:9r.6Pm/J/IZk=K'ss*.^%Ok]%'A^bg*CnGf&Er at o0MAFO_a;@+od]fcOf]#K`L$SbI-$Kd9$(MTfB6MaWTeUhKYJU%Jkl)ApL.q7K/f]!djo:#;6pmPKp at 0OE=RJg at kIe4Vr$jMAuPSHgY[X-U#dUq<K,#_Dt&^FqotC^K5 at 7WN!T1'PY(@LA'A/L-ZKZSR.adlc%sW34rpRCf-qR8&/d!fIC0C7B/8U1_05#M5APIfOtY++_Ie,:%^<T"Hg9">PTlA>L+XX at c8cYY'c:a,di!lP]qj^[r0ZPA96T\AQWBR*-2g%+)Vk+#U*S%X at mT!EZOTc;M1biD4T/hW7#gJN9b.fgM[uY,=G9bmXrH;cAg.oXID=ph:\*djuF#M=Xe*j44_s"@#/TdW)q8:<&h"3130s>j3P\E#<'q9p*kKNjajsmR2s`.*\8:Ym*>^(CM*(6fYU[R/1mWAE`WNq&<iqK.q*O-b,3`b9TnV at aLHYG&4saq9`a%KI;NpkJVIW]MqJl75KR\Y at g$B9i@>>nV.h*V4,Aea+_b1rHH+hctIK,gOH0;r4.d9".!/17VI3!,VJ1KA"710'\&2d,`jSh5^A8\1_0/4csD-hCiYO"C]sKnM='U;!K(@ZBWY&_B@$dt>mfV:[$P"uQcRn/b"H9Ye:Q1RDjEfj/_`.>>;`7A3/"EZU<)Zsr`*,[-A?\Dn.R1)DtMsC:bs]:@K!p+r+MgYbK?159`N\qdRaM1/`YnbjkSj2I#`4.?"&A&:a5IC?n_QN5-;Q3Dg-bkgOs_j=9[)&$>eo"d.V2reZ^mt#V)pq%G.DF'](jsUR+&E9PaJH-Q8Dc~>
+Gat%f9lnfD&;KZL'gBJ8TeFU_EAB6[R#GJ?lp<:.Xt0]NK\`Wo at K6;7,KZ7kl;;Up.eQ8LGO'6QXdRAkU)blc\_tI$McqoC<2*f4+ZP873W+'50>:_>\@;ri$N=?L<gieU@^KTBacocW2+cLuHIcGB`@=t^<D9o6CWY(o<P9)eK7AiHIJKlm)UJoMm,A3D(8 at bP;_^Nf9JQGnrciVXht,jR at X*5DJOliQZBG2S8n&^TGZe7m/ubcQ;mNZcp at oi2W<@:)*.K#n$6rYY$*2bZS!9/##q`:?a'VDpQ:qZt-2680.DGe8iX6f.?R;`@XT_MM]DlhR/LLpAhB(;1Yd14qqn!sEX'ElPjJ$]@c0fnCbO5B,Bo%)#ak`:^_Q50.j7BU<0 at Qq_U$hBVAmsp&iUcZJ-DY;Y[*s[FmJ=l1-j8f&0i at nRTQ:"Hcc__V4kF\A<B4.IQrPm<a1J],XQ@@Oe*#,8Q=A\k^SW=?CM>3)1o+<dQ3]^NYTtaDc87p:9K0o<2oN3\aI4QM%*/2G+Tg71S2/t:,3/3*Zi?8=gdTK[/:5ut)W.H8]On"lD%8po[09/P3`jRiB-O?aK00ca\a1OQYI<:#$UnbJ-4Kt_00GT@'+&6FR?l-"n1+)L.:L>=ef_=r6F4%DAY,29\FqTd?'iJP$?4dCqq#=]=(u9Z%]D^-+f1p4A[\s6WUtcigME)ZL6VGV+r+6q\["&-99_M86Gp5H.An:bm[PuWS`.a*NcA.kjlr'JGD+<_G>:`f/dr6">>R?-K`R4gDKhS9C!M[(h<`04KOP+']\0\7pYCq^_9UAFU9XZANOCuJD)?Cm*2?qZ:lrT3THH>B4NO3B$?B[6JZPac]mI+0EIi`.KmAbg:T@(jeW""Ff-gc48n\uNY9\W<"pZ8XmaC;f!FCeqI=%715s!9GBK&(:)/3AV1%'0dRjBA^WLMk^$PUPDN3^FdoU^=h6VEB`fFk#Z<id&N3h7rtV`&S['c<K\C2N+R$_rmsR\S=\o5mI<SU%n=!KNMuYK'_rBd(F;k66'2]69gt.E-tl]cQ$<J5UF=eGX at QKfPoYL'aW*3mT at MOX1?JA;L>eC;h)`qt.`fTV15Rp;n7#]V\'sV1`F]oh#:Y@;l51mtW_p@=6P_F-Dif"qGNWFuseJo2Ki7'5OIjP1%n1X`2pHl(W!Ka^T(4cTI\j--Bh1M6[Em!)=E!]eIX,Wlh/4#[XF]<2O1]2b?q7F)3mKT$8ZC at .RBbL,00UHOY(7e%2hi#1-Z-[hs at S>Vrk6QDl8K%(s*ulVa5!1CIN"am2+s?>R0DHl[G5[$79>$hN:jZ#g<]4=-:GJIU_*cl@:*8H:CP%`#]*hksi9;2!\J at s^HHQ"$.[B8TdG;,Yfk1-;;K<"]c+<>l/L9^%o-/n8TDQBjU89-u%rcO9@"nS7#'iD);]dO28mS._:Pc794VKu!e6q_!<='*[%GDVpE;_kO65AZFNm[3$"M8@`DKSK1X;/m-#L'#EG3hD5[KBW5P]]Z=!PGLqC)h,PB1D3,QG_3:,kRMc/JCkp:Fd?MutGYXY30+&1R"\/#VVa+c`Oel6!ggEYjL\*;t6:UQXjl,?J^\[02b$^O'a3W#W*O*^0;J at t1gDlqrBi'i-_Oql1cRd,!-$EaV/[5taR')&j\!gWjkBQjA4kDsX1i%-l2C13'Yu_Pt%YHia)&*7!foMVNqD(UW4>Ykr:@/l!=hm-Uc_!2<U2#u#F>M-":o>:s8u&B?jq%tpK5qs_"ln6^juEo';2ukoPiM!pR*q&/0[$_OK5\Hqj!MaMJ:V.C$cFA68na5pVp'3%"U?#c2sFP<.)97RZo;^P[(188riI2"l#/$WAX4ce#MgDHG?QE>El>l,0rZjq\nMr,W%](-n[9TNPkWC"**H_bk:A4(e!2ZLmaLFk$k'UN#%A!^),0f2_h>fV_G3WE3"50o_-qT+G]i5u++lLm3SIUidmrH_)_k[NKcb*$dRd#5'0!g;(T*.s)<Qj^kq)F>L5m[Nk8c-n0X<tbTZELo1t.6S:c;UcVK?24E8Pqe+f?iIin=%g_o)G>a.LD=Dj)f8RlY>Zp_3oP*eeg"e[!X'^I'Bs~>
 endstream
 endobj
-123 0 obj
+132 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 122 0 R
+/Contents 131 0 R
 >>
 endobj
-124 0 obj
-<< /Length 2476 /Filter [ /ASCII85Decode /FlateDecode ]
+133 0 obj
+<< /Length 2046 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GatU6968iG&AJ$CYbsH:0dDjUAG)7De$5g=k+PHZFUo#iM24_i!C4cLYO>ArM-UIMPj.6?OeT(lhnHg1qX+4?onPA[7c>b_0/#f2l66R)n1P-C\@jmC`F1S4k#Losjh)0$AT/B^m2)+XB1BtrhT37QPZVns9Hgn=_FXR7dG/'@PB@=i8bMr*h at qg_[Gd".p-pBE_]OjJ?O$N7o/j0"!I[$KT(S_af9>GMN3:3lr_]&;?qo:F1VI\Pq1I;F#Br,pigUm6"c?l=D0.GM,"5 at tUsj:'*_L2o2^69bXfN/kN<RKH<_>8mDXZO/SsAZIGZ(gP\mP^KjN908VUl?!l1>8_H/o#eR=n.*0ubVkX)kGN@<F3f'g]=;k]HP_8QkI'3X'bmL>sYDoUI;(T?lc>$/rc5(-F*^nY>WIO=AH`3JV%@<30[^qDD9hF)%n_f4do%NBi>JB.oTL%32k_CB2^pg]Oq9 at JtCPAp^m\LRRh).*/I9.4`),H<E*$:V_>8,ptJC^]i3BP80`4_MDdCB3-B:7XDa#7E,NeAR4^P'(56,![uk)6(0n<<3Qh[P at YW`RNCCKF8,\$3u:N_hR;>V$g_q',T<rHfK,s%XV/?-2N$%RmDZcUnjHO:0PjjJ%lN^i*LQ(:lER!1=/%5Le4Ws-*^hbdX\3PVBQ*iDm&m2\T6?>FEWRh^&k=)B5]$ZWUKSMDjm_rLjWZ at _'t:S/&4j!bM`sZJJ6[!"8,63(naeHA$]^d7_];eh/Ls#hrHL*igjo*gAQ-/`8nL4S>H&Z+od79<T=Q>N[<6-%8XSN,30Lhi at cu.P<=5;+K#UE?M>d3hp\"-M#V5#U/SBgGFU^nS#3AmTr5^.7j=(SH<?'ZH>_nB:4^tQb==RI!_Pdo)Dn_>]LQLN,]bIm$"V.Sf-Di0Z]QTc6OWIrd^,n"P*N7f5H9@\h=OG->KiAJW`HfQm>-B`+]Q at Q)C6*q!^^9$rR.8\3V'?Il70[pE.9TBVDhF98&OIO/io;J.r%T]F7VJi+loZJP1RY-t_8gnrRTSG=JXD+iSWYMBWes46=pjUAb]>[oDpJE6=JQfe[1():3dE*B,8E1j#14 at f7&FU1c@].S8nKgUT-i#H%0EpEH76eKY1&bGnQpbse43PH\RA,_"_9-d_A,"qQeu0,^N<:u4Di*u3)%FKmEC at A/Lp1CYV"ZPIcfZba;H\+[ZZ$Wf at Pd@0T7U2KA^usY+nNFiHW:Sf%uS3"g&pV6\=>-pE!B'Vhlr'.(&M`Dbju`dBAO7)+3;ag.;XLZDcO:8s:7721mEHF'uf+4L5+noT!dMq2!Kub$'t-.W7):Y_5"*pbfA,%<;dtU-Xj!e:3:(e1g(<Weq5E$Q/f._Ka2j3=JaShjkZ5;p2!)j#Dab6rE]J0<O1m at 9eu,ad96NI0*;D8_T%o/KK1:RR/p5!ddplAiW3/%gE;/RZMF"oSA1FO%*Xja">cU*S1#K\5Nf=gN(E$`og?;J38&90X`57446LjoW'.hfWgOSVooq61Dtc('!eRSMV$XjeCD=VTH`rT[6KckS^E:*9?!#/]HuiO_Se?1OSUP(X\&f at O_g@MQZEtF"RV$MC%,-<0I`:hMfq#P9'6%Z>hL&<cc.#42(7!FEZ%:HB6^3fFY5QrDt%B.YefAa#-U-+i>"0qEUtaNKDR?u/.(c5[Wi&QhAm+]g'+&6^&.Vj!!QBY>n%+deM+Un<FN&'7;SC_L9]3urD#ts6RJDC^6!oSB9_aBeVOdo#%qaVkj?f.FY;tr`aedhEC:FDFK:+hPC9gs\?)Hqm.>DeJg;5-FV+:&-IgINkVLS0B#oRI$B2if!DNBuKG2>Q]@gl_2)GGu5uG\h at _iG\&S+ge8=_gF,29)<>]aE0nl3$\g327)]].IncB>^,d\:bs%5d9uUcPkWJ<p!-99:aU%XIHtVf-[8e\7qIR1$\i.nK+R3.#9.BPLa%jLcqqH(rmuHWYTn5un>Q#;dos/(3oDZm8'!DJ7h;:b5%(W[2r`<KYu73G@[@8!^OC@`(Ue7 at +CNai):^]]nJE&R1:m1\4>;KD=Y<6U6'q99(;Y7e<AlpaH:#$u?;H)&:H&4fNP>Pbr,po*\2%kc+_sR`b_kg(@%N&SXhgDf18ln]>bX[q8tf1C`$Zi"2j>,6[+(pJg5rN#jrc'+5gL(>,MZ3=)Md0?NP:_ at s/YAI*pZad-PT)'M[`7N83eHa4sY&@.*$f5DM_mg\.<WWitE@,aB.?&"=iEVQl^*^khmj&^U:WN5nTUVL?S;u$DW'Vt+X??^"P2(/gKh(VEP?9Oh23.:KM%S-6<?YfCp4L<h?eV0-"`'E7cb[:+d:gL?pns&1Hb(K"3H*>e>HYAAG3I%gl01g(Gh>.qlAFI8I[6(h#/X`;"lC`#UhQGb[07J!^Z0q'5J1 at G4GlN$lT+hg%(S<A;cL&Vf1(L'^$;Y"&J"bg;j-Ba:,Y*P6UF9qcbCnn'#t\T%q at Do:Ahj~>
+Gat%f9lo&K%#46H'gB']ZJpWfl=`7oS$$8!Z+TJJG/oe`e"2`hHEr>)pBubP6s>+t?94Y&>J1%3!.lILqMVMkpueB?rOd)=<3o'&U7TK,K"kCV\&812a2Os77B4i+mu8#Z\eg2\:,o.*W=7D>BN+.uIp$L5^nl,tV"L's8n^q+2+f#NplG5^hnEl</SOu&ro,a'S]R;+0@>;Ng0+G0qr?6"J$3L7cMESL&r<o7NL+HG at 1q<e;:Wrb<iEl,TiLCnh;JgCJ^T3B%Sm=/@ARq&oU>HfW2*H\)W`7UJ"[LMKTXt]m\A03I.I`bQW74;NDr,Gb6k6Cp=!``Jh4$LGF4]6bHe^_miLmE\_(92`KWn3gs%+2ec!hC&BuJa]5OU#Gu9mJf2l>9ii86:c/6$CAEa$_lYBah;D\D8BMsdigLjcUJopaG<'Mt5RPt(_3BO5'1bLj=kCaj;.H^@"%MttLI/JHIO$PT<oBu)gQ^k7Ujj__aC?\5t%AVU08R!:;9+Aff`!9`l#@]k6PS6)), at uQ=&*\`b1rSb=7-Q'h<0en%(0l'q?`Z0\9Lo7jm$I<dO6\8ClDWHeR/c%1;-i!=@AE7rRulVucA<m*hT^8FSsVpIb*^TXT!Vp0RI0(fJg`3D<T1%u4c?G2=W00&ZlI7S1`UoaAht\bBqg7L>X;[-XW&Ld3fe7iq)2*F.#]%;"cS)>N,]UQ@/Tg9#:b^OZ[T[(`+?`G;ZlCPq)'dWkhEq<6BdV8iLY_#0$BPI1Wu%O1O'\_bUb2"99pVne(17U$oF``N2Ebt3C0>F.1Wg$9q]T7c(c/A$oHGkcCgTFG=3+:Y;P+[gF/OE<o&4alRKDM&%ZP6:0,5dHPcdr1guQm1gQ!akZOc=[3Xuf=9^fpok6\,C*VqTUF7\AC#I=G0K;eULh^TVMG?WSJ^0o>e!U at EFS6[OrgsA(?X1;%D`_3;@5!VW<O\cAfrS]m\'Q1r//a>X//+2Zi)q<YZ_YYm=U+G)FfIDoBG?cSK,(pKbXDVM%>2Mqj=rt/j96i^]JMSjDn#]T#:^1%^%o=\(J:I1OAU1B]>6EuUU3ip[HoLPQ.4h8[_!i at -`8GKHX_QOZmm?<Bh:jKK4c(\e<350 at -J9Sd)dUkFeaX8&WX^P_RaT<.L],?>'T<@RYF(p':MEN.a&*1_9oac/AC/HrFV)URT>fS]:E<hE,#b2>?!15]60/nKWa'LA*Z(#kQtL,g2UgO>=(\7L6\-D$!*<#QN0g.<b89WZ5QXn&Nu%(O]bR6;6gT^;&s#Y0_U>O/@U;4a%'dB-"K.k6nQq%N6jma"=9fhdbrDEodi at SWp?Z+GIIL\mo&(`E\Z?1#H#pS8FY%q8FV4D)8+JIW%AKcAiY:`p>Dq($Hm\s&;<RB<%H!\+f3,m;bi?o at Tj5Y%aBVBQ"BHjhuLE6<uq8Q4H&]L^V/lt=1GJsj(\gc]-JH>PZ?aep\APq+j.s]E)4F$QXrl7R\'n;B_3i"RTWOpZ"+^`X^LbYCqNVI7as9T]V#hdc+*4=0<$q)"[n!+JS5tWbA<^@G;T\GGB:+$`UCo@\T'"_J^<`lY*3a.NVdTf>RgDG at 5ZnJD:TE at SB;]:W<GW;b)dI<AK`c<Sn3jZ9+p9:V8l]T"r4n*S]*PqM\sXgN"=;=`lCln_i@'sd,K`@0ojC3``[9$<sJ7"$<.f!WnUnLE85#7`]^>Oq.>0Ci.-UUGe`%8G.7tO*;8FYTuBb]AlZii:X'QA<JJ at M.gq&NoIOIA=]-*'/3V"U*]>Dp;+je,FCg8&Q%&JE+kG3=0r$'<Ad%66%RLB:BkV.fe1<_.99jHi_agolLH.)m at 94P;]D7U$<QV;c*b$ANZaIou9^3W;Vf7B9U]K>ZE$XCVeL8:eAJt!eOT`>TkQ-He:5=jr0M9XhQ:>qmFCVRuplo*>X/@l^)8GmLfqpjFZ9:@#NjW;-*.E*?Z$#[!BZ&BC^J(la,'e2E7NU1C<fu!3pA!#LOu6"AFnjH.IKT12e]/e=/Y=(+n%V"M*Su65>p*s3ejNT[0PW1MFAQkfdf0o at _DM~>
 endstream
 endobj
-125 0 obj
+134 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 124 0 R
+/Contents 133 0 R
 >>
 endobj
-126 0 obj
-<< /Length 2358 /Filter [ /ASCII85Decode /FlateDecode ]
+135 0 obj
+<< /Length 2040 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=->B?8n'Roe[@/?$s-^>9f]!F%]geKuFCc:_!c=S//OCK1o'_!ETZi>Fa2fiM_h/5=7+9Y3trd>D<MokV)X\kYQg0uqe[H at kHFSJWhC/d,th&t4t(5_b:jThCJSAk$aop,7%YcI$3=0^upJOikio-WZ!/LJ`>4=DG:p at g<cidMT<P2I=:ag*l5R/D!@o.A:Db9&mUMIKm7Q!#$"5nNO[5Ur""g>CUnIcL^RV[#.s, at jca7`Na,(/93h8X)E"XEtcFi!Y[^p:'J=P34>2+G!=To?VXT:KO@,[D30Q6l_,UZq;H-oK]Ys4(K at j7REG0cg.kYD@/CY.[PDV_'S83]?3Qo?#_3u+jXWfb!U;h+=GKpg$FLBjBZQ>oY#N at pEP<,K6Tjqctq$k/?XEZHBf%68O)uRZn?9OXM9u?dG4%-d>W6G\Q6Gu=jg'dQ8CP4KWbiQ/LK@&3q43b#A)XqI@*jr=4W`sEG`rU$lHXb*Va,i>0aP;jC*(-Q+aAH8iIS.nYt.8+\FMP=*`cg[4f9\L6LVD9PtAqri46RK+d!&,NSCRj(oh,m"?3j&o&<IjHQb,#u=4%&DMDc$*G-\Z6s?lX5lI(b)"#E>fl_MdQF.(mKrp[CRslqaW at .2"ai^s at hXo,NL4OgKP:*hJsb(_W?$G6I>#\^]UTekQ[JLDfh_Qf/DTlSU"NH.S"<gY<^^@\$H8c2Ge"%MT.Zb=qMB6`2nbt,+!Pi:ESD_(Am56QG-go7QfCRUZdR1,LuG^LrM:<TRraBr5A'k*eHl%L)^sdP-=fYqd9eQ3aCWTWVc<L1%P_7if+2=k59C:'ZH:IQVZ4-RqO"=[;K.][*2G&+d>@7;"391AY at kBO2g+AuNVSl5:'gYQNaRP"#>hX]6a]ho(9BNb at te)J+9O5:.*%Ei;-K28%?PCg,9<FRV,UuN<!D>d?MYeG`%%WVSLiZMY!#H)Cu4n\KX6R)1S,B0QE.><psC@'F,m3lnf6YYeu<%]/i7=d)6!O;Pl+fO>Y&:>n\'!])Sc!@S]G0bUUrBRO_NafZRJC22D(GA6#(_*3X%&NLlabn:`STMr!!B\<P,Pf]5Bre4/</.$*&U-.<AJB<kn&ffcu+qd-cc"5[/O?Ro-#,aC:8U-aqf+?&>tp.fF1QR,0NBG>X=AHHf3_l4ijXY,t&A.dC&6N7tsL_ok3:3)\"A$.RNk[=ed#Q:=;@8EQW8B.Sf^ho%)G8JfFp11q=H<iH22k30]dJS)EO6=,TbTh1qdFkJfU_NdQ,icc)(-3-U:Eo.CVlRu2N)h:i`EeU at mk\mh[a(`Ra'F7jh1=J0)F,+o3YlgXdO&t>e23)\kXN+3HGKD-=?8)X9;.j<Xf[MBt(,>c=`[,0KLqY7:?5[/dc5m)6Y\hq%\IJZ4)F>7C)&=#TC%ASAa=i'3 at 2)<FZ,5n3!`LPO0r1eR;Pc,G3cnJRB-:,BW.qGhP4%Vn.khQnh=3HGQ<9o[:0b[s=^n7>&\hn1Wn<#=,ue,Fki&Ei-pc;6o5sf*in<c%O^qC^DPuC/AM=:Nr:'7Ek)H,#)C5e6p8cb9?>@?[`UJUfB!IYRh/BliTH$N?N3e!];_SuOS*E+)*KiqtDfqO-@=r)?#\1JT5Zojlr-]9^qnY^$ISmm6E5sW`+s_gAT>9P4+`PT)etGu<[u!9/N#9M`g4C8CM:r\V%!OY>.R[.CIKMKVc at He3o@!+VnN$_pX+7-`&U!!g`3DAJ%+g/;DZb%4Qt#KFPWpZ2IVHh8arI8$?j#PCd9:\\@<BjZC-l$aB3t:4/+KX%#J2k*6d]?(GXeMSm$Jbk6"P6g"e/Gec<cV\GR#UO(V9TecB:RFZZA^5dL5bRXu1%nFgjW9-1"&I_%N6#3*.Z8)4Y,Wc$=<YgBJr$@`VPR9m,IK_hPp`J8Ili1V#iHEZFFKeYuA(P0eE3PGtsBXe5%j4(DoBTlq4>B/pJ*+T([mZb(rd>4@,j+eHnG;+1*&?(i51D5+Y;/?d?-$@=A2,64Jr=Lj)AV#!p!j)mXr04]2:?-+@>c\"s6!MCLKHpZR,aL-Kdn9+HFT.64CKYj%jl&KXrTln5CciT$7CS*=Tfj'utJKjtDC)1I61G#8E)#6B7,c)!E*Festjgg>(lK1QYO-oQM0;@sMV)T)IWa(ja`@;4g,a7%9)>drB]7[Zd at drKP<X*f]D%<Tbp_H]`W5JnlqVe3[9eQ+7j,PRpNf96=e-V#Ka]>MRE-+TijD\oG;F8m!*V_qEfaAu/L\_7)TNi%a[qd);Aoi-$I;EX^10Ki at 8(H.t%Tb[B<o#444ZKQ&*"*D#L:=h5#Dfs^?QG1)>>u:3FKSp^7$u+#]1#Da_OPHUX'NQKH:e,;Jq\FL~>
+Gat%f9iKh,&;KZOME-heK_8d,4q<3r at 0[b6,EU_t\_(GU'<jR;*qnKp-B^h2mN-L'.4Y"bI_59_h3'd*T=!pJInkmCO-<bP at T)`NPnRI<k3'VZ[+e?iTA6'/a#[r6^Z(4DqPQ`^RanfrUti(DdDjV0nTc9\AX8Joj4Z+i$RR16WgB10bV,j:Sm>B\`b<r\mG\cmGJY`8roqF9\Xomp-\%ZCrqBjlk at B#4:1B_oF_)l"HUskMcAJp9Op.e_3Kp2BMK\_+e<nZD-bItNW6h'b't9Y"?VjW-`g*ckkJ'9=&km:Z-!MH>'OUf*nTb^J?NSdPTm6[gHZfA&>##jb]\SRj@:`$d];OiZ-l;h4\YpK>LZiK;b@:A/]gXg$jQYuW_Q50-j3-]I)6A]5lMlSrJYDJSQOg;IVJ$+>e&3(T<c>2j#3k@]W.%uJ0n/$kbNVkea1h2ES0N4LW0jWp-Wh)7o&\X'kL#,Q0qWXH@*.qBC.^'8WKs\5Ed?lU68=<U6mmX, at d+h@g)*6&Uedl-q?"KalO$CTmg;YEiI$)3/E,c_f1^Hqs0.slhI98C`ML]ll5f;tbG%78GKXQ3c`LgR]@79'Y&bJm*8PNd4I1`XA@"3m\@A%:kRCBIMCG_H>>o'J('LfdJ.S2cQbrpL6/o*:Y(e!`RECb#[TT*mDI?bHQp*&[mgu?e#qP:Q32Y\g=1eP1Cq:VmGWo^\]Ju]"UQCoWda9L9e$LNF3an?";A\9ua65)!!XB0(-c)7Hdjam1-KY_2h$0HW&qc*Neh8qc:)-HLh/,*p])gJSo@!6AH$@Y"@Y at XB<n%RunARtoKZ4n)KY!OR6BdKZTp_".:h>@^jdoVS(k.#)_E=MsLLu)LC::88RKaq"@a,PNPlsE`F,&'-IK^B")dEpFeSl"nX;]@K6Yi4jem9K_Og7j68is'4Cl0,s02A??0R>op*kaPook0Z),U/8?WgKY?7k`)\Vp*g_LZU!.oMd,'YJ=K#'Pe1AVp(p&Qofn+ at W4JPr>KR"8rGB+h_"#`7PhW at RGk_;oYs=HKbI'M>#@5s_NuO-&XmSD7];65,r>pb&=Q'V$'ho/NI,83N>e$X0?b(Zld.7<6I"HCLs51S at a,PNPlsE`F,&&R.7.]aE:qR204:3I7c at pr[-KeU<]#b^g#lS, at bI.-mEfH(X'l*7PB at Pb`sCq*i[O043f)hU@:MQ#3G],n1%V3]+gEaE(XeF`-7IiErGlfX-/Am6Us#cK4O]4b/t)2IJWW^3k"'^=4R%^5,GZ[kERJE$7`Y+Ka.C./<JJ&$Z0395==23F\RF<_<#.pY15fdC(6%4J-7HbaAsVC$,fXdUUh*XFVBk7Q^^K`B$Q]e'(rMl!1UKOeVHC/5HR47%f\A39 at tR8g/pJ6"%)]t7-kg2h4Nqutp$<U5PE`>HmY2r>c5=MV:)Cgsa3<Q]LIC[VKQMGG1NRNP0]@^g4 at N7=kBn+BTrTjVBaddh22A).k4)-hiAXbn^\pmW-WjDM@:5QW2XN2NF_46.%)r1B6Y9sSX"Y7^h6;S$a+S_^(1-]bO\R?sU2I&d3%5n<H;ZegF;H`+H4j-F;(p>WOQ5$DP0S2u(pAr0'4.%RS]b[l7S)D=l8 at 5U4N3a8h(&f-a6!fpo+#1g2E5!XK._(AR"jc?B$P7%K_OAh_aJm/hR2l']$4J&%$Si.W*AERN6?f.*7k='fZ22l*/DJDFY9J"fZ[_,-esW*@?t?)MsN^Qb9@?UfY&- at RUau!o*d#/%)^9^$Do71fKS.$/_npf.`+Tdm'20[.2>9'ZUH?8"lh5W"6IhIb]9&'`44!E[+n[Z0K'pl&tCRMS^=')E3;Ik\.1(l]+Q>*Zd:]UJ[sF%@IA=<#dY6")bd<E6C9S/L6#Z1]AXD%WmKr`X`^sY$=?HUd66[3E&hj!5K&A\3\OM>k*4RKiiY=kj4^2g-i8cdmR$%RO!CBX+-1*W<IB9CBET5Mkl$iT;`84`Id'&l]>''q.*B6Pe(O'1bP(8VlgF*DN2$(([#YCDT6JN&:,;5^LoB;3%tRKpYl~>
 endstream
 endobj
-127 0 obj
+136 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 126 0 R
+/Contents 135 0 R
 >>
 endobj
-128 0 obj
-<< /Length 2630 /Filter [ /ASCII85Decode /FlateDecode ]
+137 0 obj
+<< /Length 2048 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gb!;eD/\2f')q<+T`YYO#/"3S9:L9\j"tV3-J'GG*"M/LJZGVj'hdPPU:bZ-rq at B!)eZORF$Xdf/d<:m-AOD'4S(o)p&*g=q8LYr50Hn"kH.KThd-H%N;6cqK^_&5LYn:<O'!CASeBo>il?u/o8DkfR?tg`k2h:-Wrp\'T>1i?N29iAb?1-Yo>^[jAQ<&^PLT>8mcFCYV]0KmJ,$?qDYq#Z^G=hVYIo at 1o/e1BQEo:=o,3HuiEillcgLU;OM^<?%p7i3qt\.lXJf\XmP"%4rb113Mc^mnqZX_PnHIO`RTT-%/Y3<Sd?SOmqq^"nbW)Pd-7h5&1nC*"eZ+m,R.4]rSp=t?[fWV]IZ6Tu9!kCBf'B>ATf3A4UWF6s?Ai6))^\.ro(W?ED+4pj8J009lH$qWW:($+`@T).`tnP$6-`pYV$bna^Pc!@c][2o.-648%1fHIRt$GraXh%,oY78&P/CVrrsPZd/8$c*^\9P4L]oejq$SX:,RFjL>9j<=OuQsVT0&o<1DY9Cr(]T!N,VT:M#ru4UFF[$M&5saoJi9Lm)e7>=uQQ]@Bt+K>&IP#XO+:[$Sr>i]\1fu"i1-dY0jc/6CFNPBO8)nLY:bEI<bA/N8Ct-Y'>@F>:lF'QJ*t9TU,/"8F/8Fl#4)o+(_X55sl$+nLQA5OHiF[)Hfou2CRWa&.n_bUo5/r]W(1R,B1$.(-]nWakEoF+4hLCC.[RD]m5$A7N3XTMDkuqa6CeWmkh?QPPGEM3F:0*$r#2fUscp+GR*MNM*YuHZbZ4cXp%_+n&Wp#lpPha7n8i at XJp31IBSAi@*eI;?kk\"\0\3JNkRDBP*pl39-gV7Ag?$TNqbF"cUF5ub>ZFJ&DPB3DC;`[!!$3Eg(ai5;S3,kXRE?qD<Q*p!@X?W(Fd2.=P<e3Es'5;R;f1B2Keu$YT,U<=[n&97^O)IP6qu!lY?W>-Zlhu4\&Z-Mn?R0]XHid>mqW8 at je%fJcu%JV]mFg<84Dd/m6r/Y,O)oE`$G1la-^@OBo<\$7"\lf*+`<"ik at 8Jn#"Vfl%)`T0N>M4iUSe$L37L`p?qW/$XXc!lt435%!qn.9,J$^gUu>WFbUM\Vcg&_;@^mE4SnJ!g+H=LQ[O2aJ)-`p>/D!`r5c.WZ_@`8*]a\"3KH:NF-l>8%p<lpfRJ=*0rKR_coc3r;i)H?`hI=H#2pc[["l3`^-he)3TctC=Oh0pJcPm\fo2[Fl+aQ*@:ic[ocb_]g;g3rDGlb??XQ*\Z%HNj42j,=[L\V3)[@5pasJ'%L='$:4V]>6m>j:\?:^(ZVRHq+5eFAiW8P"YHS)T\h$C*%sar1,M.ogHZ!<M&6DsAWXFKR[b<E+;%qU`Lc'>fA0+qQ&.bhl)Ym&hD*T'$Z?N-TTdIbVA)H:5\DrViE>c'8QFK[@l5h]]L%?&/laeQ8N$KQfk]:b*,WIj;Oe at 0o1Jm,'1.WacD_9d)m3#9<Ui"XVM&P`,'K%Lfcu_PNT`p[;pQoij+kmgH;C'.I>uW*H*dT[_#VkTb8&^4%IP.)k="0S:TZ1dJJ5M2IQ8m;2n7(U]pnAPt*#f'[Q^^Wa3nR_O$m#iaDY'%7`Iq*q%n$i=-?IPRUXR/q2fndkP at DlRWnM-!^=JsS,IW$MjLL3Fc%[%q)#%@`@ip"Da+nRV&:of.URg-d-\-@^LH0+\DNKn^mr/Aab;VUM/-h*k+D#a\FL"N&ep/K3MK,1[Noa*I%!0f'Vn at aKdELJ@oNX7!(E4\=C:C9I#-3WNRD)tJIakg+^9qEkmR3DRTkPO>bV6lD=qLu.#'=f(b>$F%_)o$qs$bcSe)W&uj'L^e3%dcHf&Qf=SK^,a06m#kjU*G5.bTok`:fP>7us1$*3*1TLB?%(G#6DDgp,h"TQY5rh$C]8^9">)Qi#u\AKqnb_hPXSX%T9tYSlH$k\L0<bYR+chX4;C]'@FmE>Utj5$/H%KSAnO8BQ\5#+%OpD]8>e6W5PdoZk*3VTf#&=KF]-_%soRd!B\0ar6<5UPg!L%/+1%8L5<Z5i`%33Z?^@&Ua<g9G7&P_srjjoh['aX.tr,]0*Ll:.E5p52=*j/(`Jr<5EB_-L&dUl1>X=!3^F_GL_=;[@6BO;MY9;2h/,t`Um*@X<p:>]K_9K%c(5G=j*q5Cos3'Undb^';qL'8G=,HX`6]cmVu!VO6jr/AB0#GOC3Q;jHct4)^9`h$g:Vk=pmjb$I[/bppO'GJI\7uk:ND at qh,'KXD4IPc6.fdh%X];7KIMaq*b89M>j/,MuDV0R*UpD$PQ.".#qCD;2`0"]p[c*s6deL^VB[#qer@)80cl'>qIVT7Jo5?=?_o0^@S<n`RoodOr?oX80IZ5(ikjs\hh[jG]d?S$s.cqo"3op+G^Z*f`ug>IXBp;]Ps]bY/!n45o#P9QR!rP3kc:3Uu1^QrGXnf;>O0o*39S/(Kf(Ne,A):HTHkmM].^"Z at pAI&UH^l`LE\P_(Wm3Zn?47_TIL4#7Woh>rr<9k!NE8I+aN(Q6`1&_tF?dJ)KVNK at HN2:Gs3,6o"K2QSU,9=R+/H&aA7)'20Tb%LDKuB/.n,:hk4ZLCFTU7to5]?'rV_QmJsdB0g,mk`H!WgCZhAed[U3"#bPa at e!l^-o$na"#K&WAc~>
+Gat%f9lo&I&;KZM'fnC2^r.$RNjR"i:1Yjil%qJrLZ9^R::=AdHg&65;?06Q`!Vu,7[&,BpVl.NEXD!1>;Y0qT1-3d\L839Q3FIZ?LfY"cP_*km$BpPpPUP/-fP(5c8[J<P$F9R/4E7Tm56=r?[C:qB9F!K.9S?rS'.+L8Vpg)8RZK*LZ)et58;9J1u/[`nKmr_q6J5*BgBl5AbWk8j5TLAGKW4;h5:nm,V at 0U8D<s.,d02NFD3#dW1r?HMTR?9_u$A:8n4&)?4Tn%c<4:hUY5PaQ"\"D&k;Hi=&bms;d:6_hAK&,=2.RZT)$p'Om5#DSjhr#hOdilb5@\eLNtn=/?&I.l$5Kt9E$GW;(7YtG;WUJHN3RnU7/4Fl.[Xq7MMralc%mq>idcKrB%0M(tu`PI2CN1'[;0:)BY_sq\C"D+?dD,Wp^a'b]!:HEquf.o3D*mS25?Z=Y\[?VmsgOjidDIqpC:u at QkgHa!KnaR8[jgPA4f8U95c*[aP;\*,UL,(iP5a28l^RLlt?=(sZ-gH16ur;1nKN8kRPmS2/n\JoJu;heY7AT&9ii*cKeN[^ekoPtX9qmH1:\fgU%p.Tu1]oqk)[$u*g5[503)QbVUIBCVJ<?Z27Lmm6-)]E\O3-"`H'`R:IfH`Cr3eO'TV<@^15O<])jjBA):;\t>Q:*VGV$.1;r<gi)+#R>a#oi7M05N',P=)g>[ls&FZX/9b2laRGqQ_Ws1[^khd3af.[ipF09$tMtI'Qr:AUHr`NOFtEC+C!/;aFPWAElug\X:!3%2r.),TccF*)Is.MYsbA;Yf-(R+q]ncd1")k8H at ES#9^4YnJNnapi)!r%+"Q\S6/Y at 1O*FHC"O._g3Jg8?,=uq'Em&[oi'AR[2f):6Xf_ILV`C at e$/_8.-q75$bHu1W-l$h4(/bFQ,ENdkh8WF9T40G#!3usL#"6-YQs+ep=o8OM'M+0]W'N8\=ce%KB4pBoH1M]*DFGpl&JPtpg'abiB:3qN%(#he6Ok`#*m"Y%]Y+iCd!t!#F64H.8,;ub[Eou"H8m*NA6dAiE1[,3#H?4 at nJS1Yu;8hp;6pN2nM+GP91 at or"=ce_RMSN``ngi5K792S4;OUg\"e3EeU%;;ub3&=?@jp7T$Y>,3kL!jk-aE`j,,/F6m,**%*9ha&4X'mqhcT++Q@[P%1JlC$%]@QE$C.cQ(u4&C#63Q"c82E6"<Xp]E\cfKQB#p0Co.QK*&q/J`,pUpVUcSIJN6=toGtKqR@,cQmadV%RVZ][1]uMIaL9-&`P&#3pkUn:[CE"U6%";l;#Tn0lZmGYUg1cOY6^F<Lp7k&`n$H5a5HdtKF2O@?"Dq&&nF`X4Gb-[c=f.ZL0?Rmkg[%Ebq_(Q;keRHE/2`#<=3dW-mZ<`/!^-VSOh0,E$+1r?N="j<F5`&K)3ZQ*1\Rc>OGOlL[r$j-.qH6&VU`Gfq`A7+8;CDE0b1!O65jAf#I0hoZ3p#[0&Qr4\gh(jUq:_o#KTI)))ehbEe@#6V.Z8;u2(rnC,(]p3]bqLfK(MSieXINUk.th>'1rHOr&fC>[!Q4<nnQm1<m-;X*M>'bOH"WT@=TE.bMHXVMPeRos4+A7fGKr"hSSs@*Qa!jP_-RF?mrZDlGJgo/@d9LF`Z6_^e0hI6>Y6irSQ2Pt$c2pgoi8XR"p&qh!&3cnpjPF9!%[,>g,l4fC`;AfC]JCDm(1qk3gi9-WJdM=RGfs#$O1TI'GP$BR=mQ(bf]]')d:P%M_Yj]J>En9b8_D.\hq8Zo=b2l&;C at Kn'BY>/KpK)fEqobqpC.RIhR>,#P%Z:p$0$]R2>&>1kp.$[mf/nL>R$1PM!J'h-dEhPU.j>Am"Et-)5;Qlc%M`Q>&1(7hj4`&OR3Y"DR=)!@XT;ac*$;=84.=1tNqDhYj["<^J>>VA5uEoRi^ndLXsuRX.*P5NR]a:_>3j&FgEC)#<\UMQC6L,2Wo`..E]\AB7Sp\<e[rg-N?)i<MuP8lf['qZ?Z4S+f5[3A4F:0/*3 at Dm+r>:JmI+W:;k]R11mXADB19Y<Msg=DZ^8~>
 endstream
 endobj
-129 0 obj
+138 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 128 0 R
-/Annots 130 0 R
+/Contents 137 0 R
 >>
 endobj
-130 0 obj
+139 0 obj
+<< /Length 2048 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat%f9lJfF&;KZQ'miUaU%p;)kRT[rC9Qu0)%"Y(Jt"_[`G$.VU&Vi3PE^e78"<fm_g'FMB)$>`dI):0de5'gSFFetP?KcVWC4YJ7W at dEkO,gse)oE,rQ.(L.JNSZZHCb%&uub29'2qdY=[//l)MMVMH+OC<A>)OXX-mrX%')3c2D.`m-.NXBq2(#jqlo7hi6:<p9b`4/*s/5Z2*G)m=0%5Ir>)n1Pq$sW[faUhrc8n&WE4595_%m=th6AY at nI8ZC4Ci&BWno+fhi!eWoWmmCue\O\BHQ,sVCO9!i,4-nfk'7T8jM0,.c4=FHI6I^BkQ;t'\`]NRT*CYg+ujm]ROmb>c(:Q&KEYN2EN,`V5<Oiu"upN_R at OicXM3FQ6YjRN&jVpi9c1=Z8nf1ZT94b%pMf0G3/5R.b:k[sjX(I=-(?S^/e"dR755;$Rboj#LJ(Zeo+ at i*,>^HCXIYa3UBkN=RUVID_1^?-a-[Bu0%>#Hr#9/MW;8pHL>Xo`Hr;4,7GIafg)qL]iS\V#?WAR_."SPiTH'l2=e(:Ei[;)94]]5BQ4mIE;E1D=oem;R8tQdqM$Ap-""q>r*c9eWDudL>T&h)#R>Te at k>nAe`IXu3'-hJSk=aH3hA-J/8lV4!;]K=$,#BRu#5/3!SS\99fIgHNAr0YlZ4 at b^(<'#rJ6)KkGm@=rDR,o]R\[-rH82LE9H/M]nOo6c=PT#D2-=Rpd1m3J[-^`!M<bQd^a0aJK,+=s:#piJ8GoJE18V8^e+KGMoB8cj;m\2EndJA^3r`U at YMVqc>AlMCJ+4Ws($4WU.*6[(D0q6jnjZ`f4sb803,;Ar9c[X]bW%E^E,$d*JY/9;4O;2u;_N+;5r^iB!KI3;!fO^m3L<Ynr<G!DIpW=Y(=Tm;ZnZHR;$g^;(nJA]:uU<C`[MX`+,^"]sl`Eme[Fa#fCV^3XkW>jjU99R:#9es]oNLiR*'2+;2e.O',<id'iLO9f%$$@',VU0%b at us2TN=ebg/J&hb"@?O9CP9P)LL#7M3TP<T0eof>667Ej9eCI;$7:Cg#;\Zf$_Cr#TI!3:@Yp5FC;h)`DiDOgGtu1K]ob6PY at nL<.%tqWU67b_$uV%e(uPaHWbIiZ]'F3oN at q5Sf=.^54l#_t-%[NoCJEWI5Te2g722VX+W<MeLdF`KdDQYde32jrAY+mu,,2(m;nim\jq(PD0KC0r$rK?)!R[Pk,FSbEJnPd)o":OXN1!0$U"^&!o.k0uE.-PPo.VV):#-;??BR(`B=]6'T>@kTB4?_fR9F'I8p:f-PSP0kANlBH\cp*O'MOQ+KMP3RT$O>\n-O_OitDVVHj!!bF@&Y'lgWUqo/\M$R<>okP-WZg*=;ASXAMAoar$l#Z]$<>6kN-E;+.N+?Xg.o0;Gb-W3!&!>p+qq2`ZgASAYsbKGG+.atf\_E"::0bE"PQJAY*jI at l#39/kR`A*1M_?#SV1kY\Y5e4P(fP&)iEP#P9MJnG\/`)=d4V578uoStS7/`A!R'UI-&$GO:!UZ^PRRt\`+,D6iZ$VCJH0Xr?RBBOQ9@":3'E,i>2leH/0^;'#ih_:Htdk#h0eiV3m%[B at Tj(NXs;+rEtVJ]Q;'G^nL(fgg:3-^`<L6a7,"\VXr:knLZXt*RgPi=)B;CpVa:_anIE)X[LAl']d;aU'i7qJ+J,e+SHP5nZ4(0*."X>n1nimYtpV"j:"YHrVf]_.i-1gK*aL/7QQ?(2q\KY'""pZ("OgEVK*<+ep:_"4/I)*8O(Og.7c4c>(>Z,@, at ajM8g7Z<t,YMOVYY'_LjSVCkF8-h:_MQfk]#_Ddb&VPf>!CLo5ia_tJRRDA4:*<fQTAucM]6Up2*s[!eS1_h9ZWh5kUSOrI)\J0al(VG"A2,]hAJn/)LgjM+oN30l=,1iH1t26/N%/#/5(4G]fr4)X&R7J?6!._/qEj9*gJ;h?`.\*76K`(WB0.&5/++rnCtdOp<bQK+[uolXDIRY0\7Ba,)ABge&\]7W:7>Z(D_?K&5<jRAq0=suOEGPOX&YADM!8e>MM^Pi^dRi[hK/]R~>
+endstream
+endobj
+140 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 139 0 R
+>>
+endobj
+141 0 obj
+<< /Length 2051 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat%f9lo&I&;KZQ'fnC2+N+LNn0lJ]X]V<6M2LV53ZL(uEf/=\gdD*i+_)Ys-C3;SilSjQ]=WH6$%TbRcC41fT<i##rQHZjbUQr*ElO$Sr`n*J%SsELB6Ru)Tha>AY;kRt]LoOdbSZJ*E`eO"%Af9f]TB5#9?E'2m?TV]8VsY9V)C:0Mt+(pI]I(geTB^(i\Tm^j"mH;UTn"*BAJX%+6VtihTc%iFW]L3Co!>qS7Hi1P3A@@U,5WN8h(6^;UeIh*8W&#[PSQ;iTDA`d%$M]4&,Tj;GmkAQ#Mm.IjHC50ZBlP4F*]qq!&ZQc\o%(1/WI<c&5!'GB[u+"gYApc!aie-]@9H55odFje at 5N(R660jK:t_XId/@%8i*?H2R6!0IMbKpjE)<LU8OAB_-FO1GJInr:sW%'2gXg)DiU,X5-EP'S>uPP,Nh3:bck_drKb6cZ*39<6c(;H;r,Eb@=]if(/Y&gO9:Q:s?ttf<f)"QC:]E9r]Lu7/N$q8LkOF(l0;.&&cm,`-<sA_k^)*_WCnC!gj=fbYNr6Ggg!l2sGMKL++ck*r<i&4[!mq=Qsu*Fc/rt8gZ#hbVq*cAl9=68&0/uNl\?bQmd+%KI/D3Ki;]uJl($U.07/QldT.7ZqH]k;^8MC6?BAUAKI-c\7XK&mVe:'6im#YG'UPmlRKmGBg%+a;Bh!%AB.<,aGq(Y)&m.<!TcJN5Xgu*N\!',&uaieS^sqfjY7%*3Y:GQe&B\jS6&;Ib$o&OEG14L)GgZ2!d.pjQF`")-;@DUi'i-*a&H6?$DtN)IbA!Ob=VTF4,pPK?Su\(B-7*:4`o4[@i/,oUSprt#Rr;:@N7Ni$tJeN at iSZ%JRG"QF;QP*!j=E]@kZ&9C`c1oVD2,ZB',mT0W'Nc0_S*?`aNg%j4irVa\ro3HG2fr<]6H$b80]Xl`\dKWa^\6bs+(6RUW,\<mf+aWtdrUWA'5+ciDFs0Gqc+<A9<DB:e,0&u.F!#o&;T)9TD7$aUNt]4nB'_cU&:$a1-Z`IuTg at Sl29Ne4SOGFS6sMur@toCa=ZPEPI"CqI78 at Gk"';:IW/;dh:,G[VdEGi4tn0="!^b?-gpUlha5<j[^)'Ke%mK4bofDL<5m0IJ+I*[NWJSlZQ?$\PeDD4Aq8Y8 at GQ?Z)?=TAHBdg@!b/E4`K at 2D$_m4g'D'iOGpR#ULe!W"0$h$^4$d%5Z!0oq$Fq6VEZjN$T$M2d62uEgff9AdO`&3K+?!:oR)7E^sh9BL!soo7TTL=[EqV9q&"7^r9%*DUNHJ=dHr;FLPU^pK^t#b$dq$^K=nL.8nHKqE,aE;9g#O;8O`Si0aa*ZGZaf=ZSg%"\)C5TO*uj<:XI$@"8SJS-09&-m^\TW)jT3%>1,O1^rjg:]ro+K4Fn$IChhs/,4)-?T7B9b3r=VC-M"+]&jX!QrFnkI:rUo=;[:^YUUL/hD7rIBSfWWYlorp3,OX)aUCp!#dY3QHY+Z^@oh4a_s%kp>s_CT",]C4V`5%O8F\fjU58s<qTB='5"/T`kPXZ-Y.mY;<XDb8kPX_Q^$=cpSZIPt?JB[./3[Ks?/BJ55e;Q:@G5kI%<eL=WjJKui/!FDNO;2SB!a/*1YW\-V'sf0OgB.H_,Yp[nmlN8UM\tDq6i8G3Xg4rC("@K@:uBt[\/<9g;5?HD%QtbgS.Egi[DP<IX[DC^)as?&"DT3bpKXD0Z/n[Ys9)R_:<2Q2@/T\$naal:a]7=;4fC9***S;4fTNW](aFD*a0=kQUF!F*+)bJlF`p at Re"<HlJ1gIA<fGd#$6B.)h8P>TFT+,l;;L9p%<]3]ODR_2pi9_\?>2ba`<DY5"O?TC8HK]0H1_>0GP9bnB at UC=eb+KQ5Fo_5OHH;O*I>@K4gfInGTC_JQM0q,724-+YJNj#RT"7O\JXg7Q1@=c/c5ZmeZhJ1;,cK_FnBl8kQ7Dp^dG70NX4TkV42NJn(Cnck%EW1)p>Z0N7mOKj(-u66AGYg7g%6l4Ui-_k4iZ[lY)sMs$"jcd,Bo?@EqZHq_-(eE!FIn\epu-^&K<+G3>@rrITW`T$~>
+endstream
+endobj
+142 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 141 0 R
+>>
+endobj
+143 0 obj
+<< /Length 1985 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat%f9lo&I&;KZQ'fnC2^r.V:UFalr at Yq0THJD3CiOF`-PO2E2*r>=5)1W!K]**0GADV:_>s)4Af=pZ+lMfe8Mdl5:646[B8m2h;TLE:<0$I+HrSm^nrP`oXj6#5l8\!4;&g[ZXS3^%fT:)MbV6f]+_+A>^`CiPR at l5KYk,D[%E\m_MIdGU%\YtIas)&pm0Q at nNU"+KEN0^J4s1J-eEr4aL`Li(fe!\-6/TkbRh*!#\'d\R`<jD`@RM*mqX/HON5tsq-F)lLgJ-n`%/udAXs+1m*#SkQUT)Sj<Je:;ZQ0`plZ2!<+MGK/7qAcHJ%o!G)hVJ-U[@AeLH$u'G_oe^b2c%cs8M9:@EY0a`:=7$+Qd,NM\OA6pjQYtj at 9+*(n`B?9.p*NAeG]nm7Y^Ui?Tu8oOJ3'.:(q*Dl/#fF#h%0@;#RVkCjP&9O&M6g:49*53 at F0l8MRid-Wh)6o&\X%I<;3gQqrKUY[kJ6.]8R^X-U1?b0mY/l8gLE("s54&8m<P2oN3\0MT&QL9^ks*`Ng32&tj$3A,13nB,k9Ls4D]Tl4/F-\M\ZmBZNrb6gu)8bUN!-\=B^Bqi7XeM\(=X at d8+Sf2[`6<'d&@HlB\dinZ>X*u78b\*8"XC"KD&(cZTiY;WETmb/GKoSKW#u=<\#jJj3'iD,dS&?c$]bA[+j;hcFE[f[(lr"1b]gcJ8B395Z4(T?(aIGP;e#m)Q6A'L"j)Wtn3b,8^\tNJLC\[Hk/kP#E%m>Y[:<"LP1b<oL`WEt48f,Iq`D#9Z$E8&4>!.M,C+FL:jj=3cV\c6#2"l'd(JFA:XLtDunBA.Lnass44@=/ocX"5;E=*03$uH)BHa5>DCbPZJoXeZ:l at aGhdo-WY,$e6:=$$f%/4+>W at 79OW3C(L!?qiPZ$:_:]RAMqPaWs2m,,D!$1;[#1E8cLBMM3glhq2Hg7'Vh"@4)LREJ?>@Yu at MKNm&AuRhd%G,(mY\]C!0sa:kBf.fJTNnclTC$SrA&YA8n.G'sKDM=eBn5#*Q2Ng<(Yh%%=cVh-,eF=kU3"Ynf-ntES*Qq?).OP!6h$>FMCAfTj6!H:?ojan;GEAnCDjZMtDioAsR%F@,d/FHi4JU;'JAu:@mh at 2CLS6YZ*0YT7P]0jBWb!kCBfeF6(JkZVM+rGPgAfA<1A-%<!LR^7Y#9FOHWf1Z.$^9Lg<K3ON"&Ds-QV\ND):fdP:72;>1,mZp,D3W)[bei;QA[40NfbtMDLA#;[T.`3m,3_eRq_CL)*_R9k>Xn2SsZ9/X:!N?UQV8U]9l0CUQCEZ#U_rBKUFG`8Me*$]J,=!Q/a5KL2\<V=U,mjF0&Mt]eT]Kd+iX!7:6K$4V$dqfRZ(aa`/<<6"e66:U7u:N:<7#ZH,0=LL-?'b%I(q`i"lcjtdF&nsKdJpuI=rS:,r)-M]Kj'P!n'pgf8AT?eG91W,[Adj`an7ck,OWtt\dM@;E9W^eC8G$J11'C'bo_(rjH3NK?gd5*:_OaA"4]I77ucR<'5:DYgDB,%YuPEMkY88J@#dL6fW_O<Kb7.Kod1nIC%]n/!Di^rh<rQGg8F0&M,]eT]9d%%rD&S[/s^UErL5Th]OpjrILKmP2LK&,IpM:pb&3ZYA]=7,,NQBf:tGh[>X)YQX#YV%!.H;h^K2O2+q(u)B.*JMSR`*J5h.+!;S*X$Y)I*D5r#t$IYc^"q&mS-QL#-ljimY/c0F0)!CoCn"1?r;_Sc8jJ2]lD3e]nsrNL9UnIAC,_WXgc at mR'L+hH!K++IYDo$fGC;m7ombVpGa^M;9B`J;8+HN/nU9m-mZ6!8;4EuQBJ2:NIoWUkresVF6r03U9b4#IoB\uf9^'t#rBp:S]uQ3L$ihn\6&!(3I/5afO4Y3-OZ&>?3aAq"\)]q?j&A(1!Nl=&7]Q at TERm5j/&dn)NUlX$d]S^BMqFrMe2W!(E]&-)SKCh^:`gI^:bSj5(QR)C4%oF09.s7Mh(X04BQ=r%iV?cQN~>
+endstream
+endobj
+144 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 143 0 R
+>>
+endobj
+145 0 obj
+<< /Length 2056 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat%f9lo&K%#46H'gB']ZJpWblKH/YPHJ[K[[_/G];*KVBhqHI4k[aPqh7C8+t^SuDjefg]`@g5!i2?1B5MkdcWN""IV+QfB!lGC3Kll458Wh<rb;)nm.Td.enOo-/VTG_<Dj\bXAtWeqJ1,YZ1Oj,/@;A7;S!XLEgO1+BMDhk80Kqe_t:8'0C)aCRdQZWp^DO_Eb;B(BL")K=ncG"a.#m)]q,*^lTQ@]+O\up:9qRP,ZQ'X;-G>MU'`E)<fL%bqfetC9&h/Ls4jB,8d\cqVcitV8kLD*;,6]B02uDF0N(!S;_T,JDgnDTZA>QbOm30e=WmCohB#bAbMnu"qe8'\/?/O3n]0*(Qg.Xm0fbC$G;I?Pq=OVF6o2sGn]NXq:6ai0q9ABsEE160qJTUp1A(5:q(8b+. at L8(2).SDm0bP(6$doEWZN$V9lKgcEn-tWe$uDL3 at F/mP at AE:*cY%^bNH(LpS/Oa`I-Hj`?hEt=`J1ZZ"iN, at L90ipeG$7W_54f+N,4.)&cu<(mGar,U8RDRtnAf$lYjaE2%HF,:N/S,71O/o_0<;-,f>U=!.9#>ZOs(#2+C7C\pb(=#O)Y_rWfTh&Mba/*\83&7()l'pC_lUK]FTb#T;QI2^_5..GtJG\1mC`6tT at A]:qH1rHSb^+Vk5P,?n?:DMq*ca#I';Pt*Z+l>=o'SS4c1lnkBjq1@?UCg8\4!<>;1180-fu"71]Z,(*IM6f.Jk8-d.3%\p3:rAu.6`rK8Nsh#QI!L4i\P63pb]DnQIB]'0[HXL`^W8a#q6A1'LVB3$-huXPRKb9i.0.3q$JK!%^>8CgU>.Fn3CoHk'8b.a?!0!O6bb6fBZFdL$B(/f&[`R90g_$MrC7qk;07K$#s2a$KC!3LH)^f+jO29LlE)XVo]-O(1L4oU<#/o;o-s"1aN;[,;]q9=6:Z$Jcgg1CV5'B!VY:(bmiMn9j%i][*Pm\oFJIWe1a`(hi>R;PHj)<L*J at nR)1XNQc.@"B(8PG.6M[;*`&0A$@fs.,Nok,#kN:U4a%/>:SiaPRYJ\C&Xpbh.iETO_9k&('&R+%\GQff8NdPnrM%!$\DsAo\7PA)`MnsS`JK]3H-\TsYpILt`mSk)_-(g4 at M!.^dnMWA$E4HRS[Qeq-)gKn':Rr6..c4?_#\cGRhc/W$#h\-/au9BXrCf$lsdZ`IDTP1$uJ;=g(JA1nho4/E82c8;K*u);K)id/=_QnRT``39+ND;ButqT$hJ>'A\?dXE11fPg8MuH':O^*.iE<G_#^(\h)UKo*8p4eh)l6jl>k\J=6j2dGaHs\N4/V1$;XF:L3ji1]JsC6cT#2C:[a^l1U\S!bZHng<F#pKl((,-a&C-c;,DMSVD'QE21[,&1l#^?1 at U6T"$EcR9;6^O&Up*#q[K],YfdI0k at M9A2^;nq\DXis$Ynd3V/3Z^iR*Q^-M`VLV>(IjGs5_2UUE.SZ)*o_.*nehU?:h06($b4_(8f%g8BNs at G'6a`?ulec_jk,q+QAB4r5g-mZIRRmpMEsT at sWQR:3;SD7fcSJ*X(U4mr&gD*MB6j#Ubl'n8tQ!NaE\m\r35PKH]=-Y4GUp7>EG'2*(.bum)djl*=^`E8+O0TRri$VdMCWWRZ86`ihJclC%kahTkGmP"gOprC2r_"OLt\u]_l\:6,S([ZesO-P[_(>;+j-UX%5B0q^s4S.XQQ3Hf/cWPSb?n`K_kegD7o"6YLSqMfk)&m7GOXKI(lk at jp$p-:ASlb'r97d9:\iDs(-35`'r37iS at bag^o2!<J;-=[pQF^7DcolB>.E8W0\\9.65*G/>*8LB at q?8=KIYN3XNoQpa'^Y)K;QtMo/F83>eRilg%ea'5>qq';YKN[nX(]O$:kqCo\b?)_^`;TFa8e at 5mDR2q,eOdD8"465fj<Qqie%<.4:H)=mYViC?r.h-HS$Xp3CP[Hj76Tt73UoT.mhYL/bAJONeJe;9cPP<JK0hB*esY`ObTksI'&+.)(V0(s*oV"a.%:Q4 at K&.iofq)e!HDnqtD10(W]=C1c\7kL[U!b"pZ0fq`bC1g+*~>
+endstream
+endobj
+146 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 145 0 R
+>>
+endobj
+147 0 obj
+<< /Length 1820 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat%f9lo&I&;KZM'g+O4_"d!2Oca at uWD(kU<N";f3,YJOA at pC%L]70;#!`7L/qI7_T$//?il/7ISTR-gX/98&Z_H-6<@J7!$<@b(0hV,b<<%&&AD^J_Wt2p>keF;NP3:qr,j1Bt_'(7S4#9Us9,HVE.S-7o&d;_;fTInZlD48oqB(I4?="FNV)A3qF*OSBAh5J(2`%h,j]VVJrGciOhn/7$XT*Ahd?tb]@3iI38<R&5:fSFU##phG#[SB;F*nL5$:\hYa-S$La;HE7Ai/niTL<e:,WCma^HdY:6o&0r#8_&/?iG`SN;DrM^jR#f;8DYVrOH-5k'Pr)kB7\Fi-Y at rFI:l1s-^L?(eJ4Z3P3JsnDcW"3P3Dnf5nff.(I<aMpI`ob%I[M)qI[k*M1$YK/Gh]1SCkia(Vf+Kge0rO0=*2^7fp\nDeNabg1Q at q97,%48I+Eh<Q::o"BGMG$^511n"/ar!#f[^!'Efb`f[r/Q(c`/=@YaS_V1&U(bBQ;7VDu2?mJ#i@`$X!h0EFM0'YroT#.e:$6K<Gio?qCUV.Kf,F&`IY.N8]c#D*Ng<'n?/>4l7o6(_P2rD)[q9;^=]pp)anD25dYO'FOg4XBX$Y^2*M[SQqQ<X%=/q/rXjeJhjKjqn8k9=HX[m/1F!4)2")W`bL:e+u$;#9P9X3%?/Y#:<'IT\g.CRAY`BeO5icPKU!.tD5orL at qO[c;1,O/QmTWU+KQ.(/_\&`8M&aE?-VB>5D at 3&F&NOla57tNKbp_X^GLD5YXlLPtE",jF1#lI-W_8_Gb6P'm2LKI)G&+ZO(*C=,'U%R>snTpl)HT60@*3'7C^R8of+SChZR]-Oi9W/1\3F*.+E)R;<gU9K,Fg+Vjf"'6=f?M4;Km!785(-,Uh=bT=&=R]TRJJ$A56'%JTu!!K6=%epTmfNn&"0>K["L20Gb-U at 3+8%JWJ[ZT@"cZh%r at B=,:,3?W)^hD%?ap<eB`&i:d#7\V]5'hp30WY$_g,Dp4OiqA&ZrCE/7G?Of<82q=K)$+nf2-+gs,#&-j at ULa+&P`'W_Q04g/e_DoL1?l<[bU(Yr!M2`N)W>UM_1'(iK3?7$$57MVELSkfZ#K:[2hItK&WEAA&qD]464Zmooc02Oa^h;gU?Y&Gk=[%7 at 0Sfg40Sj#Y35g8:%,tA[$aHS%8En&f(kl)5C78!RLc')1e1!,.'2l.J>@ef6+LqZ3rVSLj=*5&>GVr47p!E+'!R];/0(,ltD2l?VkVO+51`41\`lQY"Z[hnlTG7:3TG6.J;2U!f.)h=1L16`Q;Pd(":)Kgp=,Kt><m4?h3fQHS:.`],m$)LIKB.!/KDZK5k!_JYD"Z.npFr.C^W8APYNrp563'`kZV5cX_d7:p%+*qP(n`Kn'[RsJ#&#Q`"dDd:j2EqBKQ07Q)o8MuQ6;i`Hc$<tBL6GL:n"ti&!B!T$;[6ZIU.;QMu[,PBcO2BRc,0NEB]B$h0-;FY@*om(!!kpq2@%u at Z*p.15i&K$d*GXBGeWDE?QBV`7>:#OG72@')0C@(hK(7_6OngR$HM8&6gZ\Uh1HeauI:)E]Cs&IQ56Hn5mRW_tm**WE:sh7Tq]KhQFBFF>jKE8]Jj$7l,Z"`aBJc[U9f]Qr/`nO;PsAOHs?JVn*WOCs$,h%'qCqF^0s3)%UE<</,bUIJXEMp3tN"HYDB+PAtNI]B_OhA]pIMfWB(\Tg8[UhJ2fI[`+!MPj?KFF]")#[.$2)GF>ZqKb$6A?O`0h#*t.A2"LR_U!h)H<6G3;c2J(H4]a"i0 at hf&c\J^-0?;73?kc>L=:?B%1Ysi&6$U</s$C>NdJ~>
+endstream
+endobj
+148 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 147 0 R
+>>
+endobj
+149 0 obj
+<< /Length 2948 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gatm>>BAQ/'n5n\;#Sa4\f7"Z^mdERc6i()ibnHfX53#Vp&Q?\M$Za%,j!3kp:p at k!o\YRFbuU3fE+d;".\]O!I?Q:\GDfA-n!;V^:\DsWs^t0i'3be+.`X283 at s=k5"AQ(R01*jm86OK^b<EDcKZU\5q_AXa0]:Wb#arU.EL9So3I^^X)&OSPHZCC$G4VO)W*u5)M22&QKa=iU+69Dlr->E`3ndhCPrIKZtilh]m]i3X,+tg+Lrt)@7/F-<5bFYUS+NRRm at 6b,#uXA0FU.R0S%,h.Rtkc`9r\MhL#*e$3b2:.!:GgSXkF:5bB_<\*e;_pdEp5FCEE.f7Juh!(_Y[fB6#\AKL,IX<Vb^"KJlc$&Dn\PL)(g.QcoVY at Co+:>B:jiF=#pgIQE(>/cG(UYf\,*p:)j?airIGFe4QH1RPC24$J7/kj(I?/Y at D<hP!M&n41(aNI)2H^q+3ts/I_T'>J&$SnU]nI[XAX!epF*hug-M<<gA5PmXL^rRfRA`r8k,k:kYY[KE<@nth>-2jKeDmFQZ)3CNLTTq]a![HZ55ts?:ra_3pM<-.=FgZ/nY<qu6HTFL>+md;pj&0mdDJ#)f2M4&Zn"Hu;e3+IXgV>0>s=5ucc'NWC0S'&ZsFD%S6PuEM?c=408g-*M8CmcAM"qTA`'=jj:bm)Ea,+'dO56Z#.C&29jEjX.Xr/Cr:o:3b9L=&.H/Z>#0oIaNX%jWY6D4/RVst7"SaYYcJ4ZmgPXpMeri4RHU4Yb)<i@==MhXWR"9oiQU'X(?QfWtBNNK]W+d1;YQ3oR+nCK4f#>L^cU4<-'/_&;c;/3=!)XF>`2]?g:Q;VC-&_ODMn)[<fMKkhU$P%jA\D+N16L*9 at QS)S0HVhFne'YUi3"-%@[XC-je*nNdnGeb@^Un*nhf=*Upn3/.!M7&nQ,_X=<"&k<T=->U')@A<P.bcXIVRXXYmP+`r8qgZjQ"BU6;97_XdgA=Zp5,1mMY;_, at KB]5[Q%6Fu.Ki<Bc\p]9ao7MDrNe(#AeLJk;'Wl?BT:,oWO;WZ6*-_)n(:hEoZ(MRm%3c[G1bTBSsj*f^#Y^a#E"DP#c75sgr0,G*)IJ=&)oV]H2KWK/9%nIO^1EJFAHpukI"9;\!3EobdEq*bumQ)KY*cCR/Kh<0RPqLsmZ8'#\[*.+P:=BIJ3Z:2 at 6YDI?#dGMi\/[F-1-puB!S?3k2Ul/U3<7f=k&k.9Y)EW/4)$7\2%SCfgj^pj;DqK<W/Q3D<=ASVZ=%%A.a at -/cMPFFRUp*<(AH79JBKL:.oq-<;oKu[hRkesAd\l!7VhB!<'U:HM*tEcrTq9$U"#b=U6L<"OL&r3J4+08^/o#X7U93QeKpH;hgRN17CfFeA=:q at JZno+*X!bH]urHU2DkM;9#tFm6[V(,m&kYKT?;r`"Y:X\As]hc)[U?V'U_T:pSdX=hFYTnc$5*$g@<D7L&)2`H*k?57B&J[[CdUUQ.#kDLP\E[5"B2ULbOmLij[+YAq'RG,oIP2QpTLO*2&DFHfP]R\[oc<rUAj*>qVS$o'rBrqo-H+lA^%-%G`d,:9:]jAQc$R@`9t(B.qu at GU.-2LI=>6hj at l[$qb6tpEpp;/Zo"n&0S%XC2s]=qmGj&&sJab6M_&E$\UYE"'@';LSiM>'8-+EYRG"p[AY8;/\H'U`<I5H3Oa.#G&sJ&Z2h]'?ujuAp*ABXhWaPDG/j`5!g!W:Du49#X;Lko[/*Ih:sbjC`PW?IAX9aFl<N]Dq]lMc[?eGH9%M":a3b68TgfIo?LOaFBFeVKi;q]]c2-R"1^34,mL0JqLAfUkM06`_bf:P\><gThD>ZN?3 at ua*%iC/:bVPEVgERiVORfZo7qm-o-G'.?B9:<R2B)'_:JIM%7P;%1mcVpfOYlYRLoeBmFG;S2EsNf3\'JIO\Y=5fLm!!3O\_-^\V+,\=KP1sKrDI,5)rk8[@8d(+Cub1P-6T3"DF<\[[]/&QA2%U)T_,5#-uL'E9n%[_($l=T%/J^IX[>oV;gkPN(ase!$$#u9K'k.DVtD&@,W.!;qQ<E7`O\u]0ke_G6+o1K6?OtgicP98YXI=V;ii5"q0L\+<Mq8b[E)fd"I;Xg\9M;#aYpZDb/ur#aZJ2qPE&T$'u$Hn`V_$GEd'W52]8HYH`b>*\k/gi(99+K((iW!oT8=f at qrlL6E?coQh#^E_%n"7aOIY^HL3p0,p:+h'2"IX/eo[G@%>M^8baEs6\5_qIZB+QmT>rjT$ki"hoY_.UVA=W1X(%!E9Os,'gjOV5Q\)ZFCkTh>NpNT*1,?A)_]gW&@nU??bdY7jMl'aXt<@f*Z[)#XF&-F/dnnVo,ZYRQE\8X='tI%[<115dteQKnO/0H\`,e!ejCm#56-#Q3b5^RD!qKfUW\`XiUbqC7R3la32.S6>^)!>r2TR5un^':K*`1SOgS1<--_A`HP-OjbZV&l\DkLK6B'p*nC+*=X:#[BhW]EiqD3;.[eZ="p]k;O8+6J9lVZ*RCQN!aT<@C"/"]]Rtn^0oMjlRBEBr+0\O<eqW:N3'5DomADoLqBa*0S5q(`i#(-T%EZ0NGfo']7U:V1'M!^mpL<a1EHQIV'+p&,Dg9Ut]>Y8Bn1-'>/gkV?d$tQ<U-h(5lXGdJXK?UXk^Qc#V;gCTRqG%&>7TohW9 at 5FJ,\@(V0h(9M6+MQ5;tkA(EK^EPa8Shl$,P(\l+;l.amU0FL]Btfr-\Y`0s&2-#^5&ihsA-c5Ic!'n[:o'XZ]BC1EK^]_[es;c3d+bR]dN01]%GCrb3o at 6(`*[8[FOC%J;YT\tO7>n7=*n?Eb2m.q>;fe9Sh9[[DP0=fUlSR3sr<)&N??b8C7q2;QAaW>,3UAaHCKX/WpX3KDi^<cON[A7C*q&Dqdki"0>tD4<3V^q(Ia]BiA]%eMUP)hA64!r:=nOG@^b7^Vr\Y_,Q8dk&CHKPCt?2j4.Fn;5:G~>
+endstream
+endobj
+150 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 149 0 R
+>>
+endobj
+151 0 obj
+<< /Length 3235 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gb!"sa`?,qp?o-2oVIbO"<mhtb=p/hOuQC2b&6;tR0,!+(:u$D+Xu*IY5\0 at +:7T>+t_3b at CiKZ6/pYn#JOAHbhn:\9YB%fIGrFg0'LN3s62**iOb1,&dpm-ChNcnQHDf7jSA/WZc,$'Yone9k-7ASI.i^0&&*P%///*WBA#Vtho7fRO8'cW)>l"0aM_mqgrqhWPQc`Fa<kn,!S4BqWXdjX#W)sH(HScl<`Ut[WY[g"&,?^?/CUsj<?!f`AL7Q=<7k_#9lu>DJ+ij:j32R/-9Q7OCfRmb=$:nUFrm^R';:ZF6sf%ZVNt5VO[$jZ0/SFoE.7aI^lIW%,PT7s$<7gtg++Gp5*:Ks"b15V+[\6[om$n;N"P2NS&1X=frO+bbiB3mr/I_&N?#DHM^\+)#@h(6pS-:b&W[pWA0nGM?*R at -g%%-NIcR/;go9X?\5MFo8?T3hPUt6n/FEU+QpLEPK'FVn59U at .!V(:T at sY%"JN+pF3O$Pf!"(9"YElflZ5<Zj_H8c!>e[t#]f;/Y3$Z0+!<1WMih:#JqSCH`8To/m<WI8`h^7&liJXT3*L'n2R:Au>BL'AlCd`.N]1]0K&APZOl($HVm`YJ>]A:R;jdlfZU0XaQ?'p8G6`"RB]<Y$FS7I+Agr,*m]9Xc:/0K at Q6"-EM)V_p5p at q[pBab&b!jAI'3]^GQ!q<>-6/,LsoF*B2#tbc3nK<VUMQ at dp7^h&XX("%m(\o'QPNt):17`Un9EZqt0`5?l[qN>G.SY4+2S0cg^u5--4lT5;4SolhdiV_ALKNC]\Jgd99"'P>]+$.tX23fMEos[RBmk6aIcU@,1rQpI?Gp[E:J*u?9LA.Ik"$bc9kOY;*-eoOo at rFes&rYK.:l>^!uGo?4j-,j;&23qnF>f7lOd_'+sb#/HsrF%)f[:,Y+2%!i%#EEN`FU-b9;WW\cR$#kfiRmS3b$uH9E5%`0=[lFH\tO*f:ud9&d:'K2L[Y_`M"m'dRce_U+G8PT]KV-.E)dIY$Nj/^n`.cD2?lAU9:AW8rMcCmIhQ0u;gA(\#CSqMjf0+c3'!(koE7\%s%A:@0.67fko^;8*'p*`E0kND&U<ilY_tqJFGRLGJ^OrlH?Tilofr8P,Gl,R>F+3d+85K?^na,&![m?!8DnIXYK>6c+S7aPn;]TW^67.s8^*h=tIsp.LBtjRmUf:(L,)GrOeB6`3="K40d$E79H'62:d+/>n.K$UNW:l=\$_"1tckZr*D-T:A at rAe&3]W1W&"I:G=n\1[@np4u#4T:3-8h;9nj..HhH\&4IoM#-e&mX\_\QB&ueo``hL)uSK>&d?m[IUjZdEDU;l,B<(cRR*KdY^ApR#2%K..*Z62d,hsrhPR[mFh9DU`^p.*>M1?tk4lRD92ba[aV><Sk-nrOCd+U?5k at F:\1-fI3n at JbLga]]l6L8-:a%b60bM,'fCh<af]D,Pa+[$X!<*'KoBG-K&_EJ&4r:dC?Z50cpQU>-4UO'Y^A)a=C/J1o,j\@&:bIDE2kTh]U+FJn4l<X0C*TeY[*5kX5LkadJOA><[".E*!J/eud8g4c]/Qp"gK:SKUr+ZUZCZR(D?d\R[cmA0raWB0dshd8oE=,ZcsZ2I9Fk6ie+!/foS0_=<u"9o5Fn^O7gY)c^4rB+,d\0W[8tAaB%^b3+:SDcdoK0f&nkIehY$eUI+19bcL_&)p?SUerTRK$^PpVP(^XnukYZJ8gj)AcMG.'m?DVdS at Q\A\Ir;Q;HS at g5HYeIZK<60B%3^D#*RZMe*opKiSfA#gVG+?%1P3!SG/c7/5n?REDUiJJ`*H1V\iqE(CTW;es)&_=.JD/c at FP=OUl#/,)4tj`*;:2W!?8 at .$gW/0)W+mHcVV\aHpH4(Yt8"f\2)\7Rl/-mlbjqk%E't]<.>Rb&H=KZ4*E^9-5feUj701*aG:(VU*PQ)PoT[^2gLEg2H_L$>D7_`<2sKnY"Lh\+OBY1=G&fN^-FrjPAH>A<n9Ktpt'5or*tC&kUl9/"O8^g]dcYI1+uZ8KtJ.c)jR(glK!,UoaOXZOJ"!3&@F5:HcZ=L7$bS]oBZ\QOt!e"=@4!I!@QY5L*@7#aPWM>E'gpO&J4(pVpkr(8EAC3K2e6%,g/"teR8$WpiYaBfRZ+;A[U<$3Ng*/^V!pRgIC at 3R/KQ/mi=.,7n`5.=cW0%&EbW(9Fl*'kE_f6C<W?_..MO3J_CQtfN@],b?-<UM/QYt'P"D8&KV('0gBD+4l`jkqEM6OmMW_-=>TN(`>A_Q*;[PgX7Ioq!/SM-I\L_8WSQq45X,^[Bk3)--%\t1%pOG2T$u/c(G@*t['@B#'I^06^:u-&R'UFH=r04-$_AUrh+?V+![FfCoB.<)Wn"7K4fN=,ZBaQt<nghXerI_`=SqSqh__9 at O7+?9 at SZFf-]!OS=Y&lFWCH at Fg,nQ3R(qt%jBs,,gtE<]=%)=K(&%4r$29\d!g;75kQ\X$$QduKq at Ri7%^I at 4VK?eRVD5;c*a;9b"cer-lWqssR%O"uWd,I`UpYnQ.)pbbRh&2"ks0T0#lr6*B3 at c=W`8LS<DpSME5JY*J0GCB<8D##5\H0S!qY2Olj_\r*Hd^Mlb(kB8+(3KVn53/cF7(%+U>VrS#1Z7ZaT;ans8<j?Y=bI8E`p?s%QMLrd?t;WBJouBRa7Kg\E=+cKiqaAb1YYQT[9<;f1!D%!Fd+ at R%6i%r5"W,\<Pa$orH[$RMZ45rmRhZ[0QUBi8*cX at 9k86*@1Wo3]\_*lkereW;l::Q%sbLe&4A39o_WZ"J1//4;=^a0`9W%/L,*lHbQ_L#.9[c1^1:TgYkU(_gmXK!:g!5N\4L5n8/aC6=-u6l,3C]\T<:5t;3UJR),T>NIaXQc$(W,`f.U.h#Qe at 6_4Sg/MZ'FYMJ!q:N)t]:L`VlSk^--pis&LEQkCU;nr=!]iU#OS5^jlY`E_.+.'#0M5TYDM7uE'^S>--Dg\+K#G9LVrU!PPKP.h]Z=N(U;uk]'&UY_IG)r?UPrJ8h*>dsrK6#r]QucNE,=XRTp&4,GVa$mR"P1o\+f?PQ"jt`>d2%l$%G;5^]M.<3`"6&c>&hJ^HHHtBk+oX1+B-T:A+DJ14,GKbASEo;lqUd=a]+i2p"8NR]:hT])""1fS,"[h!J&0*=##u<G&9D_+O<nJ^KZ4XVMPa:"?u[g*PJ8<FdM<HAenH0$'($Cu<W.d(Tm41@"=&S9/JqaoNuC:\^\Q\>S!k]RPd$LHVO7!DKh/LB~>
+endstream
+endobj
+152 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 151 0 R
+/Annots 153 0 R
+>>
+endobj
+153 0 obj
 [
-131 0 R
-132 0 R
+154 0 R
 ]
 endobj
-131 0 obj
+154 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 458.84 217.076 469.95 207.076 ]
+/Rect [ 328.701 177.751 541.947 168.751 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/mysql/en/stored-procedures.html)
+/S /URI >>
+/H /I
+>>
+endobj
+155 0 obj
+<< /Length 2431 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+GatU5=]680&:WeDD#"630uM;AQW#=LaGOJo!uU)uV4p('dKh`WmE0cXLm7pdHI(j/6Sa<2G(uF!qs3\/@X<.iX(Q[q1oAG at dkHV4\]<X;`>rP.7XT+o;$6f*:r=#ebb9u?(,r++j0);kDR/ogO8\a.n[C7O#fJ8_m(U#%Kh6f`G1TMbN)O>VE-gS(f7-a8C>"7HY*6M\8[_60A\)4eAd[P+n%NN(E.aV@,u&C6[0]B&*1f:hlci&a$92q+!%7(/2N;1V;![F,R`e,8bG8T%R3$c>15Tea%u=58]g$O_cGqYrD&L46R@^u4MW5V[2"$Af+'k-!EX:ugB+(qF-/\K0E^$%E#e!mgp\O#e4F-\_#&PsQ3kF.k7NX85*d220pW**Spp]jafn`e^m,YSN_e`AQe!LkTiYo8e&8mA'NXqIX?`'4i<JUR/bN\Ah7B'5l6A(\L8a'XS4r70[786Ak0GL$Rdk/nZRuBS&@l9l8:-T8Ni&3<(8`=hJmCiJAIPlRPT6,ksF,<Ci&">b7Lu6Ym5q7[Xm@)t2a`uP&bG![0c=6a=a&eVfoBC(5r3Uh+*l%-<3]h')/\G\@S-W/nalU-P-L/W'QZ1%5iqP?.Ll`o\/^an"I"5Up3'ugKb+LtL<VZg%F`C at CYS([iU^q2tNl"W<+nO#D^/hkhBXN`+RNbk;,aFk[9s5Ep-s&4&3OmH$c?1ZsIbZhV]pS\tQ%-9kGke7Cb:lq#G<9X(+M<34EL at BVF+c/XAV)$3aefbm%+<?h9mR<eS3)6?X at 8(iJsPM1TbLtE3cZ+7_-AdLO-JEgR9D`JhseIU3;`L=@]WTcRJRq>a^(;1O3"q^\/=5.3 at -UQ=L]E;]HiEq-jpiFq1l-KCRN<^D6<`HI#(f2)<:"=_]au#(ss[SLVMPEge,`sf-4\.]EqQo^1\*GBg_Ad6FmZ34Ism9O;TU73J`79[RV1-NIVRl at S*Of/:4$nC,oVcCpP=Cb>r5gM;hJXh*:ltT-RcFA6bt\D4O4'4=u?qQT.`?L'[%^0D0A4'jM<nXgC;S)`YbU,$^fO2.aGBrG'5OT6]S"<>"6`pB/!heBLRm`kYVl*LA1"4=$Fg\8-;BX73?*\j[5gOdlj,U`=io#HVCgKR-q<%1Mm.>GcVPLmI<f:\E0`_e\9H)3.mjdD*7q_6;`Xh+At8pAis\"0Ua9m]KVoY7k'[S%%W#/Q";XLQp2Mj]+sp]!HaAlLrucanJlmP$L]aSXWXRC^"U;aRhBM+k('KMWh_Dn)b"g]LT?O'[;iBa9,0\hgh!rhgj:.!g&Db3ZEG(#FZKAE/5uEV]p at bp(PaqaJt&;a>]+P%sJ4(7fX*83WH:)0`edol*LMNbTSG`NuJr`!A6d%'+`Il<Kop1:o:V:!8%.&TDE:MQJa:hc!UEG4^,cH"g0=1?:OHGl"?PtS't-bj;)94mbUS%$9[XcPGQVRp6;k9W6J\EXpfc8.s`cF,RP2q+K_Xk%oKdP,rjcSX0<)N?l_?Qb$".NR3Xu1P,D1##g_jqK^BBZPGMl[1:RO)kYf:`K.i$F!)Amr2(H,JMrb?D3t(6Z>:82.=*r[_i=OaZd0N)&8&<6ebX&2<(WF<T>0(k-/\5ErKj3%P)=EN'jF*,'/X$tG_f;[LAtraj1DlNY4p(7,rg1t7A#NDNKI<0;2hsG;8D4KA,?d94gqMTT,NlluO=33gjOmtWTr/LHdrd/fqRqouTq?O4\+3dR/pD!oChE!TWY^A;r.Shcs44mNK+%umA.C;Wb/Jm\RYIg86/=5[O!36<ckdH[cd5:?=FSMH(jilIdO<i;jBd>m8H@!5K:[ITo.3CscFc(IMK'2=0*i63K$MSTlWRo[<>!.#Fc7B]1lF\@M\`_+;@]A>Mk1/Gn)TN40>og7't%Bn&VQh:&n\Mg!uLVMi\3)@E*6l4W\LVr>e-i,Tj2eFliWFb"]Q8'BI5)?1>UX`bZMsrc>K68#)B)?DBkAE6&F_]dMs<mW^F.=OmD_hjUnFgc#XdE#gcKYC@/?%/o%,(mNOcsKO?r"(6tYCoB<T49Nq']!?Ih+HY0\*6(JmLW6IO7*,6h1:b?-VaBA`DK]&GJ#fK&CV4ec`b!;tm'#liR7eNRb.F?nQbQj,6#oKqHgQ_cr7[<J9OUfOg-NpcE(JQGJ7jTjl+"p[As7HnXgCfdZlfbO$B8eFq;cc,1 at fA'7U?d%\ms5k9>7[M49N.I<rAe:Q;os=e@>]j"nl8SsC'd/H*me/1eOj%R%>k1u7hS'Gb'N&5S'ILTURQSF.l#&[5*g/F5:7'5TYJC4fF/NY;Tth'*RNJN`8ggq8$p.`%^0DI_:Yq at FtG?WO%/ot8Uls)3kN$F,g@$/rWc4qP5g"\57Y(uS"VpIoF0(bl8r6kec3d at hq`b\V!>,f\317;6$P&tVL^FBM#bk9c%3KadXR[Q[u^~>
+endstream
+endobj
+156 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 155 0 R
+>>
+endobj
+157 0 obj
+<< /Length 1875 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gb!#\a`?E"&A at B[=Lq&+Z6<[%^nc"l9R.s at Q"7NUV^MdBJsM'6'eZppq=ujKJ/#!%\\tY6#RG*#*Zhn"o>>Zh599YbY`X%ccW\prT*O#gaL6arOgLKpi;!irK!HICA^(I at K+HGIjHF?c7',Wf@)Hesg$k%SC_IZS\2Ifd=jrr7qfjm,Z2Uu)?e]'97?fl[bai7ucp)tWlYW/j.+V\G7d3CD(1FGsmCa<]3s?:>QY4MOdq#'t\&OK^j=Z3qX+\Z+LXK;tq2u`k7VhYXfBN7sqkoSE>(q]80:nf)VBV;$;4rl]XJ0[cm@<2\g]>u0-/[j7W^SZ^cYe&@_P]arT*5n]kndhFccIe*;0`N+dc@)'GW$lQN`4SWN63`uDSGSJ9IY##l,AL3\00B:+TS]@o3ep7.fW#*0EA_@$2-Sp!%*MZpJJ`gL^8:s'Y"O;?BXmo(hua"f<TtFSTeWO0LD]X2#5'A(Oh*@;N7"o)pT^lZ=FK"d1D(X^=oncTN*n-o-bR4!,fDdete+Ehe]r2l'<,*F at 3aXNdFU``!,<sDn\&paVgp\R"XXMn05d5f\DI!N68\06KD/YJPV*Te0XV/W?C!%U:!u_9/oDbThT_Q+pD;Oj`BE-C8Z[E(jRop;oTEi/K8+nZ5@<MIL9H4Mi)DUp5oBB?'dM;J+S-GS,;J;+1Wtbc!!"f57S--e,;IhP.6)8?-hKHn!0/K)lugWhq[-`(5BQ(IAL>U8`aC^joV<YP^*5jmIl"S!$O-;%ar\=mD=G&,0%%41U(>T,10Yp+&luWeYj*^+srd;l75m0BB#'.3[5o;&4jU50;Sf&F-DQ)98OEr!lHpeG.=IU6+A9".i4$8C5lteN<njk0h!4ao"rLZP+R$q]FC1lX_0(P&SC5(+VM=ZRaDnLq4'iln?Xi-ip+m[0:$@\*d5KOdM?FcnD4:T%Kb-#hN?N at oO3r7QY^#nIk]H50*`h`2-l9/"ep0s]LECuYaJdsDNbc*c?O7_.gHCUE_$_T_jo+s:)dLC2g%+D.HeqO^e*/Bd#njP2*_unBk)5ke69pu6qIaPQb=TE*((_XC93-AQQU""52;2,PR+?tA<_^#8$iAaMcb3a>b+OB'#$CnJ9e=ocjjM&Q=m?XOUgHHp9i13m#b"JGil8l#_I_&/sT%^_Yo4JRLa&.<4[QXM_m0<7LnID#obZ*dXl-bF(N381cp5RNS>K`"i<7QK"1$lBp"9Oh[iM4ZB:]!`EbXij^2(I5>!:,mT9PYV0>2J9pi(BfAi.`'k24$g=:G!i3uoKN[;pOX>]]C"/p23h[Z5UR*&T->cJtoKH7NZ=1.8uLTq/P%=*E9Scj at O[8GOJ^[7BGX\g&1kt*(ZD-S`23Q2PFO'LpMSHNDbX9W=n5,KejS9G`1d)hdKhbbn3OZ=joIMB;_GCj@][:dg'DHi(#%m#Z/HarX?escJ+6nfsC9p-G!<d[&k\VhFshml[Ojc9%N1NXN,4bTeP/<)dqTs=P]WLg6k'nicFjc/hs4kFtk1[Emt-c7Y%H+3 at SE?&s27]$^n-sW]^C=]6#gPP]IfKEU<?q=jH-,ts;+(:Bt:IWbd_cbAgX;U>a1/ASn*St#>M=.D#\Qi%5?k+Xl3)nHY&AW+D1`)B+(]\"2kflRi+(,X_Ult]J0_CX0i&9H_WSVIEpOdoGoRt'6*[-i_X0p:(!uQn"\#UQpaCl,(*Y^Q:pO-%9$*Dk>SgC_[O.R$0+RUasrOU\YL>\O=pgj-,lMmc!#V_!5AG0/X:s<ZFn)tJ*CR*CJ9V;da!,Zbu,oBUZ:ap%t%l#GAaf8glO[p^WK^?Qa'&5uP$bq-N4?rhHDpu`\#h/pYjlC4?Jdt/TF1jWUfpP>MlHa2Kl=rul!q5o@&c~>
+endstream
+endobj
+158 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 157 0 R
+>>
+endobj
+159 0 obj
+<< /Length 2966 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+GatU6>Ar7U(4OT5&Ftte>;N"P.i+%sP;ljWBp,`lR4[!J5aCS940mpF;VYY7s*]pq7l"Yre)nKD9HKeKGHU5]lsqq7q0o%NgOt(!9E"8<#urEO'h`,V<clN<mfTRM`%lI(E95ir0.0i-PKR0#Z&#:$Q7"/PVMY_1<2%M]L8gQ#7199I>q>q8^V%3U#,\C%U%=8"4<DPf"2X"S8=F\F at I[]9p1]kh8p!ER8.n78Fof at H@2HOVmUD?C8,)ra>of+FSpZ at t06b[ek8/X:d\RH$CrJ4O6Ht,K+$Eg:SN*M'a=Q/C>FD(,'f4g*nG+SQSj[:u8WdUCY&MuTi?I\7Xb)Q!"8=T at 9PaD!lR31PQUKj('=('<fgGmWmou$XR["sIplE6M2s-%\Tc5V1Y1CBTQ37jELt/=G$?`jE%k[S'X[B[#%AI"%6MrP%WqKLQ8@]EPhDA at _)*#WE1-aB2<)gV at c<CX;Z!]QL8p2ZTEOVP9iBs%rAk##PAH8d6ID;eumfn#hD4=h'\\)Lh+T)_p>9.+>T,S35!%TorM*WQ;iQqS3LbS+L0/XnFCJ$jIkULX1pb\A7)2]VE']bo*0ib?(a7*g:n3"G"SrCCBmUm1+E,<oYi50:D\;l/_*B#J2g5Tl7L>oSZ[.R*dTWZ`V4]d";&KmiVj=5Q]#P4^pH,,6i/>?_I4^"IgT5Lg7S(7"Jr1UM1hlq7`Ae at MWpG`3:EQ%utI?WP?O=@2B^N6dX[8]Q%?A`FPX;5^5B!@+HPAYtS.9rJi?JIA']_\<LpmQuoBO*#'15JdV3"kH)!`&LsimZ'[(mF+(Dgia;.\VIu(O^%)#lccCLUEBAZ7g.F?ga!nhP(_ARg*!)!*"p`V(j`mgS$W`2r+"k^=PK&^q>O94Udu'-RBn+$BPM*7<]_B-<aW;e&4tbc4RdqT,;OL9flu2rc'ZS),#`%AQIQOY/c[I>.0 at LP]rq'(>i%ChMrA7RTU>;D%<G<]@$;Q$#t1,kmTnsF>SQ0Rk."sQX4_Aib0@,mP<`A;-L-_L.=&G$jIG4kFR:Or<tG#8c[R*Lg:D)%D4OUI6J#"btgp(Rl[eoVp.b.7$[pLLQJZ_#XB(jcMSC"0kXEo2:-a8J^o+\c#\L;>;nZ3C<GO<VT'-K=c7]93$&IW-Hr-"Yppmb`<(Q\<BCF?(1)7VF+K"t2R,"Vi>[e3Z>N"^k&^lZOj?k2HnB-&3]VUaI=[T?)b*sCPX<3\i!q&=&Z+Gq!3'gfUlKcJ;'dr-f^6iU<kV&>H'^/b;19QZANt+-3;*A0#g+rNpG*)-B\S,u(G4!g3:o-KSfj1(^5g&i&J2j,"A2^HA,6&a&delf at QR.ibm5qG1m8QM#$0Dc*#kbT'-P6I[#T\K"]c&06_Gi(OFD+r&LZ4sr#"7t&pA2Y(@l=SQ5I!/].0s')b;jMG+kmNaTnF7ObZ&/H1@<;`J679!G#0`+gZf7>WQc!-9%UiY>(%RhZs^B'"Fn+7T'YoQ:cS$dT.M+Eo(kDn at o5g%76(bBPg>$!?BR2@]LLN&@5OP$$!Y!?ZEX>Pp6Wm9GM19i^1l&#ZKkMh":lLn9>[b]+'-r2Fs^PicPuQHdqGnThN@%[)^N6p'0b<mO#X'2WRuP5nB9 at BNh/.Cd"\s3cWXZdCe?&NQlq9#)>oL)^"B,h;h]bi)14S$`hgWWZ#-mQH-i at 4_?`5$sO4H1qh/Q0j^Ok&>lN=JIX0<.j"Cnj=f%*fjGsRPOYJu(itg5U?uC\1WP*QUN=/*>-2e!8p:)Z*>00rBlRb7^n;MZfS:gnlMD"MW5)m>[f4*t.PZlnOfUsg"ME#fUZps"F_R;>R:Ummg:=9P/'4BYeN5Cl+WNh:$2l?uqUS at 1"%IFMTERJ<06SJdRrFf.'0t"5EpH#%Z:6?$1(2PRkY>qeP#aIO2@>VdI!<^F&L!bQmic at ZKUGS88\HuorkC79UY5!pkr,4Nl^LaPRj_,c6Sa`<]I7s'Z"il1U*+D'#hg?R?abjRC5[Ni6;tECEdYF=V^G at p)'b.f)&o3%O]]63):Ij=4<J:kKjJP#`0BgaOke)JR>2`DV9EU"Yq;ktL0Y5V.L%%:LPWW+Qg^q2D`!qpeQJQY!SCoPf#&)RaBU_=k/_snrBQN?WcP1+%(e?F0?,dSLmQ#Q[f$($ImBLAhZHbdS'8T1Nh1/2-GL!;2pW$/G@*RVY15 at bTt6sYOJ_EnRM%[5&,4[OPNSjaS)J1.FZ5Jr'Iih/+NeF/h2h%V\5A%K.VE#3PA7n-`Q\jt7[ECh*d;QQ4]fm$j;WM8UTOsb4Mr5\3$=Ss&DM1ClOj,KHYL?hZ5S>@cVJI+&!+tp\Ls$>\'=Zp^W4gV']CB5U2fDAi'[9WE8#42LbZ>1qg$h>9*RK\![-c1C2&l185d at YW0bmA1X-FQ@1RX\'2pL5pUB;#kskH?3Js?eHW1j=5N'N0*C<"QRsmU:HPjgB1=b)i+EQmRT*ORSe$$[2otQ6Fq73KN0+dBganX%uo!Nc4H1Vip?H=n:Qs]27%qUh,%ljok at b<)KOM"2#ekq6r/b5oA*pih*=m;d,kK'<)>I2s'm=M1P.<V;,TTWjY4#X)\@`Hd.B8+K)1gUrpOPc>2DUs9e;2?g4Fu9+G\@8;7n?6H4#a>kg6 at THAX@>7$88:JFUIe^bY3Ti.B at kd$j6#Yt>edX&4%sX#)`Os4W^f(=s#^CbG&p!hT)n&:Ap*FUa_:B)\UXT<\fXWh7bg+Y'23c3:8(*dli>hI$T08^HW-S=G[KGkV,]CcX((I&X?f"h,j<4Y'0EDV>DdlH^V%N*@\[M-)>(TcX4`'HO%F)dndan%LaN[Rp+$uM1n3(`^@a+P$sL4Wd<`=mV<,^Ip/oq$@RVsGmAZ];nce`I74OHh'*7cUhOdr4L(%`5Z<,T'XCU/(ZK`3KhV;kC9c-jP(8O(5.-7Mp[^&29r@\AsKdLmfdei=t.!oO&A8cXk)g&K]oI&pG06.~>
+endstream
+endobj
+160 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 159 0 R
+>>
+endobj
+161 0 obj
+<< /Length 1850 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+GauHLD/\/e&H;*)U%sU`FC at R8Pc,)0i$NX0ek$ca)-4u!TK70=9U8VjZjDKcpRQ-BUb'Z3(2W at C9+cH^QfmeIj#aG%n601&-K6r]1[__J(G/CEBfMJ&DHO%FcR4AWA5[<`_YDotBta+AVaK'T&2CgdbCn-m0]>5SY_`tNL1Aqn1&/=:Id72rSoJ5/-4&n^\oE<GnW_>1Zehc25Q'%fIQ+q6;@quHfnSn*aC<^iOIrAM;)J:qSqliNfmaFkp)iB@*,mRZ;$9DI=mq,O\Jk]hQQQY+f5gnWRmi^#_b)p/iF;9?WEc#Y`2dhIeW1ET:NQ&n"tSe2$a;q24c!2sW9Rc$g177aFq(eLk\\n$"6SPl&\3ghbdRk`ST;9lNDUIG9tL^%Q;[otqOF?jM,mc:*oeA8_e at V<6\ptWLbt#7ES"_^7-d0-,5+rT#9/k3n$hZgf+X+A;],@#nb"LklDF.maYpR=EHj)'M]hT.n9Ign$e+^N]E-[UmCOB/*eAD;Lidq$e26$[$=sJY1(HPHR$\Zp!,<N\2gt+lo*<g-l-4HLLT>[RCIFj0 at +BGdR1\4EX#ds&]ke(-nU'fNR"M%Gm9a@#6sQl&9p.-Xf*-r`LqFie`'r<02WTW8%Sg><:$]P'%Ue0O20E\QFA)pZPOJ6.LiKYJaZA`=QQdC;K_TX[5lc99$E:1?OQ0o4%A*LZA!kkY8D at BP(Wkis8E2s_%ANa%9T6GI5pWa0$ICO?UCL^e&`4O*H$?B5jg>#hGQ#SGl>/5c**db-79-K8:(GFHJGC\=QBB\dWukptOmcHDn!'XY+DU)@l!QV5o4/]YrM^%E:BqR.3R>NB*42@%/O0Pqi"T-$XGGZL7.7uBgTZ27\6UjU$5Iq;+skd>M0FJU8s'rN-'_1mP(i."-ph6*=+rn)0QW7Gh at 5o&J<cGeQD]R_&VktrBQE>g(>o at MR,k$^5=(O0IPb-WV[pN4rL6VVJWfMjg86E%EJ'Q;C.bp5[Hp/q0E&,MP'$,+-#t2(:kM^qfIg7*Rl2D\<>;/&g)F86pN.[F5rNc\JCgL5gK\77-qU_%':"pUo<E]=OpS'(ac>hsqbmH8\Ed\90Ml;9Z:8PN#3oUa!P>$$durS=Hm,*"4A3Z\iuPW>)QNDNoga;#f7,_#.+SpjoAJ6Je=5#-,7f7_^^o2E!Pf--dr*`&:FakI?FXP%2AFHN2eZ047i at TDCAhHf*[=qUp=dl-<a*_pPu7jjY18lnYnI%NIK*W9W'?m726eEI<<7&q57BrY1pt*j-YG[<-aTsmc$G92O- at t&J+?21iPXT;CAGl8Hl?`0%Y7%1k(2kA%RH,SbDmt_Z'(ra8ATF[AD*qG6)_`c^?Eu;"&A8e2',a)h::"eck"V at rDS\i&?0t)a*'?^;i919'++&'%,.)Sok<ufAKcq<N."$D(ibE2%J*USH$pD![ocOT\0aI='4D=k[sc7sC?J8"cU^6+W,,XWC`em?;$5@\8lod[J*QAh.[1O3*+2PD7=u6_Hep)D<W)D*3o;0.S_?Z^9b:#o/@&qqm at dP7moBm4,lY*DiOYN5`]-i%V*<Do<Q%^DCqAM4QpZ'g$a0bL8?.=2&?O<cnHj)h(NuJ+U"_"o]33C7)^*L?.+a6DcXr,D#&QqGbUY<$0S?(@,.bsk7RnG at 4+s:K\5n"r46gtp6WN'qs)pPYTcuJIrW*I<jZ;gclQMX$jmC^d06l4An]gKQ0S0<$>j+=Q=\(W0Z.4-s''WY-f@<_BSZiuq@)c>FToOg=K:rPnQB,SP7'n?i,%e at 8\WteeAd/I/3-&/ab[nm?Cblqo6S. at rr_MEe%tq*SC<tY[J7bqrUBm26=BJ2l#=#-bOT~>
+endstream
+endobj
+162 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 161 0 R
+>>
+endobj
+163 0 obj
+<< /Length 1902 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gau0DgN)%,&:N/3Y^Wi79je#*8?eX[8!0WKM/>8;gsf8F4H'Et9IS?J`L#i]hn,[T!g5gj8R)'X":=Fd^$jt*Znn(m^3!&eD>5o)S#9USgd7ms@"PmS>meo]Z":gS at O/hcfQlA"1[Q.m?,JtHR,2pY3/e:[ris:tm>bm*`o,V"puCT,Y4:4(9mD).'1 at 9d50FalFcAVFl.b>W2DHE2Y[)i3B!9Jb2ferb-qWOc]:\/*P_\5!]7Vm$#UnDs+i=#3EgH'D at PPg#@65If$;$DTGo3GgFu8OafL"K3aL&E&TDR=>:6f>[rcIWd(\_&D8+O?<g*oAmagR:HdhRr(A%=h6NT*@5!+U]`m08qYmF4Rbc$/UW%*TkUe"kYPqfg%'*)VK?KlIAfQ;\)iYhs@@9G):>s(`_*6(!.uH&=2Dlr6-#W,OcImj[:WJTl1L at H?IB"VflF7-aVdX,,12khsQihM6XL25mE<$V09p'3Ps(LEO0FLgo.9"e(79Q:p/&=UnkpOk^if^2&AHf at fgLVrR*jH/BR2%F1jiUhWXJ76Y2H"Qj]k0-c>C_%1ULb&n8D"&d=NOh62:np]P1rl7noPp&;Qk$^.[=^(@JSOaVQ_0es,V3E)9S9Ag#"gtnL_8'R*b35&5cbr^L8sl^ch+#V*#!kZiZEm1S?Ya27-e/q.,(^LKe"QJ.icaG*'Pt^TN"Gq/N+cKae1g/8!jj*%^3nGo-]$3pO64Uam*8\H5707S.&!61b*SSWBgHH3W)T3jHjI!koYX$bXehm0bE`(U8<Kp>_GL8$"aHEdSpAt?NIb$Y<E-pQ8)fe+5L#O+ACkakj'45)r!0p at qf*=,q0IKPSARugjt(D&Ag5GU3DuQ#p'`r`/k]>qeL5kPT!2&(c%#58E##BVVA,cPhW&`TJp:R*>@X^q'$X'6Cg:k$oEk;foLk"/QpT9&Mo#(*m!\)C-eIDMe#QOhC^D8@:gf3TnlFC"FSb%hrhXgeH#_9%p>2;L2"duf&%.0QHXm7&W2#3N&U;/rTI>_^'?(_oEWP8Eh1uU/\W,+LYq]'gLq3ZP0O([`Q(ICZb]6sAlf)6Z"6)nro1YB/*86!s'5r7A3L6[E`#m.b_=+/@FJ?9GT#II<5)#USnABlZ"$f_76J`2<h_J;q7bkTQ+$`im9a at d[2mB5db<ZM]a990D*57aE%doP=SPUMD<7KoREB[D>Q`#]CXI!iORC!#LO]+1c.Y&_CLOf^Io%e4IYQ&CaRiU1(el>7JGb_U:G9pV/)dKAJY at O&DBAFTZ.4VsAa,>b3TV?:YLMW+<4K>d'M:*R3Z?;iq<R4D&;nr06fKkQ'L*9Vs at W%?RWFqN8pFMcue%uU4K]MPmP8=)SH774=8CdIl6b7LHkF;rU#CT&%Q,OFNCV4*!To'&G)PlSn9X=%^6Z7AG%:MmPF`::)H->tK9]9Y7P0Ah+[1buOdUpR<FFd'76WH[m'5!,O+/hK2f,=!E,(AC"Q9kP(8]*B0#u1hu$Jo8o at R821RpFTcqB?4eKTaCS`BnQr,^r]SQE5drL_ahETJn!?E.X:bKJj/CrA<7T48_'kn"trXV/0pOoFD;moIeEhmu66r\bN,#L8[Q-6p75%T:b[r]kmq2MA?q8%TVB>UEf+jagC>+'X]<kU&neB#V!]]BEK*YH7Q9udF_E0U>6nGK>K. at d6<4bgVFV18pBD.h6^$$#7#6^A^/><$N1cuo*bZB-jq98]2mB'1Zq=T'W\>n+]?dHM14*BJBEE`'+<K0/,sFcG$>45k`&<80hdVu%C-dVOL`Lcepi!1`oV'h9.jV^EndEHr+[p?R<E^)824T8ZrkQ2XI+NVl3Wgq/m>l]+?#"8O23!">ZSgT4VJC(NZ`=^r)V"F(s]MdRXs?I&2=QIObs5eP0uQ7hUgW~>
+endstream
+endobj
+164 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 163 0 R
+>>
+endobj
+165 0 obj
+<< /Length 2968 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gau0FD3*Gk&cR6oK)HB&Yh+7MQ(f at 0k8`,oRb2gR;=o`TO'IO",IoS?PjVB?^NV)'j:&FHl/4H#I%n- at SU'AbRAJcs?<uGpICjN&)T.'MCAd_I<>mRC8N`WA::LSgo4u[1=;7Z.,FnY3cW>%'YMQp!3!o2IgZ=\.%ss<Es*KdhV6J<<gmCMBgo[)r]_KfGkB6.&+Ql25F]M2&G;!htJ%C*Q`*,f6:Z%)N]9h!s^L'&%FDa<<HNXpR6RQ1k=;Zbh4l3i>f#q'q)DQ_TY?8I,=TEp^MU=;aa8FGDdtmfsH?r<F0fL-^PY5gjX"->=s622ckIN1ePD0N-`W9;^K42?:3fcZCpV,BHcu779;RPNP`SJ^%>IFeR/;_jUYCAm2`Q9pl"-5p.Ii<VtARQ4?f;*i)`kKKFAEMr3miuHG2^)SNi[=i7dqHMkDh[E"oV7tS:TBm:RL\0$KpXLDNQ=L?ldbWmGi[Dd6u+QCMcU\9D5"NrT<;6^>BX2MZ;b&6PNqeOdm$T7r$n;"dR&.P/WfI5UT+og`;pRdK8"3InDS:",_"f6c,poT$H%Zck"r-Ec!Njj)QlL=S$maT*Fk(e6Ne!`cG^pV]pTVUSrI4Sp[I8DaLFE=(D*c<p<E9.lZ]g:d!kiUk4W.(rd&Ya"oeFIhGc^_R5Yq._UK5/;pBD-lW,h\heppZM=md-Im4ZWpF*!8Q8^c]FhprQiQk\.U.$RY\sMs@/7(7jHS at N;$3?n&)m,u-\lR#fi41K5?'^-Mh;PtO)CE[4b_1H27.Z'SiN3R1AQNMm(&\08f^Q16VFks2c.![/)<8VS?)_VEPKTgVD\RBC&d_PQg.h[U.(4;r9U>oiY=77c*>aH,L^+'IXXpon&gg%S=k:p7fZLKV8!M_`B%okY"ZH4$RiX at A$XW";'qQ:L!As#YodLBs08C^,nY2G=9'AV'Qc6oAe+_P#G at hDl@*B2ll;s_2V`"5,)0gfs$Dg>c3l at 5Y759-dXan>[k(sOb`]/tfOXij^O'T-l4(@ir6^lV!?S(l\4a%FDA.ImYreR4(!HjhmjL$^frK)_g21([p;0i5=PB*HAm[nKmEI.e]nefa6?/d/EDsc/"+=-j-GC'm$oB,**j]:YU])(Vt?VaabO42):#L<l1q1_[Hmi)7MX."h;%&XL1$0GXUD*`2Un)3Gg>dRWYmn*;QXh!qa`\]LDd%Ko0:oJV1i_,2"h%I@`"5[JQ1Hu."hB#"2?Y;tMTV,E5f[#0!ZSCnmN7O!Ba;dRSRT(-PKP(gr7Wiik:6:*c,8,Wdk%;..QGYGC<\LDJd3Ad'f*&AIZQr*j`#.R=FY9+H5B9\I&+Jr#qa7?'\*,[*DSQ`L)O3)1I#qeerM\gnOVP;%elb8Z)Sn:n_m)ue?LnoF9i\Dg*>Q$ZN&O9P:a/EcM3(j!5MX/$9c at 1g_mW]"6r#QN'f/k%!<,,TJi(Qp^j+oL,6D!*4X](/g)ZJAdl+N^eRCqulVbSj:A7_1q*]cfLWLN#PX,K1huOtD7,+/3,(UhcIf"!mLU"`"$*]$_EuO%B10t&4f_GtR8^!+/G!ZN#[BrB*DKt".H10T*Ed+$.#%#>C/6)3G at 0<q>r[FNRW]IUsPGs#Nf)h(2H at A-3_IWq=!JM\;JH3)4kfck'[WM#n0rM.l);&D5-M%l)o)VB&7+&'aHQ;,SmuJo$MjF;WVr_?KmIUrkNg#*T.2*+47hKeIOE9,mQ<f=JD'$ot#5ZJn,CL]ZZ]@aGN+j?'8D8?W6[E/]H_$82(%98SEJ*/?DE`P:82XGtEis[pda0TKM4;Y=jXd&::f^e;DI4`Gn'>j0dF-($)/_ZNIV_'R,L=[J`>*_KCY9I_VE10<QL(oGh4NN1k*t"G7g<./-JBEC[#1X:^FVfEF#@Qc[BjdsD6\c$EiI<DEYB4YGEaO/Yd6s at agmeUF4lr]Z8`8NT8fG'=UD>u>L:d-D(RFE]ce?!p`37fMt]V`bf1R/Jr-BL+h:Vd4/8])a,p1VKS9Nh)_&33fRI?]`b/J"$@bt0M(4u\'Bj:^jr!H?0(XL`R.,/!SnNT=RB(#R9'D*qT0Hln(@Ym%"nmQdG,D?o)54eD>54A>%LpCni/fd(LUU#4?2"0MlKX31qs%)G8U1l7rc`-nYnN99"cd$,+bZ<h06^+i[<Rq<[d[j<<Uf&@H.*<b1g9Q>%aIdOpN+[.\a_(B1baM\rF#rU*Aa:B at u%n?M2"rH=d"=`mkX`OAiK[TGY&:TA>EBr7&*Y!Mfl2D!.6GYeBj9)EPAEk;JI^`]nEgOi^`#UJNFM at 45Z**mTp1f*r46RNAJM[d=*>e$r;m2A;?]u4X8#A]TMIX<9cemWrfn4>%TG)[#\la-S;1^XIP$`bdU>Z&#TkPU#W?Hj1+O'WRc)=RMO)Iec<3k6F2b.r:]5sd"aG8K'N1WZaEdbI/9V[aZF$6L\7g(mcgVIKRn$'mqOf+MW.%5Zlf2:NKU(SbB)h[ZP/2^.>*XkX@?M7N8/gsiAVCH4Lp]$B4GSW<%^cVrZ7#:Au7Vi^mC=8_=:2LlWe=<hVkfNj&V,:%=J";?WYWk_gSc)$[7.lkmTbIf[dK50J[<joLbUc*'Y at mp5hc&a=.E#D./HjX$XRUEkg^q2(X_.*05bVF-$OPa<5(\!ZarnC/9*"P^B/c?9HYg%nd9C\Z1-%<UL9 at 5)pVGVK+oTceW@%s/5TP8Z486DcJBj*!^Upr!IR5f`8&lNuVZFhO"rM[-D-4[N&*fo038\ZlZJpobr_"bb/i`RW'ne^6.J5Zp/1m&f!?lW+9Z8?UQ3?HTJt$)G2Nu\J(93T-C7`>cmI$^tss]o7$GS=*d-]^Z7X/#sC0s8F%H:ie\JW<YrC*(6&oWYZ'+ZSX!+2Z&!9?RU$<T at ClKd)pAP6:dYcQ,[Bsp]jMiEJ)@]is1W at H+\0\,,_D:1>=?<a!Ahg<RioU$;#c03RfmW47b#0_*d.gA8^`(hHc_5[~>
+endstream
+endobj
+166 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 165 0 R
+>>
+endobj
+167 0 obj
+<< /Length 2713 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gat=-8TPi['#*s=0Y(:\=lAY)?f_i%8kLQdnWRua7IL6%J0_4a!Bgf^-'eN0H57&$%YfjN=4r4/)SLOS?T36&0O+JAcXUG/+'(15^7O#!$i\lOaFei5jc\8?k7 at CdO,JmnN4b`h3kbcY/oGj6HreEY'Eq-E/dc_Sj#O:n[GH_Q>3*%UT9lVl9pm^-G.h<1;bO,BG*HW]RA!im:tO#9$ba4/AiucuDA;KFB3Ahq!]m:!LZeQ\n("^Q-+Er#jF_',=D;4o&G3a5_k$5R+GPBBK+1#sF0+%-.FV6I7IP(qlRoP+\h6B6@?E!P*,SK8\tN8]X@`XLf'!9R0rVHoOLfd95(.(N3qS7\XaXp0 at 4Oa#e4=7]G!b"!fF&Mt3B[B=r^8DX=h,.k^b0&6Yidu8d1^"obsE9^A7j(BroXTXQ7Tr3f"AX!T at I,H0U0mke_n!R-][hh%Z[QdASq.[$BbQq7!5HX)slE79nfC-%=2!)M&kL`H?fO2V-`V[jG at NqaN=0YbQ]F3BO.F at 9Hd<uK_kbF$/6Z#E+YLLgnI;s+\*(3Y-6^@NCqU"2\S9^mp8&n;Kk<AgT>.DA_2G_$<`;R"e3j?7k(?CKGNuKUH@:^Gg`.;imjA:>Zp;$l-8g:`c>,0h.N#+F$iZbbFa6se`n3pLkS5dB]93lDU3+Z^690ijL-HQG at oL[+n;D"^T`<TD7)ZiK]LKh^#J?Ij+3PZ@"uK^@#3[]j6]WK>=&4TNdUuE5^qh9X#j(aBHZ(2]rBOPR\/)7[)"UQ_.YW\XH3S5\H6jFXi.<PoltKNnbVtL257D^XO9^OOStUhM+H#%'p/;,eSA%+q0I\+crC$'S?#QKfIJV.1M,<74Q at oDnUPesF)GkbZA]juj,erB_^=lc*PP_Tm*>"l4u?ERK[;'>dgNZGco,fH).D%;Y\g*M^K#pXq^#7/\u5COdSp*:k32#-C$P1QG?]PqAjL(Mc,8o6bT]maJ"Tl-=22s'[l)2"Tk_ic[\oO#H6M?-4=o053VtqNC-0[b*YN,P">;*^20].:QfrZFY^/Kb>ThSKN2Sr!5K6fM%^@VS"X-^5D at S7f9T!h<@TgN?@\C#M"kG6B!QWjA+qCF4;OZAV]]M$QbA)3fOCc$L[*SO0cma'2CO4n2.7Pamlk%L'#!Qae&8!47a_4bML*`('#^OGT]5%5B]_Sj4O8_2&D-Jo<U:CM"E?*"MI4'9><iNSF3OGeorStlWC\=>Hi]Y`L(o9%#gCXDoG.3=q%"Vd`E;GaXO9eeo^^\R#efU.#G9ql4W\meCD*:k!+]QNuIF+&Z6<u7LaF6`ID]';^!tm\X;UBU`%l,<"ROu+u1W:J3`I1'(UfQOtV4(b28E`)ZfA%S%DbH3N?1)!1Cr\@kUg5p-$mS8."AWX9eEiKS-6%]2[8!>l4\3Z0og\4DU`M2C2DRSH],jU[dC)=XOgO1d:u:E*+,/6IajVkIC?5HhgbGD)KjPdZTu at LG<`FIeb`GEq.1umDSb>uP`GFPEdJ8UQ*\hp3USujM[=$=f3KUprDpcg__l at PadAntYT\/nl09%HDOChlW)5b.59Yg^t)93\k'F4KsmL%">0KZV=4S_7;4JYP5fZR5rS[6A<Y!m/Fm,JbK2<pL<G+;kIL at jM(-48hSTj'?$A>cS%6T54lp;R at ZL<<p^bS?=d?/V;Zep+,bl6*u\p`AQ*VYZ0Kh'/K0?8U(^B*le^]ZMbL5QlWibeQ>T10T.%P;Bp(C"jjOo`9Z&r/j$:'mDcOeO1muD/\T1a=b^'jne?I1k\0`#ET,^5qRObQNc-)(i&iUA_5C35jIIA7$]G-?a^uD$hL80N3hob,bnk[2fd:b*,(p[VU1$tjcUrdQk,L]^ed15AsJpSfYKQ-L.Tsln<r>r#2)DUV\Cs??bLIiZ%?u$C%[R"Pq^q)('6K#3%<sg`YC"/"^>HdTT.(<baKl<JKs/^S:WDUVbU6?mo!l2S"jmE7kD)3TH.ZJV^Lt#'V<W*#>/85%aZm501B+DB\^Ua!oeu&!$@>ba4HECUc7r9iqp//J")7A]3rTqnKhHfMsa-sqW`Dp4q?k3%Zu]5e\=04\R.0gaG5`Z"#ZhmBq&lhNUpV_2eqR3f+9L9e,)NFGPm9Ld5W;)klLnLmf(g06<B(HD.`?l]u#.nDa[2'O[lDuN\4F<<m2K9`/;Q;M.pieqDsqG)AP'`<V32H,K?.E"p[b>*(;Nr(+NK)4,-[S[#LIiROjp0)^l996WB(U/8d04qO;&"&b4ksR^tnc)3&IZbQB+1Ql?8F->c:(Z=^L"gIQ"k3'2^+[3d7d%pH4<?@r3YENhZb4b at -m)C8*qIkAa=JdfT%/npnmq=YGCN1,IIM-8k:gD(3o>>Cg_L0<iR[,*>IfFCA*"f!.rVAf/0j0j,-(!9?hO#1-^BZd\r:/;R1:NlrKX]_STBad/T\kQiC'dp<-72#X'B(-QgD'_gXd/W?<.^*9V9:g**`"0>OWd$JhS!AVDd.X>7IR(1Hh4Qm.R7tTN/#;XKNu<<t4^<odaf1OsfRY>Cc%)tb5CGf7:k*RO/3a)WoAAG"UbrFcTgP'B/tLK%FQ_/XTL"c30],oCH3#aB,d+-c05_%q+d2",s/&a^_$K.ir3llRSJl(%lOQMKlWO8:\RuSj4*Uk$E)j4>8fiU+IrL`93^H0hd"dZ+om?T.D^i[8!uU<_qoMQ;X4>:8ePEB?V^23_^.Yd<~>
+endstream
+endobj
+168 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 167 0 R
+>>
+endobj
+169 0 obj
+<< /Length 3111 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gb!;f>Ar9;'n5n\@"JHb$Z!]8n<.[tE1G0jJkas9>SKV4"=%Y,0d\mh3OJ,grU2l`8b!dXj#1$3U%X*qjNO$5q9EhHYA_^8\2jG4B!Wn95APWDp>]<:1#!$UQUR2^qs]$mg,9=ehl<K[3rA%*p!)1\D/=pM[;Im82Cs#&)u^l$nMlGS/$oDu3P<V=^R[$-HXZm/\p6M8nnmLl<Y3<qkC,>rjt$Sd`O='U9\"/'MW/UqHs*Y9EKh6H(.BG5n^1*4BW^+t4c]7*Y'?X7gpkSg_$N'kU!1q1e_/u9_t>uq2cX$ta)RfQ>\%N^=sKn:Tm)j`YZ:s_,0jF7SM2mc$*g06dLF)1R1siu`c3;?ePr1n`gC2X0PWh`*LG58SS<_l at r]k"/_uBHDpN3iU[=\e64.G4m.Om.s)Uf%Hd$K3J-RY345.8lHt#BT1Z665=W$c\C3O:qbM8]H?Mr*QKC?)glk]L,)+`t%7n!0AabN9M3`Q>=<VuD[3BFT6;`l&H^I3>&!]#kR((19:HJYTW+@]WeiYd&4J1"Bt6'G7>]lT-8S[]:'6iX*Lff>)=g^n1G,WmgQM;im7fHG;[_]IVJ%NZHWZcGa<;>_nSEH%d'qX9Qc_%X\akI9:G59c#@%L_:r0sZ&^1JlR#$aM`&).\gVOi;a0\M;P)[j$`W!HS]S3"^e)cJ=jJ[V:ch0K4SD0!fBsX&5>h3Oh=j#B[uV/[o26$#,Jpp7mF4Fq9A/ec7>k%)#U:@N;9/]ACYL+<O!XB'974i*K`anOC]:*3,8>C!i>bNo'YoDr-JKG=AN1O^5"Sb6O$+(@,t%.<S-/2Pl!$)kk6K6ZT^ob^ecL&[r^sZp%EKb%e6TS2#E\cZmfTbNHBK,P!Za"k&B2=MRF,TIok!oPu=@V:K26q_t^^a&-!=D'ebCBZFf0;Ug#]EB>TNGX"`=K9tX+X"2Ff<C*IVnHRLo$EH4Sq$Maa^QXJ_VC"r?T9DhgNWtN/b,B.Lr*HqD_tIgpM-1OKiI\/ZRdu5HmR14#U7PHQ,G8i[QPmcZCM-K1mJ/Kped.k:9^NX2ZN0fDV)"5Je;3Q&=CHE#EB0r0g-bq[dU?!!KpHDplB*2:+%MDGRR@&kLuD1)JP%O&NLm*Vj.L9FUt8f8qN,.j>1cq_8UF%jSl%$QMXI->C1(HI&XsUH<[85-)S2,aQ.8(#V2<bec*DV([B;dHK\CmA at YG\jE^^%-,4P<:'Gu<IEfPb_O.Ums)6:9YQdc.Da__ZmOsS>7`O%)YPjR/J)c:E at _Q):*A;(V_WT4PM0eR3c2h?S.=usV^`USFF8`jP\/e*0r<AdeRjf`5_K$*8oX5bm`d,/3GLbELq).+%2HqOD^%>*_u_<[N)q*t^\^RsQ*KAr3C'a4D5A#j(7*uopORSCA1.RCPFRS;:L+OdqL?^^I<!.']_V=dJ<5f0]N"Q+ at H`[=iUL-3-))(7"kXBi:sJ:XZ&VhaCNQn&WZF\/?:V?@3tG]:Eb<GhqFh4S"UM(RR+8s:"[mP\\, at UchRAks^hEajmYP\JPHcCQoW.`7R&kX+k[6<h%W+k+Mf&p?^0H+,LZe0HVqC'optpYoL%`'Pm!lD0%#oGY:l*;$hb)@0 at m/j^[XB5'-;,Oq,7N5`?t>L\Jp7#*F#HT(KTG_/I,K(83IQ-=dCH^WiG"f2 at 3(0WtCG^Bq&#h_CUW_-`C5[LK$@<G&'JNNQ-P6,Q`6O;Ug<1`%/6_ag\NY:EFKO/0N+phU%D+u9VR at K8-*jfM!!rGGq_*R?SA(=G\J!5Ei=D2Q,^i`62TT/jJi4.[<YVK?XP9=3[<Ea-I+P+=Tp0(to]dcH;)QaO52J21MTu<SN[HETf^lRD_J4+HSAerLe^Ng2KI&rI6OG0L/EUea(1+rOWXdm`=c'oc3dn1:@Q\r]k;b&QQ3GFZ""=bDNEq)C>G1"Zs#R?[K8:k!]Pt4iT<&5s`X/;7aYg(3l=Zg7^PHgekS[MK7h4iU9qhlFr1N_;[]Y^8pe2ZtB7A(pt=6soP^hV:u6>;4dn<HQ4p7an'2jMP-=sDT]YYLJK`dFkEC#=.g+ at F]NiWD1J#cd&:>Zs6-1c%>NVY;Fdd,q'eeY^"l-k]H]8J,:;Y*1]1GhSXRD`nIX>]TW#"g*eOTD?CLJ\qJAnDBB9UR'E%L\0%)q_D2PbP1&7+W,Bt+U^GYjQ1F-CsS_6bh/j&HL8gW$++Tp<21^)[7)#iJMK*'o;%1g48p%d4^_'t(MVI_5P/WNYnP<\[aqD1-t4nAW0fY0>E??ZK[sRl/mg5t?c3s%<A8F6(/.`_V*6T`IfOE\@]bgTQXZL<g7#Eb*#F#(<A/N1Y8,gV4?N*n?FtKfc($bf)c&P(>UN6=(Lr*P()6OD8l!Hj-7<?tD.]<cLBeYYZ;m`)bP@[h3W\i,M/Uk#KZ:oH+7>0qU[/A3QVEn\[]t4:+uL"Xq\*lbG!9A"*j?/[6ZWQN*IMlUPW=GU$hD5U81",Wp!q=;cgO&(IK&IA++@;n,Io:i=h6Y:/Sn_M"UF`K[.(U?PqP"*fbqShP>SDb'Q1uXgcXBZJGD^L=rUnj24hUj#B'[\r-\,(6m:d%-i/ss;I?'I#YuF`O,)14\=b)qenYnf:o[jF"JYCmiU.5!W#ED_+!,V:3[8+*i1npU]-AWqE:eJZ82u,ELaE7se33#;<q0V>Jn+9oaBHarfF%_3k,l=%bp)@_^Wk4l3,eq,4E?1K*5q5uUZ!!oPi'7WYOFT%^UkNi)fCZQr-^el1Nln$RdrfOQMkCal(*&`a`CVO4$E7$NM[dMVF3%D4`>h9YWVPtDdGXm\Qb+sA3"OS=k.RB)SLLjr-H/n@>,B"d-&(_*m8^GE8_%&J'HW%l/t]lFLKN*h\SoB4i8(f?#iC9N2*Phl3 at doOi-\jWZUCuNRXKIHd9s!\(W2!o*)ZOI!0Nr#?LOMaBfq^E92':i<61RLS%@a%HMK%fprTHUD6sZW7/d11`tq5Z]>Yf+r^!S:Xq8A1k6?VJWAuIAR;eG]j/_>Iea[!3qJ"*F*NbZ`Su7(+lF:EA;<>_Dr;-YCP)q3I:4r,_FY4!"qgGn%ZB%%V,ug?BRqG<R107jH*dAgUR'_Ve6^o?48=3!oP_:&^Tm~>
+endstream
+endobj
+170 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 169 0 R
+/Annots 171 0 R
+>>
+endobj
+171 0 obj
+[
+172 0 R
+173 0 R
+]
+endobj
+172 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 388.956 261.181 398.955 252.181 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
 /A << /URI (http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasource-examples-howto.html)
 /S /URI >>
 /H /I
 >>
 endobj
-132 0 obj
+173 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 206.076 457.72 196.076 ]
+/Rect [ 84.0 251.281 387.948 242.281 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasource-examples-howto.html)
@@ -961,255 +1297,482 @@
 /H /I
 >>
 endobj
-133 0 obj
-<< /Length 2240 /Filter [ /ASCII85Decode /FlateDecode ]
+174 0 obj
+<< /Length 2311 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat%%CN%rc'`IH,U#epN+JJIW$u(9i9nsMO]!_ft at 3#EhVJRn?WJZo\s8-)*Q?B0'[[-uKA;T at jn):h?d^PFfh.6/!jH;?S5A+uGqJG;=)0)4coAMLtp#gH;"N1b?ME)3BYG9joB`(&8;KssbO'HAb3A15VNot%#FqQ_X?CX'?Q1Oa.Rr:uX\sfpkd#I?XRhqTtfW0p[B+NJ]kO&#ITAJC1]QF-m-6KjK1-KHD-,e\b6Yj)WFn&LO7<?bSJHfgXK$8.:W,8M(TkjKmjNiFjUPaNX+h8H)mPj%^Ng#"3?G+>WbOou_H*Zeqn*[WYmVZd]B&+JU&8QI>h61RmclH-8ATrLIT'0,gg\^E>-*,ZOIAg#9Tgf>[_Zm)kD(7MX;j at aII9-bQFEa4e5^*a;m=V>t6D&<F]"=$<^\`OC;$aaZ^,e0r at 7IP9+.Z\FIN;5'WZ3h/*Znr\':f\$*:N'<I6A-.56f:&WV6Y"kXlmBi3Vj'N;-07V.;gM9rU3_MH.u/%9&G,X"K7ArTnFo#d<D&:JktI`k+<Z/(Z/Rh[Zog7Eo.sC-cN)Vb9KeGufMi!_>t)94^<S=T#o#]kM&s+(ZTeJsOgEi at -U%-Rm0=oRA7`-"=#f$3rdc92#'rnMaeTL%24.A,\^KGB?9%@.(o"Wqp,`?jG7-DR*X<>nf$-C<`b/]Ido&'\aeXE[g9_B&=<0]C]?]Z*M)!B,gdJV6j`f:TrTg at W'r6&u"Ii/b]nOBZ4T3%Rn3-,j"g+AZIZ8%MKJdl$;WUTLi[U)l!'2OKR7kH:NNVikRM:mCC7dlN\[A_fI9m-:$86`nA,20$d2mD^`X7(7:>R_"k(9C+EIoT_'Gk$cIe"O\o?<oc&7^q:m)Q)?oRA\e[;lA9EU8$"c$SU0]D<PELM]U<QM<W at Q0i&PY1HegZ96BY?cj`O]C^3]kTt0SrAGaK[*p(WIo.0(%'S!sI^&l,)(!Ypl[";9FTT:Qbgl:Ji_Q at Zi&#a<[,R[*l>phq4MeY>eM3A!!`a7&bko$+6Hdc>1GM?Hm<VJMcT!$'6(ig_^+sITk9V&Xd,p&]G//plN0S4bLg+g]3R][jkF_*bhUlUp:FSahWr!$p-&bp)0@^1UQ_B,u[8OC3%I*--EO7Vpq58DAV`f$V8VkH!-K1OM`KF at Ci[MJn0#u1hC5o8q'%BjN=,a5=bZsM&t+HR0g]ZLTROX$C$ZR2%DIg4GXLoX+16oK\3VtN71+1NCCL]n1''m1h_d`USN(A#2o!Get:k)N6<.6IQ=U!>]M1QV&d<3AV,[?.7cUbS>f!Z'F8D^Ud:*-I't9Gf:!`SH9iO5/2`.Gqq\0^[hE9iNXAbt6-Y[cA*61_XT9:$6gbttAlbY8o<`k=3jTcPU_4A_#MUUG\A<UkX<c0<3m^,rN"6>1a,H*t%>l_;'=SK6\>>=8]Z7Tqc5JPW!"*j;e at H:A4Q:/H8 at MY&V#74f at 3gVWV.f==D)@G,MT[qGi>$cO"7W"EM5*4R3*Nf6:oVGnj/5WM1_=H\"?aRs<N%-khcL>Qiu5L3$nH95G%^YV+B1c1Wal4fF/CjiH6h':IAi%/f#)kPnmP2&[[.$+Wjt%DN\uT*KuC57c<(h!:/3om?4H0V+eH<DTG4X#Q\OA&68HLQ+hdK at D@6:tD8-T2IIQ5XA^gPR$bmEV,rWf^cAs.^7Af\.VWXnlq,!6mnN#H5l^JF(7 at IDnrtiqt9eN]k_Ri%Q(4.s_c+s#FXI\h1!)j<Hh?]c+IXVRKG at 3AQr+')!Z1?"A.3mJW at 58[82^Q1fjoL2II0GD4JjZdBe6#LLDLMEuL?\_r2H7I;3qmI-K2NgP,gYF9cePot&N"lKYli[G0hib=G2q^ZZ":0s*`O4qS"m at o(b-^OYm+;4O"o[I#TX?/\47afLatQ]-m3D3FrDiEZA>XSWA<QSdK:OF2s&9-(%iGVb6nsB+$@XO6L#5tTVju82/Y_6)<5oZ/Zda:R`j%2Hci)dKh<RPOKC[%JA:+Q"hPis:)?Z&'YJ7F=oDC)/qaXn:&.?rkT^:"c<:ZG:^PXO&:@n67%07>9dID#l:/Z/Ht/Ca)k*gS*>&qs5$)4rV_"Q\$Z%5>ijK'qNA2T0]]n,f7r>;sf8:"WMOAg8?I<(n0Mi!3II=?t^R")U3dgl[#sAnerMm&,\>koPDgrX]*)D&IR3*d:a!'uY#[4(C9?\VR5M?S?[NU4#`ZORm65P2q^ikgTp#$_Sol*,^&9g;9#8F(3#l~>
+Gat%%?'!Go'RfGRaRV*E1ujE54!,(X0ef[o+;X0U[9?VPe$s7<g:5'm9E4tI1eE$7i\h/Tdkn=N5L)Fd>k`+s\XuX2:OEe1nBY;Y5Q*Ks"6+Y>AL-to_Vt)\aPecC$KXEE5#(IBPM8$Q(D5e(k7\rg;U:-ue4UVb\JHS!iIZF*[J0F!cTWkSX(%rTQ\A\6Vm)TX_k+X*Eikotq)<_hh1""+q=O[Zh3/XN]mm]Z]An(aVhE$n+!Z=Bg\j&'5,qT$cVGo6 at 9scY_F30C?,,L?>g>rIm%<)TXrPWZR*b7A>Igc`$XG<>grS/HOdTOZF`%Ie]-2CHA@'mnP[=o\^$0]Y<]PP`..K[UmXKT'M<8dif'-iis5iD*'Xu[T^FE$G7\P`\R at so0I"/_0ArEK8n=qa'?*34LD_msIC0"s)kNR^j&+.3h^:=YK!p9',6t%&+6T23#`E!`*V_9Muajk.pZJ$jP[7X6H%gmT(qFH$FA23l#>j_%P=2nf6TJgP$fCRGu'G"`&Y#2iu<LssU28c;Ui/*RV^3t(_8iV_8O8)h91J36I`#+7<^'%rTl9*3<-S1-,QrF"dFT%$BGV!k='Pe?Q+lrX!X4'LP_g!PMN+7j at M.)pN.lE\<PSBN=Z@%3G;/"&"E:O1:73&e!biT\nd<fi7aK<`!5-seuQO=#+b&t*o%_H;ZHpfis)+:^^A<4gf/9a&Sg_IeFMTR5KZ-kBJJNaZd!f7kQ4s;*ONM2-A[ud#afFu+L6"3co3"J0g"2^5kfOG5]4r_>,Q,0P"Q27h%Z1P[[.Wpa"H\J]3co/1Q&uU)Wo!7Ac_)/tMhf%O%o at 3FG"FsWPPr")d3T_-k8hm*5MW+3`7dCfC)+_N\$\2n$@2ZEfW$:cdFHt"56Z4`u;;Si]kqMU*29Sl3rS:A:<bH1$bV9s(bpo4)`iVNgJY$>c`JE&8Wm+!P[i'4?N6"U*Aa!Lq5A"ZZ,F5O[7.\jD7_`egI>Vo8-gAGFLc-#%2/6%T$7?cO,D:o5.KBZ<M_g(%biPk[>>`$_E1!u^b/,b@`^W!3bEII0[&kah6KP)]gL&25AX9k^k%`NLMjB*jl6I_uJXiNGjWBOK/5f.?!Q/I4!C<>-F[66&&C_r239n]cqtjGnUa46,()J)=fk%"HKETAX"2cF$;!:Vn`7DM$J at 2JAFhbCXV9fi"#aYY=T!Osa0%$&:nE'bM;:`bZ5lphHQ.Wd(Ziq;3&&6_!g5&5T@#D;H7LNIe63i<uXq>.;nRqeKpX]mgI]jo(2$u_onj2;dj#sm&1:)]ncsq[flf=g]RL`:QBX*br^_IVh23Jm8D+6'H[K.+^%uXOqSs]b`E0T$ZoqO6T3>Fpbr+D`P$OO6[jk)e!j\?VQ`3ChW<"QFPHg=RB9=Z(7&aGeI`g/kj42G0Ye)bIRKu3]SkEaVb*Pg1Kg/o1$e\AJY=07EJJ9MW5&Aro[Fs[KFp_%Jh"Eg,^bjTXNkhTCm$\7nZDN=t/%Q_pX!AIe)7M9"B6"5_f\_ptW`a*2\m(t$0 at I]ncR at UuSYB:=^44%GNq1].[Eu9GKm1(.[bO"6 at +Ya4jX=UKDgN/97jU;:\fN&g8OO3$U_D1)#f#D#qirT)J6iMR6%Y'Z%\\pp&cJjXJdD]88/mrHeEh at 4hbj9CSN?VSgj5nNB2PbC2PZep3TrdLo at MJEiq-YoRfP#YA;'uK"mNJQa`O3UZ-m56G&[47D`ff%fZdNH,kSErYRE]Sll_qfo0I at DF*01O]iK=L:R&.U`/oh1jY>fsXRgZLu>ik*8X\$c?Qng9"NGUsNIJA"-V_!X\IlrFpN)M.:\/V*e%Z\oeZ!b>&L&*J)@C.LiBe2,F[F4lf"UcaT-tsEJS]c>E]<1P'fl&7EnlqY#gE\mF.E@!umV at MB1Eh0TGG<IJ'gg!bdn8qFDOolhW>rrlFqr=2fc(nN at oBY2b;r*\GCj6`?$C(?8l%"4I&]Ln-GWTs&=/nN3uq9D6O(t,&DitC=R,G#]QcCfg1*7dFP'Q^&r[aXRV3+5Ca9,0#R$6-FXBZ"U%IG6e"!9HjUq/]_h_.HDH#NUqPPJrj,`V,'Nc]Z6fn=Q@(/&?U#e5](soJM)gbuF/MVE1[W%5b]p!M].KTrUq$:CW8-k"QiC,>(=LCo7,(E]a62sbc'%YT1YItKolB>WJ0--\q.3%MD%$H<ISojKek6^')@6ZhO00Sb0J2e%/IPa9]H&G7!s$RWB8qb4u#+p>=<%)\ArDY++Ye0n6XP9A$0sjj[Mdhj*_#E#?M*pi at 2-A8FmCU1NL)d45k^D8[C8iOB&J3[0JVU.orW<C4du+~>
 endstream
 endobj
-134 0 obj
+175 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 133 0 R
+/Contents 174 0 R
 >>
 endobj
-135 0 obj
-<< /Length 2498 /Filter [ /ASCII85Decode /FlateDecode ]
+176 0 obj
+<< /Length 2825 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat%%lYkN9&HA?:6Hb.\"X7."gKBUK8_TlS>-+8&$s8:9QK_MN/@qR?a8YoUofUB]j0_,r8j)\4GON`JioJqTjN!*EiXl)lp\iRE-^;o5 at n"n$T>%@Tg7B5phheIe>K`5V-eF9?g&&;p_l(KM_uXUf3UiYE2"ZT>78?*](gDp#p",[4^:!=SEFAAW.CQl_[iE'ohq?2Wg at 0]d&'M="Sl^<[o+e.HLZ]IN'uPprG$19<10%QX[MF'3rk`qKgm1*hYL)t%Bio2PA:n"AV;F;m1_X\:B(ROs1.91YYd!Mq0gjf&0bdP!3.Acmjc1&aE8njnAj7WI'h05NC"R=K at I)H%c/1[]8]d89s00Z@!\jTZCo$WB<c9Z6'F#iJE4C:+=iIRp;9-5;2%)4D5t'^me_)d_c"8'a-gZ$VAD72%?R7?>Db+_I'g!c(P:4eI8oh:bg2Nkf7-$@(au$jThN_PprqQ')kMM<HK,]DN`38LCLJpMX'-D"`C*f=c]HM;d5[=h?jV<[H0^Ofm6MFU;K/`u[*@XMj`FQ.);[<:F8V2a2-eanTd2nMt!$A",>>X6JFm(P^cqO^G%85NEecH#.EeD*]1q"Tcfc>r&7Jb)`f.A9;-j`'2<F>pu;(HTK')@uU2##LiJ!r9\L7/;l%RLW_6u:!=7K1BL_ePU$g\S:pBKHeS2a.jkJ*mq1G>2a(0SXe\f/NT`LEgh>A/!*Fb#_FQa at 2LDg_e-2fI'uA2p2Xt;)hisf`;6dQC$`lGFCOs*(>2T(0$5p[,m;qkrMQ.#q`a:b\e+-^Pgkc;fX+_Cc00=>e\a0,a'sU0_;JT0SkmY,K-uG.lmTB(*JQ-ct//B=@C3t0nN'gL`"S3ieHE13DordYJE?HVpqW8_D3Pe3- at cYGB/]W#M(k&h-oh0g[9MFmupu_>B5 at nLKCks@e&OlJ-IW_K%Ai;I]seD$f^0&%?C[JFRb0`8iFI]U at Ejfj3NcVA3\rZaZKokX$V1MWl.)$2YjPt%[A!I98T5m<"-C;_qE"FQAL9+)^3B4cZ?s+a^o`OYkccn?Cse""?'XEZofc4!j at f)p4Z/<(_cn\U6>C!$7 at J2_ENb#+DmA@\a9%8h?Og=RL4:-#Gr;k/Role'&9/"rLW2b at +kAFVQoK2dDpc\]bXaOf;%`Ena:R_k-AN#f%.A+D,JiG=CT-.0X`K&+m>Go[+o?m'Z18UMq!b'lf!+4[SUBW=dU26C4as-&(!GhKtdp#a:R\0^+'\d.orrFAJ0n_jp.H^@JN(C]Iq,<E<m;b>mag7`^8`W!EBVTX/p>\r!]j!Xs_k>U<.0^GVOfC&9ZXm/Dqg-fs=gF>r at U+GESd?T/*E)>dg[b\C[t-TVK>TED?5aP`[Z<'n9!R::ENWDUXOf?L'jkZJig=+n8gE8A3H7H7ntH'IR`r8g=s7Znh?N,KsHTOU^C?MF&!n6SNmqO,U#h$8VIi:)`d'L?26^9_Eg[*AfAm'>6Xob:B*pcA'BN3ADBZ1uWFU at tV[c*-'Q"ecmAFWr$oaHDK,0>ea;"cs[QRGXUL4`H?m]-N/+ef>[.#:A)lsmTN0WC&i?!!;^\n2jB;B'Y!c\<$E79g1>\lm?;17r>V.Zq`a_\T!V?c(H"7-FS at 2;i1L(m#@iIjq$;&lV@(#EX#sXW)n"iq[-M^&`9q3r at .Jj(7="KufT!DmrGn^K$#*T!c$S7Ia3\4>i)e9nHBB.K<cN'NU3Mb`, at -Nb>oIMZO+BF'd+*tRBi-[A5\R_1 at si'"h9Q?OoY8(bg8&IU at mV7=D1?ni:oe+1R1o)kF[r<DWW*>8KdM07lPH\uRT3Md$9kU3mnnO\MlhEFo5j7U';cul:?E3<p!=e'@^S%k8l^!^3NT`-`Z-1E0 at 2<bpr9,f9a0@`P:=&W.a'"(BZG`pk;]kNP!!Zh;oDOK48iSMb7K(@lV*>Gi^u,Q#.C^i72%^=*72O`&qWH2?ai0P\nt."0eOX(-L5(1eK?Mj?HOMAZ0MNhJ;8P6#+Ke6B#H_AFKXuERu<rfc<TV_3OiE\TW7kl]6q$l])DG]R`nXMI?Aeq[+O<Vau$ib$%Z!^hK1AI+1N#Mf!"2FD7oafqZ.d:U3X&qX#He(>mB4.A;GUG\,$&'Pq!TVHM2<)IJWY65M#lHJ=??)dmLiAmCGAp:Ulo(Y9rH^U&=t+5fnaMmpPUm=uKm;&W-;r+8XoE51Lop]?\l(__,eeq+M;MZTD&Z<pb?"HT<%rAm,qdk/2_Q)2)<R;pBc*-DcqprEt)Dk-hQ>>GhGkX5J#q^G)65%oc^d)!nBGcVP3'3?DUC`iP0%2X.n5[t8j]CjaEtru"dXn1Q;Hd7"JHh+]Lkj7U,7)WiSVau/`PSpk)Q6W4N\+;P at FQ8O0/qp3K-\C_t3)\._4.1f$ureM*hG;$K"B.+3r;5jSV1c]1^:/r$*#JnK:MB%5GHA;3lio[h1S,8JY)u>65"/(MXNHEZEoq1J<35RIXfU$FbKl;N4r:U=J)G^,>Ql=tB~>
+GatU5>Ar7U(4OT5d.Uo&!YMR3,o[!>-CmSXl!S+l>qE4d(sk#Y/!2-=.)j=-qDjd2lVJoA(Y_,QPXY.J^[n#F:NpHDn':nlo151_+6WXmoT-=0,bC4r!Qgi[O5S^aj,EbQij?p4T)E^'l0`BX6gg`qE:2:i*c1Nj&,uEcn+e6MS0ZEP'M$I)f(mXIbJ1RT8&n=d&!$L*>uAnFAPKXM>-49;>'(_UbB6b8XQZ*rEBCj=[H/&h4'_OFU[qjUme02C/1*ZQ8U5KdKj-Xi*e!EG,`%s9"`4qKYn6D>]P%lT-Wj_.L:=VI54Oe$DL\B\c`W.;9=?MOO,?'$Q3Ff:Nk8PT5?^HM6BW_=KF>E9o(+7WBp+l8IY[<8Hh6KJ=r!f4bGLlfoke8[M#+]s7o!frkH?PrIeiLRq6oP1K$EBd+KgC1!D1^:ghoKkBihj:SmRq8>3otZR4mE2+[t^`C!f0[[5/D#^10UB/dm&Y1bZ=o)oa-uV,K7Npnm;VPt`%(I64Pr']b.W[`&#,J&PILXIGV_41",A+"\B6)2V6/:Bu$i7XC,4\87au7/O8V6*[-Y[i.8YXk++,k;EQ[VpL;[2Gnl8VD80f``?]TpDccp=1[5dc28Sp6J[Y>]KNqH;@sjuT41`]nYo2C&-0S&ek"L-+#lElN3i3aX&Wt$U-]M!8&rBZrcedkM;PYt9p"seOp=aP#:.V>N,k$U(1`V5!(F4Fd?%)\fu3r.(Q1`4'XZRN]XnG:03NCUGHZX($qQk"fpa)hZ2t`[8OPg[p%>MI2gno7T>`EHhi`i5'uqbS&uKh;&a at qhV`nDa&3Dn""Z9!2'ghi[&tHZA56ULaqKl8LQ<*1tRk_Y=iW'.&]l'jMfh^ogalT7uGt4om9(58fgO;CuN].FJ.9B>>coM!/gM$/bdgCs at f8ae#e+6kO/SKo]U"gjWT"\;`L5N2fn,!lK]V6DerA'/VdC,.kOg8h,cc&1?DeORFO@\]GfcgI?/T\PD(&q_`C1R at YL@Wmf2<94?1aprkD+6cF>+f^:OB%<iWB9s8]k6lOR(5hAZ=:8pOk;'`>KELP<L&W6!_cO52ie,pd=!)L7iIpB at G,_<G'X;^Ea)QYWA$T:aXCYD6hS!OGJ6g#@n?DTM at Pk++b"<;Pa_)=%brY&W:RQ:S+^:m_[$$+M;VB1eitZrd-oSn"r8trX+hhNe]H1.9bocgI:)`YI(c-niGOk0UC:gH0#8![]jjU;nLt8:+`KplLVcG!OEeRPa8KC-&s/1\o)iY5Q66RM>j;Gp^/X#EY&6H]SA0W';<3irOOaWuaCgTW$r05I>"pW)9AG=c':df*[-=>;bG736O=5=OH\=I7+Frc$;O8+[+]diF=-^`5CkjLqSmtcD09u^]"3q48afiOe&c/451.%C0L[CN$/BUN(jiVR^2Mo'X.\]Gm0F_L3C%%Ul`hr6HT'Q,OjmkA4ZjmS?2NMI^<b4 at KLTE:T#"HrYm<W"`B'0-\_gYN"a9j56DU3[.Np&Z7(6tOdhe at bb:OtQ;1Nh?=p/Uju+egqCd<t+q+)tD$=FH4Y_\,&$iduT_L2.3\^]US_Dq")rE5G1jp.L%e"M$n-A&WUkK9%Nd2A0Sd2Q"(bVINuV%]E[SbBar:F'"koDl\(QU03:;-*P[EM.icF=Y[72"k"mt$LP*k2un(I0K2<fd1P^,?m7a)Ckp=3U?npeXgOVf4'$S'[-ASrfu_]><SmeT4J^3UeMi0M78f*R)Nd)hUCT0%[m3iNbFp(u]4Z?AGW[k*8=[IZ8DbOKcS'5ZAK"_<RKcc(^!0fV^2M]s>'j<12Y$L=RkOPu+YldZq)G\.Q%5n!K+_lhU<k!IGUL#C`F>2ogjFGBn[/Qrg*"[4&1UmKe>q"<\hSKKj^NOI4!5YpW/NrDb(\./eP9thXG=(3Cl at D@-mer(.O9a(m1.\"2WrN&YoF[Z:j/Z!/qJkgUS?c4E\X\5]-5t)nBh#P^2M`UQ$<jGV-I3t!Y\9PA[H;W]-<bM?N-!k`Kpu2L#iRn+AqKpK(l.Ah]r-B)'34#JZXp62W_V1[fR+0n\1jG^JD"!BAfR&CU.Yj&HJR9 at qi%g\?LL@'-3r$WXp,GSr^X*\tP7Ami`iYj!SWNa5=Y at 9]?-3-UB;IUfj1;Ja5*_A5u2#QE at Z"Q4?k+o,E-FWWjo&A#s\Nr./<&n/uJQ2jQ6Bh6S at h+_msG,91KVUdn6.>%aX at O/K%ZMW%9C<s:bT\]*b4/85`tM4E-DM/Dr_h.s at Q4Y=;'7E_jWK]@nlB"ZB9>&'98TIE`JjY;u++b-s()5MH5iLq>/j='Gc7u at tE[EN6a:q0TH7Z#Uq]#D&G3_n:Za)Sk'^;MBrlDp3lA`*cJb-is")<8@"OhG/SiGKKjWn1(m:s at R5!iU:(Wq%G;c'>GTV54?e)W+a/oROKJ7S`GAs%e7 at Gcj6"L`?BW-mV!ubV2l"]`s#@#eRmW4j8gX4MV2PbqnT(c*-5P=0X@(2QAPM$tPpq\d_5-eq0T2^LCg'2Tq\92<.3Z.HLWtbd-^T41/"'%#^Ve"$YXPP,Vo:(b`G4Bfj96N.lYLnG5^Ki_4ZG7OEYfZ2:fV+:h-'rT*Q+&E":-I2*hEVE`smX"<6VU*WioHLW$HW<;.AKXntd-X)cc(QkS(_huK5oSaCa<VSsF#Q=WJm^bSeFZnSlOYY.;YNC8N"TEbeP\f1eJEhCbH_!FbLShlm,fi,[V1kUYYg0m:W/7c;o8<ONRFeoR4)@27&i<+2P\C7:5XQ4s0ggac1UL3-d*Ze`82:eZ\><%lni:nL9lURX-4pQDpDke-7\gNIR'1C`dcmPl!>E/odf~>
 endstream
 endobj
-136 0 obj
+177 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 135 0 R
+/Contents 176 0 R
 >>
 endobj
-137 0 obj
-<< /Length 2138 /Filter [ /ASCII85Decode /FlateDecode ]
+178 0 obj
+<< /Length 3335 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gb!ktD/\/e&H;*)Tm]N at 7'rn:'g[a99nsMOYTcC1 at 3#P at VJRWBk`Sg`n(7%#I^CQUl).s*JO#I@]Aq^gce\EAc0X_-p3gP8d"d+jC3]St7VP7A,G6smFm]%/8\Ko_^*/k`48V-eF#0<#Mi84]gam(R*2\Gos/A;tOLU#e"iLn1S)!f!fgQrHRN'O.;l5&NQaV#k=5Q,!r\SLVlq7U5nLV-_nV_*F2b`,75iK&Y]JqF^AXm=Z4"_1Q'J(\3?HDYJaiERe>cbp[YE5])OYM4hS]Z2^3o at ErGDL,2JO=hM\!@frJI1u`?C.=4>]&(I;SJ.>nujgnR;*AGKk.%Z9"_qhR,1No,=*CbaTU6f#^JQS>VbBZ4>!Rr8\H.0XMF'^V#NVJX#1Q,_Gso-_Ch]7]$#\B=L!ta$gi1m4;_up=dm9OI.\uELd[ijlA1?%1(0j&YECb`lmBDY3uk[G*Q47]dk&B;6#\R'#p_SbTg=HpM]-&:(M0rEnk8F\Y'mL?8*dpY]Qh1jS9?4r%U"ZdJ(Z.T(\80t98?=VU)5#V(Mq:5\KW)+*6*(H&)\8XNR\g7BGVI2U1q?`=O)f0$gOC7WIS/%R>Hq?M.!UZFc@/ghc@$6R$;p'/=ZEXkKZ5M3_gRMebhjb*.9L4!V/AgZH%W8C,r0PU1D,7Z<0`*Li`8FiAo#(np<jo(:OTAMit21itHHWbG?IcO!S<C>C<G0?1jA`)aP;3A"rX^"X/_K=?aZ+\hM0OR`V!EpJ3WM1$hObl#I>WfugjmT;#cO:+(XPp)pPQcPdVkN$iUWQB1/P/n52Y8e02#F\"X@,PH)mq7(joX8+itTPduOqQiInm6%=m6.c>Ml4 at t#8D?G_k"_L,5j6e6"2k2TEdZ?a::Iaq=k-NI:8ql&93'[`$qTqNHZ*X3XbD!OlR at b^Z/t=iO\/b6PhiIb[_-]4i2RcFj5]__P+=FrCUh')_hs+K&8K#sj%I)h-mAt257i@,Gj"l"Bm9OOj1%K&7'mPJ_[&7JQ2!+_2Kg_h/C)W:D-F*I,?QGBiEa/HGqRF*98gdMG/7]268HJ5R#%I%LP*^@+W at 7IhK=G/e2Q.U\c_=86`^]\'&MLE(7t0;E!GuEIK2'&G(3fX\@kD!(-=Z?\r>0hCg"')!G'i"bJ`*g9S`XQCXS**':P6"mePRof#+kl5P'Cj)K4=.E;qs?]rS$b?`,4SQ[6T0%+bjOrn$j/L9'cmpXE=G!O!^Y\FIAa0?.aHh=P&HJQ\kN9'hhY"Y3L+KZ-O2'J`r7XM'ZUPD8>omXQ<D0UR%nFdF9#XO+8Op7N)P,Rh9trWPn+(KWqBJI`iTH7K-0jX;%o(mek`D-'u!.5,"AJ^H at q;u""XHB4_^(6a8J0XZ\]RL"HZbY1$hA19>bHF at iVHbA#\=f`TVe(?jI`le!N-sMt&GeBO(>NAuS1(;=qmFcD+Z*12BOuYm2Ik+EIlkp\uHsIsA;Pim at eJdcV*#GYWHFGp,M/L39>.6N4gkN;Q5Jo?TlkHs<@]SQH1t:Zu^OMsOFWDIG at pJpSE(VRJ at RI5J*JRIh\Xj-KE;AFN1lDOOrJA2N=:kjV^F)Jfl"iEN_n_jaWs5eTPSc(6U>T_A)REX8H$rqPBhTgQbbT#,b861Kq$4IbQtAi0TiM[:bck at pee@Ppb!f;Eb/e]ZUCX,5S?.9Pjr8!gpEhG^V.&c[Y.DR([K4Yl:MJU/OH$C;J.gK$3<%[=]D*Nrctl]+/U-8u;gn`GV;UA6o'Nto'"%%tk8pTYf+W;Hp?GiIs)afGBZ5_#+,$Pu$Js['ML1"Un8=JclhEd'Vq:,4)OO;s2=Ar&>hgRt<9r.6Tb#q)m>c:pQl/N1OfC1Ej8t-qFL<a/>]!^%e!u<h[ncUV3>Lr\3MQNBp<BE?"!%0MBt at 2IaIo0/11`-h-V&`[jkj/t38Y`VYK[+e\Uuh[3Gn##s*?o$9^87jH?j0Zd9c]m+h,]1Vni at UmCen<ZcC]3r3,7 at eg&OMoH$#0^1_t%Y7LN;s*ajAq4^kZ+csOTEkZ5SK/=(5Vp-%&_$I2ZSN'qLTfe,:V>lP>U4lB;PO6V"a>Q:VrG9:`+D'4Njff$E^.W/#_5WOCTV!+!gK:"9&E.:;p.?<__D_utAZ*?N7nN^\r+'"G~>
+Gat=.D3*G]&cTMZ_Cum=(JVDcQ^fr;/t:pJ5qf^Xh4b)<IZS-pk!bRUP$5&/\GtEZ?EBhgg.i;h at 7etmn<o\WAn\r+]Xn%Y:1hG44KbiWanj/N3:Pgr,QZ\Y*M:PBJ+C+?r9*or6Z4<T^!M<8f+0sgMKOFIOf)Ep0DX6!()-Z0[/]nMI7dj";F-Z5nSX]"/=!:fqO=Oja<_R;?/RYC,tDEt?p\UOZ/U_.7V%"^3:rhe]R40]9ci$05"/+Ufmh;i@`?p%D^PLT4FY]ZNFq[h"17&%^U2g^"YHMmrCm`A8[D.-^/aAtV4i]>+8X%;N'5GWrp"8rmS?*S9;SF(h8n#,`s02S4'F1LOWd)@.Xn.\%f-$+:thYm?o-d-aUtHoR?>;VFf^$ASgYDDQu^`,^+0T5bM3@`r,k(UC0esh&;B$jck[lrr5/CPEG9XH-]o3*`,f5?LA?hk at EgT5Z7JK]ll#NCWlus")V\)$<QQ*]OHs!O7YNOOl-G-?0E4;o:1^HR;IJm4 at h1#L35c)I*"]=.lpmS<E`3=X\Iu\X11+bjj/fJ</c#>'&`I);Bb$&pahRnhKR9\o3V$kb7'dlIV0&+!(P]lsp3/"4M at uCY@#\hQiZKN&=r=B8P"O?R;[!5?;'BLb/s)i2Olb++4'#<B*OGj,mNBH1l(bHa'\C.G>Ks8FoaegmT*T>3Ke>5L)h]L#TZ)r at dW1gd<aX4*Y*cjMTiYf*/;Id at DnU/R9-fHp;-#fS2Xrm]6olHDD-E`N\cpI%:peJ;9rYrmW[,cUAGDI&.Kc#f':NrSJC4s"fKhOS6uSeOh*8T&GA#V$hq8)+%[+o'+)XIj9E$.Dk]=>kj`l.)X&9<G0r/&KdX]E3A]U"=U/j)43?-'m#5-MIN`V/I"J/;@V>%fr/#]Om7N*tb0m[]p4h9N_[GDbJG9R[cm"BP$GGG`)m25\.TN6YJJ=eH->"ipk_LW?B]NcP?:+ZTPYJ2j1e'T0n63L)B;';*iTID:Ep0kmhQIH4R&P5"h[1.V0BH>5&jW%KCUk\3)<rIfoX)D&L8L!p7m6gX4a/d-[p*=%l,Z*mTjE6iO3!gq%:)6o<Y26/WI5Zq=VttUc(Pr4si&/.AX(%8OgJl-uj>8$kHokUdm\(\Zl/)\!\;j4pA.lmGcuIEiJ>hF^rr'%mTEL?&B7TBVJ+/]b'n&/t.RgcNnLZ-'/Jf:Mh^lCIWJVlJWmPN\8l3LuYqG_-9s*d,m;6/r?OE?Y]WL"$<uK;V:J"<^"=J3>*fP5Z&*hgSkoOijlt6P*$eVsG$!5Hm_AgffQ#rbGZaZK@]<u=FdpI.t&kB:2T=3g&\gX2j/<*O-YLO7cW+eP-Nd<Js-<,sAja)?=RoCn.VeFaa2RJUH@?\:\gB^At6RD5`CGdQ--*0_h(nCW>.2sN=$)0$r'tFh&(!M$'c'o2qc at qiO!!+_Bj6$:k#EQNgQ@)Cpc032n\k0tlYAJ1V>djk6IBUR%g4=#.j"C=78##KRq#\Re6LU-RT8a0LVr*R%nV8u]Jg_s`*1YDf,_]EN9,XWA^cQOX:4\Z7C+\9%'5E%-AoVi\NL^Vq2;MbpRO3K6(m7Y7Xbr`lS.N1OM3?5)^pD55N,HdcWR&`BRYGk<c.\RNlXW^hTJun\SPn4Jc59c1BI+ at p(hY:l%5mXZ'qW`*ok5%ZhUiEa(f,_b`rf?J]9Ao(:kC^0Ss;.Ai,p_9VG"6EC1u(#3R,'t5+u]!)/]5#NO#Z,puc"O?;&fBD9>"qe44]iMW7:_p9qnjf.s*Mn1*76gA+$P4<O-,*[FJG#[+=03eFRnrP3$nAd?e_7d./^>N!r/&)!AP^nLp[7lf2h069VJ6^oN(%mu\IMi*mo!%u";'eqp"_<calk/S&?P<c0W3p6kNWI?ZK5n]_?<D%ca.mU/r#L_ZH\SjEMh]m0n9b,rZ[J\)jIWCa*O6MW`R+#'m/"<Ni%n#@$^5%#_cQg`:NP!NuhV&/WO)@kNRmE1F:->H;a5`\DNsbE1c[f%HVqZ!W`7VR_B9H_p>BH^e-aA,**4-T$E+$e`/JUdHl%8%7`MS&?GR@'7gYpGWjG[PE at C2oqOom6]3h=Fu7l%ug-Dhg?nV.:7KB'SD*=%UIe=Q<1rO>)P;ptd8L"R!uaUO9`mm//9_'M7G2hN/^f&m#"D4a4?&6!_![+?a!4h=/X[;Td5S$4nETBfTmn$Lm,?:!;F\=isW5g#+")SJ65b7l!=n5DGbM<TfPQFk-<,XO]h(N7Qgj$>mNc!&E?G$4RYpSlsGH^g)(bcdr:f``YTC\#-_Vt6Cm(_)4;4;u-9CEM4V5h`__YE!VQ?E\Soo;3="\]4aQ[.'d<NXPf-JSYo:OPRr;>!0RIJ1"0ihZ:b!:1tu[!<aEli#![/QQ7Z:nVGJ1q at 3-g:[PhJD7'q%M>#Aes%aKZL!d?U-b^67^RQ2kac+RT,V(kNQfe at I%>l8PT$t]SFsQUi`HV<?\F(1p5)Lrq?d8esPI_u%7T)UVSWo.>]?ReFC:48fV;"Je4nM?#MC]hp:eH(jh(aI at Qnks_hDr;*^E1YH#mmA`k65DY[c&tV1iL)9l11.^"&\Y at g,[UV;E]O*$E\6fro>TSP*c,!e9_&G@\as,UcVnOlKP<BkcYhV"n+*o.p4U4%QUR,CpdPgf+dR+Y20*L"da$*$B!S-,'89,CujTKa2n/skC:dc%!fcMjF%h#N_d6-Z<sT0EqquJS(nu54$i'dbpl<"<0SJhEf5<h#/.1k.bJZA:?Naa'K(NhDm7War+]VYlgMF>@!duf,,I<j/N-jFI'Xe@:cZZ$6Djn]?8&U_(^@b^/^;]WMIL7M^"0"jos^BcX<^ZU-s%PM&_ at VCF"`V*E*^N#qEhhCVQY(JY=;a$4"EpZgE,Y[@3S3#Ef&h'`t_+\WN>:;@p+CQqnmJEG</[OkEGm_N<$\bq1`(K\n1Ol%._XIZc">)btfoP"t at p<*oU:tD5:_de*JUE@\2R'>W:V3iG!`P[ap"MXp/%hp7T,FIN*(p\T.S#defuMSOt1V1pN[3JRRl=RJd^/'u#glV&8oLa"hC1jcLrFf>,+*.']>Zh;/)U-jDDW,T-[1$V;1o")u5:p8$V?E'YNLJK_OlTb^;Z:tDuXVjSha7F616+%4#,Y3AAYdC::?:mtR7'A$)OfSQ2m[c'6$>RoTXf[BIDK-uPs/-0l<:[ugsRQD\6[OF6u)LVF1q9N_LSB'j.>?J2]MK2m2g'Bd-"DnAEK6(/^X\mhud:$I#.2iV',]&PP>S=mj#i4+NJPRs#Y*o[$s,g5Hr&B at p8cN;:!@hqCPl16=;+hT7s.g:b;.]fT*e-Wo,e^#R!A/[B5l~>
 endstream
 endobj
-138 0 obj
+179 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 137 0 R
-/Annots 139 0 R
+/Contents 178 0 R
+/Annots 180 0 R
 >>
 endobj
-139 0 obj
+180 0 obj
 [
-140 0 R
-142 0 R
-144 0 R
-146 0 R
-148 0 R
+181 0 R
+182 0 R
 ]
 endobj
-140 0 obj
+181 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 444.49 165.28 434.49 ]
+/Rect [ 489.423 700.2 562.917 691.2 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 141 0 R
+/A << /URI (http://www.martinfowler.com/articles/injection.html)
+/S /URI >>
 /H /I
 >>
 endobj
-142 0 obj
+182 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 359.826 165.28 349.826 ]
+/Rect [ 84.0 690.3 260.211 681.3 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 143 0 R
+/A << /URI (http://www.martinfowler.com/articles/injection.html)
+/S /URI >>
 /H /I
 >>
 endobj
-144 0 obj
+183 0 obj
+<< /Length 3192 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+GatU6>Ar7U(4OT50FSY/H=8)a519ouQZa.\G1r@[kQ:'uTHuX"CJ,^?NoM#DJ)<VUOq&e\:>>IcVG&!OIcid/*r>HqjQrErM24%">F\fZq\+<c_1-eE]'q8T*"mPBjDI-Vch$HHIGi(kQEbdCS5*Bhc[mUlX1\<L(S"JFICDUs36s?$=)"7ge&d(eG;oA%IcqBE=mPZ7?(k?KWr'G at qef5N^@"IkoIG6ETkS;N>'7_.>qC6:Z^AKGd5>3J<b]d2Z6bZ9TIFHdk.;p,ROoA."d"P,Bm*:`8%4rh*oNRp-qgX.>%.eL^+Efq<G(%B:.$:4V^p?)NreX`,Be\tdu7821rBN`C*j3M<afg:VW827q\FUbC%90^/:_+6Yg5s[(=l=hXc&jq2a$+,1+jnMbg9R8CshX_jF;&m<ilq+/<5!Yir.mXk!5EQ#dEb0g&Sco7>98rN4MQ3O^+84J9P2bZZoX6=o&rcE0jTu,B-Z!ZB@(?fK?jrGdh9l0e)Y8S%L<$3VH/5rKZmOTpOa4a)T`6NK3L\Q,\fr2T"p.`+f%\_T")Vc4[Gr)[?2W0$9'!RjCNPKbqs9Gqd%)]lPO/-%g!.T&U4O*)M\gm;)ht+YL$8MZf^bogu0WK\1Gu"_81rEZtj?4XZoZA5_2Y>2co,'?eqEh^%ggN^Gq0,I<Om*qFa6-e)&0%3q0.%K9K"XG!\%4ka at 17X<kTdn8cQ1<2,(Qg&pQ/Q5Z^5S%HNp8e-_oedj3f7e'pqK at V:^@*ZNYusQ<mD*_XDKZ`X!b\FPM`tMMVs?39;JiO)*cDX8]+h.g*/?62CY4m!U<[EW8KdD6q8@">Ho_LK%g_7\D_p_\"W'MPK6RPp_b1\[b70N]"h="n`6iL&T)*u at +6jdB`W48EXFNNCSu;%s"X/>%QkoI9-l]BXD68/h"=S6]bNC'Pl4HAgb8?b^a;5CYF]P%*@a<pp\&]0+oMdA?'r%(]5P#<H(,RW'4Q2.%..`%@GcgjNZncTL*!mI4LND]8hdPI6SN1?4n`!b%!\P<TGghoS?A^^K7%CJ/%$F#IN!(nhp[uK1*0@<\@pJYbnR;D#9K>3%dE;qk_5edA%sg*K0TR:e^JPn)@ZmL$3)1u'`nWqK*\4+r=]OhYK7l1"G^FZ*kD;Vo+8u:Uc)4oE6:\-G!GL?.NJ''jWUCIlqNHM,jsRp9>i+!h%1'r;:[/Bd`j&Kpg2,r6lX>(rdU5qi[uXKjo:"dsZbLipfTo&*M-%`F=%YW&n\RE1$&EHoEmfk at Ub\M[[7.29(L.st\jsAD)DBK7L3#5 at UPYHaIfMAQkhCO[=f0ico/4+r'M)\2f/*"<8kEPD,iOD'!uTbAD/Uprqs7t%5V"+2I1/Qf-BJN=.*<O.##Io.BVuS#i$-DW*XMP,`!mS0M:JOJnaO5P`9i2G`6Ie\]Ll[Ogl#RDB8;,QaGNCqVE_$pVT>u9[)%+EYp(VeK^^NY.]KFPNUUSC3Ot028ae>U/fgm]M*P`NdS)AS[%L<m#:\d>%\_AmiRi"DC.)n2rkif1AB at ma#a&gd5K&K^S,Z):5JmD2?W0$3*iKFrK2!(6Vi^oc,l1,:n4Fb-L78&t+AlGjfXf^R"]8pYlYs"uXPi0![Ui7ZroHK5Mk%]Qj[(4#SY9p#kgI>W]$@b6%+82MC4()NH%i%P\I41'?B.hf,&6&GLFE at gb]eGU_!dKKSl^EkHP+iDg`s/M#'8hu.qFK&H at QhML(fn7:eiE/5<J2n?QajcMSReq)6t`:VCZQ-YR9s!*.>"ci^\C&*)RO[*S3G\hi'(84sJX2U8Nhg);j](BO5;*1<8a'_!OTF-+$Np"QD*$JWXf7Y/ksu0aGL2M.+8X6RC4L_#mnY1#T?"2FZM5BKM>HCfbUU;ltMTe51!6MS]0;/!ob"#R_mP3$B:1h6?8RJQT52<jUu<-G@&IEdLcU73ZZ+p1LO,E^,-d6ci<YVt`>a!dUS/@Wn2AoLcP4%T#+do>chMcEs^5rcTF3KV"naPec#Tl.>_0m'NHHWOMRR02uYF;pVMYp!<e$K at EL4TWJ<^",S;m'TrDSQsp^9l?X/Ehf(]Bb?:u<ggR%4*9%/Bif[eUP[(R=?C02kL!XU^,R2#^j3i0sa&CG\2mh]mM4k>!L4-Te-R=^:9N(nh!B1a`@rgLg5Ft+#,jH#s+%[RcH(&F)F`m0>8.Br#D9/0_YleJ2b06SK:D/<$F-H5>\)_-Dr'l7*>P7dtc:0&fW'1;(@(",+O,+k\L,4.Zj-9979'p&+T&ZSXY7$=/[QiE4U=?1n8PS;6l!Bb?i9;7+L*-$j8jC/d#\Z:=mEC"F(lW(d^5ge+r#QtC#D*AK,+ at A2mPQn]W#(ShRJ%Xq%Qf8R4C&A52`*P3e7-9+JUthPB1Lmt/-:OcZ256BRS:Q;)\%e??TD:ibG/nn9V0`CWb@"2H-Te!2Zf=FfP0.t<RVt0ZJ4::eH=UZEMH>Z8$+]p)tf\IqIQ?c)f9kmDiobo/#_:Uk1Lrlhr8EGI,1QMhS0Jd,M6R12V,mdh%PDV)GI$O`MYSg6u[u*\2<"i34)0C':MeJG/4D`C3_Ve&ojA1:D9%hK4eIIS58JE6J%ZpraLo[e-_!JW8-nI:tkC?T?7:^`!T5U5RRU+g,E[C-)C)c\Z$t6S-e;,3Kc#]^a2aS%\OWg(nij5RT8!sYcm(+e1]Ph$+V_Ar.d;*oi5!Z@"a2:=H;]Orbi4aNY`6GMJ*Hc+3@*KJ`cS.5)mFf&$Ir%K0rHdGY1'Jp>Am^dc!4EWqef4WRTDc=PL8e<n3ah':Wih3=8c_G1"!Pe\'C(W?/Iia=,8d at R'JoB@&0f at NSa\r:bLp>QrDFr/lU%C.o]#X/W(WQ`E)Ba+oC*s+gI9#,.`9?GG0OlfT8^3,>N4(T*:7,IstVKq7-&.NW592D.`*;?Ep6k6+%8`2)odgMl/.PaM^bP9Wf+l)38D6g%&K(4RM1R.&&K\I34T6\A"sQ!eZH9`9'Ked'tuOWH!<)8)"YQl:0]rG8"oVYfSO))#?oXg at a3"i<3f$]Nhg#tImVD_!$+;mcT->o_YnhRr)c,Q6P5jg.Wr%!4CTeQ37g9CU:>ju#(@^_Ll<SfqgSduIB_;F-8)^BAUp+te0i&`M4Q6#"^LV*U]H5"*3%K9sICk@<uT=p9!oY;Z"3m)HQ.LkNe at g:1u+NBF-9)3Z"^P5YtqHqGm~>
+endstream
+endobj
+184 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 183 0 R
+/Annots 185 0 R
+>>
+endobj
+185 0 obj
+[
+186 0 R
+]
+endobj
+186 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 338.826 165.28 328.826 ]
+/Rect [ 344.208 622.35 436.197 613.35 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 145 0 R
+/A << /URI (http://en.wikipedia.org/wiki/Template_method_pattern)
+/S /URI >>
 /H /I
 >>
 endobj
-146 0 obj
+187 0 obj
+<< /Length 2831 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+GauHND0+Gi')q<+U%+Jh2[N1ISSaN-^STtLP;[(0A3+it5U7V[OU)*n,J,o5n$a!&EBsVggtl=!Ysf:-/9[Y6Fr/ZipuH%7o(BpEE<uS6ZgFY4:L]AEUPQK,*r1X'T3o.u9CgKkjO;64puM?Y0M:9KG^AAb6ooAHLQ:A?BGa5b#e\Q03VrEfGb1770KX73aUA*hQE(2XDj5=J7aL%n^JL[)>a9!NrCZ4kloR&mrUo8PJ!DWg=n9ul.2B5pMD(RtHYcS0a\mi\X;Q>ek1l\gR%dH at A\#rP_%oOVQ5Ae[b$[/@UYeQ$pL)UK\#VlDa%K\4)gIJaN(.hGg6t&.-ui]t\+Z(8?F>r<QUDDl]0QTrCG!"G)EgYtQl"s"=ckjO:$O%C9gW.4c]YK!jOq4UMD'7Kp?U^aYgoEUZCU$8W7+H*-?rt4k5[u`c!X-FiY!@4f;=Vgg at SmsMNXgL_j]>N_j[Wa2!&;ZX.F370.)8!lLg(6HM9&`fc6]#Jrm;S7E4H/o1&AUdFaZu#b"nn at BS@<L+8.,;+V2r$jk4WjomP<I#%@>3/2O at 90a&KT67=!Q=r#4T+#5?^KsM%/^JBl0I*7HR"V$POV*]>=i"]%\@07Wq?:bG<>g*f#]q3s]L)\cJu.6p3<rN.M(hb7RTYjV?S+)mbW+6J16m at L8$-;3I2\<JZ]o?f#=(X2c"C%i"r<OAV;_p"%+AI at TSG=4=t<,V&pJ-I,(W&^erO3^22oZ.&59>8_ZSR=/s09kHIqro9eJEObY+jT<p454R7R^a@>,N(]Wb6tSdDt\FH3g=>RS*`G'Ae;@m&@pEc#F_+APJUR=-5\L,WA711)3&o>fB*:NJ+:;osTHOiec;DXT5BfXJ5ZkNNt%AdP3;>-3HW#9@&i5d8D[d^Jo)U(GqCcgX[VYeF=)9ZD\s"t%.p<+#]?"\l-;"d=c`O;"a`!;8dLrbQ*C^PC?&"?dJWlY%8CG%nMRU3qH](kdFS?s>%*ORR\#<_PspEHfiC&:2,cD2,d7)rLQ,<!]&7q%oBA2uVi<Y+GKjRu]ejI_9sT@,_Ed;hGY0K7B,!;&)\e:V*/`^(6hZr=5?!2'1J\MQOZ"^o"i0ZjZSfRL+P,$)qtmflB*oV_suCN/cq6cH?%q9G/",#/cCY+^W`B)^GgYH(ha<lH)n]R(P&]CH-1:$kDgR>/]We[H+En"IcV#RN=64S.PkgJbrVd!n+JSA"2?.2eL^;AW(l>TVjUdCgtAQ>[-3u<cZl![16`@<=m-u@"LkL?OF&En8)^Y>O-hBZj`ub/4oR[G)WsGE'/rlf#XJc>R6mf*I1 at ZA'rW"O62cPmqL?Q2Pp,MCJGI?J]\q6i7 at QXg<,3.;.lqR\L`kT:Uls/o!B"4AZduC>O6`e'SZFH&$8jaEOb1n\t_ at 2iI2UG1W>@=X(h^47F,rj^E)])HIkj_fDcoEK0#7,0o2[EUKnn>phWkl[W<?;e1'A8`P$"0dlo;7P/$?:LQBo=Nj#+/I;)hEJ1ctm8SL.Ib=f@/.^['QFt9ht()pUFS/JYgmmi&Z2E1S[JGFU0>$i29c;G);!9Lk&\P=d at nWF!"BVc-D`kWXo'caW\Bp4t>.qf?beAjURU<$0Yj1(@Z<RNca[koGc+AKsI/X1KDnjcoC2=6"3D3TY,>#/g;`6_DL*%`G4X)7LoEM]!4W-(GAMd&dU&qoceJa:.*R"jb=h6cFaTY6&mT\/[U[PR>ZE-lRm,2.$k'T/f';iJ*4XoTL9^nI,SZL,>69PoJEgX at 3rbknT%J$$#r:[P9!F8`)to#W47JjR<0/+d+IFKKe#Qu&tI+<d=l1&M!/l"CJJ!EECa]((9*l1R*m$V9WF4?jsqmBVi88b;bONRq4N:Qs\PeA)![8[WA^haYMF5]6/*QoJF<7`37BIJ!:5ga2E,,ENiXYGL\>:USY`>huY_h. at 0N$j'VE6ST[0U!E[*QoGBYI4Of1>bS:3`tti^N_9H22/f2-09K7CgdIp1Rfp\(IIedD"QRKVnbhmd2!9UD76`MsTP>3iafT3"\-pZanYYI"*[@uI3Vi\YmiDqp5S:c*i6mq!r`?"p(I-fOgi]kM`QL&AqC!p.]@S)n<ritZSb73leUCf2)k=^NEqee(=SnLWD)Z#^PI7.3N\^,Z&k^\1DX2Hs#GIu'&gGYBcFA5,BX"W-6D1]o*L7&$$J-(Y*LHA'&)<HB[<b5+ro&h)\fmbrbP/1fS7t:hjtpT-FAN%O3NWg=8Y?#Yhcf5o-M?,f7PkYlKH*1[W@>GcTZc;-*Qb55PB6$sPU(g,?jW'#:>Z[N?CuEdD.4M6K*)('fTm0]Ltl+#-;`J#/a^58YKd"=!;Y#u6LWGkj<K*YA11,+g3Qq%e]NIn at m!q)76;@9)c>N2gm`)Z/LrprT*Adhg-NKE.P1#qp,;%jLl<C+?8Cfi>BY.cM8+$l^#E`ic-nguqr$hZ[$_[Y\TTfadtd!k/'&)h#ONX"]@^rnR;ZHu$5W?=gE:!8(QnK at X-Wf/l!RQ at 4<Abl9EU[HCagiS8TIL'6D6sB"$8pp,CaBGE]W]7imr5cKo3(7YZ`4aE^n3rq9i5/9Ws0J6 at R6-.B-b)TRufqa*;eO$\;+bre-&Ol*!rk at h[H!(]#IFdDnPpR"V$teDp9Sqj-7XC1G'"Mp5P'"g[Bn2Y`5c[d>/OaZ)Vf.O_JJQ/^oNKVnNt_D-r'I_teT68R7YXGHr"atVP<A+,9o]>Tp:;2E82/FZ]X0d(]s3:^K7TXOj*$NsE<g$-cu?FF36#Q\L6!0^k_9l4h?iT^?j>p$FTU8YhE+lrHGj+<t#4QCbB=EJ;;!XRh(Ktuq!r<FMVb6S~>
+endstream
+endobj
+188 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 187 0 R
+>>
+endobj
+189 0 obj
+<< /Length 3515 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gatm>CN%tM&cM-)+W(@q?e?=3+6do$il<V`1q<qZ2BV5D at R[Gq9dhV];O5k-s1Lct'PF at MaiSB$,bhg(a39r-B84"/5C)q&5Dbdc\Y#EpPPL%uZ/DfKM*1PcZs4CDmc8gWq<,ruKfGQ,35,PT?5BA.6n7g(n5Z2\1a"4NY=1<r_2"(oan(B;[8ctkjc\eS/kRs8_<^&p_=KM$o:\b&[kgHb5T<=@NODZ_<LJs?W(385'KrOPca%0#7;I[.:%$nil/KO$(@s(9G2Rl-%39pR.Hcnc28.XPJ`fp`ek^548!sdlBcZ/ZY\H:HFB&oO/7c/ZKX`[c<NY(lcS at O`fjI$Nf;5"?Lek'<,]^?@)m)KCo)V*]a0Q3A;Uc)><lbN\arEmDKf<a(>i`c_87!#*WQoIpRp0h'f5"MBY[rR8DgqS^[5\#!p\$5(bj/fi+K$6*H[kVR`*uRDJ&&brVn\fkDY%NIO1$aI-5UR@]Qd>3an4ZCajkftZ]I0V1?a$Ma<KO+f)(l9icV!dR at O:gZoQmHR1mDUMhSm#LA-%JP6G!Bo^q]2jm%8,n^I"rG/X;d')4'ei;[sEnH<)n<T)$C;t2, at 2Yd(:nu#pAL)AS1PC(1^qs!&c at h32_B;_s,GNgoNgC'D;7qe*G>H/GXg\Elp?'JfAH.p<cl#=7!<&XdtAoGZ6Li1GLm9+me[;SGIQLm2lAT"a47LZ(AY at Kui=n)q-=A3QmXJ7J$gDJ3qAEk80+t%>sis'W*STbPWOV7sNMFJ,.WC8i-1i2Z,VYEmmnV>F7o4.ECGo+HiP(,pl!ZN?\b\2Br)b<<WNINd0!1rl[j":&$cne6BJ.;anqZG-JI at kdn6l/RYH,:%7'f_tdAu/\"W#JhL!(lDj:gT&-$q!_e#lu=oj7E9U7lQ6kpRUJk,=%u!+-LnLo/mW2UK]s;7sB>r#L&3\B+V3fL!1#H)DaPFV"iqiq"V'6q/D':Z$p/';-sV><7u6?l,22HF_LCl(a!QSq^m&obLSb`euf;MYqGo[bBidL1?V<XKRrO_KY5InEcV]&I#ae]'gOgZd8u#OL4,JGd"cEK#@Y*\cWC8.Pkc_"U(kUdPt$'&<0_,t4>98+GsL7<78B'mOAJ]J/*7udL;UtlrlB*#@"h857Fj,rP/+o_V3=o9M2t8e]:suG&;c6>.B/3UJAd(iHOkY#OdQTb#(BlifOiW$j;=_\o9.1&NYY^,O_<($V5fWbV]lp5R7N at H0EZ!?4Y$D at 4)8Y0kCPI^Y]GHZcjh&Lh+!.DI-*,D\ZHI]W0ugPjgj%&U&48Q;H`>]_hb``(>E7>F9G>uVou[5<1IEt<O0sjD_7hi7`J5bTPi&N#a?2[kNG[T>#)>qa-Nin"IMnP"B#E$0N:so3.>0>_X]$m&f;lJ1HLsmQrm at h:*&^hfo(8joqlZ54K^S1?5^'=JQefKQg;o$J,Ws`)'![X>\8APTlHl-+c\.n\!<oW8rnP#0G,b>m$eEhl"Z5$o6tcTl#16r8eA^(<icYB-?2.qL:gi-RDOl0B(tGARPt9/N_Vq=b]S*V4+>:M@>6KX=k1qC/4SRYb9fgF*N+GJ(4mK?]KIfRJu_;Q.SPZSSJ$3]`u)CI&A2\X*<Ibaf&g+h$W.K.O-4ZW^`S*4-+9,%9bIUC^rgn7,:;V*BHYu#-!phi.K??,o,UX.5!AN9g\81lIt%"_rihuHBj"u-Scq,Un'i=7Wuj%)+*K#HC'Et\ifNb$Of"+d?(Nr"B'_Fd/21?@a";O"-aS1jQ`?Ab,:UCj4&).eP;L-`Dd'*R]q+n2)"UX$Y_OtDfs"?4X\GN%$G1<<D+DFJ`%j\BEGG)B4at%qm]8[cG*^B8a/2m_a1q0X46]p#QfN*E=4\Nk<6ApsbKXo>eCh/QW$Yt7^$32A")=>1HH>QIT8H=EEBYj-W6S+/qO%=&\VhQ<mIrLa+QWiJ7#u=d8g*s(?$l,HK&U[[dt9dB36_(c1XQbOI"R^\<R[rck'p_1kW.QT.*B![UE?`pnt&]BHB&1,f(YW1U4kXB]_2.emAq#_7&`'\?/^3WRUiE2j2P?jY\$,.$e]s_4%h>8<L9V`S24L at A0G('!L9M1UgTsMT,N$1Scr<0mk@([.R[EXL<VT at -1&6deoe=W#P\@;rfLhD)"=I'Uf5j(qQI'lf.Q1)&W<2iCTYb'0`MqK(Yh0\JB]:Y417C#b1qB%rq:nS^t57n**K-=A$&b!)OBW++'9iu=l;\<QBA_ZU%Z']s4/1227,d-YR/gn-/.;L=>SH\$Ij->08^0s@]UX:[DMR#X[\]\o/\IG+mX_T022S<b1R3o=e6In"Q_VciNs/k'no[I?p2)fheC?_1p2Y#-2Fk](Kq?[*dD at 6Db^HI:<d7MgF12oe:b57iX.n/QtfjdO at f^9IV@>t$==A&g8u.'"MiSj85T-0pVFW=c'neqgp3ou:fJFV-kT;Uph=65VA<<He,fH08)mXj at mRlRRcgIS<f1CPW_j"!`6pCO(ZtKH>oAoBAYNu9/F<-,#--)Tp=l%*h?`hGYf!-h;si!E=do-tU(0h1F3Dn?]fi*_+U(-d+;k$i,5Ze4V(+iH5caNXd[f\Tgk_]'[kN)G5m/s"-=^J"XE7,AR;PJR0G2N&Aq)p2>pj6X("4rl2q>D(,#^2Y79ZsoATgcE`Q\$GPE=5-&],5_[2ofW+h,17Q0NYIM0p*5X3LDhX'oP$.:g1mQ=+N_BbiAQ at Kf'^2j0ib7TQ?k\@/MZY((=o;5(B^gq;0-<i4AJ]qJI1- at JU6Hu@YJ1FIq at _+Y>YU&E(:[=/D at eD7eqqLQf"fb<5:r&QV'o*POH!#S+P4TO]!MrWoLire`*?LWCUiq8X#PnuMD*NKV&d at JMY_B8U>2U$Zc\JnLRgH9]4Ie2?7gCk"?F4rTll at e-m,O.pEhe_>Q]=8eJg-L_^fJ^'As1<Rb1FupE<NUpOYuO+AojF@(*&M<GDrs=t2LR>HhTr-'NH6B-_kmDf1t>&X]6s^Rb#e%jP+`d-E at Q3FCd8"Wa\j&a9hj!h[b<9j=tT9&L%3K8,YE<XfZEL=V+[b0TcH/;m?dXuBY\9Uh=d>l@/J<'j7qbk9,o^ZNpj2d^XfbfCaWa/e>?b=I8tjOXYbs!`_6/t8$kANNnOBog+CQ<D-1MLZ:qAlZ4]ri-lR0A5/p-5ckm?%1,j5W!UIWK/p$"s/W.jKD?N8:Xc_R%[qOHdGOF1LG_U\)6b.?k\B&5#Y5Q[iP3q"Ym<).[S[[[n]rH%TB='f@(dHYin#g@;UTI[]A_GcB_#gmRXd8u1nl1m`AZ8<"X;T(7'NW!ncgGemaW'-JU4)2%(TkkP0&AtPQPFCPc=CgGg0o99>XH)1AE<P3P9SU;IA;nCrOJL3T6PSsT$F&!`5CK-(t:$NW,gDA+I=f$LMpOI5iI$uOu7YdfC]__OI)A;M=(8!E_oup?O?WsiXHCMrA)rVUI2n:P%t_>&6Z=lZA.7>7Ec3h(`<dtgGDi^G*N&Zpu!@!eSN-ILS#U`p+Xtq!(Gt<.f~>
+endstream
+endobj
+190 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 189 0 R
+/Annots 191 0 R
+>>
+endobj
+191 0 obj
+[
+192 0 R
+193 0 R
+195 0 R
+]
+endobj
+192 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 233.61 165.28 223.61 ]
+/Rect [ 401.421 452.564 518.169 443.564 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 147 0 R
+/A << /URI (http://jakarta.apache.org/commons/dbcp/)
+/S /URI >>
 /H /I
 >>
 endobj
-148 0 obj
+193 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 135.0 212.61 165.28 202.61 ]
+/Rect [ 97.5 160.582 124.752 151.582 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 149 0 R
+/A 194 0 R
 /H /I
 >>
 endobj
-150 0 obj
-<< /Length 3025 /Filter [ /ASCII85Decode /FlateDecode ]
+195 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 94.088 124.752 85.088 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 196 0 R
+/H /I
+>>
+endobj
+197 0 obj
+<< /Length 3256 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0FgN)%<&q/A5Yju8(8d',]i"qY(4'B*.a,ZO0D+/^eJO:%^J3aC4B`@rg/crW#)3E:tg/U$3E!G-rDl7r,]f;M[lqK%[0IMe/R5nEB=VDJJ=qf)^DU(u2Yi_-<3r/CGYP\38GNY0hm8Zs=hhWiD;(:SmF+6"%(1i!'/U2cM\G81trRtTp,'r6[dGH?@s#FgP=$Nbi\`g47g>k&:B)U7XE:b[pBZ";Kk<bm^Iq8)1rp$8GSko?S/3XREnO'>Z,\:uHYIJNC0p'Bo0)8+1Nb$s_GW*q0#c'$d)PDgePT'aN_5",kb<<-AbLi9,r^N`89$P0$ftH>7)=OefP=/pO(e_Q./Ah['NsXDd[H&X#Bq00p)lP)@MRFA@!b#;L`&:clf8o.m/4md?0+G>W?J+NA+0j?V2d7mldXWaY.%&eN=X'#Vag3a7M$=*gjUiG^PMq5#B$1Y,F.QZFCW4tQnB)Z3g!:ha$DudN:2%QFShmjha[E9Z6dXV]"7;H4YJ)R!NATn=ZOMts.S<b4P\6GXhi"Vdic/>c at Gi(1]Z\LA$Op,c6miHKGaPeZO,Qj5GpiVW at sd(Sr^E(,:h\XdE9DrpOsLKBPQZB6i/:)i';sO&jlV%LQQ=;0\PGUbpVfA=&UQO2ZV?cT,)(-ES0fLXUEh0EV!3H+i15s*a;6YR#6hqR/D/hCVO$,]5Fl[[(!oJK1oU<d/R'N;NJgT@`:8MeY=KgL&K?X:%V+02//ST!"d)K8Lfe\L2NHtN<lF8/>uIfuUXe`(ZF[JS"S at DGGan$pL-q;eH^cLERpNRKUdib;^D+&R+-;_3V=@Bf.BUHIO!)\G?o++_!D^Z?D[_J;VjcJMmGUrRgfp=Q%-PZOR'Ro.WG.M3]%q-9H)sX)Y7=q^&m%>A5#lg][Y at F.#*sUY&A#=/>@^A7E[*+nXCK\3^b=nLb[:rjY]5rB7MR4^bN&9;RlfI*hjM^(E2+`%pPcm6X)9%CXbj`4$&2.K)A>&m!:u)W'a5:J*&D:]"[#BM=:K+%Nu32/6Y3B&&mcP4&oG!r<np7+P^_N6:_!oh,=ra6O-k<VZF+,ekiF!AitP#X+k:A804S=Y?o>F^0.b!s*5hc+O[?S at GnNV7Tf5m9LiEtRqKDVt8&TJ[inYEHkP[<AGrb/B^uAV56`K)B#+L`70"u)1-@!kuR+!oRgO6D*pIr0jZ]UX+jT6E]#[@00P$s"ZF")h at TQCi+kkY72D0rM.Z;:uMmFIR\?>>"H(Z>@-*;K2#%O0INiniHrh;<^q>5[\'%rfDEdL5EX0+"oZp7MQ)G=&V-Ib8sLL5iVD%%Z-"`(2XLpq^%emZkJQSiJaBJ at nP2ZFPF'P0"VgO+MR51r<?bq8_rJC;\%o,c$*s,H\6:kprdp/+E9a3c)\pOt6ccpA*bAc-Y3%R(.2X-JuoV at aG69&cZW&-l>\p?L=N%DhJmgjmV;@A2JlSa4qAlThAH'HROC.6#K+?Bu>$9pW, at o/GU'%5"F-rf3WU83aMuk6]!aE''rNXM<X#E=`=siCW_6]b/3O-KLdQde=-S![=5!GPRhCU>YD*V_b7DH.gm#!$+KlR2;SMOJbQ!5iS4ne:IUen$,`0;]Hm2HMmIoY9^"b"&,=\uVdI_#/$*n#S-iJ.`o^gsbmiFu-^%7Hcb>rU05<EV<5rn>huJ5Q-9kI0/!Ka?K-nb[heTFmJS'8(2G<rC`=hQL2r/2OCE2*T'd<M532WkhqPlic>KLajoX;30[[6 at AGKXih2=@7OVXa-K,%V?C]6r1)<SP-.l;J%))HG@%kV&7(o3VYt$5>/DgUf)bF2,tKn,I"Gb]k:C``6!_kJ5tJ.?_u$&*N5K+PQ"%Ds[@99SEml#5>!k?>0WZUTPF4RqGA;5II;^>C2SQ<P:`_[@;Q$l1b7_I7K/dB.HD5?.BO66!!:VC;U&s8ZFV.[d<f\*dtToBf[i;id at 3bZ5Wan\3FCSXX<IZ&G. at ZVS%;Pke9nYFF^TQ-/UF>4=K3Pp:rXVY+Sm4kXAaV,M%UX[aV?5&e]oj66](D.0#.JS4bn<lNn!.*/i0F[+".qPjq\rR_[6krT]D-l9V;Ze5)?.]`CBihZICW$,]-I<Lf!ZIb[KDaHCNYAoK23<$s6]^j"us!.]VPUj7ASHM at Si+7sZ;^9-W_<@GCaO&@i\7.2JC>hCFg!LVrsALndmWKQ3H>nm"*b5J^BZD8$ZU.r!:R3%R`*7>$(^ZOV$js@`@#Wcja+9Z^W6p_I7;^^j^#je"P?CYP"QL*6FiNVBg,4Lh5K:+ at C$=tVS`+]jDjn/D?FRZ6T4k260RoL^Ea$.WAK/X4-N92hhs)a!r;Vnd-jY]D3 at jmp@?W)8 at 2,<3)7d$qT'qgPM+!)pCp=WkfT[_-9[4J`^<bV0kO-\_?klqXrQrGm6RH3D*?L#g:Xh!*mEiW8fKQ!@^_23!gJ4eaY9K)ag+(H9#GB(&[`s+QR<G7_5Q;spQL"[@`<_M`P2"-Tlb^Zi2db;UL0'd&<e62>uMj0gC at _7N"Am at cdU$6L8b at jqKnA)e.PT^EEO_8EN>X\1PNN+R,>lM1KZ>ZRg$9W\t'lsqUZo[_!EA=?dlORjpL)!VRQE4W;L**+!pqpZcI2;UE"o8gsd_?!kXg3P2#V!sj4$&Mc8O8.?'J8NckU at lGCa4,$AN"MH4=4 at P9$cXh.f41F>iRljR#H%+7$?$hcK"mh'UAYUigX$$qJr,4nYC>l=ms":3M(d!=ZHIao<1M'^O;Y=:Wf)R=ruf3'`]#DOokc_bp;pXA<7sh4V[N8X*D=C]$9N4kDWDN7;G7.gVG$OniH'^']OjnW'E'L4,h)jT?&0)%U/A2rh;p;8Pm8[g8JDq'$+cl0A6L7N[8&;D1W_mC>r3JgQ=L-J2h-sVf_I!;Wf]l!Wa9hVE*MMl;f4%E"C`%OmIY`,hqX6]hrYqcOb at f?Uh at AfILb&#b!VO$,$)qU^R#ph"igkKkdd#HC69):4$g7Kc8pdWr=,NN2*H1#[9:2(NpP4[,@>W2JM(,K`+[G"kC-_('~>
+Gau`VgN)%.&q0LUk\Zii(a6_BC20Xda2pA(\]U>>Xn=ln'I5C[$&58BkjQ!sMqF[&8bC1Q<='fLB_:tf#5.-Pm+"19((k>*]0*E3X##*7_-_O)]'rj13 at +0cfkiYEchSTXhM^':B=Au.g*s at K2;?B:!og3ND;l._33ROAL<XQR&Bgg5r0uTL(UFM7=WmLP0"4Ed6=V`4D^mpZ5&TOjru?bp,0tO>Mt5DRiPQo_Sf)u0MNl`1=Hr)p8^-Hf.#SnZi+ at 4=iE`Yjr[[,b\jr#r8]jZ,>&U/$gU!Mb((#Ea"4lb0hOd5#(tXha=>MnCi;(6rM')T#P0`RB^7N$#DN&0.U!epX9DgId^.2B at K;l$N'7nL<heJ.jnm'\^3,X^8I*c!1HgV@"%4baKno>R+Wcl^(;:Vija7;g"i94^EM<B'-YE6+K0DR;4-d+,$H/;#MjtO%*0Z[A/:3-?6&1(4q.lQ+6"qGW9^HF!_8)$J3`_IEsrh80(M[8].^H;J3*Z:r%]IB%j9.o7l<u&Yq?&Eae\[r[SI42B!_9qkCeJpU&$Km_W?0HUQniK)6`Ued3YKKh.G\so"eI&-Jpi]1P5<-2K..c1Si?#!4b']FtC\EQV!CKE_OTDH[8t3fmR\=hO''2J<%$qM at BV^lGiGl]B0+DIniXBIH/W*.8VURI(!LtO<FZo:hCK>0J.;$Qj>I/V\c&1[KYP`ioqr[0q&ds+n/6L0b;`(K7eL%E'GO!=]^?8NsVB._O*Ya[>!I:RpJn$s``N+!<(YXsfVn5Edhn$flj;MSnZ:Wi<l at -CH1)`rr,u5&9]@sgeWa\2GUIJ&<J>kFQ2[Ji3W48m9--L%0,3-"-=]:Wj7W3UF6G]1"H9Ar"I;mR*GWmlG4#&r0\cg(8&W]%#oboV&]G/mRjIZ5XP%mT7`A^GPVW(0EL!/1gE-YHip]4TM`V_R`iW2);g[46`D:ui6kR%1+"*BFu%NcX3b]uqF,(AJCohYne at Eij[3'u7Mr-1As?bX9EO>[ouqFL_ZQpG:fliD?_8HcX1_LQf\+FP5!99uAo#L-\U9DkFfkM+1\J),Rc]XiQoSSMHJ%?>-"s3h*!]+]9uAVf3%:3r*?RsJD-S7SL255E4O_R_i/"B#&P%f;C?CU)6-)Ack<:6Pk]/FLfuk7>+Jb-@'eA]tT]_MHhfh%p`_N+C].B1dV(mUPfb52L[)7(h9Ml1XPXs%"3p[I$B3,QT#CF=0ZT_PCpV&[(L>6j,-!6N:R.$h7"`m:slepdg-84bF#)+CV3ga%4/!0&/u+Rah6KJ%GH'GE+k*Q&2GXP%9[tRNgti,Z!]Tm)s?2P=n#'i97]"5Df:W?R6G;'pQhL-,a[m\qGZCepZ9[[dBVn'5_mnEP0 at S)9J-=/WtgCOA))B;%Fap-FD]".^iTU[ADNWeqj.8&QNDX#m4q&3!R/g?EqLQBHrJ%CY1L%nVTps3#U%XZrj1#B(j\M4'(`g]@P1a[*65:^?5k4b%o9DOWdZ>(=c9q!InlRF4<cTKj,5GK=7/6M`aGK'JVG/8BA+!qS$R0r5)2^+KP_bMl"f6dZ9HpB at Tj;-OM#h:ChPYIN.LuTg2]As/e+c?-IG:(_'PP!4!@SD"uli-K;#&YQIMQ-r;j/`BX&0=uf>gOiSQF at ejR!'\JNi3U/d;js>UaX]Ys1%LPo6M)2"oL6FUun1Ajk,>S#s,o-.OP at DJD[T.?H`-?Q11JCeY3$SP/%^Lt!k]4oSJYi#EqRPR1bU\)Iq%8I\KFja4L6-7dA&s4>AIRN$hduCl9%d'cX'R.R1e:hMg6G*pA?81%o>%i4NNdVO,4EGsa-#CcKMl#SmG4D,05ln>$d<gId5kO/!$_lV_51n(AjB+lah"tn\WQ&Mf#=C-U"3nJUpgQLfbF9B"\)tob98c;TJVN8V49J)VEcL!E1b0]VbWoH%$!]3h&k_DL0#Vp<$Yf4ePjq-C\#Sj#T`$IlX?Vn6kQTPfZ>MM]o%eqn26IEo9K;Jm?+9R\#=p?gp`#u1_[5Xe-.<:8Y]QfBf,R'gY/$]6K\tMm_N]5q<7Yn/@\ASS!'bAg4JJo8(Ze&\J0Ld]l]R;*Ca&Xo]Wq`U#cS*s1i3N[<Gd//Yi at hiG"Xtq?Wt47fpm%_4&ohU$u=oom/Ip6gO:[kK[DprRb(_Uu(=8Tlu5:6BTG6Rk at TMI@MWL3QVkF:L0TG]oK#'U<8]F.p&g]='o:iHpL<WoXoqLFYF2mKAc&X[)iQG(FV]U2r0BmS"b7FdXCUJ:OWbJ[j"bL5hhu*9],(Z?gfO7M5b;a2`0LU5T3A%A#I)X3b&8jrC$D2"Z_:uCkYFh<p]&;?()ZORc*5.TF&L'P5&HFrIP-Hb1(*2o9L8-2$.9H_5t%Z6^p0<mbQ,gCU$GCLtlK&PP(.nZj_pt+],7Xf71tFC&6b"MhU-&E"o6e$B<\^8mHD8LipR.#X7A$>C!t^Y7kCf=qht6[#R6$24mWWJ=O0QVjd#O\'%Q;#`T3[7Wt]P\m6tj5Q=dBp91gE^'F$rEi!Zs(7e:YcM;Q.AV11IXR9n(Ef\>sSkn-\[!]RkLAaccj?jkuL[+5m#%qQM^McY`pbH?HJ at 3$TDJrBdS%o+-(sY8>C_KVZ8AMMY!RAot<#DiV(MJ@?J<Th&kYIr8L<V12fkp/3`)lZh6^Vb,%mK\J/T2pG`"Atp]Y=Gq<&.#IKi8r at -AEQ6/C!;0i>AFd%$/s[_HTrE<KKdqW at 14,PiE.O/ZqYA2/$au!f;bIUhXjg+b0$$U`TW#j9;<4bET,&YLsiX:i`u8^Ddcshk1T"QZ%d9FW(A"1G0m<Lk:,)<E:SYQ6'+l\rnS)erK#Io!@+J,mi&T>mp5D)+cq6)BB(L)3392-Q3_a=)$&k7#Ub0>d?2>WmUiA"BDB^X\*+2*WnO)[`qtKN:IuT7<Lpsf#dmrg#KK]_`&orPo%:0\?P3jR0:.$dGb8h;>Mj">al at U]_82IB+lbP at hZnDe309/:AATH3DV'\Y)_(BG/'AYD)_sn2s,s&N7!G@],FUh[P4DMj[5M")'(<]hX8b\DcZ#<<UFK"+FlN&DGNV4Sq<#c23<>=."3ZmN/A5l.-2ss\iM8ceZLCpE[Ahb;SY6dDZ)grTQI.:TRT=5n`&\*XU2DUA2VJ[k,%>;f1_"`.<)6b3(iPnCUIa]/`QD7WfmjXNIC6NC0r at +*UV-JmI*JL]6S0B>peJ#%rN;I59s"^_9?lnjl`>W:*ToTf/Y5^'P`'s!7e`Gi6bu2rrE\l3!9~>
 endstream
 endobj
-151 0 obj
+198 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 150 0 R
-/Annots 152 0 R
+/Contents 197 0 R
+/Annots 199 0 R
 >>
 endobj
-152 0 obj
+199 0 obj
 [
-153 0 R
+200 0 R
+202 0 R
+204 0 R
+206 0 R
+208 0 R
+210 0 R
+212 0 R
+214 0 R
+216 0 R
 ]
 endobj
-153 0 obj
+200 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 360.09 496.0 507.58 486.0 ]
+/Rect [ 97.5 710.1 124.752 701.1 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 154 0 R
+/A 201 0 R
 /H /I
 >>
 endobj
-155 0 obj
-<< /Length 2455 /Filter [ /ASCII85Decode /FlateDecode ]
+202 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 625.114 124.752 616.114 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 203 0 R
+/H /I
+>>
+endobj
+204 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 606.214 124.752 597.214 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 205 0 R
+/H /I
+>>
+endobj
+206 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 587.314 124.752 578.314 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 207 0 R
+/H /I
+>>
+endobj
+208 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 568.414 124.752 559.414 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 209 0 R
+/H /I
+>>
+endobj
+210 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 433.742 124.752 424.742 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 211 0 R
+/H /I
+>>
+endobj
+212 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 404.942 124.752 395.942 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 213 0 R
+/H /I
+>>
+endobj
+214 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 97.5 376.142 129.252 367.142 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 215 0 R
+/H /I
+>>
+endobj
+216 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 127.236 204.348 181.488 195.348 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.0/en/grant.html)
+/S /URI >>
+/H /I
+>>
+endobj
+217 0 obj
+<< /Length 3335 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat%%=`<%a&:XAW&H(N)Yh-OK#"9+GBbMb7CoIq2<<R)P-*a>W$G!OF(B*]m-(!\o[$9SR8 at UcoB>43(>$Xm`0&HIMgEP3^1-`'6M^*RZ_-_P"47Zus3Q)CtPL\mC2kRS>]2,?"F2sVChBWsl=c(]lcWMGiSthcs/m-LSU],;UWrfuUkP]Ol;ipU8MCn(2IcXSE@&[7;\N+0+IO05g?'Ph4A(%g#\)oe?;O1:PNrBqk542ek`--pXP30(ZUJM;dYno[a`INDF<51DiQ/MC18N91SdXnWhL?nJ+ad>u7;0>hh7\/CR*0="GHb=.+9fumW+c26>?5Mi>.k\!(TI"h5@:hJCWtTiFd1R>n46TfkTmtiBrf"=sT[1\>3HF'ncfaPe.]BN3Q\)DD+]=9o*5d'PTZ)hIO'mOj6.5AYZ[Lj.Rb#qd@^aLA]6E+C,tF8;0cNdDEe]g.^]^;S=f1urfmWVZjuHl9*E&]D-6:c-fBXt^G*((tRPC7FHYafg1g7#CmJs7&4@&PoCqgd/p9+LB\c=&G2Gu5,.E_po]V/QJpfsWJX))@nWPC>'b7k&6Y5bXsdB==U,ET]m"U*+CZX@([<0ONe!k at 2cp-V6cY[[r2-3ir>8Rk$i\q:+[idHb at Z:-n!N?.fOG=*LU*n[ng]NEYSDu)jnq)osiB4ShH]BL%)hX+k!rP%<S=;RG::rE[O=*.6Pqsr_Bp1c/Q1?IV-.iMVMb%dCl6S;.HF=^3IOG;_7OYa9$`T)65?Y-LXPUJ&P,%ai[b?KrB#qJ[9"^I%cfl0J,Xl8.f<pZ'NQRJEo),am%JHu\m.KitXKc.p_[2>:'n'IA8Pf09%k%QHl at Go14BBo[f2s4>`2]?^%jE(P0QOm0Lp["<bD;F>KM;=4me):o81Q&DNlE>p^/u[!UBNOG-M8Wtq5GD0SaF<EK_sO1.#7s*BOja;Ndi0&lFX;0$^Y!ne0WV`""jK]o(#n at fS%>(h_*kQqQPj/+M,NeSEqK=O,#J>i+ElM??]T"bQ9)T3$2p"L33YC:Jrn&);R"QddG<43Us:`Xd5]`$YoTgN?Thq?a7I*HnTj-YXC'6IAHdVed;up;dRsC)/;qN]C5nESJ+qi+](ls.+T(rPE`GJXgGJSgjEnZ<\\H at h/:2.JBgX6=PfN&?\73#4^F[>r]N1gC6Qb8E,4AZ;AR`lFOJ7-!5VI;;(Fn2n0$NcP54R\4Pud0gI+'`L1uBKQUaoJTQhAs)';H<^M?+=Q[?cH9CDg;`$C__>6NT'^^;eX!$W\d8[a#^AdIJ6 at 0%kAp6Z;9qB_X;FrPbj(&<k3/%`94;,^+dJdYT"!Y at RG`HIEsTV$C#JDo=fS:r-g_K^=T>j<3+!.""drOpu6c>/Ul8;:q^l&I%q7FV=0fAY8,/^%Yr5LnI93J7a[rKAd#pl,-GSs4dMjFu>3]eV\(uWA_%l</MMR0DJ02[AKV`Gc[$?_*DN!ZV!,?cZSt,LR)pC[%-fM1$*-g)aq;X"M*@(fWMJo`hLd9V[M0I)t7nnQWRS at j%#aYHjA[O3..!KC#dnc0[nX8X.=3cYg"[PRCJAFakK=s4Yt-DNcJFncN^-?Uo+)NI7Q0XX%j4Vo0WMd]"op8Hi(D=jfStF(`O#0B5Gi`),_0`X?+SV33BH#Z\PWDB4f+FQNU?nY8FX<C*GG&,j.Km!3k_><N-R5:@(R>@[uZ1I)lU#/I*tOIGf=VY:=3A4iIoVB*[dJ6!`T6)F9hNC<-e@[TPC6,a#^B(s<`$&MS\:%8#nHLD_[rcSr)j]cONt51NRU3&^+K6\ar>*n<f]<@Ub*Y\sLnW\Gfj%fg!'1E766oV3]G)NV6ZQV_3aag"k052327\/<e>b+Q*Ds#Qj):KV>sL\$7GO!oSqo:V;<#c:^&2t17YOVXF8JA9IeB5m0ni<4bj4E$bB4eWBhI2lhH/9FEj)sG?8E-`u3,\iG,*0fq?*0GTeD823!Kp('MRa_d/&ip405#AVuSp1eZX)uod9np$8>8;G97X'ZfAlGJ2FQ;]2Bi884:Tfh=<O1D^6?VmR8e<7-2O"3lnP at b-M?3T_k$)=CXkE\NDkj<KOkARCZou;fe0UhaakE5bQ#rKt_;W&j\1Z"^MO6&=UR&H_E,7Q3.D,m5S[qNeTH"?j%7mLu1Wd"?&ie]4<G6ATT8+7B3?I(*]"+9&Gi0tN]t_>\N2_cqk4\fNk.Y2/I`:dSF)4n*L1r)OgTZ6S9]A at j1J946m\J^g7f=LJm1;H[c.Q]si[C84*[GCE2/ILS2E0<4i"3NfiVkXJ!cs$-FU!s(-^S8>fPX+T[VL-4r7QH,6UKF+ko,kS[Y$"PC.n9d-Kt='3!LYUO?jN0*=m!8J%pg0qhOis-JWHrbn$?_1aadD*45P/$c5g`TZCoI>Oq,khO^;5HRD09-/4M36?Hmikfh\d,D^;u)g3G5KUk6!+eIE(Z[,4C"+r8%r;~>
+Gatm>D/\/g')nJ0 at J!23TOIJ1\VJ'HDg.\Fh.E/C/In8hJrsc:N at _)o'\Cu%Ij7UlAn9>5J89^:Z7'bgbfhk!**kauSH!@eRP8CH0KX8Do+Q?/^gD:"#OpL#n_#2[7h<ej+7EGM17rh6:.Jr'kePYkT at a$!cgt*_]Tao3Ik2C>XU6lj4hLaee'3qAjReG@^F1q7OsPTM7K:Q=Q7kS>Ac49s09?aN^h_uB"ir&HRE^4B*W8!Ep^#Ua]5<``7rcV?DT`OuU!`-Fhn\(O]$45P80VidX$Rp at AkLgR'3ZpbTp:Ro'98L8M=]qV+hZmIc!);r5K/o116IenT7?3WS8R7?j--GT4[:K!X%I:rRe4&IYe]cV*rI,V at kSg]oR+"\f(6^7r0YWRp$(K8rpJe)k?Z<:WHkblio.N<H\%%mkKO.SGd=RB^mNcN0AcjE`2m5+Ta:`ihh]m$<g5^@2c^?>K'fkWY^o#I^.4N"7a:``o8_3<fYs!u?)ZFQ(L)f4A0sD8mHZ.+IV"fnX^O3C34;)>g1&`=beWSO"M=mW//keA$:"E4I<d\,3\r*8J7l^ZpOtleWX?%Xj7f0k`of\'+%Z'.;fs>I\J$I:\*6i.)Tdn)ed@]a%:1^=74FOY%CF93/eKrm\BXc>"Q"C+nd5e%9G-!a>Z_]&WZgJ5lRAq=5u]WXZZ$U=',G]ETdiGBJnQqC^C=n!n54;;I>\(SopV>5_R`3GIU5BL3'_8HX`-h1 at ur+`k&-,R4@:g[p6Th_$XQ-c7]\p/A]ca8X,b;?*_sGA3%U*J%p2YPp$WPJ+b4J>R#Q\h%HUaVZ7(`&>)]%JIQb5hf\#e_H+T_>+jc+unf3O\2=cmYJB"q8_V4mQ2u4USkdT.ai9bA)F;$F`qor0rl\-h2&&.L#7ct9!(aq+P,eGYTC2FZ%Du%drlRL'rX"an/(jC>5jN0`#i]L9\D0&'a-:(oCnPLMZa.dFQe:<_``1N!pq[r&&V7:a,p^:_g81H4727lBl;&!EeV,lh]F-!6=H+XG&8%kYc94.q!A1,*8>k<jpMkX5_rt3p`l'jTGfY2\T#mr!k$_q\t`Jpr68-6h9&h^Ur1-!Ls`[0N+\'\F_Dr2I`M)6YWasL(0acgYgcAH8&T4Oa%.K'\7^-OS1oOtKWAf__:=!L!NJOWg&adtIa"/H\_fF__]Z6[4phO?e:[Nl<f2#:hrNPV8k.2olB8J8*^Q),mN/bq2SZA$YiQ!e1n":`1`:\<*3/UiRU+Y^%8o&`P,g$OO>Z?\L=beoU.(H>'Sl(4[k(BYTZ;'ke;8a-\o6`LuI\1\#`>Jbt%$u/o['FUubMc9jemXt8]Sc[?j(An?^Wg;^@>:_ET:UE*N9&5k(c#H5f6J,+L/),W!Mgej&Te38Km"7$^FP'NralZY;45M)C6`Z at OO'&g*kjb?jJ[[8RdKkCbLI7!O#)KM?,aRn[dMYh^I#So4MEFmS$oLdWZ1Nj,%";c"'*qB/L[6`egE(r<e8e]'F`rK[S?VfsKgC"RE-6ARH\YG7*;^=70EOZL>9[BQ3AoaP1AM$e\`B-/$^Ei,P1`Y?ZEH&/G#160>=N:L:*S'A]eAJp at C+;5$uj6l-YM"U<Z!(fVY\r>@rF&lV0m0;Ss%#?FA:u.M1T2O[Q;K:]FMJVqqh#l%fa#/)U\g`Zt,nHnKbC<J!*rg9Ikm3$'hW\<"J%\e=\7]&uHC.p_%9)nasL$=b#Eb^I+<K9I<="7'ohgXh\'FeuG`$k=Yppb')sB9[9!jid2%4Z6Vdci176ml2f!'^<EflQ,ZfMA_c8"</?*Td7>L2aC=&@??m:uH9T9FOJ4u$-/X1V&OKTXh;N.==DK0%56`RJED!FZL3>t.C[l54X]P.n<29[h4!5;),sk-Aqp%O1NoMQB\Qk<!IqDL'S)F*eMN$UoAmA!q0o6\<\E.G<,DlNLZ-ucF(ROh<S1=2J[MNJn(eH!p+13DF>6!A8p=n3`m3lT5\_!-c%+6CO1!o9,i:+W>#O2Q#d7-l<29k3pd^J7F3c%$m at G"DLd#<p>=<DUW4]B7q at +3mT;,]:a'CehWCorL'W6^q-e')BA?"#?aI!E.Q%oZjr@@-bO36>.DGa79W\NV7l\D?$56f3+b_PfIZ%.O(dc(6nK#;Y[f9D8aF:^:s&B]-IXO9j,Iq&3n2)6G)S<h])9/k3qm[$W_%Aada!ms9Du1=06g.ah-Kg,5LffNr$'es/],ab`='@@ih^LoULF^Y&.si"3utqrBT#S#""mX&>G$(4\:l`U!+:EFANCNC8jl3)d=-^TV,'n>]Gp76<6HoQ%+.X<tG at mdEbnNjn6sP5,PmWIVO3^/WSG355[[00iqP5/F-E'u,8m"aZY[K'q5na\dda9Gd!#%A+..jfW<$8F-)lQ%c3NM;Qo@?0,_`T=JjXJdHk%V[>KP39ZS)$V80O at As7U:2pCa?.cGH"$s)G&'[\\bXV/Y>6e*C;mOD<9n>r^^jRpsXhm7&>2XI.%_HX->*8;G:W5<QjXMclC'6F%8?RGUYE8CBf[=GqJZlhN",XQd;mNYgpsG7@/CucN,QdM]0o$&l&L!DU_4s(N2`pHtH$TQs)"^sQF0P[-A_<O*>a4'-O70O?qR0/Y\8LETC$erGEHcd.jm!m/Ug_u*Zfq];dTVe.KI\j at 6$\][!_7mO_EI3e[>kDm`C2)3=b at LnI3(MQ[(U)\lE?[!1b.M/\%hiI#sF)4LJ3r0?:.&PZ'q"U($)2/7kN$_:64B0^cFK\4R<EcP*u+ghXeTJYoS3!:hR':U;sj]R+M/$9X*s>C7ac0Iql;6_3Df(""cO#CSEFI%Ymni+O39d9FIFk5)TqU2t0>?@n:oN-t=L:m9&3:)]Qhe.-\#9B'3l<6HsYPGujEOODTI5W49CeU28!0ju-4\R2jbZFiQ at P#$o!pF^,,-EC@^qNFE)XYM!/e4f.h'>pJc+a_?5`<L<Bp-@!Y!]V1D#g"CgFe_Ug#Sn`cRHXL]uOBr>.\BJsQp7!%lL8!11J+q:g198jq+-oJcT1uobABf>c8Pn$]/XPS"V[OabHgR>170,9b]g/pfimfj]*fIZHqt"mP+E5BJMu,fr`'80tb&)no>9eR;V'rS-:2/1f]30+^$>q58L1oja'0A+0m)CUGG9q.]4'eU%/Y*5TT\I4(:t6H'?YD:(LTYJMlu?qi!;lN("3<Nb^ib)X0bTu3?e&#pW7'%e:Z)Q"D`.1j[=dD*&[a,hP=0JlZTJ&CVhM at V!$O?4g219#r_5obkb7f912&l1J7QF?)eq9uF_n9\hg%Hir]#Rtir5^qIE at LX_)cQlY1J.5=%`\1\c"K>o_Y'U#LF2$cN~>
 endstream
 endobj
-156 0 obj
+218 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 155 0 R
-/Annots 157 0 R
+/Contents 217 0 R
+/Annots 219 0 R
 >>
 endobj
-157 0 obj
+219 0 obj
 [
-158 0 R
-160 0 R
+220 0 R
+222 0 R
+223 0 R
 ]
 endobj
-158 0 obj
+220 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 396.7 698.0 532.5 688.0 ]
+/Rect [ 300.081 663.3 439.32 654.3 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 159 0 R
+/A 221 0 R
 /H /I
 >>
 endobj
-160 0 obj
+222 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 687.0 415.25 677.0 ]
+/Rect [ 202.053 309.214 562.755 300.214 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A 159 0 R
+/A 14 0 R
 /H /I
 >>
 endobj
-161 0 obj
-<< /Length 2273 /Filter [ /ASCII85Decode /FlateDecode ]
+223 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 84.0 299.314 116.49 290.314 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 14 0 R
+/H /I
+>>
+endobj
+224 0 obj
+<< /Length 2156 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gau0E>Ar7S'Roe[;#ATb5f-'VW69q_+^I1:fEGQnLLK$AD:>p3=`c&@Y-IE0mnahdd&*>?79[LRKfk^jgcd2Hj3a at dk^MTU3.ue,Ee!cRaR^&(3fuqk2k^$A.Ynq8Z(lt.*ISd\N(;K<'.8Mnj2K)nc(<t4J'[YJOMjoT1?+"m1L<.mT9kGu"eV'Zb[8Qd-535C?T;,\lb`Ae+8X/Y<T<l`k9"@7^6]X),]pmKdb8JVs7I=C]UZ,#gWB9YLL8ncRYd$TeNY_TZT"L'(]Au9&!bX6LTs@:)`1a$YO-*LT7gP'YN\H8<rtCfQ2M0EKM3h?.13G&`QS7 at .jX1LM,N`DpbWXs"&%GEf(JBQL5\M@@bAt[<(IjX;QeVOScYVd*AUG>-.O<qP,$Y5Y,h5"A^:OPB6&/cm.R]?`/d/(,U?gjfS/5SCtIRp..t(6!eW!5*9hCN*'$$_E:BY<+Ga8L0OB#WlC>R.YTCq6%$%G3!d^Ue7!dZRY^pLijTi_^_UDn=0klWX/QJ5mNU"%(=_TJ0\oc"kP`WlWWlb(tii#<c'eUdUD3a$59[7"rM>EBkqhf%*A?QWtjQFkfoufD$D!Oks1%dkXb`[KEIZ$[d*3Wq=QWI73X'#kG_/X6&=Wnhd'u_FYdU^>E*RGjQ`/Wte];>2*cmN.$.`fCoRe$q(K"\-<#(=*>dYs&Mg%PR$RTEYc"It+#"Ra"CT.krp@"Q;oKQPbG$8Te8 at 83V"]'kd.VpEra>FVQeU;0[&J'$tlDUp0, at H?I7j$;XjO\&MVjXN\APG(Vg!$3@>$O8G^$*>.4qSf at u":6A\6>b+'b'!M3nIOW0pt`<)6MuHa,XF at L[IV at 9+:)IJR-,FbX]_>jCi0#o1&qiUXiRfObDh$L'!\.5d0)W=JB\6FQV\fMEGWf-4l!b#2lK71dkeo5M=8iCq:2VK65`TmSDGhOUAaa_U?J\3&jmYYoG%T]a!.nNQT2(`C8VS1Q2)2ELe'5Bd?UdgABPZITOa?sSil,@]1[JteV;e:M%dsI$OPlRo<qQQ+fMX'W=[/)\]b;K##ZF2:C9&hJ;3ha:6TRkC&Eg2<]b!^#)s)sLQ*SWnhJdsA1jS3[&R+9GFq>Hn2Gii`0qtGJT_4C5,*EC&f(m1j`V)p]9$0YmRbC6O\t,jc at ah"jB=fEdfe*6M+aSiP&VH!7jX$j+]Kql_HqI[Bb8d_ at e]`d/knXG:j/UaR)V%RG_$;hOAq^#6NeA1N%.dH?7WZ*U\V/Em"J#^UF(iZBEP#>C&"9c:E0e&gaF>QS$D'IamDC.fdA?T(@'A47"PIJ%>Mkobu6&&9f=tqQp,sqTT!<(9P(]6'HaG(k:]S[13iK7TWo9pr?Ge.qTs3"^4D4E>jr\$#9<(+D<,qfkBPB-VtpDtJT9,>r_8]1m!#\o*L[ur`g/jT!sN71:+uJ0I[929.\.iH"8pl./9,*m7$K]l=un&_E,mWIn>__&M"Y4kYml2[d33/bG1dUZCK=`'m<(2lgD_I2)-5hi3[X73WL32_H`.<>$q]cg$bem,FDm:_4%r7O*Cd:DM[*1`o:\72f-Soa+qL at 7DLUD!T4U,WKR0S4\\6mt?qZ4P:5_2$ZL3GFh+T:u*FO++k'h=Eq#H7e:Z:,9ABUL0I*.Abn_4*Fa3Y<H!uYgeOD8'NC*<K(GBm$m5/6HVlsth:kZCE'DhOCJp0f1WL*0`todJT?R\?&lf:TJ[)j&*fZcUp^,kb&dp47SsUHKSk-%'JT<.tp-N\'0#4O$PaCp5IT>S$&LLnWI[^SVkK1F<Da4TCRV^biS[[kIM-bu-^0D$[Ie^C]#i>el_^QjK9d%GC17S*t=>^r=!Q&%J7-\M`Oc<EUb\?RDEuaJ7#7K)sZY%'>TcKRJV*iXCT4m04Mc;%)iB\p8]mM2P\Yl:T;0$]?JbEH7>`&3TFXbG?WS)2BZp^5&-]YBS%PD./+!g%PrWDQra$h)dXn)W at j6(OgEb)\2.M0#`pl'n=#$eqBp,E\?o<?)0eq4:aipD@/Y/lh@>E0qP]HRZ'CW,^Qt^\K0CWm[Z:"Ql.-dC^]tPOCF0%dJ>]d8#kr1iA\>18h:Ua*gNn=6JgUVh'H)F9]OLBF]B9WA4E=/&+sB>0X8(l`hn0W/l#Pi,+-?hHm]E^lu+6VGc!Hs0O=J[8!@D&@5Me*0)- at +6^Hk%:!N0!G7n`dT'A.^U=6feL+`n,G<rjmf.[O'TAp^3TB#X`/e!O at ZQKTai^F=)VeX)G"[r,5Q1utn:PZcKT[aTu]qkZ/9Jhan~>
+Gau0E>Ar7S'Roe[d.3>&8/R4.,mqO2egqhBZj>CHV3m$iO_I=F/@lIZbLZXd*VlQWS$]kHD1P`R7%X:"^-fTF*F.D1^'lEP1&M>)l`4 at t1*SPG6:PM$;\%FB0o)YXW,uu;GX$Fq41KO+"pn_Sm<Cm)D&?+E)[9_!M26e;`^C)W3XpO&Z.7#Hlp*"9FRn$tn4.HpVHXT<,'a`t.H,k3(1a9n]+mPQQP43hiL.u-DT^q[nFXchfu6C(Xm3KclJ=RkQ]pil^!H$4f=3S7[!4r*(8V.-M,Qkf0Q!s9(A:loWb54Z.EF0bWZ0DdR6KpXRgP)(T$=)S9M"p?MnQ1h;-33cW@#4Ipb3!\NEPF3;JZ/NA'Lj<:3/KZ)69L_F/M$bMmuK3:']rT4=*Sc;"XTM]caA at of7A2o$X+Hpgj'X\C-HjkNXZQEr0m9]K"d0`O0`F`6#H*[Sg\=k"4%XXL'GaPaE[Ye)^iaMI1g`W$:86ZQd^pJ%rioo'FSq)#P`o[]S63]`r7%GIEaUp+ud4FMF<VQd?)VSB\t,h&DsHV(W1DWdRWQP44-ClO)nc:N;36d1/P;kdq8FgkVonmap#hOPrCR64tT)'%H"g06QYb'lq/(\L4Rp6:+1G^[@FAG+j at cdoh5prCJ(IaHfO7ZM1jYEdNa'qZPdSK\W6?4s.-b84P.4K6+Y[\\/X)V9O!f:iRUVC4jbtpU/fij&/eU>,K`17@@j^ldhLG$m-OY!2<BZ!AKX,P"8*=IiJ-F(Rr/aO7dQp^o^+dT`hpuHkFKn'6RC?g4L">n2jCS2)G3:g':%(iF7ImPaP$HR9SLM>I\FMW,nE<\P at 9W:*@d8qVEYH^(3NN("7Oe.aW*mn-Ua&1L6u>m"C?j?aT[\cS<qK]grEJ)cn%;g&5MQ\nn\g[3L2_m=W:%9oZHfd+76-_O)2NdnD9qY*;>R"8>'[]YJlq<(6-[d3hOKqt#WJF:U]q-mm,F467S>*eGOP,Esfd?JDH+opiO&d%.;QZ1-'R!,G1!S=9S!9=iXO``Be<r6f8;K1O/4c.1RLb?Ip\"sMC[&(g8h5,7DRrp\+]L[O?+O8-CQO$$V:1a6qLEaS35WV$PEcT5jCIbuV=>j1W6SL;4 at _e!"6h<2;:L*227qK_M?iTX\JbnFKoWs8 at .k8b^;aOWhBX=umerLr0OpaG/aN at p]pn1BEKP>$/aZ<n^VEZ4>eY?_c.TP4uV6:eOqc at 1lifMWFD-2Wq&5[kqJB@>J,AL)i,nF^V*DDf6#JW<hqEc,Qk7W3.R(tKl;DfEFNIE)pO7`+-+c24tOc^CBt>6IVI=3h,I=;87Em6R-GE\%gW89]uSdlsI/"f6/I!Fh]qlVfE9\OMQF++%=fnqMl_4eYpb8+R\[DV]*2dqM`5Rl+f?<FmXe/)c`$$97c$Bf.A6p>EdZLdQA%I#k`]BS*CTp:A1iAqNk at S$Q5C(=\3VZ?+OR!#hbr+F4mB3^2OH9Mkq at d5<fYS3<n]KH%0<QU<pA)"bm4gbPTQ%,fpsMkEc+rQL9"D%2Va[ZJc*[rq`W90(5\9<3%`L*oL_N'/B]9*:sgE%M`)kLZo9$dKF0p&<6_((p1iJ,p-+aX/5l5PM,d-kidr@"^;HW:bUB8H],Ps%i9>`A%e0Z)>PmTTG!E-;:tHj>\giVZe-#@tG<T$k;WS5AU>u+jT3Ok0nqme.PA=O\!?Va:ILbbPe-!;Aju!*jEuuMFb=5YE,P@*qeh5J/MMcUjQ*N at D(cL`-:J<5T#\1h+<,6e9 at FgSaV62J@QkIV*LZ9Hp\O";SjCr%o.^Q!nB8d5:uV*3+6p at 7/u-i#\2dGe1l5sT-V3HkXjh+R=CJC at 15r2%m?5`L[%\"DA!sV at 4&$HTV!<S1AEHs&HO<eq;M.cJ0Ta(VRC^s"H)+u)8kV`pk3=4:G$e8aaJ5MaEe)`XmMUu`D/RI$b259>\ChEIK,Pi%d@>gU;.cF:1XrKmW<Ih/hD+<^_N5()26Q.a]&el;g@=aNR'OQVKCQDJSqPKamO+8Ws6QDE0)h3H'$V.eikH.bXDLUaQW,:\G2d49Eqg$C6\4L#ag$KljkLL<B:u#^)i+2P]+;XXhTP-!,b(#'.uj+Q87 at 2/Ef51F"tk!5Gl#:itN>O^cRmqQ&pg1)R\,$Os#"Ydm%Oa4L5~>
 endstream
 endobj
-162 0 obj
+225 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 161 0 R
+/Contents 224 0 R
 >>
 endobj
-163 0 obj
-<< /Length 2837 /Filter [ /ASCII85Decode /FlateDecode ]
+226 0 obj
+<< /Length 3588 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat=.>Ar7W&q801nCoid.pFselH>o9;k+<NK:^Z+#E/<6J<hXR)i7Nc.^&gM;O/oZ-laTdqG>9HadI'bhi)T^\sg?/-fHVD9if6`CpL_]-StKMS:jc:j'ic-iF9*m-=:- at U(B>k6HS/3o%.Dp2gT\^%/Y6u=-d2h?:AT*i.]uA$h&`dmY!;(GZDlV!:KEjdGX-bNm$Ca0g]coD7sADX\5mfk,'TDd=tlsEk#\$!sKRFF4M0<SuqhYQf3_/8kP[ZV5JN^#9PRV=cZ3#Z%,jaf at u$o-7pTr',F1GJ.NYPXXFZu+kS)B>!$-n/8>\](!\"d=R/9"0E%?#/-59VO&-9f\RQ/Rr*Z@(%i2H'"5?K=]g4Ch>]Z;H\8mo(ORBr(\(\BXINOXYT('E*RTBu2ru"QRr[_1[_+OceP291`2\sAOI8CATdL,G10j+u=f1!gOXm,]k"T'`@AoSTss3l3)V3?bm:54akUtl+?o^>TEhQ1VlXbaWpN.O&rVt;V^T>a*RG'S</iV(CBUGs0kDle$!eo9ba;;Ym at 5euFLP8j$72 at V0FmP6ZG0>sY%Q>=>7m^+=b)D?EM.VA8mN3Q5<AA<ZjEY0Q73p"]FDEg at BH'eaQ?C$pp\d\FhnKaWK$dX_H7m"9KZ7tRpKYdR'R#@dg_0/%lC$Jl9$b&hmgSW.>8<`/>D>;W#DcKfJ8 at 8^!X?h&HPB7iLa[q2ILG+.)(ejG]B"5"OE\>4['/F'NF&"K-+a7p>C7*A!\0m=<f+hp2H991h!es"[7#?;gF93>A*T&AdW41i%mSDkW)YU+o\/$%l5-2VC?u9>Wg$u?PGuUgOV"c%>$`:?8CbTJ at O3f:U&C_pL,*$W_*nV/$q70 at kb^)D.k^4a!O=S+&9X?W044Gk0JiT])\mhEVi>rM))P"S_QP'psV6`+)P+gNhk:TRD,j3J,qXnsa3CS'EhoieqhXl:f$r6[.=Wj+cK#Ie.5X=;V`VBu(?sXT$)Y&Oq3,<=o79\%+iW(1ihe(\B,`\5-ZJ/Ln3:VVTiW?cLj2P5ZFPg4FFCr_q*NoK+i(#S9]aQIr at Jkh#>-]Q6K6`EAN\LAj`pc?Ni?Kd4q3,XBn+?;66Q?")'\K at 3l8W"ej>7=_. at 8MIR9k$9mVGo&FZhaVjnGHZ1%3G5q`#I^F70&#Ck4pR2!SPCfpu0gFpEN,J9V^H6&\dQH[#&sA_j5XiLQm-i%<Xtcp`tOW8?))N&(Ipq1/L^KNk\`V.h)\?FlANPuCf6=AmD_B\CMrI.g;KQnt8<+S89kisKiVc\:%7R*X07"GjcGbL.NPMk:r#EO]t8(9SWGRE(<%N85=pd<*Xu+JN-iM17QQ:[%R?PN6.D\-s,ji*d5U at DAf?E7:Z?meS_(.t)uQh)&=lgiZc4]DeUkMp5RTR^g>=-_0ts$A'_].T?K7<\fQ-BoWY05)D/X&2NXa5'MQg3#SIHic;>c*Oj0(Zkc'IIOAO8$hG3U)7J5.hB"d2?nG?;VR/Ao=kLOX8#S%=J?&+?o?1/nlVcfnC^..23,.UAoS-I[G[%/p+9s5TGu0ZV!<*ebAsWj(5EsD@^2=G#Fe*$*lbdM#oQ]al$/?$R@]2+H6?6oq9W4-Ca1c9oZ__Xp"pkAQEYPO)=>L5pFC[qXQOV7[N>n1<_"=#;T%+mlF+=8',nUt0Z3!OVX!UC%Z[dN]qtHp"E.TTsqNYFBF00cUh;L,KM5.<4k"iIP)L-+ag(;>b1QZ2BYd:8KSSj;09=PtS^/=)f3+:<G];+Xc+Z/8t<`IGs$oL!=iS)h0,3Ld^7o]*_U_.u,Z(Kur?D7l^$P:U%IKDcb:TgL)6;k6?)Ao99)`3d:S59^3k6(bi<nLs0"qOskO=A.30@<#k&qW7d?(jqiL`]t$U+GX#)p at sap*TSGkUsO/A6!;YZbJ!"W5/ob(*/T6PR at -O":[NCA#hqk*_5.%5,TeSd4lRdrrGu/L-??aqC&f++oBiLch)eM3pCoSjT?,g7?0`Clr4E>!72%GP,p2@>:&m5/HlO`cc9C$0#q1EoGcM+Jf)%Z4n^)M\C;-:mJJ_+H9[mWV-t'kF0l]p+F3HD*"O;$JO;pae'B3<*W48Z`bVHBJF.*c_dc4s0jJe\`@dLM at G^U=_l,f1S1DI-&Zm[)m8<IWT6UCBQim2NV^.u4V&"uZSU*,-^L`omqTLHrXgt<F%_WnQ at qCTaAg_@Ap'4MH=A.U?36H?F!8F5dL6&C.<F at Q,1158q;%8c'H?M25C,tc5A<nU^.>>/U,kIY<WmNN-YjgDPWZ]^nbMcFidH=j!$)m8!Gu$T9QYMSQ5o7Ig=*RGZEb%BuMh*Q+MStHf.O6]i.\fgGiXX*DVFt1RS/GTK6+Cqr:bIo<eQU%!+S9Xm.a75;QE2ehjhX?k;9^"[ZI+K\1/+KJm>o6\-%60gDIFJTKOE2nITT=<!D:j7>O=g0M/OVI4O>]s)&SkgH35-4<t3$T;@odMjA4"V/lO%Y1.5ceB/!@Y-+=EiRcZA>p@(fO)&q/8W^4OFF,r_<3b^ta\NG:Po-Pfibeb9$$%ou%iB\j>cfHB%i2!<(P_bj__EVq<'P\dj<C'KmedB]#/%-Uq_S`Zem>!mrm03B19k7iP\n/4A5EE@@`#paFKBjCKCYiua6oP at j=ef0Tr[eU;)>a&99rcXMC*5<N\ED<Q<37d at D7p*N"5kMNYX\TqciBk711eRiYS=Yi4MW*,mQVd8)1VeL$'";Fk/>Ci/W7MYA0bU<pA<N>./E9,*%8:sX7%(0mf)?"$/8>QnTs?#1nPI`IpCg2L>hAZ$,S69D[q9[nS"F&bi:e(D%U,;,bB_X=W.Oel%C<#G5=GLXDL'~>
+Gau`VD/\/u')q<+U&38!5eI]dakWS?j#E%I3_a)fLI9T&.ro2UVRSBgU^2bQml*msiXE4Fff9bu!Y-Y3GM\s[S`-'[g%FFmnSHE9+9!58n#q1($QV,H"d[EMOW1iMI:8+&jp%SIN+Q_;T=pY_]m:KS!bRK?o<U0aCQ6@'^GW=6JGJ.P)#MPKi9>:Sa*B6-SAgmI`UnQSipSNiIuYitqDX!\F%:Zn1"UL$(]H3qi]55g@?&MOcekq:nAf%sm6'N2]]a:3kBF]@HMoE2X"'B[s*OR/?%8?Xk^WB:Ss.N2[MQAO?G4dj#sr8"rK;'=RsBLs&cMC9XSNlrGS5dJ/jpjh,-L$*</WTJ2_\^GZ#:_)Z787BD:(8987c*#5GTT;YM$\&PrYPA(MO*NMaGaSn$'lW2,*NQF+A at 1YPRR"J]O&jNunop"c(,%?B!G1[4Wsm9:i>WqiIXJhVT"*du*fWoMW0%CGS1g\#gF"g^N!*^tEq'm!-_^CkK47_[s8b<Q(:L_?q/KJo=l;$%0XZZ,Q'KjBF7CKCC!u2N'C*3,8Fn'g+-pXPE></m"I>7+*a`i<R"4goK(6aZ;p]r2IFN:L>)2HDlqD*s^!s.E="M.23ZSYf-,8(5=%E/hIXN;lDM@:r`UIhuUVgY6tn\jFKH<YMJrJXFt"t.oJE*>Iu'Maj/`kdVeDS/9ID*k:`Z0RI.D%1 at MCcVemh3@,,T%gG!j<6oOu/3<;Z+.dfgF)-iL at i,p>1$\arlY4ar'rJj%jg,A^t6sl9$R-`I?U3q0U2^#K>.=fEY.Is8hna=GcLj=t+<3[cD]Cjc%T;,BnY at OA/;pgHQ``u#'%M[UPghm/8%pZlSMJKJqP8_5Rh13B1[7puJe6(G3Hls+<WqLYA/;ebH\HCmcMd$5O at R-n"3sO[ji>%u)XOP)G1nVs#ajl-Is7I]l/&@F9r at _AXUHLlV%-a-]N>#nWo_NVO*'4$=aKq$b6g at pP9n(o62a\*Ec=`OfCa/->C6bVMVn6G[mlgWK^9H5)^ufc2%*T;H^?0-!\^VL:?HArqTEg@\D.[pJ;gLc"RsZ"X!,b)67J;-F$967d#4Kl`S7ZK!s#$L>#cJ6K-j%!sc]L:@3I5h&oN$dE>[tiGF04?El`)SYH^TC,@!c%.+kS]p_&GJ[mY%g3`uO6T7R?Ph2GgY9Jd;[<%r^?1?%BDZa/^O:1W!O&B,,TnW\eGQVSBb[Q/:\C at CJ`ub4'FuPl)1[HnP^doF[g!(]HId?Jc/gILoY='IJD>^;`3Lb+4R77-%?/:ITj-2V at l+ok7L@qLE#elV6NpFs,]L/)cTO7UBR+pb^JBCFG4JbQP#3IS3rm"2u.!4/F!nNM!.'W%]O)DH3K*?6Jb[`:bJm:JdY1b%4ik[:BD=`L8@/d4r0-0q(W!<j(XB1!jQ_3uque`SQ5_N3 at 1i.AnhRAU%tshZ[]L=^n\PnUds<,fnEhKO.&GKc*u4&VFl>?'<cXi-NW93,$K2]iS5:m`J_#PUPS%0$rV&66LFVq<i`c1sApO%`bS9D%u"Nl9S-pU%uFbE6P86;+#&O=in)*L(dJ3BmU(I>D"5%D+B0Y:^\d2<JPA-]15^9$\_MiMcg?46)mdI#?n09c)$IYiF!-1GTIfg.6&s(5VcN%4Jd3S`+,?)=r7!"2N?G at JYQ0@!923H5YAhWWmhgn"5cgMoh>d#Nb-q`^b[Jnol=I9<1GV6hHis[:R#$+'I<\979MaFJ]M>ejj8G4?N5>"M?1bX=Ps]rClT1OI"hc9<q:uk#gkF/pG>[oFdgfBUEHa:d27'r>!Z?R>">m2rpXNWUS[8 at M<)J+8?V-Ag5mW_-_f]%q>U6^$+4!"_kM,>?CWH at 4B9`pA_nN8g4Inr"YES2.CK/TK+S0'@ss9Ym#olg-VqT05kcQA^,1N0%ZX="XH%&"G'^d^&&QO%c*#rF!40MQ*[OrQUoTDY<#\t&@#WLnK&abJA['(V'=6VoU3\/*?>n0=#/I$ildjKXCKj_OLa1-6UfD==E',Jd\&r3lBQj<X5IBZ77'_odajm:j2kP'&a-Tp!;T96C^aG^AFBI+pPq4'F,&LRs,u0a$0ul$3(d_i+Ad[%,4S;!*:*PCnmnmgh+T)_Tm=sX:$>p[%[_8fjZ5<4)\=I8A7741J%fGFsKX?9ZnICac8l?V8/Z?$k49,1 at T-$aM?o-R2.CWA at rt2YqHXuGgph7V87j_`;rRq4tTj'b%=Gu0`4M&>HGf[fpUirL31tNpoTLWbr`$WE""NQ;t'*(3I;=U]c^P49u_ptW`>="p00!e5hLa\uGYfDh6d7e,U(3hB:PE*7hW2N<*#>Gr`r5.$G'rcIp#I&>01&8uDS3[Rf]*9G*XA9KgQ4FjX2b(?k^:V#Tq>,7&:NphcD;'Nt:D7rm+6p">7`T-A2J<mtC$Yb9cI3!629_"ZYC8G/\=6`0rA+1rH$0'h\(4TD<;2'as#3O9&E;U'WKj5d*cQFhYNkDU.Kj+a(=,2QSrBT1+2M[CLtUNpC7rSQ)Ie79Pk`QK#(Mg#q3V"&Q^\t1W at hXg+QN@CEPk.!O:Kra><ZZcCGLh$Xh)1 at XWd5;L73 at 5hYj5eH>N[F)CTimRV+d=^aH91)cCW,f0mGe<]>8Q!f9=ZLWD(a:gslIppOfXiHf>,BhU`6EOC!L(5!ltpaA$Z^J3&%n#\:+m2IH6!K0("h9D2`OlgBNdif=T1RZ/^K^qkWrM.S$Mu0ktoDdPh<Me;"U;37@:Kt]%[3odjG3q"(9RNo,?O7q_Y6m:BWX\^qiFo+21h.KsjQTfGU<"n%#mb&jT/IY;(8/%$Q8H2;n#,?1XI828b99CoXSl`uQ\N_il14D=m4HCG at Aijpko)#6$/,02l<IqLdu0ZQ%4UFr*t0^K.e-)q0t?`\TU$Ys$'2mM&WZKa+"2[RpVYhJ7'Ck1)g.%i(%MS/)K<W1Ga_\="3.0n(HbL:AZ9Ot>(H5)Nr"`Dd#t49+sY?lK7i;n'/%0&&`3RhNcZhE0Q9b7U*6kngUZ/!R?bMPHXK1?bH"u'n*WAF:>R(rcTN\u6ArRHk&FEL'ZPk0f[b;=c`HQ:J`aB:>X8GQpEr$[(??CGgUF<b_q3Q":[!$W@*Rb5O?(JA5ks;t/91S+k)9#90u.Ss[1:b+!\K4Z,Vq;r::1Ol[@#IB at JEtkX$f$W:8c$547MWDqN;Lr/HfE*SS"$4j[g3&!N%UuXll9W9]m]5S:'C=30QWrjqY_-rc4TZ_tIH:cgM;"`!eJr9Y,tRr2CH7o0m7u1mj#(Lo88UaES/NXmZW-##s`Q;>W7kGuMJ`2L\#I4SjCFLJY!K4S5bK`l4Qs?uIb)e"MsqlI3 at ndEm=iLGV:9kGn))KrNO)*NA_'23K2L&09#o0+gSr9N5's4Ijm*W7%0:.rFhc,3F*ts6IbETG at B\@E:uUbqn^!1Yg%i`r,QfH#;GIJSc"e-b[bP*C!&l0<g3HcolVR'e-qRTo]%5XL]/eHE>K3l7L=*Rh^-DG,DN(O_?lgE[BO6Pn+\,<<Fn]Q?=A<NSI'Q-OH<:D^0T\L<Krd=;eudXR".Rq8>Wf`HR_kP_eUeGU)mPCe)>5qORTn(3iO>~>
 endstream
 endobj
-164 0 obj
+227 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 163 0 R
-/Annots 165 0 R
+/Contents 226 0 R
+/Annots 228 0 R
 >>
 endobj
-165 0 obj
+228 0 obj
 [
-166 0 R
-167 0 R
-168 0 R
-169 0 R
-170 0 R
-171 0 R
-172 0 R
-173 0 R
-174 0 R
-175 0 R
+229 0 R
 ]
 endobj
-166 0 obj
+229 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 361.64 547.25 467.77 537.25 ]
+/Rect [ 272.226 364.48 472.026 355.48 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.0/en/can-not-connect-to-server.html)
+/S /URI >>
+/H /I
+>>
+endobj
+230 0 obj
+<< /Length 3966 /Filter [ /ASCII85Decode /FlateDecode ]
+ >>
+stream
+Gatm?D/\/g')nJ0iG[<LWM4T>$QKXNg;Rs<`HL!XMo4Y\THuX.,DiQOg!.:HU#.[oPIAq'/C:e at Odp^ZiOk;`^:UoO3LSs"5Im7jplgJk@,f(R_.4J%G?GK'GnNj>R`U%cM"]rk<LERNrJSDL0.6djgtfj:HlVR$Yoi'Nr'-%D0UYVGLWP"b^e_Xs:3fj\G(0+,-C&nRGde at jpV&TZIVPoo at AIP/'?rtj8*TGK/3<b;'_%joQfH]'Ha_.,Sp>$.25Dl#U6\SV?[Ht1e8Z=P6"nJ!f-9V#]=eCc`lGL^<;>;4Re=>b`og-FB8t/>7"lm[]P/;5L$q(R.TZ1.[]d,,WT!fsoCjnrL<H84f,1n&6JouGN5!%>I<@djLR'I\.)mWd7jC)P::0M#_mIA=*pIKr1_Dp*ItO:GJX:VDQiAnfoVLP<\3YuF at 6\03S4i!$o_]i-CHWA=%n.^^9juK"(rb2N=?[8/0M(Fr?eLTf6@(R*lY^7FjIH at ZhL7-Q6GmtOZmZB6-H:uG=fPsunIeub^poQ`iu6"dY^JAm'tiiMk!T5*!rk6c\)LF<VHN8>4+Y*T]IgYa%egk^PCV'ToW>>9pbNjR5.5L^ij[FQ9kBp^n"r>^5G5l$MV,V3&!J?[W)Y.$atO"M"%gd.F5&8mH8H4H7HKtPBFRch2T,Dl[bSSh_:hrLk;>2FjM:aH#K26e%WuK^6C\$6]5:K4cSe at dHXWGmGm;6+Q]9iXMYETW""L=#p>*M[QmrEU1cM;tjf$:b*'0g;b6r&'eTYQUi6gKO$@W]f at _^$I'o$%O&ri+H^DF<lE:$LD:b>Jk3!mEl`5\`ZbW=;]XtK)MlFSFEfqZJYp1>CR0?&=a45M/rh7i^PA98)T0;[&>n54H*]I4rG@&'A at HT&cu7UImmC[L3L%VF+FL2$V[*eutj-Ghs(KBqLPo-6X;KA2>pWp\4%IXcci+qA.G([&dt8P%GajNs2WVV87t>gHCsdCG/ae/?CSEHhc+S;CFs/SF#^,cfVT7;,;4 at iIEV_([WW3U0PZ/=C,,F=55U`ehe)l5!)uQAX%n'RF4UahQj=_I/Sj&QujGo(M+u;n72aL,6ga2$4biI6:=RL_?G'jbs&P$_rN\oD-:nf68SFN52nrVoCu\(<"8Qd:%?(,?lg(4^_J9pt]H7"i\>ckr=h.UZku.,G`:IARf_CARj$CP;L'I,bU/-*Z\ifjiEh]2B&fAHf"]c2?Qr(a\ao^Z';Ki)EU@:o5r]#&YACL7tIM*nUAG3:hj)`C;Gs5RV58dD8)tLD`j5i8cjL[AdiU at L*=PnS@'t4Nsa8)jZ]&.LuWkVqq$5,F=:7*^`:OcNcON%&\!Va%W^pI7SA>I$D\ES5=-)mA.^*%r&Q6XLVu0t4,N0QG(H30OZsU'GA,/O3^BtsbKq^@F at 5U2J+nRu5q.X:AT(9Bm+)BZ`\=8>d[4eDQpH0\cJcO0E*cnV-0C1LNR#2&7Okj.U*U[Y4Z'[WC!#)b\Np0#IN*`hr`H#1q>KpX]+a]]#c<]?cuD:sQO:SY,itH!rnEoLhnOVa6 at a1C`N_/'Ip-5t at 2Ke<.u\6&\O6fK#D`eZcQHDYUke!MA1<rM1!Q#Y'V=>IoDEjE9CL=e1UF&,jDDX/"R]Q=4e=R=9abZT*l[0H_5W90ACCshkb$XGg%2m:J]us-lMLSFdi\$Jpg7BlfXHNAN<>;@6M6<6B/B-HS>TNg..&o*)?Im at gP?pLd"'"])n#s%E!5qRQP.jhUuT&"M1_VtV:aH7Y>X:LpqW#5W-H"[q=GLRUM,E9%0uh.6:VdkYbXN54"T.KTbUqPE[6CrZ6_R5)T(i`=&`YnbW+*+;ai'mV at VsYg9W"86.E8bOY+Y*?1ErT[^Ira(rnX*Zg+YE]JteVK at p$h`D9Q:)d5'ol<EWVorc$3!BFsLB;(Xp$(\jMW8f#jP2.(1A6'i*9Fh"%KJR6o`\=9\jp9&R50^0m.idmI0I50!cA:aYTu7Nf`-r1rC-o>3H7QoLGiKc1GW)D9cTYE\P0etHJ.+>THG at A>@PC%:dbV-O)u'p1%qT&+-hLuNrM(gkli+Uh5&b+KkBcN at i&MaKCn=hPUKa_$T\apln/"U!N+/a719/:[S*M$Q2i#D):m at l>ND8Y6=(dfJ&6+ at oTK0`0ST^smVB=n$aa-KK9pMR3F=4(CS&(`)ep'F/ZCo%mR#;<&2/s\a"h2*R^7KW**YrG,49k?o_p at -Z;4uZ^)'*O,B;+Q;PGZXJcAIiT$f/N++lbDe31fP+8`Bn5h$Hrio8?k5%ib8Oq>?BDKKkV9>n7H\I+ihfc0#V, at U>VHO*&QW*t1\/fkO$<l9lk@`LaU"'^+.MWc>)O=TpuJU/Gf!V'e:kEHTZo`@2geph_#Ccu'&Jb[oIII#^fo:.aP72;9u(85Uuj0A(t\%'s+OY1>kCn(^cmKd]:3d*.tu=?:%m>R*[p1JYVcq]oBp:>K&G'W?k<Ei$e at 8bK`9AXl5bK"+[ZSIlF[>J)9&3%\L*`)QFQkGMf,4L<>)dp>2TppQ8h#lM!f`R*!V?uUY*p-319jff4-JEu!I_W(3]WUJq_9%DdTp_W8r3p++u?CD#u*Ge/%S7Z9pquUrO%Y#Oq/02g!m\FKV3+$#XqPf[:90A`OG0$-5LfQW;Xr&i/*U.[&#2><'P(ETlPPB6k301mUB99TEG=D%JNm!Z"+E_BU7$`fUC3u?1ZGh4,S$;&mLP!#h+uXu_j?a&bW6)7QQ5k+/C at TA\B:!MtCh_26GIJ\b18rSpHm_GY2;e1K$]rK[bJsmF*Omk#TW*l,nY9t4Fp=iIE:^i+>mDlgR$;\Y7[>K7q[X)_;smX7Vi=EHmiXbAf:!s"-G<@c!T":1J7CC4'qUiD[G<5cr;97Obm;1V)/"s&NO<skCP''5KGj1dL%akaC@:`O8`\u0Rh3ij:6f9Z7.WC8UTqFF?"(mh+^'"iI,87J2.GWn_-Rjg58gL)0)UTa2WSiGCrQcm_*a`83^c,^;$e4b$pcSU(@R9Z_a[e;4_?dZ#tMT&jAM*28G'"WMH(mL&T6KTqT'W4b]r<F!bOR:C8Fg[Ye_1h.8A;YP-[Aej[0-<3d[MIV$F9-QhWh4GT,)JE[;:8K4-J?r[6="7Gl]VWW']pU:++mqKCW-Js#XG+.:7U,<]ZCmZ#J96')%s#pbssp<iQ.^6%KMO>/2])ED^lcAP8K_iu&X+rP!ROsJcNT&(\H!Z0L)F%Osb>oJ^qO>CZUFJ:A*!je&UK0l899?r()kTsC`mkDXfbNOcU`lX_$IN;<>ktC*_+"kSRHYonfHKQtI9krCr!tjKBJib<gOcghK(NLY>D=/$@9E?;O?QJ at m\"f424PKn.36KAY_`OQXm/tqqSXQibXmhe]R26+8L)b=loghKZbHsDkZMGAM&B1?G&W30,citXj"M at G^E)1(uSUFj5nQ:U0`_IaMIP7/pMorY,iZ;XJ8dFN2#nmg-GcTW7@/+XFLkCD@=V%A+ki^l%AinADKY<TB&59Z^U>Y0+<s.Ua`QTd<Y*c<B.t]MsbH@"4%RS6rS!s#=/i^clT$[J%<B"qoU'2W:f:;&D$JsN%h]H7n6KEgdqV^*_ntZjV42c_`&<GA. at 6Ds'UA8UVA:+@)\lJ<Cb_b0MgN=]t9/)nT/m1i7CeH>c%XA$73=`R[2A_2kR,<J`?TROn["X)H!RB0 at TJ,Ai'hRcfPLSnfEe<*p3A:@iE+N:De>9me<A at 4g1>k>)UG!?c:g_]q\H';^V#OE2&H`sN0:4=4^UU=LU=046,?M\C:s+lHH6$@.9)b-/;1V.<dBh5;8nU0$g_IohQB][Z*5ua2L=6(A+B^bV05F)>&s-/>KNPTtX#nU!>Y1+%In(uoQEQ==>\u4J(G0Ro0#"I2gPm+ki$FRHf1.JXI-UK;IdtItp"ajhOA<J;9An(ZG_>VGr!7RLb:75m77S0A[q:g1Ud4gHIn;]HoKV2S8gs~>
+endstream
+endobj
+231 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 230 0 R
+/Annots 232 0 R
+>>
+endobj
+232 0 obj
+[
+233 0 R
+234 0 R
+235 0 R
+236 0 R
+237 0 R
+238 0 R
+239 0 R
+240 0 R
+241 0 R
+242 0 R
+243 0 R
+244 0 R
+245 0 R
+]
+endobj
+233 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 525.864 719.1 571.35 710.1 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://wiki.jboss.org/wiki/Wiki.jsp?page=TransactionTimeout)
+/S /URI >>
+/H /I
+>>
+endobj
+234 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 84.0 709.2 118.497 700.2 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://wiki.jboss.org/wiki/Wiki.jsp?page=TransactionTimeout)
+/S /URI >>
+/H /I
+>>
+endobj
+235 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 544.449 450.45 554.448 441.45 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
 /A << /URI (http://lists.mysql.com/java)
 /S /URI >>
 /H /I
 >>
 endobj
-167 0 obj
+236 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 481.95 526.25 493.06 516.25 ]
+/Rect [ 84.0 440.55 172.515 431.55 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
-/A << /URI (http://lists.mysql.com/)
+/A << /URI (http://lists.mysql.com/java)
 /S /URI >>
 /H /I
 >>
 endobj
-168 0 obj
+237 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 515.25 201.69 505.25 ]
+/Rect [ 409.755 421.65 490.278 412.65 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://lists.mysql.com/)
@@ -1217,10 +1780,10 @@
 /H /I
 >>
 endobj
-169 0 obj
+238 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 223.63 515.25 312.53 505.25 ]
+/Rect [ 510.024 421.65 570.279 412.65 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://dev.mysql.com/doc/refman/5.1/en/mailing-lists.html)
@@ -1228,21 +1791,32 @@
 /H /I
 >>
 endobj
-170 0 obj
+239 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 413.3 483.25 466.92 473.25 ]
+/Rect [ 84.0 411.75 101.505 402.75 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.1/en/mailing-lists.html)
+/S /URI >>
+/H /I
+>>
+endobj
+240 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 347.97 392.85 396.228 383.85 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
 /A << /URI (http://forums.mysql.com/list.php?39)
 /S /URI >>
 /H /I
 >>
 endobj
-171 0 obj
+241 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 196.66 461.25 295.56 451.25 ]
+/Rect [ 205.491 382.95 294.501 373.95 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://forums.mysql.com)
@@ -1250,10 +1824,10 @@
 /H /I
 >>
 endobj
-172 0 obj
+242 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 317.5 461.25 527.52 451.25 ]
+/Rect [ 314.247 382.95 503.265 373.95 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://dev.mysql.com/doc/refman/5.1/en/forums.html)
@@ -1261,10 +1835,10 @@
 /H /I
 >>
 endobj
-173 0 obj
+243 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 259.7 403.5 351.94 393.5 ]
+/Rect [ 209.73 341.55 292.746 332.55 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://bugs.mysql.com/)
@@ -1272,10 +1846,10 @@
 /H /I
 >>
 endobj
-174 0 obj
+244 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 427.74 360.5 520.79 350.5 ]
+/Rect [ 360.966 312.75 444.711 303.75 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (mailto:security_at_mysql.com)
@@ -1283,10 +1857,10 @@
 /H /I
 >>
 endobj
-175 0 obj
+245 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 419.92 253.5 512.16 243.5 ]
+/Rect [ 353.928 246.15 436.944 237.15 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://bugs.mysql.com/)
@@ -1294,31 +1868,33 @@
 /H /I
 >>
 endobj
-176 0 obj
-<< /Length 2696 /Filter [ /ASCII85Decode /FlateDecode ]
+246 0 obj
+<< /Length 2572 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gatm>>BAQ-&q9SYd$pRDS*$8 at qTp+hbrO$*]d1`\%s8sB&X(M&"9XgM4_N&imuiog>mRY\a72hS:rEG4mI#bcL$rUi\*p&Jk;r$A[_-Q,Ws`k*_N-YGr;L-aqMpn9B'&+"X4`q#D^IDle:2Il5Hjlt>s,-lA-L^u<K#_:Q9pm?b=-QUSn:u_?W!Gn;tYEu2%6T53[@`WeS!3NOm$u5e6q.8nE+I/>I,MZ"$:GJK.V#nA4>?9C at E;l60l94ZhGASn^KY9-C-5NWbp.t<X_S!g9INq#H_>%0+@)en,'qNf*LPEYbo!JZ.<$HXcQo>#Rm at 1-2SaZL$HKjD;4-7 at k4tQXL4_6A6IYYL63O!@_!AOe3.3Hc);\rX:&(7C7&dW&A!i3O.ZFH71D1L2Up-Yl?$cQnWKD"$d%S?oeWZqoquqe9TU!PAN;O!S7.qSr'=]+KN(&s+B>ZuPL!t"3+b0ZHB at _iPcWsmN\"X5-7&j*]2`-ID'."]3NH;JK9a/,K at G"$[2-g(-JT5d'mm_;-AY`ZVW%+J4!!0hf9doeba<Jn+\\o$)586N:8u;5QV)(LM+5EObp3i?rk<X\mk68?7jd[5LJ[3W3Ar^+3\kbp%?&?Apn5sZ8-B6MU-R^]#U;]"M at QTgO;(6D"o*;"[R1-!^<<IZVTl-B&/CY^!_;n. at 11I-QK-Lb'H9M88iTZ02g$HO$TG]F!Kom62;ja3m%Q=9B:lshJBh`BM%52`CFL$<N$,M_Tf-[mRgFn$0^R'Dk^1CXP=gg`&66[bXcU$\d4VH#[#f9l":&3cGAl9NS"e-BF[A$SqX?QR&jMgYjD':"GYUka/4A`eO3Q:nq)b3%S?Ct-EsFe at lW,*OKXk=*S:1Ut<0MQ`%UNt3-TpnNZNuEo,g7E8Lh"=42Rt^j(op>%h\&<g%cqP=[5.E&?EKXV/bT5pb4Em<_bloO*Iac/_UNW.,*?i6!'ZT/B4dq`PoLAcK+hrNo&^,DQ[hJb#]<cA\\>hiJ+JUC^)-!3Y?q:6Qg.IPQH:5be>MX6bS/IY".Elk@[]8R7-)FlRR%LTY_`?lBp1-U(oY+7B9JF_(K`;KL/jA[;_ at 0,i?=aQ3mb:*)\sX,*])igN at en#K<2<m0=@ajJkcP58fKpW""T-o&uRrF[un%qnj&`%jK+$)YE_LU]`9f,1Rt>GDG2VQFT:s+dWFa7D_`[A`KoPIK`?l&R;RA0l)FJLZ8D"$Cs4S5R3mCmjJg3DMOoa!"SdSm)A0Hg8fYU[X!!hn&>kp,^4Kr=q+<8.D$`2XS7\7Y.[h1<)?@H at 52#>&*#JHZ=5T[L`C+-F1_J-l>"N7GYp&'Og%"$KC0f3FXG`nOf(CtijB72CSYK94KtbS\8I7""f'`)mW4,M^Ak$<`:pY\_dX35VIK/?`&"'s%HS>gG$N%;E/%[8[^A%Ti"jn,SR[eFrnbn#"OG"R\pYVh"5k<MD@<bOeiXus3cQ?#bUT)pu0=e?9EfPp.M%caI(8#/X8p!fQI4hNs8 at _Ie^X::eQijdQ\i^lt7suZV^mhbU%8W/2(f85+N+?cQ.u"@/!:k0R)mf9>CjWbc*J^(T.S.3REF17%9_L!kT'*rg#t0Fh&,t?dCO;)c8f4d.grqBFg:ZTV8ubm[N]u.E^;&Y;4$nEb/QV+VDfBlVF=W'@q%VNjiGbOoN*VkLrf+nb_=Vm:NoXl\or43d&f81?+Tu6OI.>`C)&A,-i8Y at qV\@`^8`YbIE9e=]b^u%F^(*n/cLX#$WtE^&c",!&!oH/AiE>c"%65G<$8)1u=IL5`<U&g(`3s^RTVC*DIU[2aSf$sfKg_OmcEtS%Jgom+-,_7H9cL]0qpB!4_Ke4bF5Gd1hY'[I9?c^W?B!8`pY!dd/\_=b3'.)poFd6VVW'+i/Lq0d<C2t(Bajs'&g[%=,U#MOB!?)aZ=EW1F!1lK&E#/DfL%5nds(&KNs"lF?PEC$L/%_QoKDLUoe9]a]]^X<^m_#,A&L#sM+Wu6(lJ--dFM+khOI.3TojOkM@?b^F7%tN7RPidBJcX4CG+,DhT_`)CdQ*BH'`7afQhkif%Gdn-Sl*RI"VQY4fr-qpK*j(Gm2T)%Z[aodJ0#9c1-c4\I6FO`lCtO\9KJ\.L!7GIk?Y`qj4r<.ba;ia#(c$+8#NErHdpWPnh9u'<Rnn"Tb78:)oC=b)Drnm/U.#1\Ds]l!C)9lf1up+h"V>a(@!5ocP%NRGR3i],ZE=a:3s&ZE'RJ-\<R&$BF%!PE;-09M=ID$MUIO`RbLL8RqN#P4tUJFl at VpISu]Qruje1g["%@EB'(s1;EVEibg[<Q]BhKer%:tj)eP-mcF[[.#%i^><LaJSu^9&)'d&uq3QE!ajlIXmDnTtMChGbIX5,#HC)_>*rSmT+\\F at Nh=,We&/#rHMtbAi=`#39aC1VIt)ZqB%Tb-.],/e+Qi\.4)'4iYnO\<q"epImg(9@%FMojiQW8g46JUNPm6K/<jukTcSm\#m;t@/cUaBLYB1um9NN#SbKW,CIJbm9ZMk+L(*8g=(H:7[DF?FgDVH)X3KVq\DX&"3]KO>$aTf8*5GaR7K-rodL=?m"5)B%C_)*-=p70bAL*J/3UI`-8h*$F#E5g?%hn4M!*_H/#Ri;<A/G/f,d."SAocR5#qbTG!9g=C)AfAK:<B"T at f0ZoV=qO.*`WZl$\Xq:->rgk]KeQ>drW*6T/WT~>
+GauHN968iG&AJ$Ck[nHSLA?9D2n)B59[Yi(EiOJZ6qBX3&e%<'eo^cG]-J\BQNAHf:)hNt!X1.Kq<`gKT3g?eFXJ=g-I]r!0j.hN'*E^-8`7B?hlp8Ig6653q;^U'VS!Tl04%=i/_>NVDorg`(0i;f\tq[hng#\$;PgiWcZp;tXF-!cB&2XER]j<Q4=YUG'(%7Cj>6dg^*tA^Q0,A#`%43%/hiVlTg-$u:Yd5;[cL+lbO3JAPUfn19>>"X6Zs$kaIK<0Cqfpj14;ZXm"Os/?HL0p.!^]RNX'7#\LN8#]$%*q`fXqT0lV/O%Ar7,GF?#)jqgE06IJ=pK#m=9N"!+Z's]9%.R?tV'otu*:=O\ki#Y`5;.;s3APjX-FJRtd=ZZj(&G$]2?=_[?>I(Y1esr*A*u6??bp-5_L[uc at _#<mtE]n%%Pr+bn<tHuY"CZIB6:&^R0<eMr37aA9[@3KqBcGn54F41MoR6,/h<G65AUIfVFnZAcO[U^[[B_h7ZhLb3mrmL"%6MojQt+dB"i"?t\(o(n/LMPJQ['>m17'F_\&HsPr>!Z at X=?ZUmXaEqCAE_L;O*#1[)cd'2 at FgVY?<4Z[`8\]48'hl7\h>h(;1R*Q>MTOEqjMX$65V+Cs\d'R72;"[>XE,B.s:B.oBc::bj&..arE@(cmoP1Q>rb5g/E>,bQIbaHVKP^n:klS<]%kdDFOEa?n(J(Dja^S3Pjm^,XBEA,FL=T7&_g"imm8eL+;d8Grh6^OV4r@#j\k<&]sNf<dg`RhAV!2MDDrc\CagbjM+SUS@!/pQ.GASjCCX,</F8\s2E4p+m_]),BX:lUWD_F"ZLhK[T9?L4Zs-=ZH,3LZ^Fr&pbNSOq-G,8PLq86X8jER,`bu6!_]5R6gfq16GD%<D0IM>];OgHeXMk^"D"3f4TZ`48g_R+gh^nY+V*p&C:5c*j at E-'WHUdIaMKl9I2d.h/&"LW<nS at Yma02"dVosHI)(Jk'qU$^dKi26PIn^jn+QT\ONZ24qL>7li":>Z;!T&0;.Z<LgRS00\6_TU?^"a)(a.&M9%p6%n&4a-Lml+E<<k]]!$'A7>gfTqq$rC;238\UTJ$kmtT%A/?5'`06]i*ZqV at 4?c_)agS<.G6P$s[C<^rehZ&,bC<qUbR'>i%F(>%g[LXJ;^X=1.m&ktgZn-F._EpeXPfu[8R=PCA8kIf]]`5.q)JZoOWu\8PSRRG^:fVuDoH(PPcKAX[qGBB+GPm!e27E4cnQ at .S3/qLTY^[M_-D[>!Xaql$Iu7,$ng@;!3"C9]MEL/;>,q_e*ub!N>As\>GZh#)@lTaHHaOH>hQ8TJ3 at t>IO'74e!gjW^>sJ;T%SXroaaW?@;*GsdK'H3!55_r.D!D0Z.m_4e at +.1pYkMuq-!_uP6V/GrG2:i#rHKP(B355<pb`[.Q&8U>kY_KCKD<t8!A&Q\bBJ#oq!j'pm[i2+'[O!F-IW("LJkM`SS,>*$,ZIr+'>sM at i:ttr8:Ke-C7*QWJY^Fef8N137I38n[_3P)1'/1cKj_+[fZKQ\LL%#i4EV^4=e^C1:GlS'YaBme)aCjFq at -HC_O9Fj!2/S5g'h/X#KN6qM<+"!HFR%YM7aUIW\qWAR75-RTF]WGP+r$)?]a\e!9J%#Ga<!Z`'lVcB2GK6F1kq_W+7[,[/C at 1S$lrgk4X*kXQaM3h]9Ieh^Z5:0#7TDe;lo]KsB)a7H36nY2VdT7mj6B>2imr=-(P/$rCqKYpLWj)u#kUf.*mn/3f<$K2#"['N1_#eQQ"&p\7u!peq&@<-Q`YmeGiM`TDoa.R-HT[eu^Z+b/B%;Ch,^>3U5(fb0q's0nXkG%ak3>45Q!u at d,GdB$T=je2r5jNXX!etWX1[p&Tgkai8X9=!/L='P<g0Psn.^>uenV&@Sgd38S7/WA1\,[Dfr3g6]"nGl[77jdJ1#[u/8%2tqi4/ER1_Q/)J^=FBG5l6jo/f)W*3/3j<klqK&%'<%LE2g6c9Z1CRFYI_2ka?[Hn).rZ82c(>KlhiSV$S6l'pqff.1O)4no70pRcoG$LHLCY0#CthZ3?N3^RJRpgeR at n`kSWYjXBK^6SPR;qGS]HX0t&&i&k%%+JJ?oid6_>IB3(Z1<r?]B>;[2C6Rr?*9FITGm"<e"#U5&nLb#n")V17fB%^$6:4oRBph<2ViopgV^Xdg at lG_\,Q?;*u4lZ!m#n_>d#IFc`n at d64=)Ke?Id,)t>Bjg]0/*24)BPZ,5&734G[4TYBX_gq at 1Z)7.t(KPIcGia.lt15b``dg>o-Y,T]LN=B(!doW$H%D[,&?oo=/ate`%$n)n'QY(<,hAn$&@A_?Ze"`ho_;+b&[&CZTYJ/2h=KpOr16:*_Ac]7oNo!k**^UOLjPBZF;8A4Z)7P4[Q^N2tVV!r`Q_N:^mJ:cnQNPtoM-MsDVFaI<SC@!Y2&NGu,Fmd)Ulg;iCu%e0:^2C,I+Y/',Rkhf;keht_K[)7$S0E"l_JJagcLl)qaUP<0+@)G7UQrh[st"0R*F-Up(`+;)F>5lKQch,!EGeZopqa+-VauD\]'18EUjasW7c.;R*#"_9)et[YEEW~>
 endstream
 endobj
-177 0 obj
+247 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 176 0 R
-/Annots 178 0 R
+/Contents 246 0 R
+/Annots 248 0 R
 >>
 endobj
-178 0 obj
+248 0 obj
 [
-179 0 R
+249 0 R
+250 0 R
+251 0 R
 ]
 endobj
-179 0 obj
+249 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 253.05 112.336 345.29 102.336 ]
+/Rect [ 84.0 300.706 167.016 291.706 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://bugs.mysql.com/)
@@ -1326,31 +1902,21 @@
 /H /I
 >>
 endobj
-180 0 obj
-<< /Length 357 /Filter [ /ASCII85Decode /FlateDecode ]
- >>
-stream
-Gat=e4\rsL&;GE/MAr%Yr^cC?1)>F;"[c:.D20NZ5#>_;`1kDha%[`t.YJB(EqfG.5'Q1L7-$o>UR6YW&VU9 at T^FLAiDj.1&@3+m"qD#DWW8?Y\E\tWUU<^rButNFQAI5+7F+us.XE`fVioaJa2#kukMt9lM,fr[/sS#:B'2lF8Yp at icItq%BmoR+mrpG;kBl<l:*@#ZS/Br]DT7ahltcOXq0s27 at JkGh'uP%h%<*_tID7-T;]iO6]5rs1du_eLUfD](BHf,T8sAmaWR[i_V/>Kr!!<L&Le!<I,A6p<cEI:4`7.5XJfO1)7f#;XNnIUGb.Bl4q at ghN7))&4b1rPD0V8W at H`'Y'gmR4~>
-endstream
-endobj
-181 0 obj
-<< /Type /Page
-/Parent 1 0 R
-/MediaBox [ 0 0 612 792 ]
-/Resources 3 0 R
-/Contents 180 0 R
-/Annots 182 0 R
+250 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 443.712 259.306 566.454 250.306 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.1/en/cj-news.html)
+/S /URI >>
+/H /I
 >>
 endobj
-182 0 obj
-[
-183 0 R
-]
-endobj
-183 0 obj
+251 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 120.0 698.0 269.16 688.0 ]
+/Rect [ 84.0 249.406 98.499 240.406 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A << /URI (http://dev.mysql.com/doc/refman/5.1/en/cj-news.html)
@@ -1358,350 +1924,391 @@
 /H /I
 >>
 endobj
-186 0 obj
+254 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\240\0\115\0\171\0\123\0\121\0\114\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112)
- /Parent 184 0 R
- /Next 188 0 R
- /A 185 0 R
+ /Parent 252 0 R
+ /Next 256 0 R
+ /A 253 0 R
 >> endobj
-188 0 obj
+256 0 obj
 <<
  /Title (\376\377\0\124\0\141\0\142\0\154\0\145\0\40\0\157\0\146\0\40\0\103\0\157\0\156\0\164\0\145\0\156\0\164\0\163)
- /Parent 184 0 R
- /Prev 186 0 R
- /Next 190 0 R
- /A 187 0 R
+ /Parent 252 0 R
+ /Prev 254 0 R
+ /Next 258 0 R
+ /A 255 0 R
 >> endobj
-190 0 obj
+258 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\61\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\163)
- /Parent 184 0 R
- /First 192 0 R
- /Last 192 0 R
- /Prev 188 0 R
- /Next 193 0 R
+ /Parent 252 0 R
+ /First 260 0 R
+ /Last 260 0 R
+ /Prev 256 0 R
+ /Next 261 0 R
  /Count -1
- /A 189 0 R
+ /A 257 0 R
 >> endobj
-192 0 obj
+260 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\61\0\56\0\61\0\56\0\240\0\112\0\141\0\166\0\141\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156\0\163\0\40\0\123\0\165\0\160\0\160\0\157\0\162\0\164\0\145\0\144)
- /Parent 190 0 R
- /A 191 0 R
+ /Parent 258 0 R
+ /A 259 0 R
 >> endobj
-193 0 obj
+261 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\62\0\56\0\240\0\111\0\156\0\163\0\164\0\141\0\154\0\154\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112)
- /Parent 184 0 R
- /First 195 0 R
- /Last 203 0 R
- /Prev 190 0 R
- /Next 205 0 R
+ /Title (\376\377\0\61\0\56\0\62\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\111\0\156\0\163\0\164\0\141\0\154\0\154\0\141\0\164\0\151\0\157\0\156)
+ /Parent 252 0 R
+ /First 263 0 R
+ /Last 271 0 R
+ /Prev 258 0 R
+ /Next 273 0 R
  /Count -6
- /A 154 0 R
+ /A 221 0 R
 >> endobj
-195 0 obj
+263 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\56\0\61\0\56\0\240\0\111\0\156\0\163\0\164\0\141\0\154\0\154\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\146\0\162\0\157\0\155\0\40\0\141\0\40\0\102\0\151\0\156\0\141\0\162\0\171\0\40\0\104\0\151\0\163\0\164\0\162\0\151\0\142\0\165\0\164\0\151\0\157\0\156)
- /Parent 193 0 R
- /Next 196 0 R
- /A 194 0 R
+ /Parent 261 0 R
+ /Next 264 0 R
+ /A 262 0 R
 >> endobj
-196 0 obj
+264 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\56\0\62\0\56\0\240\0\111\0\156\0\163\0\164\0\141\0\154\0\154\0\151\0\156\0\147\0\40\0\164\0\150\0\145\0\40\0\104\0\162\0\151\0\166\0\145\0\162\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\151\0\156\0\147\0\40\0\164\0\150\0\145\0\40\0\103\0\114\0\101\0\123\0\123\0\120\0\101\0\124\0\110)
- /Parent 193 0 R
- /Prev 195 0 R
- /Next 198 0 R
- /A 21 0 R
+ /Parent 261 0 R
+ /Prev 263 0 R
+ /Next 266 0 R
+ /A 35 0 R
 >> endobj
-198 0 obj
+266 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\56\0\63\0\56\0\240\0\125\0\160\0\147\0\162\0\141\0\144\0\151\0\156\0\147\0\40\0\146\0\162\0\157\0\155\0\40\0\141\0\156\0\40\0\117\0\154\0\144\0\145\0\162\0\40\0\126\0\145\0\162\0\163\0\151\0\157\0\156)
- /Parent 193 0 R
- /First 200 0 R
- /Last 202 0 R
- /Prev 196 0 R
- /Next 203 0 R
+ /Parent 261 0 R
+ /First 268 0 R
+ /Last 270 0 R
+ /Prev 264 0 R
+ /Next 271 0 R
  /Count -2
- /A 197 0 R
+ /A 265 0 R
 >> endobj
-200 0 obj
+268 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\56\0\63\0\56\0\61\0\56\0\240\0\125\0\160\0\147\0\162\0\141\0\144\0\151\0\156\0\147\0\40\0\146\0\162\0\157\0\155\0\40\0\115\0\171\0\123\0\121\0\114\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\63\0\56\0\60\0\40\0\164\0\157\0\40\0\63\0\56\0\61)
- /Parent 198 0 R
- /Next 202 0 R
- /A 199 0 R
+ /Parent 266 0 R
+ /Next 270 0 R
+ /A 267 0 R
 >> endobj
-202 0 obj
+270 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\56\0\63\0\56\0\62\0\56\0\240\0\112\0\104\0\102\0\103\0\55\0\123\0\160\0\145\0\143\0\151\0\146\0\151\0\143\0\40\0\111\0\163\0\163\0\165\0\145\0\163\0\40\0\127\0\150\0\145\0\156\0\40\0\125\0\160\0\147\0\162\0\141\0\144\0\151\0\156\0\147\0\40\0\164\0\157\0\40\0\115\0\171\0\123\0\121\0\114\0\40\0\123\0\145\0\162\0\166\0\145\0\162\0\40\0\64\0\56\0\61\0\40\0\157\0\162\0\40\0\116\0\145\0\167\0\145\0\162)
- /Parent 198 0 R
- /Prev 200 0 R
- /A 201 0 R
+ /Parent 266 0 R
+ /Prev 268 0 R
+ /A 269 0 R
 >> endobj
-203 0 obj
+271 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\56\0\64\0\56\0\240\0\111\0\156\0\163\0\164\0\141\0\154\0\154\0\151\0\156\0\147\0\40\0\146\0\162\0\157\0\155\0\40\0\164\0\150\0\145\0\40\0\104\0\145\0\166\0\145\0\154\0\157\0\160\0\155\0\145\0\156\0\164\0\40\0\123\0\157\0\165\0\162\0\143\0\145\0\40\0\124\0\162\0\145\0\145)
- /Parent 193 0 R
- /Prev 198 0 R
- /A 18 0 R
+ /Parent 261 0 R
+ /Prev 266 0 R
+ /A 32 0 R
 >> endobj
-205 0 obj
+273 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\63\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\105\0\170\0\141\0\155\0\160\0\154\0\145\0\163)
- /Parent 184 0 R
- /Prev 193 0 R
- /Next 207 0 R
- /A 204 0 R
+ /Parent 252 0 R
+ /Prev 261 0 R
+ /Next 275 0 R
+ /A 272 0 R
 >> endobj
-207 0 obj
+275 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\50\0\112\0\104\0\102\0\103\0\51\0\40\0\122\0\145\0\146\0\145\0\162\0\145\0\156\0\143\0\145)
- /Parent 184 0 R
- /First 208 0 R
- /Last 218 0 R
- /Prev 205 0 R
- /Next 220 0 R
- /Count -6
- /A 206 0 R
+ /Parent 252 0 R
+ /First 276 0 R
+ /Last 288 0 R
+ /Prev 273 0 R
+ /Next 290 0 R
+ /Count -7
+ /A 274 0 R
 >> endobj
-208 0 obj
+276 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\61\0\56\0\240\0\104\0\162\0\151\0\166\0\145\0\162\0\57\0\104\0\141\0\164\0\141\0\163\0\157\0\165\0\162\0\143\0\145\0\40\0\103\0\154\0\141\0\163\0\163\0\40\0\116\0\141\0\155\0\145\0\163\0\54\0\40\0\125\0\122\0\114\0\40\0\123\0\171\0\156\0\164\0\141\0\170\0\40\0\141\0\156\0\144\0\40\0\103\0\157\0\156\0\146\0\151\0\147\0\165\0\162\0\141\0\164\0\151\0\157\0\156\0\40\0\120\0\162\0\157\0\160\0\145\0\162\0\164\0\151\0\145\0\163\0\40\0\146\0\157\0\162\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112)
- /Parent 207 0 R
- /Next 210 0 R
- /A 159 0 R
+ /Parent 275 0 R
+ /Next 278 0 R
+ /A 14 0 R
 >> endobj
-210 0 obj
+278 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\62\0\56\0\240\0\112\0\104\0\102\0\103\0\40\0\101\0\120\0\111\0\40\0\111\0\155\0\160\0\154\0\145\0\155\0\145\0\156\0\164\0\141\0\164\0\151\0\157\0\156\0\40\0\116\0\157\0\164\0\145\0\163)
- /Parent 207 0 R
- /Prev 208 0 R
- /Next 212 0 R
- /A 209 0 R
+ /Parent 275 0 R
+ /Prev 276 0 R
+ /Next 280 0 R
+ /A 277 0 R
 >> endobj
-212 0 obj
+280 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\63\0\56\0\240\0\112\0\141\0\166\0\141\0\54\0\40\0\112\0\104\0\102\0\103\0\40\0\141\0\156\0\144\0\40\0\115\0\171\0\123\0\121\0\114\0\40\0\124\0\171\0\160\0\145\0\163)
- /Parent 207 0 R
- /Prev 210 0 R
- /Next 214 0 R
- /A 211 0 R
+ /Parent 275 0 R
+ /Prev 278 0 R
+ /Next 282 0 R
+ /A 279 0 R
 >> endobj
-214 0 obj
+282 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\64\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\103\0\150\0\141\0\162\0\141\0\143\0\164\0\145\0\162\0\40\0\123\0\145\0\164\0\163\0\40\0\141\0\156\0\144\0\40\0\125\0\156\0\151\0\143\0\157\0\144\0\145)
- /Parent 207 0 R
- /Prev 212 0 R
- /Next 216 0 R
- /A 213 0 R
+ /Parent 275 0 R
+ /Prev 280 0 R
+ /Next 284 0 R
+ /A 281 0 R
 >> endobj
-216 0 obj
+284 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\65\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\151\0\156\0\147\0\40\0\123\0\145\0\143\0\165\0\162\0\145\0\154\0\171\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\123\0\123\0\114)
- /Parent 207 0 R
- /Prev 214 0 R
- /Next 218 0 R
- /A 215 0 R
+ /Parent 275 0 R
+ /Prev 282 0 R
+ /Next 286 0 R
+ /A 283 0 R
 >> endobj
-218 0 obj
+286 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\64\0\56\0\66\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\115\0\141\0\163\0\164\0\145\0\162\0\57\0\123\0\154\0\141\0\166\0\145\0\40\0\122\0\145\0\160\0\154\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\40\0\167\0\151\0\164\0\150\0\40\0\122\0\145\0\160\0\154\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\151\0\157\0\156)
- /Parent 207 0 R
- /Prev 216 0 R
- /A 217 0 R
+ /Parent 275 0 R
+ /Prev 284 0 R
+ /Next 288 0 R
+ /A 285 0 R
 >> endobj
-220 0 obj
+288 0 obj
 <<
+ /Title (\376\377\0\61\0\56\0\64\0\56\0\67\0\56\0\240\0\115\0\141\0\160\0\160\0\151\0\156\0\147\0\40\0\115\0\171\0\123\0\121\0\114\0\40\0\105\0\162\0\162\0\157\0\162\0\40\0\116\0\165\0\155\0\142\0\145\0\162\0\163\0\40\0\164\0\157\0\40\0\123\0\121\0\114\0\123\0\164\0\141\0\164\0\145\0\163)
+ /Parent 275 0 R
+ /Prev 286 0 R
+ /A 287 0 R
+>> endobj
+290 0 obj
+<<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\116\0\157\0\164\0\145\0\163\0\40\0\141\0\156\0\144\0\40\0\124\0\151\0\160\0\163)
- /Parent 184 0 R
- /First 222 0 R
- /Last 241 0 R
- /Prev 207 0 R
- /Next 243 0 R
- /Count -11
- /A 219 0 R
+ /Parent 252 0 R
+ /First 292 0 R
+ /Last 316 0 R
+ /Prev 275 0 R
+ /Next 318 0 R
+ /Count -15
+ /A 289 0 R
 >> endobj
-222 0 obj
+292 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\61\0\56\0\240\0\102\0\141\0\163\0\151\0\143\0\40\0\112\0\104\0\102\0\103\0\40\0\103\0\157\0\156\0\143\0\145\0\160\0\164\0\163)
- /Parent 220 0 R
- /First 224 0 R
- /Last 230 0 R
- /Next 231 0 R
+ /Parent 290 0 R
+ /First 294 0 R
+ /Last 300 0 R
+ /Next 301 0 R
  /Count -4
- /A 221 0 R
+ /A 291 0 R
 >> endobj
-224 0 obj
+294 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\61\0\56\0\61\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\151\0\156\0\147\0\40\0\164\0\157\0\40\0\115\0\171\0\123\0\121\0\114\0\40\0\125\0\163\0\151\0\156\0\147\0\40\0\164\0\150\0\145\0\40\0\104\0\162\0\151\0\166\0\145\0\162\0\115\0\141\0\156\0\141\0\147\0\145\0\162\0\40\0\111\0\156\0\164\0\145\0\162\0\146\0\141\0\143\0\145)
- /Parent 222 0 R
- /Next 226 0 R
- /A 223 0 R
+ /Parent 292 0 R
+ /Next 296 0 R
+ /A 293 0 R
 >> endobj
-226 0 obj
+296 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\61\0\56\0\62\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\123\0\164\0\141\0\164\0\145\0\155\0\145\0\156\0\164\0\163\0\40\0\164\0\157\0\40\0\105\0\170\0\145\0\143\0\165\0\164\0\145\0\40\0\123\0\121\0\114)
- /Parent 222 0 R
- /Prev 224 0 R
- /Next 228 0 R
- /A 225 0 R
+ /Parent 292 0 R
+ /Prev 294 0 R
+ /Next 298 0 R
+ /A 295 0 R
 >> endobj
-228 0 obj
+298 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\61\0\56\0\63\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\103\0\141\0\154\0\154\0\141\0\142\0\154\0\145\0\123\0\164\0\141\0\164\0\145\0\155\0\145\0\156\0\164\0\163\0\40\0\164\0\157\0\40\0\105\0\170\0\145\0\143\0\165\0\164\0\145\0\40\0\123\0\164\0\157\0\162\0\145\0\144\0\40\0\120\0\162\0\157\0\143\0\145\0\144\0\165\0\162\0\145\0\163)
- /Parent 222 0 R
- /Prev 226 0 R
- /Next 230 0 R
- /A 227 0 R
+ /Parent 292 0 R
+ /Prev 296 0 R
+ /Next 300 0 R
+ /A 297 0 R
 >> endobj
-230 0 obj
+300 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\61\0\56\0\64\0\56\0\240\0\122\0\145\0\164\0\162\0\151\0\145\0\166\0\151\0\156\0\147\0\40\0\101\0\125\0\124\0\117\0\137\0\111\0\116\0\103\0\122\0\105\0\115\0\105\0\116\0\124\0\40\0\103\0\157\0\154\0\165\0\155\0\156\0\40\0\126\0\141\0\154\0\165\0\145\0\163)
- /Parent 222 0 R
- /Prev 228 0 R
- /A 229 0 R
+ /Parent 292 0 R
+ /Prev 298 0 R
+ /A 299 0 R
 >> endobj
-231 0 obj
+301 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\167\0\151\0\164\0\150\0\40\0\112\0\62\0\105\0\105\0\40\0\141\0\156\0\144\0\40\0\117\0\164\0\150\0\145\0\162\0\40\0\112\0\141\0\166\0\141\0\40\0\106\0\162\0\141\0\155\0\145\0\167\0\157\0\162\0\153\0\163)
- /Parent 220 0 R
- /First 233 0 R
- /Last 239 0 R
- /Prev 222 0 R
- /Next 241 0 R
- /Count -4
- /A 26 0 R
+ /Parent 290 0 R
+ /First 303 0 R
+ /Last 308 0 R
+ /Prev 292 0 R
+ /Next 316 0 R
+ /Count -8
+ /A 17 0 R
 >> endobj
-233 0 obj
+303 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\61\0\56\0\240\0\107\0\145\0\156\0\145\0\162\0\141\0\154\0\40\0\112\0\62\0\105\0\105\0\40\0\103\0\157\0\156\0\143\0\145\0\160\0\164\0\163)
- /Parent 231 0 R
- /First 235 0 R
- /Last 235 0 R
- /Next 237 0 R
+ /Parent 301 0 R
+ /First 305 0 R
+ /Last 305 0 R
+ /Next 306 0 R
  /Count -1
- /A 232 0 R
+ /A 302 0 R
 >> endobj
-235 0 obj
+305 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\61\0\56\0\61\0\56\0\240\0\125\0\156\0\144\0\145\0\162\0\163\0\164\0\141\0\156\0\144\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\120\0\157\0\157\0\154\0\151\0\156\0\147)
- /Parent 233 0 R
- /A 234 0 R
+ /Parent 303 0 R
+ /A 304 0 R
 >> endobj
-237 0 obj
+306 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\62\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\167\0\151\0\164\0\150\0\40\0\124\0\157\0\155\0\143\0\141\0\164)
- /Parent 231 0 R
- /Prev 233 0 R
- /Next 239 0 R
- /A 236 0 R
+ /Parent 301 0 R
+ /Prev 303 0 R
+ /Next 307 0 R
+ /A 20 0 R
 >> endobj
-239 0 obj
+307 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\63\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\167\0\151\0\164\0\150\0\40\0\112\0\102\0\157\0\163\0\163)
- /Parent 231 0 R
- /Prev 237 0 R
- /A 238 0 R
+ /Parent 301 0 R
+ /Prev 306 0 R
+ /Next 308 0 R
+ /A 22 0 R
 >> endobj
-241 0 obj
+308 0 obj
 <<
+ /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\64\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\167\0\151\0\164\0\150\0\40\0\123\0\160\0\162\0\151\0\156\0\147)
+ /Parent 301 0 R
+ /First 310 0 R
+ /Last 314 0 R
+ /Prev 307 0 R
+ /Count -3
+ /A 24 0 R
+>> endobj
+310 0 obj
+<<
+ /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\64\0\56\0\61\0\56\0\240\0\125\0\163\0\151\0\156\0\147\0\40\0\112\0\144\0\142\0\143\0\124\0\145\0\155\0\160\0\154\0\141\0\164\0\145)
+ /Parent 308 0 R
+ /Next 312 0 R
+ /A 309 0 R
+>> endobj
+312 0 obj
+<<
+ /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\64\0\56\0\62\0\56\0\240\0\124\0\162\0\141\0\156\0\163\0\141\0\143\0\164\0\151\0\157\0\156\0\141\0\154\0\40\0\112\0\104\0\102\0\103\0\40\0\101\0\143\0\143\0\145\0\163\0\163)
+ /Parent 308 0 R
+ /Prev 310 0 R
+ /Next 314 0 R
+ /A 311 0 R
+>> endobj
+314 0 obj
+<<
+ /Title (\376\377\0\61\0\56\0\65\0\56\0\62\0\56\0\64\0\56\0\63\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\151\0\157\0\156\0\40\0\120\0\157\0\157\0\154\0\151\0\156\0\147)
+ /Parent 308 0 R
+ /Prev 312 0 R
+ /A 313 0 R
+>> endobj
+316 0 obj
+<<
  /Title (\376\377\0\61\0\56\0\65\0\56\0\63\0\56\0\240\0\103\0\157\0\155\0\155\0\157\0\156\0\40\0\120\0\162\0\157\0\142\0\154\0\145\0\155\0\163\0\40\0\141\0\156\0\144\0\40\0\123\0\157\0\154\0\165\0\164\0\151\0\157\0\156\0\163)
- /Parent 220 0 R
- /Prev 231 0 R
- /A 240 0 R
+ /Parent 290 0 R
+ /Prev 301 0 R
+ /A 315 0 R
 >> endobj
-243 0 obj
+318 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\66\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\123\0\165\0\160\0\160\0\157\0\162\0\164)
- /Parent 184 0 R
- /First 245 0 R
- /Last 249 0 R
- /Prev 220 0 R
+ /Parent 252 0 R
+ /First 320 0 R
+ /Last 324 0 R
+ /Prev 290 0 R
  /Count -3
- /A 242 0 R
+ /A 317 0 R
 >> endobj
-245 0 obj
+320 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\66\0\56\0\61\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\103\0\157\0\155\0\155\0\165\0\156\0\151\0\164\0\171\0\40\0\123\0\165\0\160\0\160\0\157\0\162\0\164)
- /Parent 243 0 R
- /Next 247 0 R
- /A 244 0 R
+ /Parent 318 0 R
+ /Next 322 0 R
+ /A 319 0 R
 >> endobj
-247 0 obj
+322 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\66\0\56\0\62\0\56\0\240\0\110\0\157\0\167\0\40\0\164\0\157\0\40\0\122\0\145\0\160\0\157\0\162\0\164\0\40\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\102\0\165\0\147\0\163\0\40\0\157\0\162\0\40\0\120\0\162\0\157\0\142\0\154\0\145\0\155\0\163)
- /Parent 243 0 R
- /Prev 245 0 R
- /Next 249 0 R
- /A 246 0 R
+ /Parent 318 0 R
+ /Prev 320 0 R
+ /Next 324 0 R
+ /A 321 0 R
 >> endobj
-249 0 obj
+324 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\66\0\56\0\63\0\56\0\240\0\103\0\157\0\156\0\156\0\145\0\143\0\164\0\157\0\162\0\57\0\112\0\40\0\103\0\150\0\141\0\156\0\147\0\145\0\40\0\110\0\151\0\163\0\164\0\157\0\162\0\171)
- /Parent 243 0 R
- /Prev 247 0 R
- /A 248 0 R
+ /Parent 318 0 R
+ /Prev 322 0 R
+ /A 323 0 R
 >> endobj
-250 0 obj
+325 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F3
-/BaseFont /Helvetica-Bold
+/Name /F11
+/BaseFont /Courier-Bold
 /Encoding /WinAnsiEncoding >>
 endobj
-251 0 obj
+326 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F5
-/BaseFont /Times-Roman
+/Name /F1
+/BaseFont /Helvetica
 /Encoding /WinAnsiEncoding >>
 endobj
-252 0 obj
+327 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F10
-/BaseFont /Courier-Oblique
+/Name /F12
+/BaseFont /Courier-BoldOblique
 /Encoding /WinAnsiEncoding >>
 endobj
-253 0 obj
+328 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F6
-/BaseFont /Times-Italic
+/Name /F10
+/BaseFont /Courier-Oblique
 /Encoding /WinAnsiEncoding >>
 endobj
-254 0 obj
+329 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F1
-/BaseFont /Helvetica
+/Name /F5
+/BaseFont /Times-Roman
 /Encoding /WinAnsiEncoding >>
 endobj
-255 0 obj
+330 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F11
-/BaseFont /Courier-Bold
+/Name /F3
+/BaseFont /Helvetica-Bold
 /Encoding /WinAnsiEncoding >>
 endobj
-256 0 obj
+331 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F9
 /BaseFont /Courier
 /Encoding /WinAnsiEncoding >>
 endobj
-257 0 obj
+332 0 obj
 << /Type /Font
 /Subtype /Type1
-/Name /F12
-/BaseFont /Courier-BoldOblique
+/Name /F6
+/BaseFont /Times-Italic
 /Encoding /WinAnsiEncoding >>
 endobj
-258 0 obj
+333 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F7
@@ -1710,606 +2317,741 @@
 endobj
 1 0 obj
 << /Type /Pages
-/Count 44
-/Kids [6 0 R 15 0 R 23 0 R 29 0 R 31 0 R 36 0 R 54 0 R 71 0 R 73 0 R 75 0 R 77 0 R 79 0 R 81 0 R 83 0 R 85 0 R 87 0 R 89 0 R 91 0 R 93 0 R 97 0 R 99 0 R 103 0 R 105 0 R 107 0 R 109 0 R 111 0 R 113 0 R 115 0 R 117 0 R 119 0 R 121 0 R 123 0 R 125 0 R 127 0 R 129 0 R 134 0 R 136 0 R 138 0 R 151 0 R 156 0 R 162 0 R 164 0 R 177 0 R 181 0 R ] >>
+/Count 57
+/Kids [6 0 R 29 0 R 37 0 R 43 0 R 45 0 R 52 0 R 79 0 R 81 0 R 83 0 R 85 0 R 87 0 R 89 0 R 91 0 R 93 0 R 95 0 R 97 0 R 99 0 R 105 0 R 107 0 R 111 0 R 113 0 R 117 0 R 124 0 R 126 0 R 128 0 R 130 0 R 132 0 R 134 0 R 136 0 R 138 0 R 140 0 R 142 0 R 144 0 R 146 0 R 148 0 R 150 0 R 152 0 R 156 0 R 158 0 R 160 0 R 162 0 R 164 0 R 166 0 R 168 0 R 170 0 R 175 0 R 177 0 R 179 0 R 184 0 R 188 0 R 190 0 R 198 0 R 218 0 R 225 0 R 227 0 R 231 0 R 247 0 R ] >>
 endobj
 2 0 obj
 << /Type /Catalog
 /Pages 1 0 R
- /Outlines 184 0 R
+ /Outlines 252 0 R
  /PageMode /UseOutlines
- /Names << /Dests << /Names [  (connector-j-versions) [ 6 0 R /XYZ 115.0 405.75 null ] (connector-j-installing) [ 15 0 R /XYZ 115.0 512.0 null ] (connector-j-examples) [ 36 0 R /XYZ 115.0 293.56 null ] (connector-j-reference) [ 54 0 R /XYZ 115.0 567.0 null ] (connector-j-usagenotes) [ 107 0 R /XYZ 115.0 260.048 null ] (connector-j-support) [ 164 0 R /XYZ 115.0 639.0 null ] (connector-j) [ null /XYZ 0.0 0.0 null ] ] >> >>
+ /Names << /Dests << /Names [  (connector-j-versions) [ 6 0 R /XYZ 79.0 296.075 null ] (connector-j-installing) [ 29 0 R /XYZ 79.0 436.1 null ] (connector-j-examples) [ 45 0 R /XYZ 79.0 105.06 null ] (connector-j-reference) [ 52 0 R /XYZ 79.0 461.3 null ] (connector-j-usagenotes) [ 150 0 R /XYZ 79.0 725.0 null ] (connector-j-support) [ 231 0 R /XYZ 79.0 522.5 null ] (connector-j) [ null /XYZ 0.0 0.0 null ] ] >> >>
  >>
 endobj
 3 0 obj
 << 
-/Font << /F3 250 0 R /F5 251 0 R /F10 252 0 R /F1 254 0 R /F6 253 0 R /F9 256 0 R /F11 255 0 R /F12 257 0 R /F7 258 0 R >> 
+/Font << /F1 326 0 R /F11 325 0 R /F12 327 0 R /F10 328 0 R /F5 329 0 R /F3 330 0 R /F9 331 0 R /F6 332 0 R /F7 333 0 R >> 
 /ProcSet [ /PDF /ImageC /Text ] >> 
 endobj
-18 0 obj
+14 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 115.0 307.5 null]
+/D [52 0 R /XYZ 79.0 407.75 null]
 >>
 endobj
-21 0 obj
+17 0 obj
 <<
 /S /GoTo
-/D [15 0 R /XYZ 115.0 156.25 null]
+/D [166 0 R /XYZ 79.0 526.632 null]
 >>
 endobj
-26 0 obj
+20 0 obj
 <<
 /S /GoTo
-/D [123 0 R /XYZ 115.0 107.28 null]
+/D [170 0 R /XYZ 79.0 297.456 null]
 >>
 endobj
-42 0 obj
+22 0 obj
 <<
 /S /GoTo
-/D [109 0 R /XYZ 115.0 469.016 null]
+/D [177 0 R /XYZ 79.0 483.148 null]
 >>
 endobj
-44 0 obj
+24 0 obj
 <<
 /S /GoTo
-/D [111 0 R /XYZ 115.0 596.0 null]
+/D [177 0 R /XYZ 79.0 112.853 null]
 >>
 endobj
-46 0 obj
+32 0 obj
 <<
 /S /GoTo
-/D [113 0 R /XYZ 115.0 683.0 null]
+/D [45 0 R /XYZ 79.0 648.5 null]
 >>
 endobj
-48 0 obj
+35 0 obj
 <<
 /S /GoTo
-/D [113 0 R /XYZ 115.0 458.87 null]
+/D [29 0 R /XYZ 79.0 156.65 null]
 >>
 endobj
-50 0 obj
+55 0 obj
 <<
 /S /GoTo
-/D [113 0 R /XYZ 115.0 142.412 null]
+/D [150 0 R /XYZ 79.0 378.111 null]
 >>
 endobj
-52 0 obj
+57 0 obj
 <<
 /S /GoTo
-/D [115 0 R /XYZ 115.0 403.136 null]
+/D [152 0 R /XYZ 79.0 561.2 null]
 >>
 endobj
-57 0 obj
+59 0 obj
 <<
 /S /GoTo
-/D [117 0 R /XYZ 115.0 683.0 null]
+/D [156 0 R /XYZ 79.0 706.1 null]
 >>
 endobj
-59 0 obj
+61 0 obj
 <<
 /S /GoTo
-/D [117 0 R /XYZ 115.0 217.498 null]
+/D [156 0 R /XYZ 79.0 504.672 null]
 >>
 endobj
-62 0 obj
+63 0 obj
 <<
 /S /GoTo
-/D [119 0 R /XYZ 115.0 190.056 null]
+/D [156 0 R /XYZ 79.0 216.904 null]
 >>
 endobj
 65 0 obj
 <<
 /S /GoTo
-/D [121 0 R /XYZ 115.0 213.72 null]
+/D [158 0 R /XYZ 79.0 513.234 null]
 >>
 endobj
 67 0 obj
 <<
 /S /GoTo
-/D [127 0 R /XYZ 115.0 672.0 null]
+/D [158 0 R /XYZ 79.0 192.142 null]
 >>
 endobj
 69 0 obj
 <<
 /S /GoTo
-/D [156 0 R /XYZ 115.0 576.0 null]
+/D [160 0 R /XYZ 79.0 425.361 null]
 >>
 endobj
-141 0 obj
+71 0 obj
 <<
 /S /GoTo
-/D [138 0 R /XYZ 115.0 155.61 null]
+/D [162 0 R /XYZ 79.0 470.562 null]
 >>
 endobj
-143 0 obj
+73 0 obj
 <<
 /S /GoTo
-/D [151 0 R /XYZ 115.0 554.0 null]
+/D [164 0 R /XYZ 79.0 555.738 null]
 >>
 endobj
-145 0 obj
+75 0 obj
 <<
 /S /GoTo
-/D [151 0 R /XYZ 115.0 418.0 null]
+/D [168 0 R /XYZ 79.0 590.9 null]
 >>
 endobj
-147 0 obj
+77 0 obj
 <<
 /S /GoTo
-/D [151 0 R /XYZ 115.0 96.784 null]
+/D [218 0 R /XYZ 79.0 209.814 null]
 >>
 endobj
-149 0 obj
+194 0 obj
 <<
 /S /GoTo
-/D [162 0 R /XYZ 115.0 148.72 null]
+/D [198 0 R /XYZ 79.0 325.342 null]
 >>
 endobj
-154 0 obj
+196 0 obj
 <<
 /S /GoTo
-/D [15 0 R /XYZ 115.0 512.0 null]
+/D [218 0 R /XYZ 79.0 716.0 null]
 >>
 endobj
-159 0 obj
+201 0 obj
 <<
 /S /GoTo
-/D [54 0 R /XYZ 115.0 507.0 null]
+/D [218 0 R /XYZ 79.0 593.6 null]
 >>
 endobj
-184 0 obj
+203 0 obj
 <<
- /First 186 0 R
- /Last 243 0 R
+/S /GoTo
+/D [218 0 R /XYZ 79.0 343.914 null]
+>>
+endobj
+205 0 obj
+<<
+/S /GoTo
+/D [227 0 R /XYZ 79.0 515.28 null]
+>>
+endobj
+207 0 obj
+<<
+/S /GoTo
+/D [227 0 R /XYZ 79.0 409.08 null]
+>>
+endobj
+209 0 obj
+<<
+/S /GoTo
+/D [227 0 R /XYZ 79.0 351.48 null]
+>>
+endobj
+211 0 obj
+<<
+/S /GoTo
+/D [227 0 R /XYZ 79.0 149.308 null]
+>>
+endobj
+213 0 obj
+<<
+/S /GoTo
+/D [231 0 R /XYZ 79.0 696.2 null]
+>>
+endobj
+215 0 obj
+<<
+/S /GoTo
+/D [231 0 R /XYZ 79.0 628.7 null]
+>>
+endobj
+221 0 obj
+<<
+/S /GoTo
+/D [29 0 R /XYZ 79.0 436.1 null]
+>>
+endobj
+252 0 obj
+<<
+ /First 254 0 R
+ /Last 318 0 R
 >> endobj
-185 0 obj
+253 0 obj
 <<
 /S /GoTo
 /D [null /XYZ 0.0 0.0 null]
 >>
 endobj
-187 0 obj
+255 0 obj
 <<
 /S /GoTo
 /D [null /XYZ 0.0 0.0 null]
 >>
 endobj
-189 0 obj
+257 0 obj
 <<
 /S /GoTo
-/D [6 0 R /XYZ 115.0 405.75 null]
+/D [6 0 R /XYZ 79.0 296.075 null]
 >>
 endobj
-191 0 obj
+259 0 obj
 <<
 /S /GoTo
-/D [6 0 R /XYZ 115.0 186.75 null]
+/D [29 0 R /XYZ 79.0 696.2 null]
 >>
 endobj
-194 0 obj
+262 0 obj
 <<
 /S /GoTo
-/D [15 0 R /XYZ 115.0 441.0 null]
+/D [29 0 R /XYZ 79.0 372.65 null]
 >>
 endobj
-197 0 obj
+265 0 obj
 <<
 /S /GoTo
-/D [23 0 R /XYZ 115.0 172.224 null]
+/D [37 0 R /XYZ 79.0 276.604 null]
 >>
 endobj
-199 0 obj
+267 0 obj
 <<
 /S /GoTo
-/D [29 0 R /XYZ 115.0 704.0 null]
+/D [37 0 R /XYZ 79.0 196.504 null]
 >>
 endobj
-201 0 obj
+269 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 115.0 544.0 null]
+/D [43 0 R /XYZ 79.0 204.8 null]
 >>
 endobj
-204 0 obj
+272 0 obj
 <<
 /S /GoTo
-/D [36 0 R /XYZ 115.0 293.56 null]
+/D [45 0 R /XYZ 79.0 105.06 null]
 >>
 endobj
-206 0 obj
+274 0 obj
 <<
 /S /GoTo
-/D [54 0 R /XYZ 115.0 567.0 null]
+/D [52 0 R /XYZ 79.0 461.3 null]
 >>
 endobj
-209 0 obj
+277 0 obj
 <<
 /S /GoTo
-/D [87 0 R /XYZ 115.0 254.474 null]
+/D [97 0 R /XYZ 79.0 123.854 null]
 >>
 endobj
-211 0 obj
+279 0 obj
 <<
 /S /GoTo
-/D [91 0 R /XYZ 115.0 106.336 null]
+/D [107 0 R /XYZ 79.0 466.25 null]
 >>
 endobj
-213 0 obj
+281 0 obj
 <<
 /S /GoTo
-/D [97 0 R /XYZ 115.0 429.75 null]
+/D [111 0 R /XYZ 79.0 162.95 null]
 >>
 endobj
-215 0 obj
+283 0 obj
 <<
 /S /GoTo
-/D [99 0 R /XYZ 115.0 287.0 null]
+/D [113 0 R /XYZ 79.0 134.4 null]
 >>
 endobj
-217 0 obj
+285 0 obj
 <<
 /S /GoTo
-/D [105 0 R /XYZ 115.0 341.36 null]
+/D [124 0 R /XYZ 79.0 219.488 null]
 >>
 endobj
-219 0 obj
+287 0 obj
 <<
 /S /GoTo
-/D [107 0 R /XYZ 115.0 260.048 null]
+/D [126 0 R /XYZ 79.0 243.612 null]
 >>
 endobj
-221 0 obj
+289 0 obj
 <<
 /S /GoTo
-/D [107 0 R /XYZ 115.0 232.048 null]
+/D [150 0 R /XYZ 79.0 725.0 null]
 >>
 endobj
-223 0 obj
+291 0 obj
 <<
 /S /GoTo
-/D [107 0 R /XYZ 115.0 185.298 null]
+/D [150 0 R /XYZ 79.0 709.25 null]
 >>
 endobj
-225 0 obj
+293 0 obj
 <<
 /S /GoTo
-/D [109 0 R /XYZ 115.0 205.558 null]
+/D [150 0 R /XYZ 79.0 667.85 null]
 >>
 endobj
-227 0 obj
+295 0 obj
 <<
 /S /GoTo
-/D [111 0 R /XYZ 115.0 245.006 null]
+/D [150 0 R /XYZ 79.0 127.099 null]
 >>
 endobj
-229 0 obj
+297 0 obj
 <<
 /S /GoTo
-/D [117 0 R /XYZ 115.0 392.998 null]
+/D [152 0 R /XYZ 79.0 242.826 null]
 >>
 endobj
-232 0 obj
+299 0 obj
 <<
 /S /GoTo
-/D [125 0 R /XYZ 115.0 704.0 null]
+/D [160 0 R /XYZ 79.0 553.836 null]
 >>
 endobj
-234 0 obj
+302 0 obj
 <<
 /S /GoTo
-/D [125 0 R /XYZ 115.0 659.5 null]
+/D [166 0 R /XYZ 79.0 485.232 null]
 >>
 endobj
-236 0 obj
+304 0 obj
 <<
 /S /GoTo
-/D [129 0 R /XYZ 115.0 256.576 null]
+/D [166 0 R /XYZ 79.0 444.957 null]
 >>
 endobj
-238 0 obj
+309 0 obj
 <<
 /S /GoTo
-/D [136 0 R /XYZ 115.0 306.816 null]
+/D [184 0 R /XYZ 79.0 657.5 null]
 >>
 endobj
-240 0 obj
+311 0 obj
 <<
 /S /GoTo
-/D [138 0 R /XYZ 115.0 549.24 null]
+/D [188 0 R /XYZ 79.0 679.8 null]
 >>
 endobj
-242 0 obj
+313 0 obj
 <<
 /S /GoTo
-/D [164 0 R /XYZ 115.0 639.0 null]
+/D [190 0 R /XYZ 79.0 546.214 null]
 >>
 endobj
-244 0 obj
+315 0 obj
 <<
 /S /GoTo
-/D [164 0 R /XYZ 115.0 611.0 null]
+/D [190 0 R /XYZ 79.0 254.682 null]
 >>
 endobj
-246 0 obj
+317 0 obj
 <<
 /S /GoTo
-/D [164 0 R /XYZ 115.0 445.25 null]
+/D [231 0 R /XYZ 79.0 522.5 null]
 >>
 endobj
-248 0 obj
+319 0 obj
 <<
 /S /GoTo
-/D [177 0 R /XYZ 115.0 107.336 null]
+/D [231 0 R /XYZ 79.0 497.75 null]
 >>
 endobj
+321 0 obj
+<<
+/S /GoTo
+/D [231 0 R /XYZ 79.0 378.95 null]
+>>
+endobj
+323 0 obj
+<<
+/S /GoTo
+/D [247 0 R /XYZ 79.0 296.706 null]
+>>
+endobj
 xref
-0 259
+0 334
 0000000000 65535 f 
-0000145968 00000 n 
-0000146351 00000 n 
-0000146869 00000 n 
+0000212183 00000 n 
+0000212674 00000 n 
+0000213185 00000 n 
 0000000015 00000 n 
 0000000071 00000 n 
-0000002349 00000 n 
-0000002469 00000 n 
-0000002528 00000 n 
-0000002702 00000 n 
-0000002882 00000 n 
-0000003057 00000 n 
-0000003231 00000 n 
-0000003442 00000 n 
-0000003671 00000 n 
-0000006637 00000 n 
-0000006760 00000 n 
-0000006801 00000 n 
-0000147048 00000 n 
-0000006934 00000 n 
-0000007066 00000 n 
-0000147113 00000 n 
-0000007200 00000 n 
-0000009924 00000 n 
-0000010047 00000 n 
-0000010081 00000 n 
-0000147179 00000 n 
-0000010218 00000 n 
-0000010353 00000 n 
-0000013592 00000 n 
-0000013700 00000 n 
-0000016323 00000 n 
-0000016446 00000 n 
-0000016480 00000 n 
-0000016659 00000 n 
-0000016830 00000 n 
-0000019483 00000 n 
-0000019606 00000 n 
-0000019689 00000 n 
-0000019878 00000 n 
-0000020013 00000 n 
-0000020147 00000 n 
-0000147246 00000 n 
-0000020281 00000 n 
-0000147314 00000 n 
-0000020415 00000 n 
-0000147380 00000 n 
-0000020549 00000 n 
-0000147446 00000 n 
-0000020683 00000 n 
-0000147513 00000 n 
-0000020817 00000 n 
-0000147581 00000 n 
-0000020949 00000 n 
-0000023690 00000 n 
-0000023813 00000 n 
-0000023889 00000 n 
-0000147649 00000 n 
-0000024021 00000 n 
+0000003371 00000 n 
+0000003491 00000 n 
+0000003613 00000 n 
+0000003787 00000 n 
+0000003967 00000 n 
+0000004142 00000 n 
+0000004351 00000 n 
+0000004578 00000 n 
+0000213364 00000 n 
+0000004713 00000 n 
+0000004845 00000 n 
+0000213429 00000 n 
+0000004980 00000 n 
+0000005111 00000 n 
+0000213496 00000 n 
+0000005246 00000 n 
+0000213563 00000 n 
+0000005381 00000 n 
+0000213630 00000 n 
+0000005515 00000 n 
+0000005723 00000 n 
+0000005931 00000 n 
+0000006137 00000 n 
+0000009407 00000 n 
+0000009530 00000 n 
+0000009571 00000 n 
+0000213697 00000 n 
+0000009706 00000 n 
+0000009838 00000 n 
+0000213761 00000 n 
+0000009975 00000 n 
+0000013344 00000 n 
+0000013467 00000 n 
+0000013508 00000 n 
+0000013647 00000 n 
+0000013783 00000 n 
+0000013986 00000 n 
+0000017791 00000 n 
+0000017899 00000 n 
+0000021145 00000 n 
+0000021268 00000 n 
+0000021316 00000 n 
+0000021497 00000 n 
+0000021670 00000 n 
+0000021864 00000 n 
+0000022001 00000 n 
+0000025109 00000 n 
+0000025232 00000 n 
+0000025336 00000 n 
+0000213826 00000 n 
+0000025468 00000 n 
+0000213893 00000 n 
+0000025600 00000 n 
+0000213958 00000 n 
+0000025732 00000 n 
+0000214023 00000 n 
+0000025864 00000 n 
+0000214090 00000 n 
+0000025996 00000 n 
+0000214157 00000 n 
+0000026127 00000 n 
+0000214224 00000 n 
+0000026259 00000 n 
+0000214291 00000 n 
+0000026391 00000 n 
+0000214358 00000 n 
+0000026523 00000 n 
+0000214425 00000 n 
+0000026655 00000 n 
+0000214492 00000 n 
+0000026787 00000 n 
+0000214557 00000 n 
+0000026919 00000 n 
+0000030339 00000 n 
+0000030447 00000 n 
+0000034040 00000 n 
+0000034148 00000 n 
+0000037709 00000 n 
+0000037817 00000 n 
+0000041895 00000 n 
+0000042003 00000 n 
+0000046159 00000 n 
+0000046267 00000 n 
+0000050508 00000 n 
+0000050616 00000 n 
+0000054642 00000 n 
+0000054750 00000 n 
+0000058934 00000 n 
+0000059042 00000 n 
+0000063431 00000 n 
+0000063539 00000 n 
+0000067563 00000 n 
+0000067671 00000 n 
+0000071089 00000 n 
+0000071213 00000 n 
+0000071258 00000 n 
+0000071448 00000 n 
+0000071657 00000 n 
+0000071864 00000 n 
+0000075644 00000 n 
+0000075754 00000 n 
+0000079366 00000 n 
+0000079492 00000 n 
+0000079521 00000 n 
+0000079733 00000 n 
+0000083364 00000 n 
+0000083474 00000 n 
+0000086440 00000 n 
+0000086566 00000 n 
+0000086595 00000 n 
+0000086804 00000 n 
+0000089968 00000 n 
+0000090094 00000 n 
+0000090147 00000 n 
+0000090364 00000 n 
+0000090579 00000 n 
+0000090792 00000 n 
+0000091002 00000 n 
+0000094969 00000 n 
+0000095079 00000 n 
+0000097850 00000 n 
+0000097960 00000 n 
+0000100270 00000 n 
+0000100380 00000 n 
+0000102712 00000 n 
+0000102822 00000 n 
+0000105009 00000 n 
+0000105119 00000 n 
+0000107259 00000 n 
+0000107369 00000 n 
+0000109503 00000 n 
+0000109613 00000 n 
+0000111755 00000 n 
+0000111865 00000 n 
+0000114007 00000 n 
+0000114117 00000 n 
+0000116262 00000 n 
+0000116372 00000 n 
+0000118451 00000 n 
+0000118561 00000 n 
+0000120711 00000 n 
+0000120821 00000 n 
+0000122735 00000 n 
+0000122845 00000 n 
+0000125887 00000 n 
+0000125997 00000 n 
+0000129326 00000 n 
+0000129452 00000 n 
+0000129481 00000 n 
+0000129692 00000 n 
+0000132217 00000 n 
+0000132327 00000 n 
+0000134296 00000 n 
+0000134406 00000 n 
+0000137466 00000 n 
+0000137576 00000 n 
+0000139520 00000 n 
+0000139630 00000 n 
+0000141626 00000 n 
+0000141736 00000 n 
+0000144798 00000 n 
+0000144908 00000 n 
 0000147715 00000 n 
-0000024153 00000 n 
-0000024285 00000 n 
-0000147783 00000 n 
-0000024417 00000 n 
-0000024549 00000 n 
-0000147851 00000 n 
-0000024681 00000 n 
-0000147918 00000 n 
-0000024813 00000 n 
-0000147984 00000 n 
-0000024945 00000 n 
-0000027814 00000 n 
-0000027922 00000 n 
-0000030971 00000 n 
-0000031079 00000 n 
-0000034234 00000 n 
-0000034342 00000 n 
-0000037700 00000 n 
-0000037808 00000 n 
-0000041142 00000 n 
-0000041250 00000 n 
-0000044569 00000 n 
-0000044677 00000 n 
-0000048220 00000 n 
-0000048328 00000 n 
-0000051791 00000 n 
-0000051899 00000 n 
-0000055130 00000 n 
-0000055238 00000 n 
-0000058281 00000 n 
-0000058389 00000 n 
-0000061302 00000 n 
-0000061410 00000 n 
-0000064071 00000 n 
-0000064194 00000 n 
-0000064221 00000 n 
-0000064430 00000 n 
-0000066983 00000 n 
-0000067091 00000 n 
-0000069579 00000 n 
-0000069703 00000 n 
-0000069732 00000 n 
-0000069950 00000 n 
-0000072696 00000 n 
-0000072806 00000 n 
-0000076716 00000 n 
-0000076826 00000 n 
-0000079125 00000 n 
-0000079235 00000 n 
-0000082000 00000 n 
-0000082110 00000 n 
-0000084821 00000 n 
-0000084931 00000 n 
-0000087211 00000 n 
-0000087321 00000 n 
-0000089096 00000 n 
-0000089206 00000 n 
-0000091875 00000 n 
-0000091985 00000 n 
-0000093857 00000 n 
-0000093967 00000 n 
-0000095942 00000 n 
-0000096052 00000 n 
-0000098239 00000 n 
-0000098349 00000 n 
-0000100919 00000 n 
-0000101029 00000 n 
-0000103481 00000 n 
-0000103591 00000 n 
-0000106315 00000 n 
-0000106441 00000 n 
-0000106478 00000 n 
-0000106714 00000 n 
-0000106949 00000 n 
-0000109283 00000 n 
-0000109393 00000 n 
-0000111985 00000 n 
-0000112095 00000 n 
-0000114327 00000 n 
-0000114453 00000 n 
-0000114514 00000 n 
-0000148050 00000 n 
-0000114650 00000 n 
-0000148118 00000 n 
-0000114788 00000 n 
-0000148185 00000 n 
-0000114926 00000 n 
-0000148252 00000 n 
-0000115062 00000 n 
-0000148320 00000 n 
-0000115198 00000 n 
-0000118317 00000 n 
-0000118443 00000 n 
-0000118472 00000 n 
-0000148388 00000 n 
-0000118607 00000 n 
-0000121156 00000 n 
-0000121282 00000 n 
-0000121319 00000 n 
-0000148454 00000 n 
-0000121452 00000 n 
-0000121586 00000 n 
-0000123953 00000 n 
-0000124063 00000 n 
-0000126994 00000 n 
-0000127120 00000 n 
-0000127221 00000 n 
-0000127399 00000 n 
-0000127573 00000 n 
-0000127746 00000 n 
-0000127954 00000 n 
-0000128139 00000 n 
-0000128313 00000 n 
-0000128513 00000 n 
-0000128683 00000 n 
-0000128860 00000 n 
-0000129031 00000 n 
-0000131821 00000 n 
-0000131947 00000 n 
-0000131976 00000 n 
-0000132151 00000 n 
-0000132601 00000 n 
-0000132727 00000 n 
-0000132756 00000 n 
-0000148520 00000 n 
-0000148574 00000 n 
-0000132955 00000 n 
-0000148634 00000 n 
-0000133157 00000 n 
-0000148694 00000 n 
-0000133358 00000 n 
-0000148760 00000 n 
-0000133645 00000 n 
-0000133888 00000 n 
-0000148826 00000 n 
-0000134187 00000 n 
-0000134597 00000 n 
-0000148892 00000 n 
-0000135033 00000 n 
-0000148960 00000 n 
-0000135394 00000 n 
-0000149026 00000 n 
-0000135771 00000 n 
-0000136274 00000 n 
-0000149092 00000 n 
-0000136648 00000 n 
-0000149159 00000 n 
-0000136893 00000 n 
-0000137225 00000 n 
-0000149225 00000 n 
-0000137851 00000 n 
-0000149293 00000 n 
-0000138159 00000 n 
-0000149361 00000 n 
-0000138447 00000 n 
-0000149428 00000 n 
-0000138772 00000 n 
-0000149494 00000 n 
-0000139080 00000 n 
-0000149562 00000 n 
-0000139539 00000 n 
-0000149631 00000 n 
-0000139861 00000 n 
-0000149700 00000 n 
-0000140137 00000 n 
-0000149769 00000 n 
-0000140581 00000 n 
-0000149838 00000 n 
-0000140910 00000 n 
-0000149907 00000 n 
-0000141370 00000 n 
-0000141733 00000 n 
-0000149976 00000 n 
-0000142220 00000 n 
-0000150043 00000 n 
-0000142517 00000 n 
-0000150110 00000 n 
-0000142834 00000 n 
-0000150179 00000 n 
-0000143151 00000 n 
-0000150248 00000 n 
-0000143447 00000 n 
-0000150316 00000 n 
-0000143740 00000 n 
-0000150383 00000 n 
-0000144006 00000 n 
-0000150450 00000 n 
-0000144299 00000 n 
-0000150518 00000 n 
-0000144681 00000 n 
-0000144956 00000 n 
-0000145070 00000 n 
-0000145181 00000 n 
-0000145297 00000 n 
-0000145409 00000 n 
-0000145518 00000 n 
-0000145631 00000 n 
-0000145738 00000 n 
-0000145858 00000 n 
+0000147825 00000 n 
+0000151030 00000 n 
+0000151156 00000 n 
+0000151193 00000 n 
+0000151431 00000 n 
+0000151666 00000 n 
+0000154071 00000 n 
+0000154181 00000 n 
+0000157100 00000 n 
+0000157210 00000 n 
+0000160639 00000 n 
+0000160765 00000 n 
+0000160802 00000 n 
+0000161004 00000 n 
+0000161203 00000 n 
+0000164489 00000 n 
+0000164615 00000 n 
+0000164644 00000 n 
+0000164849 00000 n 
+0000167774 00000 n 
+0000167884 00000 n 
+0000171493 00000 n 
+0000171619 00000 n 
+0000171664 00000 n 
+0000171858 00000 n 
+0000214624 00000 n 
+0000171996 00000 n 
+0000214692 00000 n 
+0000172132 00000 n 
+0000175482 00000 n 
+0000175608 00000 n 
+0000175701 00000 n 
+0000214758 00000 n 
+0000175835 00000 n 
+0000214824 00000 n 
+0000175973 00000 n 
+0000214892 00000 n 
+0000176111 00000 n 
+0000214959 00000 n 
+0000176249 00000 n 
+0000215026 00000 n 
+0000176387 00000 n 
+0000215093 00000 n 
+0000176525 00000 n 
+0000215161 00000 n 
+0000176663 00000 n 
+0000215227 00000 n 
+0000176801 00000 n 
+0000177005 00000 n 
+0000180434 00000 n 
+0000180560 00000 n 
+0000180605 00000 n 
+0000215293 00000 n 
+0000180741 00000 n 
+0000180881 00000 n 
+0000181017 00000 n 
+0000183267 00000 n 
+0000183377 00000 n 
+0000187059 00000 n 
+0000187185 00000 n 
+0000187214 00000 n 
+0000187436 00000 n 
+0000191496 00000 n 
+0000191622 00000 n 
+0000191747 00000 n 
+0000191956 00000 n 
+0000192163 00000 n 
+0000192343 00000 n 
+0000192520 00000 n 
+0000192696 00000 n 
+0000192906 00000 n 
+0000193113 00000 n 
+0000193300 00000 n 
+0000193476 00000 n 
+0000193679 00000 n 
+0000193853 00000 n 
+0000194034 00000 n 
+0000194209 00000 n 
+0000196875 00000 n 
+0000197001 00000 n 
+0000197046 00000 n 
+0000197220 00000 n 
+0000197426 00000 n 
+0000215358 00000 n 
+0000215412 00000 n 
+0000197628 00000 n 
+0000215472 00000 n 
+0000197830 00000 n 
+0000215532 00000 n 
+0000198031 00000 n 
+0000215598 00000 n 
+0000198318 00000 n 
+0000198561 00000 n 
+0000215663 00000 n 
+0000198872 00000 n 
+0000199282 00000 n 
+0000215729 00000 n 
+0000199718 00000 n 
+0000215796 00000 n 
+0000200079 00000 n 
+0000215863 00000 n 
+0000200456 00000 n 
+0000200959 00000 n 
+0000215928 00000 n 
+0000201333 00000 n 
+0000215994 00000 n 
+0000201578 00000 n 
+0000201910 00000 n 
+0000216059 00000 n 
+0000202535 00000 n 
+0000216126 00000 n 
+0000202843 00000 n 
+0000216193 00000 n 
+0000203131 00000 n 
+0000216260 00000 n 
+0000203456 00000 n 
+0000216326 00000 n 
+0000203764 00000 n 
+0000216394 00000 n 
+0000204238 00000 n 
+0000216462 00000 n 
+0000204595 00000 n 
+0000216528 00000 n 
+0000204917 00000 n 
+0000216595 00000 n 
+0000205193 00000 n 
+0000216662 00000 n 
+0000205637 00000 n 
+0000216730 00000 n 
+0000205966 00000 n 
+0000216798 00000 n 
+0000206426 00000 n 
+0000206789 00000 n 
+0000216866 00000 n 
+0000207276 00000 n 
+0000216934 00000 n 
+0000207573 00000 n 
+0000207890 00000 n 
+0000208206 00000 n 
+0000208516 00000 n 
+0000217002 00000 n 
+0000208859 00000 n 
+0000217068 00000 n 
+0000209108 00000 n 
+0000217134 00000 n 
+0000209413 00000 n 
+0000217202 00000 n 
+0000209662 00000 n 
+0000217270 00000 n 
+0000209955 00000 n 
+0000217336 00000 n 
+0000210221 00000 n 
+0000217403 00000 n 
+0000210514 00000 n 
+0000217470 00000 n 
+0000210896 00000 n 
+0000211171 00000 n 
+0000211284 00000 n 
+0000211393 00000 n 
+0000211513 00000 n 
+0000211629 00000 n 
+0000211740 00000 n 
+0000211854 00000 n 
+0000211961 00000 n 
+0000212073 00000 n 
 trailer
 <<
-/Size 259
+/Size 334
 /Root 2 0 R
 /Info 4 0 R
 >>
 startxref
-150587
+217538
 %%EOF

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Blob.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Blob.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Blob.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -26,6 +26,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 
 import java.sql.SQLException;
@@ -45,7 +46,7 @@
  * class is new in the JDBC 2.0 API.
  * 
  * @author Mark Matthews
- * @version $Id: Blob.java 4489 2005-11-01 00:43:01Z mmatthews $
+ * @version $Id: Blob.java 6437 2007-05-24 20:17:01Z mmatthews $
  */
 public class Blob implements java.sql.Blob, OutputStreamWatcher {
 
@@ -57,8 +58,16 @@
 
 	/** The binary data that makes up this BLOB */
 	private byte[] binaryData = null;
+	private boolean isClosed = false;
 
 	/**
+     * Creates a Blob without data
+     */
+    Blob() {
+        setBinaryData(Constants.EMPTY_BYTE_ARRAY);
+    }
+    
+	/**
 	 * Creates a BLOB encapsulating the given binary data
 	 * 
 	 * @param data
@@ -78,11 +87,11 @@
 	 * @param columnIndexToSet
 	 *            DOCUMENT ME!
 	 */
-	Blob(byte[] data, ResultSet creatorResultSetToSet, int columnIndexToSet) {
+	Blob(byte[] data, ResultSetInternalMethods creatorResultSetToSet, int columnIndexToSet) {
 		setBinaryData(data);
 	}
 
-	private byte[] getBinaryData() {
+	private synchronized byte[] getBinaryData() {
 		return this.binaryData;
 	}
 
@@ -94,7 +103,9 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public java.io.InputStream getBinaryStream() throws SQLException {
+	public synchronized java.io.InputStream getBinaryStream() throws SQLException {
+		checkClosed();
+		
 		return new ByteArrayInputStream(getBinaryData());
 	}
 
@@ -113,14 +124,28 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public byte[] getBytes(long pos, int length) throws SQLException {
+	public synchronized byte[] getBytes(long pos, int length) throws SQLException {
+		checkClosed();
+		
 		if (pos < 1) {
 			throw SQLError.createSQLException(Messages.getString("Blob.2"), //$NON-NLS-1$
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 		}
 
+		pos--;
+		
+		if (pos > this.binaryData.length) {
+			throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		if (pos + length > this.binaryData.length) {
+			throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
 		byte[] newData = new byte[length];
-		System.arraycopy(getBinaryData(), (int) (pos - 1), newData, 0, length);
+		System.arraycopy(getBinaryData(), (int) (pos), newData, 0, length);
 
 		return newData;
 	}
@@ -134,14 +159,16 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public long length() throws SQLException {
+	public synchronized long length() throws SQLException {
+		checkClosed();
+		
 		return getBinaryData().length;
 	}
 
 	/**
 	 * @see java.sql.Blob#position(byte[], long)
 	 */
-	public long position(byte[] pattern, long start) throws SQLException {
+	public synchronized long position(byte[] pattern, long start) throws SQLException {
 		throw SQLError.createSQLException("Not implemented"); //$NON-NLS-1$
 	}
 
@@ -159,19 +186,23 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public long position(java.sql.Blob pattern, long start) throws SQLException {
+	public synchronized long position(java.sql.Blob pattern, long start) throws SQLException {
+		checkClosed();
+		
 		return position(pattern.getBytes(0, (int) pattern.length()), start);
 	}
 
-	private void setBinaryData(byte[] newBinaryData) {
+	private synchronized void setBinaryData(byte[] newBinaryData) {
 		this.binaryData = newBinaryData;
 	}
 
 	/**
 	 * @see Blob#setBinaryStream(long)
 	 */
-	public OutputStream setBinaryStream(long indexToWriteAt)
+	public synchronized OutputStream setBinaryStream(long indexToWriteAt)
 			throws SQLException {
+		checkClosed();
+		
 		if (indexToWriteAt < 1) {
 			throw SQLError.createSQLException(Messages.getString("Blob.0"), //$NON-NLS-1$
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
@@ -190,15 +221,19 @@
 	/**
 	 * @see Blob#setBytes(long, byte[])
 	 */
-	public int setBytes(long writeAt, byte[] bytes) throws SQLException {
+	public synchronized int setBytes(long writeAt, byte[] bytes) throws SQLException {
+		checkClosed();
+		
 		return setBytes(writeAt, bytes, 0, bytes.length);
 	}
 
 	/**
 	 * @see Blob#setBytes(long, byte[], int, int)
 	 */
-	public int setBytes(long writeAt, byte[] bytes, int offset, int length)
+	public synchronized int setBytes(long writeAt, byte[] bytes, int offset, int length)
 			throws SQLException {
+		checkClosed();
+		
 		OutputStream bytesOut = setBinaryStream(writeAt);
 
 		try {
@@ -220,14 +255,14 @@
 	/**
 	 * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
 	 */
-	public void streamClosed(byte[] byteData) {
+	public synchronized void streamClosed(byte[] byteData) {
 		this.binaryData = byteData;
 	}
 
 	/**
 	 * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
 	 */
-	public void streamClosed(WatchableOutputStream out) {
+	public synchronized void streamClosed(WatchableOutputStream out) {
 		int streamSize = out.size();
 
 		if (streamSize < this.binaryData.length) {
@@ -239,9 +274,110 @@
 	}
 
 	/**
-	 * @see Blob#truncate(long)
-	 */
-	public void truncate(long arg0) throws SQLException {
-		throw new NotImplemented();
+     * Truncates the <code>BLOB</code> value that this <code>Blob</code>
+     * object represents to be <code>len</code> bytes in length.
+     * <p>
+     * <b>Note:</b> If the value specified for <code>len</code>
+     * is greater then the length+1 of the <code>BLOB</code> value then the 
+     * behavior is undefined. Some JDBC drivers may throw a 
+     * <code>SQLException</code> while other drivers may support this 
+     * operation.
+     *
+     * @param len the length, in bytes, to which the <code>BLOB</code> value
+     *        that this <code>Blob</code> object represents should be truncated
+     * @exception SQLException if there is an error accessing the
+     *            <code>BLOB</code> value or if len is less than 0
+     * @exception SQLFeatureNotSupportedException if the JDBC driver does not support
+     * this method
+     * @since 1.4
+     */
+	public synchronized void truncate(long len) throws SQLException {
+		checkClosed();
+		
+		if (len < 1) {
+			throw SQLError.createSQLException("\"len\" argument can not be < 1.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		if (len > this.binaryData.length) {
+			throw SQLError.createSQLException("\"len\" argument can not be larger than the BLOB's length.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		// TODO: Do this without copying byte[]s by maintaining some end pointer
+		// on the original data
+		
+		byte[] newData = new byte[(int)len];
+		System.arraycopy(getBinaryData(), 0, newData, 0, (int)len);
+		this.binaryData = newData;
 	}
+
+	/**
+     * This method frees the <code>Blob</code> object and releases the resources that 
+     * it holds. The object is invalid once the <code>free</code>
+     * method is called.
+     *<p>
+     * After <code>free</code> has been called, any attempt to invoke a
+     * method other than <code>free</code> will result in a <code>SQLException</code> 
+     * being thrown.  If <code>free</code> is called multiple times, the subsequent
+     * calls to <code>free</code> are treated as a no-op.
+     *<p>
+     * 
+     * @throws SQLException if an error occurs releasing
+     * the Blob's resources
+     * @exception SQLFeatureNotSupportedException if the JDBC driver does not support
+     * this method
+     * @since 1.6
+     */
+	
+	public synchronized void free() throws SQLException {
+		this.binaryData = null;
+		this.isClosed = true;
+	}
+
+	/**
+     * Returns an <code>InputStream</code> object that contains a partial <code>Blob</code> value, 
+     * starting  with the byte specified by pos, which is length bytes in length.
+     *
+     * @param pos the offset to the first byte of the partial value to be retrieved.
+     *  The first byte in the <code>Blob</code> is at position 1
+     * @param length the length in bytes of the partial value to be retrieved
+     * @return <code>InputStream</code> through which the partial <code>Blob</code> value can be read.
+     * @throws SQLException if pos is less than 1 or if pos is greater than the number of bytes
+     * in the <code>Blob</code> or if pos + length is greater than the number of bytes 
+     * in the <code>Blob</code>
+     *
+     * @exception SQLFeatureNotSupportedException if the JDBC driver does not support
+     * this method
+     * @since 1.6
+     */
+	public synchronized InputStream getBinaryStream(long pos, long length) throws SQLException {
+		checkClosed();
+		
+		if (pos < 1) {
+			throw SQLError.createSQLException("\"pos\" argument can not be < 1.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		pos--;
+		
+		if (pos > this.binaryData.length) {
+			throw SQLError.createSQLException("\"pos\" argument can not be larger than the BLOB's length.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		if (pos + length > this.binaryData.length) {
+			throw SQLError.createSQLException("\"pos\" + \"length\" arguments can not be larger than the BLOB's length.", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		return new ByteArrayInputStream(getBinaryData(), (int)pos, (int)length);
+	}
+	
+	private synchronized void checkClosed() throws SQLException {
+		if (this.isClosed) {
+			throw SQLError.createSQLException("Invalid operation on closed BLOB", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/BlobFromLocator.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/BlobFromLocator.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/BlobFromLocator.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -32,6 +32,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
 /**
  * The representation (mapping) in the JavaTM programming language of an SQL
  * BLOB value. An SQL BLOB is a built-in type that stores a Binary Large Object
@@ -57,7 +59,7 @@
 	private List primaryKeyValues = null;
 
 	/** The ResultSet that created this BLOB */
-	private ResultSet creatorResultSet;
+	private ResultSetImpl creatorResultSet;
 
 	private String blobColumnName = null;
 
@@ -72,7 +74,7 @@
 	/**
 	 * Creates an updatable BLOB that can update in-place
 	 */
-	BlobFromLocator(ResultSet creatorResultSetToSet, int blobColumnIndex)
+	BlobFromLocator(ResultSetImpl creatorResultSetToSet, int blobColumnIndex)
 			throws SQLException {
 		this.creatorResultSet = creatorResultSetToSet;
 
@@ -271,7 +273,6 @@
 	 *             if a database error occurs
 	 */
 	public byte[] getBytes(long pos, int length) throws SQLException {
-		java.sql.ResultSet blobRs = null;
 		java.sql.PreparedStatement pStmt = null;
 
 		try {
@@ -280,18 +281,18 @@
 
 			return getBytesInternal(pStmt, pos, length);
 		} finally {
-			if (blobRs != null) {
+			if (pStmt != null) {
 				try {
-					blobRs.close();
+					pStmt.close();
 				} catch (SQLException sqlEx) {
 					; // do nothing
 				}
 
-				blobRs = null;
+				pStmt = null;
 			}
 		}
 	}
-
+	
 	/**
 	 * Returns the number of bytes in the BLOB value designated by this Blob
 	 * object.
@@ -545,7 +546,7 @@
 			blobRs = pStmt.executeQuery();
 
 			if (blobRs.next()) {
-				return ((com.mysql.jdbc.ResultSet) blobRs).getBytes(1, true);
+				return ((com.mysql.jdbc.ResultSetImpl) blobRs).getBytes(1, true);
 			}
 
 			throw SQLError.createSQLException(
@@ -668,4 +669,14 @@
 			super.close();
 		}
 	}
+
+	public void free() throws SQLException {
+		this.creatorResultSet = null;
+		this.primaryKeyColumns = null;
+		this.primaryKeyValues = null;
+	}
+
+	public InputStream getBinaryStream(long pos, long length) throws SQLException {
+		throw new NotYetImplementedException();
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Buffer.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Buffer.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Buffer.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -33,7 +33,7 @@
 /**
  * Buffer contains code to read and write packets from/to the MySQL server.
  * 
- * @version $Id: Buffer.java 5417 2006-06-20 21:33:56Z mmatthews $
+ * @version $Id: Buffer.java 6445 2007-06-07 15:25:59Z mmatthews $
  * @author Mark Matthews
  */
 class Buffer {
@@ -174,9 +174,19 @@
 
 		this.position += len;
 
-		return (int) len; // this is safe, as this is only
+		return (int) len;
 	}
 
+	public void fastSkipLenByteArray() {
+		long len = this.readFieldLength();
+
+		if (len == NULL_LENGTH || len == 0) {
+			return;
+		}
+		
+		this.position += len;
+	}
+	
 	protected final byte[] getBufferSource() {
 		return this.byteBuffer;
 	}
@@ -564,7 +574,7 @@
 	// encoding
 	final void writeLenString(String s, String encoding, String serverEncoding,
 			SingleByteCharsetConverter converter, boolean parserKnowsUnicode,
-			Connection conn)
+			ConnectionImpl conn)
 			throws UnsupportedEncodingException, SQLException {
 		byte[] b = null;
 
@@ -623,7 +633,7 @@
 	}
 	
 	//	 Write null-terminated string in the given encoding
-	final void writeString(String s, String encoding, Connection conn) throws SQLException {
+	final void writeString(String s, String encoding, ConnectionImpl conn) throws SQLException {
 		ensureCapacity((s.length() * 2) + 1);
 		try {
 			writeStringNoNull(s, encoding, encoding, false, conn);
@@ -650,7 +660,7 @@
 	// Write a String using the specified character
 	// encoding
 	final void writeStringNoNull(String s, String encoding,
-			String serverEncoding, boolean parserKnowsUnicode, Connection conn)
+			String serverEncoding, boolean parserKnowsUnicode, ConnectionImpl conn)
 			throws UnsupportedEncodingException, SQLException {
 		byte[] b = StringUtils.getBytes(s, encoding, serverEncoding,
 				parserKnowsUnicode, conn);

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/BufferRow.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/BufferRow.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/BufferRow.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,753 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.TimeZone;
+
+/**
+ * A RowHolder implementation that holds one row packet (which is re-used by the
+ * driver, and thus saves memory allocations), and tries when possible to avoid
+ * allocations to break out the results as individual byte[]s.
+ * 
+ * (this isn't possible when doing things like reading floating point values).
+ * 
+ * @version $Id: $
+ */
+public class BufferRow extends ResultSetRow {
+	private Buffer rowFromServer;
+
+	/**
+	 * The beginning of the row packet
+	 */
+	private int homePosition = 0;
+	
+	/**
+	 * The home position before the is-null bitmask for server-side
+	 * prepared statement result sets
+	 */
+	private int preNullBitmaskHomePosition = 0;
+
+	/**
+	 * The last-requested index, used as an optimization, if you ask for the
+	 * same index, we won't seek to find it. If you ask for an index that is >
+	 * than the last one requested, we start seeking from the last requested
+	 * index.
+	 */
+	private int lastRequestedIndex = -1;
+
+	/**
+	 * The position of the last-requested index, optimization in concert with
+	 * lastRequestedIndex.
+	 */
+	private int lastRequestedPos;
+
+	/**
+	 * The metadata of the fields of this result set.
+	 */
+	private Field[] metadata;
+
+	/**
+	 * Is this a row from a server-side prepared statement? If so, they're
+	 * encoded differently, so we have different ways of finding where each
+	 * column is, and unpacking them.
+	 */
+	private boolean isBinaryEncoded;
+
+	/**
+	 * If binary-encoded, the NULL status of each column is at the beginning of
+	 * the row, so we
+	 */
+	private boolean[] isNull;
+
+	private List openStreams;
+
+	public BufferRow(Buffer buf, Field[] fields, boolean isBinaryEncoded)
+			throws SQLException {
+		this.rowFromServer = buf;
+		this.metadata = fields;
+		this.isBinaryEncoded = isBinaryEncoded;
+		this.homePosition = this.rowFromServer.getPosition();
+		this.preNullBitmaskHomePosition = this.homePosition;
+
+		if (fields != null) {
+			setMetadata(fields);
+		}
+	}
+
+	public synchronized void closeOpenStreams() {
+		if (this.openStreams != null) {
+			// This would've looked slicker in a "for" loop
+			// but we want to skip over streams that fail to
+			// close (they probably won't ever)
+			// to be more robust and close everything we _can_
+
+			Iterator iter = this.openStreams.iterator();
+
+			while (iter.hasNext()) {
+
+				try {
+					((InputStream) iter.next()).close();
+				} catch (IOException e) {
+					// ignore - it can't really happen in this case
+				}
+			}
+
+			this.openStreams.clear();
+		}
+	}
+
+	private int findAndSeekToOffset(int index) throws SQLException {
+		if (!this.isBinaryEncoded) {
+
+			if (index == 0) {
+				this.lastRequestedIndex = 0;
+				this.lastRequestedPos = this.homePosition;
+				this.rowFromServer.setPosition(this.homePosition);
+
+				return 0;
+			}
+
+			if (index == this.lastRequestedIndex) {
+				this.rowFromServer.setPosition(this.lastRequestedPos);
+
+				return this.lastRequestedPos;
+			}
+
+			int startingIndex = 0;
+
+			if (index > this.lastRequestedIndex) {
+				if (this.lastRequestedIndex >= 0) {
+					startingIndex = this.lastRequestedIndex;
+				} else {
+					startingIndex = 0;
+				}
+				
+				this.rowFromServer.setPosition(this.lastRequestedPos);
+			} else {
+				this.rowFromServer.setPosition(this.homePosition);
+			}
+
+			for (int i = startingIndex; i < index; i++) {
+				this.rowFromServer.fastSkipLenByteArray();
+			}
+
+			this.lastRequestedIndex = index;
+			this.lastRequestedPos = this.rowFromServer.getPosition();
+
+			return this.lastRequestedPos;
+		}
+
+		return findAndSeekToOffsetForBinaryEncoding(index);
+	}
+
+	private int findAndSeekToOffsetForBinaryEncoding(int index)
+			throws SQLException {
+		if (index == 0) {
+			this.lastRequestedIndex = 0;
+			this.lastRequestedPos = this.homePosition;
+
+			return 0;
+		}
+
+		if (index == this.lastRequestedIndex) {
+			this.rowFromServer.setPosition(this.lastRequestedPos);
+
+			return this.lastRequestedPos;
+		}
+
+		int startingIndex = 0;
+
+		if (index > this.lastRequestedIndex) {
+			if (this.lastRequestedIndex >= 0) {
+				startingIndex = this.lastRequestedIndex;
+			} else {
+				// First-time "scan"
+				startingIndex = 0;
+				this.lastRequestedPos = this.homePosition;
+			}
+			
+			this.rowFromServer.setPosition(this.lastRequestedPos);
+		} else {
+			this.rowFromServer.setPosition(this.homePosition);
+		}
+
+		for (int i = startingIndex; i < index; i++) {
+			if (this.isNull[i]) {
+				continue;
+			}
+
+			int curPosition = this.rowFromServer.getPosition();
+
+			switch (this.metadata[i].getMysqlType()) {
+			case MysqlDefs.FIELD_TYPE_NULL:
+				break; // for dummy binds
+
+			case MysqlDefs.FIELD_TYPE_TINY:
+
+				this.rowFromServer.setPosition(curPosition + 1);
+				break;
+
+			case MysqlDefs.FIELD_TYPE_SHORT:
+			case MysqlDefs.FIELD_TYPE_YEAR:
+				this.rowFromServer.setPosition(curPosition + 2);
+
+				break;
+			case MysqlDefs.FIELD_TYPE_LONG:
+			case MysqlDefs.FIELD_TYPE_INT24:
+				this.rowFromServer.setPosition(curPosition + 4);
+
+				break;
+			case MysqlDefs.FIELD_TYPE_LONGLONG:
+				this.rowFromServer.setPosition(curPosition + 8);
+
+				break;
+			case MysqlDefs.FIELD_TYPE_FLOAT:
+				this.rowFromServer.setPosition(curPosition + 4);
+
+				break;
+			case MysqlDefs.FIELD_TYPE_DOUBLE:
+				this.rowFromServer.setPosition(curPosition + 8);
+
+				break;
+			case MysqlDefs.FIELD_TYPE_TIME:
+				this.rowFromServer.fastSkipLenByteArray();
+
+				break;
+			case MysqlDefs.FIELD_TYPE_DATE:
+
+				this.rowFromServer.fastSkipLenByteArray();
+
+				break;
+			case MysqlDefs.FIELD_TYPE_DATETIME:
+			case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+				this.rowFromServer.fastSkipLenByteArray();
+
+				break;
+			case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+			case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+			case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+			case MysqlDefs.FIELD_TYPE_BLOB:
+			case MysqlDefs.FIELD_TYPE_VAR_STRING:
+			case MysqlDefs.FIELD_TYPE_VARCHAR:
+			case MysqlDefs.FIELD_TYPE_STRING:
+			case MysqlDefs.FIELD_TYPE_DECIMAL:
+			case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
+			case MysqlDefs.FIELD_TYPE_GEOMETRY:
+			case MysqlDefs.FIELD_TYPE_BIT:
+				this.rowFromServer.fastSkipLenByteArray();
+
+				break;
+
+			default:
+				throw SQLError.createSQLException(Messages
+						.getString("MysqlIO.97") //$NON-NLS-1$
+						+ this.metadata[i].getMysqlType()
+						+ Messages.getString("MysqlIO.98")
+						+ (i + 1)
+						+ Messages.getString("MysqlIO.99") //$NON-NLS-1$ //$NON-NLS-2$
+						+ this.metadata.length
+						+ Messages.getString("MysqlIO.100"), //$NON-NLS-1$
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+
+		this.lastRequestedIndex = index;
+		this.lastRequestedPos = this.rowFromServer.getPosition();
+
+		return this.lastRequestedPos;
+	}
+
+	public synchronized InputStream getBinaryInputStream(int columnIndex)
+			throws SQLException {
+		if (this.isBinaryEncoded) {
+			if (isNull(columnIndex)) {
+				return null;
+			}
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		if (length == Buffer.NULL_LENGTH) {
+			return null;
+		}
+
+		InputStream stream = new ByteArrayInputStream(this.rowFromServer
+				.getByteBuffer(), offset, (int) length);
+
+		if (this.openStreams == null) {
+			this.openStreams = new LinkedList();
+		}
+
+		return stream;
+	}
+
+	public byte[] getColumnValue(int index) throws SQLException {
+		findAndSeekToOffset(index);
+
+		if (!this.isBinaryEncoded) {
+			return this.rowFromServer.readLenByteArray(0);
+		}
+
+		if (this.isNull[index]) {
+			return null;
+		}
+
+		switch (this.metadata[index].getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_NULL:
+			return null;
+
+		case MysqlDefs.FIELD_TYPE_TINY:
+			return new byte[] { this.rowFromServer.readByte() };
+
+		case MysqlDefs.FIELD_TYPE_SHORT:
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			return this.rowFromServer.getBytes(2);
+
+		case MysqlDefs.FIELD_TYPE_LONG:
+		case MysqlDefs.FIELD_TYPE_INT24:
+			return this.rowFromServer.getBytes(4);
+
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			return this.rowFromServer.getBytes(8);
+
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			return this.rowFromServer.getBytes(4);
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			return this.rowFromServer.getBytes(8);
+
+		case MysqlDefs.FIELD_TYPE_TIME:
+		case MysqlDefs.FIELD_TYPE_DATE:
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+		case MysqlDefs.FIELD_TYPE_BLOB:
+		case MysqlDefs.FIELD_TYPE_VAR_STRING:
+		case MysqlDefs.FIELD_TYPE_VARCHAR:
+		case MysqlDefs.FIELD_TYPE_STRING:
+		case MysqlDefs.FIELD_TYPE_DECIMAL:
+		case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
+		case MysqlDefs.FIELD_TYPE_GEOMETRY:
+		case MysqlDefs.FIELD_TYPE_BIT:
+			return this.rowFromServer.readLenByteArray(0);
+
+		default:
+			throw SQLError.createSQLException(Messages.getString("MysqlIO.97") //$NON-NLS-1$
+					+ this.metadata[index].getMysqlType()
+					+ Messages.getString("MysqlIO.98")
+					+ (index + 1)
+					+ Messages.getString("MysqlIO.99") //$NON-NLS-1$ //$NON-NLS-2$
+					+ this.metadata.length + Messages.getString("MysqlIO.100"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	public int getInt(int columnIndex) throws SQLException {
+
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		if (length == Buffer.NULL_LENGTH) {
+			return 0;
+		}
+
+		return StringUtils.getInt(this.rowFromServer.getByteBuffer(), offset,
+				offset + (int) length);
+	}
+
+	public long getLong(int columnIndex) throws SQLException {
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+		
+		if (length == Buffer.NULL_LENGTH) {
+			return 0;
+		}
+
+		return StringUtils.getLong(this.rowFromServer.getByteBuffer(), offset,
+				offset + (int) length);
+	}
+
+	public double getNativeDouble(int columnIndex) throws SQLException {
+		if (isNull(columnIndex)) {
+			return 0;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+	
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeDouble(this.rowFromServer.getByteBuffer(), offset);
+	}
+
+	public float getNativeFloat(int columnIndex) throws SQLException {
+		if (isNull(columnIndex)) {
+			return 0;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeFloat(this.rowFromServer.getByteBuffer(), offset);
+	}
+
+	public int getNativeInt(int columnIndex) throws SQLException {
+		if (isNull(columnIndex)) {
+			return 0;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeInt(this.rowFromServer.getByteBuffer(), offset);
+	}
+
+	public long getNativeLong(int columnIndex) throws SQLException {
+		if (isNull(columnIndex)) {
+			return 0;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeLong(this.rowFromServer.getByteBuffer(), offset);
+	}
+
+	public short getNativeShort(int columnIndex) throws SQLException {
+		if (isNull(columnIndex)) {
+			return 0;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeShort(this.rowFromServer.getByteBuffer(), offset);
+	}
+
+	public Timestamp getNativeTimestamp(int columnIndex,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeTimestamp(this.rowFromServer.getByteBuffer(), offset,
+				(int) length, targetCalendar, tz, rollForward, conn, rs);
+	}
+
+	public Reader getReader(int columnIndex) throws SQLException {
+		InputStream stream = getBinaryInputStream(columnIndex);
+
+		if (stream == null) {
+			return null;
+		}
+
+		try {
+			return new InputStreamReader(stream, this.metadata[columnIndex]
+					.getCharacterSet());
+		} catch (UnsupportedEncodingException e) {
+			SQLException sqlEx = SQLError.createSQLException("");
+
+			sqlEx.initCause(e);
+
+			throw sqlEx;
+		}
+	}
+
+	public String getString(int columnIndex, String encoding, ConnectionImpl conn)
+			throws SQLException {
+		if (this.isBinaryEncoded) {
+			if (isNull(columnIndex)) {
+				return null;
+			}
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+
+		if (length == Buffer.NULL_LENGTH) {
+			return null;
+		}
+
+		if (length == 0) {
+			return "";
+		}
+
+		// TODO: I don't like this, would like to push functionality back
+		// to the buffer class somehow
+
+		int offset = this.rowFromServer.getPosition();
+
+		return getString(encoding, conn, this.rowFromServer.getByteBuffer(),
+				offset, (int) length);
+	}
+
+	public Time getTimeFast(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		return getTimeFast(columnIndex, this.rowFromServer.getByteBuffer(),
+				offset, (int)length, targetCalendar, tz, rollForward, conn, rs);
+	}
+
+	public Timestamp getTimestampFast(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		return getTimestampFast(columnIndex,
+				this.rowFromServer.getByteBuffer(), offset, (int)length, targetCalendar, tz,
+				rollForward, conn, rs);
+	}
+
+	public boolean isFloatingPointNumber(int index) throws SQLException {
+		if (this.isBinaryEncoded) {
+			switch (this.metadata[index].getSQLType()) {
+			case Types.FLOAT:
+			case Types.DOUBLE:
+			case Types.DECIMAL:
+			case Types.NUMERIC:
+				return true;
+			default:
+				return false;
+			}
+		}
+
+		findAndSeekToOffset(index);
+
+		long length = this.rowFromServer.readFieldLength();
+
+		if (length == Buffer.NULL_LENGTH) {
+			return false;
+		}
+
+		if (length == 0) {
+			return false;
+		}
+
+		for (int i = 0; i < (int) length; i++) {
+			char c = (char) this.rowFromServer.readByte();
+
+			if ((c == 'e') || (c == 'E')) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public boolean isNull(int index) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			findAndSeekToOffset(index);
+
+			return this.rowFromServer.readFieldLength() == Buffer.NULL_LENGTH;
+		}
+
+		return this.isNull[index];
+	}
+
+	public long length(int index) throws SQLException {
+		findAndSeekToOffset(index);
+
+		long length = this.rowFromServer.readFieldLength();
+
+		if (length == Buffer.NULL_LENGTH) {
+			return 0;
+		}
+
+		return length;
+	}
+
+	public void setColumnValue(int index, byte[] value) throws SQLException {
+		throw new OperationNotSupportedException();
+	}
+
+	public void setMetadata(Field[] f) throws SQLException {
+		super.setMetadata(f);
+
+		if (this.isBinaryEncoded) {
+			setupIsNullBitmask();
+		}
+	}
+
+	/**
+	 * Unpacks the bitmask at the head of the row packet that tells us what
+	 * columns hold null values, and sets the "home" position directly after the
+	 * bitmask.
+	 */
+	private void setupIsNullBitmask() throws SQLException {
+		if (this.isNull != null) {
+			return; // we've already done this
+		}
+		
+		this.rowFromServer.setPosition(this.preNullBitmaskHomePosition);
+		
+		int nullCount = (this.metadata.length + 9) / 8;
+
+		byte[] nullBitMask = new byte[nullCount];
+
+		for (int i = 0; i < nullCount; i++) {
+			nullBitMask[i] = this.rowFromServer.readByte();
+		}
+
+		this.homePosition = this.rowFromServer.getPosition();
+
+		this.isNull = new boolean[this.metadata.length];
+
+		int nullMaskPos = 0;
+		int bit = 4; // first two bits are reserved for future use
+
+		for (int i = 0; i < this.metadata.length; i++) {
+
+			this.isNull[i] = ((nullBitMask[nullMaskPos] & bit) != 0);
+
+			if (((bit <<= 1) & 255) == 0) {
+				bit = 1; /* To next byte */
+
+				nullMaskPos++;
+			}
+		}
+	}
+
+	public Date getDateFast(int columnIndex, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		return getDateFast(columnIndex, this.rowFromServer.getByteBuffer(),
+				offset, (int)length, conn, rs);
+	}
+
+	public java.sql.Date getNativeDate(int columnIndex, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+		
+		return getNativeDate(columnIndex, this.rowFromServer.getByteBuffer(),
+				offset, (int) length, conn, rs);
+	}
+
+	public Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar,
+			int jdbcType, int mysqlType, TimeZone tz,
+			boolean rollForward, ConnectionImpl conn, ResultSetImpl rs)
+			throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+
+		return getNativeDateTimeValue(columnIndex, this.rowFromServer
+				.getByteBuffer(), offset, (int) length, targetCalendar, jdbcType,
+				mysqlType, tz, rollForward, conn, rs);
+	}
+
+	public Time getNativeTime(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		if (isNull(columnIndex)) {
+			return null;
+		}
+		
+		findAndSeekToOffset(columnIndex);
+
+		long length = this.rowFromServer.readFieldLength();
+		
+		int offset = this.rowFromServer.getPosition();
+		
+		return getNativeTime(columnIndex, this.rowFromServer.getByteBuffer(),
+				offset, (int) length, targetCalendar, tz, rollForward, conn, rs);
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ByteArrayRow.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ByteArrayRow.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ByteArrayRow.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,278 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * A RowHolder implementation that is for cached results (a-la
+ * mysql_store_result()).
+ * 
+ * @version $Id: $
+ */
+public class ByteArrayRow extends ResultSetRow {
+
+	byte[][] internalRowData;
+
+	public ByteArrayRow(byte[][] internalRowData) {
+		this.internalRowData = internalRowData;
+	}
+
+	public byte[] getColumnValue(int index) throws SQLException {
+		return this.internalRowData[index];
+	}
+
+	public void setColumnValue(int index, byte[] value) throws SQLException {
+		this.internalRowData[index] = value;
+	}
+
+	public String getString(int index, String encoding, ConnectionImpl conn)
+			throws SQLException {
+		byte[] columnData = this.internalRowData[index];
+
+		if (columnData == null) {
+			return null;
+		}
+
+		return getString(encoding, conn, columnData, 0, columnData.length);
+	}
+
+	public boolean isNull(int index) throws SQLException {
+		return this.internalRowData[index] == null;
+	}
+
+	public boolean isFloatingPointNumber(int index) throws SQLException {
+		byte[] numAsBytes = this.internalRowData[index];
+
+		if (this.internalRowData[index] == null
+				|| this.internalRowData[index].length == 0) {
+			return false;
+		}
+
+		for (int i = 0; i < numAsBytes.length; i++) {
+			if (((char) numAsBytes[i] == 'e') || ((char) numAsBytes[i] == 'E')) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	public long length(int index) throws SQLException {
+		if (this.internalRowData[index] == null) {
+			return 0;
+		}
+
+		return this.internalRowData[index].length;
+	}
+
+	public int getInt(int columnIndex) {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return StringUtils.getInt(this.internalRowData[columnIndex]);
+	}
+
+	public long getLong(int columnIndex) {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return StringUtils.getLong(this.internalRowData[columnIndex]);
+	}
+
+	public Timestamp getTimestampFast(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		byte[] columnValue = this.internalRowData[columnIndex];
+
+		if (columnValue == null) {
+			return null;
+		}
+
+		return getTimestampFast(columnIndex, this.internalRowData[columnIndex],
+				0, columnValue.length, targetCalendar, tz, rollForward, conn,
+				rs);
+	}
+
+	public double getNativeDouble(int columnIndex) throws SQLException {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return getNativeDouble(this.internalRowData[columnIndex], 0);
+	}
+
+	public float getNativeFloat(int columnIndex) throws SQLException {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return getNativeFloat(this.internalRowData[columnIndex], 0);
+	}
+
+	public int getNativeInt(int columnIndex) throws SQLException {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return getNativeInt(this.internalRowData[columnIndex], 0);
+	}
+
+	public long getNativeLong(int columnIndex) throws SQLException {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return getNativeLong(this.internalRowData[columnIndex], 0);
+	}
+
+	public short getNativeShort(int columnIndex) throws SQLException {
+		if (this.internalRowData[columnIndex] == null) {
+			return 0;
+		}
+
+		return getNativeShort(this.internalRowData[columnIndex], 0);
+	}
+
+	public Timestamp getNativeTimestamp(int columnIndex,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException {
+		byte[] bits = this.internalRowData[columnIndex];
+
+		if (bits == null) {
+			return null;
+		}
+
+		return getNativeTimestamp(bits, 0, bits.length, targetCalendar, tz,
+				rollForward, conn, rs);
+	}
+
+	public void closeOpenStreams() {
+		// no-op for this type
+	}
+
+	public InputStream getBinaryInputStream(int columnIndex)
+			throws SQLException {
+		if (this.internalRowData[columnIndex] == null) {
+			return null;
+		}
+
+		return new ByteArrayInputStream(this.internalRowData[columnIndex]);
+	}
+
+	public Reader getReader(int columnIndex) throws SQLException {
+		InputStream stream = getBinaryInputStream(columnIndex);
+
+		if (stream == null) {
+			return null;
+		}
+
+		try {
+			return new InputStreamReader(stream, this.metadata[columnIndex]
+					.getCharacterSet());
+		} catch (UnsupportedEncodingException e) {
+			SQLException sqlEx = SQLError.createSQLException("");
+
+			sqlEx.initCause(e);
+
+			throw sqlEx;
+		}
+	}
+
+	public Time getTimeFast(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		byte[] columnValue = this.internalRowData[columnIndex];
+
+		if (columnValue == null) {
+			return null;
+		}
+
+		return getTimeFast(columnIndex, this.internalRowData[columnIndex], 0,
+				columnValue.length, targetCalendar, tz, rollForward, conn, rs);
+	}
+
+	public Date getDateFast(int columnIndex, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		byte[] columnValue = this.internalRowData[columnIndex];
+
+		if (columnValue == null) {
+			return null;
+		}
+
+		return getDateFast(columnIndex, this.internalRowData[columnIndex], 0,
+				columnValue.length, conn, rs);
+	}
+
+	public Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar,
+			int jdbcType, int mysqlType, TimeZone tz,
+			boolean rollForward, ConnectionImpl conn, ResultSetImpl rs)
+			throws SQLException {
+		byte[] columnValue = this.internalRowData[columnIndex];
+
+		if (columnValue == null) {
+			return null;
+		}
+
+		return getNativeDateTimeValue(columnIndex, columnValue, 0,
+				columnValue.length, targetCalendar, jdbcType, mysqlType, tz,
+				rollForward, conn, rs);
+	}
+
+	public Date getNativeDate(int columnIndex, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		byte[] columnValue = this.internalRowData[columnIndex];
+
+		if (columnValue == null) {
+			return null;
+		}
+
+		return getNativeDate(columnIndex, columnValue, 0, columnValue.length,
+				conn, rs);
+	}
+
+	public Time getNativeTime(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+		byte[] columnValue = this.internalRowData[columnIndex];
+
+		if (columnValue == null) {
+			return null;
+		}
+
+		return getNativeTime(columnIndex, columnValue, 0, columnValue.length,
+				targetCalendar, tz, rollForward, conn, rs);
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/CachedResultSetMetaData.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/CachedResultSetMetaData.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/CachedResultSetMetaData.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+
+package com.mysql.jdbc;
+
+import java.util.Map;
+
+class CachedResultSetMetaData {
+		/** Map column names (and all of their permutations) to column indices */
+		Map columnNameToIndex = null;
+
+		/** Cached Field info */
+		Field[] fields;
+
+		/** Map of fully-specified column names to column indices */
+		Map fullColumnNameToIndex = null;
+
+		/** Cached ResultSetMetaData */
+		java.sql.ResultSetMetaData metadata;
+	}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/CallableStatement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/CallableStatement.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/CallableStatement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2005 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -26,6 +26,8 @@
 import java.io.Reader;
 import java.io.UnsupportedEncodingException;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 
 import java.net.URL;
@@ -46,9 +48,11 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
+import java.util.Properties;
 
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
 /**
  * Representation of stored procedures for JDBC
  * 
@@ -58,7 +62,38 @@
  */
 public class CallableStatement extends PreparedStatement implements
 		java.sql.CallableStatement {
-	class CallableStatementParam {
+	protected final static Constructor JDBC_4_CSTMT_2_ARGS_CTOR;
+	
+	protected final static Constructor JDBC_4_CSTMT_4_ARGS_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_CSTMT_2_ARGS_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4CallableStatement")
+						.getConstructor(
+								new Class[] { ConnectionImpl.class,
+										CallableStatementParamInfo.class });
+				JDBC_4_CSTMT_4_ARGS_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4CallableStatement")
+						.getConstructor(
+								new Class[] { ConnectionImpl.class,
+										String.class, String.class,
+										Boolean.TYPE });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_CSTMT_4_ARGS_CTOR = null;
+			JDBC_4_CSTMT_2_ARGS_CTOR = null;
+		}
+	}
+	
+	protected class CallableStatementParam {
 		int desiredJdbcType;
 
 		int index;
@@ -107,7 +142,7 @@
 		}
 	}
 
-	class CallableStatementParamInfo {
+	protected class CallableStatementParamInfo {
 		String catalogInUse;
 
 		boolean isFunctionCall;
@@ -179,8 +214,8 @@
 			
 			if (isFunctionCall) {
 				this.numParameters += 1;
-			}
 		}
+		}
 
 		private void addParametersFromDBMD(java.sql.ResultSet paramTypesRs)
 				throws SQLException {
@@ -274,7 +309,7 @@
 			if (this.parameterList == null) {
 				return 0;
 			}
-
+			
 			return this.parameterList.size();
 		}
 
@@ -335,7 +370,7 @@
 	 * quite a bit out there in the wild (Websphere, FreeBSD, anyone?)
 	 */
 
-	class CallableStatementParamInfoJDBC3 extends CallableStatementParamInfo
+	protected class CallableStatementParamInfoJDBC3 extends CallableStatementParamInfo
 			implements ParameterMetaData {
 
 		CallableStatementParamInfoJDBC3(java.sql.ResultSet paramTypesRs)
@@ -346,6 +381,54 @@
 		public CallableStatementParamInfoJDBC3(CallableStatementParamInfo paramInfo) {
 			super(paramInfo);
 		}
+		
+		/**
+	     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+	     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+	     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+	     * object. If this does not implement the interface and is not a wrapper, return false.
+	     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+	     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+	     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+	     *
+	     * @param interfaces a Class defining an interface.
+	     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+	     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+	     * for an object with the given interface.
+	     * @since 1.6
+	     */
+		public boolean isWrapperFor(Class iface) throws SQLException {
+			checkClosed();
+			
+			// This works for classes that aren't actually wrapping
+			// anything
+			return iface.isInstance(this);
+		}
+
+	    /**
+	     * Returns an object that implements the given interface to allow access to non-standard methods,
+	     * or standard methods not exposed by the proxy.
+	     * The result may be either the object found to implement the interface or a proxy for that object.
+	     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+	     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+	     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+	     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+	     *
+	     * @param iface A Class defining an interface that the result must implement.
+	     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+	     * @throws java.sql.SQLException If no object found that implements the interface 
+	     * @since 1.6
+	     */
+		public Object unwrap(Class iface) throws java.sql.SQLException {
+	    	try {
+	    		// This works for classes that aren't actually wrapping
+	    		// anything
+	    		return Util.cast(iface, this);
+	        } catch (ClassCastException cce) {
+	            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+	            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+	        }
+	    }
 	}
 
 	private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE;
@@ -375,15 +458,15 @@
 
 	private boolean callingStoredFunction = false;
 
-	private ResultSet functionReturnValueResults;
+	private ResultSetInternalMethods functionReturnValueResults;
 
 	private boolean hasOutputParams = false;
 
 	// private List parameterList;
 	// private Map parameterMap;
-	private ResultSet outputParameterResults;
+	private ResultSetInternalMethods outputParameterResults;
 
-	private boolean outputParamWasNull = false;
+	protected boolean outputParamWasNull = false;
 
 	private int[] parameterIndexToRsIndex;
 
@@ -402,7 +485,7 @@
 	 * @throws SQLException
 	 *             if an error occurs
 	 */
-	public CallableStatement(Connection conn,
+	public CallableStatement(ConnectionImpl conn,
 			CallableStatementParamInfo paramInfo) throws SQLException {
 		super(conn, paramInfo.nativeSql, paramInfo.catalogInUse);
 
@@ -415,32 +498,48 @@
 	}
 
 	/**
-	 * Creates a new CallableStatement
-	 * 
-	 * @param conn
-	 *            the connection creating this statement
-	 * @param catalog
-	 *            catalog the current catalog
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs
+	 * Creates a callable statement instance -- We need to provide factory-style methods
+	 * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
+	 * the class verifier complains when it tries to load JDBC4-only interface
+	 * classes that are present in JDBC4 method signatures.
 	 */
-	public CallableStatement(Connection conn, String catalog)
-			throws SQLException {
-		super(conn, catalog, null);
 
-		determineParameterTypes();
-		generateParameterMap();
-		
-		if (this.callingStoredFunction) {
-			this.parameterCount += 1;
+	protected static CallableStatement getInstance(ConnectionImpl conn, String sql,
+			String catalog, boolean isFunctionCall) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new CallableStatement(conn, sql, catalog, isFunctionCall);
 		}
+
+		return (CallableStatement) Util.handleNewInstance(
+				JDBC_4_CSTMT_4_ARGS_CTOR, new Object[] { conn, sql, catalog,
+						Boolean.valueOf(isFunctionCall) });
 	}
+	
+	/**
+	 * Creates a callable statement instance -- We need to provide factory-style methods
+	 * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
+	 * the class verifier complains when it tries to load JDBC4-only interface
+	 * classes that are present in JDBC4 method signatures.
+	 */
 
+	protected static CallableStatement getInstance(ConnectionImpl conn,
+			CallableStatementParamInfo paramInfo) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new CallableStatement(conn, paramInfo);
+		}
+
+		return (CallableStatement) Util.handleNewInstance(
+				JDBC_4_CSTMT_2_ARGS_CTOR, new Object[] { conn, paramInfo });
+
+	}
+	
 	private int[] placeholderToParameterIndexMap;
 	
-	
 	private void generateParameterMap() throws SQLException {
+		if (this.paramInfo == null) {
+			return;
+		}
+		
 		// if the user specified some parameters as literals, we need to
 		// provide a map from the specified placeholders to the actual
 		// parameter numbers
@@ -504,7 +603,7 @@
 	 * @throws SQLException
 	 *             if an error occurs
 	 */
-	public CallableStatement(Connection conn, String sql, String catalog,
+	public CallableStatement(ConnectionImpl conn, String sql, String catalog,
 			boolean isFunctionCall) throws SQLException {
 		super(conn, sql, catalog);
 
@@ -560,7 +659,14 @@
 		CallableStatementParam paramDescriptor = this.paramInfo
 				.getParameter(localParamIndex);
 
-		if (!paramDescriptor.isOut) {
+		// We don't have reliable metadata in this case, trust
+		// the caller
+		
+		if (this.connection.getNoAccessToProcedureBodies()) {
+			paramDescriptor.isOut = true;
+			paramDescriptor.isIn = true;
+			paramDescriptor.inOutModifier = DatabaseMetaData.procedureColumnInOut;
+		} else if (!paramDescriptor.isOut) {
 			throw SQLError.createSQLException(
 					Messages.getString("CallableStatement.9") + paramIndex //$NON-NLS-1$
 							+ Messages.getString("CallableStatement.10"), //$NON-NLS-1$
@@ -653,7 +759,7 @@
 			row[3] = StringUtils.s2b(String.valueOf(i), this.connection); // COLUMN_NAME
 
 			row[4] = StringUtils.s2b(String
-					.valueOf(DatabaseMetaData.procedureColumnInOut),
+					.valueOf(DatabaseMetaData.procedureColumnIn),
 					this.connection);
 
 			row[5] = StringUtils.s2b(String.valueOf(Types.VARCHAR),
@@ -670,7 +776,7 @@
 
 			row[12] = null;
 
-			resultRows.add(row);
+			resultRows.add(new ByteArrayRow(row));
 		}
 
 		java.sql.ResultSet paramTypesRs = DatabaseMetaData.buildResultSet(
@@ -701,7 +807,7 @@
 
 			paramTypesRs = dbmd.getProcedureColumns(this.connection
 					.versionMeetsMinimum(5, 0, 2)
-					& useCatalog ? this.currentCatalog : null, null, procName,
+					&& useCatalog ? this.currentCatalog : null, null, procName,
 					"%"); //$NON-NLS-1$
 
 			convertGetProcedureColumnsToInternalDescriptors(paramTypesRs);
@@ -823,13 +929,16 @@
 	}
 
 	private String extractProcedureName() throws SQLException {
+		String sanitizedSql = StringUtils.stripComments(this.originalSql, 
+				"`\"'", "`\"'", true, false, true, true);
+		
 		// TODO: Do this with less memory allocation
-		int endCallIndex = StringUtils.indexOfIgnoreCase(this.originalSql,
+		int endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql,
 				"CALL "); //$NON-NLS-1$
 		int offset = 5;
 
 		if (endCallIndex == -1) {
-			endCallIndex = StringUtils.indexOfIgnoreCase(this.originalSql,
+			endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql,
 					"SELECT ");
 			offset = 7;
 		}
@@ -837,7 +946,7 @@
 		if (endCallIndex != -1) {
 			StringBuffer nameBuf = new StringBuffer();
 
-			String trimmedStatement = this.originalSql.substring(
+			String trimmedStatement = sanitizedSql.substring(
 					endCallIndex + offset).trim();
 
 			int statementLength = trimmedStatement.length();
@@ -854,9 +963,9 @@
 
 			return nameBuf.toString();
 		}
+		
 		throw SQLError.createSQLException(Messages.getString("CallableStatement.1"), //$NON-NLS-1$
 				SQLError.SQL_STATE_GENERAL_ERROR);
-
 	}
 
 	/**
@@ -870,7 +979,7 @@
 	 * @throws SQLException
 	 *             if the parameter name is null or empty.
 	 */
-	private String fixParameterName(String paramNameIn) throws SQLException {
+	protected String fixParameterName(String paramNameIn) throws SQLException {
 		if ((paramNameIn == null) || (paramNameIn.length() == 0)) {
 			throw SQLError.createSQLException(
 					((Messages.getString("CallableStatement.0") + paramNameIn) == null) //$NON-NLS-1$
@@ -897,7 +1006,7 @@
 	 * @see java.sql.CallableStatement#getArray(int)
 	 */
 	public synchronized Array getArray(int i) throws SQLException {
-		ResultSet rs = getOutputParameters(i);
+		ResultSetInternalMethods rs = getOutputParameters(i);
 
 		Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i));
 
@@ -911,7 +1020,7 @@
 	 */
 	public synchronized Array getArray(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Array retValue = rs.getArray(fixParameterName(parameterName));
@@ -926,7 +1035,7 @@
 	 */
 	public synchronized BigDecimal getBigDecimal(int parameterIndex)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		BigDecimal retValue = rs
 				.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -954,7 +1063,7 @@
 	 */
 	public synchronized BigDecimal getBigDecimal(int parameterIndex, int scale)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		BigDecimal retValue = rs.getBigDecimal(
 				mapOutputParameterIndexToRsIndex(parameterIndex), scale);
@@ -969,7 +1078,7 @@
 	 */
 	public synchronized BigDecimal getBigDecimal(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName));
@@ -983,7 +1092,7 @@
 	 * @see java.sql.CallableStatement#getBlob(int)
 	 */
 	public synchronized Blob getBlob(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Blob retValue = rs
 				.getBlob(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -997,7 +1106,7 @@
 	 * @see java.sql.CallableStatement#getBlob(java.lang.String)
 	 */
 	public synchronized Blob getBlob(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Blob retValue = rs.getBlob(fixParameterName(parameterName));
@@ -1012,7 +1121,7 @@
 	 */
 	public synchronized boolean getBoolean(int parameterIndex)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		boolean retValue = rs
 				.getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1027,7 +1136,7 @@
 	 */
 	public synchronized boolean getBoolean(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		boolean retValue = rs.getBoolean(fixParameterName(parameterName));
@@ -1041,7 +1150,7 @@
 	 * @see java.sql.CallableStatement#getByte(int)
 	 */
 	public synchronized byte getByte(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		byte retValue = rs
 				.getByte(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1055,7 +1164,7 @@
 	 * @see java.sql.CallableStatement#getByte(java.lang.String)
 	 */
 	public synchronized byte getByte(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		byte retValue = rs.getByte(fixParameterName(parameterName));
@@ -1069,7 +1178,7 @@
 	 * @see java.sql.CallableStatement#getBytes(int)
 	 */
 	public synchronized byte[] getBytes(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		byte[] retValue = rs
 				.getBytes(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1084,7 +1193,7 @@
 	 */
 	public synchronized byte[] getBytes(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		byte[] retValue = rs.getBytes(fixParameterName(parameterName));
@@ -1098,7 +1207,7 @@
 	 * @see java.sql.CallableStatement#getClob(int)
 	 */
 	public synchronized Clob getClob(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Clob retValue = rs
 				.getClob(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1112,7 +1221,7 @@
 	 * @see java.sql.CallableStatement#getClob(java.lang.String)
 	 */
 	public synchronized Clob getClob(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Clob retValue = rs.getClob(fixParameterName(parameterName));
@@ -1126,7 +1235,7 @@
 	 * @see java.sql.CallableStatement#getDate(int)
 	 */
 	public synchronized Date getDate(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Date retValue = rs
 				.getDate(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1141,7 +1250,7 @@
 	 */
 	public synchronized Date getDate(int parameterIndex, Calendar cal)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Date retValue = rs.getDate(
 				mapOutputParameterIndexToRsIndex(parameterIndex), cal);
@@ -1155,7 +1264,7 @@
 	 * @see java.sql.CallableStatement#getDate(java.lang.String)
 	 */
 	public synchronized Date getDate(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Date retValue = rs.getDate(fixParameterName(parameterName));
@@ -1171,7 +1280,7 @@
 	 */
 	public synchronized Date getDate(String parameterName, Calendar cal)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Date retValue = rs.getDate(fixParameterName(parameterName), cal);
@@ -1186,7 +1295,7 @@
 	 */
 	public synchronized double getDouble(int parameterIndex)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		double retValue = rs
 				.getDouble(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1201,7 +1310,7 @@
 	 */
 	public synchronized double getDouble(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		double retValue = rs.getDouble(fixParameterName(parameterName));
@@ -1215,7 +1324,7 @@
 	 * @see java.sql.CallableStatement#getFloat(int)
 	 */
 	public synchronized float getFloat(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		float retValue = rs
 				.getFloat(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1230,7 +1339,7 @@
 	 */
 	public synchronized float getFloat(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		float retValue = rs.getFloat(fixParameterName(parameterName));
@@ -1244,7 +1353,7 @@
 	 * @see java.sql.CallableStatement#getInt(int)
 	 */
 	public synchronized int getInt(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		int retValue = rs
 				.getInt(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1258,7 +1367,7 @@
 	 * @see java.sql.CallableStatement#getInt(java.lang.String)
 	 */
 	public synchronized int getInt(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		int retValue = rs.getInt(fixParameterName(parameterName));
@@ -1272,7 +1381,7 @@
 	 * @see java.sql.CallableStatement#getLong(int)
 	 */
 	public synchronized long getLong(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		long retValue = rs
 				.getLong(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1286,7 +1395,7 @@
 	 * @see java.sql.CallableStatement#getLong(java.lang.String)
 	 */
 	public synchronized long getLong(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		long retValue = rs.getLong(fixParameterName(parameterName));
@@ -1296,7 +1405,7 @@
 		return retValue;
 	}
 
-	private int getNamedParamIndex(String paramName, boolean forOut)
+	protected int getNamedParamIndex(String paramName, boolean forOut)
 	throws SQLException {
 		if (this.connection.getNoAccessToProcedureBodies()) {
 			throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies",
@@ -1308,15 +1417,15 @@
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 		}
 
-		CallableStatementParam namedParamInfo = this.paramInfo
-		.getParameter(paramName);
-
 		if (this.paramInfo == null) {
 			throw SQLError.createSQLException(
 					Messages.getString("CallableStatement.3") + paramName + Messages.getString("CallableStatement.4"), //$NON-NLS-1$ //$NON-NLS-2$
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 		}
 
+		CallableStatementParam namedParamInfo = this.paramInfo
+			.getParameter(paramName);
+
 		if (forOut && !namedParamInfo.isOut) {
 			throw SQLError.createSQLException(
 					Messages.getString("CallableStatement.5") + paramName //$NON-NLS-1$
@@ -1346,7 +1455,7 @@
 			throws SQLException {
 		CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex);
 
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Object retVal = rs.getObjectStoredProc(
 				mapOutputParameterIndexToRsIndex(parameterIndex),
@@ -1362,7 +1471,7 @@
 	 */
 	public synchronized Object getObject(int parameterIndex, Map map)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Object retVal = rs.getObject(
 				mapOutputParameterIndexToRsIndex(parameterIndex), map);
@@ -1377,7 +1486,7 @@
 	 */
 	public synchronized Object getObject(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Object retValue = rs.getObject(fixParameterName(parameterName));
@@ -1393,7 +1502,7 @@
 	 */
 	public synchronized Object getObject(String parameterName, Map map)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Object retValue = rs.getObject(fixParameterName(parameterName), map);
@@ -1413,7 +1522,7 @@
 	 *             if no output parameters were defined, or if no output
 	 *             parameters were returned.
 	 */
-	private ResultSet getOutputParameters(int paramIndex) throws SQLException {
+	protected ResultSetInternalMethods getOutputParameters(int paramIndex) throws SQLException {
 		this.outputParamWasNull = false;
 
 		if (paramIndex == 1 && this.callingStoredFunction
@@ -1438,17 +1547,17 @@
 	public synchronized ParameterMetaData getParameterMetaData()
 			throws SQLException {
 		if (this.placeholderToParameterIndexMap == null) {
-			return (CallableStatementParamInfoJDBC3) this.paramInfo;
+		return (CallableStatementParamInfoJDBC3) this.paramInfo;
 		} else {
 			return new CallableStatementParamInfoJDBC3(this.paramInfo);
-		}
 	}
+	}
 
 	/**
 	 * @see java.sql.CallableStatement#getRef(int)
 	 */
 	public synchronized Ref getRef(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Ref retValue = rs
 				.getRef(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1462,7 +1571,7 @@
 	 * @see java.sql.CallableStatement#getRef(java.lang.String)
 	 */
 	public synchronized Ref getRef(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Ref retValue = rs.getRef(fixParameterName(parameterName));
@@ -1476,7 +1585,7 @@
 	 * @see java.sql.CallableStatement#getShort(int)
 	 */
 	public synchronized short getShort(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		short retValue = rs
 				.getShort(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1491,7 +1600,7 @@
 	 */
 	public synchronized short getShort(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		short retValue = rs.getShort(fixParameterName(parameterName));
@@ -1506,7 +1615,7 @@
 	 */
 	public synchronized String getString(int parameterIndex)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		String retValue = rs
 				.getString(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1521,7 +1630,7 @@
 	 */
 	public synchronized String getString(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		String retValue = rs.getString(fixParameterName(parameterName));
@@ -1535,7 +1644,7 @@
 	 * @see java.sql.CallableStatement#getTime(int)
 	 */
 	public synchronized Time getTime(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Time retValue = rs
 				.getTime(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1550,7 +1659,7 @@
 	 */
 	public synchronized Time getTime(int parameterIndex, Calendar cal)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Time retValue = rs.getTime(
 				mapOutputParameterIndexToRsIndex(parameterIndex), cal);
@@ -1564,7 +1673,7 @@
 	 * @see java.sql.CallableStatement#getTime(java.lang.String)
 	 */
 	public synchronized Time getTime(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Time retValue = rs.getTime(fixParameterName(parameterName));
@@ -1580,7 +1689,7 @@
 	 */
 	public synchronized Time getTime(String parameterName, Calendar cal)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Time retValue = rs.getTime(fixParameterName(parameterName), cal);
@@ -1595,7 +1704,7 @@
 	 */
 	public synchronized Timestamp getTimestamp(int parameterIndex)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Timestamp retValue = rs
 				.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1610,7 +1719,7 @@
 	 */
 	public synchronized Timestamp getTimestamp(int parameterIndex, Calendar cal)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		Timestamp retValue = rs.getTimestamp(
 				mapOutputParameterIndexToRsIndex(parameterIndex), cal);
@@ -1625,7 +1734,7 @@
 	 */
 	public synchronized Timestamp getTimestamp(String parameterName)
 			throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName));
@@ -1641,7 +1750,7 @@
 	 */
 	public synchronized Timestamp getTimestamp(String parameterName,
 			Calendar cal) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName),
@@ -1656,7 +1765,7 @@
 	 * @see java.sql.CallableStatement#getURL(int)
 	 */
 	public synchronized URL getURL(int parameterIndex) throws SQLException {
-		ResultSet rs = getOutputParameters(parameterIndex);
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
 
 		URL retValue = rs
 				.getURL(mapOutputParameterIndexToRsIndex(parameterIndex));
@@ -1670,7 +1779,7 @@
 	 * @see java.sql.CallableStatement#getURL(java.lang.String)
 	 */
 	public synchronized URL getURL(String parameterName) throws SQLException {
-		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely not going to be
 		// from ?=
 
 		URL retValue = rs.getURL(fixParameterName(parameterName));
@@ -1680,7 +1789,7 @@
 		return retValue;
 	}
 
-	private int mapOutputParameterIndexToRsIndex(int paramIndex)
+	protected int mapOutputParameterIndexToRsIndex(int paramIndex)
 			throws SQLException {
 
 		if (this.returnValueParam != null && paramIndex == 1) {
@@ -1820,7 +1929,7 @@
 					outParameterStmt = this.connection.createStatement();
 					outParamRs = outParameterStmt
 							.executeQuery(outParameterQuery.toString());
-					this.outputParameterResults = ((com.mysql.jdbc.ResultSet) outParamRs)
+					this.outputParameterResults = ((com.mysql.jdbc.ResultSetInternalMethods) outParamRs)
 							.copy();
 
 					if (!this.outputParameterResults.next()) {
@@ -1952,11 +2061,11 @@
 					PreparedStatement setPstmt = null;
 
 					try {
-						setPstmt = this.connection
+						setPstmt = (PreparedStatement) this.connection
 								.clientPrepareStatement(queryBuf.toString());
 
-						byte[] parameterAsBytes = this
-								.getBytesRepresentation(inParamInfo.index);
+						byte[] parameterAsBytes = getBytesRepresentation(
+								inParamInfo.index);
 
 						if (parameterAsBytes != null) {
 							if (parameterAsBytes.length > 8
@@ -1971,7 +2080,22 @@
 								setPstmt.setBytesNoEscapeNoQuotes(1,
 										parameterAsBytes);
 							} else {
-								setPstmt.setBytes(1, parameterAsBytes);
+								int sqlType = inParamInfo.desiredJdbcType;
+								
+								switch (sqlType) {
+								case Types.BIT:
+								case Types.BINARY: 
+								case Types.BLOB: 
+								case Types.JAVA_OBJECT:
+								case Types.LONGVARBINARY: 
+								case Types.VARBINARY:
+									setPstmt.setBytes(1, parameterAsBytes);
+									break;
+								default:
+									// the inherited PreparedStatement methods
+									// have already escaped and quoted these parameters
+									setPstmt.setBytesNoEscape(1, parameterAsBytes);
+								}
 							}
 						} else {
 							setPstmt.setNull(1, Types.NULL);
@@ -2153,4 +2277,75 @@
 		
 		return super.getParameterIndexOffset();
 	}
+	
+	public void setAsciiStream(String parameterName, InputStream x) throws SQLException {
+		setAsciiStream(getNamedParamIndex(parameterName, false), x);
+		
+	}
+
+	public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException {
+		setAsciiStream(getNamedParamIndex(parameterName, false), x, length);
+		
+	}
+
+	public void setBinaryStream(String parameterName, InputStream x) throws SQLException {
+		setBinaryStream(getNamedParamIndex(parameterName, false), x);
+		
+	}
+
+	public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException {
+		setBinaryStream(getNamedParamIndex(parameterName, false), x, length);
+		
+	}
+
+	public void setBlob(String parameterName, Blob x) throws SQLException {
+		setBlob(getNamedParamIndex(parameterName, false), x);
+		
+	}
+
+	public void setBlob(String parameterName, InputStream inputStream) throws SQLException {
+		setBlob(getNamedParamIndex(parameterName, false), inputStream);
+		
+	}
+
+	public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException {
+		setBlob(getNamedParamIndex(parameterName, false), inputStream, length);
+		
+	}
+
+	public void setCharacterStream(String parameterName, Reader reader) throws SQLException {
+		setCharacterStream(getNamedParamIndex(parameterName, false), reader);
+		
+	}
+
+	public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException {
+		setCharacterStream(getNamedParamIndex(parameterName, false), reader, length);
+		
+	}
+
+	public void setClob(String parameterName, Clob x) throws SQLException {
+		setClob(getNamedParamIndex(parameterName, false), x);
+		
+	}
+
+	public void setClob(String parameterName, Reader reader) throws SQLException {
+		setClob(getNamedParamIndex(parameterName, false), reader);
+		
+	}
+
+	public void setClob(String parameterName, Reader reader, long length) throws SQLException {
+		setClob(getNamedParamIndex(parameterName, false), reader, length);
+		
+	}
+
+	public void setNCharacterStream(String parameterName, Reader value) throws SQLException {
+		setNCharacterStream(getNamedParamIndex(parameterName, false), value);
+		
+	}
+
+	public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException {
+		setNCharacterStream(getNamedParamIndex(parameterName, false), value, length);
+		
+	}
+
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/CharsetMapping.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/CharsetMapping.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/CharsetMapping.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2006 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -34,7 +34,7 @@
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
-import java.util.SortedSet;
+import java.util.TreeMap;
 
 /**
  * Mapping between MySQL charset names and Java charset names. I've investigated
@@ -51,7 +51,12 @@
 	 * Map of MySQL-4.1 charset indexes to Java encoding names
 	 */
 	public static final String[] INDEX_TO_CHARSET;
-
+	
+	/**
+	 * Map of MySQL-4.1 collation index to collation names
+	 */
+	public static final String[] INDEX_TO_COLLATION;
+	
 	/** Mapping of Java charset names to MySQL charset names */
 	private static final Map JAVA_TO_MYSQL_CHARSET_MAP;
 
@@ -63,9 +68,56 @@
 	private static final Map MULTIBYTE_CHARSETS;
 
 	private static final Map MYSQL_TO_JAVA_CHARSET_MAP;
+	
+	private static final Map MYSQL_ENCODING_NAME_TO_CHARSET_INDEX_MAP;
+	
+	private static final String NOT_USED = "ISO8859_1"; // punting for not-used character sets
 
+	public static final Map STATIC_CHARSET_TO_NUM_BYTES_MAP;
+
 	static {	
+		HashMap tempNumBytesMap = new HashMap();
 		
+		tempNumBytesMap.put("big5", Constants.integerValueOf(2));
+		tempNumBytesMap.put("dec8" , Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp850", Constants.integerValueOf(1));
+		tempNumBytesMap.put("hp8", Constants.integerValueOf(1));
+		tempNumBytesMap.put("koi8r", Constants.integerValueOf(1));
+		tempNumBytesMap.put("latin1", Constants.integerValueOf(1));
+		tempNumBytesMap.put("latin2", Constants.integerValueOf(1));
+		tempNumBytesMap.put("swe7", Constants.integerValueOf(1));
+		tempNumBytesMap.put("ascii", Constants.integerValueOf(1));
+		tempNumBytesMap.put("ujis", Constants.integerValueOf(3));
+		tempNumBytesMap.put("sjis", Constants.integerValueOf(2));
+		tempNumBytesMap.put("hebrew", Constants.integerValueOf(1));
+		tempNumBytesMap.put("tis620", Constants.integerValueOf(1));
+		tempNumBytesMap.put("euckr", Constants.integerValueOf(2));
+		tempNumBytesMap.put("koi8u", Constants.integerValueOf(1));
+		tempNumBytesMap.put("gb2312", Constants.integerValueOf(2));
+		tempNumBytesMap.put("greek", Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp1250", Constants.integerValueOf(1));
+		tempNumBytesMap.put("gbk", Constants.integerValueOf(2));
+		tempNumBytesMap.put("latin5", Constants.integerValueOf(1));
+		tempNumBytesMap.put("armscii8", Constants.integerValueOf(1));
+		tempNumBytesMap.put("utf8", Constants.integerValueOf(3));
+		tempNumBytesMap.put("ucs2", Constants.integerValueOf(2));
+		tempNumBytesMap.put("cp866", Constants.integerValueOf(1));
+		tempNumBytesMap.put("keybcs2", Constants.integerValueOf(1));
+		tempNumBytesMap.put("macce", Constants.integerValueOf(1));
+		tempNumBytesMap.put("macroman", Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp852" , Constants.integerValueOf(1));
+		tempNumBytesMap.put("latin7", Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp1251", Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp1256" , Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp1257", Constants.integerValueOf(1));
+		tempNumBytesMap.put("binary", Constants.integerValueOf(1));
+		tempNumBytesMap.put("geostd8", Constants.integerValueOf(1));
+		tempNumBytesMap.put("cp932", Constants.integerValueOf(2));
+		tempNumBytesMap.put("eucjpms", Constants.integerValueOf(3));
+		
+		STATIC_CHARSET_TO_NUM_BYTES_MAP = Collections.unmodifiableMap(
+				tempNumBytesMap);
+
 		CHARSET_CONFIG.setProperty("javaToMysqlMappings",
 			//
 			// Note: This used to be stored in Charsets.properties,
@@ -77,7 +129,7 @@
 			//                           denotes preferred value)      
 			//
 			"US-ASCII =			usa7,"
-	 		+ "US-ASCII =			ascii,"
+	 		+ "US-ASCII =			>4.1.0 ascii,"
 	 		+ "Big5 = 				big5,"
 	 		+ "GBK = 				gbk,"
 	 		+ "SJIS = 				sjis,"
@@ -103,8 +155,8 @@
 			+ "ISO8859_13 =		estonia,"
 			+ "Cp437 =             *>4.1.0 cp850,"
 	 		+ "Cp437 =				dos,"
-	 		+ "Cp850 =				Cp850,"
-			+ "Cp852 = 			Cp852,"
+	 		+ "Cp850 =				cp850,"
+			+ "Cp852 = 			cp852,"
 	 		+ "Cp866 = 			cp866,"
 	 		+ "KOI8_R = 			koi8_ru,"
 			+ "KOI8_R = 			>4.1.0 koi8r,"
@@ -117,7 +169,7 @@
 			+ "Cp1251 = 			cp1251csas,"
 			+ "Cp1256 = 			cp1256,"
 	 		+ "Cp1251 = 			win1251ukr,"
-	 		+ "Cp1252 =             latin1,"
+	 		+ "Cp1252 =             latin1,"
 			+ "Cp1257 = 			cp1257,"
 			+ "MacRoman = 			macroman,"
 			+ "MacCentralEurope = 	macce,"
@@ -139,7 +191,9 @@
 	        + "GREEK =			greek,"
 	        + "EUCKR =			euckr,"
 	        + "GB2312 =		gb2312,"
-	        + "LATIN2 =		latin2");
+	        + "LATIN2 =		latin2,"
+	        + "UTF-16 = 	>5.2.0 utf16,"
+	        + "UTF-32 = 	>5.2.0 utf32");
 
 		HashMap javaToMysqlMap = new HashMap();
 
@@ -179,7 +233,7 @@
 
 		MYSQL_TO_JAVA_CHARSET_MAP = Collections.unmodifiableMap(mysqlToJavaMap);
 
-		HashMap ucMap = new HashMap(JAVA_TO_MYSQL_CHARSET_MAP.size());
+		TreeMap ucMap = new TreeMap(String.CASE_INSENSITIVE_ORDER);
 
 		Iterator javaNamesKeys = JAVA_TO_MYSQL_CHARSET_MAP.keySet().iterator();
 
@@ -235,23 +289,23 @@
 
 		MULTIBYTE_CHARSETS = Collections.unmodifiableMap(tempMapMulti);
 
-		INDEX_TO_CHARSET = new String[211];
+		INDEX_TO_CHARSET = new String[255];
 
 		try {
 			INDEX_TO_CHARSET[1] = getJavaEncodingForMysqlEncoding("big5", null);
 			INDEX_TO_CHARSET[2] = getJavaEncodingForMysqlEncoding("czech", null);
-			INDEX_TO_CHARSET[3] = getJavaEncodingForMysqlEncoding("dec8", null);
-			INDEX_TO_CHARSET[4] = getJavaEncodingForMysqlEncoding("dos", null);
+			INDEX_TO_CHARSET[3] = "ISO8859_1"; // punting for "dec8"
+			INDEX_TO_CHARSET[4] = "ISO8859_1"; // punting for "dos"
 			INDEX_TO_CHARSET[5] = getJavaEncodingForMysqlEncoding("german1",
 					null);
-			INDEX_TO_CHARSET[6] = getJavaEncodingForMysqlEncoding("hp8", null);
+			INDEX_TO_CHARSET[6] = "ISO8859_1"; // punting for "hp8"
 			INDEX_TO_CHARSET[7] = getJavaEncodingForMysqlEncoding("koi8_ru",
 					null);
 			INDEX_TO_CHARSET[8] = getJavaEncodingForMysqlEncoding("latin1",
 					null);
 			INDEX_TO_CHARSET[9] = getJavaEncodingForMysqlEncoding("latin2",
 					null);
-			INDEX_TO_CHARSET[10] = getJavaEncodingForMysqlEncoding("swe7", null);
+			INDEX_TO_CHARSET[10] = "ISO8859_1"; // punting for "swe7"
 			INDEX_TO_CHARSET[11] = getJavaEncodingForMysqlEncoding("usa7", null);
 			INDEX_TO_CHARSET[12] = getJavaEncodingForMysqlEncoding("ujis", null);
 			INDEX_TO_CHARSET[13] = getJavaEncodingForMysqlEncoding("sjis", null);
@@ -261,6 +315,9 @@
 					null);
 			INDEX_TO_CHARSET[16] = getJavaEncodingForMysqlEncoding("hebrew",
 					null);
+			
+			INDEX_TO_CHARSET[17] = NOT_USED; // not used in the server 
+			
 			INDEX_TO_CHARSET[18] = getJavaEncodingForMysqlEncoding("tis620",
 					null);
 			INDEX_TO_CHARSET[19] = getJavaEncodingForMysqlEncoding("euc_kr",
@@ -269,8 +326,7 @@
 					null);
 			INDEX_TO_CHARSET[21] = getJavaEncodingForMysqlEncoding("hungarian",
 					null);
-			INDEX_TO_CHARSET[22] = getJavaEncodingForMysqlEncoding("koi8_ukr",
-					null);
+			INDEX_TO_CHARSET[22] = "KOI8_R"; //punting for "koi8_ukr"
 			INDEX_TO_CHARSET[23] = getJavaEncodingForMysqlEncoding(
 					"win1251ukr", null);
 			INDEX_TO_CHARSET[24] = getJavaEncodingForMysqlEncoding("gb2312",
@@ -288,60 +344,62 @@
 					null);
 			INDEX_TO_CHARSET[31] = getJavaEncodingForMysqlEncoding("latin1_de",
 					null);
-			INDEX_TO_CHARSET[32] = getJavaEncodingForMysqlEncoding("armscii8",
-					null);
+			INDEX_TO_CHARSET[32] = "ISO8859_1"; // punting "armscii8"
 			INDEX_TO_CHARSET[33] = getJavaEncodingForMysqlEncoding("utf8", null);
-			INDEX_TO_CHARSET[34] = getJavaEncodingForMysqlEncoding("win1250ch",
-					null);
+			INDEX_TO_CHARSET[34] = "Cp1250"; // punting "win1250ch"
 			INDEX_TO_CHARSET[35] = getJavaEncodingForMysqlEncoding("ucs2", null);
 			INDEX_TO_CHARSET[36] = getJavaEncodingForMysqlEncoding("cp866",
 					null);
-			INDEX_TO_CHARSET[37] = getJavaEncodingForMysqlEncoding("keybcs2",
-					null);
+			INDEX_TO_CHARSET[37] = "Cp895"; // punting "keybcs2"
 			INDEX_TO_CHARSET[38] = getJavaEncodingForMysqlEncoding("macce",
 					null);
 			INDEX_TO_CHARSET[39] = getJavaEncodingForMysqlEncoding("macroman",
 					null);
-			INDEX_TO_CHARSET[40] = getJavaEncodingForMysqlEncoding("pclatin2",
-					null);
+			INDEX_TO_CHARSET[40] = "latin2"; // punting "pclatin2"
 			INDEX_TO_CHARSET[41] = getJavaEncodingForMysqlEncoding("latvian",
 					null);
 			INDEX_TO_CHARSET[42] = getJavaEncodingForMysqlEncoding("latvian1",
 					null);
-			INDEX_TO_CHARSET[43] = getJavaEncodingForMysqlEncoding("maccebin",
+			INDEX_TO_CHARSET[43] = getJavaEncodingForMysqlEncoding("macce",
 					null);
-			INDEX_TO_CHARSET[44] = getJavaEncodingForMysqlEncoding("macceciai",
+			INDEX_TO_CHARSET[44] = getJavaEncodingForMysqlEncoding("macce",
 					null);
-			INDEX_TO_CHARSET[45] = getJavaEncodingForMysqlEncoding("maccecias",
+			INDEX_TO_CHARSET[45] = getJavaEncodingForMysqlEncoding("macce",
 					null);
-			INDEX_TO_CHARSET[46] = getJavaEncodingForMysqlEncoding("maccecsas",
+			INDEX_TO_CHARSET[46] = getJavaEncodingForMysqlEncoding("macce",
 					null);
-			INDEX_TO_CHARSET[47] = getJavaEncodingForMysqlEncoding("latin1bin",
+			INDEX_TO_CHARSET[47] = getJavaEncodingForMysqlEncoding("latin1",
 					null);
 			INDEX_TO_CHARSET[48] = getJavaEncodingForMysqlEncoding(
-					"latin1cias", null);
+					"latin1", null);
 			INDEX_TO_CHARSET[49] = getJavaEncodingForMysqlEncoding(
-					"latin1csas", null);
-			INDEX_TO_CHARSET[50] = getJavaEncodingForMysqlEncoding("cp1251bin",
+					"latin1", null);
+			INDEX_TO_CHARSET[50] = getJavaEncodingForMysqlEncoding("cp1251",
 					null);
 			INDEX_TO_CHARSET[51] = getJavaEncodingForMysqlEncoding(
-					"cp1251cias", null);
+					"cp1251", null);
 			INDEX_TO_CHARSET[52] = getJavaEncodingForMysqlEncoding(
-					"cp1251csas", null);
+					"cp1251", null);
 			INDEX_TO_CHARSET[53] = getJavaEncodingForMysqlEncoding(
-					"macromanbin", null);
+					"macroman", null);
 			INDEX_TO_CHARSET[54] = getJavaEncodingForMysqlEncoding(
-					"macromancias", null);
+					"macroman", null);
 			INDEX_TO_CHARSET[55] = getJavaEncodingForMysqlEncoding(
-					"macromanciai", null);
+					"macroman", null);
 			INDEX_TO_CHARSET[56] = getJavaEncodingForMysqlEncoding(
-					"macromancsas", null);
+					"macroman", null);
 			INDEX_TO_CHARSET[57] = getJavaEncodingForMysqlEncoding("cp1256",
 					null);
+			
+			INDEX_TO_CHARSET[58] = NOT_USED; // not used
+			INDEX_TO_CHARSET[59] = NOT_USED; // not used
+			INDEX_TO_CHARSET[60] = NOT_USED; // not used
+			INDEX_TO_CHARSET[61] = NOT_USED; // not used
+			INDEX_TO_CHARSET[62] = NOT_USED; // not used 
+			
 			INDEX_TO_CHARSET[63] = getJavaEncodingForMysqlEncoding("binary",
 					null);
-			INDEX_TO_CHARSET[64] = getJavaEncodingForMysqlEncoding("armscii",
-					null);
+			INDEX_TO_CHARSET[64] = "ISO8859_2"; // punting "armscii"
 			INDEX_TO_CHARSET[65] = getJavaEncodingForMysqlEncoding("ascii",
 					null);
 			INDEX_TO_CHARSET[66] = getJavaEncodingForMysqlEncoding("cp1250",
@@ -350,18 +408,19 @@
 					null);
 			INDEX_TO_CHARSET[68] = getJavaEncodingForMysqlEncoding("cp866",
 					null);
-			INDEX_TO_CHARSET[69] = getJavaEncodingForMysqlEncoding("dec8", null);
+			INDEX_TO_CHARSET[69] = "US-ASCII"; // punting for "dec8"
 			INDEX_TO_CHARSET[70] = getJavaEncodingForMysqlEncoding("greek",
 					null);
 			INDEX_TO_CHARSET[71] = getJavaEncodingForMysqlEncoding("hebrew",
 					null);
-			INDEX_TO_CHARSET[72] = getJavaEncodingForMysqlEncoding("hp8", null);
-			INDEX_TO_CHARSET[73] = getJavaEncodingForMysqlEncoding("keybcs2",
-					null);
+			INDEX_TO_CHARSET[72] = "US-ASCII"; // punting for "hp8"
+			INDEX_TO_CHARSET[73] = "Cp895"; // punting for "keybcs2"
 			INDEX_TO_CHARSET[74] = getJavaEncodingForMysqlEncoding("koi8r",
 					null);
-			INDEX_TO_CHARSET[75] = getJavaEncodingForMysqlEncoding("koi8ukr",
-					null);
+			INDEX_TO_CHARSET[75] = "KOI8_r"; // punting for koi8ukr"
+			
+			INDEX_TO_CHARSET[76] = NOT_USED; // not used
+			
 			INDEX_TO_CHARSET[77] = getJavaEncodingForMysqlEncoding("latin2",
 					null);
 			INDEX_TO_CHARSET[78] = getJavaEncodingForMysqlEncoding("latin5",
@@ -372,7 +431,7 @@
 					null);
 			INDEX_TO_CHARSET[81] = getJavaEncodingForMysqlEncoding("cp852",
 					null);
-			INDEX_TO_CHARSET[82] = getJavaEncodingForMysqlEncoding("swe7", null);
+			INDEX_TO_CHARSET[82] = "ISO8859_1"; // punting for "swe7"
 			INDEX_TO_CHARSET[83] = getJavaEncodingForMysqlEncoding("utf8", null);
 			INDEX_TO_CHARSET[84] = getJavaEncodingForMysqlEncoding("big5", null);
 			INDEX_TO_CHARSET[85] = getJavaEncodingForMysqlEncoding("euckr",
@@ -385,10 +444,8 @@
 					null);
 			INDEX_TO_CHARSET[90] = getJavaEncodingForMysqlEncoding("ucs2", null);
 			INDEX_TO_CHARSET[91] = getJavaEncodingForMysqlEncoding("ujis", null);
-			INDEX_TO_CHARSET[92] = getJavaEncodingForMysqlEncoding("geostd8",
-					null);
-			INDEX_TO_CHARSET[93] = getJavaEncodingForMysqlEncoding("geostd8",
-					null);
+			INDEX_TO_CHARSET[92] = "US-ASCII"; //punting for "geostd8"
+			INDEX_TO_CHARSET[93] = "US-ASCII"; // punting for "geostd8"
 			INDEX_TO_CHARSET[94] = getJavaEncodingForMysqlEncoding("latin1",
 					null);
 			INDEX_TO_CHARSET[95] = getJavaEncodingForMysqlEncoding("cp932",
@@ -400,6 +457,10 @@
 			INDEX_TO_CHARSET[98] = getJavaEncodingForMysqlEncoding("eucjpms",
 					null);
 			
+			for (int i = 99; i < 128; i++) {
+				INDEX_TO_CHARSET[i] = NOT_USED; // not used
+			}
+			
 			INDEX_TO_CHARSET[128] = getJavaEncodingForMysqlEncoding("ucs2",
 					null);
 			INDEX_TO_CHARSET[129] = getJavaEncodingForMysqlEncoding("ucs2",
@@ -439,6 +500,10 @@
 			INDEX_TO_CHARSET[146] = getJavaEncodingForMysqlEncoding("ucs2",
 					null);
 
+			for (int i = 147; i < 192; i++) {
+				INDEX_TO_CHARSET[i] = NOT_USED; // not used
+			}
+			
 			INDEX_TO_CHARSET[192] = getJavaEncodingForMysqlEncoding("utf8",
 					null);
 			INDEX_TO_CHARSET[193] = getJavaEncodingForMysqlEncoding("utf8",
@@ -477,11 +542,284 @@
 					null);
 			INDEX_TO_CHARSET[210] = getJavaEncodingForMysqlEncoding("utf8",
 					null);
-
+			// 5.2+ charsets
+			INDEX_TO_CHARSET[211] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			
+			for (int i = 212; i < 224; i++) {
+				INDEX_TO_CHARSET[i] = NOT_USED;
+			}
+			
+			for (int i = 224; i <= 243; i++) {
+				INDEX_TO_CHARSET[i] = getJavaEncodingForMysqlEncoding("utf8",
+						null);
+			}
+			
+			for (int i = 101; i<= 120; i++) {
+				INDEX_TO_CHARSET[i] = getJavaEncodingForMysqlEncoding("utf16",
+						null);
+			}
+			
+			for (int i = 160; i<= 179; i++) {
+				INDEX_TO_CHARSET[i] = getJavaEncodingForMysqlEncoding("utf32",
+						null);
+			}
+			
+			for (int i = 244; i < 254; i++) {
+				INDEX_TO_CHARSET[i] = NOT_USED;
+			}
+			
+			INDEX_TO_CHARSET[254] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			
+			// Sanity check
+			
+			for (int i = 1; i < INDEX_TO_CHARSET.length; i++) {
+				if (INDEX_TO_CHARSET[i] == null) {
+					throw new RuntimeException("Assertion failure: No mapping from charset index " + i + " to a Java character set");
+				}
+			}
 		} catch (SQLException sqlEx) {
 			// ignore, it won't happen in this case
 		}
 		
+		INDEX_TO_COLLATION = new String[255];
+		
+		INDEX_TO_COLLATION[1] = "big5_chinese_ci";
+		INDEX_TO_COLLATION[2] = "latin2_czech_cs";
+		INDEX_TO_COLLATION[3] = "dec8_swedish_ci";
+		INDEX_TO_COLLATION[4] = "cp850_general_ci";
+		INDEX_TO_COLLATION[5] = "latin1_german1_ci";
+		INDEX_TO_COLLATION[6] = "hp8_english_ci";
+		INDEX_TO_COLLATION[7] = "koi8r_general_ci";
+		INDEX_TO_COLLATION[8] = "latin1_swedish_ci";
+		INDEX_TO_COLLATION[9] = "latin2_general_ci";
+		INDEX_TO_COLLATION[10] = "swe7_swedish_ci";
+		INDEX_TO_COLLATION[11] = "ascii_general_ci";
+		INDEX_TO_COLLATION[12] = "ujis_japanese_ci";
+		INDEX_TO_COLLATION[13] = "sjis_japanese_ci";
+		INDEX_TO_COLLATION[14] = "cp1251_bulgarian_ci";
+		INDEX_TO_COLLATION[15] = "latin1_danish_ci";
+		INDEX_TO_COLLATION[16] = "hebrew_general_ci";
+		INDEX_TO_COLLATION[18] = "tis620_thai_ci";
+		INDEX_TO_COLLATION[19] = "euckr_korean_ci";
+		INDEX_TO_COLLATION[20] = "latin7_estonian_cs";
+		INDEX_TO_COLLATION[21] = "latin2_hungarian_ci";
+		INDEX_TO_COLLATION[22] = "koi8u_general_ci";
+		INDEX_TO_COLLATION[23] = "cp1251_ukrainian_ci";
+		INDEX_TO_COLLATION[24] = "gb2312_chinese_ci";
+		INDEX_TO_COLLATION[25] = "greek_general_ci";
+		INDEX_TO_COLLATION[26] = "cp1250_general_ci";
+		INDEX_TO_COLLATION[27] = "latin2_croatian_ci";
+		INDEX_TO_COLLATION[28] = "gbk_chinese_ci";
+		INDEX_TO_COLLATION[29] = "cp1257_lithuanian_ci";
+		INDEX_TO_COLLATION[30] = "latin5_turkish_ci";
+		INDEX_TO_COLLATION[31] = "latin1_german2_ci";
+		INDEX_TO_COLLATION[32] = "armscii8_general_ci";
+		INDEX_TO_COLLATION[33] = "utf8_general_ci";
+		INDEX_TO_COLLATION[34] = "cp1250_czech_cs";
+		INDEX_TO_COLLATION[35] = "ucs2_general_ci";
+		INDEX_TO_COLLATION[36] = "cp866_general_ci";
+		INDEX_TO_COLLATION[37] = "keybcs2_general_ci";
+		INDEX_TO_COLLATION[38] = "macce_general_ci";
+		INDEX_TO_COLLATION[39] = "macroman_general_ci";
+		INDEX_TO_COLLATION[40] = "cp852_general_ci";
+		INDEX_TO_COLLATION[41] = "latin7_general_ci";
+		INDEX_TO_COLLATION[42] = "latin7_general_cs";
+		INDEX_TO_COLLATION[43] = "macce_bin";
+		INDEX_TO_COLLATION[44] = "cp1250_croatian_ci";
+		INDEX_TO_COLLATION[47] = "latin1_bin";
+		INDEX_TO_COLLATION[48] = "latin1_general_ci";
+		INDEX_TO_COLLATION[49] = "latin1_general_cs";
+		INDEX_TO_COLLATION[50] = "cp1251_bin";
+		INDEX_TO_COLLATION[51] = "cp1251_general_ci";
+		INDEX_TO_COLLATION[52] = "cp1251_general_cs";
+		INDEX_TO_COLLATION[53] = "macroman_bin";
+		INDEX_TO_COLLATION[57] = "cp1256_general_ci";
+		INDEX_TO_COLLATION[58] = "cp1257_bin";
+		INDEX_TO_COLLATION[59] = "cp1257_general_ci";
+		INDEX_TO_COLLATION[63] = "binary";
+		INDEX_TO_COLLATION[64] = "armscii8_bin";
+		INDEX_TO_COLLATION[65] = "ascii_bin";
+		INDEX_TO_COLLATION[66] = "cp1250_bin";
+		INDEX_TO_COLLATION[67] = "cp1256_bin";
+		INDEX_TO_COLLATION[68] = "cp866_bin";
+		INDEX_TO_COLLATION[69] = "dec8_bin";
+		INDEX_TO_COLLATION[70] = "greek_bin";
+		INDEX_TO_COLLATION[71] = "hebrew_bin";
+		INDEX_TO_COLLATION[72] = "hp8_bin";
+		INDEX_TO_COLLATION[73] = "keybcs2_bin";
+		INDEX_TO_COLLATION[74] = "koi8r_bin";
+		INDEX_TO_COLLATION[75] = "koi8u_bin";
+		INDEX_TO_COLLATION[77] = "latin2_bin";
+		INDEX_TO_COLLATION[78] = "latin5_bin";
+		INDEX_TO_COLLATION[79] = "latin7_bin";
+		INDEX_TO_COLLATION[80] = "cp850_bin";
+		INDEX_TO_COLLATION[81] = "cp852_bin";
+		INDEX_TO_COLLATION[82] = "swe7_bin";
+		INDEX_TO_COLLATION[83] = "utf8_bin";
+		INDEX_TO_COLLATION[84] = "big5_bin";
+		INDEX_TO_COLLATION[85] = "euckr_bin";
+		INDEX_TO_COLLATION[86] = "gb2312_bin";
+		INDEX_TO_COLLATION[87] = "gbk_bin";
+		INDEX_TO_COLLATION[88] = "sjis_bin";
+		INDEX_TO_COLLATION[89] = "tis620_bin";
+		INDEX_TO_COLLATION[90] = "ucs2_bin";
+		INDEX_TO_COLLATION[91] = "ujis_bin";
+		INDEX_TO_COLLATION[92] = "geostd8_general_ci";
+		INDEX_TO_COLLATION[93] = "geostd8_bin";
+		INDEX_TO_COLLATION[94] = "latin1_spanish_ci";
+		INDEX_TO_COLLATION[95] = "cp932_japanese_ci";
+		INDEX_TO_COLLATION[96] = "cp932_bin";
+		INDEX_TO_COLLATION[97] = "eucjpms_japanese_ci";
+		INDEX_TO_COLLATION[98] = "eucjpms_bin";
+		INDEX_TO_COLLATION[99] = "cp1250_polish_ci";
+		INDEX_TO_COLLATION[128] = "ucs2_unicode_ci";
+		INDEX_TO_COLLATION[129] = "ucs2_icelandic_ci";
+		INDEX_TO_COLLATION[130] = "ucs2_latvian_ci";
+		INDEX_TO_COLLATION[131] = "ucs2_romanian_ci";
+		INDEX_TO_COLLATION[132] = "ucs2_slovenian_ci";
+		INDEX_TO_COLLATION[133] = "ucs2_polish_ci";
+		INDEX_TO_COLLATION[134] = "ucs2_estonian_ci";
+		INDEX_TO_COLLATION[135] = "ucs2_spanish_ci";
+		INDEX_TO_COLLATION[136] = "ucs2_swedish_ci";
+		INDEX_TO_COLLATION[137] = "ucs2_turkish_ci";
+		INDEX_TO_COLLATION[138] = "ucs2_czech_ci";
+		INDEX_TO_COLLATION[139] = "ucs2_danish_ci";
+		INDEX_TO_COLLATION[140] = "ucs2_lithuanian_ci ";
+		INDEX_TO_COLLATION[141] = "ucs2_slovak_ci";
+		INDEX_TO_COLLATION[142] = "ucs2_spanish2_ci";
+		INDEX_TO_COLLATION[143] = "ucs2_roman_ci";
+		INDEX_TO_COLLATION[144] = "ucs2_persian_ci";
+		INDEX_TO_COLLATION[145] = "ucs2_esperanto_ci";
+		INDEX_TO_COLLATION[146] = "ucs2_hungarian_ci";
+		INDEX_TO_COLLATION[192] = "utf8_unicode_ci";
+		INDEX_TO_COLLATION[193] = "utf8_icelandic_ci";
+		INDEX_TO_COLLATION[194] = "utf8_latvian_ci";
+		INDEX_TO_COLLATION[195] = "utf8_romanian_ci";
+		INDEX_TO_COLLATION[196] = "utf8_slovenian_ci";
+		INDEX_TO_COLLATION[197] = "utf8_polish_ci";
+		INDEX_TO_COLLATION[198] = "utf8_estonian_ci";
+		INDEX_TO_COLLATION[199] = "utf8_spanish_ci";
+		INDEX_TO_COLLATION[200] = "utf8_swedish_ci";
+		INDEX_TO_COLLATION[201] = "utf8_turkish_ci";
+		INDEX_TO_COLLATION[202] = "utf8_czech_ci";
+		INDEX_TO_COLLATION[203] = "utf8_danish_ci";
+		INDEX_TO_COLLATION[204] = "utf8_lithuanian_ci ";
+		INDEX_TO_COLLATION[205] = "utf8_slovak_ci";
+		INDEX_TO_COLLATION[206] = "utf8_spanish2_ci";
+		INDEX_TO_COLLATION[207] = "utf8_roman_ci";
+		INDEX_TO_COLLATION[208] = "utf8_persian_ci";
+		INDEX_TO_COLLATION[209] = "utf8_esperanto_ci";
+		INDEX_TO_COLLATION[210] = "utf8_hungarian_ci";
+		
+		// TODO: Remove duplicates
+		
+		INDEX_TO_COLLATION[33] ="utf8mb3_general_ci";
+		INDEX_TO_COLLATION[83] ="utf8mb3_bin";
+		INDEX_TO_COLLATION[192] ="utf8mb3_unicode_ci";
+		INDEX_TO_COLLATION[193] ="utf8mb3_icelandic_ci";
+		INDEX_TO_COLLATION[194] ="utf8mb3_latvian_ci";
+		INDEX_TO_COLLATION[195] ="utf8mb3_romanian_ci";
+		INDEX_TO_COLLATION[196] ="utf8mb3_slovenian_ci";
+		INDEX_TO_COLLATION[197] ="utf8mb3_polish_ci";
+		INDEX_TO_COLLATION[198] ="utf8mb3_estonian_ci";
+		INDEX_TO_COLLATION[199] ="utf8mb3_spanish_ci";
+		INDEX_TO_COLLATION[200] ="utf8mb3_swedish_ci";
+		INDEX_TO_COLLATION[201] ="utf8mb3_turkish_ci";
+		INDEX_TO_COLLATION[202] ="utf8mb3_czech_ci";
+		INDEX_TO_COLLATION[203] ="utf8mb3_danish_ci";
+		INDEX_TO_COLLATION[204] ="utf8mb3_lithuanian_ci";
+		INDEX_TO_COLLATION[205] ="utf8mb3_slovak_ci";
+		INDEX_TO_COLLATION[206] ="utf8mb3_spanish2_ci";
+		INDEX_TO_COLLATION[207] ="utf8mb3_roman_ci";
+		INDEX_TO_COLLATION[208] ="utf8mb3_persian_ci";
+		INDEX_TO_COLLATION[209] ="utf8mb3_esperanto_ci";
+		INDEX_TO_COLLATION[210] ="utf8mb3_hungarian_ci";
+		INDEX_TO_COLLATION[211] ="utf8mb3_sinhala_ci";
+		INDEX_TO_COLLATION[254] ="utf8mb3_general_cs";
+		
+		INDEX_TO_COLLATION[45] ="utf8_general_ci";
+		INDEX_TO_COLLATION[46] ="utf8_bin";
+		INDEX_TO_COLLATION[224] ="utf8_unicode_ci";
+		INDEX_TO_COLLATION[225] ="utf8_icelandic_ci";
+		INDEX_TO_COLLATION[226] ="utf8_latvian_ci";
+		INDEX_TO_COLLATION[227] ="utf8_romanian_ci";
+		INDEX_TO_COLLATION[228] ="utf8_slovenian_ci";
+		INDEX_TO_COLLATION[229] ="utf8_polish_ci";
+		INDEX_TO_COLLATION[230] ="utf8_estonian_ci";
+		INDEX_TO_COLLATION[231] ="utf8_spanish_ci";
+		INDEX_TO_COLLATION[232] ="utf8_swedish_ci";
+		INDEX_TO_COLLATION[233] ="utf8_turkish_ci";
+		INDEX_TO_COLLATION[234] ="utf8_czech_ci";
+		INDEX_TO_COLLATION[235] ="utf8_danish_ci";
+		INDEX_TO_COLLATION[236] ="utf8_lithuanian_ci";
+		INDEX_TO_COLLATION[237] ="utf8_slovak_ci";
+		INDEX_TO_COLLATION[238] ="utf8_spanish2_ci";
+		INDEX_TO_COLLATION[239] ="utf8_roman_ci";
+		INDEX_TO_COLLATION[240] ="utf8_persian_ci";
+		INDEX_TO_COLLATION[241] ="utf8_esperanto_ci";
+		INDEX_TO_COLLATION[242] ="utf8_hungarian_ci";
+		INDEX_TO_COLLATION[243] ="utf8_sinhala_ci";
+		
+		INDEX_TO_COLLATION[54] = "utf16_general_ci";
+		INDEX_TO_COLLATION[55] = "utf16_bin";
+		INDEX_TO_COLLATION[101] = "utf16_unicode_ci";
+		INDEX_TO_COLLATION[102] = "utf16_icelandic_ci";
+		INDEX_TO_COLLATION[103] = "utf16_latvian_ci";
+		INDEX_TO_COLLATION[104] = "utf16_romanian_ci";
+		INDEX_TO_COLLATION[105] = "utf16_slovenian_ci";
+		INDEX_TO_COLLATION[106] = "utf16_polish_ci";
+		INDEX_TO_COLLATION[107] = "utf16_estonian_ci";
+		INDEX_TO_COLLATION[108] = "utf16_spanish_ci";
+		INDEX_TO_COLLATION[109] = "utf16_swedish_ci";
+		INDEX_TO_COLLATION[110] = "utf16_turkish_ci";
+		INDEX_TO_COLLATION[111] = "utf16_czech_ci";
+		INDEX_TO_COLLATION[112] = "utf16_danish_ci";
+		INDEX_TO_COLLATION[113] = "utf16_lithuanian_ci";
+		INDEX_TO_COLLATION[114] = "utf16_slovak_ci";
+		INDEX_TO_COLLATION[115] = "utf16_spanish2_ci";
+		INDEX_TO_COLLATION[116] = "utf16_roman_ci";
+		INDEX_TO_COLLATION[117] = "utf16_persian_ci";
+		INDEX_TO_COLLATION[118] = "utf16_esperanto_ci";
+		INDEX_TO_COLLATION[119] = "utf16_hungarian_ci";
+		INDEX_TO_COLLATION[120] = "utf16_sinhala_ci";
+		
+		INDEX_TO_COLLATION[60] = "utf32_general_ci";
+		INDEX_TO_COLLATION[61] = "utf32_bin";
+		INDEX_TO_COLLATION[160] = "utf32_unicode_ci";
+		INDEX_TO_COLLATION[161] = "utf32_icelandic_ci";
+		INDEX_TO_COLLATION[162] = "utf32_latvian_ci";
+		INDEX_TO_COLLATION[163] = "utf32_romanian_ci";
+		INDEX_TO_COLLATION[164] = "utf32_slovenian_ci";
+		INDEX_TO_COLLATION[165] = "utf32_polish_ci";
+		INDEX_TO_COLLATION[166] = "utf32_estonian_ci";
+		INDEX_TO_COLLATION[167] = "utf32_spanish_ci";
+		INDEX_TO_COLLATION[168] = "utf32_swedish_ci";
+		INDEX_TO_COLLATION[169] = "utf32_turkish_ci";
+		INDEX_TO_COLLATION[170] = "utf32_czech_ci";
+		INDEX_TO_COLLATION[171] = "utf32_danish_ci";
+		INDEX_TO_COLLATION[172] = "utf32_lithuanian_ci";
+		INDEX_TO_COLLATION[173] = "utf32_slovak_ci";
+		INDEX_TO_COLLATION[174] = "utf32_spanish2_ci";
+		INDEX_TO_COLLATION[175] = "utf32_roman_ci";
+		INDEX_TO_COLLATION[176] = "utf32_persian_ci";
+		INDEX_TO_COLLATION[177] = "utf32_esperanto_ci";
+		INDEX_TO_COLLATION[178] = "utf32_hungarian_ci";
+		INDEX_TO_COLLATION[179] = "utf32_sinhala_ci";
+		
+		Map indexMap = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+		
+		for (int i = 0; i < INDEX_TO_CHARSET.length; i++) {
+			String mysqlEncodingName = INDEX_TO_CHARSET[i];
+			
+			if (mysqlEncodingName != null) {
+				indexMap.put(INDEX_TO_CHARSET[i], Constants.integerValueOf(i));
+			}
+		}
+		
+		MYSQL_ENCODING_NAME_TO_CHARSET_INDEX_MAP = Collections.unmodifiableMap(indexMap);
+		
 		Map tempMap = new HashMap();
 		
 		tempMap.put("czech", "latin2");
@@ -513,18 +851,18 @@
 			Collections.unmodifiableMap(tempMap);
 	}
 
-	final static String getJavaEncodingForMysqlEncoding(String mysqlEncoding,
+	public final static String getJavaEncodingForMysqlEncoding(String mysqlEncoding,
 			Connection conn) throws SQLException {
-		
-		if (conn != null && conn.versionMeetsMinimum(4, 1, 0) && 
-				"latin1".equalsIgnoreCase(mysqlEncoding)) {
-			return "Cp1252";
-		}
-		
+		
+		if (conn != null && conn.versionMeetsMinimum(4, 1, 0) && 
+				"latin1".equalsIgnoreCase(mysqlEncoding)) {
+			return "Cp1252";
+		}
+		
 		return (String) MYSQL_TO_JAVA_CHARSET_MAP.get(mysqlEncoding);
 	}
 
-	final static String getMysqlEncodingForJavaEncoding(String javaEncodingUC,
+	public final static String getMysqlEncodingForJavaEncoding(String javaEncodingUC,
 			Connection conn) throws SQLException {
 		List mysqlEncodings = (List) CharsetMapping.JAVA_UC_TO_MYSQL_CHARSET_MAP
 				.get(javaEncodingUC);
@@ -588,7 +926,7 @@
 	 * @return the Java encoding name that error messages use
 	 * @throws SQLException if determination of the character encoding fails
 	 */
-	final static String getCharacterEncodingForErrorMessages(Connection conn) throws SQLException {
+	final static String getCharacterEncodingForErrorMessages(ConnectionImpl conn) throws SQLException {
 		String errorMessageFile = conn.getServerVariable("language");
 		
 		if (errorMessageFile == null || errorMessageFile.length() == 0) {
@@ -723,6 +1061,20 @@
 					+ "\"" + configKey + "\" in Charsets.properties resource");
 		}
 	}
+	
+	public static int getCharsetIndexForMysqlEncodingName(String name) {
+		if (name == null) {
+			return 0;
+		}
+		
+		Integer asInt = (Integer)MYSQL_ENCODING_NAME_TO_CHARSET_INDEX_MAP.get(name);
+		
+		if (asInt == null) {
+			return 0;
+		}
+		
+		return asInt.intValue();
+	}
 }
 
 class VersionedStringProperty {
@@ -791,6 +1143,7 @@
 				subminorVersion);
 	}
 
+	
 	public String toString() {
 		return propertyInfo;
 	}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Clob.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Clob.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Clob.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -33,15 +33,21 @@
 
 import java.sql.SQLException;
 
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
 /**
  * Simplistic implementation of java.sql.Clob for MySQL Connector/J
  * 
  * @author Mark Matthews
- * @version $Id: Clob.java 5417 2006-06-20 21:33:56Z mmatthews $
+ * @version $Id: Clob.java 6342 2007-03-09 21:45:01Z mmatthews $
  */
 public class Clob implements java.sql.Clob, OutputStreamWatcher, WriterWatcher {
 	private String charData;
 
+    Clob() {
+        this.charData = "";
+    }
+    
 	Clob(String charDataInit) {
 		this.charData = charDataInit;
 	}
@@ -287,4 +293,12 @@
 
 		this.charData = out.toString();
 	}
+
+	public void free() throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	public Reader getCharacterStream(long pos, long length) throws SQLException {
+		throw new NotYetImplementedException();
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/CommunicationsException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/CommunicationsException.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/CommunicationsException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -40,158 +40,22 @@
  * @version $Id: CommunicationsException.java,v 1.1.2.1 2005/05/13 18:58:37
  *          mmatthews Exp $
  */
-public class CommunicationsException extends SQLException {
+public class CommunicationsException extends SQLException implements StreamingNotifiable {
 
-	private static final long DEFAULT_WAIT_TIMEOUT_SECONDS = 28800;
-
-	private static final int DUE_TO_TIMEOUT_FALSE = 0;
-
-	private static final int DUE_TO_TIMEOUT_MAYBE = 2;
-
-	private static final int DUE_TO_TIMEOUT_TRUE = 1;
-
+	
 	private String exceptionMessage;
 
-	public CommunicationsException(Connection conn, long lastPacketSentTimeMs,
+	private boolean streamingResultSetInPlay = false;
+
+	public CommunicationsException(ConnectionImpl conn, long lastPacketSentTimeMs,
 			Exception underlyingException) {
 
-		long serverTimeoutSeconds = 0;
-		boolean isInteractiveClient = false;
-
-		if (conn != null) {
-			isInteractiveClient = conn.getInteractiveClient();
-
-			String serverTimeoutSecondsStr = null;
-
-			if (isInteractiveClient) {
-				serverTimeoutSecondsStr = conn
-						.getServerVariable("interactive_timeout"); //$NON-NLS-1$
-			} else {
-				serverTimeoutSecondsStr = conn
-						.getServerVariable("wait_timeout"); //$NON-NLS-1$
-			}
-
-			if (serverTimeoutSecondsStr != null) {
-				try {
-					serverTimeoutSeconds = Long
-							.parseLong(serverTimeoutSecondsStr);
-				} catch (NumberFormatException nfe) {
-					serverTimeoutSeconds = 0;
-				}
-			}
-		}
-
-		StringBuffer exceptionMessageBuf = new StringBuffer();
-
-		if (lastPacketSentTimeMs == 0) {
-			lastPacketSentTimeMs = System.currentTimeMillis();
-		}
-
-		long timeSinceLastPacket = (System.currentTimeMillis() - lastPacketSentTimeMs) / 1000;
-
-		int dueToTimeout = DUE_TO_TIMEOUT_FALSE;
-
-		StringBuffer timeoutMessageBuf = null;
-
-		if (serverTimeoutSeconds != 0) {
-			if (timeSinceLastPacket > serverTimeoutSeconds) {
-				dueToTimeout = DUE_TO_TIMEOUT_TRUE;
-
-				timeoutMessageBuf = new StringBuffer();
-
-				timeoutMessageBuf.append(Messages
-						.getString("CommunicationsException.2")); //$NON-NLS-1$
-
-				if (!isInteractiveClient) {
-					timeoutMessageBuf.append(Messages
-							.getString("CommunicationsException.3")); //$NON-NLS-1$
-				} else {
-					timeoutMessageBuf.append(Messages
-							.getString("CommunicationsException.4")); //$NON-NLS-1$
-				}
-
-			}
-		} else if (timeSinceLastPacket > DEFAULT_WAIT_TIMEOUT_SECONDS) {
-			dueToTimeout = DUE_TO_TIMEOUT_MAYBE;
-
-			timeoutMessageBuf = new StringBuffer();
-
-			timeoutMessageBuf.append(Messages
-					.getString("CommunicationsException.5")); //$NON-NLS-1$
-			timeoutMessageBuf.append(Messages
-					.getString("CommunicationsException.6")); //$NON-NLS-1$
-			timeoutMessageBuf.append(Messages
-					.getString("CommunicationsException.7")); //$NON-NLS-1$
-			timeoutMessageBuf.append(Messages
-					.getString("CommunicationsException.8")); //$NON-NLS-1$
-		}
-
-		if (dueToTimeout == DUE_TO_TIMEOUT_TRUE
-				|| dueToTimeout == DUE_TO_TIMEOUT_MAYBE) {
-
-			exceptionMessageBuf.append(Messages
-					.getString("CommunicationsException.9")); //$NON-NLS-1$
-			exceptionMessageBuf.append(timeSinceLastPacket);
-			exceptionMessageBuf.append(Messages
-					.getString("CommunicationsException.10")); //$NON-NLS-1$
-
-			if (timeoutMessageBuf != null) {
-				exceptionMessageBuf.append(timeoutMessageBuf);
-			}
-
-			exceptionMessageBuf.append(Messages
-					.getString("CommunicationsException.11")); //$NON-NLS-1$
-			exceptionMessageBuf.append(Messages
-					.getString("CommunicationsException.12")); //$NON-NLS-1$
-			exceptionMessageBuf.append(Messages
-					.getString("CommunicationsException.13")); //$NON-NLS-1$
-
-		} else {
-			//
-			// Attempt to determine the reason for the underlying exception
-			// (we can only make a best-guess here)
-			//
-
-			if (underlyingException instanceof BindException) {
-				// too many client connections???
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.14")); //$NON-NLS-1$
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.15")); //$NON-NLS-1$
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.16")); //$NON-NLS-1$
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.17")); //$NON-NLS-1$
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.18")); //$NON-NLS-1$
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.19")); //$NON-NLS-1$
-			}
-		}
-
-		if (exceptionMessageBuf.length() == 0) {
-			// We haven't figured out a good reason, so copy it.
-			exceptionMessageBuf.append(Messages
-					.getString("CommunicationsException.20")); //$NON-NLS-1$
-
-			if (underlyingException != null) {
-				exceptionMessageBuf.append(Messages
-						.getString("CommunicationsException.21")); //$NON-NLS-1$
-				exceptionMessageBuf.append(Util
-						.stackTraceToString(underlyingException));
-			}
-			
-			if (conn != null && conn.getMaintainTimeStats() && 
-					!conn.getParanoid()) {
-				exceptionMessageBuf.append("\n\nLast packet sent to the server was ");
-				exceptionMessageBuf.append(System.currentTimeMillis() - lastPacketSentTimeMs);
-				exceptionMessageBuf.append(" ms ago.");
-			}
-		}
-
-		this.exceptionMessage = exceptionMessageBuf.toString();
+		this.exceptionMessage = SQLError.createLinkFailureMessageBasedOnHeuristics(conn,
+				lastPacketSentTimeMs, underlyingException, this.streamingResultSetInPlay);
 	}
 
+	
+
 	/*
 	 * (non-Javadoc)
 	 * 
@@ -210,4 +74,11 @@
 		return SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE;
 	}
 
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.StreamingNotifiable#setWasStreamingResults()
+	 */
+	public void setWasStreamingResults() {
+		this.streamingResultSetInPlay = true;
+	}
+
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Connection.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Connection.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Connection.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -19,1810 +19,30 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+*/
 
-
- */
 package com.mysql.jdbc;
 
-import com.mysql.jdbc.log.Log;
-import com.mysql.jdbc.log.LogFactory;
-import com.mysql.jdbc.log.NullLogger;
-import com.mysql.jdbc.profiler.ProfileEventSink;
-import com.mysql.jdbc.profiler.ProfilerEvent;
-import com.mysql.jdbc.util.LRUCache;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.io.UnsupportedEncodingException;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.Method;
-import java.math.BigDecimal;
-
-import java.net.URL;
-
-import java.sql.Blob;
-import java.sql.Clob;
-import java.sql.Date;
-import java.sql.ParameterMetaData;
-import java.sql.Ref;
 import java.sql.SQLException;
-import java.sql.SQLWarning;
-import java.sql.Savepoint;
-import java.sql.Time;
-import java.sql.Timestamp;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Stack;
-import java.util.StringTokenizer;
 import java.util.TimeZone;
-import java.util.Timer;
-import java.util.TreeMap;
 
+import com.mysql.jdbc.log.Log;
+
 /**
- * A Connection represents a session with a specific database. Within the
- * context of a Connection, SQL statements are executed and results are
- * returned.
- * <P>
- * A Connection's database is able to provide information describing its tables,
- * its supported SQL grammar, its stored procedures, the capabilities of this
- * connection, etc. This information is obtained with the getMetaData method.
- * </p>
+ * This interface contains methods that are considered the "vendor extension"
+ * to the JDBC API for MySQL's implementation of java.sql.Connection.
  * 
- * @author Mark Matthews
- * @version $Id: Connection.java 5907 2006-10-19 15:40:51Z mmatthews $
- * @see java.sql.Connection
+ * For those looking further into the driver implementation, it is not
+ * an API that is used for plugability of implementations inside our driver
+ * (which is why there are still references to ConnectionImpl throughout the
+ * code).
+ * 
+ * @version $Id: $
+ *
  */
-public class Connection extends ConnectionProperties implements
-		java.sql.Connection {
-	/**
-	 * Used as a key for caching callable statements which (may) depend on
-	 * current catalog...In 5.0.x, they don't (currently), but stored procedure
-	 * names soon will, so current catalog is a (hidden) component of the name.
-	 */
-	class CompoundCacheKey {
-		String componentOne;
+public interface Connection extends java.sql.Connection, ConnectionProperties {
 
-		String componentTwo;
-
-		int hashCode;
-
-		CompoundCacheKey(String partOne, String partTwo) {
-			this.componentOne = partOne;
-			this.componentTwo = partTwo;
-
-			// Handle first component (in most cases, currentCatalog)
-			// being NULL....
-			this.hashCode = (((this.componentOne != null) ? this.componentOne
-					: "") + this.componentTwo).hashCode();
-		}
-
-		/*
-		 * (non-Javadoc)
-		 * 
-		 * @see java.lang.Object#equals(java.lang.Object)
-		 */
-		public boolean equals(Object obj) {
-			if (obj instanceof CompoundCacheKey) {
-				CompoundCacheKey another = (CompoundCacheKey) obj;
-
-				boolean firstPartEqual = false;
-
-				if (this.componentOne == null) {
-					firstPartEqual = (another.componentOne == null);
-				} else {
-					firstPartEqual = this.componentOne
-							.equals(another.componentOne);
-				}
-
-				return (firstPartEqual && this.componentTwo
-						.equals(another.componentTwo));
-			}
-
-			return false;
-		}
-
-		/*
-		 * (non-Javadoc)
-		 * 
-		 * @see java.lang.Object#hashCode()
-		 */
-		public int hashCode() {
-			return this.hashCode;
-		}
-	}
-
 	/**
-	 * Wrapper class for UltraDev CallableStatements that are really
-	 * PreparedStatments. Nice going, UltraDev developers.
-	 */
-	class UltraDevWorkAround implements java.sql.CallableStatement {
-		private java.sql.PreparedStatement delegate = null;
-
-		UltraDevWorkAround(java.sql.PreparedStatement pstmt) {
-			this.delegate = pstmt;
-		}
-
-		public void addBatch() throws SQLException {
-			this.delegate.addBatch();
-		}
-
-		public void addBatch(java.lang.String p1) throws SQLException {
-			this.delegate.addBatch(p1);
-		}
-
-		public void cancel() throws SQLException {
-			this.delegate.cancel();
-		}
-
-		public void clearBatch() throws SQLException {
-			this.delegate.clearBatch();
-		}
-
-		public void clearParameters() throws SQLException {
-			this.delegate.clearParameters();
-		}
-
-		public void clearWarnings() throws SQLException {
-			this.delegate.clearWarnings();
-		}
-
-		public void close() throws SQLException {
-			this.delegate.close();
-		}
-
-		public boolean execute() throws SQLException {
-			return this.delegate.execute();
-		}
-
-		public boolean execute(java.lang.String p1) throws SQLException {
-			return this.delegate.execute(p1);
-		}
-
-		/**
-		 * @see Statement#execute(String, int)
-		 */
-		public boolean execute(String arg0, int arg1) throws SQLException {
-			return this.delegate.execute(arg0, arg1);
-		}
-
-		/**
-		 * @see Statement#execute(String, int[])
-		 */
-		public boolean execute(String arg0, int[] arg1) throws SQLException {
-			return this.delegate.execute(arg0, arg1);
-		}
-
-		/**
-		 * @see Statement#execute(String, String[])
-		 */
-		public boolean execute(String arg0, String[] arg1) throws SQLException {
-			return this.delegate.execute(arg0, arg1);
-		}
-
-		public int[] executeBatch() throws SQLException {
-			return this.delegate.executeBatch();
-		}
-
-		public java.sql.ResultSet executeQuery() throws SQLException {
-			return this.delegate.executeQuery();
-		}
-
-		public java.sql.ResultSet executeQuery(java.lang.String p1)
-				throws SQLException {
-			return this.delegate.executeQuery(p1);
-		}
-
-		public int executeUpdate() throws SQLException {
-			return this.delegate.executeUpdate();
-		}
-
-		public int executeUpdate(java.lang.String p1) throws SQLException {
-			return this.delegate.executeUpdate(p1);
-		}
-
-		/**
-		 * @see Statement#executeUpdate(String, int)
-		 */
-		public int executeUpdate(String arg0, int arg1) throws SQLException {
-			return this.delegate.executeUpdate(arg0, arg1);
-		}
-
-		/**
-		 * @see Statement#executeUpdate(String, int[])
-		 */
-		public int executeUpdate(String arg0, int[] arg1) throws SQLException {
-			return this.delegate.executeUpdate(arg0, arg1);
-		}
-
-		/**
-		 * @see Statement#executeUpdate(String, String[])
-		 */
-		public int executeUpdate(String arg0, String[] arg1)
-				throws SQLException {
-			return this.delegate.executeUpdate(arg0, arg1);
-		}
-
-		public java.sql.Array getArray(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getArray(String)
-		 */
-		public java.sql.Array getArray(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.math.BigDecimal getBigDecimal(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * DOCUMENT ME!
-		 * 
-		 * @param p1
-		 *            DOCUMENT ME!
-		 * @param p2
-		 *            DOCUMENT ME!
-		 * @return DOCUMENT ME!
-		 * @throws SQLException
-		 *             DOCUMENT ME!
-		 * @deprecated
-		 */
-		public java.math.BigDecimal getBigDecimal(int p1, int p2)
-				throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getBigDecimal(String)
-		 */
-		public BigDecimal getBigDecimal(String arg0) throws SQLException {
-			return null;
-		}
-
-		public java.sql.Blob getBlob(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getBlob(String)
-		 */
-		public java.sql.Blob getBlob(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public boolean getBoolean(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getBoolean(String)
-		 */
-		public boolean getBoolean(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public byte getByte(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getByte(String)
-		 */
-		public byte getByte(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public byte[] getBytes(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getBytes(String)
-		 */
-		public byte[] getBytes(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.sql.Clob getClob(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getClob(String)
-		 */
-		public Clob getClob(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.sql.Connection getConnection() throws SQLException {
-			return this.delegate.getConnection();
-		}
-
-		public java.sql.Date getDate(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public java.sql.Date getDate(int p1, final Calendar p2)
-				throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getDate(String)
-		 */
-		public Date getDate(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#getDate(String, Calendar)
-		 */
-		public Date getDate(String arg0, Calendar arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public double getDouble(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getDouble(String)
-		 */
-		public double getDouble(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public int getFetchDirection() throws SQLException {
-			return this.delegate.getFetchDirection();
-		}
-
-		public int getFetchSize() throws java.sql.SQLException {
-			return this.delegate.getFetchSize();
-		}
-
-		public float getFloat(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getFloat(String)
-		 */
-		public float getFloat(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see Statement#getGeneratedKeys()
-		 */
-		public java.sql.ResultSet getGeneratedKeys() throws SQLException {
-			return this.delegate.getGeneratedKeys();
-		}
-
-		public int getInt(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getInt(String)
-		 */
-		public int getInt(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public long getLong(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getLong(String)
-		 */
-		public long getLong(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public int getMaxFieldSize() throws SQLException {
-			return this.delegate.getMaxFieldSize();
-		}
-
-		public int getMaxRows() throws SQLException {
-			return this.delegate.getMaxRows();
-		}
-
-		public java.sql.ResultSetMetaData getMetaData() throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public boolean getMoreResults() throws SQLException {
-			return this.delegate.getMoreResults();
-		}
-
-		/**
-		 * @see Statement#getMoreResults(int)
-		 */
-		public boolean getMoreResults(int arg0) throws SQLException {
-			return this.delegate.getMoreResults();
-		}
-
-		public java.lang.Object getObject(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public java.lang.Object getObject(int p1, final java.util.Map p2)
-				throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getObject(String)
-		 */
-		public Object getObject(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#getObject(String, Map)
-		 */
-		public Object getObject(String arg0, Map arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see PreparedStatement#getParameterMetaData()
-		 */
-		public ParameterMetaData getParameterMetaData() throws SQLException {
-			return this.delegate.getParameterMetaData();
-		}
-
-		public int getQueryTimeout() throws SQLException {
-			return this.delegate.getQueryTimeout();
-		}
-
-		public java.sql.Ref getRef(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getRef(String)
-		 */
-		public Ref getRef(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.sql.ResultSet getResultSet() throws SQLException {
-			return this.delegate.getResultSet();
-		}
-
-		public int getResultSetConcurrency() throws SQLException {
-			return this.delegate.getResultSetConcurrency();
-		}
-
-		/**
-		 * @see Statement#getResultSetHoldability()
-		 */
-		public int getResultSetHoldability() throws SQLException {
-			return this.delegate.getResultSetHoldability();
-		}
-
-		public int getResultSetType() throws SQLException {
-			return this.delegate.getResultSetType();
-		}
-
-		public short getShort(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getShort(String)
-		 */
-		public short getShort(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.lang.String getString(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getString(String)
-		 */
-		public String getString(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.sql.Time getTime(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public java.sql.Time getTime(int p1, final java.util.Calendar p2)
-				throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getTime(String)
-		 */
-		public Time getTime(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#getTime(String, Calendar)
-		 */
-		public Time getTime(String arg0, Calendar arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.sql.Timestamp getTimestamp(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public java.sql.Timestamp getTimestamp(int p1,
-				final java.util.Calendar p2) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#getTimestamp(String)
-		 */
-		public Timestamp getTimestamp(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#getTimestamp(String, Calendar)
-		 */
-		public Timestamp getTimestamp(String arg0, Calendar arg1)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public int getUpdateCount() throws SQLException {
-			return this.delegate.getUpdateCount();
-		}
-
-		/**
-		 * @see CallableStatement#getURL(int)
-		 */
-		public URL getURL(int arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#getURL(String)
-		 */
-		public URL getURL(String arg0) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public java.sql.SQLWarning getWarnings() throws SQLException {
-			return this.delegate.getWarnings();
-		}
-
-		public void registerOutParameter(int p1, int p2) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public void registerOutParameter(int p1, int p2, int p3)
-				throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public void registerOutParameter(int p1, int p2, java.lang.String p3)
-				throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		/**
-		 * @see CallableStatement#registerOutParameter(String, int)
-		 */
-		public void registerOutParameter(String arg0, int arg1)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#registerOutParameter(String, int, int)
-		 */
-		public void registerOutParameter(String arg0, int arg1, int arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#registerOutParameter(String, int, String)
-		 */
-		public void registerOutParameter(String arg0, int arg1, String arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setArray(int p1, final java.sql.Array p2)
-				throws SQLException {
-			this.delegate.setArray(p1, p2);
-		}
-
-		public void setAsciiStream(int p1, final java.io.InputStream p2, int p3)
-				throws SQLException {
-			this.delegate.setAsciiStream(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setAsciiStream(String, InputStream, int)
-		 */
-		public void setAsciiStream(String arg0, InputStream arg1, int arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setBigDecimal(int p1, final java.math.BigDecimal p2)
-				throws SQLException {
-			this.delegate.setBigDecimal(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setBigDecimal(String, BigDecimal)
-		 */
-		public void setBigDecimal(String arg0, BigDecimal arg1)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setBinaryStream(int p1, final java.io.InputStream p2, int p3)
-				throws SQLException {
-			this.delegate.setBinaryStream(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setBinaryStream(String, InputStream, int)
-		 */
-		public void setBinaryStream(String arg0, InputStream arg1, int arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setBlob(int p1, final java.sql.Blob p2) throws SQLException {
-			this.delegate.setBlob(p1, p2);
-		}
-
-		public void setBoolean(int p1, boolean p2) throws SQLException {
-			this.delegate.setBoolean(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setBoolean(String, boolean)
-		 */
-		public void setBoolean(String arg0, boolean arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setByte(int p1, byte p2) throws SQLException {
-			this.delegate.setByte(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setByte(String, byte)
-		 */
-		public void setByte(String arg0, byte arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setBytes(int p1, byte[] p2) throws SQLException {
-			this.delegate.setBytes(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setBytes(String, byte[])
-		 */
-		public void setBytes(String arg0, byte[] arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setCharacterStream(int p1, final java.io.Reader p2, int p3)
-				throws SQLException {
-			this.delegate.setCharacterStream(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setCharacterStream(String, Reader, int)
-		 */
-		public void setCharacterStream(String arg0, Reader arg1, int arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setClob(int p1, final java.sql.Clob p2) throws SQLException {
-			this.delegate.setClob(p1, p2);
-		}
-
-		public void setCursorName(java.lang.String p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public void setDate(int p1, final java.sql.Date p2) throws SQLException {
-			this.delegate.setDate(p1, p2);
-		}
-
-		public void setDate(int p1, final java.sql.Date p2,
-				final java.util.Calendar p3) throws SQLException {
-			this.delegate.setDate(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setDate(String, Date)
-		 */
-		public void setDate(String arg0, Date arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#setDate(String, Date, Calendar)
-		 */
-		public void setDate(String arg0, Date arg1, Calendar arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setDouble(int p1, double p2) throws SQLException {
-			this.delegate.setDouble(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setDouble(String, double)
-		 */
-		public void setDouble(String arg0, double arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setEscapeProcessing(boolean p1) throws SQLException {
-			this.delegate.setEscapeProcessing(p1);
-		}
-
-		public void setFetchDirection(int p1) throws SQLException {
-			this.delegate.setFetchDirection(p1);
-		}
-
-		public void setFetchSize(int p1) throws SQLException {
-			this.delegate.setFetchSize(p1);
-		}
-
-		public void setFloat(int p1, float p2) throws SQLException {
-			this.delegate.setFloat(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setFloat(String, float)
-		 */
-		public void setFloat(String arg0, float arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setInt(int p1, int p2) throws SQLException {
-			this.delegate.setInt(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setInt(String, int)
-		 */
-		public void setInt(String arg0, int arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setLong(int p1, long p2) throws SQLException {
-			this.delegate.setLong(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setLong(String, long)
-		 */
-		public void setLong(String arg0, long arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setMaxFieldSize(int p1) throws SQLException {
-			this.delegate.setMaxFieldSize(p1);
-		}
-
-		public void setMaxRows(int p1) throws SQLException {
-			this.delegate.setMaxRows(p1);
-		}
-
-		public void setNull(int p1, int p2) throws SQLException {
-			this.delegate.setNull(p1, p2);
-		}
-
-		public void setNull(int p1, int p2, java.lang.String p3)
-				throws SQLException {
-			this.delegate.setNull(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setNull(String, int)
-		 */
-		public void setNull(String arg0, int arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#setNull(String, int, String)
-		 */
-		public void setNull(String arg0, int arg1, String arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setObject(int p1, final java.lang.Object p2)
-				throws SQLException {
-			this.delegate.setObject(p1, p2);
-		}
-
-		public void setObject(int p1, final java.lang.Object p2, int p3)
-				throws SQLException {
-			this.delegate.setObject(p1, p2, p3);
-		}
-
-		public void setObject(int p1, final java.lang.Object p2, int p3, int p4)
-				throws SQLException {
-			this.delegate.setObject(p1, p2, p3, p4);
-		}
-
-		/**
-		 * @see CallableStatement#setObject(String, Object)
-		 */
-		public void setObject(String arg0, Object arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#setObject(String, Object, int)
-		 */
-		public void setObject(String arg0, Object arg1, int arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#setObject(String, Object, int, int)
-		 */
-		public void setObject(String arg0, Object arg1, int arg2, int arg3)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setQueryTimeout(int p1) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public void setRef(int p1, final Ref p2) throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-
-		public void setShort(int p1, short p2) throws SQLException {
-			this.delegate.setShort(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setShort(String, short)
-		 */
-		public void setShort(String arg0, short arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setString(int p1, java.lang.String p2)
-				throws java.sql.SQLException {
-			this.delegate.setString(p1, p2);
-		}
-
-		/**
-		 * @see CallableStatement#setString(String, String)
-		 */
-		public void setString(String arg0, String arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setTime(int p1, final java.sql.Time p2) throws SQLException {
-			this.delegate.setTime(p1, p2);
-		}
-
-		public void setTime(int p1, final java.sql.Time p2,
-				final java.util.Calendar p3) throws SQLException {
-			this.delegate.setTime(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setTime(String, Time)
-		 */
-		public void setTime(String arg0, Time arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#setTime(String, Time, Calendar)
-		 */
-		public void setTime(String arg0, Time arg1, Calendar arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public void setTimestamp(int p1, final java.sql.Timestamp p2)
-				throws SQLException {
-			this.delegate.setTimestamp(p1, p2);
-		}
-
-		public void setTimestamp(int p1, final java.sql.Timestamp p2,
-				final java.util.Calendar p3) throws SQLException {
-			this.delegate.setTimestamp(p1, p2, p3);
-		}
-
-		/**
-		 * @see CallableStatement#setTimestamp(String, Timestamp)
-		 */
-		public void setTimestamp(String arg0, Timestamp arg1)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * @see CallableStatement#setTimestamp(String, Timestamp, Calendar)
-		 */
-		public void setTimestamp(String arg0, Timestamp arg1, Calendar arg2)
-				throws SQLException {
-			throw new NotImplemented();
-		}
-
-		/**
-		 * DOCUMENT ME!
-		 * 
-		 * @param p1
-		 *            DOCUMENT ME!
-		 * @param p2
-		 *            DOCUMENT ME!
-		 * @param p3
-		 *            DOCUMENT ME!
-		 * @throws SQLException
-		 *             DOCUMENT ME!
-		 * @deprecated
-		 */
-		public void setUnicodeStream(int p1, final java.io.InputStream p2,
-				int p3) throws SQLException {
-			this.delegate.setUnicodeStream(p1, p2, p3);
-		}
-
-		/**
-		 * @see PreparedStatement#setURL(int, URL)
-		 */
-		public void setURL(int arg0, URL arg1) throws SQLException {
-			this.delegate.setURL(arg0, arg1);
-		}
-
-		/**
-		 * @see CallableStatement#setURL(String, URL)
-		 */
-		public void setURL(String arg0, URL arg1) throws SQLException {
-			throw new NotImplemented();
-		}
-
-		public boolean wasNull() throws SQLException {
-			throw SQLError.createSQLException("Not supported");
-		}
-	}
-
-	/**
-	 * Marker for character set converter not being available (not written,
-	 * multibyte, etc) Used to prevent multiple instantiation requests.
-	 */
-	private static final Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object();
-
-	/**
-	 * The mapping between MySQL charset names and Java charset names.
-	 * Initialized by loadCharacterSetMapping()
-	 */
-	public static Map charsetMap;
-
-	/** Default logger class name */
-	protected static final String DEFAULT_LOGGER_CLASS = "com.mysql.jdbc.log.StandardLogger";
-
-	private final static int HISTOGRAM_BUCKETS = 20;
-
-	/** Logger instance name */
-	private static final String LOGGER_INSTANCE_NAME = "MySQL";
-
-	/**
-	 * Map mysql transaction isolation level name to
-	 * java.sql.Connection.TRANSACTION_XXX
-	 */
-	private static Map mapTransIsolationNameToValue = null;
-
-	/** Null logger shared by all connections at startup */
-	private static final Log NULL_LOGGER = new NullLogger(LOGGER_INSTANCE_NAME);
-
-	private static Map roundRobinStatsMap;
-
-	private static final Map serverCollationByUrl = new HashMap();
-
-	private static final Map serverConfigByUrl = new HashMap();
-
-	private static Timer cancelTimer;
-
-	static {
-		mapTransIsolationNameToValue = new HashMap(8);
-		mapTransIsolationNameToValue.put("READ-UNCOMMITED", new Integer(
-				TRANSACTION_READ_UNCOMMITTED));
-		mapTransIsolationNameToValue.put("READ-UNCOMMITTED", new Integer(
-				TRANSACTION_READ_UNCOMMITTED));
-		mapTransIsolationNameToValue.put("READ-COMMITTED", new Integer(
-				TRANSACTION_READ_COMMITTED));
-		mapTransIsolationNameToValue.put("REPEATABLE-READ", new Integer(
-				TRANSACTION_REPEATABLE_READ));
-		mapTransIsolationNameToValue.put("SERIALIZABLE", new Integer(
-				TRANSACTION_SERIALIZABLE));
-	}
-
-	protected static SQLException appendMessageToException(SQLException sqlEx,
-			String messageToAppend) {
-		String origMessage = sqlEx.getMessage();
-		String sqlState = sqlEx.getSQLState();
-		int vendorErrorCode = sqlEx.getErrorCode();
-
-		StringBuffer messageBuf = new StringBuffer(origMessage.length()
-				+ messageToAppend.length());
-		messageBuf.append(origMessage);
-		messageBuf.append(messageToAppend);
-
-		SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf
-				.toString(), sqlState, vendorErrorCode);
-
-		//
-		// Try and maintain the original stack trace,
-		// only works on JDK-1.4 and newer
-		//
-
-		try {
-			// Have to do this with reflection, otherwise older JVMs croak
-			Method getStackTraceMethod = null;
-			Method setStackTraceMethod = null;
-			Object theStackTraceAsObject = null;
-
-			Class stackTraceElementClass = Class
-					.forName("java.lang.StackTraceElement");
-			Class stackTraceElementArrayClass = Array.newInstance(
-					stackTraceElementClass, new int[] { 0 }).getClass();
-
-			getStackTraceMethod = Throwable.class.getMethod("getStackTrace",
-					new Class[] {});
-
-			setStackTraceMethod = Throwable.class.getMethod("setStackTrace",
-					new Class[] { stackTraceElementArrayClass });
-
-			if (getStackTraceMethod != null && setStackTraceMethod != null) {
-				theStackTraceAsObject = getStackTraceMethod.invoke(sqlEx,
-						new Object[0]);
-				setStackTraceMethod.invoke(sqlExceptionWithNewMessage,
-						new Object[] { theStackTraceAsObject });
-			}
-		} catch (NoClassDefFoundError noClassDefFound) {
-
-		} catch (NoSuchMethodException noSuchMethodEx) {
-
-		} catch (Throwable catchAll) {
-
-		}
-
-		return sqlExceptionWithNewMessage;
-	}
-
-	protected static Timer getCancelTimer() {
-		return cancelTimer;
-	}
-
-	private static synchronized int getNextRoundRobinHostIndex(String url,
-			List hostList) {
-		if (roundRobinStatsMap == null) {
-			roundRobinStatsMap = new HashMap();
-		}
-
-		int[] index = (int[]) roundRobinStatsMap.get(url);
-
-		if (index == null) {
-			index = new int[1];
-			index[0] = -1;
-
-			roundRobinStatsMap.put(url, index);
-		}
-
-		index[0]++;
-
-		if (index[0] >= hostList.size()) {
-			index[0] = 0;
-		}
-
-		return index[0];
-	}
-
-	private static boolean nullSafeCompare(String s1, String s2) {
-		if (s1 == null && s2 == null) {
-			return true;
-		}
-
-		if (s1 == null && s2 != null) {
-			return false;
-		}
-
-		return s1.equals(s2);
-	}
-
-	/** Are we in autoCommit mode? */
-	private boolean autoCommit = true;
-
-	/** A map of SQL to parsed prepared statement parameters. */
-	private Map cachedPreparedStatementParams;
-
-	/**
-	 * For servers > 4.1.0, what character set is the metadata returned in?
-	 */
-	private String characterSetMetadata = null;
-
-	/**
-	 * The character set we want results and result metadata returned in (null ==
-	 * results in any charset, metadata in UTF-8).
-	 */
-	private String characterSetResultsOnServer = null;
-
-	/**
-	 * Holds cached mappings to charset converters to avoid static
-	 * synchronization and at the same time save memory (each charset converter
-	 * takes approx 65K of static data).
-	 */
-	private Map charsetConverterMap = new HashMap(CharsetMapping
-			.getNumberOfCharsetsConfigured());
-
-	/**
-	 * The mapping between MySQL charset names and the max number of chars in
-	 * them. Lazily instantiated via getMaxBytesPerChar().
-	 */
-	private Map charsetToNumBytesMap;
-
-	/** The point in time when this connection was created */
-	private long connectionCreationTimeMillis = 0;
-
-	/** ID used when profiling */
-	private long connectionId;
-
-	/** The database we're currently using (called Catalog in JDBC terms). */
-	private String database = null;
-
-	/** Internal DBMD to use for various database-version specific features */
-	private DatabaseMetaData dbmd = null;
-
-	private TimeZone defaultTimeZone;
-
-	/** The event sink to use for profiling */
-	private ProfileEventSink eventSink;
-
-	private boolean executingFailoverReconnect = false;
-
-	/** Are we failed-over to a non-master host */
-	private boolean failedOver = false;
-
-	/** Why was this connection implicitly closed, if known? (for diagnostics) */
-	private Throwable forceClosedReason;
-
-	/** Where was this connection implicitly closed? (for diagnostics) */
-	private Throwable forcedClosedLocation;
-
-	/** Does the server suuport isolation levels? */
-	private boolean hasIsolationLevels = false;
-
-	/** Does this version of MySQL support quoted identifiers? */
-	private boolean hasQuotedIdentifiers = false;
-
-	/** The hostname we're connected to */
-	private String host = null;
-
-	/** The list of host(s) to try and connect to */
-	private List hostList = null;
-
-	/** How many hosts are in the host list? */
-	private int hostListSize = 0;
-
-	/**
-	 * We need this 'bootstrapped', because 4.1 and newer will send fields back
-	 * with this even before we fill this dynamically from the server.
-	 */
-	private String[] indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
-
-	/** The I/O abstraction interface (network conn to MySQL server */
-	private MysqlIO io = null;
-	
-	private boolean isClientTzUTC = false;
-	
-	/** Has this connection been closed? */
-	private boolean isClosed = true;
-
-	/** Is this connection associated with a global tx? */
-	private boolean isInGlobalTx = false;
-
-	/** Is this connection running inside a JDK-1.3 VM? */
-	private boolean isRunningOnJDK13 = false;
-
-	/** isolation level */
-	private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
-
-	private boolean isServerTzUTC = false;
-
-	/** When did the last query finish? */
-	private long lastQueryFinishedTime = 0;
-
-	/** The logger we're going to use */
-	private Log log = NULL_LOGGER;
-
-	/**
-	 * If gathering metrics, what was the execution time of the longest query so
-	 * far ?
-	 */
-	private long longestQueryTimeMs = 0;
-
-	/** Is the server configured to use lower-case table names only? */
-	private boolean lowerCaseTableNames = false;
-
-	/** When did the master fail? */
-	private long masterFailTimeMillis = 0L;
-
-	/**
-	 * The largest packet we can send (changed once we know what the server
-	 * supports, we get this at connection init).
-	 */
-	private int maxAllowedPacket = 65536;
-
-	private long maximumNumberTablesAccessed = 0;
-
-	/** Has the max-rows setting been changed from the default? */
-	private boolean maxRowsChanged = false;
-
-	/** When was the last time we reported metrics? */
-	private long metricsLastReportedMs;
-
-	private long minimumNumberTablesAccessed = Long.MAX_VALUE;
-
-	/** Mutex */
-	private final Object mutex = new Object();
-
-	/** The JDBC URL we're using */
-	private String myURL = null;
-
-	/** Does this connection need to be tested? */
-	private boolean needsPing = false;
-
-	private int netBufferLength = 16384;
-
-	private boolean noBackslashEscapes = false;
-
-	private long numberOfPreparedExecutes = 0;
-
-	private long numberOfPrepares = 0;
-
-	private long numberOfQueriesIssued = 0;
-
-	private long numberOfResultSetsCreated = 0;
-
-	private long[] numTablesMetricsHistBreakpoints;
-
-	private int[] numTablesMetricsHistCounts;
-
-	private long[] oldHistBreakpoints = null;
-
-	private int[] oldHistCounts = null;
-
-	/** A map of currently open statements */
-	private Map openStatements;
-
-	private LRUCache parsedCallableStatementCache;
-
-	private boolean parserKnowsUnicode = false;
-
-	/** The password we used */
-	private String password = null;
-
-	private long[] perfMetricsHistBreakpoints;
-
-	private int[] perfMetricsHistCounts;
-
-	/** Point of origin where this Connection was created */
-	private Throwable pointOfOrigin;
-
-	/** The port number we're connected to (defaults to 3306) */
-	private int port = 3306;
-
-	/**
-	 * Used only when testing failover functionality for regressions, causes the
-	 * failover code to not retry the master first
-	 */
-	private boolean preferSlaveDuringFailover = false;
-
-	/** Properties for this connection specified by user */
-	private Properties props = null;
-
-	/** Number of queries we've issued since the master failed */
-	private long queriesIssuedFailedOver = 0;
-
-	/** Should we retrieve 'info' messages from the server? */
-	private boolean readInfoMsg = false;
-
-	/** Are we in read-only mode? */
-	private boolean readOnly = false;
-
-	/** The timezone of the server */
-	private TimeZone serverTimezoneTZ = null;
-
-	/** The map of server variables that we retrieve at connection init. */
-	private Map serverVariables = null;
-
-	private long shortestQueryTimeMs = Long.MAX_VALUE;
-
-	/** A map of statements that have had setMaxRows() called on them */
-	private Map statementsUsingMaxRows;
-
-	private double totalQueryTimeMs = 0;
-
-	/** Are transactions supported by the MySQL server we are connected to? */
-	private boolean transactionsSupported = false;
-
-	/**
-	 * The type map for UDTs (not implemented, but used by some third-party
-	 * vendors, most notably IBM WebSphere)
-	 */
-	private Map typeMap;
-
-	/** Has ANSI_QUOTES been enabled on the server? */
-	private boolean useAnsiQuotes = false;
-
-	/** The user we're connected as */
-	private String user = null;
-
-	/**
-	 * Should we use server-side prepared statements? (auto-detected, but can be
-	 * disabled by user)
-	 */
-	private boolean useServerPreparedStmts = false;
-	
-	private LRUCache serverSideStatementCheckCache;
-
-	private LRUCache serverSideStatementCache;
-	private Calendar sessionCalendar;
-	private Calendar utcCalendar;
-	
-	private String origHostToConnectTo;
-	
-	private int origPortToConnectTo;
-
-	// we don't want to be able to publicly clone this...
-	
-	private String origDatabaseToConnectTo;
-
-	private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server
-
-	private boolean usePlatformCharsetConverters;
-	
-	
-	/**
-	 * Creates a connection to a MySQL Server.
-	 * 
-	 * @param hostToConnectTo
-	 *            the hostname of the database server
-	 * @param portToConnectTo
-	 *            the port number the server is listening on
-	 * @param info
-	 *            a Properties[] list holding the user and password
-	 * @param databaseToConnectTo
-	 *            the database to connect to
-	 * @param url
-	 *            the URL of the connection
-	 * @param d
-	 *            the Driver instantation of the connection
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	Connection(String hostToConnectTo, int portToConnectTo, Properties info,
-			String databaseToConnectTo, String url)
-			throws SQLException {
-		this.charsetToNumBytesMap = new HashMap();
-		this.cancelTimer = new Timer(true);
-		this.connectionCreationTimeMillis = System.currentTimeMillis();
-		this.pointOfOrigin = new Throwable();
-		
-		// Stash away for later, used to clone this connection for Statement.cancel
-		// and Statement.setQueryTimeout().
-		//
-		
-		this.origHostToConnectTo = hostToConnectTo;
-		this.origPortToConnectTo = portToConnectTo;
-		this.origDatabaseToConnectTo = databaseToConnectTo;
-
-		try {
-			Blob.class.getMethod("truncate", new Class[] {Long.TYPE});
-			
-			this.isRunningOnJDK13 = false;
-		} catch (NoSuchMethodException nsme) {
-			this.isRunningOnJDK13 = true;
-		}
-		
-		this.sessionCalendar = new GregorianCalendar();
-		this.utcCalendar = new GregorianCalendar();
-		this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
-		
-		//
-		// Normally, this code would be in initializeDriverProperties,
-		// but we need to do this as early as possible, so we can start
-		// logging to the 'correct' place as early as possible...this.log
-		// points to 'NullLogger' for every connection at startup to avoid
-		// NPEs and the overhead of checking for NULL at every logging call.
-		//
-		// We will reset this to the configured logger during properties
-		// initialization.
-		//
-		this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME);
-
-		// We store this per-connection, due to static synchronization
-		// issues in Java's built-in TimeZone class...
-		this.defaultTimeZone = TimeZone.getDefault();
-		if ("GMT".equalsIgnoreCase(this.defaultTimeZone.getID())) {
-			this.isClientTzUTC = true;
-		} else {
-			this.isClientTzUTC = false;
-		}
-
-		this.openStatements = new HashMap();
-		this.serverVariables = new HashMap();
-		this.hostList = new ArrayList();
-
-		if (hostToConnectTo == null) {
-			this.host = "localhost";
-			this.hostList.add(this.host);
-		} else if (hostToConnectTo.indexOf(",") != -1) {
-			// multiple hosts separated by commas (failover)
-			StringTokenizer hostTokenizer = new StringTokenizer(
-					hostToConnectTo, ",", false);
-
-			while (hostTokenizer.hasMoreTokens()) {
-				this.hostList.add(hostTokenizer.nextToken().trim());
-			}
-		} else {
-			this.host = hostToConnectTo;
-			this.hostList.add(this.host);
-		}
-
-		this.hostListSize = this.hostList.size();
-		this.port = portToConnectTo;
-
-		if (databaseToConnectTo == null) {
-			databaseToConnectTo = "";
-		}
-
-		this.database = databaseToConnectTo;
-		this.myURL = url;
-		this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
-		this.password = info
-				.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
-
-		if ((this.user == null) || this.user.equals("")) {
-			this.user = "";
-		}
-
-		if (this.password == null) {
-			this.password = "";
-		}
-
-		this.props = info;
-		initializeDriverProperties(info);
-
-		try {
-			createNewIO(false);
-			this.connectionId = this.io.getThreadId();
-			this.dbmd = new DatabaseMetaData(this, this.database);
-		} catch (SQLException ex) {
-			cleanup(ex);
-
-			// don't clobber SQL exceptions
-			throw ex;
-		} catch (Exception ex) {
-			cleanup(ex);
-
-			StringBuffer mesg = new StringBuffer();
-
-			if (getParanoid()) {
-				mesg.append("Cannot connect to MySQL server on ");
-				mesg.append(this.host);
-				mesg.append(":");
-				mesg.append(this.port);
-				mesg.append(".\n\n");
-				mesg.append("Make sure that there is a MySQL server ");
-				mesg.append("running on the machine/port you are trying ");
-				mesg
-						.append("to connect to and that the machine this software is "
-								+ "running on ");
-				mesg.append("is able to connect to this host/port "
-						+ "(i.e. not firewalled). ");
-				mesg
-						.append("Also make sure that the server has not been started "
-								+ "with the --skip-networking ");
-				mesg.append("flag.\n\n");
-			} else {
-				mesg.append("Unable to connect to database.");
-			}
-
-			mesg.append("Underlying exception: \n\n");
-			mesg.append(ex.getClass().getName());
-
-			if (!getParanoid()) {
-				mesg.append(Util.stackTraceToString(ex));
-			}
-
-			throw SQLError.createSQLException(mesg.toString(),
-					SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
-		}
-	}
-
-	private void addToHistogram(int[] histogramCounts,
-			long[] histogramBreakpoints, long value, int numberOfTimes,
-			long currentLowerBound, long currentUpperBound) {
-		if (histogramCounts == null) {
-			createInitialHistogram(histogramBreakpoints,
-					currentLowerBound, currentUpperBound);
-		}
-
-		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
-			if (histogramBreakpoints[i] >= value) {
-				histogramCounts[i] += numberOfTimes;
-
-				break;
-			}
-		}
-	}
-
-	private void addToPerformanceHistogram(long value, int numberOfTimes) {
-		checkAndCreatePerformanceHistogram();
-
-		addToHistogram(this.perfMetricsHistCounts,
-				this.perfMetricsHistBreakpoints, value, numberOfTimes,
-				this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
-						: this.shortestQueryTimeMs, this.longestQueryTimeMs);
-	}
-
-	private void addToTablesAccessedHistogram(long value, int numberOfTimes) {
-		checkAndCreateTablesAccessedHistogram();
-
-		addToHistogram(this.numTablesMetricsHistCounts,
-				this.numTablesMetricsHistBreakpoints, value, numberOfTimes,
-				this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
-						: this.minimumNumberTablesAccessed,
-				this.maximumNumberTablesAccessed);
-	}
-
-	/**
-	 * Builds the map needed for 4.1.0 and newer servers that maps field-level
-	 * charset/collation info to a java character encoding name.
-	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	private void buildCollationMapping() throws SQLException {
-		if (versionMeetsMinimum(4, 1, 0)) {
-
-			TreeMap sortedCollationMap = null;
-
-			if (getCacheServerConfiguration()) {
-				synchronized (serverConfigByUrl) {
-					sortedCollationMap = (TreeMap) serverCollationByUrl
-							.get(getURL());
-				}
-			}
-
-			com.mysql.jdbc.Statement stmt = null;
-			com.mysql.jdbc.ResultSet results = null;
-
-			try {
-				if (sortedCollationMap == null) {
-					sortedCollationMap = new TreeMap();
-
-					stmt = (com.mysql.jdbc.Statement) createStatement();
-
-					if (stmt.getMaxRows() != 0) {
-						stmt.setMaxRows(0);
-					}
-
-					results = (com.mysql.jdbc.ResultSet) stmt
-							.executeQuery("SHOW COLLATION");
-
-					while (results.next()) {
-						String charsetName = results.getString(2);
-						Integer charsetIndex = new Integer(results.getInt(3));
-
-						sortedCollationMap.put(charsetIndex, charsetName);
-					}
-
-					if (getCacheServerConfiguration()) {
-						synchronized (serverConfigByUrl) {
-							serverCollationByUrl.put(getURL(),
-									sortedCollationMap);
-						}
-					}
-
-				}
-
-				// Now, merge with what we already know
-				int highestIndex = ((Integer) sortedCollationMap.lastKey())
-						.intValue();
-
-				if (CharsetMapping.INDEX_TO_CHARSET.length > highestIndex) {
-					highestIndex = CharsetMapping.INDEX_TO_CHARSET.length;
-				}
-
-				this.indexToCharsetMapping = new String[highestIndex + 1];
-
-				for (int i = 0; i < CharsetMapping.INDEX_TO_CHARSET.length; i++) {
-					this.indexToCharsetMapping[i] = CharsetMapping.INDEX_TO_CHARSET[i];
-				}
-
-				for (Iterator indexIter = sortedCollationMap.entrySet()
-						.iterator(); indexIter.hasNext();) {
-					Map.Entry indexEntry = (Map.Entry) indexIter.next();
-
-					String mysqlCharsetName = (String) indexEntry.getValue();
-
-					this.indexToCharsetMapping[((Integer) indexEntry.getKey())
-							.intValue()] = CharsetMapping
-							.getJavaEncodingForMysqlEncoding(mysqlCharsetName,
-									this);
-				}
-			} catch (java.sql.SQLException e) {
-				throw e;
-			} finally {
-				if (results != null) {
-					try {
-						results.close();
-					} catch (java.sql.SQLException sqlE) {
-						;
-					}
-				}
-
-				if (stmt != null) {
-					try {
-						stmt.close();
-					} catch (java.sql.SQLException sqlE) {
-						;
-					}
-				}
-			}
-		} else {
-			// Safety, we already do this as an initializer, but this makes
-			// the intent more clear
-			this.indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
-		}
-	}
-	
-	private boolean canHandleAsServerPreparedStatement(String sql) 
-		throws SQLException {
-		if (sql == null || sql.length() == 0) {
-			return true;
-		}
-
-		if (getCachePreparedStatements()) {
-			synchronized (this.serverSideStatementCheckCache) {
-				Boolean flag = (Boolean)this.serverSideStatementCheckCache.get(sql);
-				
-				if (flag != null) {
-					return flag.booleanValue();
-				}
-					
-				boolean canHandle = canHandleAsServerPreparedStatementNoCache(sql);
-				
-				if (sql.length() < getPreparedStatementCacheSqlLimit()) {
-					this.serverSideStatementCheckCache.put(sql, 
-							canHandle ? Boolean.TRUE : Boolean.FALSE);
-				}
-					
-				return canHandle;
-			}
-		}
-		
-		return canHandleAsServerPreparedStatementNoCache(sql);
-	}
-
-	private boolean canHandleAsServerPreparedStatementNoCache(String sql) 
-		throws SQLException {
-		
-		// Can't use server-side prepare for CALL
-		if (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "CALL")) {
-			return false;
-		}
-		
-		boolean canHandleAsStatement = true;
-		
-		if (!versionMeetsMinimum(5, 0, 7) && 
-				(StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "SELECT")
-				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
-						"DELETE")
-				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
-						"INSERT")
-				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
-						"UPDATE")
-				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
-						"REPLACE"))) {
-
-			// check for limit ?[,?]
-
-			/*
-			 * The grammar for this (from the server) is: ULONG_NUM | ULONG_NUM
-			 * ',' ULONG_NUM | ULONG_NUM OFFSET_SYM ULONG_NUM
-			 */
-
-			int currentPos = 0;
-			int statementLength = sql.length();
-			int lastPosToLook = statementLength - 7; // "LIMIT ".length()
-			boolean allowBackslashEscapes = !this.noBackslashEscapes;
-			char quoteChar = this.useAnsiQuotes ? '"' : '\'';
-			boolean foundLimitWithPlaceholder = false;
-
-			while (currentPos < lastPosToLook) {
-				int limitStart = StringUtils.indexOfIgnoreCaseRespectQuotes(
-						currentPos, sql, "LIMIT ", quoteChar,
-						allowBackslashEscapes);
-
-				if (limitStart == -1) {
-					break;
-				}
-
-				currentPos = limitStart + 7;
-
-				while (currentPos < statementLength) {
-					char c = sql.charAt(currentPos);
-
-					//
-					// Have we reached the end
-					// of what can be in a LIMIT clause?
-					//
-
-					if (!Character.isDigit(c) && !Character.isWhitespace(c)
-							&& c != ',' && c != '?') {
-						break;
-					}
-
-					if (c == '?') {
-						foundLimitWithPlaceholder = true;
-						break;
-					}
-
-					currentPos++;
-				}
-			}
-
-			canHandleAsStatement = !foundLimitWithPlaceholder;
-		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE TABLE")) {
-			canHandleAsStatement = false;
-		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "DO")) {
-			canHandleAsStatement = false;
-		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SET")) {
-			canHandleAsStatement = false;
-		}
-
-		
-		
-		return canHandleAsStatement;
-	}
-
-	/**
 	 * Changes the user on this connection by performing a re-authentication. If
 	 * authentication fails, the connection will remain under the context of the
 	 * current user.
@@ -1835,1690 +55,95 @@
 	 *             if authentication fails, or some other error occurs while
 	 *             performing the command.
 	 */
-	public void changeUser(String userName, String newPassword)
-			throws SQLException {
-		if ((userName == null) || userName.equals("")) {
-			userName = "";
-		}
+	public abstract void changeUser(String userName, String newPassword)
+			throws SQLException;
 
-		if (newPassword == null) {
-			newPassword = "";
-		}
+	public abstract void clearHasTriedMaster();
 
-		this.io.changeUser(userName, newPassword, this.database);
-		this.user = userName;
-		this.password = newPassword;
-
-		if (versionMeetsMinimum(4, 1, 0)) {
-			configureClientCharacterSet();
-		}
-		
-		setupServerForTruncationChecks();
-	}
-
-	private void checkAndCreatePerformanceHistogram() {
-		if (this.perfMetricsHistCounts == null) {
-			this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
-		}
-
-		if (this.perfMetricsHistBreakpoints == null) {
-			this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
-		}
-	}
-
-	private void checkAndCreateTablesAccessedHistogram() {
-		if (this.numTablesMetricsHistCounts == null) {
-			this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
-		}
-
-		if (this.numTablesMetricsHistBreakpoints == null) {
-			this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
-		}
-	}
-
-	private void checkClosed() throws SQLException {
-		if (this.isClosed) {
-			StringBuffer messageBuf = new StringBuffer(
-					"No operations allowed after connection closed.");
-
-			if (this.forcedClosedLocation != null || this.forceClosedReason != null) {
-				messageBuf
-				.append("Connection was implicitly closed ");
-			}
-			
-			if (this.forcedClosedLocation != null) {
-				messageBuf.append("\n\n");
-				messageBuf
-						.append(" at (stack trace):\n");
-				messageBuf.append(Util
-						.stackTraceToString(this.forcedClosedLocation));
-			}
-
-			if (this.forceClosedReason != null) {
-				if (this.forcedClosedLocation != null) {
-					messageBuf.append("\n\nDue ");
-				} else {
-					messageBuf.append("due ");
-				}
-				
-				messageBuf.append("to underlying exception/error:\n");
-				messageBuf.append(Util
-						.stackTraceToString(this.forceClosedReason));
-			}
-
-			throw SQLError.createSQLException(messageBuf.toString(),
-					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
-		}
-	}
-
 	/**
-	 * If useUnicode flag is set and explicit client character encoding isn't
-	 * specified then assign encoding from server if any.
+	 * Prepares a statement on the client, using client-side emulation 
+	 * (irregardless of the configuration property 'useServerPrepStmts') 
+	 * with the same semantics as the java.sql.Connection.prepareStatement() 
+	 * method with the same argument types.
 	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
+	 * @see java.sql.Connection#prepareStatement(String)
 	 */
-	private void checkServerEncoding() throws SQLException {
-		if (getUseUnicode() && (getEncoding() != null)) {
-			// spec'd by client, don't map
-			return;
-		}
+	public abstract java.sql.PreparedStatement clientPrepareStatement(String sql)
+			throws SQLException;
 
-		String serverEncoding = (String) this.serverVariables
-				.get("character_set");
-
-		if (serverEncoding == null) {
-			// must be 4.1.1 or newer?
-			serverEncoding = (String) this.serverVariables
-					.get("character_set_server");
-		}
-
-		String mappedServerEncoding = null;
-
-		if (serverEncoding != null) {
-			mappedServerEncoding = CharsetMapping
-					.getJavaEncodingForMysqlEncoding(serverEncoding
-							.toUpperCase(Locale.ENGLISH), this);
-		}
-
-		//
-		// First check if we can do the encoding ourselves
-		//
-		if (!getUseUnicode() && (mappedServerEncoding != null)) {
-			SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding);
-
-			if (converter != null) { // we know how to convert this ourselves
-				setUseUnicode(true); // force the issue
-				setEncoding(mappedServerEncoding);
-
-				return;
-			}
-		}
-
-		//
-		// Now, try and find a Java I/O converter that can do
-		// the encoding for us
-		//
-		if (serverEncoding != null) {
-			if (mappedServerEncoding == null) {
-				// We don't have a mapping for it, so try
-				// and canonicalize the name....
-				if (Character.isLowerCase(serverEncoding.charAt(0))) {
-					char[] ach = serverEncoding.toCharArray();
-					ach[0] = Character.toUpperCase(serverEncoding.charAt(0));
-					setEncoding(new String(ach));
-				}
-			}
-
-			if (mappedServerEncoding == null) {
-				throw SQLError.createSQLException("Unknown character encoding on server '"
-						+ serverEncoding
-						+ "', use 'characterEncoding=' property "
-						+ " to provide correct mapping",
-						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
-			}
-
-			//
-			// Attempt to use the encoding, and bail out if it
-			// can't be used
-			//
-			try {
-				"abc".getBytes(mappedServerEncoding);
-				setEncoding(mappedServerEncoding);
-				setUseUnicode(true);
-			} catch (UnsupportedEncodingException UE) {
-				throw SQLError.createSQLException(
-						"The driver can not map the character encoding '"
-								+ getEncoding()
-								+ "' that your server is using "
-								+ "to a character encoding your JVM understands. You "
-								+ "can specify this mapping manually by adding \"useUnicode=true\" "
-								+ "as well as \"characterEncoding=[an_encoding_your_jvm_understands]\" "
-								+ "to your JDBC URL.", "0S100");
-			}
-		}
-	}
-
 	/**
-	 * Set transaction isolation level to the value received from server if any.
-	 * Is called by connectionInit(...)
+	 * Prepares a statement on the client, using client-side emulation 
+	 * (irregardless of the configuration property 'useServerPrepStmts') 
+	 * with the same semantics as the java.sql.Connection.prepareStatement() 
+	 * method with the same argument types.
 	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
+	 * @see java.sql.Connection#prepareStatement(String, int)
 	 */
-	private void checkTransactionIsolationLevel() throws SQLException {
-		String txIsolationName = null;
+	public abstract java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException;
 
-		if (versionMeetsMinimum(4, 0, 3)) {
-			txIsolationName = "tx_isolation";
-		} else {
-			txIsolationName = "transaction_isolation";
-		}
-
-		String s = (String) this.serverVariables.get(txIsolationName);
-
-		if (s != null) {
-			Integer intTI = (Integer) mapTransIsolationNameToValue.get(s);
-
-			if (intTI != null) {
-				this.isolationLevel = intTI.intValue();
-			}
-		}
-	}
-
 	/**
-	 * Destroys this connection and any underlying resources
+	 * Prepares a statement on the client, using client-side emulation 
+	 * (irregardless of the configuration property 'useServerPrepStmts') 
+	 * with the same semantics as the java.sql.Connection.prepareStatement() 
+	 * method with the same argument types.
 	 * 
-	 * @param fromWhere
-	 *            DOCUMENT ME!
-	 * @param whyCleanedUp
-	 *            DOCUMENT ME!
+	 * @see java.sql.Connection#prepareStatement(String, int, int)
 	 */
-	private void cleanup(Throwable whyCleanedUp) {
-		try {
-			if ((this.io != null) && !isClosed()) {
-				realClose(false, false, false, whyCleanedUp);
-			} else if (this.io != null) {
-				this.io.forceClose();
-			}
-		} catch (SQLException sqlEx) {
-			// ignore, we're going away.
-			;
-		}
+	public abstract java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException;
 
-		this.isClosed = true;
-	}
-
 	/**
-	 * After this call, getWarnings returns null until a new warning is reported
-	 * for this connection.
+	 * Prepares a statement on the client, using client-side emulation 
+	 * (irregardless of the configuration property 'useServerPrepStmts') 
+	 * with the same semantics as the java.sql.Connection.prepareStatement() 
+	 * method with the same argument types.
 	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * @see java.sql.Connection#prepareStatement(String, int[])
 	 */
-	public void clearWarnings() throws SQLException {
-		// firstWarning = null;
-	}
+	public abstract java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException;
 
 	/**
-	 * DOCUMENT ME!
+	 * Prepares a statement on the client, using client-side emulation 
+	 * (irregardless of the configuration property 'useServerPrepStmts') 
+	 * with the same semantics as the java.sql.Connection.prepareStatement() 
+	 * method with the same argument types.
 	 * 
-	 * @param sql
-	 *            DOCUMENT ME!
-	 * @return DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
+	 * @see java.sql.Connection#prepareStatement(String, int, int, int)
 	 */
-	public PreparedStatement clientPrepareStatement(String sql)
-			throws SQLException {
-		return clientPrepareStatement(sql,
-				java.sql.ResultSet.TYPE_SCROLL_SENSITIVE,
-				java.sql.ResultSet.CONCUR_READ_ONLY);
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @param sql
-	 *            DOCUMENT ME!
-	 * @param resultSetType
-	 *            DOCUMENT ME!
-	 * @param resultSetConcurrency
-	 *            DOCUMENT ME!
-	 * @return DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	public PreparedStatement clientPrepareStatement(String sql,
-			int resultSetType, int resultSetConcurrency) throws SQLException {
-		return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
-	}
+	public abstract java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException;
 	
-	protected PreparedStatement clientPrepareStatement(String sql,
-			int resultSetType, int resultSetConcurrency, 
-			boolean processEscapeCodesIfNeeded) throws SQLException {
-		checkClosed();
-
-		String nativeSql = processEscapeCodesIfNeeded && getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
-		
-		PreparedStatement pStmt = null;
-
-		if (getCachePreparedStatements()) {
-			synchronized (this.cachedPreparedStatementParams) {
-				PreparedStatement.ParseInfo pStmtInfo = (PreparedStatement.ParseInfo) this.cachedPreparedStatementParams
-						.get(nativeSql);
-	
-				if (pStmtInfo == null) {
-					pStmt = new com.mysql.jdbc.PreparedStatement(this, nativeSql,
-							this.database);
-	
-					PreparedStatement.ParseInfo parseInfo = pStmt.getParseInfo();
-	
-					if (parseInfo.statementLength < getPreparedStatementCacheSqlLimit()) {
-						if (this.cachedPreparedStatementParams.size() >= getPreparedStatementCacheSize()) {
-							Iterator oldestIter = this.cachedPreparedStatementParams
-									.keySet().iterator();
-							long lruTime = Long.MAX_VALUE;
-							String oldestSql = null;
-	
-							while (oldestIter.hasNext()) {
-								String sqlKey = (String) oldestIter.next();
-								PreparedStatement.ParseInfo lruInfo = (PreparedStatement.ParseInfo) this.cachedPreparedStatementParams
-										.get(sqlKey);
-	
-								if (lruInfo.lastUsed < lruTime) {
-									lruTime = lruInfo.lastUsed;
-									oldestSql = sqlKey;
-								}
-							}
-	
-							if (oldestSql != null) {
-								this.cachedPreparedStatementParams
-										.remove(oldestSql);
-							}
-						}
-	
-						this.cachedPreparedStatementParams.put(nativeSql, pStmt
-								.getParseInfo());
-					}
-				} else {
-					pStmtInfo.lastUsed = System.currentTimeMillis();
-					pStmt = new com.mysql.jdbc.PreparedStatement(this, nativeSql,
-							this.database, pStmtInfo);
-				}
-			}
-		} else {
-			pStmt = new com.mysql.jdbc.PreparedStatement(this, nativeSql,
-					this.database);
-		}
-
-		pStmt.setResultSetType(resultSetType);
-		pStmt.setResultSetConcurrency(resultSetConcurrency);
-
-		return pStmt;
-	}
-
 	/**
-	 * In some cases, it is desirable to immediately release a Connection's
-	 * database and JDBC resources instead of waiting for them to be
-	 * automatically released (cant think why off the top of my head) <B>Note:</B>
-	 * A Connection is automatically closed when it is garbage collected.
-	 * Certain fatal errors also result in a closed connection.
+	 * Prepares a statement on the client, using client-side emulation 
+	 * (irregardless of the configuration property 'useServerPrepStmts') 
+	 * with the same semantics as the java.sql.Connection.prepareStatement() 
+	 * method with the same argument types.
 	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * @see java.sql.Connection#prepareStatement(String, String[])
 	 */
-	public void close() throws SQLException {
-		realClose(true, true, false, null);
-	}
+	public abstract java.sql.PreparedStatement clientPrepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException;
 
 	/**
-	 * Closes all currently open statements.
-	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
+	 * Returns the number of statements active on this connection, which
+	 * haven't been .close()d.
 	 */
-	private void closeAllOpenStatements() throws SQLException {
-		SQLException postponedException = null;
+	public abstract int getActiveStatementCount();
 
-		if (this.openStatements != null) {
-			List currentlyOpenStatements = new ArrayList(); // we need this to
-			// avoid
-			// ConcurrentModificationEx
-
-			for (Iterator iter = this.openStatements.keySet().iterator(); iter
-					.hasNext();) {
-				currentlyOpenStatements.add(iter.next());
-			}
-
-			int numStmts = currentlyOpenStatements.size();
-
-			for (int i = 0; i < numStmts; i++) {
-				Statement stmt = (Statement) currentlyOpenStatements.get(i);
-
-				try {
-					stmt.realClose(false, true);
-				} catch (SQLException sqlEx) {
-					postponedException = sqlEx; // throw it later, cleanup all
-					// statements first
-				}
-			}
-
-			if (postponedException != null) {
-				throw postponedException;
-			}
-		}
-	}
-
-	private void closeStatement(java.sql.Statement stmt) {
-		if (stmt != null) {
-			try {
-				stmt.close();
-			} catch (SQLException sqlEx) {
-				; // ignore
-			}
-
-			stmt = null;
-		}
-	}
-
-	// --------------------------JDBC 2.0-----------------------------
-
 	/**
-	 * The method commit() makes all changes made since the previous
-	 * commit/rollback permanent and releases any database locks currently held
-	 * by the Connection. This method should only be used when auto-commit has
-	 * been disabled.
-	 * <p>
-	 * <b>Note:</b> MySQL does not support transactions, so this method is a
-	 * no-op.
-	 * </p>
+	 * Reports how long this connection has been idle. 
+	 * This time (reported in milliseconds) is updated once a query has 
+	 * completed.
 	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 * @see setAutoCommit
-	 */
-	public void commit() throws SQLException {
-		synchronized (getMutex()) {
-			checkClosed();
-	
-			try {
-				// no-op if _relaxAutoCommit == true
-				if (this.autoCommit && !getRelaxAutoCommit()) {
-					throw SQLError.createSQLException("Can't call commit when autocommit=true");
-				} else if (this.transactionsSupported) {
-					execSQL(null, "commit", -1, null,
-							java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.database, true,
-							false);
-				}
-			} catch (SQLException sqlException) {
-				if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
-						.equals(sqlException.getSQLState())) {
-					throw SQLError.createSQLException(
-							"Communications link failure during commit(). Transaction resolution unknown.",
-							SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN);
-				}
-	
-				throw sqlException;
-			} finally {
-				this.needsPing = this.getReconnectAtTxEnd();
-			}
-	
-			return;
-		}
-	}
-
-	/**
-	 * Configures client-side properties for character set information.
-	 * 
-	 * @throws SQLException
-	 *             if unable to configure the specified character set.
-	 */
-	private void configureCharsetProperties() throws SQLException {
-		if (getEncoding() != null) {
-			// Attempt to use the encoding, and bail out if it
-			// can't be used
-			try {
-				String testString = "abc";
-				testString.getBytes(getEncoding());
-			} catch (UnsupportedEncodingException UE) {
-				// Try the MySQL character encoding, then....
-				String oldEncoding = getEncoding();
-
-				setEncoding(CharsetMapping.getJavaEncodingForMysqlEncoding(
-						oldEncoding, this));
-
-				if (getEncoding() == null) {
-					throw SQLError.createSQLException(
-							"Java does not support the MySQL character encoding "
-									+ " " + "encoding '" + oldEncoding + "'.",
-							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
-				}
-
-				try {
-					String testString = "abc";
-					testString.getBytes(getEncoding());
-				} catch (UnsupportedEncodingException encodingEx) {
-					throw SQLError.createSQLException("Unsupported character "
-							+ "encoding '" + getEncoding() + "'.",
-							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
-				}
-			}
-		}
-	}
-
-	/**
-	 * Sets up client character set for MySQL-4.1 and newer if the user This
-	 * must be done before any further communication with the server!
-	 * 
-	 * @return true if this routine actually configured the client character
-	 *         set, or false if the driver needs to use 'older' methods to
-	 *         detect the character set, as it is connected to a MySQL server
-	 *         older than 4.1.0
-	 * @throws SQLException
-	 *             if an exception happens while sending 'SET NAMES' to the
-	 *             server, or the server sends character set information that
-	 *             the client doesn't know about.
-	 */
-	private boolean configureClientCharacterSet() throws SQLException {
-		String realJavaEncoding = getEncoding();
-		boolean characterSetAlreadyConfigured = false;
-
-		try {
-			if (versionMeetsMinimum(4, 1, 0)) {
-				characterSetAlreadyConfigured = true;
-
-				setUseUnicode(true);
-
-				configureCharsetProperties();
-				realJavaEncoding = getEncoding(); // we need to do this again
-				// to grab this for
-				// versions > 4.1.0
-
-				try {
-					String serverEncodingToSet = 
-						CharsetMapping.INDEX_TO_CHARSET[this.io.serverCharsetIndex];
-					
-					if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) {
-						throw SQLError.createSQLException(
-								"Unknown initial character set index '"
-										+ this.io.serverCharsetIndex
-										+ "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
-								SQLError.SQL_STATE_GENERAL_ERROR);
-					}
-					
-					if (versionMeetsMinimum(4, 1, 0) && 
-							"ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) {
-						serverEncodingToSet = "Cp1252";
-					}
-					
-					setEncoding(serverEncodingToSet);
-				} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
-					if (realJavaEncoding != null) {
-						// user knows best, try it
-						setEncoding(realJavaEncoding);
-					} else {
-						throw SQLError.createSQLException(
-								"Unknown initial character set index '"
-										+ this.io.serverCharsetIndex
-										+ "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
-								SQLError.SQL_STATE_GENERAL_ERROR);
-					}
-				}
-
-				if (getEncoding() == null) {
-					// punt?
-					setEncoding("ISO8859_1");
-				}
-
-				//
-				// Has the user has 'forced' the character encoding via
-				// driver properties?
-				//
-				if (getUseUnicode()) {
-					if (realJavaEncoding != null) {
-
-						//
-						// Now, inform the server what character set we
-						// will be using from now-on...
-						//
-						if (realJavaEncoding.equalsIgnoreCase("UTF-8")
-								|| realJavaEncoding.equalsIgnoreCase("UTF8")) {
-							// charset names are case-sensitive
-
-							if (!getUseOldUTF8Behavior()) {
-								execSQL(null, "SET NAMES utf8", -1, null,
-										java.sql.ResultSet.TYPE_FORWARD_ONLY,
-										java.sql.ResultSet.CONCUR_READ_ONLY,
-										false, this.database, true, false);
-							}
-
-							setEncoding(realJavaEncoding);
-						} /* not utf-8 */else {
-							String mysqlEncodingName = CharsetMapping
-									.getMysqlEncodingForJavaEncoding(
-											realJavaEncoding
-													.toUpperCase(Locale.ENGLISH),
-											this);
-
-							/*
-							 * if ("koi8_ru".equals(mysqlEncodingName)) { //
-							 * This has a _different_ name in 4.1...
-							 * mysqlEncodingName = "ko18r"; } else if
-							 * ("euc_kr".equals(mysqlEncodingName)) { //
-							 * Different name in 4.1 mysqlEncodingName =
-							 * "euckr"; }
-							 */
-
-							if (mysqlEncodingName != null) {
-								execSQL(null, "SET NAMES " + mysqlEncodingName,
-										-1, null,
-										java.sql.ResultSet.TYPE_FORWARD_ONLY,
-										java.sql.ResultSet.CONCUR_READ_ONLY,
-										false, this.database, true, false);
-							}
-
-							// Switch driver's encoding now, since the server
-							// knows what we're sending...
-							//
-							setEncoding(realJavaEncoding);
-						}
-					} else if (getEncoding() != null) {
-						// Tell the server we'll use the server default charset
-						// to send our
-						// queries from now on....
-						String mysqlEncodingName = CharsetMapping
-								.getMysqlEncodingForJavaEncoding(getEncoding()
-										.toUpperCase(Locale.ENGLISH), this);
-
-						execSQL(null, "SET NAMES " + mysqlEncodingName, -1,
-								null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
-								java.sql.ResultSet.CONCUR_READ_ONLY, false,
-								this.database, true, false);
-
-						realJavaEncoding = getEncoding();
-					}
-
-				}
-
-				//
-				// We know how to deal with any charset coming back from
-				// the database, so tell the server not to do conversion
-				// if the user hasn't 'forced' a result-set character set
-				//
-
-				if (getCharacterSetResults() == null) {
-					execSQL(null, "SET character_set_results = NULL", -1, null,
-							java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.database, true, 
-							false);
-				} else {
-					String charsetResults = getCharacterSetResults();
-					String mysqlEncodingName = null;
-
-					if ("UTF-8".equalsIgnoreCase(charsetResults)
-							|| "UTF8".equalsIgnoreCase(charsetResults)) {
-						mysqlEncodingName = "utf8";
-					} else {
-						mysqlEncodingName = CharsetMapping
-								.getMysqlEncodingForJavaEncoding(charsetResults
-										.toUpperCase(Locale.ENGLISH), this);
-					}
-
-					StringBuffer setBuf = new StringBuffer(
-							"SET character_set_results = ".length()
-									+ mysqlEncodingName.length());
-					setBuf.append("SET character_set_results = ").append(
-							mysqlEncodingName);
-
-					execSQL(null, setBuf.toString(), -1, null,
-							java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.database, true, false);
-				}
-
-				if (getConnectionCollation() != null) {
-					StringBuffer setBuf = new StringBuffer(
-							"SET collation_connection = ".length()
-									+ getConnectionCollation().length());
-					setBuf.append("SET collation_connection = ").append(
-							getConnectionCollation());
-
-					execSQL(null, setBuf.toString(), -1, null,
-							java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.database, true, false);
-				}
-			} else {
-				// Use what the server has specified
-				realJavaEncoding = getEncoding(); // so we don't get
-				// swapped out in the finally
-				// block....
-			}
-		} finally {
-			// Failsafe, make sure that the driver's notion of character
-			// encoding matches what the user has specified.
-			//
-			setEncoding(realJavaEncoding);
-		}
-
-		return characterSetAlreadyConfigured;
-	}
-
-	/**
-	 * Configures the client's timezone if required.
-	 * 
-	 * @throws SQLException
-	 *             if the timezone the server is configured to use can't be
-	 *             mapped to a Java timezone.
-	 */
-	private void configureTimezone() throws SQLException {
-		String configuredTimeZoneOnServer = (String) this.serverVariables
-				.get("timezone");
-
-		if (configuredTimeZoneOnServer == null) {
-			configuredTimeZoneOnServer = (String) this.serverVariables
-					.get("time_zone");
-
-			if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
-				configuredTimeZoneOnServer = (String) this.serverVariables
-						.get("system_time_zone");
-			}
-		}
-
-		if (getUseTimezone() && configuredTimeZoneOnServer != null) {
-			// user can specify/override as property
-			String canoncicalTimezone = getServerTimezone();
-
-			if ((canoncicalTimezone == null)
-					|| (canoncicalTimezone.length() == 0)) {
-				String serverTimezoneStr = configuredTimeZoneOnServer;
-
-				try {
-					canoncicalTimezone = TimeUtil
-							.getCanoncialTimezone(serverTimezoneStr);
-
-					if (canoncicalTimezone == null) {
-						throw SQLError.createSQLException("Can't map timezone '"
-								+ serverTimezoneStr + "' to "
-								+ " canonical timezone.",
-								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-					}
-				} catch (IllegalArgumentException iae) {
-					throw SQLError.createSQLException(iae.getMessage(),
-							SQLError.SQL_STATE_GENERAL_ERROR);
-				}
-			}
-
-			this.serverTimezoneTZ = TimeZone.getTimeZone(canoncicalTimezone);
-
-			//
-			// The Calendar class has the behavior of mapping
-			// unknown timezones to 'GMT' instead of throwing an
-			// exception, so we must check for this...
-			//
-			if (!canoncicalTimezone.equalsIgnoreCase("GMT")
-					&& this.serverTimezoneTZ.getID().equals("GMT")) {
-				throw SQLError.createSQLException("No timezone mapping entry for '"
-						+ canoncicalTimezone + "'",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-
-			if ("GMT".equalsIgnoreCase(this.serverTimezoneTZ.getID())) {
-				this.isServerTzUTC = true;
-			} else {
-				this.isServerTzUTC = false;
-			}
-		}
-	}
-
-	private void createInitialHistogram(long[] breakpoints,
-			long lowerBound, long upperBound) {
-
-		double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25;
-
-		if (bucketSize < 1) {
-			bucketSize = 1;
-		}
-
-		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
-			breakpoints[i] = lowerBound;
-			lowerBound += bucketSize;
-		}
-	}
-
-	/**
-	 * Creates an IO channel to the server
-	 * 
-	 * @param isForReconnect
-	 *            is this request for a re-connect
-	 * @return a new MysqlIO instance connected to a server
-	 * @throws SQLException
-	 *             if a database access error occurs
-	 * @throws CommunicationsException
-	 *             DOCUMENT ME!
-	 */
-	protected com.mysql.jdbc.MysqlIO createNewIO(boolean isForReconnect)
-			throws SQLException {
-		MysqlIO newIo = null;
-
-		Properties mergedProps = new Properties();
-
-		mergedProps = exposeAsProperties(this.props);
-
-		long queriesIssuedFailedOverCopy = this.queriesIssuedFailedOver;
-		this.queriesIssuedFailedOver = 0;
-
-		try {
-			if (!getHighAvailability() && !this.failedOver) {
-				boolean connectionGood = false;
-				Exception connectionNotEstablishedBecause = null;
-				
-				int hostIndex = 0;
-
-				//
-				// TODO: Eventually, when there's enough metadata
-				// on the server to support it, we should come up
-				// with a smarter way to pick what server to connect
-				// to...perhaps even making it 'pluggable'
-				//
-				if (getRoundRobinLoadBalance()) {
-					hostIndex = getNextRoundRobinHostIndex(getURL(),
-							this.hostList);
-				}
-
-				for (; hostIndex < this.hostListSize; hostIndex++) {
-
-					if (hostIndex == 0) {
-						this.hasTriedMasterFlag = true;
-					}
-					
-					try {
-						String newHostPortPair = (String) this.hostList
-								.get(hostIndex);
-
-						int newPort = 3306;
-
-						String[] hostPortPair = NonRegisteringDriver
-								.parseHostPortPair(newHostPortPair);
-						String newHost = hostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];
-
-						if (newHost == null || newHost.trim().length() == 0) {
-							newHost = "localhost";
-						}
-
-						if (hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
-							try {
-								newPort = Integer
-										.parseInt(hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
-							} catch (NumberFormatException nfe) {
-								throw SQLError.createSQLException(
-										"Illegal connection port value '"
-												+ hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]
-												+ "'",
-										SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
-							}
-						}
-
-						this.io = new MysqlIO(newHost, newPort, mergedProps,
-								getSocketFactoryClassName(), this,
-								getSocketTimeout());
-	
-						this.io.doHandshake(this.user, this.password,
-								this.database);
-						this.isClosed = false;
-
-						// save state from old connection
-						boolean oldAutoCommit = getAutoCommit();
-						int oldIsolationLevel = this.isolationLevel;
-						boolean oldReadOnly = isReadOnly();
-						String oldCatalog = getCatalog();
-
-						// Server properties might be different
-						// from previous connection, so initialize
-						// again...
-						initializePropsFromServer();
-
-						if (isForReconnect) {
-							// Restore state from old connection
-							setAutoCommit(oldAutoCommit);
-
-							if (this.hasIsolationLevels) {
-								setTransactionIsolation(oldIsolationLevel);
-							}
-
-							setCatalog(oldCatalog);
-						}
-
-						if (hostIndex != 0) {
-							setFailedOverState();
-							queriesIssuedFailedOverCopy = 0;
-						} else {
-							this.failedOver = false;
-							queriesIssuedFailedOverCopy = 0;
-
-							if (this.hostListSize > 1) {
-								setReadOnly(false);
-							} else {
-								setReadOnly(oldReadOnly);
-							}
-						}
-
-						connectionGood = true;
-						
-						break; // low-level connection succeeded
-					} catch (Exception EEE) {
-						if (this.io != null) {
-							this.io.forceClose();
-						}
-
-						connectionNotEstablishedBecause = EEE;
-						
-						connectionGood = false;
-						
-						if (EEE instanceof SQLException) {
-							SQLException sqlEx = (SQLException)EEE;
-						
-							String sqlState = sqlEx.getSQLState();
-	
-							// If this isn't a communications failure, it will probably never succeed, so
-							// give up right here and now ....
-							if ((sqlState == null)
-									|| !sqlState
-											.equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) {
-								throw sqlEx;
-							}
-						}
-
-						// Check next host, it might be up...
-						if (getRoundRobinLoadBalance()) {
-							hostIndex = getNextRoundRobinHostIndex(getURL(),
-									this.hostList) - 1 /* incremented by for loop next time around */;
-						} else if ((this.hostListSize - 1) == hostIndex) {
-							throw new CommunicationsException(this,
-									(this.io != null) ? this.io
-											.getLastPacketSentTimeMs() : 0,
-											EEE);
-						}
-					}
-				}
-				
-				if (!connectionGood) {
-					// We've really failed!
-					throw SQLError.createSQLException(
-							"Could not create connection to database server due to underlying exception: '"
-									+ connectionNotEstablishedBecause
-									+ "'."
-									+ (getParanoid() ? ""
-											: Util
-													.stackTraceToString(connectionNotEstablishedBecause)),
-							SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
-				}
-			} else {
-				double timeout = getInitialTimeout();
-				boolean connectionGood = false;
-
-				Exception connectionException = null;
-
-				int hostIndex = 0;
-
-				if (getRoundRobinLoadBalance()) {
-					hostIndex = getNextRoundRobinHostIndex(getURL(),
-							this.hostList);
-				}
-
-				for (; (hostIndex < this.hostListSize) && !connectionGood; hostIndex++) {
-					if (hostIndex == 0) {
-						this.hasTriedMasterFlag = true;
-					}
-					
-					if (this.preferSlaveDuringFailover && hostIndex == 0) {
-						hostIndex++;
-					}
-
-					for (int attemptCount = 0; (attemptCount < getMaxReconnects())
-							&& !connectionGood; attemptCount++) {
-						try {
-							if (this.io != null) {
-								this.io.forceClose();
-							}
-
-							String newHostPortPair = (String) this.hostList
-									.get(hostIndex);
-
-							int newPort = 3306;
-
-							String[] hostPortPair = NonRegisteringDriver
-									.parseHostPortPair(newHostPortPair);
-							String newHost = hostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];
-
-							if (newHost == null || newHost.trim().length() == 0) {
-								newHost = "localhost";
-							}
-
-							if (hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
-								try {
-									newPort = Integer
-											.parseInt(hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
-								} catch (NumberFormatException nfe) {
-									throw SQLError.createSQLException(
-											"Illegal connection port value '"
-													+ hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]
-													+ "'",
-											SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
-								}
-							}
-
-							this.io = new MysqlIO(newHost, newPort,
-									mergedProps, getSocketFactoryClassName(),
-									this, getSocketTimeout());
-							this.io.doHandshake(this.user, this.password,
-									this.database);
-
-							pingInternal(false);
-							this.isClosed = false;
-
-							// save state from old connection
-							boolean oldAutoCommit = getAutoCommit();
-							int oldIsolationLevel = this.isolationLevel;
-							boolean oldReadOnly = isReadOnly();
-							String oldCatalog = getCatalog();
-
-							// Server properties might be different
-							// from previous connection, so initialize
-							// again...
-							initializePropsFromServer();
-
-							if (isForReconnect) {
-								// Restore state from old connection
-								setAutoCommit(oldAutoCommit);
-
-								if (this.hasIsolationLevels) {
-									setTransactionIsolation(oldIsolationLevel);
-								}
-
-								setCatalog(oldCatalog);
-							}
-
-							connectionGood = true;
-
-							if (hostIndex != 0) {
-								setFailedOverState();
-								queriesIssuedFailedOverCopy = 0;
-							} else {
-								this.failedOver = false;
-								queriesIssuedFailedOverCopy = 0;
-
-								if (this.hostListSize > 1) {
-									setReadOnly(false);
-								} else {
-									setReadOnly(oldReadOnly);
-								}
-							}
-
-							break;
-						} catch (Exception EEE) {
-							connectionException = EEE;
-							connectionGood = false;
-							
-							// Check next host, it might be up...
-							if (getRoundRobinLoadBalance()) {
-								hostIndex = getNextRoundRobinHostIndex(getURL(),
-										this.hostList) - 1 /* incremented by for loop next time around */;
-							}
-						}
-
-						if (connectionGood) {
-							break;
-						}
-
-						if (attemptCount > 0) {
-							try {
-								Thread.sleep((long) timeout * 1000);
-							} catch (InterruptedException IE) {
-								;
-							}
-						}
-					} // end attempts for a single host
-				} // end iterator for list of hosts
-
-				if (!connectionGood) {
-					// We've really failed!
-					throw SQLError.createSQLException(
-							"Server connection failure during transaction. Due to underlying exception: '"
-									+ connectionException
-									+ "'."
-									+ (getParanoid() ? ""
-											: Util
-													.stackTraceToString(connectionException))
-									+ "\nAttempted reconnect "
-									+ getMaxReconnects() + " times. Giving up.",
-							SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
-				}
-			}
-
-			if (getParanoid() && !getHighAvailability()
-					&& (this.hostListSize <= 1)) {
-				this.password = null;
-				this.user = null;
-			}
-
-			if (isForReconnect) {
-				//
-				// Retrieve any 'lost' prepared statements if re-connecting
-				//
-				Iterator statementIter = this.openStatements.values()
-						.iterator();
-
-				//
-				// We build a list of these outside the map of open statements,
-				// because
-				// in the process of re-preparing, we might end up having to
-				// close
-				// a prepared statement, thus removing it from the map, and
-				// generating
-				// a ConcurrentModificationException
-				//
-				Stack serverPreparedStatements = null;
-
-				while (statementIter.hasNext()) {
-					Object statementObj = statementIter.next();
-
-					if (statementObj instanceof ServerPreparedStatement) {
-						if (serverPreparedStatements == null) {
-							serverPreparedStatements = new Stack();
-						}
-
-						serverPreparedStatements.add(statementObj);
-					}
-				}
-
-				if (serverPreparedStatements != null) {
-					while (!serverPreparedStatements.isEmpty()) {
-						((ServerPreparedStatement) serverPreparedStatements
-								.pop()).rePrepare();
-					}
-				}
-			}
-
-			return newIo;
-		} finally {
-			this.queriesIssuedFailedOver = queriesIssuedFailedOverCopy;
-		}
-	}
-
-	private void createPreparedStatementCaches() {
-		int cacheSize = getPreparedStatementCacheSize();
-		
-		this.cachedPreparedStatementParams = new HashMap(cacheSize);
-		
-		this.serverSideStatementCheckCache = new LRUCache(cacheSize);
-		
-		this.serverSideStatementCache = new LRUCache(cacheSize) {
-			protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
-				if (this.maxElements <= 1) {
-					return false;
-				}
-				
-				boolean removeIt = super.removeEldestEntry(eldest);
-				
-				if (removeIt) {
-					ServerPreparedStatement ps = 
-						(ServerPreparedStatement)eldest.getValue();
-					ps.isCached = false;
-					ps.setClosed(false);
-					
-					try {
-						ps.close();
-					} catch (SQLException sqlEx) {
-						// punt
-					}
-				}
-				
-				return removeIt;
-			}
-		};
-	}
-
-	/**
-	 * SQL statements without parameters are normally executed using Statement
-	 * objects. If the same SQL statement is executed many times, it is more
-	 * efficient to use a PreparedStatement
-	 * 
-	 * @return a new Statement object
-	 * @throws SQLException
-	 *             passed through from the constructor
-	 */
-	public java.sql.Statement createStatement() throws SQLException {
-		return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
-				java.sql.ResultSet.CONCUR_READ_ONLY);
-	}
-
-	/**
-	 * JDBC 2.0 Same as createStatement() above, but allows the default result
-	 * set type and result set concurrency type to be overridden.
-	 * 
-	 * @param resultSetType
-	 *            a result set type, see ResultSet.TYPE_XXX
-	 * @param resultSetConcurrency
-	 *            a concurrency type, see ResultSet.CONCUR_XXX
-	 * @return a new Statement object
-	 * @exception SQLException
-	 *                if a database-access error occurs.
-	 */
-	public java.sql.Statement createStatement(int resultSetType,
-			int resultSetConcurrency) throws SQLException {
-		checkClosed();
-
-		Statement stmt = new com.mysql.jdbc.Statement(this, this.database);
-		stmt.setResultSetType(resultSetType);
-		stmt.setResultSetConcurrency(resultSetConcurrency);
-
-		return stmt;
-	}
-
-	/**
-	 * @see Connection#createStatement(int, int, int)
-	 */
-	public java.sql.Statement createStatement(int resultSetType,
-			int resultSetConcurrency, int resultSetHoldability)
-			throws SQLException {
-		if (getPedantic()) {
-			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
-				throw SQLError.createSQLException(
-						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-		}
-
-		return createStatement(resultSetType, resultSetConcurrency);
-	}
-
-	protected void dumpTestcaseQuery(String query) {
-		System.err.println(query);
-	}
-
-	protected Connection duplicate() throws SQLException {
-		return new Connection(	this.origHostToConnectTo, 
-				this.origPortToConnectTo,
-				this.props,
-				this.origDatabaseToConnectTo,
-				this.myURL);
-	}
-
-	/**
-	 * Send a query to the server. Returns one of the ResultSet objects. This is
-	 * synchronized, so Statement's queries will be serialized.
-	 * 
-	 * @param callingStatement
-	 *            DOCUMENT ME!
-	 * @param sql
-	 *            the SQL statement to be executed
-	 * @param maxRows
-	 *            DOCUMENT ME!
-	 * @param packet
-	 *            DOCUMENT ME!
-	 * @param resultSetType
-	 *            DOCUMENT ME!
-	 * @param resultSetConcurrency
-	 *            DOCUMENT ME!
-	 * @param streamResults
-	 *            DOCUMENT ME!
-	 * @param queryIsSelectOnly
-	 *            DOCUMENT ME!
-	 * @param catalog
-	 *            DOCUMENT ME!
-	 * @param unpackFields
-	 *            DOCUMENT ME!
-	 * @return a ResultSet holding the results
-	 * @exception SQLException
-	 *                if a database error occurs
-	 */
-
-	// ResultSet execSQL(Statement callingStatement, String sql,
-	// int maxRowsToRetreive, String catalog) throws SQLException {
-	// return execSQL(callingStatement, sql, maxRowsToRetreive, null,
-	// java.sql.ResultSet.TYPE_FORWARD_ONLY,
-	// java.sql.ResultSet.CONCUR_READ_ONLY, catalog);
-	// }
-	// ResultSet execSQL(Statement callingStatement, String sql, int maxRows,
-	// int resultSetType, int resultSetConcurrency, boolean streamResults,
-	// boolean queryIsSelectOnly, String catalog, boolean unpackFields) throws
-	// SQLException {
-	// return execSQL(callingStatement, sql, maxRows, null, resultSetType,
-	// resultSetConcurrency, streamResults, queryIsSelectOnly, catalog,
-	// unpackFields);
-	// }
-	ResultSet execSQL(Statement callingStatement, String sql, int maxRows,
-			Buffer packet, int resultSetType, int resultSetConcurrency,
-			boolean streamResults, String catalog,
-			boolean unpackFields) throws SQLException {
-		return execSQL(callingStatement, sql, maxRows, packet, resultSetType,
-				resultSetConcurrency, streamResults,
-				catalog, unpackFields, false);
-	}
-
-	ResultSet execSQL(Statement callingStatement, String sql, int maxRows,
-			Buffer packet, int resultSetType, int resultSetConcurrency,
-			boolean streamResults, String catalog,
-			boolean unpackFields,
-			boolean isBatch) throws SQLException {
-		//
-		// Fall-back if the master is back online if we've
-		// issued queriesBeforeRetryMaster queries since
-		// we failed over
-		//
-		synchronized (this.mutex) {
-			long queryStartTime = 0;
-
-			int endOfQueryPacketPosition = 0;
-
-			if (packet != null) {
-				endOfQueryPacketPosition = packet.getPosition();
-			}
-
-			if (getGatherPerformanceMetrics()) {
-				queryStartTime = System.currentTimeMillis();
-			}
-
-			this.lastQueryFinishedTime = 0; // we're busy!
-
-			if (this.failedOver && this.autoCommit && !isBatch) {
-				if (shouldFallBack() && !this.executingFailoverReconnect) {
-					try {
-						this.executingFailoverReconnect = true;
-
-						createNewIO(true);
-
-						String connectedHost = this.io.getHost();
-
-						if ((connectedHost != null)
-								&& this.hostList.get(0).equals(connectedHost)) {
-							this.failedOver = false;
-							this.queriesIssuedFailedOver = 0;
-							setReadOnly(false);
-						}
-					} finally {
-						this.executingFailoverReconnect = false;
-					}
-				}
-			}
-
-			if ((getHighAvailability() || this.failedOver)
-					&& (this.autoCommit || getAutoReconnectForPools())
-					&& this.needsPing && !isBatch) {
-				try {
-					pingInternal(false);
-
-					this.needsPing = false;
-				} catch (Exception Ex) {
-					createNewIO(true);
-				}
-			}
-
-			try {
-				if (packet == null) {
-					String encoding = null;
-
-					if (getUseUnicode()) {
-						encoding = getEncoding();
-					}
-
-					return this.io.sqlQueryDirect(callingStatement, sql,
-							encoding, null, maxRows, this, resultSetType,
-							resultSetConcurrency, streamResults, catalog,
-							unpackFields);
-				}
-
-				return this.io.sqlQueryDirect(callingStatement, null, null,
-						packet, maxRows, this, resultSetType,
-						resultSetConcurrency, streamResults, catalog,
-						unpackFields);
-			} catch (java.sql.SQLException sqlE) {
-				// don't clobber SQL exceptions
-
-				if (getDumpQueriesOnException()) {
-					String extractedSql = extractSqlFromPacket(sql, packet,
-							endOfQueryPacketPosition);
-					StringBuffer messageBuf = new StringBuffer(extractedSql
-							.length() + 32);
-					messageBuf
-							.append("\n\nQuery being executed when exception was thrown:\n\n");
-					messageBuf.append(extractedSql);
-
-					sqlE = appendMessageToException(sqlE, messageBuf.toString());
-				}
-
-				if ((getHighAvailability() || this.failedOver)) {
-					this.needsPing = true;
-				} else {
-					String sqlState = sqlE.getSQLState();
-
-					if ((sqlState != null)
-							&& sqlState
-									.equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) {
-						cleanup(sqlE);
-					}
-				}
-
-				throw sqlE;
-			} catch (Exception ex) {
-				if ((getHighAvailability() || this.failedOver)) {
-					this.needsPing = true;
-				} else if (ex instanceof IOException) {
-					cleanup(ex);
-				}
-
-				String exceptionType = ex.getClass().getName();
-				String exceptionMessage = ex.getMessage();
-
-				if (!getParanoid()) {
-					exceptionMessage += "\n\nNested Stack Trace:\n";
-					exceptionMessage += Util.stackTraceToString(ex);
-				}
-
-				throw new java.sql.SQLException(
-						"Error during query: Unexpected Exception: "
-								+ exceptionType + " message given: "
-								+ exceptionMessage,
-						SQLError.SQL_STATE_GENERAL_ERROR);
-			} finally {
-				if (getMaintainTimeStats()) {
-					this.lastQueryFinishedTime = System.currentTimeMillis();
-				}
-
-				if (this.failedOver) {
-					this.queriesIssuedFailedOver++;
-				}
-
-				if (getGatherPerformanceMetrics()) {
-					long queryTime = System.currentTimeMillis()
-							- queryStartTime;
-
-					registerQueryExecutionTime(queryTime);
-				}
-			}
-		}
-	}
-
-	protected String extractSqlFromPacket(String possibleSqlQuery,
-			Buffer queryPacket, int endOfQueryPacketPosition)
-			throws SQLException {
-
-		String extractedSql = null;
-
-		if (possibleSqlQuery != null) {
-			if (possibleSqlQuery.length() > getMaxQuerySizeToLog()) {
-				StringBuffer truncatedQueryBuf = new StringBuffer(
-						possibleSqlQuery.substring(0, getMaxQuerySizeToLog()));
-				truncatedQueryBuf.append(Messages.getString("MysqlIO.25"));
-				extractedSql = truncatedQueryBuf.toString();
-			} else {
-				extractedSql = possibleSqlQuery;
-			}
-		}
-
-		if (extractedSql == null) {
-			// This is probably from a client-side prepared
-			// statement
-
-			int extractPosition = endOfQueryPacketPosition;
-
-			boolean truncated = false;
-
-			if (endOfQueryPacketPosition > getMaxQuerySizeToLog()) {
-				extractPosition = getMaxQuerySizeToLog();
-				truncated = true;
-			}
-
-			extractedSql = new String(queryPacket.getByteBuffer(), 5,
-					(extractPosition - 5));
-
-			if (truncated) {
-				extractedSql += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
-			}
-		}
-
-		return extractedSql;
-
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @throws Throwable
-	 *             DOCUMENT ME!
-	 */
-	protected void finalize() throws Throwable {
-		cleanup(null);
-	}
-
-	protected StringBuffer generateConnectionCommentBlock(StringBuffer buf) {
-		buf.append("/* conn id ");
-		buf.append(getId());
-		buf.append(" */ ");
-
-		return buf;
-	}
-
-	public int getActiveStatementCount() {
-		// Might not have one of these if
-		// not tracking open resources
-		if (this.openStatements != null) {
-			synchronized (this.openStatements) {
-				return this.openStatements.size();
-			}
-		}
-
-		return 0;
-	}
-
-	/**
-	 * Gets the current auto-commit state
-	 * 
-	 * @return Current state of auto-commit
-	 * @exception SQLException
-	 *                if an error occurs
-	 * @see setAutoCommit
-	 */
-	public boolean getAutoCommit() throws SQLException {
-		return this.autoCommit;
-	}
-
-	/**
-	 * Optimization to only use one calendar per-session, or calculate it for
-	 * each call, depending on user configuration
-	 */
-	protected Calendar getCalendarInstanceForSessionOrNew() {
-		if (getDynamicCalendars()) {
-			return Calendar.getInstance();
-		}
-
-		return getSessionLockedCalendar();
-	}
-
-	/**
-	 * Return the connections current catalog name, or null if no catalog name
-	 * is set, or we dont support catalogs.
-	 * <p>
-	 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
-	 * </p>
-	 * 
-	 * @return the current catalog name or null
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public String getCatalog() throws SQLException {
-		return this.database;
-	}
-
-	/**
-	 * @return Returns the characterSetMetadata.
-	 */
-	protected String getCharacterSetMetadata() {
-		return characterSetMetadata;
-	}
-
-	/**
-	 * Returns the locally mapped instance of a charset converter (to avoid
-	 * overhead of static synchronization).
-	 * 
-	 * @param javaEncodingName
-	 *            the encoding name to retrieve
-	 * @return a character converter, or null if one couldn't be mapped.
-	 */
-	SingleByteCharsetConverter getCharsetConverter(
-			String javaEncodingName) throws SQLException {
-		if (javaEncodingName == null) {
-			return null;
-		}
-
-		if (this.usePlatformCharsetConverters) {
-			return null; // we'll use Java's built-in routines for this
-			             // they're finally fast enough
-		}
-		
-		SingleByteCharsetConverter converter = null;
-		
-		synchronized (this.charsetConverterMap) {
-			Object asObject = this.charsetConverterMap
-			.get(javaEncodingName);
-
-			if (asObject == CHARSET_CONVERTER_NOT_AVAILABLE_MARKER) {
-				return null;
-			}
-			
-			converter = (SingleByteCharsetConverter)asObject;
-			
-			if (converter == null) {
-				try {
-					converter = SingleByteCharsetConverter.getInstance(
-							javaEncodingName, this);
-
-					if (converter == null) {
-						this.charsetConverterMap.put(javaEncodingName,
-								CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
-					} else {
-						this.charsetConverterMap.put(javaEncodingName, converter);
-					}
-				} catch (UnsupportedEncodingException unsupEncEx) {
-					this.charsetConverterMap.put(javaEncodingName,
-							CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
-
-					converter = null;
-				}
-			}
-		}
-
-		return converter;
-	}
-
-	/**
-	 * Returns the Java character encoding name for the given MySQL server
-	 * charset index
-	 * 
-	 * @param charsetIndex
-	 * @return the Java character encoding name for the given MySQL server
-	 *         charset index
-	 * @throws SQLException
-	 *             if the character set index isn't known by the driver
-	 */
-	protected String getCharsetNameForIndex(int charsetIndex)
-			throws SQLException {
-		String charsetName = null;
-
-		if (getUseOldUTF8Behavior()) {
-			return getEncoding();
-		}
-
-		if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) {
-			try {
-				charsetName = this.indexToCharsetMapping[charsetIndex];
-
-				if ("sjis".equalsIgnoreCase(charsetName)) {
-					// Use our encoding so that code pages like Cp932 work
-					if (CharsetMapping.isAliasForSjis(getEncoding())) {
-						charsetName = getEncoding();
-					}
-				}
-			} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
-				throw SQLError.createSQLException(
-						"Unknown character set index for field '"
-								+ charsetIndex + "' received from server.",
-						SQLError.SQL_STATE_GENERAL_ERROR);
-			}
-
-			// Punt
-			if (charsetName == null) {
-				charsetName = getEncoding();
-			}
-		} else {
-			charsetName = getEncoding();
-		}
-
-		return charsetName;
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return Returns the defaultTimeZone.
-	 */
-	protected TimeZone getDefaultTimeZone() {
-		return this.defaultTimeZone;
-	}
-
-	/**
-	 * @see Connection#getHoldability()
-	 */
-	public int getHoldability() throws SQLException {
-		return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
-	}
-
-	long getId() {
-		return this.connectionId;
-	}
-
-	/**
-	 * NOT JDBC-Compliant, but clients can use this method to determine how long
-	 * this connection has been idle. This time (reported in milliseconds) is
-	 * updated once a query has completed.
-	 * 
 	 * @return number of ms that this connection has been idle, 0 if the driver
 	 *         is busy retrieving results.
 	 */
-	public long getIdleFor() {
-		if (this.lastQueryFinishedTime == 0) {
-			return 0;
-		}
+	public abstract long getIdleFor();
 
-		long now = System.currentTimeMillis();
-		long idleTime = now - this.lastQueryFinishedTime;
-
-		return idleTime;
-	}
-
 	/**
-	 * Returns the IO channel to the server
-	 * 
-	 * @return the IO channel to the server
-	 * @throws SQLException
-	 *             if the connection is closed.
-	 */
-	protected MysqlIO getIO() throws SQLException {
-		if ((this.io == null) || this.isClosed) {
-			throw SQLError.createSQLException(
-					"Operation not allowed on closed connection",
-					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
-		}
-
-		return this.io;
-	}
-
-	/**
 	 * Returns the log mechanism that should be used to log information from/for
 	 * this Connection.
 	 * 
@@ -3526,683 +151,49 @@
 	 * @throws SQLException
 	 *             if an error occurs
 	 */
-	public Log getLog() throws SQLException {
-		return this.log;
-	}
+	public abstract Log getLog() throws SQLException;
 
 	/**
-	 * Returns the maximum packet size the MySQL server will accept
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	int getMaxAllowedPacket() {
-		return this.maxAllowedPacket;
-	}
-
-	protected int getMaxBytesPerChar(String javaCharsetName)
-			throws SQLException {
-		// TODO: Check if we can actually run this query at this point in time
-		String charset = CharsetMapping.getMysqlEncodingForJavaEncoding(
-				javaCharsetName, this);
-
-		if (versionMeetsMinimum(4, 1, 0)) {
-			synchronized (this.charsetToNumBytesMap) {
-				if (this.charsetToNumBytesMap.isEmpty()) {
-					
-					java.sql.Statement stmt = null;
-					java.sql.ResultSet rs = null;
-	
-					try {
-						stmt = getMetadataSafeStatement();
-	
-						rs = stmt.executeQuery("SHOW CHARACTER SET");
-	
-						while (rs.next()) {
-							this.charsetToNumBytesMap.put(rs.getString("Charset"),
-									new Integer(rs.getInt("Maxlen")));
-						}
-	
-						rs.close();
-						rs = null;
-	
-						stmt.close();
-	
-						stmt = null;
-					} finally {
-						if (rs != null) {
-							rs.close();
-							rs = null;
-						}
-	
-						if (stmt != null) {
-							stmt.close();
-							stmt = null;
-						}
-					}
-				}
-			}
-
-			Integer mbPerChar = (Integer) this.charsetToNumBytesMap
-					.get(charset);
-
-			if (mbPerChar != null) {
-				return mbPerChar.intValue();
-			}
-
-			return 1; // we don't know
-		}
-
-		return 1; // we don't know
-	}
-
-	/**
-	 * A connection's database is able to provide information describing its
-	 * tables, its supported SQL grammar, its stored procedures, the
-	 * capabilities of this connection, etc. This information is made available
-	 * through a DatabaseMetaData object.
-	 * 
-	 * @return a DatabaseMetaData object for this connection
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public java.sql.DatabaseMetaData getMetaData() throws SQLException {
-		checkClosed();
-
-		if (getUseInformationSchema() &&
-				this.versionMeetsMinimum(5, 0, 7)) {
-			return new DatabaseMetaDataUsingInfoSchema(this, this.database);
-		}
-			
-		return new DatabaseMetaData(this, this.database);
-	}
-
-	protected java.sql.Statement getMetadataSafeStatement() throws SQLException {
-		java.sql.Statement stmt = createStatement();
-
-		if (stmt.getMaxRows() != 0) {
-			stmt.setMaxRows(0);
-		}
-
-		stmt.setEscapeProcessing(false);
-
-		return stmt;
-	}
-
-	/**
-	 * Returns the Mutex all queries are locked against
-	 * 
-	 * @return DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	Object getMutex() throws SQLException {
-		if (this.io == null) {
-			throw SQLError.createSQLException(
-					"Connection.close() has already been called. Invalid operation in this state.",
-					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
-		}
-
-		reportMetricsIfNeeded();
-
-		return this.mutex;
-	}
-
-	/**
-	 * Returns the packet buffer size the MySQL server reported upon connection
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	int getNetBufferLength() {
-		return this.netBufferLength;
-	}
-
-	/**
 	 * Returns the server's character set
 	 * 
 	 * @return the server's character set.
 	 */
-	protected String getServerCharacterEncoding() {
-		return (String) this.serverVariables.get("character_set");
-	}
+	public abstract String getServerCharacterEncoding();
 
-	int getServerMajorVersion() {
-		return this.io.getServerMajorVersion();
-	}
-
-	int getServerMinorVersion() {
-		return this.io.getServerMinorVersion();
-	}
-
-	int getServerSubMinorVersion() {
-		return this.io.getServerSubMinorVersion();
-	}
-
 	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
+	 * Returns the TimeZone that represents the configured
+	 * timezone for the server.
 	 */
-	public TimeZone getServerTimezoneTZ() {
-		return this.serverTimezoneTZ;
-	}
+	public abstract TimeZone getServerTimezoneTZ();
 
-	String getServerVariable(String variableName) {
-		if (this.serverVariables != null) {
-			return (String) this.serverVariables.get(variableName);
-		}
-
-		return null;
-	}
-
-	String getServerVersion() {
-		return this.io.getServerVersion();
-	}
-
-	protected Calendar getSessionLockedCalendar() {
-	
-		return this.sessionCalendar;
-	}
-	
-	
 	/**
-	 * Get this Connection's current transaction isolation mode.
+	 * Returns the comment that will be prepended to all statements
+	 * sent to the server.
 	 * 
-	 * @return the current TRANSACTION_ mode value
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * @return the comment that will be prepended to all statements
+	 * sent to the server.
 	 */
-	public int getTransactionIsolation() throws SQLException {
+	public abstract String getStatementComment();
 
-		if (this.hasIsolationLevels && !getUseLocalSessionState()) {
-			java.sql.Statement stmt = null;
-			java.sql.ResultSet rs = null;
-
-			try {
-				stmt = getMetadataSafeStatement();
-
-				String query = null;
-
-				if (versionMeetsMinimum(4, 0, 3)) {
-					query = "SHOW VARIABLES LIKE 'tx_isolation'";
-				} else {
-					query = "SHOW VARIABLES LIKE 'transaction_isolation'";
-				}
-
-				rs = stmt.executeQuery(query);
-
-				if (rs.next()) {
-					String s = rs.getString(2);
-
-					if (s != null) {
-						Integer intTI = (Integer) mapTransIsolationNameToValue
-								.get(s);
-
-						if (intTI != null) {
-							return intTI.intValue();
-						}
-					}
-
-					throw SQLError.createSQLException(
-							"Could not map transaction isolation '" + s
-									+ " to a valid JDBC level.",
-							SQLError.SQL_STATE_GENERAL_ERROR);
-				}
-
-				throw SQLError.createSQLException(
-						"Could not retrieve transaction isolation level from server",
-						SQLError.SQL_STATE_GENERAL_ERROR);
-
-			} finally {
-				if (rs != null) {
-					try {
-						rs.close();
-					} catch (Exception ex) {
-						// ignore
-						;
-					}
-
-					rs = null;
-				}
-
-				if (stmt != null) {
-					try {
-						stmt.close();
-					} catch (Exception ex) {
-						// ignore
-						;
-					}
-
-					stmt = null;
-				}
-			}
-		}
-
-		synchronized (this) {
-			return this.isolationLevel;
-		}
-	}
-
 	/**
-	 * JDBC 2.0 Get the type-map object associated with this connection. By
-	 * default, the map returned is empty.
-	 * 
-	 * @return the type map
-	 * @throws SQLException
-	 *             if a database error occurs
+	 * Has this connection tried to execute a query on the "master"
+	 * server (first host in a multiple host list).
 	 */
-	public synchronized java.util.Map getTypeMap() throws SQLException {
-		if (this.typeMap == null) {
-			this.typeMap = new HashMap();
-		}
+	public abstract boolean hasTriedMaster();
 
-		return this.typeMap;
-	}
-
-	String getURL() {
-		return this.myURL;
-	}
-
-	String getUser() {
-		return this.user;
-	}
-
-	protected Calendar getUtcCalendar() {
-		return this.utcCalendar;
-	}
-
 	/**
-	 * The first warning reported by calls on this Connection is returned.
-	 * <B>Note:</B> Sebsequent warnings will be changed to this
-	 * java.sql.SQLWarning
-	 * 
-	 * @return the first java.sql.SQLWarning or null
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * Is this connection currently a participant in an XA transaction?
 	 */
-	public SQLWarning getWarnings() throws SQLException {
-		return null;
-	}
+	public abstract boolean isInGlobalTx();
 
-	public boolean hasSameProperties(Connection c) {
-		return this.props.equals(c.props);
-	}
-
-	protected void incrementNumberOfPreparedExecutes() {
-		if (getGatherPerformanceMetrics()) {
-			this.numberOfPreparedExecutes++;
-
-			// We need to increment this, because
-			// server-side prepared statements bypass
-			// any execution by the connection itself...
-			this.numberOfQueriesIssued++;
-		}
-	}
-	
-	protected void incrementNumberOfPrepares() {
-		if (getGatherPerformanceMetrics()) {
-			this.numberOfPrepares++;
-		}
-	}
-
-	protected void incrementNumberOfResultSetsCreated() {
-		if (getGatherPerformanceMetrics()) {
-			this.numberOfResultSetsCreated++;
-		}
-	}
-
 	/**
-	 * Initializes driver properties that come from URL or properties passed to
-	 * the driver manager.
-	 * 
-	 * @param info
-	 *            DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	private void initializeDriverProperties(Properties info)
-			throws SQLException {
-		initializeProperties(info);
-		
-		this.usePlatformCharsetConverters = getUseJvmCharsetConverters();
-
-		this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME);
-
-		if (getProfileSql() || getUseUsageAdvisor()) {
-			this.eventSink = ProfileEventSink.getInstance(this);
-		}
-
-		if (getCachePreparedStatements()) {
-			createPreparedStatementCaches();		
-		}
-
-		if (getNoDatetimeStringSync() && getUseTimezone()) {
-			throw SQLError.createSQLException(
-					"Can't enable noDatetimeSync and useTimezone configuration "
-							+ "properties at the same time",
-					SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
-		}
-		
-		if (getCacheCallableStatements()) {
-			this.parsedCallableStatementCache = new LRUCache(
-					getCallableStatementCacheSize());
-		}
-	}
-
-	/**
-	 * Sets varying properties that depend on server information. Called once we
-	 * have connected to the server.
-	 * 
-	 * @param info
-	 *            DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	private void initializePropsFromServer() throws SQLException {
-		setSessionVariables();
-
-		//
-		// the "boolean" type didn't come along until MySQL-4.1
-		//
-
-		if (!versionMeetsMinimum(4, 1, 0)) {
-			setTransformedBitIsBoolean(false);
-		}
-
-		// We need to do this before any further data gets
-		// sent to the server....
-		boolean clientCharsetIsConfigured = configureClientCharacterSet();
-
-		this.parserKnowsUnicode = versionMeetsMinimum(4, 1, 0);
-
-		//
-		// Users can turn off detection of server-side prepared statements
-		//
-		if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) {
-			this.useServerPreparedStmts = true;
-
-			if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) {
-				this.useServerPreparedStmts = false; // 4.1.2+ style prepared
-				// statements
-				// don't work on these versions
-			}
-		}
-
-		this.serverVariables.clear();
-
-		//
-		// If version is greater than 3.21.22 get the server
-		// variables.
-		if (versionMeetsMinimum(3, 21, 22)) {
-			loadServerVariables();
-
-			buildCollationMapping();
-
-			LicenseConfiguration.checkLicenseType(this.serverVariables);
-
-			String lowerCaseTables = (String) this.serverVariables
-					.get("lower_case_table_names");
-
-			this.lowerCaseTableNames = "on".equalsIgnoreCase(lowerCaseTables)
-					|| "1".equalsIgnoreCase(lowerCaseTables)
-					|| "2".equalsIgnoreCase(lowerCaseTables);
-
-			configureTimezone();
-
-			if (this.serverVariables.containsKey("max_allowed_packet")) {
-				this.maxAllowedPacket = Integer
-						.parseInt((String) this.serverVariables
-								.get("max_allowed_packet"));
-				
-				int preferredBlobSendChunkSize = getBlobSendChunkSize();
-				
-				int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, 
-						this.maxAllowedPacket) - 
-						ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE 
-						- 11 /* LONG_DATA and MySQLIO packet header size */;
-				
-				setBlobSendChunkSize(String.valueOf(allowedBlobSendChunkSize));
-			}
-
-			if (this.serverVariables.containsKey("net_buffer_length")) {
-				this.netBufferLength = Integer
-						.parseInt((String) this.serverVariables
-								.get("net_buffer_length"));
-			}
-
-			checkTransactionIsolationLevel();
-
-			//
-			// We only do this for servers older than 4.1.0, because
-			// 4.1.0 and newer actually send the server charset
-			// during the handshake, and that's handled at the
-			// top of this method...
-			//
-			if (!clientCharsetIsConfigured) {
-				checkServerEncoding();
-			}
-
-			this.io.checkForCharsetMismatch();
-
-			if (this.serverVariables.containsKey("sql_mode")) {
-				int sqlMode = 0;
-
-				String sqlModeAsString = (String) this.serverVariables
-						.get("sql_mode");
-				try {
-					sqlMode = Integer.parseInt(sqlModeAsString);
-				} catch (NumberFormatException nfe) {
-					// newer versions of the server has this as a string-y
-					// list...
-					sqlMode = 0;
-
-					if (sqlModeAsString != null) {
-						if (sqlModeAsString.indexOf("ANSI_QUOTES") != -1) {
-							sqlMode |= 4;
-						}
-
-						if (sqlModeAsString.indexOf("NO_BACKSLASH_ESCAPES") != -1) {
-							this.noBackslashEscapes = true;
-						}
-					}
-				}
-
-				if ((sqlMode & 4) > 0) {
-					this.useAnsiQuotes = true;
-				} else {
-					this.useAnsiQuotes = false;
-				}
-			}
-		}
-		
-		this.errorMessageEncoding = 
-			CharsetMapping.getCharacterEncodingForErrorMessages(this);
-		
-		boolean overrideDefaultAutocommit = false;
-		
-		String initConnectValue = (String) this.serverVariables
-		.get("init_connect");
-
-		if (versionMeetsMinimum(4, 1, 2) && initConnectValue != null
-				&& initConnectValue.length() > 0) {
-			// auto-commit might have changed
-			java.sql.ResultSet rs = null;
-			java.sql.Statement stmt = null;
-			
-			try {
-				stmt = getMetadataSafeStatement();
-				
-				rs = stmt.executeQuery("SELECT @@session.autocommit");
-				
-				if (rs.next()) {
-					this.autoCommit = rs.getBoolean(1);
-					if (this.autoCommit != true) {
-						overrideDefaultAutocommit = true;
-					}
-				}
-				
-			} finally {
-				if (rs != null) {
-					try {
-						rs.close();
-					} catch (SQLException sqlEx) {
-						// do nothing
-					}
-				}
-				
-				if (stmt != null) {
-					try {
-						stmt.close();
-					} catch (SQLException sqlEx) {
-						// do nothing
-					}
-				}
-			}
-		}
-	
-		if (versionMeetsMinimum(3, 23, 15)) {
-			this.transactionsSupported = true;
-			
-			if (!overrideDefaultAutocommit) {
-				setAutoCommit(true); // to override anything
-				// the server is set to...reqd
-				// by JDBC spec.
-			}
-		} else {
-			this.transactionsSupported = false;
-		}
-		
-
-		if (versionMeetsMinimum(3, 23, 36)) {
-			this.hasIsolationLevels = true;
-		} else {
-			this.hasIsolationLevels = false;
-		}
-
-		this.hasQuotedIdentifiers = versionMeetsMinimum(3, 23, 6);
-
-		this.io.resetMaxBuf();
-
-		//
-		// If we're using MySQL 4.1.0 or newer, we need to figure
-		// out what character set metadata will be returned in,
-		// and then map that to a Java encoding name.
-		//
-		if (this.io.versionMeetsMinimum(4, 1, 0)) {
-			String characterSetResultsOnServerMysql = (String) this.serverVariables
-					.get("character_set_results");
-
-			if (characterSetResultsOnServerMysql == null
-					|| StringUtils.startsWithIgnoreCaseAndWs(
-							characterSetResultsOnServerMysql, "NULL")) {
-				String defaultMetadataCharsetMysql = (String) this.serverVariables
-						.get("character_set_system");
-				String defaultMetadataCharset = null;
-
-				if (defaultMetadataCharsetMysql != null) {
-					defaultMetadataCharset = CharsetMapping
-							.getJavaEncodingForMysqlEncoding(
-									defaultMetadataCharsetMysql, this);
-				} else {
-					defaultMetadataCharset = "UTF-8";
-				}
-
-				this.characterSetMetadata = defaultMetadataCharset;
-			} else {
-				this.characterSetResultsOnServer = CharsetMapping
-						.getJavaEncodingForMysqlEncoding(
-								characterSetResultsOnServerMysql, this);
-				this.characterSetMetadata = this.characterSetResultsOnServer;
-			}
-		}
-
-		//
-		// Query cache is broken wrt. multi-statements before MySQL-4.1.10
-		//
-
-		if (this.versionMeetsMinimum(4, 1, 0)
-				&& !this.versionMeetsMinimum(4, 1, 10)
-				&& getAllowMultiQueries()) {
-			if ("ON".equalsIgnoreCase((String) this.serverVariables
-					.get("query_cache_type"))
-					&& !"0".equalsIgnoreCase((String) this.serverVariables
-							.get("query_cache_size"))) {
-				setAllowMultiQueries(false);
-			}
-		}
-		
-		//
-		// Server can do this more efficiently for us
-		//
-		
-		setupServerForTruncationChecks();
-	}
-
-	private void setupServerForTruncationChecks() throws SQLException {
-		if (getJdbcCompliantTruncation()) {
-			if (versionMeetsMinimum(5, 0, 2)) {
-				
-				String currentSqlMode = 
-					(String)this.serverVariables.get("sql_mode");
-				
-				boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1;
-				
-				if (currentSqlMode == null ||
-						currentSqlMode.length() == 0 || !strictTransTablesIsSet) {
-					StringBuffer commandBuf = new StringBuffer("SET sql_mode='");
-					
-					if (currentSqlMode != null && currentSqlMode.length() > 0) {
-						commandBuf.append(currentSqlMode);
-						commandBuf.append(",");
-					}
-					
-					commandBuf.append("STRICT_TRANS_TABLES'");
-					
-					execSQL(null,  commandBuf.toString(), -1, null,
-							java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.database, true, false);
-					
-					setJdbcCompliantTruncation(false); // server's handling this for us now
-				} else if (strictTransTablesIsSet) {
-					// We didn't set it, but someone did, so we piggy back on it
-					setJdbcCompliantTruncation(false); // server's handling this for us now
-				}
-				
-			}
-		}
-	}
-
-	protected boolean isClientTzUTC() {
-		return this.isClientTzUTC;
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	public boolean isClosed() {
-		return this.isClosed;
-	}
-
-	protected boolean isCursorFetchEnabled() throws SQLException {
-		return (versionMeetsMinimum(5, 0, 2) && getUseCursorFetch());
-	}
-
-	public boolean isInGlobalTx() {
-		return this.isInGlobalTx;
-	}
-
-	/**
 	 * Is this connection connected to the first host in the list if
 	 * there is a list of servers in the URL?
 	 * 
 	 * @return true if this connection is connected to the first in 
 	 * the list.
 	 */
-	public synchronized boolean isMasterConnection() {
-		return !this.failedOver;
-	}
+	public abstract boolean isMasterConnection();
 
 	/**
 	 * Is the server in a sql_mode that doesn't allow us to use \\ to escape
@@ -4210,857 +201,31 @@
 	 * 
 	 * @return Returns the noBackslashEscapes.
 	 */
-	public boolean isNoBackslashEscapesSet() {
-		return this.noBackslashEscapes;
-	}
+	public abstract boolean isNoBackslashEscapesSet();
 
-	boolean isReadInfoMsgEnabled() {
-		return this.readInfoMsg;
-	}
-
 	/**
-	 * Tests to see if the connection is in Read Only Mode. Note that we cannot
-	 * really put the database in read only mode, but we pretend we can by
-	 * returning the value of the readOnly flag
-	 * 
-	 * @return true if the connection is read only
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public boolean isReadOnly() throws SQLException {
-		return this.readOnly;
-	}
-
-	protected boolean isRunningOnJDK13() {
-		return this.isRunningOnJDK13;
-	}
-
-	public synchronized boolean isSameResource(Connection otherConnection) {
-		if (otherConnection == null) {
-			return false;
-		}
-		
-		boolean directCompare = true;
-		
-		String otherHost = otherConnection.origHostToConnectTo;
-		String otherOrigDatabase = otherConnection.origDatabaseToConnectTo;
-		String otherCurrentCatalog = otherConnection.database;
-		
-		if (!nullSafeCompare(otherHost, this.origHostToConnectTo)) {
-			directCompare = false;
-		} else if (otherHost != null & otherHost.indexOf(",") == -1 && 
-				otherHost.indexOf(":") == -1) {
-			// need to check port numbers
-			directCompare = (otherConnection.origPortToConnectTo == 
-				this.origPortToConnectTo);
-		}
-		
-		if (directCompare) {
-			if (!nullSafeCompare(otherOrigDatabase, this.origDatabaseToConnectTo)) {			directCompare = false;
-				directCompare = false;
-			} else if (!nullSafeCompare(otherCurrentCatalog, this.database)) {
-				directCompare = false;
-			}
-		}
-
-		if (directCompare) {
-			return true;
-		}
-		
-		// Has the user explicitly set a resourceId?
-		String otherResourceId = otherConnection.getResourceId();
-		String myResourceId = getResourceId();
-		
-		if (otherResourceId != null || myResourceId != null) {
-			directCompare = nullSafeCompare(otherResourceId, myResourceId);
-			
-			if (directCompare) {
-				return true;
-			}
-		}
-		
-		return false;	
-	}
-
-	protected boolean isServerTzUTC() {
-		return this.isServerTzUTC;
-	}
-
-	/**
-	 * Loads the result of 'SHOW VARIABLES' into the serverVariables field so
-	 * that the driver can configure itself.
-	 * 
-	 * @throws SQLException
-	 *             if the 'SHOW VARIABLES' query fails for any reason.
-	 */
-	private void loadServerVariables() throws SQLException {
-
-		if (getCacheServerConfiguration()) {
-			synchronized (serverConfigByUrl) {
-				Map cachedVariableMap = (Map) serverConfigByUrl.get(getURL());
-
-				if (cachedVariableMap != null) {
-					this.serverVariables = cachedVariableMap;
-
-					return;
-				}
-			}
-		}
-
-		com.mysql.jdbc.Statement stmt = null;
-		com.mysql.jdbc.ResultSet results = null;
-
-		try {
-			stmt = (com.mysql.jdbc.Statement) createStatement();
-			stmt.setEscapeProcessing(false);
-
-			results = (com.mysql.jdbc.ResultSet) stmt
-					.executeQuery("SHOW VARIABLES");
-
-			while (results.next()) {
-				this.serverVariables.put(results.getString(1), results
-						.getString(2));
-			}
-
-			if (getCacheServerConfiguration()) {
-				synchronized (serverConfigByUrl) {
-					serverConfigByUrl.put(getURL(), this.serverVariables);
-				}
-			}
-		} catch (SQLException e) {
-			throw e;
-		} finally {
-			if (results != null) {
-				try {
-					results.close();
-				} catch (SQLException sqlE) {
-					;
-				}
-			}
-
-			if (stmt != null) {
-				try {
-					stmt.close();
-				} catch (SQLException sqlE) {
-					;
-				}
-			}
-		}
-	}
-
-	/**
 	 * Is the server configured to use lower-case table names only?
 	 * 
 	 * @return true if lower_case_table_names is 'on'
 	 */
-	public boolean lowerCaseTableNames() {
-		return this.lowerCaseTableNames;
-	}
+	public abstract boolean lowerCaseTableNames();
 
 	/**
-	 * Has the maxRows value changed?
-	 * 
-	 * @param stmt
-	 *            DOCUMENT ME!
+	 * Does the server this connection is connected to
+	 * support unicode?
 	 */
-	void maxRowsChanged(Statement stmt) {
-		synchronized (this.mutex) {
-			if (this.statementsUsingMaxRows == null) {
-				this.statementsUsingMaxRows = new HashMap();
-			}
+	public abstract boolean parserKnowsUnicode();
 
-			this.statementsUsingMaxRows.put(stmt, stmt);
-
-			this.maxRowsChanged = true;
-		}
-	}
-
 	/**
-	 * A driver may convert the JDBC sql grammar into its system's native SQL
-	 * grammar prior to sending it; nativeSQL returns the native form of the
-	 * statement that the driver would have sent.
+	 * Detect if the connection is still good by sending a ping command
+	 * to the server.
 	 * 
-	 * @param sql
-	 *            a SQL statement that may contain one or more '?' parameter
-	 *            placeholders
-	 * @return the native form of this statement
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public String nativeSQL(String sql) throws SQLException {
-		if (sql == null) {
-			return null;
-		}
-
-		Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
-				serverSupportsConvertFn(),
-				this);
-
-		if (escapedSqlResult instanceof String) {
-			return (String) escapedSqlResult;
-		}
-
-		return ((EscapeProcessorResult) escapedSqlResult).escapedSql;
-	}
-
-	private CallableStatement parseCallableStatement(String sql)
-			throws SQLException {
-		Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
-				serverSupportsConvertFn(), this);
-
-		boolean isFunctionCall = false;
-		String parsedSql = null;
-
-		if (escapedSqlResult instanceof EscapeProcessorResult) {
-			parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
-			isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction;
-		} else {
-			parsedSql = (String) escapedSqlResult;
-			isFunctionCall = false;
-		}
-
-		return new CallableStatement(this, parsedSql, this.database,
-				isFunctionCall);
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	public boolean parserKnowsUnicode() {
-		return this.parserKnowsUnicode;
-	}
-
-	/**
-	 * Detect if the connection is still good
-	 * 
 	 * @throws SQLException
 	 *             if the ping fails
 	 */
-	public void ping() throws SQLException {
-		pingInternal(true);
-	}
+	public abstract void ping() throws SQLException;
 
-	private void pingInternal(boolean checkForClosedConnection)
-			throws SQLException {
-		if (checkForClosedConnection) {
-			checkClosed();
-		}
-
-		// Need MySQL-3.22.1, but who uses anything older!?
-		this.io.sendCommand(MysqlDefs.PING, null, null, false, null);
-	}
-
 	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @param sql
-	 *            DOCUMENT ME!
-	 * @return DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	public java.sql.CallableStatement prepareCall(String sql)
-			throws SQLException {
-		if (this.getUseUltraDevWorkAround()) {
-			return new UltraDevWorkAround(prepareStatement(sql));
-		}
-
-		return prepareCall(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
-				java.sql.ResultSet.CONCUR_READ_ONLY);
-	}
-
-	/**
-	 * JDBC 2.0 Same as prepareCall() above, but allows the default result set
-	 * type and result set concurrency type to be overridden.
-	 * 
-	 * @param sql
-	 *            the SQL representing the callable statement
-	 * @param resultSetType
-	 *            a result set type, see ResultSet.TYPE_XXX
-	 * @param resultSetConcurrency
-	 *            a concurrency type, see ResultSet.CONCUR_XXX
-	 * @return a new CallableStatement object containing the pre-compiled SQL
-	 *         statement
-	 * @exception SQLException
-	 *                if a database-access error occurs.
-	 */
-	public java.sql.CallableStatement prepareCall(String sql,
-			int resultSetType, int resultSetConcurrency) throws SQLException {
-		if (versionMeetsMinimum(5, 0, 0)) {
-			CallableStatement cStmt = null;
-
-			if (!getCacheCallableStatements()) {
-
-				cStmt = parseCallableStatement(sql);
-			} else {
-				synchronized (this.parsedCallableStatementCache) {
-					CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql);
-	
-					CallableStatement.CallableStatementParamInfo cachedParamInfo = (CallableStatement.CallableStatementParamInfo) this.parsedCallableStatementCache
-							.get(key);
-	
-					if (cachedParamInfo != null) {
-						cStmt = new CallableStatement(this, cachedParamInfo);
-					} else {
-						cStmt = parseCallableStatement(sql);
-	
-						cachedParamInfo = cStmt.paramInfo;
-	
-						this.parsedCallableStatementCache.put(key, cachedParamInfo);
-					}
-				}
-			}
-
-			cStmt.setResultSetType(resultSetType);
-			cStmt.setResultSetConcurrency(resultSetConcurrency);
-
-			return cStmt;
-		}
-
-		throw SQLError.createSQLException("Callable statements not " + "supported.",
-				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
-	}
-
-	/**
-	 * @see Connection#prepareCall(String, int, int, int)
-	 */
-	public java.sql.CallableStatement prepareCall(String sql,
-			int resultSetType, int resultSetConcurrency,
-			int resultSetHoldability) throws SQLException {
-		if (getPedantic()) {
-			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
-				throw SQLError.createSQLException(
-						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-		}
-
-		CallableStatement cStmt = (com.mysql.jdbc.CallableStatement) prepareCall(
-				sql, resultSetType, resultSetConcurrency);
-
-		return cStmt;
-	}
-
-	/**
-	 * A SQL statement with or without IN parameters can be pre-compiled and
-	 * stored in a PreparedStatement object. This object can then be used to
-	 * efficiently execute this statement multiple times.
-	 * <p>
-	 * <B>Note:</B> This method is optimized for handling parametric SQL
-	 * statements that benefit from precompilation if the driver supports
-	 * precompilation. In this case, the statement is not sent to the database
-	 * until the PreparedStatement is executed. This has no direct effect on
-	 * users; however it does affect which method throws certain
-	 * java.sql.SQLExceptions
-	 * </p>
-	 * <p>
-	 * MySQL does not support precompilation of statements, so they are handled
-	 * by the driver.
-	 * </p>
-	 * 
-	 * @param sql
-	 *            a SQL statement that may contain one or more '?' IN parameter
-	 *            placeholders
-	 * @return a new PreparedStatement object containing the pre-compiled
-	 *         statement.
-	 * @exception SQLException
-	 *                if a database access error occurs.
-	 */
-	public java.sql.PreparedStatement prepareStatement(String sql)
-			throws SQLException {
-		return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
-				java.sql.ResultSet.CONCUR_READ_ONLY);
-	}
-
-	/**
-	 * @see Connection#prepareStatement(String, int)
-	 */
-	public java.sql.PreparedStatement prepareStatement(String sql,
-			int autoGenKeyIndex) throws SQLException {
-		java.sql.PreparedStatement pStmt = prepareStatement(sql);
-
-		((com.mysql.jdbc.PreparedStatement) pStmt)
-				.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
-
-		return pStmt;
-	}
-
-	/**
-	 * JDBC 2.0 Same as prepareStatement() above, but allows the default result
-	 * set type and result set concurrency type to be overridden.
-	 * 
-	 * @param sql
-	 *            the SQL query containing place holders
-	 * @param resultSetType
-	 *            a result set type, see ResultSet.TYPE_XXX
-	 * @param resultSetConcurrency
-	 *            a concurrency type, see ResultSet.CONCUR_XXX
-	 * @return a new PreparedStatement object containing the pre-compiled SQL
-	 *         statement
-	 * @exception SQLException
-	 *                if a database-access error occurs.
-	 */
-	public java.sql.PreparedStatement prepareStatement(String sql,
-			int resultSetType, int resultSetConcurrency) throws SQLException {
-		checkClosed();
-
-		//
-		// FIXME: Create warnings if can't create results of the given
-		// type or concurrency
-		//
-		PreparedStatement pStmt = null;
-		
-		boolean canServerPrepare = true;
-		
-		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
-		
-		if (getEmulateUnsupportedPstmts()) {
-			canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
-		}
-		
-		if (this.useServerPreparedStmts && canServerPrepare) {
-			if (this.getCachePreparedStatements()) {
-				synchronized (this.serverSideStatementCache) {
-					pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
-					
-					if (pStmt != null) {
-						((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
-						pStmt.clearParameters();
-					}
-
-					if (pStmt == null) {
-						try {
-							pStmt = new com.mysql.jdbc.ServerPreparedStatement(this, nativeSql,
-									this.database, resultSetType, resultSetConcurrency);
-							if (sql.length() < getPreparedStatementCacheSqlLimit()) {
-								((com.mysql.jdbc.ServerPreparedStatement)pStmt).isCached = true;
-							}
-						} catch (SQLException sqlEx) {
-							// Punt, if necessary
-							if (getEmulateUnsupportedPstmts()) {
-								pStmt = clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
-								
-								if (sql.length() < getPreparedStatementCacheSqlLimit()) {
-									this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
-								}
-							} else {
-								throw sqlEx;
-							}
-						}
-					}
-				}
-			} else {
-				try {
-					pStmt = new com.mysql.jdbc.ServerPreparedStatement(this, nativeSql,
-							this.database, resultSetType, resultSetConcurrency);
-				} catch (SQLException sqlEx) {
-					// Punt, if necessary
-					if (getEmulateUnsupportedPstmts()) {
-						pStmt = clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
-					} else {
-						throw sqlEx;
-					}
-				}
-			}
-		} else {
-			pStmt = clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
-		}
-
-		return pStmt;
-	}
-
-	/**
-	 * @see Connection#prepareStatement(String, int, int, int)
-	 */
-	public java.sql.PreparedStatement prepareStatement(String sql,
-			int resultSetType, int resultSetConcurrency,
-			int resultSetHoldability) throws SQLException {
-		if (getPedantic()) {
-			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
-				throw SQLError.createSQLException(
-						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-		}
-
-		return prepareStatement(sql, resultSetType, resultSetConcurrency);
-	}
-
-	/**
-	 * @see Connection#prepareStatement(String, int[])
-	 */
-	public java.sql.PreparedStatement prepareStatement(String sql,
-			int[] autoGenKeyIndexes) throws SQLException {
-		java.sql.PreparedStatement pStmt = prepareStatement(sql);
-
-		((com.mysql.jdbc.PreparedStatement) pStmt)
-				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
-						&& (autoGenKeyIndexes.length > 0));
-
-		return pStmt;
-	}
-
-	/**
-	 * @see Connection#prepareStatement(String, String[])
-	 */
-	public java.sql.PreparedStatement prepareStatement(String sql,
-			String[] autoGenKeyColNames) throws SQLException {
-		java.sql.PreparedStatement pStmt = prepareStatement(sql);
-
-		((com.mysql.jdbc.PreparedStatement) pStmt)
-				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
-						&& (autoGenKeyColNames.length > 0));
-
-		return pStmt;
-	}
-
-	/**
-	 * Closes connection and frees resources.
-	 * 
-	 * @param calledExplicitly
-	 *            is this being called from close()
-	 * @param issueRollback
-	 *            should a rollback() be issued?
-	 * @throws SQLException
-	 *             if an error occurs
-	 */
-	protected void realClose(boolean calledExplicitly, boolean issueRollback,
-			boolean skipLocalTeardown, Throwable reason) throws SQLException {
-		SQLException sqlEx = null;
-
-		if (this.isClosed()) {
-			return;
-		}
-		
-		this.forceClosedReason = reason;
-		
-		try {
-			if (!skipLocalTeardown) {
-				if (!getAutoCommit() && issueRollback) {
-					try {
-						rollback();
-					} catch (SQLException ex) {
-						sqlEx = ex;
-					}
-				}
-
-				reportMetrics();
-
-				if (getUseUsageAdvisor()) {
-					if (!calledExplicitly) {
-						String message = "Connection implicitly closed by Driver. You should call Connection.close() from your code to free resources more efficiently and avoid resource leaks.";
-
-						this.eventSink.consumeEvent(new ProfilerEvent(
-								ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
-								this.getCatalog(), this.getId(), -1, -1, System
-										.currentTimeMillis(), 0, null,
-								this.pointOfOrigin, message));
-					}
-
-					long connectionLifeTime = System.currentTimeMillis()
-							- this.connectionCreationTimeMillis;
-
-					if (connectionLifeTime < 500) {
-						String message = "Connection lifetime of < .5 seconds. You might be un-necessarily creating short-lived connections and should investigate connection pooling to be more efficient.";
-
-						this.eventSink.consumeEvent(new ProfilerEvent(
-								ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
-								this.getCatalog(), this.getId(), -1, -1, System
-										.currentTimeMillis(), 0, null,
-								this.pointOfOrigin, message));
-					}
-				}
-
-				try {
-					closeAllOpenStatements();
-				} catch (SQLException ex) {
-					sqlEx = ex;
-				}
-
-				if (this.io != null) {
-					try {
-						this.io.quit();
-					} catch (Exception e) {
-						;
-					}
-
-				}
-			} else {
-				this.io.forceClose();
-			}
-		} finally {
-			this.openStatements = null;
-			this.io = null;
-			ProfileEventSink.removeInstance(this);
-			this.isClosed = true;
-		}
-
-		if (sqlEx != null) {
-			throw sqlEx;
-		}
-
-	}
-
-	protected void recachePreparedStatement(ServerPreparedStatement pstmt) {
-		synchronized (this.serverSideStatementCache) {
-			this.serverSideStatementCache.put(pstmt.originalSql, pstmt);
-		}
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @param queryTimeMs
-	 */
-	protected void registerQueryExecutionTime(long queryTimeMs) {
-		if (queryTimeMs > this.longestQueryTimeMs) {
-			this.longestQueryTimeMs = queryTimeMs;
-
-			repartitionPerformanceHistogram();
-		}
-
-		addToPerformanceHistogram(queryTimeMs, 1);
-
-		if (queryTimeMs < this.shortestQueryTimeMs) {
-			this.shortestQueryTimeMs = (queryTimeMs == 0) ? 1 : queryTimeMs;
-		}
-
-		this.numberOfQueriesIssued++;
-
-		this.totalQueryTimeMs += queryTimeMs;
-	}
-
-	/**
-	 * Register a Statement instance as open.
-	 * 
-	 * @param stmt
-	 *            the Statement instance to remove
-	 */
-	void registerStatement(Statement stmt) {
-		synchronized (this.openStatements) {
-			this.openStatements.put(stmt, stmt);
-		}
-	}
-
-	/**
-	 * @see Connection#releaseSavepoint(Savepoint)
-	 */
-	public void releaseSavepoint(Savepoint arg0) throws SQLException {
-		// this is a no-op
-	}
-
-	private void repartitionHistogram(int[] histCounts, long[] histBreakpoints,
-			long currentLowerBound, long currentUpperBound) {
-
-		if (oldHistCounts == null) {
-			oldHistCounts = new int[histCounts.length];
-			oldHistBreakpoints = new long[histBreakpoints.length];
-		}
-
-		for (int i = 0; i < histCounts.length; i++) {
-			oldHistCounts[i] = histCounts[i];
-		}
-
-		for (int i = 0; i < oldHistBreakpoints.length; i++) {
-			oldHistBreakpoints[i] = histBreakpoints[i];
-		}
-
-		createInitialHistogram(histBreakpoints, currentLowerBound,
-				currentUpperBound);
-
-		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
-			addToHistogram(histCounts, histBreakpoints, oldHistBreakpoints[i],
-					oldHistCounts[i], currentLowerBound, currentUpperBound);
-		}
-	}
-
-	private void repartitionPerformanceHistogram() {
-		checkAndCreatePerformanceHistogram();
-
-		repartitionHistogram(this.perfMetricsHistCounts,
-				this.perfMetricsHistBreakpoints,
-				this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
-						: this.shortestQueryTimeMs, this.longestQueryTimeMs);
-	}
-
-	private void repartitionTablesAccessedHistogram() {
-		checkAndCreateTablesAccessedHistogram();
-
-		repartitionHistogram(this.numTablesMetricsHistCounts,
-				this.numTablesMetricsHistBreakpoints,
-				this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
-						: this.minimumNumberTablesAccessed,
-				this.maximumNumberTablesAccessed);
-	}
-
-	private void reportMetrics() {
-		if (getGatherPerformanceMetrics()) {
-			StringBuffer logMessage = new StringBuffer(256);
-
-			logMessage.append("** Performance Metrics Report **\n");
-			logMessage.append("\nLongest reported query: "
-					+ this.longestQueryTimeMs + " ms");
-			logMessage.append("\nShortest reported query: "
-					+ this.shortestQueryTimeMs + " ms");
-			logMessage
-					.append("\nAverage query execution time: "
-							+ (this.totalQueryTimeMs / this.numberOfQueriesIssued)
-							+ " ms");
-			logMessage.append("\nNumber of statements executed: "
-					+ this.numberOfQueriesIssued);
-			logMessage.append("\nNumber of result sets created: "
-					+ this.numberOfResultSetsCreated);
-			logMessage.append("\nNumber of statements prepared: "
-					+ this.numberOfPrepares);
-			logMessage.append("\nNumber of prepared statement executions: "
-					+ this.numberOfPreparedExecutes);
-
-			if (this.perfMetricsHistBreakpoints != null) {
-				logMessage.append("\n\n\tTiming Histogram:\n");
-				int maxNumPoints = 20;
-				int highestCount = Integer.MIN_VALUE;
-
-				for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
-					if (this.perfMetricsHistCounts[i] > highestCount) {
-						highestCount = this.perfMetricsHistCounts[i];
-					}
-				}
-
-				if (highestCount == 0) {
-					highestCount = 1; // avoid DIV/0
-				}
-
-				for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {
-
-					if (i == 0) {
-						logMessage.append("\n\tless than "
-								+ this.perfMetricsHistBreakpoints[i + 1]
-								+ " ms: \t" + this.perfMetricsHistCounts[i]);
-					} else {
-						logMessage.append("\n\tbetween "
-								+ this.perfMetricsHistBreakpoints[i] + " and "
-								+ this.perfMetricsHistBreakpoints[i + 1]
-								+ " ms: \t" + this.perfMetricsHistCounts[i]);
-					}
-
-					logMessage.append("\t");
-
-					int numPointsToGraph = (int) (maxNumPoints * ((double) this.perfMetricsHistCounts[i] / (double) highestCount));
-
-					for (int j = 0; j < numPointsToGraph; j++) {
-						logMessage.append("*");
-					}
-
-					if (this.longestQueryTimeMs < this.perfMetricsHistCounts[i + 1]) {
-						break;
-					}
-				}
-
-				if (this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.longestQueryTimeMs) {
-					logMessage.append("\n\tbetween ");
-					logMessage
-							.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
-					logMessage.append(" and ");
-					logMessage
-							.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
-					logMessage.append(" ms: \t");
-					logMessage
-							.append(this.perfMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
-				}
-			}
-
-			if (this.numTablesMetricsHistBreakpoints != null) {
-				logMessage.append("\n\n\tTable Join Histogram:\n");
-				int maxNumPoints = 20;
-				int highestCount = Integer.MIN_VALUE;
-
-				for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
-					if (this.numTablesMetricsHistCounts[i] > highestCount) {
-						highestCount = this.numTablesMetricsHistCounts[i];
-					}
-				}
-
-				if (highestCount == 0) {
-					highestCount = 1; // avoid DIV/0
-				}
-
-				for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {
-
-					if (i == 0) {
-						logMessage.append("\n\t"
-								+ this.numTablesMetricsHistBreakpoints[i + 1]
-								+ " tables or less: \t\t"
-								+ this.numTablesMetricsHistCounts[i]);
-					} else {
-						logMessage.append("\n\tbetween "
-								+ this.numTablesMetricsHistBreakpoints[i]
-								+ " and "
-								+ this.numTablesMetricsHistBreakpoints[i + 1]
-								+ " tables: \t"
-								+ this.numTablesMetricsHistCounts[i]);
-					}
-
-					logMessage.append("\t");
-
-					int numPointsToGraph = (int) (maxNumPoints * ((double) this.numTablesMetricsHistCounts[i] / (double) highestCount));
-
-					for (int j = 0; j < numPointsToGraph; j++) {
-						logMessage.append("*");
-					}
-
-					if (this.maximumNumberTablesAccessed < this.numTablesMetricsHistBreakpoints[i + 1]) {
-						break;
-					}
-				}
-
-				if (this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.maximumNumberTablesAccessed) {
-					logMessage.append("\n\tbetween ");
-					logMessage
-							.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
-					logMessage.append(" and ");
-					logMessage
-							.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
-					logMessage.append(" tables: ");
-					logMessage
-							.append(this.numTablesMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
-				}
-			}
-
-			this.log.logInfo(logMessage);
-
-			this.metricsLastReportedMs = System.currentTimeMillis();
-		}
-	}
-
-	/**
-	 * Reports currently collected metrics if this feature is enabled and the
-	 * timeout has passed.
-	 */
-	private void reportMetricsIfNeeded() {
-		if (getGatherPerformanceMetrics()) {
-			if ((System.currentTimeMillis() - this.metricsLastReportedMs) > getReportMetricsIntervalMillis()) {
-				reportMetrics();
-			}
-		}
-	}
-
-	protected void reportNumberOfTablesAccessed(int numTablesAccessed) {
-		if (numTablesAccessed < this.minimumNumberTablesAccessed) {
-			this.minimumNumberTablesAccessed = numTablesAccessed;
-		}
-
-		if (numTablesAccessed > this.maximumNumberTablesAccessed) {
-			this.maximumNumberTablesAccessed = numTablesAccessed;
-
-			repartitionTablesAccessedHistogram();
-		}
-
-		addToTablesAccessedHistogram(numTablesAccessed, 1);
-	}
-
-	/**
 	 * Resets the server-side state of this connection. Doesn't work for MySQL
 	 * versions older than 4.0.6 or if isParanoid() is set (it will become a
 	 * no-op in these cases). Usually only used from connection pooling code.
@@ -5068,669 +233,131 @@
 	 * @throws SQLException
 	 *             if the operation fails while resetting server state.
 	 */
-	public void resetServerState() throws SQLException {
-		if (!getParanoid()
-				&& ((this.io != null) & versionMeetsMinimum(4, 0, 6))) {
-			changeUser(this.user, this.password);
-		}
-	}
+	public abstract void resetServerState() throws SQLException;
 
 	/**
-	 * The method rollback() drops all changes made since the previous
-	 * commit/rollback and releases any database locks currently held by the
-	 * Connection.
+	 * Prepares a statement on the server (irregardless of the 
+	 * configuration property 'useServerPrepStmts') with the same semantics
+	 * as the java.sql.Connection.prepareStatement() method with the 
+	 * same argument types.
 	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 * @see commit
+	 * @see java.sql.Connection#prepareStatement(String)
 	 */
-	public void rollback() throws SQLException {
-		synchronized (getMutex()) {
-			checkClosed();
-	
-			try {
-				// no-op if _relaxAutoCommit == true
-				if (this.autoCommit && !getRelaxAutoCommit()) {
-					throw SQLError.createSQLException(
-							"Can't call rollback when autocommit=true",
-							SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
-				} else if (this.transactionsSupported) {
-					try {
-						rollbackNoChecks();
-					} catch (SQLException sqlEx) {
-						// We ignore non-transactional tables if told to do so
-						if (getIgnoreNonTxTables()
-								&& (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
-							throw sqlEx;
-						}
-					}
-				}
-			} catch (SQLException sqlException) {
-				if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
-						.equals(sqlException.getSQLState())) {
-					throw SQLError.createSQLException(
-							"Communications link failure during rollback(). Transaction resolution unknown.",
-							SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN);
-				}
-	
-				throw sqlException;
-			} finally {
-				this.needsPing = this.getReconnectAtTxEnd();
-			}
-		}
-	}
+	public abstract java.sql.PreparedStatement serverPrepareStatement(String sql)
+		throws SQLException;
 
 	/**
-	 * @see Connection#rollback(Savepoint)
+	 * Prepares a statement on the server (irregardless of the 
+	 * configuration property 'useServerPrepStmts') with the same semantics
+	 * as the java.sql.Connection.prepareStatement() method with the 
+	 * same argument types.
+	 * 
+	 * @see java.sql.Connection#prepareStatement(String, int)
 	 */
-	public void rollback(Savepoint savepoint) throws SQLException {
+	public abstract java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException;
 
-		if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
-			synchronized (getMutex()) {
-				checkClosed();
-	
-				try {
-					StringBuffer rollbackQuery = new StringBuffer(
-							"ROLLBACK TO SAVEPOINT ");
-					rollbackQuery.append('`');
-					rollbackQuery.append(savepoint.getSavepointName());
-					rollbackQuery.append('`');
-	
-					java.sql.Statement stmt = null;
-	
-					try {
-						stmt = createStatement();
-	
-						stmt.executeUpdate(rollbackQuery.toString());
-					} catch (SQLException sqlEx) {
-						int errno = sqlEx.getErrorCode();
-	
-						if (errno == 1181) {
-							String msg = sqlEx.getMessage();
-	
-							if (msg != null) {
-								int indexOfError153 = msg.indexOf("153");
-	
-								if (indexOfError153 != -1) {
-									throw SQLError.createSQLException("Savepoint '"
-											+ savepoint.getSavepointName()
-											+ "' does not exist",
-											SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
-											errno);
-								}
-							}
-						}
-	
-						// We ignore non-transactional tables if told to do so
-						if (getIgnoreNonTxTables()
-								&& (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
-							throw sqlEx;
-						}
-	
-						if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
-								.equals(sqlEx.getSQLState())) {
-							throw SQLError.createSQLException(
-									"Communications link failure during rollback(). Transaction resolution unknown.",
-									SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN);
-						}
-	
-						throw sqlEx;
-					} finally {
-						closeStatement(stmt);
-					}
-				} finally {
-					this.needsPing = this.getReconnectAtTxEnd();
-				}
-			}
-		} else {
-			throw new NotImplemented();
-		}
-	}
-
-	private void rollbackNoChecks() throws SQLException {
-		execSQL(null, "rollback", -1, null,
-				java.sql.ResultSet.TYPE_FORWARD_ONLY,
-				java.sql.ResultSet.CONCUR_READ_ONLY, false,
-				this.database, true, false);
-	}
-
 	/**
-	 * DOCUMENT ME!
+	 * Prepares a statement on the server (irregardless of the 
+	 * configuration property 'useServerPrepStmts') with the same semantics
+	 * as the java.sql.Connection.prepareStatement() method with the 
+	 * same argument types.
 	 * 
-	 * @param sql
-	 *            DOCUMENT ME!
-	 * @return DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
+	 * @see java.sql.Connection#prepareStatement(String, int, int)
 	 */
-	public ServerPreparedStatement serverPrepare(String sql)
-			throws SQLException {
-
-		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
-		
-		return new ServerPreparedStatement(this, nativeSql, this.getCatalog(),
-				java.sql.ResultSet.TYPE_SCROLL_SENSITIVE,
-				java.sql.ResultSet.CONCUR_READ_ONLY);
-	}
-
-	protected boolean serverSupportsConvertFn() throws SQLException {
-		return versionMeetsMinimum(4, 0, 2);
-	}
-
+	public abstract java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException;
 	/**
-	 * If a connection is in auto-commit mode, than all its SQL statements will
-	 * be executed and committed as individual transactions. Otherwise, its SQL
-	 * statements are grouped into transactions that are terminated by either
-	 * commit() or rollback(). By default, new connections are in auto- commit
-	 * mode. The commit occurs when the statement completes or the next execute
-	 * occurs, whichever comes first. In the case of statements returning a
-	 * ResultSet, the statement completes when the last row of the ResultSet has
-	 * been retrieved or the ResultSet has been closed. In advanced cases, a
-	 * single statement may return multiple results as well as output parameter
-	 * values. Here the commit occurs when all results and output param values
-	 * have been retrieved.
-	 * <p>
-	 * <b>Note:</b> MySQL does not support transactions, so this method is a
-	 * no-op.
-	 * </p>
+	 * Prepares a statement on the server (irregardless of the 
+	 * configuration property 'useServerPrepStmts') with the same semantics
+	 * as the java.sql.Connection.prepareStatement() method with the 
+	 * same argument types.
 	 * 
-	 * @param autoCommitFlag -
-	 *            true enables auto-commit; false disables it
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * @see java.sql.Connection#prepareStatement(String, int, int, int)
 	 */
-	public void setAutoCommit(boolean autoCommitFlag) throws SQLException {
-		synchronized (getMutex()) {
-			checkClosed();
+	public abstract java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException;
 	
-			if (getAutoReconnectForPools()) {
-				setHighAvailability(true);
-			}
-	
-			try {
-				if (this.transactionsSupported) {
-	
-					boolean needsSetOnServer = true;
-	
-					if (this.getUseLocalSessionState()
-							&& this.autoCommit == autoCommitFlag) {
-						needsSetOnServer = false;
-					} else if (!this.getHighAvailability()) {
-						needsSetOnServer = this.getIO()
-								.isSetNeededForAutoCommitMode(autoCommitFlag);
-					}
-	
-					// this internal value must be set first as failover depends on
-					// it
-					// being set to true to fail over (which is done by most
-					// app servers and connection pools at the end of
-					// a transaction), and the driver issues an implicit set
-					// based on this value when it (re)-connects to a server
-					// so the value holds across connections
-					this.autoCommit = autoCommitFlag;
-	
-					if (needsSetOnServer) {
-						execSQL(null, autoCommitFlag ? "SET autocommit=1"
-								: "SET autocommit=0", -1, null,
-								java.sql.ResultSet.TYPE_FORWARD_ONLY,
-								java.sql.ResultSet.CONCUR_READ_ONLY, false,
-								this.database, true, false);
-					}
-	
-				} else {
-					if ((autoCommitFlag == false) && !getRelaxAutoCommit()) {
-						throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 "
-								+ "do not support transactions",
-								SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
-					}
-	
-					this.autoCommit = autoCommitFlag;
-				}
-			} finally {
-				if (this.getAutoReconnectForPools()) {
-					setHighAvailability(false);
-				}
-			}
-	
-			return;
-		}
-	}
-
 	/**
-	 * A sub-space of this Connection's database may be selected by setting a
-	 * catalog name. If the driver does not support catalogs, it will silently
-	 * ignore this request
-	 * <p>
-	 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
-	 * </p>
+	 * Prepares a statement on the server (irregardless of the 
+	 * configuration property 'useServerPrepStmts') with the same semantics
+	 * as the java.sql.Connection.prepareStatement() method with the 
+	 * same argument types.
 	 * 
-	 * @param catalog
-	 *            the database for this connection to use
-	 * @throws SQLException
-	 *             if a database access error occurs
+	 * @see java.sql.Connection#prepareStatement(String, int[])
 	 */
-	public void setCatalog(String catalog) throws SQLException {
-		synchronized (getMutex()) {
-			checkClosed();
+	public abstract java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException;
 	
-			if (catalog == null) {
-				throw SQLError.createSQLException("Catalog can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-			
-			if (getUseLocalSessionState()) {
-				if (this.lowerCaseTableNames) {
-					if (this.database.equalsIgnoreCase(catalog)) {
-						return;
-					}
-				} else {
-					if (this.database.equals(catalog)) {
-						return;
-					}
-				}
-			}
-			
-			String quotedId = this.dbmd.getIdentifierQuoteString();
-	
-			if ((quotedId == null) || quotedId.equals(" ")) {
-				quotedId = "";
-			}
-	
-			StringBuffer query = new StringBuffer("USE ");
-			query.append(quotedId);
-			query.append(catalog);
-			query.append(quotedId);
-	
-			execSQL(null, query.toString(), -1, null,
-					java.sql.ResultSet.TYPE_FORWARD_ONLY,
-					java.sql.ResultSet.CONCUR_READ_ONLY, false,
-					this.database, true, false);
-			
-			this.database = catalog;
-		}
-	}
-
 	/**
-	 * @param failedOver
-	 *            The failedOver to set.
-	 */
-	public synchronized void setFailedOver(boolean flag) {
-		this.failedOver = flag;
-	}
-
-	/**
-	 * Sets state for a failed-over connection
+	 * Prepares a statement on the server (irregardless of the 
+	 * configuration property 'useServerPrepStmts') with the same semantics
+	 * as the java.sql.Connection.prepareStatement() method with the 
+	 * same argument types.
 	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
+	 * @see java.sql.Connection#prepareStatement(String, String[])
 	 */
-	private void setFailedOverState() throws SQLException {
-		if (getFailOverReadOnly()) {
-			setReadOnly(true);
-		}
+	public abstract java.sql.PreparedStatement serverPrepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException;
 
-		this.queriesIssuedFailedOver = 0;
-		this.failedOver = true;
-		this.masterFailTimeMillis = System.currentTimeMillis();
-	}
-
 	/**
-	 * @see Connection#setHoldability(int)
+	 * @param failedOver
+	 *            The failedOver to set.
 	 */
-	public void setHoldability(int arg0) throws SQLException {
-		// do nothing
-	}
+	public abstract void setFailedOver(boolean flag);
 
-	public void setInGlobalTx(boolean flag) {
-		this.isInGlobalTx = flag;
-	}
-
-	// exposed for testing
 	/**
 	 * @param preferSlaveDuringFailover
 	 *            The preferSlaveDuringFailover to set.
 	 */
-	public void setPreferSlaveDuringFailover(boolean flag) {
-		this.preferSlaveDuringFailover = flag;
-	}
+	public abstract void setPreferSlaveDuringFailover(boolean flag);
 
-	void setReadInfoMsgEnabled(boolean flag) {
-		this.readInfoMsg = flag;
-	}
-
 	/**
-	 * You can put a connection in read-only mode as a hint to enable database
-	 * optimizations <B>Note:</B> setReadOnly cannot be called while in the
-	 * middle of a transaction
+	 * Sets the comment that will be prepended to all statements
+	 * sent to the server. Do not use slash-star or star-slash tokens 
+	 * in the comment as these will be added by the driver itself.
 	 * 
-	 * @param readOnlyFlag -
-	 *            true enables read-only mode; false disables it
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * @param comment  the comment that will be prepended to all statements
+	 * sent to the server.
 	 */
-	public void setReadOnly(boolean readOnlyFlag) throws SQLException {
-		checkClosed();
-		this.readOnly = readOnlyFlag;
-	}
+	public abstract void setStatementComment(String comment);
 
 	/**
-	 * @see Connection#setSavepoint()
-	 */
-	public java.sql.Savepoint setSavepoint() throws SQLException {
-		MysqlSavepoint savepoint = new MysqlSavepoint();
-
-		setSavepoint(savepoint);
-
-		return savepoint;
-	}
-
-	private void setSavepoint(MysqlSavepoint savepoint) throws SQLException {
-
-		if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
-			synchronized (getMutex()) {
-				checkClosed();
-	
-				StringBuffer savePointQuery = new StringBuffer("SAVEPOINT ");
-				savePointQuery.append('`');
-				savePointQuery.append(savepoint.getSavepointName());
-				savePointQuery.append('`');
-	
-				java.sql.Statement stmt = null;
-	
-				try {
-					stmt = createStatement();
-	
-					stmt.executeUpdate(savePointQuery.toString());
-				} finally {
-					closeStatement(stmt);
-				}
-			}
-		} else {
-			throw new NotImplemented();
-		}
-	}
-
-	/**
-	 * @see Connection#setSavepoint(String)
-	 */
-	public synchronized java.sql.Savepoint setSavepoint(String name) throws SQLException {
-		MysqlSavepoint savepoint = new MysqlSavepoint(name);
-
-		setSavepoint(savepoint);
-
-		return savepoint;
-	}
-
-	/**
-	 * 
-	 */
-	private void setSessionVariables() throws SQLException {
-		if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) {
-			List variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'",
-					false);
-
-			int numVariablesToSet = variablesToSet.size();
-
-			java.sql.Statement stmt = null;
-
-			try {
-				stmt = getMetadataSafeStatement();
-
-				for (int i = 0; i < numVariablesToSet; i++) {
-					String variableValuePair = (String) variablesToSet.get(i);
-
-					if (variableValuePair.startsWith("@")) {
-						stmt.executeUpdate("SET " + variableValuePair);
-					} else {
-						stmt.executeUpdate("SET SESSION " + variableValuePair);
-					}
-				}
-			} finally {
-				if (stmt != null) {
-					stmt.close();
-				}
-			}
-		}
-
-	}
-
-	/**
-     * Attempts to change the transaction isolation level for this
-     * <code>Connection</code> object to the one given.
-     * The constants defined in the interface <code>Connection</code>
-     * are the possible transaction isolation levels.
-     * <P>
-     * <B>Note:</B> If this method is called during a transaction, the result
-     * is implementation-defined.
-     *
-     * @param level one of the following <code>Connection</code> constants:
-     *        <code>Connection.TRANSACTION_READ_UNCOMMITTED</code>,
-     *        <code>Connection.TRANSACTION_READ_COMMITTED</code>,
-     *        <code>Connection.TRANSACTION_REPEATABLE_READ</code>, or
-     *        <code>Connection.TRANSACTION_SERIALIZABLE</code>.
-     *        (Note that <code>Connection.TRANSACTION_NONE</code> cannot be used 
-     *        because it specifies that transactions are not supported.)
-     * @exception SQLException if a database access error occurs
-     *            or the given parameter is not one of the <code>Connection</code>
-     *            constants
-     * @see DatabaseMetaData#supportsTransactionIsolationLevel 
-     * @see #getTransactionIsolation
-     */
-	public synchronized void setTransactionIsolation(int level) throws SQLException {
-		checkClosed();
-
-		if (this.hasIsolationLevels) {
-			String sql = null;
-
-			boolean shouldSendSet = false;
-
-			if (getAlwaysSendSetIsolation()) {
-				shouldSendSet = true;
-			} else {
-				if (level != this.isolationLevel) {
-					shouldSendSet = true;
-				}
-			}
-
-			if (getUseLocalSessionState()) {
-				shouldSendSet = this.isolationLevel != level;
-			}
-
-			if (shouldSendSet) {
-				switch (level) {
-				case java.sql.Connection.TRANSACTION_NONE:
-					throw SQLError.createSQLException("Transaction isolation level "
-							+ "NONE not supported by MySQL");
-
-				case java.sql.Connection.TRANSACTION_READ_COMMITTED:
-					sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";
-
-					break;
-
-				case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
-					sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
-
-					break;
-
-				case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
-					sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
-
-					break;
-
-				case java.sql.Connection.TRANSACTION_SERIALIZABLE:
-					sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";
-
-					break;
-
-				default:
-					throw SQLError.createSQLException("Unsupported transaction "
-							+ "isolation level '" + level + "'",
-							SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
-				}
-
-				execSQL(null, sql, -1, null,
-						java.sql.ResultSet.TYPE_FORWARD_ONLY,
-						java.sql.ResultSet.CONCUR_READ_ONLY,false,
-						this.database, true, false);
-
-				this.isolationLevel = level;
-			}
-		} else {
-			throw SQLError.createSQLException("Transaction Isolation Levels are "
-					+ "not supported on MySQL versions older than 3.23.36.",
-					SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
-		}
-	}
-	
-	/**
-	 * JDBC 2.0 Install a type-map object as the default type-map for this
-	 * connection
-	 * 
-	 * @param map
-	 *            the type mapping
-	 * @throws SQLException
-	 *             if a database error occurs.
-	 */
-	public synchronized void setTypeMap(java.util.Map map) throws SQLException {
-		this.typeMap = map;
-	}
-	
-	/**
-	 * Should we try to connect back to the master? We try when we've been
-	 * failed over >= this.secondsBeforeRetryMaster _or_ we've issued >
-	 * this.queriesIssuedFailedOver
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	private boolean shouldFallBack() {
-		long secondsSinceFailedOver = (System.currentTimeMillis() - this.masterFailTimeMillis) / 1000;
-
-		// Done this way so we can set a condition in the debugger
-		boolean tryFallback = ((secondsSinceFailedOver >= getSecondsBeforeRetryMaster()) || (this.queriesIssuedFailedOver >= getQueriesBeforeRetryMaster()));
-
-		return tryFallback;
-	}
-	
-	/**
 	 * Used by MiniAdmin to shutdown a MySQL server
 	 * 
 	 * @throws SQLException
 	 *             if the command can not be issued.
 	 */
-	public void shutdownServer() throws SQLException {
-		try {
-			this.io.sendCommand(MysqlDefs.SHUTDOWN, null, null, false, null);
-		} catch (Exception ex) {
-			throw SQLError.createSQLException("Unhandled exception '" + ex.toString()
-					+ "'", SQLError.SQL_STATE_GENERAL_ERROR);
-		}
-	}
-	
+	public abstract void shutdownServer() throws SQLException;
+
 	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
+	 * Does the server this connection is connected to
+	 * support quoted isolation levels?
 	 */
-	public boolean supportsIsolationLevel() {
-		return this.hasIsolationLevels;
-	}
-	
+	public abstract boolean supportsIsolationLevel();
+
 	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
+	 * Does the server this connection is connected to
+	 * support quoted identifiers?
 	 */
-	public boolean supportsQuotedIdentifiers() {
-		return this.hasQuotedIdentifiers;
-	}
-	
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	public boolean supportsTransactions() {
-		return this.transactionsSupported;
-	}
-	
-	/**
-	 * Remove the given statement from the list of open statements
-	 * 
-	 * @param stmt
-	 *            the Statement instance to remove
-	 */
-	void unregisterStatement(Statement stmt) {
-		if (this.openStatements != null) {
-			synchronized (this.openStatements) {
-				this.openStatements.remove(stmt);
-			}
-		}
-	}
+	public abstract boolean supportsQuotedIdentifiers();
 
 	/**
-	 * Called by statements on their .close() to let the connection know when it
-	 * is safe to set the connection back to 'default' row limits.
-	 * 
-	 * @param stmt
-	 *            the statement releasing it's max-rows requirement
-	 * @throws SQLException
-	 *             if a database error occurs issuing the statement that sets
-	 *             the limit default.
+	 * Does the server this connection is connected to
+	 * support quoted identifiers?
 	 */
-	void unsetMaxRows(Statement stmt) throws SQLException {
-		synchronized (this.mutex) {
-			if (this.statementsUsingMaxRows != null) {
-				Object found = this.statementsUsingMaxRows.remove(stmt);
+	public abstract boolean supportsTransactions();
 
-				if ((found != null)
-						&& (this.statementsUsingMaxRows.size() == 0)) {
-					execSQL(null, "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1,
-							null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false, 
-							this.database, true, false);
-
-					this.maxRowsChanged = false;
-				}
-			}
-		}
-	}
-	
-	boolean useAnsiQuotedIdentifiers() {
-		return this.useAnsiQuotes;
-	}
-
 	/**
-	 * Has maxRows() been set?
-	 * 
-	 * @return DOCUMENT ME!
+	 * Does the server this connection is connected to
+	 * meet or exceed the given version?
 	 */
-	boolean useMaxRows() {
-		synchronized (this.mutex) {
-			return this.maxRowsChanged;
-		}
-	}
-
-	public boolean versionMeetsMinimum(int major, int minor, int subminor)
-			throws SQLException {
-		checkClosed();
-
-		return this.io.versionMeetsMinimum(major, minor, subminor);
-	}
-
-	protected String getErrorMessageEncoding() {
-		return errorMessageEncoding;
-	}
+	public abstract boolean versionMeetsMinimum(int major, int minor,
+			int subminor) throws SQLException;
 	
-	/*
-	 * For testing failover scenarios
-	 */
-	private boolean hasTriedMasterFlag = false;
+	public abstract void reportQueryTime(long millisOrNanos);
 	
-	public void clearHasTriedMaster() {
-		this.hasTriedMasterFlag = false;
-	}
-	
-	public boolean hasTriedMaster() {
-		return this.hasTriedMasterFlag;
-	}
+	public abstract boolean isAbonormallyLongQuery(long millisOrNanos);
+
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -42,7 +42,7 @@
 	 * @param lastPacketSentTimeMs
 	 * @param underlyingException
 	 */
-	public ConnectionFeatureNotAvailableException(Connection conn,
+	public ConnectionFeatureNotAvailableException(ConnectionImpl conn,
 			long lastPacketSentTimeMs, Exception underlyingException) {
 		super(conn, lastPacketSentTimeMs, underlyingException);
 	}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionImpl.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionImpl.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionImpl.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,5441 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.sql.Blob;
+import java.sql.DatabaseMetaData;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Savepoint;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.Timer;
+import java.util.TreeMap;
+
+import com.mysql.jdbc.log.Log;
+import com.mysql.jdbc.log.LogFactory;
+import com.mysql.jdbc.log.NullLogger;
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+import com.mysql.jdbc.util.LRUCache;
+
+/**
+ * A Connection represents a session with a specific database. Within the
+ * context of a Connection, SQL statements are executed and results are
+ * returned.
+ * <P>
+ * A Connection's database is able to provide information describing its tables,
+ * its supported SQL grammar, its stored procedures, the capabilities of this
+ * connection, etc. This information is obtained with the getMetaData method.
+ * </p>
+ * 
+ * @author Mark Matthews
+ * @version $Id: ConnectionImpl.java 6613 2007-10-04 16:56:05Z mmatthews $
+ * @see java.sql.Connection
+ */
+public class ConnectionImpl extends ConnectionPropertiesImpl implements
+		Connection {
+	private static final String JDBC_LOCAL_CHARACTER_SET_RESULTS = "jdbc.local.character_set_results";
+	
+	/**
+	 * Used as a key for caching callable statements which (may) depend on
+	 * current catalog...In 5.0.x, they don't (currently), but stored procedure
+	 * names soon will, so current catalog is a (hidden) component of the name.
+	 */
+	class CompoundCacheKey {
+		String componentOne;
+
+		String componentTwo;
+
+		int hashCode;
+
+		CompoundCacheKey(String partOne, String partTwo) {
+			this.componentOne = partOne;
+			this.componentTwo = partTwo;
+
+			// Handle first component (in most cases, currentCatalog)
+			// being NULL....
+			this.hashCode = (((this.componentOne != null) ? this.componentOne
+					: "") + this.componentTwo).hashCode();
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.Object#equals(java.lang.Object)
+		 */
+		public boolean equals(Object obj) {
+			if (obj instanceof CompoundCacheKey) {
+				CompoundCacheKey another = (CompoundCacheKey) obj;
+
+				boolean firstPartEqual = false;
+
+				if (this.componentOne == null) {
+					firstPartEqual = (another.componentOne == null);
+				} else {
+					firstPartEqual = this.componentOne
+							.equals(another.componentOne);
+				}
+
+				return (firstPartEqual && this.componentTwo
+						.equals(another.componentTwo));
+			}
+
+			return false;
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.Object#hashCode()
+		 */
+		public int hashCode() {
+			return this.hashCode;
+		}
+	}
+
+	/**
+	 * Marker for character set converter not being available (not written,
+	 * multibyte, etc) Used to prevent multiple instantiation requests.
+	 */
+	private static final Object CHARSET_CONVERTER_NOT_AVAILABLE_MARKER = new Object();
+
+	/**
+	 * The mapping between MySQL charset names and Java charset names.
+	 * Initialized by loadCharacterSetMapping()
+	 */
+	public static Map charsetMap;
+
+	/** Default logger class name */
+	protected static final String DEFAULT_LOGGER_CLASS = "com.mysql.jdbc.log.StandardLogger";
+
+	private final static int HISTOGRAM_BUCKETS = 20;
+
+	/** Logger instance name */
+	private static final String LOGGER_INSTANCE_NAME = "MySQL";
+
+	/**
+	 * Map mysql transaction isolation level name to
+	 * java.sql.Connection.TRANSACTION_XXX
+	 */
+	private static Map mapTransIsolationNameToValue = null;
+
+	/** Null logger shared by all connections at startup */
+	private static final Log NULL_LOGGER = new NullLogger(LOGGER_INSTANCE_NAME);
+
+	private static Map roundRobinStatsMap;
+
+	private static final Map serverCollationByUrl = new HashMap();
+
+	private static final Map serverConfigByUrl = new HashMap();
+
+	private long queryTimeCount;
+	private double queryTimeSum;
+	private double queryTimeSumSquares;
+	private double queryTimeMean;
+	
+	private static Timer cancelTimer;
+	
+	private List connectionLifecycleInterceptors;
+	
+	private static final Constructor JDBC_4_CONNECTION_CTOR;
+	
+	static {
+		mapTransIsolationNameToValue = new HashMap(8);
+		mapTransIsolationNameToValue.put("READ-UNCOMMITED", Constants.integerValueOf(
+				TRANSACTION_READ_UNCOMMITTED));
+		mapTransIsolationNameToValue.put("READ-UNCOMMITTED", Constants.integerValueOf(
+				TRANSACTION_READ_UNCOMMITTED));
+		mapTransIsolationNameToValue.put("READ-COMMITTED", Constants.integerValueOf(
+				TRANSACTION_READ_COMMITTED));
+		mapTransIsolationNameToValue.put("REPEATABLE-READ", Constants.integerValueOf(
+				TRANSACTION_REPEATABLE_READ));
+		mapTransIsolationNameToValue.put("SERIALIZABLE", Constants.integerValueOf(
+				TRANSACTION_SERIALIZABLE));
+		
+		boolean createdNamedTimer = false;
+		
+		// Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named
+		// timer on older VMs.
+		try {
+			Constructor ctr = Timer.class.getConstructor(new Class[] {String.class, Boolean.TYPE});
+			
+			cancelTimer = (Timer)ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE});
+			createdNamedTimer = true;
+		} catch (Throwable t) {
+			createdNamedTimer = false;
+		}
+		
+		if (!createdNamedTimer) {
+			cancelTimer = new Timer(true);
+		}
+		
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_CONNECTION_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4Connection").getConstructor(
+						new Class[] { String.class, Integer.TYPE,
+								Properties.class, String.class, String.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_CONNECTION_CTOR = null;
+		}
+	}
+
+	protected static SQLException appendMessageToException(SQLException sqlEx,
+			String messageToAppend) {
+		String origMessage = sqlEx.getMessage();
+		String sqlState = sqlEx.getSQLState();
+		int vendorErrorCode = sqlEx.getErrorCode();
+
+		StringBuffer messageBuf = new StringBuffer(origMessage.length()
+				+ messageToAppend.length());
+		messageBuf.append(origMessage);
+		messageBuf.append(messageToAppend);
+
+		SQLException sqlExceptionWithNewMessage = SQLError.createSQLException(messageBuf
+				.toString(), sqlState, vendorErrorCode);
+
+		//
+		// Try and maintain the original stack trace,
+		// only works on JDK-1.4 and newer
+		//
+
+		try {
+			// Have to do this with reflection, otherwise older JVMs croak
+			Method getStackTraceMethod = null;
+			Method setStackTraceMethod = null;
+			Object theStackTraceAsObject = null;
+
+			Class stackTraceElementClass = Class
+					.forName("java.lang.StackTraceElement");
+			Class stackTraceElementArrayClass = Array.newInstance(
+					stackTraceElementClass, new int[] { 0 }).getClass();
+
+			getStackTraceMethod = Throwable.class.getMethod("getStackTrace",
+					new Class[] {});
+
+			setStackTraceMethod = Throwable.class.getMethod("setStackTrace",
+					new Class[] { stackTraceElementArrayClass });
+
+			if (getStackTraceMethod != null && setStackTraceMethod != null) {
+				theStackTraceAsObject = getStackTraceMethod.invoke(sqlEx,
+						new Object[0]);
+				setStackTraceMethod.invoke(sqlExceptionWithNewMessage,
+						new Object[] { theStackTraceAsObject });
+			}
+		} catch (NoClassDefFoundError noClassDefFound) {
+
+		} catch (NoSuchMethodException noSuchMethodEx) {
+
+		} catch (Throwable catchAll) {
+
+		}
+
+		return sqlExceptionWithNewMessage;
+	}
+
+	protected static Timer getCancelTimer() {
+		return cancelTimer;
+	}
+
+	
+	/**
+	 * Creates a connection instance -- We need to provide factory-style methods
+	 * so we can support both JDBC3 (and older) and JDBC4 runtimes, otherwise
+	 * the class verifier complains when it tries to load JDBC4-only interface
+	 * classes that are present in JDBC4 method signatures.
+	 */
+
+	protected static Connection getInstance(String hostToConnectTo,
+			int portToConnectTo, Properties info, String databaseToConnectTo,
+			String url) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new ConnectionImpl(hostToConnectTo, portToConnectTo, info,
+					databaseToConnectTo, url);
+		}
+
+		return (Connection) Util.handleNewInstance(JDBC_4_CONNECTION_CTOR,
+				new Object[] {
+							hostToConnectTo, Constants.integerValueOf(portToConnectTo), info,
+							databaseToConnectTo, url });
+	}
+
+	private static synchronized int getNextRoundRobinHostIndex(String url,
+			List hostList) {
+		if (roundRobinStatsMap == null) {
+			roundRobinStatsMap = new HashMap();
+		}
+
+		int[] index = (int[]) roundRobinStatsMap.get(url);
+
+		if (index == null) {
+			index = new int[1];
+			index[0] = -1;
+
+			roundRobinStatsMap.put(url, index);
+		}
+
+		index[0]++;
+
+		if (index[0] >= hostList.size()) {
+			index[0] = 0;
+		}
+
+		return index[0];
+	}
+
+	private static boolean nullSafeCompare(String s1, String s2) {
+		if (s1 == null && s2 == null) {
+			return true;
+		}
+
+		if (s1 == null && s2 != null) {
+			return false;
+		}
+
+		return s1.equals(s2);
+	}
+
+	/** Are we in autoCommit mode? */
+	private boolean autoCommit = true;
+
+	/** A map of SQL to parsed prepared statement parameters. */
+	private Map cachedPreparedStatementParams;
+
+	/**
+	 * For servers > 4.1.0, what character set is the metadata returned in?
+	 */
+	private String characterSetMetadata = null;
+
+	/**
+	 * The character set we want results and result metadata returned in (null ==
+	 * results in any charset, metadata in UTF-8).
+	 */
+	private String characterSetResultsOnServer = null;
+
+	/**
+	 * Holds cached mappings to charset converters to avoid static
+	 * synchronization and at the same time save memory (each charset converter
+	 * takes approx 65K of static data).
+	 */
+	private Map charsetConverterMap = new HashMap(CharsetMapping
+			.getNumberOfCharsetsConfigured());
+
+	/**
+	 * The mapping between MySQL charset names and the max number of chars in
+	 * them. Lazily instantiated via getMaxBytesPerChar().
+	 */
+	private Map charsetToNumBytesMap;
+
+	/** The point in time when this connection was created */
+	private long connectionCreationTimeMillis = 0;
+
+	/** ID used when profiling */
+	private long connectionId;
+
+	/** The database we're currently using (called Catalog in JDBC terms). */
+	private String database = null;
+
+	/** Internal DBMD to use for various database-version specific features */
+	private DatabaseMetaData dbmd = null;
+
+	private TimeZone defaultTimeZone;
+
+	/** The event sink to use for profiling */
+	private ProfileEventSink eventSink;
+
+	private boolean executingFailoverReconnect = false;
+
+	/** Are we failed-over to a non-master host */
+	private boolean failedOver = false;
+
+	/** Why was this connection implicitly closed, if known? (for diagnostics) */
+	private Throwable forceClosedReason;
+
+	/** Where was this connection implicitly closed? (for diagnostics) */
+	private Throwable forcedClosedLocation;
+
+	/** Does the server suuport isolation levels? */
+	private boolean hasIsolationLevels = false;
+
+	/** Does this version of MySQL support quoted identifiers? */
+	private boolean hasQuotedIdentifiers = false;
+
+	/** The hostname we're connected to */
+	private String host = null;
+
+	/** The list of host(s) to try and connect to */
+	private List hostList = null;
+
+	/** How many hosts are in the host list? */
+	private int hostListSize = 0;
+
+	/**
+	 * We need this 'bootstrapped', because 4.1 and newer will send fields back
+	 * with this even before we fill this dynamically from the server.
+	 */
+	private String[] indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
+	
+	/** The I/O abstraction interface (network conn to MySQL server */
+	private MysqlIO io = null;
+	
+	private boolean isClientTzUTC = false;
+
+	/** Has this connection been closed? */
+	private boolean isClosed = true;
+
+	/** Is this connection associated with a global tx? */
+	private boolean isInGlobalTx = false;
+
+	/** Is this connection running inside a JDK-1.3 VM? */
+	private boolean isRunningOnJDK13 = false;
+
+	/** isolation level */
+	private int isolationLevel = java.sql.Connection.TRANSACTION_READ_COMMITTED;
+
+	private boolean isServerTzUTC = false;
+
+	/** When did the last query finish? */
+	private long lastQueryFinishedTime = 0;
+
+	/** The logger we're going to use */
+	private Log log = NULL_LOGGER;
+
+	/**
+	 * If gathering metrics, what was the execution time of the longest query so
+	 * far ?
+	 */
+	private long longestQueryTimeMs = 0;
+
+	/** Is the server configured to use lower-case table names only? */
+	private boolean lowerCaseTableNames = false;
+
+	/** When did the master fail? */
+	private long masterFailTimeMillis = 0L;
+
+	/**
+	 * The largest packet we can send (changed once we know what the server
+	 * supports, we get this at connection init).
+	 */
+	private int maxAllowedPacket = 65536;
+
+	private long maximumNumberTablesAccessed = 0;
+
+	/** Has the max-rows setting been changed from the default? */
+	private boolean maxRowsChanged = false;
+
+	/** When was the last time we reported metrics? */
+	private long metricsLastReportedMs;
+
+	private long minimumNumberTablesAccessed = Long.MAX_VALUE;
+
+	/** Mutex */
+	private final Object mutex = new Object();
+
+	/** The JDBC URL we're using */
+	private String myURL = null;
+
+	/** Does this connection need to be tested? */
+	private boolean needsPing = false;
+
+	private int netBufferLength = 16384;
+
+	private boolean noBackslashEscapes = false;
+
+	private long numberOfPreparedExecutes = 0;
+
+	private long numberOfPrepares = 0;
+
+	private long numberOfQueriesIssued = 0;
+
+	private long numberOfResultSetsCreated = 0;
+
+	private long[] numTablesMetricsHistBreakpoints;
+
+	private int[] numTablesMetricsHistCounts;
+
+	private long[] oldHistBreakpoints = null;
+
+	private int[] oldHistCounts = null;
+
+	/** A map of currently open statements */
+	private Map openStatements;
+
+	private LRUCache parsedCallableStatementCache;
+
+	private boolean parserKnowsUnicode = false;
+
+	/** The password we used */
+	private String password = null;
+
+	private long[] perfMetricsHistBreakpoints;
+
+	private int[] perfMetricsHistCounts;
+
+	/** Point of origin where this Connection was created */
+	private Throwable pointOfOrigin;
+
+	/** The port number we're connected to (defaults to 3306) */
+	private int port = 3306;
+
+	/**
+	 * Used only when testing failover functionality for regressions, causes the
+	 * failover code to not retry the master first
+	 */
+	private boolean preferSlaveDuringFailover = false;
+
+	/** Properties for this connection specified by user */
+	protected Properties props = null;
+
+	/** Number of queries we've issued since the master failed */
+	private long queriesIssuedFailedOver = 0;
+
+	/** Should we retrieve 'info' messages from the server? */
+	private boolean readInfoMsg = false;
+
+	/** Are we in read-only mode? */
+	private boolean readOnly = false;
+
+	/** Cache of ResultSet metadata */
+	protected LRUCache resultSetMetadataCache;
+	
+	/** The timezone of the server */
+	private TimeZone serverTimezoneTZ = null;
+
+	/** The map of server variables that we retrieve at connection init. */
+	private Map serverVariables = null;
+
+	private long shortestQueryTimeMs = Long.MAX_VALUE;
+
+	/** A map of statements that have had setMaxRows() called on them */
+	private Map statementsUsingMaxRows;
+
+	private double totalQueryTimeMs = 0;
+
+	/** Are transactions supported by the MySQL server we are connected to? */
+	private boolean transactionsSupported = false;
+
+	/**
+	 * The type map for UDTs (not implemented, but used by some third-party
+	 * vendors, most notably IBM WebSphere)
+	 */
+	private Map typeMap;
+
+	/** Has ANSI_QUOTES been enabled on the server? */
+	private boolean useAnsiQuotes = false;
+
+	/** The user we're connected as */
+	private String user = null;
+	
+	/**
+	 * Should we use server-side prepared statements? (auto-detected, but can be
+	 * disabled by user)
+	 */
+	private boolean useServerPreparedStmts = false;
+
+	private LRUCache serverSideStatementCheckCache;
+	private LRUCache serverSideStatementCache;
+	private Calendar sessionCalendar;
+	
+	private Calendar utcCalendar;
+	
+	private String origHostToConnectTo;
+
+	// we don't want to be able to publicly clone this...
+	
+	private int origPortToConnectTo;
+
+	private String origDatabaseToConnectTo;
+
+	private String errorMessageEncoding = "Cp1252"; // to begin with, changes after we talk to the server
+	
+	private boolean usePlatformCharsetConverters;
+	
+	/*
+	 * For testing failover scenarios
+	 */
+	private boolean hasTriedMasterFlag = false;
+
+	/**
+	 * The comment (if any) that we'll prepend to all statements
+	 * sent to the server (to show up in "SHOW PROCESSLIST")
+	 */
+	private String statementComment = null;
+
+	/**'
+	 * For the delegate only
+	 */
+	protected ConnectionImpl() {	
+	}
+	
+	/**
+	 * Creates a connection to a MySQL Server.
+	 * 
+	 * @param hostToConnectTo
+	 *            the hostname of the database server
+	 * @param portToConnectTo
+	 *            the port number the server is listening on
+	 * @param info
+	 *            a Properties[] list holding the user and password
+	 * @param databaseToConnectTo
+	 *            the database to connect to
+	 * @param url
+	 *            the URL of the connection
+	 * @param d
+	 *            the Driver instantation of the connection
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info,
+			String databaseToConnectTo, String url)
+			throws SQLException {
+		this.charsetToNumBytesMap = new HashMap();
+	
+		this.connectionCreationTimeMillis = System.currentTimeMillis();
+		this.pointOfOrigin = new Throwable();
+		
+		// Stash away for later, used to clone this connection for Statement.cancel
+		// and Statement.setQueryTimeout().
+		//
+		
+		this.origHostToConnectTo = hostToConnectTo;
+		this.origPortToConnectTo = portToConnectTo;
+		this.origDatabaseToConnectTo = databaseToConnectTo;
+
+		try {
+			Blob.class.getMethod("truncate", new Class[] {Long.TYPE});
+			
+			this.isRunningOnJDK13 = false;
+		} catch (NoSuchMethodException nsme) {
+			this.isRunningOnJDK13 = true;
+		}
+		
+		this.sessionCalendar = new GregorianCalendar();
+		this.utcCalendar = new GregorianCalendar();
+		this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
+		
+		//
+		// Normally, this code would be in initializeDriverProperties,
+		// but we need to do this as early as possible, so we can start
+		// logging to the 'correct' place as early as possible...this.log
+		// points to 'NullLogger' for every connection at startup to avoid
+		// NPEs and the overhead of checking for NULL at every logging call.
+		//
+		// We will reset this to the configured logger during properties
+		// initialization.
+		//
+		this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME);
+
+		// We store this per-connection, due to static synchronization
+		// issues in Java's built-in TimeZone class...
+		this.defaultTimeZone = Util.getDefaultTimeZone();
+		
+		if ("GMT".equalsIgnoreCase(this.defaultTimeZone.getID())) {
+			this.isClientTzUTC = true;
+		} else {
+			this.isClientTzUTC = false;
+		}
+
+		this.openStatements = new HashMap();
+		this.serverVariables = new HashMap();
+		this.hostList = new ArrayList();
+
+		if (hostToConnectTo == null) {
+			this.host = "localhost";
+			this.hostList.add(this.host);
+		} else if (hostToConnectTo.indexOf(',') != -1) {
+			// multiple hosts separated by commas (failover)
+			StringTokenizer hostTokenizer = new StringTokenizer(
+					hostToConnectTo, ",", false);
+
+			while (hostTokenizer.hasMoreTokens()) {
+				this.hostList.add(hostTokenizer.nextToken().trim());
+			}
+		} else {
+			this.host = hostToConnectTo;
+			this.hostList.add(this.host);
+		}
+
+		this.hostListSize = this.hostList.size();
+		this.port = portToConnectTo;
+
+		if (databaseToConnectTo == null) {
+			databaseToConnectTo = "";
+		}
+
+		this.database = databaseToConnectTo;
+		this.myURL = url;
+		this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
+		this.password = info
+				.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
+
+		if ((this.user == null) || this.user.equals("")) {
+			this.user = "";
+		}
+
+		if (this.password == null) {
+			this.password = "";
+		}
+
+		this.props = info;
+		initializeDriverProperties(info);
+
+		try {
+			createNewIO(false);
+			this.dbmd = getMetaData();
+		} catch (SQLException ex) {
+			cleanup(ex);
+
+			// don't clobber SQL exceptions
+			throw ex;
+		} catch (Exception ex) {
+			cleanup(ex);
+
+			StringBuffer mesg = new StringBuffer(128);
+
+			if (getParanoid()) {
+				mesg.append("Cannot connect to MySQL server on ");
+				mesg.append(this.host);
+				mesg.append(":");
+				mesg.append(this.port);
+				mesg.append(".\n\n");
+				mesg.append("Make sure that there is a MySQL server ");
+				mesg.append("running on the machine/port you are trying ");
+				mesg
+						.append("to connect to and that the machine this software is "
+								+ "running on ");
+				mesg.append("is able to connect to this host/port "
+						+ "(i.e. not firewalled). ");
+				mesg
+						.append("Also make sure that the server has not been started "
+								+ "with the --skip-networking ");
+				mesg.append("flag.\n\n");
+			} else {
+				mesg.append("Unable to connect to database.");
+			}
+
+			mesg.append("Underlying exception: \n\n");
+			mesg.append(ex.getClass().getName());
+
+			if (!getParanoid()) {
+				mesg.append(Util.stackTraceToString(ex));
+			}
+
+			throw SQLError.createSQLException(mesg.toString(),
+					SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE);
+		}
+	}
+
+	private void addToHistogram(int[] histogramCounts,
+			long[] histogramBreakpoints, long value, int numberOfTimes,
+			long currentLowerBound, long currentUpperBound) {
+		if (histogramCounts == null) {
+			createInitialHistogram(histogramBreakpoints,
+					currentLowerBound, currentUpperBound);
+		}
+
+		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
+			if (histogramBreakpoints[i] >= value) {
+				histogramCounts[i] += numberOfTimes;
+
+				break;
+			}
+		}
+	}
+
+	private void addToPerformanceHistogram(long value, int numberOfTimes) {
+		checkAndCreatePerformanceHistogram();
+
+		addToHistogram(this.perfMetricsHistCounts,
+				this.perfMetricsHistBreakpoints, value, numberOfTimes,
+				this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
+						: this.shortestQueryTimeMs, this.longestQueryTimeMs);
+	}
+
+	private void addToTablesAccessedHistogram(long value, int numberOfTimes) {
+		checkAndCreateTablesAccessedHistogram();
+
+		addToHistogram(this.numTablesMetricsHistCounts,
+				this.numTablesMetricsHistBreakpoints, value, numberOfTimes,
+				this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
+						: this.minimumNumberTablesAccessed,
+				this.maximumNumberTablesAccessed);
+	}
+	
+	/**
+	 * Builds the map needed for 4.1.0 and newer servers that maps field-level
+	 * charset/collation info to a java character encoding name.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void buildCollationMapping() throws SQLException {
+		if (versionMeetsMinimum(4, 1, 0)) {
+
+			TreeMap sortedCollationMap = null;
+
+			if (getCacheServerConfiguration()) {
+				synchronized (serverConfigByUrl) {
+					sortedCollationMap = (TreeMap) serverCollationByUrl
+							.get(getURL());
+				}
+			}
+
+			java.sql.Statement stmt = null;
+			java.sql.ResultSet results = null;
+
+			try {
+				if (sortedCollationMap == null) {
+					sortedCollationMap = new TreeMap();
+
+					stmt = createStatement();
+
+					if (stmt.getMaxRows() != 0) {
+						stmt.setMaxRows(0);
+					}
+
+					results = stmt
+							.executeQuery("SHOW COLLATION");
+
+					while (results.next()) {
+						String charsetName = results.getString(2);
+						Integer charsetIndex = Constants.integerValueOf(results.getInt(3));
+
+						sortedCollationMap.put(charsetIndex, charsetName);
+					}
+
+					if (getCacheServerConfiguration()) {
+						synchronized (serverConfigByUrl) {
+							serverCollationByUrl.put(getURL(),
+									sortedCollationMap);
+						}
+					}
+
+				}
+
+				// Now, merge with what we already know
+				int highestIndex = ((Integer) sortedCollationMap.lastKey())
+						.intValue();
+
+				if (CharsetMapping.INDEX_TO_CHARSET.length > highestIndex) {
+					highestIndex = CharsetMapping.INDEX_TO_CHARSET.length;
+				}
+
+				this.indexToCharsetMapping = new String[highestIndex + 1];
+
+				for (int i = 0; i < CharsetMapping.INDEX_TO_CHARSET.length; i++) {
+					this.indexToCharsetMapping[i] = CharsetMapping.INDEX_TO_CHARSET[i];
+				}
+
+				for (Iterator indexIter = sortedCollationMap.entrySet()
+						.iterator(); indexIter.hasNext();) {
+					Map.Entry indexEntry = (Map.Entry) indexIter.next();
+
+					String mysqlCharsetName = (String) indexEntry.getValue();
+
+					this.indexToCharsetMapping[((Integer) indexEntry.getKey())
+							.intValue()] = CharsetMapping
+							.getJavaEncodingForMysqlEncoding(mysqlCharsetName,
+									this);
+				}
+			} catch (java.sql.SQLException e) {
+				throw e;
+			} finally {
+				if (results != null) {
+					try {
+						results.close();
+					} catch (java.sql.SQLException sqlE) {
+						// ignore
+					}
+				}
+
+				if (stmt != null) {
+					try {
+						stmt.close();
+					} catch (java.sql.SQLException sqlE) {
+						// ignore
+					}
+				}
+			}
+		} else {
+			// Safety, we already do this as an initializer, but this makes
+			// the intent more clear
+			this.indexToCharsetMapping = CharsetMapping.INDEX_TO_CHARSET;
+		}
+	}
+
+	private boolean canHandleAsServerPreparedStatement(String sql) 
+		throws SQLException {
+		if (sql == null || sql.length() == 0) {
+			return true;
+		}
+
+		if (getCachePreparedStatements()) {
+			synchronized (this.serverSideStatementCheckCache) {
+				Boolean flag = (Boolean)this.serverSideStatementCheckCache.get(sql);
+				
+				if (flag != null) {
+					return flag.booleanValue();
+				}
+					
+				boolean canHandle = canHandleAsServerPreparedStatementNoCache(sql);
+				
+				if (sql.length() < getPreparedStatementCacheSqlLimit()) {
+					this.serverSideStatementCheckCache.put(sql, 
+							canHandle ? Boolean.TRUE : Boolean.FALSE);
+				}
+					
+				return canHandle;
+			}
+		}
+		
+		return canHandleAsServerPreparedStatementNoCache(sql);
+	}
+
+	private boolean canHandleAsServerPreparedStatementNoCache(String sql) 
+		throws SQLException {
+		
+		// Can't use server-side prepare for CALL
+		if (StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "CALL")) {
+			return false;
+		}
+		
+		boolean canHandleAsStatement = true;
+		
+		if (!versionMeetsMinimum(5, 0, 7) && 
+				(StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql, "SELECT")
+				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
+						"DELETE")
+				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
+						"INSERT")
+				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
+						"UPDATE")
+				|| StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(sql,
+						"REPLACE"))) {
+
+			// check for limit ?[,?]
+
+			/*
+			 * The grammar for this (from the server) is: ULONG_NUM | ULONG_NUM
+			 * ',' ULONG_NUM | ULONG_NUM OFFSET_SYM ULONG_NUM
+			 */
+
+			int currentPos = 0;
+			int statementLength = sql.length();
+			int lastPosToLook = statementLength - 7; // "LIMIT ".length()
+			boolean allowBackslashEscapes = !this.noBackslashEscapes;
+			char quoteChar = this.useAnsiQuotes ? '"' : '\'';
+			boolean foundLimitWithPlaceholder = false;
+
+			while (currentPos < lastPosToLook) {
+				int limitStart = StringUtils.indexOfIgnoreCaseRespectQuotes(
+						currentPos, sql, "LIMIT ", quoteChar,
+						allowBackslashEscapes);
+
+				if (limitStart == -1) {
+					break;
+				}
+
+				currentPos = limitStart + 7;
+
+				while (currentPos < statementLength) {
+					char c = sql.charAt(currentPos);
+
+					//
+					// Have we reached the end
+					// of what can be in a LIMIT clause?
+					//
+
+					if (!Character.isDigit(c) && !Character.isWhitespace(c)
+							&& c != ',' && c != '?') {
+						break;
+					}
+
+					if (c == '?') {
+						foundLimitWithPlaceholder = true;
+						break;
+					}
+
+					currentPos++;
+				}
+			}
+
+			canHandleAsStatement = !foundLimitWithPlaceholder;
+		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE TABLE")) {
+			canHandleAsStatement = false;
+		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "DO")) {
+			canHandleAsStatement = false;
+		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "SET")) {
+			canHandleAsStatement = false;
+		}
+
+		
+		
+		return canHandleAsStatement;
+	}
+
+	/**
+	 * Changes the user on this connection by performing a re-authentication. If
+	 * authentication fails, the connection will remain under the context of the
+	 * current user.
+	 * 
+	 * @param userName
+	 *            the username to authenticate with
+	 * @param newPassword
+	 *            the password to authenticate with
+	 * @throws SQLException
+	 *             if authentication fails, or some other error occurs while
+	 *             performing the command.
+	 */
+	public void changeUser(String userName, String newPassword)
+			throws SQLException {
+		if ((userName == null) || userName.equals("")) {
+			userName = "";
+		}
+
+		if (newPassword == null) {
+			newPassword = "";
+		}
+
+		this.io.changeUser(userName, newPassword, this.database);
+		this.user = userName;
+		this.password = newPassword;
+
+		if (versionMeetsMinimum(4, 1, 0)) {
+			configureClientCharacterSet(true);
+		}
+		
+		setupServerForTruncationChecks();
+	}
+
+	private boolean characterSetNamesMatches(String mysqlEncodingName) {
+		// set names is equivalent to character_set_client ..._results and ..._connection,
+		// but we set _results later, so don't check it here.
+		
+		return (mysqlEncodingName != null && 
+				mysqlEncodingName.equalsIgnoreCase((String)this.serverVariables.get("character_set_client")) &&
+				mysqlEncodingName.equalsIgnoreCase((String)this.serverVariables.get("character_set_connection")));
+	}
+
+	private void checkAndCreatePerformanceHistogram() {
+		if (this.perfMetricsHistCounts == null) {
+			this.perfMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
+		}
+
+		if (this.perfMetricsHistBreakpoints == null) {
+			this.perfMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
+		}
+	}
+
+	private void checkAndCreateTablesAccessedHistogram() {
+		if (this.numTablesMetricsHistCounts == null) {
+			this.numTablesMetricsHistCounts = new int[HISTOGRAM_BUCKETS];
+		}
+
+		if (this.numTablesMetricsHistBreakpoints == null) {
+			this.numTablesMetricsHistBreakpoints = new long[HISTOGRAM_BUCKETS];
+		}
+	}
+
+	protected void checkClosed() throws SQLException {
+		if (this.isClosed) {
+			StringBuffer messageBuf = new StringBuffer(
+					"No operations allowed after connection closed.");
+
+			if (this.forcedClosedLocation != null || this.forceClosedReason != null) {
+				messageBuf
+				.append("Connection was implicitly closed ");
+			}
+			
+			if (this.forcedClosedLocation != null) {
+				messageBuf.append("\n\n at (stack trace):\n");
+				messageBuf.append(Util
+						.stackTraceToString(this.forcedClosedLocation));
+			}
+
+			if (this.forceClosedReason != null) {
+				if (this.forcedClosedLocation != null) {
+					messageBuf.append("\n\nDue ");
+				} else {
+					messageBuf.append("due ");
+				}
+				
+				messageBuf.append("to underlying exception/error:\n");
+				messageBuf.append(Util
+						.stackTraceToString(this.forceClosedReason));
+			}
+
+			throw SQLError.createSQLException(messageBuf.toString(),
+					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
+		}
+	}
+
+	/**
+	 * If useUnicode flag is set and explicit client character encoding isn't
+	 * specified then assign encoding from server if any.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void checkServerEncoding() throws SQLException {
+		if (getUseUnicode() && (getEncoding() != null)) {
+			// spec'd by client, don't map
+			return;
+		}
+
+		String serverEncoding = (String) this.serverVariables
+				.get("character_set");
+
+		if (serverEncoding == null) {
+			// must be 4.1.1 or newer?
+			serverEncoding = (String) this.serverVariables
+					.get("character_set_server");
+		}
+
+		String mappedServerEncoding = null;
+
+		if (serverEncoding != null) {
+			mappedServerEncoding = CharsetMapping
+					.getJavaEncodingForMysqlEncoding(serverEncoding
+							.toUpperCase(Locale.ENGLISH), this);
+		}
+
+		//
+		// First check if we can do the encoding ourselves
+		//
+		if (!getUseUnicode() && (mappedServerEncoding != null)) {
+			SingleByteCharsetConverter converter = getCharsetConverter(mappedServerEncoding);
+
+			if (converter != null) { // we know how to convert this ourselves
+				setUseUnicode(true); // force the issue
+				setEncoding(mappedServerEncoding);
+
+				return;
+			}
+		}
+
+		//
+		// Now, try and find a Java I/O converter that can do
+		// the encoding for us
+		//
+		if (serverEncoding != null) {
+			if (mappedServerEncoding == null) {
+				// We don't have a mapping for it, so try
+				// and canonicalize the name....
+				if (Character.isLowerCase(serverEncoding.charAt(0))) {
+					char[] ach = serverEncoding.toCharArray();
+					ach[0] = Character.toUpperCase(serverEncoding.charAt(0));
+					setEncoding(new String(ach));
+				}
+			}
+
+			if (mappedServerEncoding == null) {
+				throw SQLError.createSQLException("Unknown character encoding on server '"
+						+ serverEncoding
+						+ "', use 'characterEncoding=' property "
+						+ " to provide correct mapping",
+						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+			}
+
+			//
+			// Attempt to use the encoding, and bail out if it
+			// can't be used
+			//
+			try {
+				"abc".getBytes(mappedServerEncoding);
+				setEncoding(mappedServerEncoding);
+				setUseUnicode(true);
+			} catch (UnsupportedEncodingException UE) {
+				throw SQLError.createSQLException(
+						"The driver can not map the character encoding '"
+								+ getEncoding()
+								+ "' that your server is using "
+								+ "to a character encoding your JVM understands. You "
+								+ "can specify this mapping manually by adding \"useUnicode=true\" "
+								+ "as well as \"characterEncoding=[an_encoding_your_jvm_understands]\" "
+								+ "to your JDBC URL.", "0S100");
+			}
+		}
+	}
+
+	/**
+	 * Set transaction isolation level to the value received from server if any.
+	 * Is called by connectionInit(...)
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void checkTransactionIsolationLevel() throws SQLException {
+		String txIsolationName = null;
+
+		if (versionMeetsMinimum(4, 0, 3)) {
+			txIsolationName = "tx_isolation";
+		} else {
+			txIsolationName = "transaction_isolation";
+		}
+
+		String s = (String) this.serverVariables.get(txIsolationName);
+
+		if (s != null) {
+			Integer intTI = (Integer) mapTransIsolationNameToValue.get(s);
+
+			if (intTI != null) {
+				this.isolationLevel = intTI.intValue();
+			}
+		}
+	}
+
+	/**
+	 * Clobbers the physical network connection and marks
+	 * this connection as closed.
+	 * 
+	 * @throws SQLException
+	 */
+	protected void abortInternal() throws SQLException {
+		io.forceClose();
+		io = null;
+		isClosed = true;
+		cleanup(null);
+	}
+	
+	/**
+	 * Destroys this connection and any underlying resources
+	 * 
+	 * @param fromWhere
+	 *            DOCUMENT ME!
+	 * @param whyCleanedUp
+	 *            DOCUMENT ME!
+	 */
+	private void cleanup(Throwable whyCleanedUp) {
+		try {
+			if ((this.io != null) && !isClosed()) {
+				realClose(false, false, false, whyCleanedUp);
+			} else if (this.io != null) {
+				this.io.forceClose();
+			}
+		} catch (SQLException sqlEx) {
+			// ignore, we're going away.
+			;
+		}
+
+		this.isClosed = true;
+	}
+
+	public void clearHasTriedMaster() {
+		this.hasTriedMasterFlag = false;
+	}
+	
+	/**
+	 * After this call, getWarnings returns null until a new warning is reported
+	 * for this connection.
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void clearWarnings() throws SQLException {
+		// firstWarning = null;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param sql
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.PreparedStatement clientPrepareStatement(String sql)
+			throws SQLException {
+		return clientPrepareStatement(sql,
+				java.sql.ResultSet.TYPE_SCROLL_SENSITIVE,
+				java.sql.ResultSet.CONCUR_READ_ONLY);
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int)
+	 */
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException {
+		java.sql.PreparedStatement pStmt = clientPrepareStatement(sql);
+
+		((com.mysql.jdbc.PreparedStatement) pStmt)
+				.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
+
+		return pStmt;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param sql
+	 *            DOCUMENT ME!
+	 * @param resultSetType
+	 *            DOCUMENT ME!
+	 * @param resultSetConcurrency
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
+	}
+
+
+	
+	protected java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency, 
+			boolean processEscapeCodesIfNeeded) throws SQLException {
+		checkClosed();
+
+		String nativeSql = processEscapeCodesIfNeeded && getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
+		
+		PreparedStatement pStmt = null;
+
+		if (getCachePreparedStatements()) {
+			synchronized (this.cachedPreparedStatementParams) {
+				PreparedStatement.ParseInfo pStmtInfo = (PreparedStatement.ParseInfo) this.cachedPreparedStatementParams
+						.get(nativeSql);
+	
+				if (pStmtInfo == null) {
+					pStmt = com.mysql.jdbc.PreparedStatement.getInstance(this, nativeSql,
+							this.database);
+	
+					PreparedStatement.ParseInfo parseInfo = pStmt.getParseInfo();
+	
+					if (parseInfo.statementLength < getPreparedStatementCacheSqlLimit()) {
+						if (this.cachedPreparedStatementParams.size() >= getPreparedStatementCacheSize()) {
+							Iterator oldestIter = this.cachedPreparedStatementParams
+									.keySet().iterator();
+							long lruTime = Long.MAX_VALUE;
+							String oldestSql = null;
+	
+							while (oldestIter.hasNext()) {
+								String sqlKey = (String) oldestIter.next();
+								PreparedStatement.ParseInfo lruInfo = (PreparedStatement.ParseInfo) this.cachedPreparedStatementParams
+										.get(sqlKey);
+	
+								if (lruInfo.lastUsed < lruTime) {
+									lruTime = lruInfo.lastUsed;
+									oldestSql = sqlKey;
+								}
+							}
+	
+							if (oldestSql != null) {
+								this.cachedPreparedStatementParams
+										.remove(oldestSql);
+							}
+						}
+	
+						this.cachedPreparedStatementParams.put(nativeSql, pStmt
+								.getParseInfo());
+					}
+				} else {
+					pStmtInfo.lastUsed = System.currentTimeMillis();
+					pStmt = new com.mysql.jdbc.PreparedStatement(this, nativeSql,
+							this.database, pStmtInfo);
+				}
+			}
+		} else {
+			pStmt = com.mysql.jdbc.PreparedStatement.getInstance(this, nativeSql,
+					this.database);
+		}
+
+		pStmt.setResultSetType(resultSetType);
+		pStmt.setResultSetConcurrency(resultSetConcurrency);
+
+		return pStmt;
+	}
+	
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, int[])
+	 */
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException {
+		
+		PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
+		
+		pStmt
+				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
+						&& (autoGenKeyIndexes.length > 0));
+
+		return pStmt;
+	}
+
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, String[])
+	 */
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException {
+		PreparedStatement pStmt = (PreparedStatement) clientPrepareStatement(sql);
+
+		pStmt
+				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
+						&& (autoGenKeyColNames.length > 0));
+
+		return pStmt;
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		return clientPrepareStatement(sql, resultSetType, resultSetConcurrency, true);
+	}
+	
+	// --------------------------JDBC 2.0-----------------------------
+
+	/**
+	 * In some cases, it is desirable to immediately release a Connection's
+	 * database and JDBC resources instead of waiting for them to be
+	 * automatically released (cant think why off the top of my head) <B>Note:</B>
+	 * A Connection is automatically closed when it is garbage collected.
+	 * Certain fatal errors also result in a closed connection.
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public synchronized void close() throws SQLException {
+		if (this.connectionLifecycleInterceptors != null) {
+			new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
+				void forEach(Object each) throws SQLException {
+					((ConnectionLifecycleInterceptor)each).close();
+				}
+			}.doForAll();
+		}
+		
+		realClose(true, true, false, null);
+	}
+
+	/**
+	 * Closes all currently open statements.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void closeAllOpenStatements() throws SQLException {
+		SQLException postponedException = null;
+
+		if (this.openStatements != null) {
+			List currentlyOpenStatements = new ArrayList(); // we need this to
+			// avoid
+			// ConcurrentModificationEx
+
+			for (Iterator iter = this.openStatements.keySet().iterator(); iter
+					.hasNext();) {
+				currentlyOpenStatements.add(iter.next());
+			}
+
+			int numStmts = currentlyOpenStatements.size();
+
+			for (int i = 0; i < numStmts; i++) {
+				StatementImpl stmt = (StatementImpl) currentlyOpenStatements.get(i);
+
+				try {
+					stmt.realClose(false, true);
+				} catch (SQLException sqlEx) {
+					postponedException = sqlEx; // throw it later, cleanup all
+					// statements first
+				}
+			}
+
+			if (postponedException != null) {
+				throw postponedException;
+			}
+		}
+	}
+
+	private void closeStatement(java.sql.Statement stmt) {
+		if (stmt != null) {
+			try {
+				stmt.close();
+			} catch (SQLException sqlEx) {
+				; // ignore
+			}
+
+			stmt = null;
+		}
+	}
+
+	/**
+	 * The method commit() makes all changes made since the previous
+	 * commit/rollback permanent and releases any database locks currently held
+	 * by the Connection. This method should only be used when auto-commit has
+	 * been disabled.
+	 * <p>
+	 * <b>Note:</b> MySQL does not support transactions, so this method is a
+	 * no-op.
+	 * </p>
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * @see setAutoCommit
+	 */
+	public void commit() throws SQLException {
+		synchronized (getMutex()) {
+			checkClosed();
+			
+			try {
+				if (this.connectionLifecycleInterceptors != null) {
+					IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
+
+						void forEach(Object each) throws SQLException {
+							if (!((ConnectionLifecycleInterceptor)each).commit()) {
+								this.stopIterating = true;
+							}
+						}
+					};
+					
+					iter.doForAll();
+					
+					if (!iter.fullIteration()) {
+						return;
+					}
+				}
+				
+				// no-op if _relaxAutoCommit == true
+				if (this.autoCommit && !getRelaxAutoCommit()) {
+					throw SQLError.createSQLException("Can't call commit when autocommit=true");
+				} else if (this.transactionsSupported) {
+					if (getUseLocalSessionState() && versionMeetsMinimum(5, 0, 0)) {
+						if (!this.io.inTransactionOnServer()) {
+							return; // effectively a no-op
+						}
+					}
+					
+					execSQL(null, "commit", -1, null,
+							java.sql.ResultSet.TYPE_FORWARD_ONLY,
+							java.sql.ResultSet.CONCUR_READ_ONLY, false,
+							this.database, null,
+							false);
+				}
+			} catch (SQLException sqlException) {
+				if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
+						.equals(sqlException.getSQLState())) {
+					throw SQLError.createSQLException(
+							"Communications link failure during commit(). Transaction resolution unknown.",
+							SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN);
+				}
+	
+				throw sqlException;
+			} finally {
+				this.needsPing = this.getReconnectAtTxEnd();
+			}
+	
+			return;
+		}
+	}
+	
+	/**
+	 * Configures client-side properties for character set information.
+	 * 
+	 * @throws SQLException
+	 *             if unable to configure the specified character set.
+	 */
+	private void configureCharsetProperties() throws SQLException {
+		if (getEncoding() != null) {
+			// Attempt to use the encoding, and bail out if it
+			// can't be used
+			try {
+				String testString = "abc";
+				testString.getBytes(getEncoding());
+			} catch (UnsupportedEncodingException UE) {
+				// Try the MySQL character encoding, then....
+				String oldEncoding = getEncoding();
+
+				setEncoding(CharsetMapping.getJavaEncodingForMysqlEncoding(
+						oldEncoding, this));
+
+				if (getEncoding() == null) {
+					throw SQLError.createSQLException(
+							"Java does not support the MySQL character encoding "
+									+ " " + "encoding '" + oldEncoding + "'.",
+							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+				}
+
+				try {
+					String testString = "abc";
+					testString.getBytes(getEncoding());
+				} catch (UnsupportedEncodingException encodingEx) {
+					throw SQLError.createSQLException("Unsupported character "
+							+ "encoding '" + getEncoding() + "'.",
+							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Sets up client character set for MySQL-4.1 and newer if the user This
+	 * must be done before any further communication with the server!
+	 * 
+	 * @return true if this routine actually configured the client character
+	 *         set, or false if the driver needs to use 'older' methods to
+	 *         detect the character set, as it is connected to a MySQL server
+	 *         older than 4.1.0
+	 * @throws SQLException
+	 *             if an exception happens while sending 'SET NAMES' to the
+	 *             server, or the server sends character set information that
+	 *             the client doesn't know about.
+	 */
+	private boolean configureClientCharacterSet(boolean dontCheckServerMatch) throws SQLException {
+		String realJavaEncoding = getEncoding();
+		boolean characterSetAlreadyConfigured = false;
+
+		try {
+			if (versionMeetsMinimum(4, 1, 0)) {
+				characterSetAlreadyConfigured = true;
+
+				setUseUnicode(true);
+
+				configureCharsetProperties();
+				realJavaEncoding = getEncoding(); // we need to do this again
+				// to grab this for
+				// versions > 4.1.0
+
+				try {
+
+					// Fault injection for testing server character set indices
+		            
+		            if (props != null && props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex") != null) {
+		            	this.io.serverCharsetIndex = Integer.parseInt(
+		            			props.getProperty(
+		            					"com.mysql.jdbc.faultInjection.serverCharsetIndex"));	
+		            }
+		            
+					String serverEncodingToSet = 
+						CharsetMapping.INDEX_TO_CHARSET[this.io.serverCharsetIndex];
+					
+					if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) {
+						if (realJavaEncoding != null) {
+							// user knows best, try it
+							setEncoding(realJavaEncoding);
+						} else {
+							throw SQLError.createSQLException(
+									"Unknown initial character set index '"
+											+ this.io.serverCharsetIndex
+											+ "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
+									SQLError.SQL_STATE_GENERAL_ERROR);
+						}
+					}
+					
+					// "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1
+					if (versionMeetsMinimum(4, 1, 0) && 
+							"ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) {
+						serverEncodingToSet = "Cp1252";
+					}
+					
+					setEncoding(serverEncodingToSet);
+				
+				} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
+					if (realJavaEncoding != null) {
+						// user knows best, try it
+						setEncoding(realJavaEncoding);
+					} else {
+						throw SQLError.createSQLException(
+								"Unknown initial character set index '"
+										+ this.io.serverCharsetIndex
+										+ "' received from server. Initial client character set can be forced via the 'characterEncoding' property.",
+								SQLError.SQL_STATE_GENERAL_ERROR);
+					}
+				}
+
+				if (getEncoding() == null) {
+					// punt?
+					setEncoding("ISO8859_1");
+				}
+
+				//
+				// Has the user has 'forced' the character encoding via
+				// driver properties?
+				//
+				if (getUseUnicode()) {
+					if (realJavaEncoding != null) {
+
+						//
+						// Now, inform the server what character set we
+						// will be using from now-on...
+						//
+						if (realJavaEncoding.equalsIgnoreCase("UTF-8")
+								|| realJavaEncoding.equalsIgnoreCase("UTF8")) {
+							// charset names are case-sensitive
+
+							if (!getUseOldUTF8Behavior()) {
+								if (dontCheckServerMatch || !characterSetNamesMatches("utf8")) {
+									execSQL(null, "SET NAMES utf8", -1, null,
+											java.sql.ResultSet.TYPE_FORWARD_ONLY,
+											java.sql.ResultSet.CONCUR_READ_ONLY,
+											false, this.database, null, false);
+								}
+							}
+
+							setEncoding(realJavaEncoding);
+						} /* not utf-8 */else {
+							String mysqlEncodingName = CharsetMapping
+									.getMysqlEncodingForJavaEncoding(
+											realJavaEncoding
+													.toUpperCase(Locale.ENGLISH),
+											this);
+
+							/*
+							 * if ("koi8_ru".equals(mysqlEncodingName)) { //
+							 * This has a _different_ name in 4.1...
+							 * mysqlEncodingName = "ko18r"; } else if
+							 * ("euc_kr".equals(mysqlEncodingName)) { //
+							 * Different name in 4.1 mysqlEncodingName =
+							 * "euckr"; }
+							 */
+
+							if (mysqlEncodingName != null) {
+								
+								if (dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName)) {
+									execSQL(null, "SET NAMES " + mysqlEncodingName,
+										-1, null,
+										java.sql.ResultSet.TYPE_FORWARD_ONLY,
+										java.sql.ResultSet.CONCUR_READ_ONLY,
+										false, this.database, null, false);
+								}
+							}
+
+							// Switch driver's encoding now, since the server
+							// knows what we're sending...
+							//
+							setEncoding(realJavaEncoding);
+						}
+					} else if (getEncoding() != null) {
+						// Tell the server we'll use the server default charset
+						// to send our
+						// queries from now on....
+						String mysqlEncodingName = CharsetMapping
+								.getMysqlEncodingForJavaEncoding(getEncoding()
+										.toUpperCase(Locale.ENGLISH), this);
+
+						if (dontCheckServerMatch || !characterSetNamesMatches(mysqlEncodingName)) {
+							execSQL(null, "SET NAMES " + mysqlEncodingName, -1,
+								null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
+								java.sql.ResultSet.CONCUR_READ_ONLY, false,
+								this.database, null, false);
+						}
+
+						realJavaEncoding = getEncoding();
+					}
+
+				}
+
+				//
+				// We know how to deal with any charset coming back from
+				// the database, so tell the server not to do conversion
+				// if the user hasn't 'forced' a result-set character set
+				//
+
+				String onServer = null;
+				boolean isNullOnServer = false;
+				
+				if (this.serverVariables != null) {
+					onServer = (String)this.serverVariables.get("character_set_results");
+					
+					isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0;
+				}
+				
+				if (getCharacterSetResults() == null) {
+					
+					//
+					// Only send if needed, if we're caching server variables
+					// we -have- to send, because we don't know what it was
+					// before we cached them.
+					//
+					if (!isNullOnServer) {
+						execSQL(null, "SET character_set_results = NULL", -1, null,
+								java.sql.ResultSet.TYPE_FORWARD_ONLY,
+								java.sql.ResultSet.CONCUR_READ_ONLY, false,
+								this.database, null, 
+								false);
+						if (!this.usingCachedConfig) {
+							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, null);
+						}
+					} else {
+						if (!this.usingCachedConfig) {
+							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
+						}
+					}
+				} else {
+					String charsetResults = getCharacterSetResults();
+					String mysqlEncodingName = null;
+
+					if ("UTF-8".equalsIgnoreCase(charsetResults)
+							|| "UTF8".equalsIgnoreCase(charsetResults)) {
+						mysqlEncodingName = "utf8";
+					} else {
+						mysqlEncodingName = CharsetMapping
+								.getMysqlEncodingForJavaEncoding(charsetResults
+										.toUpperCase(Locale.ENGLISH), this);
+					}
+
+					//
+					// Only change the value if needed
+					//
+					
+					if (!mysqlEncodingName.equalsIgnoreCase(
+							(String)this.serverVariables.get("character_set_results"))) {
+						StringBuffer setBuf = new StringBuffer(
+								"SET character_set_results = ".length()
+										+ mysqlEncodingName.length());
+						setBuf.append("SET character_set_results = ").append(
+								mysqlEncodingName);
+	
+						execSQL(null, setBuf.toString(), -1, null,
+								java.sql.ResultSet.TYPE_FORWARD_ONLY,
+								java.sql.ResultSet.CONCUR_READ_ONLY, false,
+								this.database, null, false);
+						
+						if (!this.usingCachedConfig) {
+							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, 
+								mysqlEncodingName);
+						}
+					} else {
+						if (!this.usingCachedConfig) {
+							this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer);
+						}
+					}
+				}
+
+				if (getConnectionCollation() != null) {
+					StringBuffer setBuf = new StringBuffer(
+							"SET collation_connection = ".length()
+									+ getConnectionCollation().length());
+					setBuf.append("SET collation_connection = ").append(
+							getConnectionCollation());
+
+					execSQL(null, setBuf.toString(), -1, null,
+							java.sql.ResultSet.TYPE_FORWARD_ONLY,
+							java.sql.ResultSet.CONCUR_READ_ONLY, false,
+							this.database, null, false);
+				}
+			} else {
+				// Use what the server has specified
+				realJavaEncoding = getEncoding(); // so we don't get
+				// swapped out in the finally
+				// block....
+			}
+		} finally {
+			// Failsafe, make sure that the driver's notion of character
+			// encoding matches what the user has specified.
+			//
+			setEncoding(realJavaEncoding);
+		}
+
+		return characterSetAlreadyConfigured;
+	}
+
+	/**
+	 * Configures the client's timezone if required.
+	 * 
+	 * @throws SQLException
+	 *             if the timezone the server is configured to use can't be
+	 *             mapped to a Java timezone.
+	 */
+	private void configureTimezone() throws SQLException {
+		String configuredTimeZoneOnServer = (String) this.serverVariables
+				.get("timezone");
+
+		if (configuredTimeZoneOnServer == null) {
+			configuredTimeZoneOnServer = (String) this.serverVariables
+					.get("time_zone");
+
+			if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
+				configuredTimeZoneOnServer = (String) this.serverVariables
+						.get("system_time_zone");
+			}
+		}
+
+		if (getUseTimezone() && configuredTimeZoneOnServer != null) {
+			// user can specify/override as property
+			String canoncicalTimezone = getServerTimezone();
+
+			if ((canoncicalTimezone == null)
+					|| (canoncicalTimezone.length() == 0)) {
+				String serverTimezoneStr = configuredTimeZoneOnServer;
+
+				try {
+					canoncicalTimezone = TimeUtil
+							.getCanoncialTimezone(serverTimezoneStr);
+
+					if (canoncicalTimezone == null) {
+						throw SQLError.createSQLException("Can't map timezone '"
+								+ serverTimezoneStr + "' to "
+								+ " canonical timezone.",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				} catch (IllegalArgumentException iae) {
+					throw SQLError.createSQLException(iae.getMessage(),
+							SQLError.SQL_STATE_GENERAL_ERROR);
+				}
+			}
+
+			this.serverTimezoneTZ = TimeZone.getTimeZone(canoncicalTimezone);
+
+			//
+			// The Calendar class has the behavior of mapping
+			// unknown timezones to 'GMT' instead of throwing an
+			// exception, so we must check for this...
+			//
+			if (!canoncicalTimezone.equalsIgnoreCase("GMT")
+					&& this.serverTimezoneTZ.getID().equals("GMT")) {
+				throw SQLError.createSQLException("No timezone mapping entry for '"
+						+ canoncicalTimezone + "'",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			if ("GMT".equalsIgnoreCase(this.serverTimezoneTZ.getID())) {
+				this.isServerTzUTC = true;
+			} else {
+				this.isServerTzUTC = false;
+			}
+		}
+	}
+
+	private void createInitialHistogram(long[] breakpoints,
+			long lowerBound, long upperBound) {
+
+		double bucketSize = (((double) upperBound - (double) lowerBound) / HISTOGRAM_BUCKETS) * 1.25;
+
+		if (bucketSize < 1) {
+			bucketSize = 1;
+		}
+
+		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
+			breakpoints[i] = lowerBound;
+			lowerBound += bucketSize;
+		}
+	}
+
+	/**
+	 * Creates an IO channel to the server
+	 * 
+	 * @param isForReconnect
+	 *            is this request for a re-connect
+	 * @return a new MysqlIO instance connected to a server
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @throws CommunicationsException
+	 *             DOCUMENT ME!
+	 */
+	protected void createNewIO(boolean isForReconnect)
+			throws SQLException {
+		Properties mergedProps  = exposeAsProperties(this.props);
+
+		long queriesIssuedFailedOverCopy = this.queriesIssuedFailedOver;
+		this.queriesIssuedFailedOver = 0;
+
+		try {
+			if (!getHighAvailability() && !this.failedOver) {
+				boolean connectionGood = false;
+				Exception connectionNotEstablishedBecause = null;
+				
+				int hostIndex = 0;
+
+				//
+				// TODO: Eventually, when there's enough metadata
+				// on the server to support it, we should come up
+				// with a smarter way to pick what server to connect
+				// to...perhaps even making it 'pluggable'
+				//
+				if (getRoundRobinLoadBalance()) {
+					hostIndex = getNextRoundRobinHostIndex(getURL(),
+							this.hostList);
+				}
+
+				for (; hostIndex < this.hostListSize; hostIndex++) {
+
+					if (hostIndex == 0) {
+						this.hasTriedMasterFlag = true;
+					}
+					
+					try {
+						String newHostPortPair = (String) this.hostList
+								.get(hostIndex);
+
+						int newPort = 3306;
+
+						String[] hostPortPair = NonRegisteringDriver
+								.parseHostPortPair(newHostPortPair);
+						String newHost = hostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];
+
+						if (newHost == null || StringUtils.isEmptyOrWhitespaceOnly(newHost)) {
+							newHost = "localhost";
+						}
+
+						if (hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
+							try {
+								newPort = Integer
+										.parseInt(hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
+							} catch (NumberFormatException nfe) {
+								throw SQLError.createSQLException(
+										"Illegal connection port value '"
+												+ hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]
+												+ "'",
+										SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+							}
+						}
+
+						this.io = new MysqlIO(newHost, newPort, mergedProps,
+								getSocketFactoryClassName(), this,
+								getSocketTimeout(), 
+								this.largeRowSizeThreshold.getValueAsInt());
+	
+						this.io.doHandshake(this.user, this.password,
+								this.database);
+						this.connectionId = this.io.getThreadId();
+						this.isClosed = false;
+
+						// save state from old connection
+						boolean oldAutoCommit = getAutoCommit();
+						int oldIsolationLevel = this.isolationLevel;
+						boolean oldReadOnly = isReadOnly();
+						String oldCatalog = getCatalog();
+
+						// Server properties might be different
+						// from previous connection, so initialize
+						// again...
+						initializePropsFromServer();
+
+						if (isForReconnect) {
+							// Restore state from old connection
+							setAutoCommit(oldAutoCommit);
+
+							if (this.hasIsolationLevels) {
+								setTransactionIsolation(oldIsolationLevel);
+							}
+
+							setCatalog(oldCatalog);
+						}
+
+						if (hostIndex != 0) {
+							setFailedOverState();
+							queriesIssuedFailedOverCopy = 0;
+						} else {
+							this.failedOver = false;
+							queriesIssuedFailedOverCopy = 0;
+
+							if (this.hostListSize > 1) {
+								setReadOnlyInternal(false);
+							} else {
+								setReadOnlyInternal(oldReadOnly);
+							}
+						}
+
+						connectionGood = true;
+						
+						break; // low-level connection succeeded
+					} catch (Exception EEE) {
+						if (this.io != null) {
+							this.io.forceClose();
+						}
+
+						connectionNotEstablishedBecause = EEE;
+						
+						connectionGood = false;
+						
+						if (EEE instanceof SQLException) {
+							SQLException sqlEx = (SQLException)EEE;
+						
+							String sqlState = sqlEx.getSQLState();
+	
+							// If this isn't a communications failure, it will probably never succeed, so
+							// give up right here and now ....
+							if ((sqlState == null)
+									|| !sqlState
+											.equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) {
+								throw sqlEx;
+							}
+						}
+
+						// Check next host, it might be up...
+						if (getRoundRobinLoadBalance()) {
+							hostIndex = getNextRoundRobinHostIndex(getURL(),
+									this.hostList) - 1 /* incremented by for loop next time around */;
+						} else if ((this.hostListSize - 1) == hostIndex) {
+							throw SQLError.createCommunicationsException(this,
+									(this.io != null) ? this.io
+											.getLastPacketSentTimeMs() : 0,
+											EEE);
+						}
+					}
+				}
+				
+				if (!connectionGood) {
+					// We've really failed!
+					throw SQLError.createSQLException(
+							"Could not create connection to database server due to underlying exception: '"
+									+ connectionNotEstablishedBecause
+									+ "'."
+									+ (getParanoid() ? ""
+											: Util
+													.stackTraceToString(connectionNotEstablishedBecause)),
+							SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
+				}
+			} else {
+				double timeout = getInitialTimeout();
+				boolean connectionGood = false;
+
+				Exception connectionException = null;
+
+				int hostIndex = 0;
+
+				if (getRoundRobinLoadBalance()) {
+					hostIndex = getNextRoundRobinHostIndex(getURL(),
+							this.hostList);
+				}
+
+				for (; (hostIndex < this.hostListSize) && !connectionGood; hostIndex++) {
+					if (hostIndex == 0) {
+						this.hasTriedMasterFlag = true;
+					}
+					
+					if (this.preferSlaveDuringFailover && hostIndex == 0) {
+						hostIndex++;
+					}
+
+					for (int attemptCount = 0; (attemptCount < getMaxReconnects())
+							&& !connectionGood; attemptCount++) {
+						try {
+							if (this.io != null) {
+								this.io.forceClose();
+							}
+
+							String newHostPortPair = (String) this.hostList
+									.get(hostIndex);
+
+							int newPort = 3306;
+
+							String[] hostPortPair = NonRegisteringDriver
+									.parseHostPortPair(newHostPortPair);
+							String newHost = hostPortPair[NonRegisteringDriver.HOST_NAME_INDEX];
+
+							if (newHost == null || StringUtils.isEmptyOrWhitespaceOnly(newHost)) {
+								newHost = "localhost";
+							}
+
+							if (hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX] != null) {
+								try {
+									newPort = Integer
+											.parseInt(hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]);
+								} catch (NumberFormatException nfe) {
+									throw SQLError.createSQLException(
+											"Illegal connection port value '"
+													+ hostPortPair[NonRegisteringDriver.PORT_NUMBER_INDEX]
+													+ "'",
+											SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+								}
+							}
+
+							this.io = new MysqlIO(newHost, newPort,
+									mergedProps, getSocketFactoryClassName(),
+									this, getSocketTimeout(),
+									this.largeRowSizeThreshold.getValueAsInt());
+							this.io.doHandshake(this.user, this.password,
+									this.database);
+							pingInternal(false);
+							this.connectionId = this.io.getThreadId();
+							this.isClosed = false;
+
+							// save state from old connection
+							boolean oldAutoCommit = getAutoCommit();
+							int oldIsolationLevel = this.isolationLevel;
+							boolean oldReadOnly = isReadOnly();
+							String oldCatalog = getCatalog();
+
+							// Server properties might be different
+							// from previous connection, so initialize
+							// again...
+							initializePropsFromServer();
+
+							if (isForReconnect) {
+								// Restore state from old connection
+								setAutoCommit(oldAutoCommit);
+
+								if (this.hasIsolationLevels) {
+									setTransactionIsolation(oldIsolationLevel);
+								}
+
+								setCatalog(oldCatalog);
+							}
+
+							connectionGood = true;
+
+							if (hostIndex != 0) {
+								setFailedOverState();
+								queriesIssuedFailedOverCopy = 0;
+							} else {
+								this.failedOver = false;
+								queriesIssuedFailedOverCopy = 0;
+
+								if (this.hostListSize > 1) {
+									setReadOnlyInternal(false);
+								} else {
+									setReadOnlyInternal(oldReadOnly);
+								}
+							}
+
+							break;
+						} catch (Exception EEE) {
+							connectionException = EEE;
+							connectionGood = false;
+							
+							// Check next host, it might be up...
+							if (getRoundRobinLoadBalance()) {
+								hostIndex = getNextRoundRobinHostIndex(getURL(),
+										this.hostList) - 1 /* incremented by for loop next time around */;
+							}
+						}
+
+						if (connectionGood) {
+							break;
+						}
+
+						if (attemptCount > 0) {
+							try {
+								Thread.sleep((long) timeout * 1000);
+							} catch (InterruptedException IE) {
+								// ignore
+							}
+						}
+					} // end attempts for a single host
+				} // end iterator for list of hosts
+
+				if (!connectionGood) {
+					// We've really failed!
+					throw SQLError.createSQLException(
+							"Server connection failure during transaction. Due to underlying exception: '"
+									+ connectionException
+									+ "'."
+									+ (getParanoid() ? ""
+											: Util
+													.stackTraceToString(connectionException))
+									+ "\nAttempted reconnect "
+									+ getMaxReconnects() + " times. Giving up.",
+							SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
+				}
+			}
+
+			if (getParanoid() && !getHighAvailability()
+					&& (this.hostListSize <= 1)) {
+				this.password = null;
+				this.user = null;
+			}
+
+			if (isForReconnect) {
+				//
+				// Retrieve any 'lost' prepared statements if re-connecting
+				//
+				Iterator statementIter = this.openStatements.values()
+						.iterator();
+
+				//
+				// We build a list of these outside the map of open statements,
+				// because
+				// in the process of re-preparing, we might end up having to
+				// close
+				// a prepared statement, thus removing it from the map, and
+				// generating
+				// a ConcurrentModificationException
+				//
+				Stack serverPreparedStatements = null;
+
+				while (statementIter.hasNext()) {
+					Object statementObj = statementIter.next();
+
+					if (statementObj instanceof ServerPreparedStatement) {
+						if (serverPreparedStatements == null) {
+							serverPreparedStatements = new Stack();
+						}
+
+						serverPreparedStatements.add(statementObj);
+					}
+				}
+
+				if (serverPreparedStatements != null) {
+					while (!serverPreparedStatements.isEmpty()) {
+						((ServerPreparedStatement) serverPreparedStatements
+								.pop()).rePrepare();
+					}
+				}
+			}
+		} finally {
+			this.queriesIssuedFailedOver = queriesIssuedFailedOverCopy;
+			
+			if (this.io != null && getStatementInterceptors() != null) {
+				this.io.initializeStatementInterceptors(
+						getStatementInterceptors(), mergedProps);
+			}
+		}
+	}
+
+	private void createPreparedStatementCaches() {
+		int cacheSize = getPreparedStatementCacheSize();
+		
+		this.cachedPreparedStatementParams = new HashMap(cacheSize);
+		
+		this.serverSideStatementCheckCache = new LRUCache(cacheSize);
+		
+		this.serverSideStatementCache = new LRUCache(cacheSize) {
+			protected boolean removeEldestEntry(java.util.Map.Entry eldest) {
+				if (this.maxElements <= 1) {
+					return false;
+				}
+				
+				boolean removeIt = super.removeEldestEntry(eldest);
+				
+				if (removeIt) {
+					ServerPreparedStatement ps = 
+						(ServerPreparedStatement)eldest.getValue();
+					ps.isCached = false;
+					ps.setClosed(false);
+					
+					try {
+						ps.close();
+					} catch (SQLException sqlEx) {
+						// punt
+					}
+				}
+				
+				return removeIt;
+			}
+		};
+	}
+
+	/**
+	 * SQL statements without parameters are normally executed using Statement
+	 * objects. If the same SQL statement is executed many times, it is more
+	 * efficient to use a PreparedStatement
+	 * 
+	 * @return a new Statement object
+	 * @throws SQLException
+	 *             passed through from the constructor
+	 */
+	public java.sql.Statement createStatement() throws SQLException {
+		return createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+				java.sql.ResultSet.CONCUR_READ_ONLY);
+	}
+
+	/**
+	 * JDBC 2.0 Same as createStatement() above, but allows the default result
+	 * set type and result set concurrency type to be overridden.
+	 * 
+	 * @param resultSetType
+	 *            a result set type, see ResultSet.TYPE_XXX
+	 * @param resultSetConcurrency
+	 *            a concurrency type, see ResultSet.CONCUR_XXX
+	 * @return a new Statement object
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Statement createStatement(int resultSetType,
+			int resultSetConcurrency) throws SQLException {
+		checkClosed();
+
+		StatementImpl stmt = new com.mysql.jdbc.StatementImpl(this, this.database);
+		stmt.setResultSetType(resultSetType);
+		stmt.setResultSetConcurrency(resultSetConcurrency);
+
+		return stmt;
+	}
+
+	/**
+	 * @see Connection#createStatement(int, int, int)
+	 */
+	public java.sql.Statement createStatement(int resultSetType,
+			int resultSetConcurrency, int resultSetHoldability)
+			throws SQLException {
+		if (getPedantic()) {
+			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
+				throw SQLError.createSQLException(
+						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		return createStatement(resultSetType, resultSetConcurrency);
+	}
+
+	protected void dumpTestcaseQuery(String query) {
+		System.err.println(query);
+	}
+
+	protected Connection duplicate() throws SQLException {
+		return new ConnectionImpl(	this.origHostToConnectTo, 
+				this.origPortToConnectTo,
+				this.props,
+				this.origDatabaseToConnectTo,
+				this.myURL);
+	}
+
+	/**
+	 * Send a query to the server. Returns one of the ResultSet objects. This is
+	 * synchronized, so Statement's queries will be serialized.
+	 * 
+	 * @param callingStatement
+	 *            DOCUMENT ME!
+	 * @param sql
+	 *            the SQL statement to be executed
+	 * @param maxRows
+	 *            DOCUMENT ME!
+	 * @param packet
+	 *            DOCUMENT ME!
+	 * @param resultSetType
+	 *            DOCUMENT ME!
+	 * @param resultSetConcurrency
+	 *            DOCUMENT ME!
+	 * @param streamResults
+	 *            DOCUMENT ME!
+	 * @param queryIsSelectOnly
+	 *            DOCUMENT ME!
+	 * @param catalog
+	 *            DOCUMENT ME!
+	 * @param unpackFields
+	 *            DOCUMENT ME!
+	 * @return a ResultSet holding the results
+	 * @exception SQLException
+	 *                if a database error occurs
+	 */
+
+	// ResultSet execSQL(Statement callingStatement, String sql,
+	// int maxRowsToRetreive, String catalog) throws SQLException {
+	// return execSQL(callingStatement, sql, maxRowsToRetreive, null,
+	// java.sql.ResultSet.TYPE_FORWARD_ONLY,
+	// java.sql.ResultSet.CONCUR_READ_ONLY, catalog);
+	// }
+	// ResultSet execSQL(Statement callingStatement, String sql, int maxRows,
+	// int resultSetType, int resultSetConcurrency, boolean streamResults,
+	// boolean queryIsSelectOnly, String catalog, boolean unpackFields) throws
+	// SQLException {
+	// return execSQL(callingStatement, sql, maxRows, null, resultSetType,
+	// resultSetConcurrency, streamResults, queryIsSelectOnly, catalog,
+	// unpackFields);
+	// }
+	ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows,
+			Buffer packet, int resultSetType, int resultSetConcurrency,
+			boolean streamResults, String catalog,
+			Field[] cachedMetadata) throws SQLException {
+		return execSQL(callingStatement, sql, maxRows, packet, resultSetType,
+				resultSetConcurrency, streamResults,
+				catalog, cachedMetadata, false);
+	}
+
+	ResultSetInternalMethods execSQL(StatementImpl callingStatement, String sql, int maxRows,
+			Buffer packet, int resultSetType, int resultSetConcurrency,
+			boolean streamResults, String catalog,
+			Field[] cachedMetadata,
+			boolean isBatch) throws SQLException {
+		//
+		// Fall-back if the master is back online if we've
+		// issued queriesBeforeRetryMaster queries since
+		// we failed over
+		//
+		synchronized (this.mutex) {
+			long queryStartTime = 0;
+
+			int endOfQueryPacketPosition = 0;
+
+			if (packet != null) {
+				endOfQueryPacketPosition = packet.getPosition();
+			}
+
+			if (getGatherPerformanceMetrics()) {
+				queryStartTime = System.currentTimeMillis();
+			}
+
+			this.lastQueryFinishedTime = 0; // we're busy!
+
+			if (this.failedOver && this.autoCommit && !isBatch) {
+				if (shouldFallBack() && !this.executingFailoverReconnect) {
+					try {
+						this.executingFailoverReconnect = true;
+
+						createNewIO(true);
+
+						String connectedHost = this.io.getHost();
+
+						if ((connectedHost != null)
+								&& this.hostList.get(0).equals(connectedHost)) {
+							this.failedOver = false;
+							this.queriesIssuedFailedOver = 0;
+							setReadOnlyInternal(false);
+						}
+					} finally {
+						this.executingFailoverReconnect = false;
+					}
+				}
+			}
+
+			if ((getHighAvailability() || this.failedOver)
+					&& (this.autoCommit || getAutoReconnectForPools())
+					&& this.needsPing && !isBatch) {
+				try {
+					pingInternal(false);
+
+					this.needsPing = false;
+				} catch (Exception Ex) {
+					createNewIO(true);
+				}
+			}
+
+			try {
+				if (packet == null) {
+					String encoding = null;
+
+					if (getUseUnicode()) {
+						encoding = getEncoding();
+					}
+
+					return this.io.sqlQueryDirect(callingStatement, sql,
+							encoding, null, maxRows, resultSetType,
+							resultSetConcurrency, streamResults, catalog,
+							cachedMetadata);
+				}
+
+				return this.io.sqlQueryDirect(callingStatement, null, null,
+						packet, maxRows, resultSetType,
+						resultSetConcurrency, streamResults, catalog,
+						cachedMetadata);
+			} catch (java.sql.SQLException sqlE) {
+				// don't clobber SQL exceptions
+
+				if (getDumpQueriesOnException()) {
+					String extractedSql = extractSqlFromPacket(sql, packet,
+							endOfQueryPacketPosition);
+					StringBuffer messageBuf = new StringBuffer(extractedSql
+							.length() + 32);
+					messageBuf
+							.append("\n\nQuery being executed when exception was thrown:\n\n");
+					messageBuf.append(extractedSql);
+
+					sqlE = appendMessageToException(sqlE, messageBuf.toString());
+				}
+
+				if ((getHighAvailability() || this.failedOver)) {
+					this.needsPing = true;
+				} else {
+					String sqlState = sqlE.getSQLState();
+
+					if ((sqlState != null)
+							&& sqlState
+									.equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) {
+						cleanup(sqlE);
+					}
+				}
+
+				throw sqlE;
+			} catch (Exception ex) {
+				if ((getHighAvailability() || this.failedOver)) {
+					this.needsPing = true;
+				} else if (ex instanceof IOException) {
+					cleanup(ex);
+				}
+
+				String exceptionType = ex.getClass().getName();
+				String exceptionMessage = ex.getMessage();
+
+				if (!getParanoid()) {
+					exceptionMessage += "\n\nNested Stack Trace:\n";
+					exceptionMessage += Util.stackTraceToString(ex);
+				}
+
+				throw new java.sql.SQLException(
+						"Error during query: Unexpected Exception: "
+								+ exceptionType + " message given: "
+								+ exceptionMessage,
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			} finally {
+				if (getMaintainTimeStats()) {
+					this.lastQueryFinishedTime = System.currentTimeMillis();
+				}
+
+				if (this.failedOver) {
+					this.queriesIssuedFailedOver++;
+				}
+
+				if (getGatherPerformanceMetrics()) {
+					long queryTime = System.currentTimeMillis()
+							- queryStartTime;
+
+					registerQueryExecutionTime(queryTime);
+				}
+			}
+		}
+	}
+
+	protected String extractSqlFromPacket(String possibleSqlQuery,
+			Buffer queryPacket, int endOfQueryPacketPosition)
+			throws SQLException {
+
+		String extractedSql = null;
+
+		if (possibleSqlQuery != null) {
+			if (possibleSqlQuery.length() > getMaxQuerySizeToLog()) {
+				StringBuffer truncatedQueryBuf = new StringBuffer(
+						possibleSqlQuery.substring(0, getMaxQuerySizeToLog()));
+				truncatedQueryBuf.append(Messages.getString("MysqlIO.25"));
+				extractedSql = truncatedQueryBuf.toString();
+			} else {
+				extractedSql = possibleSqlQuery;
+			}
+		}
+
+		if (extractedSql == null) {
+			// This is probably from a client-side prepared
+			// statement
+
+			int extractPosition = endOfQueryPacketPosition;
+
+			boolean truncated = false;
+
+			if (endOfQueryPacketPosition > getMaxQuerySizeToLog()) {
+				extractPosition = getMaxQuerySizeToLog();
+				truncated = true;
+			}
+
+			extractedSql = new String(queryPacket.getByteBuffer(), 5,
+					(extractPosition - 5));
+
+			if (truncated) {
+				extractedSql += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
+			}
+		}
+
+		return extractedSql;
+
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Throwable
+	 *             DOCUMENT ME!
+	 */
+	protected void finalize() throws Throwable {
+		cleanup(null);
+		
+		super.finalize();
+	}
+
+	protected StringBuffer generateConnectionCommentBlock(StringBuffer buf) {
+		buf.append("/* conn id ");
+		buf.append(getId());
+		buf.append(" */ ");
+
+		return buf;
+	}
+
+	public int getActiveStatementCount() {
+		// Might not have one of these if
+		// not tracking open resources
+		if (this.openStatements != null) {
+			synchronized (this.openStatements) {
+				return this.openStatements.size();
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Gets the current auto-commit state
+	 * 
+	 * @return Current state of auto-commit
+	 * @exception SQLException
+	 *                if an error occurs
+	 * @see setAutoCommit
+	 */
+	public boolean getAutoCommit() throws SQLException {
+		return this.autoCommit;
+	}
+
+	/**
+	 * Optimization to only use one calendar per-session, or calculate it for
+	 * each call, depending on user configuration
+	 */
+	protected Calendar getCalendarInstanceForSessionOrNew() {
+		if (getDynamicCalendars()) {
+			return Calendar.getInstance();
+		}
+
+		return getSessionLockedCalendar();
+	}
+
+	/**
+	 * Return the connections current catalog name, or null if no catalog name
+	 * is set, or we dont support catalogs.
+	 * <p>
+	 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
+	 * </p>
+	 * 
+	 * @return the current catalog name or null
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getCatalog() throws SQLException {
+		return this.database;
+	}
+
+	/**
+	 * @return Returns the characterSetMetadata.
+	 */
+	protected String getCharacterSetMetadata() {
+		return characterSetMetadata;
+	}
+
+	/**
+	 * Returns the locally mapped instance of a charset converter (to avoid
+	 * overhead of static synchronization).
+	 * 
+	 * @param javaEncodingName
+	 *            the encoding name to retrieve
+	 * @return a character converter, or null if one couldn't be mapped.
+	 */
+	SingleByteCharsetConverter getCharsetConverter(
+			String javaEncodingName) throws SQLException {
+		if (javaEncodingName == null) {
+			return null;
+		}
+
+		if (this.usePlatformCharsetConverters) {
+			return null; // we'll use Java's built-in routines for this
+			             // they're finally fast enough
+		}
+		
+		SingleByteCharsetConverter converter = null;
+		
+		synchronized (this.charsetConverterMap) {
+			Object asObject = this.charsetConverterMap
+			.get(javaEncodingName);
+
+			if (asObject == CHARSET_CONVERTER_NOT_AVAILABLE_MARKER) {
+				return null;
+			}
+			
+			converter = (SingleByteCharsetConverter)asObject;
+			
+			if (converter == null) {
+				try {
+					converter = SingleByteCharsetConverter.getInstance(
+							javaEncodingName, this);
+
+					if (converter == null) {
+						this.charsetConverterMap.put(javaEncodingName,
+								CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
+					} else {
+						this.charsetConverterMap.put(javaEncodingName, converter);
+					}
+				} catch (UnsupportedEncodingException unsupEncEx) {
+					this.charsetConverterMap.put(javaEncodingName,
+							CHARSET_CONVERTER_NOT_AVAILABLE_MARKER);
+
+					converter = null;
+				}
+			}
+		}
+
+		return converter;
+	}
+
+	/**
+	 * Returns the Java character encoding name for the given MySQL server
+	 * charset index
+	 * 
+	 * @param charsetIndex
+	 * @return the Java character encoding name for the given MySQL server
+	 *         charset index
+	 * @throws SQLException
+	 *             if the character set index isn't known by the driver
+	 */
+	protected String getCharsetNameForIndex(int charsetIndex)
+			throws SQLException {
+		String charsetName = null;
+
+		if (getUseOldUTF8Behavior()) {
+			return getEncoding();
+		}
+
+		if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) {
+			try {
+				charsetName = this.indexToCharsetMapping[charsetIndex];
+
+				if ("sjis".equalsIgnoreCase(charsetName) || 
+						"MS932".equalsIgnoreCase(charsetName) /* for JDK6 */) {
+					// Use our encoding so that code pages like Cp932 work
+					if (CharsetMapping.isAliasForSjis(getEncoding())) {
+						charsetName = getEncoding();
+					}
+				}
+			} catch (ArrayIndexOutOfBoundsException outOfBoundsEx) {
+				throw SQLError.createSQLException(
+						"Unknown character set index for field '"
+								+ charsetIndex + "' received from server.",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+
+			// Punt
+			if (charsetName == null) {
+				charsetName = getEncoding();
+			}
+		} else {
+			charsetName = getEncoding();
+		}
+
+		return charsetName;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the defaultTimeZone.
+	 */
+	protected TimeZone getDefaultTimeZone() {
+		return this.defaultTimeZone;
+	}
+
+	protected String getErrorMessageEncoding() {
+		return errorMessageEncoding;
+	}
+
+	/**
+	 * @see Connection#getHoldability()
+	 */
+	public int getHoldability() throws SQLException {
+		return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT;
+	}
+
+	long getId() {
+		return this.connectionId;
+	}
+
+	/**
+	 * NOT JDBC-Compliant, but clients can use this method to determine how long
+	 * this connection has been idle. This time (reported in milliseconds) is
+	 * updated once a query has completed.
+	 * 
+	 * @return number of ms that this connection has been idle, 0 if the driver
+	 *         is busy retrieving results.
+	 */
+	public long getIdleFor() {
+		if (this.lastQueryFinishedTime == 0) {
+			return 0;
+		}
+
+		long now = System.currentTimeMillis();
+		long idleTime = now - this.lastQueryFinishedTime;
+
+		return idleTime;
+	}
+
+	/**
+	 * Returns the IO channel to the server
+	 * 
+	 * @return the IO channel to the server
+	 * @throws SQLException
+	 *             if the connection is closed.
+	 */
+	protected MysqlIO getIO() throws SQLException {
+		if ((this.io == null) || this.isClosed) {
+			throw SQLError.createSQLException(
+					"Operation not allowed on closed connection",
+					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
+		}
+
+		return this.io;
+	}
+
+	/**
+	 * Returns the log mechanism that should be used to log information from/for
+	 * this Connection.
+	 * 
+	 * @return the Log instance to use for logging messages.
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public Log getLog() throws SQLException {
+		return this.log;
+	}
+
+	/**
+	 * Returns the maximum packet size the MySQL server will accept
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	int getMaxAllowedPacket() {
+		return this.maxAllowedPacket;
+	}
+
+	protected int getMaxBytesPerChar(String javaCharsetName)
+	throws SQLException {
+		// TODO: Check if we can actually run this query at this point in time
+		String charset = CharsetMapping.getMysqlEncodingForJavaEncoding(
+				javaCharsetName, this);
+		
+		if (versionMeetsMinimum(4, 1, 0)) {
+			Map mapToCheck = null;
+			
+			if (!getUseDynamicCharsetInfo()) {
+				mapToCheck = CharsetMapping.STATIC_CHARSET_TO_NUM_BYTES_MAP;
+			} else {
+				mapToCheck = this.charsetToNumBytesMap;
+			
+				synchronized (this.charsetToNumBytesMap) {
+					if (this.charsetToNumBytesMap.isEmpty()) {
+						
+						java.sql.Statement stmt = null;
+						java.sql.ResultSet rs = null;
+		
+						try {
+							stmt = getMetadataSafeStatement();
+		
+							rs = stmt.executeQuery("SHOW CHARACTER SET");
+		
+							while (rs.next()) {
+								this.charsetToNumBytesMap.put(rs.getString("Charset"),
+										Constants.integerValueOf(rs.getInt("Maxlen")));
+							}
+		
+							rs.close();
+							rs = null;
+		
+							stmt.close();
+		
+							stmt = null;
+						} finally {
+							if (rs != null) {
+								rs.close();
+								rs = null;
+							}
+		
+							if (stmt != null) {
+								stmt.close();
+								stmt = null;
+							}
+						}
+					}
+				}
+			}
+		
+			Integer mbPerChar = (Integer) mapToCheck.get(charset);
+		
+			if (mbPerChar != null) {
+				return mbPerChar.intValue();
+			}
+		
+			return 1; // we don't know
+		}
+		
+		return 1; // we don't know
+	}
+
+	/**
+	 * A connection's database is able to provide information describing its
+	 * tables, its supported SQL grammar, its stored procedures, the
+	 * capabilities of this connection, etc. This information is made available
+	 * through a DatabaseMetaData object.
+	 * 
+	 * @return a DatabaseMetaData object for this connection
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public java.sql.DatabaseMetaData getMetaData() throws SQLException {
+		checkClosed();	
+		return com.mysql.jdbc.DatabaseMetaData.getInstance(this, this.database);
+	}
+
+	protected java.sql.Statement getMetadataSafeStatement() throws SQLException {
+		java.sql.Statement stmt = createStatement();
+
+		if (stmt.getMaxRows() != 0) {
+			stmt.setMaxRows(0);
+		}
+
+		stmt.setEscapeProcessing(false);
+
+		return stmt;
+	}
+
+	/**
+	 * Returns the Mutex all queries are locked against
+	 * 
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	Object getMutex() throws SQLException {
+		if (this.io == null) {
+			throw SQLError.createSQLException(
+					"Connection.close() has already been called. Invalid operation in this state.",
+					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
+		}
+
+		reportMetricsIfNeeded();
+
+		return this.mutex;
+	}
+
+	/**
+	 * Returns the packet buffer size the MySQL server reported upon connection
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	int getNetBufferLength() {
+		return this.netBufferLength;
+	}
+
+	/**
+	 * Returns the server's character set
+	 * 
+	 * @return the server's character set.
+	 */
+	public String getServerCharacterEncoding() {
+		if (this.io.versionMeetsMinimum(4, 1, 0)) {
+			return (String) this.serverVariables.get("character_set_server");
+		} else {
+			return (String) this.serverVariables.get("character_set");
+		}
+	}
+
+	int getServerMajorVersion() {
+		return this.io.getServerMajorVersion();
+	}
+
+	int getServerMinorVersion() {
+		return this.io.getServerMinorVersion();
+	}
+
+	int getServerSubMinorVersion() {
+		return this.io.getServerSubMinorVersion();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public TimeZone getServerTimezoneTZ() {
+		return this.serverTimezoneTZ;
+	}
+	
+	
+	String getServerVariable(String variableName) {
+		if (this.serverVariables != null) {
+			return (String) this.serverVariables.get(variableName);
+		}
+
+		return null;
+	}
+
+	String getServerVersion() {
+		return this.io.getServerVersion();
+	}
+
+	protected Calendar getSessionLockedCalendar() {
+	
+		return this.sessionCalendar;
+	}
+
+	/**
+	 * Get this Connection's current transaction isolation mode.
+	 * 
+	 * @return the current TRANSACTION_ mode value
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int getTransactionIsolation() throws SQLException {
+
+		if (this.hasIsolationLevels && !getUseLocalSessionState()) {
+			java.sql.Statement stmt = null;
+			java.sql.ResultSet rs = null;
+
+			try {
+				stmt = getMetadataSafeStatement();
+
+				String query = null;
+
+				int offset = 0;
+				
+				if (versionMeetsMinimum(4, 0, 3)) {
+					query = "SELECT @@session.tx_isolation";
+					offset = 1;
+				} else {
+					query = "SHOW VARIABLES LIKE 'transaction_isolation'";
+					offset = 2;
+				}
+
+				rs = stmt.executeQuery(query);
+
+				if (rs.next()) {
+					String s = rs.getString(offset);
+
+					if (s != null) {
+						Integer intTI = (Integer) mapTransIsolationNameToValue
+								.get(s);
+
+						if (intTI != null) {
+							return intTI.intValue();
+						}
+					}
+
+					throw SQLError.createSQLException(
+							"Could not map transaction isolation '" + s
+									+ " to a valid JDBC level.",
+							SQLError.SQL_STATE_GENERAL_ERROR);
+				}
+
+				throw SQLError.createSQLException(
+						"Could not retrieve transaction isolation level from server",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+
+			} finally {
+				if (rs != null) {
+					try {
+						rs.close();
+					} catch (Exception ex) {
+						// ignore
+						;
+					}
+
+					rs = null;
+				}
+
+				if (stmt != null) {
+					try {
+						stmt.close();
+					} catch (Exception ex) {
+						// ignore
+						;
+					}
+
+					stmt = null;
+				}
+			}
+		}
+
+		return this.isolationLevel;
+	}
+
+	/**
+	 * JDBC 2.0 Get the type-map object associated with this connection. By
+	 * default, the map returned is empty.
+	 * 
+	 * @return the type map
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public synchronized java.util.Map getTypeMap() throws SQLException {
+		if (this.typeMap == null) {
+			this.typeMap = new HashMap();
+		}
+
+		return this.typeMap;
+	}
+
+	String getURL() {
+		return this.myURL;
+	}
+
+	String getUser() {
+		return this.user;
+	}
+
+	protected Calendar getUtcCalendar() {
+		return this.utcCalendar;
+	}
+	
+	/**
+	 * The first warning reported by calls on this Connection is returned.
+	 * <B>Note:</B> Sebsequent warnings will be changed to this
+	 * java.sql.SQLWarning
+	 * 
+	 * @return the first java.sql.SQLWarning or null
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public SQLWarning getWarnings() throws SQLException {
+		return null;
+	}
+
+	public boolean hasSameProperties(Connection c) {
+		return this.props.equals(((ConnectionImpl)c).props);
+	}
+
+	public boolean hasTriedMaster() {
+		return this.hasTriedMasterFlag;
+	}
+
+	protected void incrementNumberOfPreparedExecutes() {
+		if (getGatherPerformanceMetrics()) {
+			this.numberOfPreparedExecutes++;
+
+			// We need to increment this, because
+			// server-side prepared statements bypass
+			// any execution by the connection itself...
+			this.numberOfQueriesIssued++;
+		}
+	}
+
+	protected void incrementNumberOfPrepares() {
+		if (getGatherPerformanceMetrics()) {
+			this.numberOfPrepares++;
+		}
+	}
+
+	protected void incrementNumberOfResultSetsCreated() {
+		if (getGatherPerformanceMetrics()) {
+			this.numberOfResultSetsCreated++;
+		}
+	}
+
+	/**
+	 * Initializes driver properties that come from URL or properties passed to
+	 * the driver manager.
+	 * 
+	 * @param info
+	 *            DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void initializeDriverProperties(Properties info)
+			throws SQLException {
+		initializeProperties(info);
+		
+		this.usePlatformCharsetConverters = getUseJvmCharsetConverters();
+
+		this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME);
+
+		if (getProfileSql() || getUseUsageAdvisor()) {
+			this.eventSink = ProfileEventSink.getInstance(this);
+		}
+
+		if (getCachePreparedStatements()) {
+			createPreparedStatementCaches();		
+		}
+
+		if (getNoDatetimeStringSync() && getUseTimezone()) {
+			throw SQLError.createSQLException(
+					"Can't enable noDatetimeSync and useTimezone configuration "
+							+ "properties at the same time",
+					SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+		}
+		
+		if (getCacheCallableStatements()) {
+			this.parsedCallableStatementCache = new LRUCache(
+					getCallableStatementCacheSize());
+		}
+		
+		if (getAllowMultiQueries()) {
+			setCacheResultSetMetadata(false); // we don't handle this yet
+		}
+		
+		if (getCacheResultSetMetadata()) {
+			this.resultSetMetadataCache = new LRUCache(
+					getMetadataCacheSize());
+		}
+	}
+
+	/**
+	 * Sets varying properties that depend on server information. Called once we
+	 * have connected to the server.
+	 * 
+	 * @param info
+	 *            DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void initializePropsFromServer() throws SQLException {
+		String connectionInterceptorClasses = getConnectionLifecycleInterceptors();
+		
+		this.connectionLifecycleInterceptors = null;
+		
+		if (connectionInterceptorClasses != null) {
+			this.connectionLifecycleInterceptors = Util.loadExtensions(this, this.props, 
+					connectionInterceptorClasses, 
+					"Connection.badLifecycleInterceptor");
+			
+			Iterator iter = this.connectionLifecycleInterceptors.iterator();
+			
+			new IterateBlock(iter) {
+				void forEach(Object each) throws SQLException {
+					// TODO: Fully initialize, or continue on error?
+					((ConnectionLifecycleInterceptor)each).init(ConnectionImpl.this, props);
+				}
+			}.doForAll();
+		}
+		
+		setSessionVariables();
+
+		//
+		// the "boolean" type didn't come along until MySQL-4.1
+		//
+
+		if (!versionMeetsMinimum(4, 1, 0)) {
+			setTransformedBitIsBoolean(false);
+		}
+
+		this.parserKnowsUnicode = versionMeetsMinimum(4, 1, 0);
+
+		//
+		// Users can turn off detection of server-side prepared statements
+		//
+		if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) {
+			this.useServerPreparedStmts = true;
+
+			if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) {
+				this.useServerPreparedStmts = false; // 4.1.2+ style prepared
+				// statements
+				// don't work on these versions
+			}
+		}
+
+		this.serverVariables.clear();
+
+		//
+		// If version is greater than 3.21.22 get the server
+		// variables.
+		if (versionMeetsMinimum(3, 21, 22)) {
+			loadServerVariables();
+
+			buildCollationMapping();
+
+			LicenseConfiguration.checkLicenseType(this.serverVariables);
+
+			String lowerCaseTables = (String) this.serverVariables
+					.get("lower_case_table_names");
+
+			this.lowerCaseTableNames = "on".equalsIgnoreCase(lowerCaseTables)
+					|| "1".equalsIgnoreCase(lowerCaseTables)
+					|| "2".equalsIgnoreCase(lowerCaseTables);
+
+			configureTimezone();
+
+			if (this.serverVariables.containsKey("max_allowed_packet")) {
+				this.maxAllowedPacket = getServerVariableAsInt("max_allowed_packet", 1024 * 1024);
+				
+				int preferredBlobSendChunkSize = getBlobSendChunkSize();
+				
+				int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, 
+						this.maxAllowedPacket) - 
+						ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE 
+						- 11 /* LONG_DATA and MySQLIO packet header size */;
+				
+				setBlobSendChunkSize(String.valueOf(allowedBlobSendChunkSize));
+			}
+
+			if (this.serverVariables.containsKey("net_buffer_length")) {
+				this.netBufferLength = getServerVariableAsInt("net_buffer_length", 16 * 1024);
+			}
+
+			checkTransactionIsolationLevel();
+			
+			if (!versionMeetsMinimum(4, 1, 0)) {
+				checkServerEncoding();
+			}
+
+			this.io.checkForCharsetMismatch();
+
+			if (this.serverVariables.containsKey("sql_mode")) {
+				int sqlMode = 0;
+
+				String sqlModeAsString = (String) this.serverVariables
+						.get("sql_mode");
+				try {
+					sqlMode = Integer.parseInt(sqlModeAsString);
+				} catch (NumberFormatException nfe) {
+					// newer versions of the server has this as a string-y
+					// list...
+					sqlMode = 0;
+
+					if (sqlModeAsString != null) {
+						if (sqlModeAsString.indexOf("ANSI_QUOTES") != -1) {
+							sqlMode |= 4;
+						}
+
+						if (sqlModeAsString.indexOf("NO_BACKSLASH_ESCAPES") != -1) {
+							this.noBackslashEscapes = true;
+						}
+					}
+				}
+
+				if ((sqlMode & 4) > 0) {
+					this.useAnsiQuotes = true;
+				} else {
+					this.useAnsiQuotes = false;
+				}
+			}
+		}
+		
+		this.errorMessageEncoding = 
+			CharsetMapping.getCharacterEncodingForErrorMessages(this);
+		
+		
+		boolean overrideDefaultAutocommit = isAutoCommitNonDefaultOnServer();
+	
+		configureClientCharacterSet(false);
+
+		if (versionMeetsMinimum(3, 23, 15)) {
+			this.transactionsSupported = true;
+			
+			if (!overrideDefaultAutocommit) {
+				setAutoCommit(true); // to override anything
+				// the server is set to...reqd
+				// by JDBC spec.
+			}
+		} else {
+			this.transactionsSupported = false;
+		}
+		
+
+		if (versionMeetsMinimum(3, 23, 36)) {
+			this.hasIsolationLevels = true;
+		} else {
+			this.hasIsolationLevels = false;
+		}
+
+		this.hasQuotedIdentifiers = versionMeetsMinimum(3, 23, 6);
+
+		this.io.resetMaxBuf();
+
+		//
+		// If we're using MySQL 4.1.0 or newer, we need to figure
+		// out what character set metadata will be returned in,
+		// and then map that to a Java encoding name.
+		//
+		// We've already set it, and it might be different than what
+		// was originally on the server, which is why we use the
+		// "special" key to retrieve it
+		if (this.io.versionMeetsMinimum(4, 1, 0)) {
+			String characterSetResultsOnServerMysql = (String) this.serverVariables
+					.get(JDBC_LOCAL_CHARACTER_SET_RESULTS);
+
+			if (characterSetResultsOnServerMysql == null
+					|| StringUtils.startsWithIgnoreCaseAndWs(
+							characterSetResultsOnServerMysql, "NULL")
+					|| characterSetResultsOnServerMysql.length() == 0) {
+				String defaultMetadataCharsetMysql = (String) this.serverVariables
+						.get("character_set_system");
+				String defaultMetadataCharset = null;
+
+				if (defaultMetadataCharsetMysql != null) {
+					defaultMetadataCharset = CharsetMapping
+							.getJavaEncodingForMysqlEncoding(
+									defaultMetadataCharsetMysql, this);
+				} else {
+					defaultMetadataCharset = "UTF-8";
+				}
+
+				this.characterSetMetadata = defaultMetadataCharset;
+			} else {
+				this.characterSetResultsOnServer = CharsetMapping
+						.getJavaEncodingForMysqlEncoding(
+								characterSetResultsOnServerMysql, this);
+				this.characterSetMetadata = this.characterSetResultsOnServer;
+			}
+		}
+
+		//
+		// Query cache is broken wrt. multi-statements before MySQL-4.1.10
+		//
+
+		if (this.versionMeetsMinimum(4, 1, 0)
+				&& !this.versionMeetsMinimum(4, 1, 10)
+				&& getAllowMultiQueries()) {
+			if ("ON".equalsIgnoreCase((String) this.serverVariables
+					.get("query_cache_type"))
+					&& !"0".equalsIgnoreCase((String) this.serverVariables
+							.get("query_cache_size"))) {
+				setAllowMultiQueries(false);
+			}
+		}
+		
+		//
+		// Server can do this more efficiently for us
+		//
+		
+		setupServerForTruncationChecks();
+	}
+
+	private int getServerVariableAsInt(String variableName, int fallbackValue)
+			throws SQLException {
+		try {
+			return Integer.parseInt((String) this.serverVariables
+					.get(variableName));
+		} catch (NumberFormatException nfe) {
+			getLog().logWarn(Messages.getString("Connection.BadValueInServerVariables", new Object[] {variableName, 
+					this.serverVariables.get(variableName), new Integer(fallbackValue)}));
+			
+			return fallbackValue;
+		}
+	}
+
+	/**
+	 * Has the default autocommit value of 0 been changed on the server
+	 * via init_connect?
+	 * 
+	 * @return true if autocommit is not the default of '0' on the server.
+	 * 
+	 * @throws SQLException
+	 */
+	private boolean isAutoCommitNonDefaultOnServer() throws SQLException {
+		boolean overrideDefaultAutocommit = false;
+		
+		String initConnectValue = (String) this.serverVariables
+		.get("init_connect");
+
+		if (versionMeetsMinimum(4, 1, 2) && initConnectValue != null
+				&& initConnectValue.length() > 0) {
+			if (!getElideSetAutoCommits()) {
+				// auto-commit might have changed
+				java.sql.ResultSet rs = null;
+				java.sql.Statement stmt = null;
+				
+				try {
+					stmt = getMetadataSafeStatement();
+					
+					rs = stmt.executeQuery("SELECT @@session.autocommit");
+					
+					if (rs.next()) {
+						this.autoCommit = rs.getBoolean(1);
+						if (this.autoCommit != true) {
+							overrideDefaultAutocommit = true;
+						}
+					}
+					
+				} finally {
+					if (rs != null) {
+						try {
+							rs.close();
+						} catch (SQLException sqlEx) {
+							// do nothing
+						}
+					}
+					
+					if (stmt != null) {
+						try {
+							stmt.close();
+						} catch (SQLException sqlEx) {
+							// do nothing
+						}
+					}
+				}
+			} else {
+				if (this.getIO().isSetNeededForAutoCommitMode(true)) {
+					// we're not in standard autocommit=true mode
+					this.autoCommit = false;
+					overrideDefaultAutocommit = true;
+				}
+			}
+		}
+		
+		return overrideDefaultAutocommit;
+	}
+
+	protected boolean isClientTzUTC() {
+		return this.isClientTzUTC;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isClosed() {
+		return this.isClosed;
+	}
+
+	protected boolean isCursorFetchEnabled() throws SQLException {
+		return (versionMeetsMinimum(5, 0, 2) && getUseCursorFetch());
+	}
+
+	public boolean isInGlobalTx() {
+		return this.isInGlobalTx;
+	}
+
+	/**
+	 * Is this connection connected to the first host in the list if
+	 * there is a list of servers in the URL?
+	 * 
+	 * @return true if this connection is connected to the first in 
+	 * the list.
+	 */
+	public synchronized boolean isMasterConnection() {
+		return !this.failedOver;
+	}
+
+	/**
+	 * Is the server in a sql_mode that doesn't allow us to use \\ to escape
+	 * things?
+	 * 
+	 * @return Returns the noBackslashEscapes.
+	 */
+	public boolean isNoBackslashEscapesSet() {
+		return this.noBackslashEscapes;
+	}
+
+	boolean isReadInfoMsgEnabled() {
+		return this.readInfoMsg;
+	}
+
+	/**
+	 * Tests to see if the connection is in Read Only Mode. Note that we cannot
+	 * really put the database in read only mode, but we pretend we can by
+	 * returning the value of the readOnly flag
+	 * 
+	 * @return true if the connection is read only
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean isReadOnly() throws SQLException {
+		return this.readOnly;
+	}
+
+	protected boolean isRunningOnJDK13() {
+		return this.isRunningOnJDK13;
+	}
+
+	public synchronized boolean isSameResource(Connection otherConnection) {
+		if (otherConnection == null) {
+			return false;
+		}
+		
+		boolean directCompare = true;
+		
+		String otherHost = ((ConnectionImpl)otherConnection).origHostToConnectTo;
+		String otherOrigDatabase = ((ConnectionImpl)otherConnection).origDatabaseToConnectTo;
+		String otherCurrentCatalog = ((ConnectionImpl)otherConnection).database;
+		
+		if (!nullSafeCompare(otherHost, this.origHostToConnectTo)) {
+			directCompare = false;
+		} else if (otherHost != null && otherHost.indexOf(',') == -1 && 
+				otherHost.indexOf(':') == -1) {
+			// need to check port numbers
+			directCompare = (((ConnectionImpl)otherConnection).origPortToConnectTo == 
+				this.origPortToConnectTo);
+		}
+		
+		if (directCompare) {
+			if (!nullSafeCompare(otherOrigDatabase, this.origDatabaseToConnectTo)) {			directCompare = false;
+				directCompare = false;
+			} else if (!nullSafeCompare(otherCurrentCatalog, this.database)) {
+				directCompare = false;
+			}
+		}
+
+		if (directCompare) {
+			return true;
+		}
+		
+		// Has the user explicitly set a resourceId?
+		String otherResourceId = ((ConnectionImpl)otherConnection).getResourceId();
+		String myResourceId = getResourceId();
+		
+		if (otherResourceId != null || myResourceId != null) {
+			directCompare = nullSafeCompare(otherResourceId, myResourceId);
+			
+			if (directCompare) {
+				return true;
+			}
+		}
+		
+		return false;	
+	}
+
+	protected boolean isServerTzUTC() {
+		return this.isServerTzUTC;
+	}
+
+	private boolean usingCachedConfig = false;
+	
+	/**
+	 * Loads the result of 'SHOW VARIABLES' into the serverVariables field so
+	 * that the driver can configure itself.
+	 * 
+	 * @throws SQLException
+	 *             if the 'SHOW VARIABLES' query fails for any reason.
+	 */
+	private void loadServerVariables() throws SQLException {
+
+		if (getCacheServerConfiguration()) {
+			synchronized (serverConfigByUrl) {
+				Map cachedVariableMap = (Map) serverConfigByUrl.get(getURL());
+
+				if (cachedVariableMap != null) {
+					this.serverVariables = cachedVariableMap;
+					this.usingCachedConfig = true;
+
+					return;
+				}
+			}
+		}
+
+		java.sql.Statement stmt = null;
+		java.sql.ResultSet results = null;
+
+		try {
+			stmt = createStatement();
+			stmt.setEscapeProcessing(false);
+
+			String query = "SHOW VARIABLES";
+			
+			if (versionMeetsMinimum(5, 0, 3)) {
+				query = "SHOW VARIABLES WHERE Variable_name ='language'"
+					+ " OR Variable_name = 'net_write_timeout'"
+					+ " OR Variable_name = 'interactive_timeout'"
+					+ " OR Variable_name = 'wait_timeout'"
+					+ " OR Variable_name = 'character_set_client'"
+					+ " OR Variable_name = 'character_set_connection'"
+					+ " OR Variable_name = 'character_set'"
+					+ " OR Variable_name = 'character_set_server'"
+					+ " OR Variable_name = 'tx_isolation'"
+					+ " OR Variable_name = 'transaction_isolation'"
+					+ " OR Variable_name = 'character_set_results'"
+					+ " OR Variable_name = 'timezone'"
+					+ " OR Variable_name = 'time_zone'"
+					+ " OR Variable_name = 'system_time_zone'"
+					+ " OR Variable_name = 'lower_case_table_names'"
+					+ " OR Variable_name = 'max_allowed_packet'"
+					+ " OR Variable_name = 'net_buffer_length'"
+					+ " OR Variable_name = 'sql_mode'"
+					+ " OR Variable_name = 'query_cache_type'"
+					+ " OR Variable_name = 'query_cache_size'"
+					+ " OR Variable_name = 'init_connect'";
+			}
+			
+			results = stmt.executeQuery(query);
+
+			while (results.next()) {
+				this.serverVariables.put(results.getString(1), results
+						.getString(2));
+			}
+
+			if (getCacheServerConfiguration()) {
+				synchronized (serverConfigByUrl) {
+					serverConfigByUrl.put(getURL(), this.serverVariables);
+				}
+			}
+		} catch (SQLException e) {
+			throw e;
+		} finally {
+			if (results != null) {
+				try {
+					results.close();
+				} catch (SQLException sqlE) {
+					;
+				}
+			}
+
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (SQLException sqlE) {
+					;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Is the server configured to use lower-case table names only?
+	 * 
+	 * @return true if lower_case_table_names is 'on'
+	 */
+	public boolean lowerCaseTableNames() {
+		return this.lowerCaseTableNames;
+	}
+
+	/**
+	 * Has the maxRows value changed?
+	 * 
+	 * @param stmt
+	 *            DOCUMENT ME!
+	 */
+	void maxRowsChanged(StatementImpl stmt) {
+		synchronized (this.mutex) {
+			if (this.statementsUsingMaxRows == null) {
+				this.statementsUsingMaxRows = new HashMap();
+			}
+
+			this.statementsUsingMaxRows.put(stmt, stmt);
+
+			this.maxRowsChanged = true;
+		}
+	}
+
+	/**
+	 * A driver may convert the JDBC sql grammar into its system's native SQL
+	 * grammar prior to sending it; nativeSQL returns the native form of the
+	 * statement that the driver would have sent.
+	 * 
+	 * @param sql
+	 *            a SQL statement that may contain one or more '?' parameter
+	 *            placeholders
+	 * @return the native form of this statement
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String nativeSQL(String sql) throws SQLException {
+		if (sql == null) {
+			return null;
+		}
+
+		Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
+				serverSupportsConvertFn(),
+				this);
+
+		if (escapedSqlResult instanceof String) {
+			return (String) escapedSqlResult;
+		}
+
+		return ((EscapeProcessorResult) escapedSqlResult).escapedSql;
+	}
+
+	private CallableStatement parseCallableStatement(String sql)
+			throws SQLException {
+		Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
+				serverSupportsConvertFn(), this);
+
+		boolean isFunctionCall = false;
+		String parsedSql = null;
+
+		if (escapedSqlResult instanceof EscapeProcessorResult) {
+			parsedSql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
+			isFunctionCall = ((EscapeProcessorResult) escapedSqlResult).callingStoredFunction;
+		} else {
+			parsedSql = (String) escapedSqlResult;
+			isFunctionCall = false;
+		}
+
+		return CallableStatement.getInstance(this, parsedSql, this.database,
+				isFunctionCall);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean parserKnowsUnicode() {
+		return this.parserKnowsUnicode;
+	}
+
+	/**
+	 * Detect if the connection is still good
+	 * 
+	 * @throws SQLException
+	 *             if the ping fails
+	 */
+	public void ping() throws SQLException {
+		pingInternal(true);
+	}
+
+	protected void pingInternal(boolean checkForClosedConnection)
+			throws SQLException {
+		if (checkForClosedConnection) {
+			checkClosed();
+		}
+
+		// Need MySQL-3.22.1, but who uses anything older!?
+		this.io.sendCommand(MysqlDefs.PING, null, null, false, null);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param sql
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.CallableStatement prepareCall(String sql)
+			throws SQLException {
+
+		return prepareCall(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
+				java.sql.ResultSet.CONCUR_READ_ONLY);
+	}
+
+	/**
+	 * JDBC 2.0 Same as prepareCall() above, but allows the default result set
+	 * type and result set concurrency type to be overridden.
+	 * 
+	 * @param sql
+	 *            the SQL representing the callable statement
+	 * @param resultSetType
+	 *            a result set type, see ResultSet.TYPE_XXX
+	 * @param resultSetConcurrency
+	 *            a concurrency type, see ResultSet.CONCUR_XXX
+	 * @return a new CallableStatement object containing the pre-compiled SQL
+	 *         statement
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.CallableStatement prepareCall(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		if (versionMeetsMinimum(5, 0, 0)) {
+			CallableStatement cStmt = null;
+
+			if (!getCacheCallableStatements()) {
+
+				cStmt = parseCallableStatement(sql);
+			} else {
+				synchronized (this.parsedCallableStatementCache) {
+					CompoundCacheKey key = new CompoundCacheKey(getCatalog(), sql);
+	
+					CallableStatement.CallableStatementParamInfo cachedParamInfo = (CallableStatement.CallableStatementParamInfo) this.parsedCallableStatementCache
+							.get(key);
+	
+					if (cachedParamInfo != null) {
+						cStmt = CallableStatement.getInstance(this, cachedParamInfo);
+					} else {
+						cStmt = parseCallableStatement(sql);
+	
+						cachedParamInfo = cStmt.paramInfo;
+	
+						this.parsedCallableStatementCache.put(key, cachedParamInfo);
+					}
+				}
+			}
+
+			cStmt.setResultSetType(resultSetType);
+			cStmt.setResultSetConcurrency(resultSetConcurrency);
+
+			return cStmt;
+		}
+
+		throw SQLError.createSQLException("Callable statements not " + "supported.",
+				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+	}
+
+	/**
+	 * @see Connection#prepareCall(String, int, int, int)
+	 */
+	public java.sql.CallableStatement prepareCall(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		if (getPedantic()) {
+			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
+				throw SQLError.createSQLException(
+						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		CallableStatement cStmt = (com.mysql.jdbc.CallableStatement) prepareCall(
+				sql, resultSetType, resultSetConcurrency);
+
+		return cStmt;
+	}
+
+	/**
+	 * A SQL statement with or without IN parameters can be pre-compiled and
+	 * stored in a PreparedStatement object. This object can then be used to
+	 * efficiently execute this statement multiple times.
+	 * <p>
+	 * <B>Note:</B> This method is optimized for handling parametric SQL
+	 * statements that benefit from precompilation if the driver supports
+	 * precompilation. In this case, the statement is not sent to the database
+	 * until the PreparedStatement is executed. This has no direct effect on
+	 * users; however it does affect which method throws certain
+	 * java.sql.SQLExceptions
+	 * </p>
+	 * <p>
+	 * MySQL does not support precompilation of statements, so they are handled
+	 * by the driver.
+	 * </p>
+	 * 
+	 * @param sql
+	 *            a SQL statement that may contain one or more '?' IN parameter
+	 *            placeholders
+	 * @return a new PreparedStatement object containing the pre-compiled
+	 *         statement.
+	 * @exception SQLException
+	 *                if a database access error occurs.
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql)
+			throws SQLException {
+		return prepareStatement(sql, java.sql.ResultSet.TYPE_FORWARD_ONLY,
+				java.sql.ResultSet.CONCUR_READ_ONLY);
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int)
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException {
+		java.sql.PreparedStatement pStmt = prepareStatement(sql);
+
+		((com.mysql.jdbc.PreparedStatement) pStmt)
+				.setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
+
+		return pStmt;
+	}
+
+	/**
+	 * JDBC 2.0 Same as prepareStatement() above, but allows the default result
+	 * set type and result set concurrency type to be overridden.
+	 * 
+	 * @param sql
+	 *            the SQL query containing place holders
+	 * @param resultSetType
+	 *            a result set type, see ResultSet.TYPE_XXX
+	 * @param resultSetConcurrency
+	 *            a concurrency type, see ResultSet.CONCUR_XXX
+	 * @return a new PreparedStatement object containing the pre-compiled SQL
+	 *         statement
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		checkClosed();
+
+		//
+		// FIXME: Create warnings if can't create results of the given
+		// type or concurrency
+		//
+		PreparedStatement pStmt = null;
+		
+		boolean canServerPrepare = true;
+		
+		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
+		
+		if (getEmulateUnsupportedPstmts()) {
+			canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
+		}
+		
+		if (this.useServerPreparedStmts && canServerPrepare) {
+			if (this.getCachePreparedStatements()) {
+				synchronized (this.serverSideStatementCache) {
+					pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
+					
+					if (pStmt != null) {
+						((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
+						pStmt.clearParameters();
+					}
+
+					if (pStmt == null) {
+						try {
+							pStmt = ServerPreparedStatement.getInstance(this, nativeSql,
+									this.database, resultSetType, resultSetConcurrency);
+							if (sql.length() < getPreparedStatementCacheSqlLimit()) {
+								((com.mysql.jdbc.ServerPreparedStatement)pStmt).isCached = true;
+							}
+							
+							pStmt.setResultSetType(resultSetType);
+							pStmt.setResultSetConcurrency(resultSetConcurrency);
+						} catch (SQLException sqlEx) {
+							// Punt, if necessary
+							if (getEmulateUnsupportedPstmts()) {
+								pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
+								
+								if (sql.length() < getPreparedStatementCacheSqlLimit()) {
+									this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
+								}
+							} else {
+								throw sqlEx;
+							}
+						}
+					}
+				}
+			} else {
+				try {
+					pStmt = ServerPreparedStatement.getInstance(this, nativeSql,
+							this.database, resultSetType, resultSetConcurrency);
+					
+					pStmt.setResultSetType(resultSetType);
+					pStmt.setResultSetConcurrency(resultSetConcurrency);
+				} catch (SQLException sqlEx) {
+					// Punt, if necessary
+					if (getEmulateUnsupportedPstmts()) {
+						pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
+					} else {
+						throw sqlEx;
+					}
+				}
+			}
+		} else {
+			pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
+		}
+		
+		return pStmt;
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int, int, int)
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		if (getPedantic()) {
+			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
+				throw SQLError.createSQLException(
+						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		return prepareStatement(sql, resultSetType, resultSetConcurrency);
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int[])
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException {
+		java.sql.PreparedStatement pStmt = prepareStatement(sql);
+
+		((com.mysql.jdbc.PreparedStatement) pStmt)
+				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
+						&& (autoGenKeyIndexes.length > 0));
+
+		return pStmt;
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, String[])
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException {
+		java.sql.PreparedStatement pStmt = prepareStatement(sql);
+
+		((com.mysql.jdbc.PreparedStatement) pStmt)
+				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
+						&& (autoGenKeyColNames.length > 0));
+
+		return pStmt;
+	}
+
+	/**
+	 * Closes connection and frees resources.
+	 * 
+	 * @param calledExplicitly
+	 *            is this being called from close()
+	 * @param issueRollback
+	 *            should a rollback() be issued?
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected void realClose(boolean calledExplicitly, boolean issueRollback,
+			boolean skipLocalTeardown, Throwable reason) throws SQLException {
+		SQLException sqlEx = null;
+
+		if (this.isClosed()) {
+			return;
+		}
+		
+		this.forceClosedReason = reason;
+		
+		try {
+			if (!skipLocalTeardown) {
+				if (!getAutoCommit() && issueRollback) {
+					try {
+						rollback();
+					} catch (SQLException ex) {
+						sqlEx = ex;
+					}
+				}
+
+				reportMetrics();
+
+				if (getUseUsageAdvisor()) {
+					if (!calledExplicitly) {
+						String message = "Connection implicitly closed by Driver. You should call Connection.close() from your code to free resources more efficiently and avoid resource leaks.";
+
+						this.eventSink.consumeEvent(new ProfilerEvent(
+								ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
+								this.getCatalog(), this.getId(), -1, -1, System
+										.currentTimeMillis(), 0, Constants.MILLIS_I18N,
+										null,
+								this.pointOfOrigin, message));
+					}
+
+					long connectionLifeTime = System.currentTimeMillis()
+							- this.connectionCreationTimeMillis;
+
+					if (connectionLifeTime < 500) {
+						String message = "Connection lifetime of < .5 seconds. You might be un-necessarily creating short-lived connections and should investigate connection pooling to be more efficient.";
+
+						this.eventSink.consumeEvent(new ProfilerEvent(
+								ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
+								this.getCatalog(), this.getId(), -1, -1, System
+										.currentTimeMillis(), 0, Constants.MILLIS_I18N,
+										null,
+								this.pointOfOrigin, message));
+					}
+				}
+
+				try {
+					closeAllOpenStatements();
+				} catch (SQLException ex) {
+					sqlEx = ex;
+				}
+
+				if (this.io != null) {
+					try {
+						this.io.quit();
+					} catch (Exception e) {
+						;
+					}
+
+				}
+			} else {
+				this.io.forceClose();
+			}
+		} finally {
+			this.openStatements = null;
+			this.io = null;
+			ProfileEventSink.removeInstance(this);
+			this.isClosed = true;
+		}
+
+		if (sqlEx != null) {
+			throw sqlEx;
+		}
+
+	}
+
+	protected void recachePreparedStatement(ServerPreparedStatement pstmt) throws SQLException {
+		if (pstmt.isPoolable()) {
+			synchronized (this.serverSideStatementCache) {
+				this.serverSideStatementCache.put(pstmt.originalSql, pstmt);
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param queryTimeMs
+	 */
+	protected void registerQueryExecutionTime(long queryTimeMs) {
+		if (queryTimeMs > this.longestQueryTimeMs) {
+			this.longestQueryTimeMs = queryTimeMs;
+
+			repartitionPerformanceHistogram();
+		}
+
+		addToPerformanceHistogram(queryTimeMs, 1);
+
+		if (queryTimeMs < this.shortestQueryTimeMs) {
+			this.shortestQueryTimeMs = (queryTimeMs == 0) ? 1 : queryTimeMs;
+		}
+
+		this.numberOfQueriesIssued++;
+
+		this.totalQueryTimeMs += queryTimeMs;
+	}
+
+	/**
+	 * Register a Statement instance as open.
+	 * 
+	 * @param stmt
+	 *            the Statement instance to remove
+	 */
+	void registerStatement(StatementImpl stmt) {
+		synchronized (this.openStatements) {
+			this.openStatements.put(stmt, stmt);
+		}
+	}
+
+	/**
+	 * @see Connection#releaseSavepoint(Savepoint)
+	 */
+	public void releaseSavepoint(Savepoint arg0) throws SQLException {
+		// this is a no-op
+	}
+
+	private void repartitionHistogram(int[] histCounts, long[] histBreakpoints,
+			long currentLowerBound, long currentUpperBound) {
+
+		if (oldHistCounts == null) {
+			oldHistCounts = new int[histCounts.length];
+			oldHistBreakpoints = new long[histBreakpoints.length];
+		}
+
+		System.arraycopy(histCounts, 0, oldHistCounts, 0, histCounts.length);
+		
+		System.arraycopy(histBreakpoints, 0, oldHistBreakpoints, 0,
+				histBreakpoints.length);
+	
+		createInitialHistogram(histBreakpoints, currentLowerBound,
+				currentUpperBound);
+
+		for (int i = 0; i < HISTOGRAM_BUCKETS; i++) {
+			addToHistogram(histCounts, histBreakpoints, oldHistBreakpoints[i],
+					oldHistCounts[i], currentLowerBound, currentUpperBound);
+		}
+	}
+
+	private void repartitionPerformanceHistogram() {
+		checkAndCreatePerformanceHistogram();
+
+		repartitionHistogram(this.perfMetricsHistCounts,
+				this.perfMetricsHistBreakpoints,
+				this.shortestQueryTimeMs == Long.MAX_VALUE ? 0
+						: this.shortestQueryTimeMs, this.longestQueryTimeMs);
+	}
+
+	private void repartitionTablesAccessedHistogram() {
+		checkAndCreateTablesAccessedHistogram();
+
+		repartitionHistogram(this.numTablesMetricsHistCounts,
+				this.numTablesMetricsHistBreakpoints,
+				this.minimumNumberTablesAccessed == Long.MAX_VALUE ? 0
+						: this.minimumNumberTablesAccessed,
+				this.maximumNumberTablesAccessed);
+	}
+
+	private void reportMetrics() {
+		if (getGatherPerformanceMetrics()) {
+			StringBuffer logMessage = new StringBuffer(256);
+
+			logMessage.append("** Performance Metrics Report **\n");
+			logMessage.append("\nLongest reported query: "
+					+ this.longestQueryTimeMs + " ms");
+			logMessage.append("\nShortest reported query: "
+					+ this.shortestQueryTimeMs + " ms");
+			logMessage
+					.append("\nAverage query execution time: "
+							+ (this.totalQueryTimeMs / this.numberOfQueriesIssued)
+							+ " ms");
+			logMessage.append("\nNumber of statements executed: "
+					+ this.numberOfQueriesIssued);
+			logMessage.append("\nNumber of result sets created: "
+					+ this.numberOfResultSetsCreated);
+			logMessage.append("\nNumber of statements prepared: "
+					+ this.numberOfPrepares);
+			logMessage.append("\nNumber of prepared statement executions: "
+					+ this.numberOfPreparedExecutes);
+
+			if (this.perfMetricsHistBreakpoints != null) {
+				logMessage.append("\n\n\tTiming Histogram:\n");
+				int maxNumPoints = 20;
+				int highestCount = Integer.MIN_VALUE;
+
+				for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
+					if (this.perfMetricsHistCounts[i] > highestCount) {
+						highestCount = this.perfMetricsHistCounts[i];
+					}
+				}
+
+				if (highestCount == 0) {
+					highestCount = 1; // avoid DIV/0
+				}
+
+				for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {
+
+					if (i == 0) {
+						logMessage.append("\n\tless than "
+								+ this.perfMetricsHistBreakpoints[i + 1]
+								+ " ms: \t" + this.perfMetricsHistCounts[i]);
+					} else {
+						logMessage.append("\n\tbetween "
+								+ this.perfMetricsHistBreakpoints[i] + " and "
+								+ this.perfMetricsHistBreakpoints[i + 1]
+								+ " ms: \t" + this.perfMetricsHistCounts[i]);
+					}
+
+					logMessage.append("\t");
+
+					int numPointsToGraph = (int) (maxNumPoints * ((double) this.perfMetricsHistCounts[i] / (double) highestCount));
+
+					for (int j = 0; j < numPointsToGraph; j++) {
+						logMessage.append("*");
+					}
+
+					if (this.longestQueryTimeMs < this.perfMetricsHistCounts[i + 1]) {
+						break;
+					}
+				}
+
+				if (this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.longestQueryTimeMs) {
+					logMessage.append("\n\tbetween ");
+					logMessage
+							.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
+					logMessage.append(" and ");
+					logMessage
+							.append(this.perfMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
+					logMessage.append(" ms: \t");
+					logMessage
+							.append(this.perfMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
+				}
+			}
+
+			if (this.numTablesMetricsHistBreakpoints != null) {
+				logMessage.append("\n\n\tTable Join Histogram:\n");
+				int maxNumPoints = 20;
+				int highestCount = Integer.MIN_VALUE;
+
+				for (int i = 0; i < (HISTOGRAM_BUCKETS); i++) {
+					if (this.numTablesMetricsHistCounts[i] > highestCount) {
+						highestCount = this.numTablesMetricsHistCounts[i];
+					}
+				}
+
+				if (highestCount == 0) {
+					highestCount = 1; // avoid DIV/0
+				}
+
+				for (int i = 0; i < (HISTOGRAM_BUCKETS - 1); i++) {
+
+					if (i == 0) {
+						logMessage.append("\n\t"
+								+ this.numTablesMetricsHistBreakpoints[i + 1]
+								+ " tables or less: \t\t"
+								+ this.numTablesMetricsHistCounts[i]);
+					} else {
+						logMessage.append("\n\tbetween "
+								+ this.numTablesMetricsHistBreakpoints[i]
+								+ " and "
+								+ this.numTablesMetricsHistBreakpoints[i + 1]
+								+ " tables: \t"
+								+ this.numTablesMetricsHistCounts[i]);
+					}
+
+					logMessage.append("\t");
+
+					int numPointsToGraph = (int) (maxNumPoints * ((double) this.numTablesMetricsHistCounts[i] / (double) highestCount));
+
+					for (int j = 0; j < numPointsToGraph; j++) {
+						logMessage.append("*");
+					}
+
+					if (this.maximumNumberTablesAccessed < this.numTablesMetricsHistBreakpoints[i + 1]) {
+						break;
+					}
+				}
+
+				if (this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2] < this.maximumNumberTablesAccessed) {
+					logMessage.append("\n\tbetween ");
+					logMessage
+							.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 2]);
+					logMessage.append(" and ");
+					logMessage
+							.append(this.numTablesMetricsHistBreakpoints[HISTOGRAM_BUCKETS - 1]);
+					logMessage.append(" tables: ");
+					logMessage
+							.append(this.numTablesMetricsHistCounts[HISTOGRAM_BUCKETS - 1]);
+				}
+			}
+
+			this.log.logInfo(logMessage);
+
+			this.metricsLastReportedMs = System.currentTimeMillis();
+		}
+	}
+
+	/**
+	 * Reports currently collected metrics if this feature is enabled and the
+	 * timeout has passed.
+	 */
+	private void reportMetricsIfNeeded() {
+		if (getGatherPerformanceMetrics()) {
+			if ((System.currentTimeMillis() - this.metricsLastReportedMs) > getReportMetricsIntervalMillis()) {
+				reportMetrics();
+			}
+		}
+	}
+
+	protected void reportNumberOfTablesAccessed(int numTablesAccessed) {
+		if (numTablesAccessed < this.minimumNumberTablesAccessed) {
+			this.minimumNumberTablesAccessed = numTablesAccessed;
+		}
+
+		if (numTablesAccessed > this.maximumNumberTablesAccessed) {
+			this.maximumNumberTablesAccessed = numTablesAccessed;
+
+			repartitionTablesAccessedHistogram();
+		}
+
+		addToTablesAccessedHistogram(numTablesAccessed, 1);
+	}
+
+	/**
+	 * Resets the server-side state of this connection. Doesn't work for MySQL
+	 * versions older than 4.0.6 or if isParanoid() is set (it will become a
+	 * no-op in these cases). Usually only used from connection pooling code.
+	 * 
+	 * @throws SQLException
+	 *             if the operation fails while resetting server state.
+	 */
+	public void resetServerState() throws SQLException {
+		if (!getParanoid()
+				&& ((this.io != null) && versionMeetsMinimum(4, 0, 6))) {
+			changeUser(this.user, this.password);
+		}
+	}
+
+	/**
+	 * The method rollback() drops all changes made since the previous
+	 * commit/rollback and releases any database locks currently held by the
+	 * Connection.
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * @see commit
+	 */
+	public void rollback() throws SQLException {
+		synchronized (getMutex()) {
+			checkClosed();
+	
+			try {
+				if (this.connectionLifecycleInterceptors != null) {
+					IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
+
+						void forEach(Object each) throws SQLException {
+							if (!((ConnectionLifecycleInterceptor)each).rollback()) {
+								this.stopIterating = true;
+							}
+						}
+					};
+					
+					iter.doForAll();
+					
+					if (!iter.fullIteration()) {
+						return;
+					}
+				}
+				// no-op if _relaxAutoCommit == true
+				if (this.autoCommit && !getRelaxAutoCommit()) {
+					throw SQLError.createSQLException(
+							"Can't call rollback when autocommit=true",
+							SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
+				} else if (this.transactionsSupported) {
+					try {
+						rollbackNoChecks();
+					} catch (SQLException sqlEx) {
+						// We ignore non-transactional tables if told to do so
+						if (getIgnoreNonTxTables()
+								&& (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
+							throw sqlEx;
+						}
+					}
+				}
+			} catch (SQLException sqlException) {
+				if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
+						.equals(sqlException.getSQLState())) {
+					throw SQLError.createSQLException(
+							"Communications link failure during rollback(). Transaction resolution unknown.",
+							SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN);
+				}
+	
+				throw sqlException;
+			} finally {
+				this.needsPing = this.getReconnectAtTxEnd();
+			}
+		}
+	}
+
+	/**
+	 * @see Connection#rollback(Savepoint)
+	 */
+	public void rollback(final Savepoint savepoint) throws SQLException {
+
+		if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
+			synchronized (getMutex()) {
+				checkClosed();
+	
+				try {
+					if (this.connectionLifecycleInterceptors != null) {
+						IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
+
+							void forEach(Object each) throws SQLException {
+								if (!((ConnectionLifecycleInterceptor)each).rollback(savepoint)) {
+									this.stopIterating = true;
+								}
+							}
+						};
+						
+						iter.doForAll();
+						
+						if (!iter.fullIteration()) {
+							return;
+						}
+					}
+					
+					StringBuffer rollbackQuery = new StringBuffer(
+							"ROLLBACK TO SAVEPOINT ");
+					rollbackQuery.append('`');
+					rollbackQuery.append(savepoint.getSavepointName());
+					rollbackQuery.append('`');
+	
+					java.sql.Statement stmt = null;
+	
+					try {
+						stmt = createStatement();
+	
+						stmt.executeUpdate(rollbackQuery.toString());
+					} catch (SQLException sqlEx) {
+						int errno = sqlEx.getErrorCode();
+	
+						if (errno == 1181) {
+							String msg = sqlEx.getMessage();
+	
+							if (msg != null) {
+								int indexOfError153 = msg.indexOf("153");
+	
+								if (indexOfError153 != -1) {
+									throw SQLError.createSQLException("Savepoint '"
+											+ savepoint.getSavepointName()
+											+ "' does not exist",
+											SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
+											errno);
+								}
+							}
+						}
+	
+						// We ignore non-transactional tables if told to do so
+						if (getIgnoreNonTxTables()
+								&& (sqlEx.getErrorCode() != SQLError.ER_WARNING_NOT_COMPLETE_ROLLBACK)) {
+							throw sqlEx;
+						}
+	
+						if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE
+								.equals(sqlEx.getSQLState())) {
+							throw SQLError.createSQLException(
+									"Communications link failure during rollback(). Transaction resolution unknown.",
+									SQLError.SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN);
+						}
+	
+						throw sqlEx;
+					} finally {
+						closeStatement(stmt);
+					}
+				} finally {
+					this.needsPing = this.getReconnectAtTxEnd();
+				}
+			}
+		} else {
+			throw new NotImplemented();
+		}
+	}
+
+	private void rollbackNoChecks() throws SQLException {
+		if (getUseLocalSessionState() && versionMeetsMinimum(5, 0, 0)) {
+			if (!this.io.inTransactionOnServer()) {
+				return; // effectively a no-op
+			}
+		}
+		
+		execSQL(null, "rollback", -1, null,
+				java.sql.ResultSet.TYPE_FORWARD_ONLY,
+				java.sql.ResultSet.CONCUR_READ_ONLY, false,
+				this.database, null, false);
+	}
+
+	/**
+	 * @see java.sql.Connection#prepareStatement(String)
+	 */
+	public java.sql.PreparedStatement serverPrepareStatement(String sql)
+		throws SQLException {
+
+		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
+
+		return ServerPreparedStatement.getInstance(this, nativeSql, this.getCatalog(),
+				java.sql.ResultSet.TYPE_SCROLL_SENSITIVE,
+				java.sql.ResultSet.CONCUR_READ_ONLY);
+	}
+
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, int)
+	 */
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException {
+		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
+
+		PreparedStatement pStmt = ServerPreparedStatement.getInstance(this, nativeSql, this.getCatalog(),
+				java.sql.ResultSet.TYPE_SCROLL_SENSITIVE,
+				java.sql.ResultSet.CONCUR_READ_ONLY);
+		
+		pStmt.setRetrieveGeneratedKeys(
+				autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
+
+		return pStmt;
+	}
+	
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, int, int)
+	 */
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
+
+		return ServerPreparedStatement.getInstance(this, nativeSql, this.getCatalog(),
+				resultSetType,
+				resultSetConcurrency);
+	}
+	
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, int, int, int)
+	 */
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		if (getPedantic()) {
+			if (resultSetHoldability != java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT) {
+				throw SQLError.createSQLException(
+						"HOLD_CUSRORS_OVER_COMMIT is only supported holdability level",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		return serverPrepareStatement(sql, resultSetType, resultSetConcurrency);
+	}
+	
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, int[])
+	 */
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException {
+		
+		PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql);
+		
+		pStmt
+				.setRetrieveGeneratedKeys((autoGenKeyIndexes != null)
+						&& (autoGenKeyIndexes.length > 0));
+
+		return pStmt;
+	}
+
+	/**
+	 * @see java.sql.Connection#prepareStatement(String, String[])
+	 */
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException {
+		PreparedStatement pStmt = (PreparedStatement) serverPrepareStatement(sql);
+
+		pStmt
+				.setRetrieveGeneratedKeys((autoGenKeyColNames != null)
+						&& (autoGenKeyColNames.length > 0));
+
+		return pStmt;
+	}
+	
+	protected boolean serverSupportsConvertFn() throws SQLException {
+		return versionMeetsMinimum(4, 0, 2);
+	}
+
+	/**
+	 * If a connection is in auto-commit mode, than all its SQL statements will
+	 * be executed and committed as individual transactions. Otherwise, its SQL
+	 * statements are grouped into transactions that are terminated by either
+	 * commit() or rollback(). By default, new connections are in auto- commit
+	 * mode. The commit occurs when the statement completes or the next execute
+	 * occurs, whichever comes first. In the case of statements returning a
+	 * ResultSet, the statement completes when the last row of the ResultSet has
+	 * been retrieved or the ResultSet has been closed. In advanced cases, a
+	 * single statement may return multiple results as well as output parameter
+	 * values. Here the commit occurs when all results and output param values
+	 * have been retrieved.
+	 * <p>
+	 * <b>Note:</b> MySQL does not support transactions, so this method is a
+	 * no-op.
+	 * </p>
+	 * 
+	 * @param autoCommitFlag -
+	 *            true enables auto-commit; false disables it
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setAutoCommit(final boolean autoCommitFlag) throws SQLException {
+		synchronized (getMutex()) {
+			checkClosed();
+			
+			if (this.connectionLifecycleInterceptors != null) {
+				IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
+
+					void forEach(Object each) throws SQLException {
+						if (!((ConnectionLifecycleInterceptor)each).setAutoCommit(autoCommitFlag)) {
+							this.stopIterating = true;
+						}
+					}
+				};
+				
+				iter.doForAll();
+				
+				if (!iter.fullIteration()) {
+					return;
+				}
+			}
+	
+			if (getAutoReconnectForPools()) {
+				setHighAvailability(true);
+			}
+	
+			try {
+				if (this.transactionsSupported) {
+	
+					boolean needsSetOnServer = true;
+	
+					if (this.getUseLocalSessionState()
+							&& this.autoCommit == autoCommitFlag) {
+						needsSetOnServer = false;
+					} else if (!this.getHighAvailability()) {
+						needsSetOnServer = this.getIO()
+								.isSetNeededForAutoCommitMode(autoCommitFlag);
+					}
+	
+					// this internal value must be set first as failover depends on
+					// it
+					// being set to true to fail over (which is done by most
+					// app servers and connection pools at the end of
+					// a transaction), and the driver issues an implicit set
+					// based on this value when it (re)-connects to a server
+					// so the value holds across connections
+					this.autoCommit = autoCommitFlag;
+	
+					if (needsSetOnServer) {
+						execSQL(null, autoCommitFlag ? "SET autocommit=1"
+								: "SET autocommit=0", -1, null,
+								java.sql.ResultSet.TYPE_FORWARD_ONLY,
+								java.sql.ResultSet.CONCUR_READ_ONLY, false,
+								this.database, null, false);
+					}
+	
+				} else {
+					if ((autoCommitFlag == false) && !getRelaxAutoCommit()) {
+						throw SQLError.createSQLException("MySQL Versions Older than 3.23.15 "
+								+ "do not support transactions",
+								SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
+					}
+	
+					this.autoCommit = autoCommitFlag;
+				}
+			} finally {
+				if (this.getAutoReconnectForPools()) {
+					setHighAvailability(false);
+				}
+			}
+	
+			return;
+		}
+	}
+
+	/**
+	 * A sub-space of this Connection's database may be selected by setting a
+	 * catalog name. If the driver does not support catalogs, it will silently
+	 * ignore this request
+	 * <p>
+	 * <b>Note:</b> MySQL's notion of catalogs are individual databases.
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            the database for this connection to use
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setCatalog(final String catalog) throws SQLException {
+		synchronized (getMutex()) {
+			checkClosed();
+	
+			if (catalog == null) {
+				throw SQLError.createSQLException("Catalog can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+			
+			if (this.connectionLifecycleInterceptors != null) {
+				IterateBlock iter = new IterateBlock(this.connectionLifecycleInterceptors.iterator()) {
+
+					void forEach(Object each) throws SQLException {
+						if (!((ConnectionLifecycleInterceptor)each).setCatalog(catalog)) {
+							this.stopIterating = true;
+						}
+					}
+				};
+				
+				iter.doForAll();
+				
+				if (!iter.fullIteration()) {
+					return;
+				}
+			}
+			
+			if (getUseLocalSessionState()) {
+				if (this.lowerCaseTableNames) {
+					if (this.database.equalsIgnoreCase(catalog)) {
+						return;
+					}
+				} else {
+					if (this.database.equals(catalog)) {
+						return;
+					}
+				}
+			}
+			
+			String quotedId = this.dbmd.getIdentifierQuoteString();
+	
+			if ((quotedId == null) || quotedId.equals(" ")) {
+				quotedId = "";
+			}
+	
+			StringBuffer query = new StringBuffer("USE ");
+			query.append(quotedId);
+			query.append(catalog);
+			query.append(quotedId);
+	
+			execSQL(null, query.toString(), -1, null,
+					java.sql.ResultSet.TYPE_FORWARD_ONLY,
+					java.sql.ResultSet.CONCUR_READ_ONLY, false,
+					this.database, null, false);
+			
+			this.database = catalog;
+		}
+	}
+
+	/**
+	 * @param failedOver
+	 *            The failedOver to set.
+	 */
+	public synchronized void setFailedOver(boolean flag) {
+		this.failedOver = flag;
+	}
+
+	/**
+	 * Sets state for a failed-over connection
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void setFailedOverState() throws SQLException {
+		if (getFailOverReadOnly()) {
+			setReadOnlyInternal(true);
+		}
+
+		this.queriesIssuedFailedOver = 0;
+		this.failedOver = true;
+		this.masterFailTimeMillis = System.currentTimeMillis();
+	}
+
+	/**
+	 * @see Connection#setHoldability(int)
+	 */
+	public void setHoldability(int arg0) throws SQLException {
+		// do nothing
+	}
+
+	public void setInGlobalTx(boolean flag) {
+		this.isInGlobalTx = flag;
+	}
+
+	// exposed for testing
+	/**
+	 * @param preferSlaveDuringFailover
+	 *            The preferSlaveDuringFailover to set.
+	 */
+	public void setPreferSlaveDuringFailover(boolean flag) {
+		this.preferSlaveDuringFailover = flag;
+	}
+
+	void setReadInfoMsgEnabled(boolean flag) {
+		this.readInfoMsg = flag;
+	}
+
+	/**
+	 * You can put a connection in read-only mode as a hint to enable database
+	 * optimizations <B>Note:</B> setReadOnly cannot be called while in the
+	 * middle of a transaction
+	 * 
+	 * @param readOnlyFlag -
+	 *            true enables read-only mode; false disables it
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setReadOnly(boolean readOnlyFlag) throws SQLException {
+		checkClosed();
+		
+		// Ignore calls to this method if we're failed over and
+		// we're configured to fail over read-only.
+		if (this.failedOver && getFailOverReadOnly() && !readOnlyFlag) {
+			return;
+		}
+	
+		setReadOnlyInternal(readOnlyFlag);
+	}
+	
+	protected void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException {
+		this.readOnly = readOnlyFlag;
+	}
+	
+	/**
+	 * @see Connection#setSavepoint()
+	 */
+	public java.sql.Savepoint setSavepoint() throws SQLException {
+		MysqlSavepoint savepoint = new MysqlSavepoint();
+
+		setSavepoint(savepoint);
+
+		return savepoint;
+	}
+
+	private void setSavepoint(MysqlSavepoint savepoint) throws SQLException {
+
+		if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) {
+			synchronized (getMutex()) {
+				checkClosed();
+	
+				StringBuffer savePointQuery = new StringBuffer("SAVEPOINT ");
+				savePointQuery.append('`');
+				savePointQuery.append(savepoint.getSavepointName());
+				savePointQuery.append('`');
+	
+				java.sql.Statement stmt = null;
+	
+				try {
+					stmt = createStatement();
+	
+					stmt.executeUpdate(savePointQuery.toString());
+				} finally {
+					closeStatement(stmt);
+				}
+			}
+		} else {
+			throw new NotImplemented();
+		}
+	}
+	
+	/**
+	 * @see Connection#setSavepoint(String)
+	 */
+	public synchronized java.sql.Savepoint setSavepoint(String name) throws SQLException {
+		MysqlSavepoint savepoint = new MysqlSavepoint(name);
+
+		setSavepoint(savepoint);
+
+		return savepoint;
+	}
+	
+	/**
+	 * 
+	 */
+	private void setSessionVariables() throws SQLException {
+		if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) {
+			List variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'",
+					false);
+
+			int numVariablesToSet = variablesToSet.size();
+
+			java.sql.Statement stmt = null;
+
+			try {
+				stmt = getMetadataSafeStatement();
+
+				for (int i = 0; i < numVariablesToSet; i++) {
+					String variableValuePair = (String) variablesToSet.get(i);
+
+					if (variableValuePair.startsWith("@")) {
+						stmt.executeUpdate("SET " + variableValuePair);
+					} else {
+						stmt.executeUpdate("SET SESSION " + variableValuePair);
+					}
+				}
+			} finally {
+				if (stmt != null) {
+					stmt.close();
+				}
+			}
+		}
+
+	}
+	
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param level
+	 *            DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public synchronized void setTransactionIsolation(int level) throws SQLException {
+		checkClosed();
+
+		if (this.hasIsolationLevels) {
+			String sql = null;
+
+			boolean shouldSendSet = false;
+
+			if (getAlwaysSendSetIsolation()) {
+				shouldSendSet = true;
+			} else {
+				if (level != this.isolationLevel) {
+					shouldSendSet = true;
+				}
+			}
+
+			if (getUseLocalSessionState()) {
+				shouldSendSet = this.isolationLevel != level;
+			}
+
+			if (shouldSendSet) {
+				switch (level) {
+				case java.sql.Connection.TRANSACTION_NONE:
+					throw SQLError.createSQLException("Transaction isolation level "
+							+ "NONE not supported by MySQL");
+
+				case java.sql.Connection.TRANSACTION_READ_COMMITTED:
+					sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";
+
+					break;
+
+				case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
+					sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
+
+					break;
+
+				case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
+					sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";
+
+					break;
+
+				case java.sql.Connection.TRANSACTION_SERIALIZABLE:
+					sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";
+
+					break;
+
+				default:
+					throw SQLError.createSQLException("Unsupported transaction "
+							+ "isolation level '" + level + "'",
+							SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+				}
+
+				execSQL(null, sql, -1, null,
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY,false,
+						this.database, null, false);
+
+				this.isolationLevel = level;
+			}
+		} else {
+			throw SQLError.createSQLException("Transaction Isolation Levels are "
+					+ "not supported on MySQL versions older than 3.23.36.",
+					SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+		}
+	}
+	
+	/**
+	 * JDBC 2.0 Install a type-map object as the default type-map for this
+	 * connection
+	 * 
+	 * @param map
+	 *            the type mapping
+	 * @throws SQLException
+	 *             if a database error occurs.
+	 */
+	public synchronized void setTypeMap(java.util.Map map) throws SQLException {
+		this.typeMap = map;
+	}
+	
+	private void setupServerForTruncationChecks() throws SQLException {
+		if (getJdbcCompliantTruncation()) {
+			if (versionMeetsMinimum(5, 0, 2)) {
+				String currentSqlMode = 
+					(String)this.serverVariables.get("sql_mode");
+				
+				boolean strictTransTablesIsSet = StringUtils.indexOfIgnoreCase(currentSqlMode, "STRICT_TRANS_TABLES") != -1;
+				
+				if (currentSqlMode == null ||
+						currentSqlMode.length() == 0 || !strictTransTablesIsSet) {
+					StringBuffer commandBuf = new StringBuffer("SET sql_mode='");
+					
+					if (currentSqlMode != null && currentSqlMode.length() > 0) {
+						commandBuf.append(currentSqlMode);
+						commandBuf.append(",");
+					}
+					
+					commandBuf.append("STRICT_TRANS_TABLES'");
+					
+					execSQL(null,  commandBuf.toString(), -1, null,
+							java.sql.ResultSet.TYPE_FORWARD_ONLY,
+							java.sql.ResultSet.CONCUR_READ_ONLY, false,
+							this.database, null, false);
+					
+					setJdbcCompliantTruncation(false); // server's handling this for us now
+				} else if (strictTransTablesIsSet) {
+					// We didn't set it, but someone did, so we piggy back on it
+					setJdbcCompliantTruncation(false); // server's handling this for us now
+				}
+				
+			}
+		}
+	}
+	
+	/**
+	 * Should we try to connect back to the master? We try when we've been
+	 * failed over >= this.secondsBeforeRetryMaster _or_ we've issued >
+	 * this.queriesIssuedFailedOver
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	private boolean shouldFallBack() {
+		long secondsSinceFailedOver = (System.currentTimeMillis() - this.masterFailTimeMillis) / 1000;
+
+		// Done this way so we can set a condition in the debugger
+		boolean tryFallback = ((secondsSinceFailedOver >= getSecondsBeforeRetryMaster()) || (this.queriesIssuedFailedOver >= getQueriesBeforeRetryMaster()));
+
+		return tryFallback;
+	}
+	
+	/**
+	 * Used by MiniAdmin to shutdown a MySQL server
+	 * 
+	 * @throws SQLException
+	 *             if the command can not be issued.
+	 */
+	public void shutdownServer() throws SQLException {
+		try {
+			this.io.sendCommand(MysqlDefs.SHUTDOWN, null, null, false, null);
+		} catch (Exception ex) {
+			throw SQLError.createSQLException("Unhandled exception '" + ex.toString()
+					+ "'", SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean supportsIsolationLevel() {
+		return this.hasIsolationLevels;
+	}
+	
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean supportsQuotedIdentifiers() {
+		return this.hasQuotedIdentifiers;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean supportsTransactions() {
+		return this.transactionsSupported;
+	}
+
+	/**
+	 * Remove the given statement from the list of open statements
+	 * 
+	 * @param stmt
+	 *            the Statement instance to remove
+	 */
+	void unregisterStatement(StatementImpl stmt) {
+		if (this.openStatements != null) {
+			synchronized (this.openStatements) {
+				this.openStatements.remove(stmt);
+			}
+		}
+	}
+
+	/**
+	 * Called by statements on their .close() to let the connection know when it
+	 * is safe to set the connection back to 'default' row limits.
+	 * 
+	 * @param stmt
+	 *            the statement releasing it's max-rows requirement
+	 * @throws SQLException
+	 *             if a database error occurs issuing the statement that sets
+	 *             the limit default.
+	 */
+	void unsetMaxRows(StatementImpl stmt) throws SQLException {
+		synchronized (this.mutex) {
+			if (this.statementsUsingMaxRows != null) {
+				Object found = this.statementsUsingMaxRows.remove(stmt);
+
+				if ((found != null)
+						&& (this.statementsUsingMaxRows.size() == 0)) {
+					execSQL(null, "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1,
+							null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
+							java.sql.ResultSet.CONCUR_READ_ONLY, false, 
+							this.database, null, false);
+
+					this.maxRowsChanged = false;
+				}
+			}
+		}
+	}
+	
+	boolean useAnsiQuotedIdentifiers() {
+		return this.useAnsiQuotes;
+	}
+	
+	/**
+	 * Has maxRows() been set?
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	boolean useMaxRows() {
+		synchronized (this.mutex) {
+			return this.maxRowsChanged;
+		}
+	}
+	
+	public boolean versionMeetsMinimum(int major, int minor, int subminor)
+			throws SQLException {
+		checkClosed();
+
+		return this.io.versionMeetsMinimum(major, minor, subminor);
+	}
+
+	/**
+	 * Returns cached metadata (or null if not cached) for the given query,
+	 * which must match _exactly_.	 
+	 *  
+	 * This method is synchronized by the caller on getMutex(), so if
+	 * calling this method from internal code in the driver, make sure it's
+	 * synchronized on the mutex that guards communication with the server.
+	 * 
+	 * @param sql
+	 *            the query that is the key to the cache
+	 * 
+	 * @return metadata cached for the given SQL, or none if it doesn't
+	 *                  exist.
+	 */
+	protected CachedResultSetMetaData getCachedMetaData(String sql) {
+		if (this.resultSetMetadataCache != null) {
+			synchronized (this.resultSetMetadataCache) {
+				return (CachedResultSetMetaData) this.resultSetMetadataCache
+						.get(sql);
+			}
+		}
+
+		return null; // no cache exists
+	}
+
+	/**
+	 * Caches CachedResultSetMetaData that has been placed in the cache using
+	 * the given SQL as a key.
+	 * 
+	 * This method is synchronized by the caller on getMutex(), so if
+	 * calling this method from internal code in the driver, make sure it's
+	 * synchronized on the mutex that guards communication with the server.
+	 * 
+	 * @param sql the query that the metadata pertains too.
+	 * @param cachedMetaData metadata (if it exists) to populate the cache.
+	 * @param resultSet the result set to retreive metadata from, or apply to.
+	 *
+	 * @throws SQLException
+	 */
+	protected void initializeResultsMetadataFromCache(String sql,
+			CachedResultSetMetaData cachedMetaData, ResultSetInternalMethods resultSet)
+			throws SQLException {
+
+		if (cachedMetaData == null) {
+			
+			// read from results
+			cachedMetaData = new CachedResultSetMetaData();
+
+			// assume that users will use named-based
+			// lookups
+			resultSet.buildIndexMapping();
+			resultSet.initializeWithMetadata();
+
+			if (resultSet instanceof UpdatableResultSet) {
+				((UpdatableResultSet)resultSet).checkUpdatability();
+			}
+
+			resultSet.populateCachedMetaData(cachedMetaData);
+
+			this.resultSetMetadataCache.put(sql, cachedMetaData);
+		} else {
+			resultSet.initializeFromCachedMetaData(cachedMetaData);
+			resultSet.initializeWithMetadata();
+			
+			if (resultSet instanceof UpdatableResultSet) {
+				((UpdatableResultSet)resultSet).checkUpdatability();
+			}
+		}
+	}
+	
+	/**
+	 * Returns the comment that will be prepended to all statements
+	 * sent to the server.
+	 * 
+	 * @return the comment that will be prepended to all statements
+	 * sent to the server.
+	 */
+	public String getStatementComment() {
+		return this.statementComment;
+	}
+
+	/**
+	 * Sets the comment that will be prepended to all statements
+	 * sent to the server. Do not use slash-star or star-slash tokens 
+	 * in the comment as these will be added by the driver itself.
+	 * 
+	 * @param comment  the comment that will be prepended to all statements
+	 * sent to the server.
+	 */
+	public void setStatementComment(String comment) {
+		this.statementComment = comment;
+	}
+	
+	public synchronized void reportQueryTime(long millisOrNanos) {
+		this.queryTimeCount++;
+		this.queryTimeSum += millisOrNanos;
+		this.queryTimeSumSquares += (millisOrNanos * millisOrNanos);
+		this.queryTimeMean = ((this.queryTimeMean * (this.queryTimeCount - 1)) + millisOrNanos)
+				/ this.queryTimeCount;
+	}
+	
+	public synchronized boolean isAbonormallyLongQuery(long millisOrNanos) {
+		if (this.queryTimeCount < 15) {
+			return false; // need a minimum amount for this to make sense
+		}
+		
+		double stddev = Math.sqrt((this.queryTimeSumSquares - ((this.queryTimeSum*this.queryTimeSum) / this.queryTimeCount)) / (this.queryTimeCount - 1));
+		
+		return millisOrNanos > (this.queryTimeMean + 5 * stddev);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionLifecycleInterceptor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,115 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.sql.Savepoint;
+
+/**
+ * Implementors of this interface can be installed via the 
+ * "connectionLifecycleInterceptors" configuration property and receive
+ * events and alter behavior of "lifecycle" methods on our connection
+ * implementation.
+ * 
+ * The driver will create one instance of a given interceptor per-connection.
+ */
+public interface ConnectionLifecycleInterceptor extends Extension {
+	/**
+	 * Called when an application calls Connection.close(), before the driver
+	 * processes its own internal logic for close.
+	 * 
+	 * @throws SQLException
+	 */
+	public abstract void close() throws SQLException;
+	
+	/**
+	 * Called when an application calls Connection.commit(), before the
+	 * driver processes its own internal logic for commit().
+	 * 
+	 * Interceptors should return "true" if the driver should perform
+	 * its own internal logic for commit(), or "false" if not.
+	 * 
+	 * @return "true" if the driver should perform
+	 * its own internal logic for commit(), or "false" if not.
+	 * 
+	 * @throws SQLException if an error occurs
+	 */
+	public abstract boolean commit() throws SQLException;
+	
+	/**
+	 * Called when an application calls Connection.rollback(), before the
+	 * driver processes its own internal logic for rollback().
+	 * 
+	 * Interceptors should return "true" if the driver should perform
+	 * its own internal logic for rollback(), or "false" if not.
+	 * 
+	 * @return "true" if the driver should perform
+	 * its own internal logic for rollback(), or "false" if not.
+	 * 
+	 * @throws SQLException if an error occurs
+	 */
+	public abstract boolean rollback() throws SQLException;
+	
+	/**
+	 * Called when an application calls Connection.rollback(), before the
+	 * driver processes its own internal logic for rollback().
+	 * 
+	 * Interceptors should return "true" if the driver should perform
+	 * its own internal logic for rollback(), or "false" if not.
+	 * 
+	 * @return "true" if the driver should perform
+	 * its own internal logic for rollback(), or "false" if not.
+	 * 
+	 * @throws SQLException if an error occurs
+	 */
+	public abstract boolean rollback(Savepoint s) throws SQLException;
+	
+	/**
+	 * Called when an application calls Connection.setAutoCommit(), before the
+	 * driver processes its own internal logic for setAutoCommit().
+	 * 
+	 * Interceptors should return "true" if the driver should perform
+	 * its own internal logic for setAutoCommit(), or "false" if not.
+	 * 
+	 * @return "true" if the driver should perform
+	 * its own internal logic for setAutoCommit(), or "false" if not.
+	 * 
+	 * @throws SQLException if an error occurs
+	 */
+	public abstract boolean setAutoCommit(boolean flag) throws SQLException;
+	
+	/**
+	 * Called when an application calls Connection.setCatalog(), before the
+	 * driver processes its own internal logic for setCatalog().
+	 * 
+	 * Interceptors should return "true" if the driver should perform
+	 * its own internal logic for setCatalog(), or "false" if not.
+	 * 
+	 * @return "true" if the driver should perform
+	 * its own internal logic for setCatalog(), or "false" if not.
+	 * 
+	 * @throws SQLException if an error occurs
+	 */
+	public abstract boolean setCatalog(String catalog) throws SQLException;
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionProperties.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionProperties.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -19,2006 +19,313 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-
-
  */
+
 package com.mysql.jdbc;
 
-import com.mysql.jdbc.log.Jdk14Logger;
-import com.mysql.jdbc.log.Log;
-import com.mysql.jdbc.log.StandardLogger;
-
-import java.io.Serializable;
-import java.io.UnsupportedEncodingException;
-
-import java.sql.DriverPropertyInfo;
 import java.sql.SQLException;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.TreeMap;
+public interface ConnectionProperties {
 
-import javax.naming.RefAddr;
-import javax.naming.Reference;
-import javax.naming.StringRefAddr;
-
-/**
- * Represents configurable properties for Connections and DataSources. Can also
- * expose properties as JDBC DriverPropertyInfo if required as well.
- * 
- * @author Mark Matthews
- * @version $Id: ConnectionProperties.java,v 1.1.2.2 2005/05/17 14:58:56
- *          mmatthews Exp $
- */
-public class ConnectionProperties implements Serializable {
-	
-	private static final long serialVersionUID = 4257801713007640580L;
-
-	class BooleanConnectionProperty extends ConnectionProperty implements Serializable {
-	
-		private static final long serialVersionUID = 2540132501709159404L;
-
-		/**
-		 * DOCUMENT ME!
-		 * 
-		 * @param propertyNameToSet
-		 * @param defaultValueToSet
-		 * @param descriptionToSet
-		 *            DOCUMENT ME!
-		 * @param sinceVersionToSet
-		 *            DOCUMENT ME!
-		 */
-		BooleanConnectionProperty(String propertyNameToSet,
-				boolean defaultValueToSet, String descriptionToSet,
-				String sinceVersionToSet, String category, int orderInCategory) {
-			super(propertyNameToSet, new Boolean(defaultValueToSet), null, 0,
-					0, descriptionToSet, sinceVersionToSet, category,
-					orderInCategory);
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getAllowableValues()
-		 */
-		String[] getAllowableValues() {
-			return new String[] { "true", "false", "yes", "no" };
-		}
-
-		boolean getValueAsBoolean() {
-			return ((Boolean) this.valueAsObject).booleanValue();
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#hasValueConstraints()
-		 */
-		boolean hasValueConstraints() {
-			return true;
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#initializeFrom(java.util.Properties)
-		 */
-		void initializeFrom(String extractedValue) throws SQLException {
-			if (extractedValue != null) {
-				validateStringValues(extractedValue);
-
-				this.valueAsObject = new Boolean(extractedValue
-						.equalsIgnoreCase("TRUE")
-						|| extractedValue.equalsIgnoreCase("YES"));
-			} else {
-				this.valueAsObject = this.defaultValue;
-			}
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#isRangeBased()
-		 */
-		boolean isRangeBased() {
-			return false;
-		}
-
-		void setValue(boolean valueFlag) {
-			this.valueAsObject = new Boolean(valueFlag);
-		}
-	}
-
-	abstract class ConnectionProperty implements Serializable {
-		String[] allowableValues;
-
-		String categoryName;
-
-		Object defaultValue;
-
-		int lowerBound;
-
-		int order;
-
-		String propertyName;
-
-		String sinceVersion;
-
-		int upperBound;
-
-		Object valueAsObject;
-
-		boolean required;
-		
-		String description;
-		
-		public ConnectionProperty() {}
-		
-		ConnectionProperty(String propertyNameToSet, Object defaultValueToSet,
-				String[] allowableValuesToSet, int lowerBoundToSet,
-				int upperBoundToSet, String descriptionToSet,
-				String sinceVersionToSet, String category, int orderInCategory) {
-			
-			this.description = descriptionToSet;
-			this.propertyName = propertyNameToSet;
-			this.defaultValue = defaultValueToSet;
-			this.valueAsObject = defaultValueToSet;
-			this.allowableValues = allowableValuesToSet;
-			this.lowerBound = lowerBoundToSet;
-			this.upperBound = upperBoundToSet;
-			this.required = false;
-			this.sinceVersion = sinceVersionToSet;
-			this.categoryName = category;
-			this.order = orderInCategory;
-		}
-
-		String[] getAllowableValues() {
-			return this.allowableValues;
-		}
-
-		/**
-		 * @return Returns the categoryName.
-		 */
-		String getCategoryName() {
-			return this.categoryName;
-		}
-
-		Object getDefaultValue() {
-			return this.defaultValue;
-		}
-
-		int getLowerBound() {
-			return this.lowerBound;
-		}
-
-		/**
-		 * @return Returns the order.
-		 */
-		int getOrder() {
-			return this.order;
-		}
-
-		String getPropertyName() {
-			return this.propertyName;
-		}
-
-		int getUpperBound() {
-			return this.upperBound;
-		}
-
-		Object getValueAsObject() {
-			return this.valueAsObject;
-		}
-
-		abstract boolean hasValueConstraints();
-
-		void initializeFrom(Properties extractFrom) throws SQLException {
-			String extractedValue = extractFrom.getProperty(getPropertyName());
-			extractFrom.remove(getPropertyName());
-			initializeFrom(extractedValue);
-		}
-
-		void initializeFrom(Reference ref) throws SQLException {
-			RefAddr refAddr = ref.get(getPropertyName());
-
-			if (refAddr != null) {
-				String refContentAsString = (String) refAddr.getContent();
-
-				initializeFrom(refContentAsString);
-			}
-		}
-
-		abstract void initializeFrom(String extractedValue) throws SQLException;
-
-		abstract boolean isRangeBased();
-
-		/**
-		 * @param categoryName
-		 *            The categoryName to set.
-		 */
-		void setCategoryName(String categoryName) {
-			this.categoryName = categoryName;
-		}
-
-		/**
-		 * @param order
-		 *            The order to set.
-		 */
-		void setOrder(int order) {
-			this.order = order;
-		}
-
-		void setValueAsObject(Object obj) {
-			this.valueAsObject = obj;
-		}
-
-		void storeTo(Reference ref) {
-			if (getValueAsObject() != null) {
-				ref.add(new StringRefAddr(getPropertyName(), getValueAsObject()
-						.toString()));
-			}
-		}
-
-		DriverPropertyInfo getAsDriverPropertyInfo() {
-			DriverPropertyInfo dpi = new DriverPropertyInfo(this.propertyName, null);
-			dpi.choices = getAllowableValues();
-			dpi.value = (this.valueAsObject != null) ? this.valueAsObject.toString() : null;
-			dpi.required = this.required;
-			dpi.description = this.description;
-			
-			return dpi;
-		}
-		
-
-		void validateStringValues(String valueToValidate) throws SQLException {
-			String[] validateAgainst = getAllowableValues();
-
-			if (valueToValidate == null) {
-				return;
-			}
-
-			if ((validateAgainst == null) || (validateAgainst.length == 0)) {
-				return;
-			}
-
-			for (int i = 0; i < validateAgainst.length; i++) {
-				if ((validateAgainst[i] != null)
-						&& validateAgainst[i].equalsIgnoreCase(valueToValidate)) {
-					return;
-				}
-			}
-
-			StringBuffer errorMessageBuf = new StringBuffer();
-
-			errorMessageBuf.append("The connection property '");
-			errorMessageBuf.append(getPropertyName());
-			errorMessageBuf.append("' only accepts values of the form: ");
-
-			if (validateAgainst.length != 0) {
-				errorMessageBuf.append("'");
-				errorMessageBuf.append(validateAgainst[0]);
-				errorMessageBuf.append("'");
-
-				for (int i = 1; i < (validateAgainst.length - 1); i++) {
-					errorMessageBuf.append(", ");
-					errorMessageBuf.append("'");
-					errorMessageBuf.append(validateAgainst[i]);
-					errorMessageBuf.append("'");
-				}
-
-				errorMessageBuf.append(" or '");
-				errorMessageBuf
-						.append(validateAgainst[validateAgainst.length - 1]);
-				errorMessageBuf.append("'");
-			}
-
-			errorMessageBuf.append(". The value '");
-			errorMessageBuf.append(valueToValidate);
-			errorMessageBuf.append("' is not in this set.");
-
-			throw SQLError.createSQLException(errorMessageBuf.toString(),
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-		}
-	}
-
-	class IntegerConnectionProperty extends ConnectionProperty implements Serializable {
-	
-		private static final long serialVersionUID = -8147538210820248187L;
-		
-		int multiplier = 1;
-
-		IntegerConnectionProperty(String propertyNameToSet,
-				int defaultValueToSet, int lowerBoundToSet,
-				int upperBoundToSet, String descriptionToSet,
-				String sinceVersionToSet, String category, int orderInCategory) {
-			super(propertyNameToSet, new Integer(defaultValueToSet), null,
-					lowerBoundToSet, upperBoundToSet, descriptionToSet,
-					sinceVersionToSet, category, orderInCategory);
-		}
-
-		/**
-		 * DOCUMENT ME!
-		 * 
-		 * @param propertyNameToSet
-		 * @param defaultValueToSet
-		 * @param descriptionToSet
-		 * @param sinceVersionToSet
-		 *            DOCUMENT ME!
-		 */
-
-		IntegerConnectionProperty(String propertyNameToSet,
-				int defaultValueToSet, String descriptionToSet,
-				String sinceVersionToSet, String category, int orderInCategory) {
-			this(propertyNameToSet, defaultValueToSet, 0, 0, descriptionToSet,
-					sinceVersionToSet, category, orderInCategory);
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getAllowableValues()
-		 */
-		String[] getAllowableValues() {
-			return null;
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getLowerBound()
-		 */
-		int getLowerBound() {
-			return this.lowerBound;
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getUpperBound()
-		 */
-		int getUpperBound() {
-			return this.upperBound;
-		}
-
-		int getValueAsInt() {
-			return ((Integer) this.valueAsObject).intValue();
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#hasValueConstraints()
-		 */
-		boolean hasValueConstraints() {
-			return false;
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#initializeFrom(java.lang.String)
-		 */
-		void initializeFrom(String extractedValue) throws SQLException {
-			if (extractedValue != null) {
-				try {
-					// Parse decimals, too
-					int intValue = Double.valueOf(extractedValue).intValue();
-
-					/*
-					 * if (isRangeBased()) { if ((intValue < getLowerBound()) ||
-					 * (intValue > getUpperBound())) { throw new
-					 * SQLException("The connection property '" +
-					 * getPropertyName() + "' only accepts integer values in the
-					 * range of " + getLowerBound() + " - " + getUpperBound() + ",
-					 * the value '" + extractedValue + "' exceeds this range.",
-					 * SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } }
-					 */
-					this.valueAsObject = new Integer(intValue * multiplier);
-				} catch (NumberFormatException nfe) {
-					throw SQLError.createSQLException("The connection property '"
-							+ getPropertyName()
-							+ "' only accepts integer values. The value '"
-							+ extractedValue
-							+ "' can not be converted to an integer.",
-							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-				}
-			} else {
-				this.valueAsObject = this.defaultValue;
-			}
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#isRangeBased()
-		 */
-		boolean isRangeBased() {
-			return getUpperBound() != getLowerBound();
-		}
-
-		void setValue(int valueFlag) {
-			this.valueAsObject = new Integer(valueFlag);
-		}
-	}
-
-	class MemorySizeConnectionProperty extends IntegerConnectionProperty implements Serializable {
-
-		private static final long serialVersionUID = 7351065128998572656L;
-
-		MemorySizeConnectionProperty(String propertyNameToSet,
-				int defaultValueToSet, int lowerBoundToSet,
-				int upperBoundToSet, String descriptionToSet,
-				String sinceVersionToSet, String category, int orderInCategory) {
-			super(propertyNameToSet, defaultValueToSet, lowerBoundToSet,
-					upperBoundToSet, descriptionToSet, sinceVersionToSet,
-					category, orderInCategory);
-			// TODO Auto-generated constructor stub
-		}
-
-		void initializeFrom(String extractedValue) throws SQLException {
-			if (extractedValue != null) {
-				if (extractedValue.endsWith("k")
-						|| extractedValue.endsWith("K")
-						|| extractedValue.endsWith("kb")
-						|| extractedValue.endsWith("Kb")
-						|| extractedValue.endsWith("kB")) {
-					multiplier = 1024;
-					int indexOfK = StringUtils.indexOfIgnoreCase(
-							extractedValue, "k");
-					extractedValue = extractedValue.substring(0, indexOfK);
-				} else if (extractedValue.endsWith("m")
-						|| extractedValue.endsWith("M")
-						|| extractedValue.endsWith("G")
-						|| extractedValue.endsWith("mb")
-						|| extractedValue.endsWith("Mb")
-						|| extractedValue.endsWith("mB")) {
-					multiplier = 1024 * 1024;
-					int indexOfM = StringUtils.indexOfIgnoreCase(
-							extractedValue, "m");
-					extractedValue = extractedValue.substring(0, indexOfM);
-				} else if (extractedValue.endsWith("g")
-						|| extractedValue.endsWith("G")
-						|| extractedValue.endsWith("gb")
-						|| extractedValue.endsWith("Gb")
-						|| extractedValue.endsWith("gB")) {
-					multiplier = 1024 * 1024 * 1024;
-					int indexOfG = StringUtils.indexOfIgnoreCase(
-							extractedValue, "g");
-					extractedValue = extractedValue.substring(0, indexOfG);
-				}
-			}
-
-			super.initializeFrom(extractedValue);
-		}
-
-		void setValue(String value) throws SQLException {
-			initializeFrom(value);
-		}
-	}
-
-	class StringConnectionProperty extends ConnectionProperty implements Serializable {
-	
-		private static final long serialVersionUID = 5432127962785948272L;
-
-		StringConnectionProperty(String propertyNameToSet,
-				String defaultValueToSet, String descriptionToSet,
-				String sinceVersionToSet, String category, int orderInCategory) {
-			this(propertyNameToSet, defaultValueToSet, null, descriptionToSet,
-					sinceVersionToSet, category, orderInCategory);
-		}
-
-		/**
-		 * DOCUMENT ME!
-		 * 
-		 * @param propertyNameToSet
-		 * @param defaultValueToSet
-		 * @param allowableValuesToSet
-		 * @param descriptionToSet
-		 * @param sinceVersionToSet
-		 *            DOCUMENT ME!
-		 */
-		StringConnectionProperty(String propertyNameToSet,
-				String defaultValueToSet, String[] allowableValuesToSet,
-				String descriptionToSet, String sinceVersionToSet,
-				String category, int orderInCategory) {
-			super(propertyNameToSet, defaultValueToSet, allowableValuesToSet,
-					0, 0, descriptionToSet, sinceVersionToSet, category,
-					orderInCategory);
-		}
-
-		String getValueAsString() {
-			return (String) this.valueAsObject;
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#hasValueConstraints()
-		 */
-		boolean hasValueConstraints() {
-			return (this.allowableValues != null)
-					&& (this.allowableValues.length > 0);
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#initializeFrom(java.util.Properties)
-		 */
-		void initializeFrom(String extractedValue) throws SQLException {
-			if (extractedValue != null) {
-				validateStringValues(extractedValue);
-
-				this.valueAsObject = extractedValue;
-			} else {
-				this.valueAsObject = this.defaultValue;
-			}
-		}
-
-		/**
-		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#isRangeBased()
-		 */
-		boolean isRangeBased() {
-			return false;
-		}
-
-		void setValue(String valueFlag) {
-			this.valueAsObject = valueFlag;
-		}
-	}
-
-	private static final String CONNECTION_AND_AUTH_CATEGORY = "Connection/Authentication";
-
-	private static final String DEBUGING_PROFILING_CATEGORY = "Debuging/Profiling";
-
-	private static final String HA_CATEGORY = "High Availability and Clustering";
-
-	private static final String MISC_CATEGORY = "Miscellaneous";
-
-	private static final String PERFORMANCE_CATEGORY = "Performance Extensions";
-	
-	private static final String SECURITY_CATEGORY = "Security";
-	
-	private static final String[] PROPERTY_CATEGORIES = new String[] {
-		CONNECTION_AND_AUTH_CATEGORY, HA_CATEGORY, SECURITY_CATEGORY,
-		PERFORMANCE_CATEGORY, DEBUGING_PROFILING_CATEGORY, MISC_CATEGORY };
-
-	private static final ArrayList PROPERTY_LIST = new ArrayList();
-
-	private static final String STANDARD_LOGGER_NAME = StandardLogger.class.getName();
-
-	protected static final String ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL = "convertToNull";
-
-	protected static final String ZERO_DATETIME_BEHAVIOR_EXCEPTION = "exception";
-
-	protected static final String ZERO_DATETIME_BEHAVIOR_ROUND = "round";
-
-	static {
-		try {
-			java.lang.reflect.Field[] declaredFields = ConnectionProperties.class
-					.getDeclaredFields();
-
-			for (int i = 0; i < declaredFields.length; i++) {
-				if (ConnectionProperties.ConnectionProperty.class
-						.isAssignableFrom(declaredFields[i].getType())) {
-					PROPERTY_LIST.add(declaredFields[i]);
-				}
-			}
-		} catch (Exception ex) {
-			throw new RuntimeException(ex.toString());
-		}
-	}
-
 	/**
-	 * Exposes all ConnectionPropertyInfo instances as DriverPropertyInfo
-	 * 
-	 * @param info
-	 *            the properties to load into these ConnectionPropertyInfo
-	 *            instances
-	 * @param slotsToReserve
-	 *            the number of DPI slots to reserve for 'standard' DPI
-	 *            properties (user, host, password, etc)
-	 * @return a list of all ConnectionPropertyInfo instances, as
-	 *         DriverPropertyInfo
-	 * @throws SQLException
-	 *             if an error occurs
-	 */
-	protected static DriverPropertyInfo[] exposeAsDriverPropertyInfo(
-			Properties info, int slotsToReserve) throws SQLException {
-		return (new ConnectionProperties() {
-		}).exposeAsDriverPropertyInfoInternal(info, slotsToReserve);
-	}
-
-	private BooleanConnectionProperty allowLoadLocalInfile = new BooleanConnectionProperty(
-			"allowLoadLocalInfile",
-			true,
-			"Should the driver allow use of 'LOAD DATA LOCAL INFILE...' (defaults to 'true').",
-			"3.0.3", SECURITY_CATEGORY, Integer.MAX_VALUE);
-
-	private BooleanConnectionProperty allowMultiQueries = new BooleanConnectionProperty(
-			"allowMultiQueries",
-			false,
-			"Allow the use of ';' to delimit multiple queries during one statement (true/false, defaults to 'false'",
-			"3.1.1", SECURITY_CATEGORY, 1);
-
-	private BooleanConnectionProperty allowNanAndInf = new BooleanConnectionProperty(
-			"allowNanAndInf",
-			false,
-			"Should the driver allow NaN or +/- INF values in PreparedStatement.setDouble()?",
-			"3.1.5", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty allowUrlInLocalInfile = new BooleanConnectionProperty(
-			"allowUrlInLocalInfile",
-			false,
-			"Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements?",
-			"3.1.4", SECURITY_CATEGORY, Integer.MAX_VALUE);
-
-	private BooleanConnectionProperty alwaysSendSetIsolation = new BooleanConnectionProperty(
-			"alwaysSendSetIsolation",
-			true,
-			"Should the driver always communicate with the database when "
-					+ " Connection.setTransactionIsolation() is called? "
-					+ "If set to false, the driver will only communicate with the "
-					+ "database when the requested transaction isolation is different "
-					+ "than the whichever is newer, the last value that was set via "
-					+ "Connection.setTransactionIsolation(), or the value that was read from "
-					+ "the server when the connection was established.",
-			"3.1.7", PERFORMANCE_CATEGORY, Integer.MAX_VALUE);
-
-	private BooleanConnectionProperty autoClosePStmtStreams = new BooleanConnectionProperty(
-			"autoClosePStmtStreams", 
-			false,
-			"Should the driver automatically call .close() on streams/readers passed as "
-			+ "arguments via set*() methods?",
-			"3.1.12",
-			MISC_CATEGORY,
-			Integer.MIN_VALUE);
-	
-	private BooleanConnectionProperty autoDeserialize = new BooleanConnectionProperty(
-			"autoDeserialize",
-			false,
-			"Should the driver automatically detect and de-serialize objects stored in BLOB fields?",
-			"3.1.5", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty autoGenerateTestcaseScript = new BooleanConnectionProperty(
-			"autoGenerateTestcaseScript", false,
-			"Should the driver dump the SQL it is executing, including server-side "
-					+ "prepared statements to STDERR?", "3.1.9",
-			DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	private boolean autoGenerateTestcaseScriptAsBoolean = false;
-
-	private BooleanConnectionProperty autoReconnect = new BooleanConnectionProperty(
-			"autoReconnect",
-			false,
-			"Should the driver try to re-establish stale and/or dead connections? "
-					+ "  If enabled the driver will throw an exception for a queries issued on a stale or dead connection, "
-					+ " which belong to the current transaction, but will attempt reconnect before the next query issued on the "
-					+ "connection in a new transaction. The use of this feature "
-					+ "is not recommended, because it has side effects related to session state and data consistency when applications don't"
-					+ "handle SQLExceptions properly, and is only designed to be used "
-					+ "when you are unable to configure your application to handle SQLExceptions resulting from dead and"
-					+ "stale connections properly. Alternatively, investigate setting the MySQL server variable \"wait_timeout\""
-					+ "to some high value rather than the default of 8 hours.",
-			"1.1", HA_CATEGORY, 0);
-
-	private BooleanConnectionProperty autoReconnectForPools = new BooleanConnectionProperty(
-			"autoReconnectForPools",
-			false,
-			"Use a reconnection strategy appropriate for connection pools (defaults to 'false')",
-			"3.1.3", HA_CATEGORY, 1);
-
-	private boolean autoReconnectForPoolsAsBoolean = false;
-
-	private MemorySizeConnectionProperty blobSendChunkSize = new MemorySizeConnectionProperty(
-			"blobSendChunkSize",
-			1024 * 1024,
-			1,
-			Integer.MAX_VALUE,
-			"Chunk to use when sending BLOB/CLOBs via ServerPreparedStatements",
-			"3.1.9", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty cacheCallableStatements = new BooleanConnectionProperty(
-			"cacheCallableStmts", false,
-			"Should the driver cache the parsing stage of CallableStatements",
-			"3.1.2", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty cachePreparedStatements = new BooleanConnectionProperty(
-			"cachePrepStmts",
-			false,
-			"Should the driver cache the parsing stage of PreparedStatements of client-side "
-					+ "prepared statements, the \"check\" for suitability of server-side prepared "
-					+ " and server-side prepared statements themselves?",
-			"3.0.10", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty cacheResultSetMetadata = new BooleanConnectionProperty(
-			"cacheResultSetMetadata",
-			false,
-			"Should the driver cache ResultSetMetaData for Statements and PreparedStatements? (Req. JDK-1.4+, true/false, default 'false')",
-			"3.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private boolean cacheResultSetMetaDataAsBoolean;
-
-	private BooleanConnectionProperty cacheServerConfiguration = new BooleanConnectionProperty(
-			"cacheServerConfiguration",
-			false,
-			"Should the driver cache the results of "
-					+ "'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis?",
-			"3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty callableStatementCacheSize = new IntegerConnectionProperty(
-			"callableStmtCacheSize",
-			100,
-			0,
-			Integer.MAX_VALUE,
-			"If 'cacheCallableStmts' is enabled, how many callable statements should be cached?",
-			"3.1.2", PERFORMANCE_CATEGORY, 5);
-
-	private BooleanConnectionProperty capitalizeTypeNames = new BooleanConnectionProperty(
-			"capitalizeTypeNames",
-			false,
-			"Capitalize type names in DatabaseMetaData? (usually only useful when using WebObjects, true/false, defaults to 'false')",
-			"2.0.7", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private StringConnectionProperty characterEncoding = new StringConnectionProperty(
-			"characterEncoding",
-			null,
-			"If 'useUnicode' is set to true, what character encoding should the driver use when dealing with strings? (defaults is to 'autodetect')",
-			"1.1g", MISC_CATEGORY, 5);
-
-	private String characterEncodingAsString = null;
-
-	private StringConnectionProperty characterSetResults = new StringConnectionProperty(
-			"characterSetResults", null,
-			"Character set to tell the server to return results as.", "3.0.13",
-			MISC_CATEGORY, 6);
-
-	private BooleanConnectionProperty clobberStreamingResults = new BooleanConnectionProperty(
-			"clobberStreamingResults",
-			false,
-			"This will cause a 'streaming' ResultSet to be automatically closed, "
-					+ "and any outstanding data still streaming from the server to be discarded if another query is executed "
-					+ "before all the data has been read from the server.",
-			"3.0.9", MISC_CATEGORY, Integer.MIN_VALUE);
-	
-	private StringConnectionProperty clobCharacterEncoding = new StringConnectionProperty(
-			"clobCharacterEncoding",
-			null,
-			"The character encoding to use for sending and retrieving TEXT, MEDIUMTEXT " +
-			"and LONGTEXT values instead of the configured connection characterEncoding",
-			"5.0.0", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private StringConnectionProperty connectionCollation = new StringConnectionProperty(
-			"connectionCollation",
-			null,
-			"If set, tells the server to use this collation via 'set collation_connection'",
-			"3.0.13", MISC_CATEGORY, 7);
-
-	private IntegerConnectionProperty connectTimeout = new IntegerConnectionProperty(
-			"connectTimeout", 0, 0, Integer.MAX_VALUE,
-			"Timeout for socket connect (in milliseconds), with 0 being no timeout. "
-					+ "Only works on JDK-1.4 or newer. Defaults to '0'.",
-			"3.0.1", CONNECTION_AND_AUTH_CATEGORY, 9);
-
-	private BooleanConnectionProperty continueBatchOnError = new BooleanConnectionProperty(
-			"continueBatchOnError",
-			true,
-			"Should the driver continue processing batch commands if "
-					+ "one statement fails. The JDBC spec allows either way (defaults to 'true').",
-			"3.0.3", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty createDatabaseIfNotExist = new BooleanConnectionProperty(
-			"createDatabaseIfNotExist",
-			false,
-			"Creates the database given in the URL if it doesn't yet exist. Assumes "
-					+ " the configured user has permissions to create databases.",
-			"3.1.9", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty defaultFetchSize = new IntegerConnectionProperty("defaultFetchSize", 0, "The driver will call setFetchSize(n) with this value on all newly-created Statements", "3.1.9", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty detectServerPreparedStmts = new BooleanConnectionProperty(
-			"useServerPrepStmts",
-			true,
-			"Use server-side prepared statements if the server supports them? (defaults to 'true').",
-			"3.1.0", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty dontTrackOpenResources = new BooleanConnectionProperty(
-			"dontTrackOpenResources",
-			false,
-			"The JDBC specification requires the driver to automatically track and close resources, "
-					+ "however if your application doesn't do a good job of "
-					+ "explicitly calling close() on statements or result sets, "
-					+ "this can cause memory leakage. Setting this property to true "
-					+ "relaxes this constraint, and can be more memory efficient for "
-					+ "some applications.", "3.1.7", PERFORMANCE_CATEGORY,
-			Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty dumpQueriesOnException = new BooleanConnectionProperty(
-			"dumpQueriesOnException",
-			false,
-			"Should the driver dump the contents of the query sent to the server in the message for SQLExceptions?",
-			"3.1.3", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty dynamicCalendars = new BooleanConnectionProperty(
-			"dynamicCalendars",
-			false,
-			"Should the driver retrieve the default"
-					+ " calendar when required, or cache it per connection/session?",
-			"3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty elideSetAutoCommits = new BooleanConnectionProperty(
-			"elideSetAutoCommits",
-			false,
-			"If using MySQL-4.1 or newer, should the driver only issue 'set autocommit=n' queries when the server's state doesn't match the requested state by Connection.setAutoCommit(boolean)?",
-			"3.1.3", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty emptyStringsConvertToZero = new BooleanConnectionProperty(
-			"emptyStringsConvertToZero", true,
-			"Should the driver allow conversions from empty string "
-					+ "fields to numeric values of '0'?", "3.1.8",
-			MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty emulateLocators = new BooleanConnectionProperty(
-			"emulateLocators", false, "N/A", "3.1.0", MISC_CATEGORY,
-			Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty emulateUnsupportedPstmts = new BooleanConnectionProperty(
-			"emulateUnsupportedPstmts",
-			true,
-			"Should the driver detect prepared statements that are not supported by the server, and "
-					+ "replace them with client-side emulated versions?",
-			"3.1.7", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty enableDeprecatedAutoreconnect = new BooleanConnectionProperty(
-			"enableDeprecatedAutoreconnect",
-			false,
-			"Auto-reconnect functionality is deprecated starting with version 3.2, and will be removed in version 3.3. Set this "
-					+ "property to 'true' to disable the check for the feature being configured.",
-			"3.2.1", HA_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty enablePacketDebug = new BooleanConnectionProperty(
-			"enablePacketDebug",
-			false,
-			"When enabled, a ring-buffer of 'packetDebugBufferSize' packets will be kept, and dumped when exceptions are thrown in key areas in the driver's code",
-			"3.1.3", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty explainSlowQueries = new BooleanConnectionProperty(
-			"explainSlowQueries",
-			false,
-			"If 'logSlowQueries' is enabled, should the driver automatically issue an 'EXPLAIN' on the"
-					+ " server and send the results to the configured log at a WARN level?",
-			"3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	/** When failed-over, set connection to read-only? */
-	private BooleanConnectionProperty failOverReadOnly = new BooleanConnectionProperty(
-			"failOverReadOnly",
-			true,
-			"When failing over in autoReconnect mode, should the connection be set to 'read-only'?",
-			"3.0.12", HA_CATEGORY, 2);
-
-	private BooleanConnectionProperty gatherPerformanceMetrics = new BooleanConnectionProperty(
-			"gatherPerfMetrics",
-			false,
-			"Should the driver gather performance metrics, and report them via the configured logger every 'reportMetricsIntervalMillis' milliseconds?",
-			"3.1.2", DEBUGING_PROFILING_CATEGORY, 1);
-
-	private boolean highAvailabilityAsBoolean = false;
-
-	private BooleanConnectionProperty holdResultsOpenOverStatementClose = new BooleanConnectionProperty(
-			"holdResultsOpenOverStatementClose",
-			false,
-			"Should the driver close result sets on Statement.close() as required by the JDBC specification?",
-			"3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty ignoreNonTxTables = new BooleanConnectionProperty(
-			"ignoreNonTxTables",
-			false,
-			"Ignore non-transactional table warning for rollback? (defaults to 'false').",
-			"3.0.9", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty initialTimeout = new IntegerConnectionProperty(
-			"initialTimeout", 2, 1, Integer.MAX_VALUE,
-			"If autoReconnect is enabled, the"
-					+ " initial time to wait between"
-					+ " re-connect attempts (in seconds, defaults to '2').",
-			"1.1", HA_CATEGORY, 5);
-
-	private BooleanConnectionProperty isInteractiveClient = new BooleanConnectionProperty(
-			"interactiveClient",
-			false,
-			"Set the CLIENT_INTERACTIVE flag, which tells MySQL "
-					+ "to timeout connections based on INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT",
-			"3.1.0", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty jdbcCompliantTruncation = new BooleanConnectionProperty(
-			"jdbcCompliantTruncation",
-			true,
-			"Should the driver throw java.sql.DataTruncation"
-					+ " exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings"
-					+ "(MySQL 4.1.0 and newer)?", "3.1.2", MISC_CATEGORY,
-			Integer.MIN_VALUE);
-
-	private boolean jdbcCompliantTruncationForReads = 
-		this.jdbcCompliantTruncation.getValueAsBoolean();
-	
-	private MemorySizeConnectionProperty locatorFetchBufferSize = new MemorySizeConnectionProperty(
-			"locatorFetchBufferSize",
-			1024 * 1024,
-			0,
-			Integer.MAX_VALUE,
-			"If 'emulateLocators' is configured to 'true', what size "
-					+ " buffer should be used when fetching BLOB data for getBinaryInputStream?",
-			"3.2.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private StringConnectionProperty loggerClassName = new StringConnectionProperty(
-			"logger", STANDARD_LOGGER_NAME,
-			"The name of a class that implements '" + Log.class.getName()
-					+ "' that will be used to log messages to."
-					+ "(default is '" + STANDARD_LOGGER_NAME + "', which "
-					+ "logs to STDERR)", "3.1.1", DEBUGING_PROFILING_CATEGORY,
-			0);
-
-	private BooleanConnectionProperty logSlowQueries = new BooleanConnectionProperty(
-			"logSlowQueries",
-			false,
-			"Should queries that take longer than 'slowQueryThresholdMillis' be logged?",
-			"3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty maintainTimeStats = new BooleanConnectionProperty(
-			"maintainTimeStats",
-			true,
-			"Should the driver maintain various internal timers to enable "
-					+ "idle time calculations as well as more verbose error messages when "
-					+ "the connection to the server fails? Setting this property to "
-					+ "false removes at least two calls to System.getCurrentTimeMillis() "
-					+ "per query.", "3.1.9", PERFORMANCE_CATEGORY,
-			Integer.MAX_VALUE);
-
-	private boolean maintainTimeStatsAsBoolean = true;
-
-	private IntegerConnectionProperty maxQuerySizeToLog = new IntegerConnectionProperty(
-			"maxQuerySizeToLog",
-			2048,
-			0,
-			Integer.MAX_VALUE,
-			"Controls the maximum length/size of a query that will get logged when profiling or tracing",
-			"3.1.3", DEBUGING_PROFILING_CATEGORY, 4);
-
-	private IntegerConnectionProperty maxReconnects = new IntegerConnectionProperty(
-			"maxReconnects",
-			3,
-			1,
-			Integer.MAX_VALUE,
-			"Maximum number of reconnects to attempt if autoReconnect is true, default is '3'.",
-			"1.1", HA_CATEGORY, 4);
-
-	private IntegerConnectionProperty maxRows = new IntegerConnectionProperty(
-			"maxRows", -1, -1, Integer.MAX_VALUE,
-			"The maximum number of rows to return "
-					+ " (0, the default means return all rows).",
-			"all versions", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private int maxRowsAsInt = -1;
-
-	private IntegerConnectionProperty metadataCacheSize = new IntegerConnectionProperty(
-			"metadataCacheSize",
-			50,
-			1,
-			Integer.MAX_VALUE,
-			"The number of queries to cache"
-					+ "ResultSetMetadata for if cacheResultSetMetaData is set to 'true' (default 50)",
-			"3.1.1", PERFORMANCE_CATEGORY, 5);
-
-	private BooleanConnectionProperty noAccessToProcedureBodies = new BooleanConnectionProperty(
-			"noAccessToProcedureBodies",
-			false,
-			"When determining procedure parameter types for CallableStatements, and the connected user "
-			+ " can't access procedure bodies through \"SHOW CREATE PROCEDURE\" or select on mysql.proc "
-			+ " should the driver instead create basic metadata (all parameters reported as INOUT VARCHARs) instead "
-			+ " of throwing an exception?",
-			"5.0.3", MISC_CATEGORY, Integer.MIN_VALUE);
-			
-	private BooleanConnectionProperty noDatetimeStringSync = new BooleanConnectionProperty(
-			"noDatetimeStringSync",
-			false,
-			"Don't ensure that ResultSet.getDatetimeType().toString().equals(ResultSet.getString())",
-			"3.1.7", MISC_CATEGORY, Integer.MIN_VALUE);
-	
-	private BooleanConnectionProperty noTimezoneConversionForTimeType = new BooleanConnectionProperty(
-			"noTimezoneConversionForTimeType",
-			false,
-			"Don't convert TIME values using the server timezone if 'useTimezone'='true'",
-			"5.0.0", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty nullCatalogMeansCurrent = new BooleanConnectionProperty(
-			"nullCatalogMeansCurrent",
-			true,
-			"When DatabaseMetadataMethods ask for a 'catalog' parameter, does the value null mean use the current catalog? "
-					+ "(this is not JDBC-compliant, but follows legacy behavior from earlier versions of the driver)",
-			"3.1.8", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty nullNamePatternMatchesAll = new BooleanConnectionProperty(
-			"nullNamePatternMatchesAll",
-			true,
-			"Should DatabaseMetaData methods that accept *pattern parameters treat null the same as '%' "
-					+ " (this is not JDBC-compliant, however older versions of the driver accepted this departure from the specification)",
-			"3.1.8", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty packetDebugBufferSize = new IntegerConnectionProperty(
-			"packetDebugBufferSize",
-			20,
-			0,
-			Integer.MAX_VALUE,
-			"The maximum number of packets to retain when 'enablePacketDebug' is true",
-			"3.1.3", DEBUGING_PROFILING_CATEGORY, 7);
-
-	private BooleanConnectionProperty paranoid = new BooleanConnectionProperty(
-			"paranoid",
-			false,
-			"Take measures to prevent exposure sensitive information in error messages and clear "
-					+ "data structures holding sensitive data when possible? (defaults to 'false')",
-			"3.0.1", SECURITY_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty pedantic = new BooleanConnectionProperty(
-			"pedantic", false, "Follow the JDBC spec to the letter.", "3.0.0",
-			MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty pinGlobalTxToPhysicalConnection = new BooleanConnectionProperty(
-			"pinGlobalTxToPhysicalConnection", false, "When using XAConnections, should the driver ensure that "
-			+ " operations on a given XID are always routed to the same physical connection? This allows the XAConnection"
-			+ " to support \"XA START ... JOIN\" after \"XA END\" has been called",
-			"5.0.1", MISC_CATEGORY, Integer.MIN_VALUE);
-	
-	private IntegerConnectionProperty preparedStatementCacheSize = new IntegerConnectionProperty(
-			"prepStmtCacheSize", 25, 0, Integer.MAX_VALUE,
-			"If prepared statement caching is enabled, "
-					+ "how many prepared statements should be cached?",
-			"3.0.10", PERFORMANCE_CATEGORY, 10);
-
-	private IntegerConnectionProperty preparedStatementCacheSqlLimit = new IntegerConnectionProperty(
-			"prepStmtCacheSqlLimit",
-			256,
-			1,
-			Integer.MAX_VALUE,
-			"If prepared statement caching is enabled, "
-					+ "what's the largest SQL the driver will cache the parsing for?",
-			"3.0.10", PERFORMANCE_CATEGORY, 11);
-
-	private BooleanConnectionProperty processEscapeCodesForPrepStmts = 
-		new BooleanConnectionProperty("processEscapeCodesForPrepStmts",
-				true,
-				"Should the driver process escape codes in queries that are prepared?",
-				"3.1.12",
-				MISC_CATEGORY, Integer.MIN_VALUE);
-	
-	private StringConnectionProperty profileSql = new StringConnectionProperty(
-			"profileSql",
-			null,
-			"Deprecated, use 'profileSQL' instead. Trace queries and their execution/fetch times on STDERR (true/false) defaults to 'false'",
-			"2.0.14", DEBUGING_PROFILING_CATEGORY, 3);
-
-	private BooleanConnectionProperty profileSQL = new BooleanConnectionProperty(
-			"profileSQL",
-			false,
-			"Trace queries and their execution/fetch times to the configured logger (true/false) defaults to 'false'",
-			"3.1.0", DEBUGING_PROFILING_CATEGORY, 1);
-
-	private boolean profileSQLAsBoolean = false;
-
-	private StringConnectionProperty propertiesTransform = new StringConnectionProperty(
-			NonRegisteringDriver.PROPERTIES_TRANSFORM_KEY,
-			null,
-			"An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that the driver will use to modify URL properties passed to the driver before attempting a connection",
-			"3.1.4", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty queriesBeforeRetryMaster = new IntegerConnectionProperty(
-			"queriesBeforeRetryMaster",
-			50,
-			1,
-			Integer.MAX_VALUE,
-			"Number of queries to issue before falling back to master when failed over "
-					+ "(when using multi-host failover). Whichever condition is met first, "
-					+ "'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an "
-					+ "attempt to be made to reconnect to the master. Defaults to 50.",
-			"3.0.2", HA_CATEGORY, 7);
-
-	private BooleanConnectionProperty reconnectAtTxEnd = new BooleanConnectionProperty(
-			"reconnectAtTxEnd", false,
-			"If autoReconnect is set to true, should the driver attempt reconnections"
-					+ "at the end of every transaction?", "3.0.10",
-			HA_CATEGORY, 4);
-
-	private boolean reconnectTxAtEndAsBoolean = false;
-
-	private BooleanConnectionProperty relaxAutoCommit = new BooleanConnectionProperty(
-			"relaxAutoCommit",
-			false,
-			"If the version of MySQL the driver connects to does not support transactions, still allow calls to commit(), rollback() and setAutoCommit() (true/false, defaults to 'false')?",
-			"2.0.13", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty reportMetricsIntervalMillis = new IntegerConnectionProperty(
-			"reportMetricsIntervalMillis",
-			30000,
-			0,
-			Integer.MAX_VALUE,
-			"If 'gatherPerfMetrics' is enabled, how often should they be logged (in ms)?",
-			"3.1.2", DEBUGING_PROFILING_CATEGORY, 3);
-
-	private BooleanConnectionProperty requireSSL = new BooleanConnectionProperty(
-			"requireSSL", false,
-			"Require SSL connection if useSSL=true? (defaults to 'false').",
-			"3.1.0", SECURITY_CATEGORY, 3);
-
-	private StringConnectionProperty resourceId = new StringConnectionProperty(
-			"resourceId",
-			null, "A globally unique name that identifies the resource that this datasource or connection is " +
-			"connected to, used for XAResource.isSameRM() when the driver can't determine this value based on " +
-			"hostnames used in the URL",
-			"5.0.1",
-			HA_CATEGORY,
-			Integer.MIN_VALUE);
-			
-	private BooleanConnectionProperty retainStatementAfterResultSetClose = new BooleanConnectionProperty(
-			"retainStatementAfterResultSetClose",
-			false,
-			"Should the driver retain the Statement reference in a ResultSet after ResultSet.close()"
-					+ " has been called. This is not JDBC-compliant after JDBC-4.0.",
-			"3.1.11", MISC_CATEGORY, Integer.MIN_VALUE);
-	
-	private BooleanConnectionProperty rewriteBatchedStatements = new BooleanConnectionProperty(
-			"rewriteBatchedStatements",
-			false, 
-			"Should the driver use multiqueries (irregardless of the setting of \"allowMultiQueries\") as well as "
-			+ "rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential "
-			+ "for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly.\n\n"
-			+ "Notice that for prepared statements, server-side prepared statements can not currently take advantage of "
-			+ "this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream()," 
-			+ "the driver won't be able to determine the optimium number of parameters per batch and you might receive " 
-			+ "an error from the driver that the resultant packet is too large.\n\n"
-			+ "Statement.getGeneratedKeys() for these rewritten statements only works when the entire " 
-			+ "batch includes INSERT statements.",
-			"3.1.13", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty rollbackOnPooledClose = new BooleanConnectionProperty(
-			"rollbackOnPooledClose",
-			true,
-			"Should the driver issue a rollback() when the logical connection in a pool is closed?",
-			"3.0.15", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty roundRobinLoadBalance = new BooleanConnectionProperty(
-			"roundRobinLoadBalance",
-			false,
-			"When autoReconnect is enabled, and failoverReadonly is false, should we pick hosts to connect to on a round-robin basis?",
-			"3.1.2", HA_CATEGORY, 5);
-
-	private BooleanConnectionProperty runningCTS13 = new BooleanConnectionProperty(
-			"runningCTS13",
-			false,
-			"Enables workarounds for bugs in Sun's JDBC compliance testsuite version 1.3",
-			"3.1.7", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private IntegerConnectionProperty secondsBeforeRetryMaster = new IntegerConnectionProperty(
-			"secondsBeforeRetryMaster",
-			30,
-			1,
-			Integer.MAX_VALUE,
-			"How long should the driver wait, when failed over, before attempting "
-					+ "to reconnect to the master server? Whichever condition is met first, "
-					+ "'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an "
-					+ "attempt to be made to reconnect to the master. Time in seconds, defaults to 30",
-			"3.0.2", HA_CATEGORY, 8);
-
-	private StringConnectionProperty serverTimezone = new StringConnectionProperty(
-			"serverTimezone",
-			null,
-			"Override detection/mapping of timezone. Used when timezone from server doesn't map to Java timezone",
-			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private StringConnectionProperty sessionVariables = new StringConnectionProperty(
-			"sessionVariables", null,
-			"A comma-separated list of name/value pairs to be sent as SET SESSION ... to "
-					+ " the server when the driver connects.", "3.1.8",
-			MISC_CATEGORY, Integer.MAX_VALUE);
-
-	private IntegerConnectionProperty slowQueryThresholdMillis = new IntegerConnectionProperty(
-			"slowQueryThresholdMillis",
-			2000,
-			0,
-			Integer.MAX_VALUE,
-			"If 'logSlowQueries' is enabled, how long should a query (in ms) before it is logged as 'slow'?",
-			"3.1.2", DEBUGING_PROFILING_CATEGORY, 9);
-
-	private StringConnectionProperty socketFactoryClassName = new StringConnectionProperty(
-			"socketFactory",
-			StandardSocketFactory.class.getName(),
-			"The name of the class that the driver should use for creating socket connections to the server. This class must implement the interface 'com.mysql.jdbc.SocketFactory' and have public no-args constructor.",
-			"3.0.3", CONNECTION_AND_AUTH_CATEGORY, 4);
-
-	private IntegerConnectionProperty socketTimeout = new IntegerConnectionProperty(
-			"socketTimeout",
-			0,
-			0,
-			Integer.MAX_VALUE,
-			"Timeout on network socket operations (0, the default means no timeout).",
-			"3.0.1", CONNECTION_AND_AUTH_CATEGORY, 10);
-
-	private BooleanConnectionProperty strictFloatingPoint = new BooleanConnectionProperty(
-			"strictFloatingPoint", false,
-			"Used only in older versions of compliance test", "3.0.0",
-			MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty strictUpdates = new BooleanConnectionProperty(
-			"strictUpdates",
-			true,
-			"Should the driver do strict checking (all primary keys selected) of updatable result sets (true, false, defaults to 'true')?",
-			"3.0.4", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty overrideSupportsIntegrityEnhancementFacility =
-		new BooleanConnectionProperty("overrideSupportsIntegrityEnhancementFacility",
-				false,
-				"Should the driver return \"true\" for DatabaseMetaData.supportsIntegrityEnhancementFacility() "
-				+ "even if the database doesn't support it to workaround applications that require this method to return "
-				+ "\"true\" to signal support of foreign keys, even though the SQL specification states that this facility "
-				+ "contains much more than just foreign key support (one such application being OpenOffice)?",
-				"3.1.12", MISC_CATEGORY, Integer.MIN_VALUE);
-	
-	private BooleanConnectionProperty tinyInt1isBit = new BooleanConnectionProperty(
-			"tinyInt1isBit",
-			true,
-			"Should the driver treat the datatype TINYINT(1) as the BIT type "
-					+ "(because the server silently converts BIT -> TINYINT(1) when creating tables)?",
-			"3.0.16", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty traceProtocol = new BooleanConnectionProperty(
-			"traceProtocol", false,
-			"Should trace-level network protocol be logged?", "3.1.2",
-			DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty transformedBitIsBoolean = new BooleanConnectionProperty(
-			"transformedBitIsBoolean",
-			false,
-			"If the driver converts TINYINT(1) to a different type, should it use BOOLEAN instead of BIT "
-					+ " for future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT type?",
-			"3.1.9", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useCompression = new BooleanConnectionProperty(
-			"useCompression",
-			false,
-			"Use zlib compression when communicating with the server (true/false)? Defaults to 'false'.",
-			"3.0.17", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE);
-
-	private StringConnectionProperty useConfig = new StringConnectionProperty(
-			"useConfigs",
-			null,
-			"Load the comma-delimited list of configuration properties before parsing the "
-					+ "URL or applying user-specified properties. These configurations are explained in the 'Configurations' of the documentation.",
-			"3.1.5", CONNECTION_AND_AUTH_CATEGORY, Integer.MAX_VALUE);
-
-	private BooleanConnectionProperty useCursorFetch = new BooleanConnectionProperty(
-			"useCursorFetch",
-			false,
-			"If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a statement, should "
-			+ " that statement use cursor-based fetching to retrieve rows?",
-			"5.0.0", PERFORMANCE_CATEGORY, Integer.MAX_VALUE);
-	
-	private BooleanConnectionProperty useFastIntParsing = new BooleanConnectionProperty(
-			"useFastIntParsing",
-			true,
-			"Use internal String->Integer conversion routines to avoid excessive object creation?",
-			"3.1.4", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useHostsInPrivileges = new BooleanConnectionProperty(
-			"useHostsInPrivileges",
-			true,
-			"Add '@hostname' to users in DatabaseMetaData.getColumn/TablePrivileges() (true/false), defaults to 'true'.",
-			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE);
-	private BooleanConnectionProperty useInformationSchema = new BooleanConnectionProperty(
-			"useInformationSchema",
-			false,
-			"When connected to MySQL-5.0.7 or newer, should the driver use the INFORMATION_SCHEMA to " 
-			+ " derive information used by DatabaseMetaData?",
-			"5.0.0", MISC_CATEGORY, Integer.MIN_VALUE);
-	private BooleanConnectionProperty useJDBCCompliantTimezoneShift = new BooleanConnectionProperty(
-			"useJDBCCompliantTimezoneShift",
-			false,
-			"Should the driver use JDBC-compliant rules when converting TIME/TIMESTAMP/DATETIME values' timezone information " +
-			"for those JDBC arguments which take a java.util.Calendar argument? (Notice that this " +
-			"option is exclusive of the \"useTimezone=true\" configuration option.)",
-			"5.0.0",
-			MISC_CATEGORY, Integer.MIN_VALUE);
-	private BooleanConnectionProperty useLocalSessionState = new BooleanConnectionProperty(
-			"useLocalSessionState",
-			false,
-			"Should the driver refer to the internal values of autocommit and transaction isolation that are set "
-					+ " by Connection.setAutoCommit() and Connection.setTransactionIsolation(), rather than querying the database?",
-			"3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useOldAliasMetadataBehavior = new BooleanConnectionProperty(
-			"useOldAliasMetadataBehavior",
-			true,
-			"Should the driver use the legacy behavior for \"AS\" clauses on columns and tables, and only "
-		    + "return aliases (if any) for ResultSetMetaData.getColumnName() or ResultSetMetaData.getTableName() "
-		    + "rather than the original column/table name?",
-		    "5.0.4",
-		    MISC_CATEGORY,
-		    Integer.MIN_VALUE);
-	
-	private BooleanConnectionProperty useOldUTF8Behavior = new BooleanConnectionProperty(
-			"useOldUTF8Behavior",
-			false,
-			"Use the UTF-8 behavior the driver did when communicating with 4.0 and older servers",
-			"3.1.6", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private boolean useOldUTF8BehaviorAsBoolean = false;
-
-	private BooleanConnectionProperty useOnlyServerErrorMessages = new BooleanConnectionProperty(
-			"useOnlyServerErrorMessages",
-			true,
-			"Don't prepend 'standard' SQLState error messages to error messages returned by the server.",
-			"3.0.15", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useReadAheadInput = new BooleanConnectionProperty(
-			"useReadAheadInput",
-			true,
-			"Use newer, optimized non-blocking, buffered input stream when reading from the server?",
-			"3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useSqlStateCodes = new BooleanConnectionProperty(
-			"useSqlStateCodes",
-			true,
-			"Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes (true/false), default is 'true'",
-			"3.1.3", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useSSL = new BooleanConnectionProperty(
-			"useSSL",
-			false,
-			"Use SSL when communicating with the server (true/false), defaults to 'false'",
-			"3.0.2", SECURITY_CATEGORY, 2);
-
-	private BooleanConnectionProperty useStreamLengthsInPrepStmts = new BooleanConnectionProperty(
-			"useStreamLengthsInPrepStmts",
-			true,
-			"Honor stream length parameter in "
-					+ "PreparedStatement/ResultSet.setXXXStream() method calls (true/false, defaults to 'true')?",
-			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useTimezone = new BooleanConnectionProperty(
-			"useTimezone",
-			false,
-			"Convert time/date types between client and server timezones (true/false, defaults to 'false')?",
-			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useUltraDevWorkAround = new BooleanConnectionProperty(
-			"ultraDevHack",
-			false,
-			"Create PreparedStatements for prepareCall() when required, because UltraDev "
-					+ " is broken and issues a prepareCall() for _all_ statements? (true/false, defaults to 'false')",
-			"2.0.3", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useUnbufferedInput = new BooleanConnectionProperty(
-			"useUnbufferedInput", true,
-			"Don't use BufferedInputStream for reading data from the server",
-			"3.0.11", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useUnicode = new BooleanConnectionProperty(
-			"useUnicode",
-			true,
-			"Should the driver use Unicode character encodings when handling strings? Should only be used when the driver can't determine the character set mapping, or you are trying to 'force' the driver to use a character set that MySQL either doesn't natively support (such as UTF-8), true/false, defaults to 'true'",
-			"1.1g", MISC_CATEGORY, 0);
-
-	// Cache these values, they are 'hot'
-	private boolean useUnicodeAsBoolean = true;
-
-	private BooleanConnectionProperty useUsageAdvisor = new BooleanConnectionProperty(
-			"useUsageAdvisor",
-			false,
-			"Should the driver issue 'usage' warnings advising proper and efficient usage of JDBC and MySQL Connector/J to the log (true/false, defaults to 'false')?",
-			"3.1.1", DEBUGING_PROFILING_CATEGORY, 10);
-
-	private boolean useUsageAdvisorAsBoolean = false;
-
-	private BooleanConnectionProperty yearIsDateType = new BooleanConnectionProperty(
-			"yearIsDateType",
-			true,
-			"Should the JDBC driver treat the MySQL type \"YEAR\" as a java.sql.Date, or as a SHORT?",
-			"3.1.9", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private StringConnectionProperty zeroDateTimeBehavior = new StringConnectionProperty(
-			"zeroDateTimeBehavior",
-			ZERO_DATETIME_BEHAVIOR_EXCEPTION,
-			new String[] { ZERO_DATETIME_BEHAVIOR_EXCEPTION,
-					ZERO_DATETIME_BEHAVIOR_ROUND,
-					ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL },
-			"What should happen when the driver encounters DATETIME values that are composed "
-					+ "entirely of zeroes (used by MySQL to represent invalid dates)? "
-					+ "Valid values are '"
-					+ ZERO_DATETIME_BEHAVIOR_EXCEPTION
-					+ "', '"
-					+ ZERO_DATETIME_BEHAVIOR_ROUND
-					+ "' and '"
-					+ ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL + "'.", "3.1.4",
-			MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty useJvmCharsetConverters = new BooleanConnectionProperty("useJvmCharsetConverters",
-			true, "Always use the character encoding routines built into the JVM, rather than using "
-			+ "lookup tables for single-byte character sets? (The default of \"true\" for this is appropriate for " 
-			+ "newer JVMs", "5.0.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
-	
-	private BooleanConnectionProperty useGmtMillisForDatetimes = new BooleanConnectionProperty("useGmtMillisForDatetimes", false, "Convert between session timezone and GMT before creating Date and Timestamp instances (value of \"false\" is legacy behavior, \"true\" leads to more JDBC-compliant behavior.", "3.1.12", MISC_CATEGORY, Integer.MIN_VALUE);
-
-	private BooleanConnectionProperty dumpMetadataOnColumnNotFound = new BooleanConnectionProperty("dumpMetadataOnColumnNotFound", false, "Should the driver dump the field-level metadata of a result set into " + "the exception message when ResultSet.findColumn() fails?", "3.1.13", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
-
-	protected DriverPropertyInfo[] exposeAsDriverPropertyInfoInternal(
-			Properties info, int slotsToReserve) throws SQLException {
-		initializeProperties(info);
-
-		int numProperties = PROPERTY_LIST.size();
-
-		int listSize = numProperties + slotsToReserve;
-
-		DriverPropertyInfo[] driverProperties = new DriverPropertyInfo[listSize];
-
-		for (int i = slotsToReserve; i < listSize; i++) {
-			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
-					.get(i - slotsToReserve);
-
-			try {
-				ConnectionProperty propToExpose = (ConnectionProperty) propertyField
-						.get(this);
-
-				if (info != null) {
-					propToExpose.initializeFrom(info);
-				}
-
-				
-				driverProperties[i] = propToExpose.getAsDriverPropertyInfo();
-			} catch (IllegalAccessException iae) {
-				throw SQLError.createSQLException("Internal properties failure",
-						SQLError.SQL_STATE_GENERAL_ERROR);
-			}
-		}
-
-		return driverProperties;
-	}
-
-	protected Properties exposeAsProperties(Properties info)
-			throws SQLException {
-		if (info == null) {
-			info = new Properties();
-		}
-
-		int numPropertiesToSet = PROPERTY_LIST.size();
-
-		for (int i = 0; i < numPropertiesToSet; i++) {
-			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
-					.get(i);
-
-			try {
-				ConnectionProperty propToGet = (ConnectionProperty) propertyField
-						.get(this);
-
-				Object propValue = propToGet.getValueAsObject();
-
-				if (propValue != null) {
-					info.setProperty(propToGet.getPropertyName(), propValue
-							.toString());
-				}
-			} catch (IllegalAccessException iae) {
-				throw SQLError.createSQLException("Internal properties failure",
-						SQLError.SQL_STATE_GENERAL_ERROR);
-			}
-		}
-
-		return info;
-	}
-
-	/**
 	 * Returns a description of the connection properties as an XML document.
 	 * 
 	 * @return the connection properties as an XML document.
 	 * @throws SQLException
 	 *             if an error occurs.
 	 */
-	public String exposeAsXml() throws SQLException {
-		StringBuffer xmlBuf = new StringBuffer();
-		xmlBuf.append("<ConnectionProperties>");
+	public abstract String exposeAsXml() throws SQLException;
 
-		int numPropertiesToSet = PROPERTY_LIST.size();
-
-		int numCategories = PROPERTY_CATEGORIES.length;
-
-		Map propertyListByCategory = new HashMap();
-
-		for (int i = 0; i < numCategories; i++) {
-			propertyListByCategory.put(PROPERTY_CATEGORIES[i], new Map[] {
-					new TreeMap(), new TreeMap() });
-		}
-
-		//
-		// The following properties are not exposed as 'normal' properties, but
-		// they are
-		// settable nonetheless, so we need to have them documented, make sure
-		// that they sort 'first' as #1 and #2 in the category
-		//
-		StringConnectionProperty userProp = new StringConnectionProperty(
-				NonRegisteringDriver.USER_PROPERTY_KEY, null,
-				"The user to connect as", "all", CONNECTION_AND_AUTH_CATEGORY,
-				Integer.MIN_VALUE + 1);
-		StringConnectionProperty passwordProp = new StringConnectionProperty(
-				NonRegisteringDriver.PASSWORD_PROPERTY_KEY, null,
-				"The password to use when connecting", "all",
-				CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE + 2);
-
-		Map[] connectionSortMaps = (Map[]) propertyListByCategory
-				.get(CONNECTION_AND_AUTH_CATEGORY);
-		connectionSortMaps[0].put(new Integer(userProp.getOrder()), userProp);
-		connectionSortMaps[0].put(new Integer(passwordProp.getOrder()),
-				passwordProp);
-
-		try {
-			for (int i = 0; i < numPropertiesToSet; i++) {
-				java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
-						.get(i);
-				ConnectionProperty propToGet = (ConnectionProperty) propertyField
-						.get(this);
-				Map[] sortMaps = (Map[]) propertyListByCategory.get(propToGet
-						.getCategoryName());
-				int orderInCategory = propToGet.getOrder();
-
-				if (orderInCategory == Integer.MIN_VALUE) {
-					sortMaps[1].put(propToGet.getPropertyName(), propToGet);
-				} else {
-					sortMaps[0].put(new Integer(orderInCategory), propToGet);
-				}
-			}
-
-			for (int j = 0; j < numCategories; j++) {
-				Map[] sortMaps = (Map[]) propertyListByCategory
-						.get(PROPERTY_CATEGORIES[j]);
-				Iterator orderedIter = sortMaps[0].values().iterator();
-				Iterator alphaIter = sortMaps[1].values().iterator();
-
-				xmlBuf.append("\n <PropertyCategory name=\"");
-				xmlBuf.append(PROPERTY_CATEGORIES[j]);
-				xmlBuf.append("\">");
-
-				while (orderedIter.hasNext()) {
-					ConnectionProperty propToGet = (ConnectionProperty) orderedIter
-							.next();
-					
-					xmlBuf.append("\n  <Property name=\"");
-					xmlBuf.append(propToGet.getPropertyName());
-					xmlBuf.append("\" required=\"");
-					xmlBuf.append(propToGet.required ? "Yes" : "No");
-
-					xmlBuf.append("\" default=\"");
-
-					if (propToGet.getDefaultValue() != null) {
-						xmlBuf.append(propToGet.getDefaultValue());
-					}
-
-					xmlBuf.append("\" sortOrder=\"");
-					xmlBuf.append(propToGet.getOrder());
-					xmlBuf.append("\" since=\"");
-					xmlBuf.append(propToGet.sinceVersion);
-					xmlBuf.append("\">\n");
-					xmlBuf.append("    ");
-					xmlBuf.append(propToGet.description);
-					xmlBuf.append("\n  </Property>");
-				}
-
-				while (alphaIter.hasNext()) {
-					ConnectionProperty propToGet = (ConnectionProperty) alphaIter
-							.next();
-					
-					xmlBuf.append("\n  <Property name=\"");
-					xmlBuf.append(propToGet.getPropertyName());
-					xmlBuf.append("\" required=\"");
-					xmlBuf.append(propToGet.required ? "Yes" : "No");
-
-					xmlBuf.append("\" default=\"");
-
-					if (propToGet.getDefaultValue() != null) {
-						xmlBuf.append(propToGet.getDefaultValue());
-					}
-
-					xmlBuf.append("\" sortOrder=\"alpha\" since=\"");
-					xmlBuf.append(propToGet.sinceVersion);
-					xmlBuf.append("\">\n");
-					xmlBuf.append("    ");
-					xmlBuf.append(propToGet.description);
-					xmlBuf.append("\n  </Property>");
-				}
-
-				xmlBuf.append("\n </PropertyCategory>");
-			}
-		} catch (IllegalAccessException iae) {
-			throw SQLError.createSQLException("Internal properties failure",
-					SQLError.SQL_STATE_GENERAL_ERROR);
-		}
-
-		xmlBuf.append("\n</ConnectionProperties>");
-
-		return xmlBuf.toString();
-	}
-
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getAllowLoadLocalInfile() {
-		return this.allowLoadLocalInfile.getValueAsBoolean();
-	}
+	public abstract boolean getAllowLoadLocalInfile();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getAllowMultiQueries() {
-		return this.allowMultiQueries.getValueAsBoolean();
-	}
+	public abstract boolean getAllowMultiQueries();
 
 	/**
 	 * @return Returns the allowNanAndInf.
 	 */
-	public boolean getAllowNanAndInf() {
-		return allowNanAndInf.getValueAsBoolean();
-	}
+	public abstract boolean getAllowNanAndInf();
 
 	/**
 	 * @return Returns the allowUrlInLocalInfile.
 	 */
-	public boolean getAllowUrlInLocalInfile() {
-		return this.allowUrlInLocalInfile.getValueAsBoolean();
-	}
+	public abstract boolean getAllowUrlInLocalInfile();
 
 	/**
 	 * @return Returns the alwaysSendSetIsolation.
 	 */
-	public boolean getAlwaysSendSetIsolation() {
-		return this.alwaysSendSetIsolation.getValueAsBoolean();
-	}
+	public abstract boolean getAlwaysSendSetIsolation();
 
 	/**
 	 * @return Returns the autoDeserialize.
 	 */
-	public boolean getAutoDeserialize() {
-		return autoDeserialize.getValueAsBoolean();
-	}
+	public abstract boolean getAutoDeserialize();
 
-	public boolean getAutoGenerateTestcaseScript() {
-		return this.autoGenerateTestcaseScriptAsBoolean;
-	}
+	public abstract boolean getAutoGenerateTestcaseScript();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getAutoReconnectForPools() {
-		return this.autoReconnectForPoolsAsBoolean;
-	}
+	public abstract boolean getAutoReconnectForPools();
 
 	/**
 	 * @return Returns the blobSendChunkSize.
 	 */
-	public int getBlobSendChunkSize() {
-		return blobSendChunkSize.getValueAsInt();
-	}
+	public abstract int getBlobSendChunkSize();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns if cacheCallableStatements is enabled
 	 */
-	public boolean getCacheCallableStatements() {
-		return this.cacheCallableStatements.getValueAsBoolean();
-	}
+	public abstract boolean getCacheCallableStatements();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the cachePreparedStatements.
 	 */
-	public boolean getCachePreparedStatements() {
-		return ((Boolean) this.cachePreparedStatements.getValueAsObject())
-				.booleanValue();
-	}
+	public abstract boolean getCachePreparedStatements();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return DOCUMENT ME!
 	 */
-	public boolean getCacheResultSetMetadata() {
-		return this.cacheResultSetMetaDataAsBoolean;
-	}
+	public abstract boolean getCacheResultSetMetadata();
 
 	/**
 	 * @return Returns the cacheServerConfiguration.
 	 */
-	public boolean getCacheServerConfiguration() {
-		return cacheServerConfiguration.getValueAsBoolean();
-	}
+	public abstract boolean getCacheServerConfiguration();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the callableStatementCacheSize.
 	 */
-	public int getCallableStatementCacheSize() {
-		return this.callableStatementCacheSize.getValueAsInt();
-	}
+	public abstract int getCallableStatementCacheSize();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getCapitalizeTypeNames() {
-		return this.capitalizeTypeNames.getValueAsBoolean();
-	}
+	public abstract boolean getCapitalizeTypeNames();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the characterSetResults.
 	 */
-	public String getCharacterSetResults() {
-		return this.characterSetResults.getValueAsString();
-	}
+	public abstract String getCharacterSetResults();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the clobberStreamingResults.
 	 */
-	public boolean getClobberStreamingResults() {
-		return this.clobberStreamingResults.getValueAsBoolean();
-	}
+	public abstract boolean getClobberStreamingResults();
 
-	public String getClobCharacterEncoding() {
-		return this.clobCharacterEncoding.getValueAsString();
-	}
+	public abstract String getClobCharacterEncoding();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the connectionCollation.
 	 */
-	public String getConnectionCollation() {
-		return this.connectionCollation.getValueAsString();
-	}
+	public abstract String getConnectionCollation();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getConnectTimeout() {
-		return this.connectTimeout.getValueAsInt();
-	}
+	public abstract int getConnectTimeout();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getContinueBatchOnError() {
-		return this.continueBatchOnError.getValueAsBoolean();
-	}
+	public abstract boolean getContinueBatchOnError();
 
-	public boolean getCreateDatabaseIfNotExist() {
-		return this.createDatabaseIfNotExist.getValueAsBoolean();
-	}
+	public abstract boolean getCreateDatabaseIfNotExist();
 
-	public int getDefaultFetchSize() {
-		return this.defaultFetchSize.getValueAsInt();
-	}
+	public abstract int getDefaultFetchSize();
 
 	/**
 	 * @return Returns the dontTrackOpenResources.
 	 */
-	public boolean getDontTrackOpenResources() {
-		return this.dontTrackOpenResources.getValueAsBoolean();
-	}
+	public abstract boolean getDontTrackOpenResources();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the dumpQueriesOnException.
 	 */
-	public boolean getDumpQueriesOnException() {
-		return this.dumpQueriesOnException.getValueAsBoolean();
-	}
+	public abstract boolean getDumpQueriesOnException();
 
 	/**
 	 * @return Returns the dynamicCalendars.
 	 */
-	public boolean getDynamicCalendars() {
-		return this.dynamicCalendars.getValueAsBoolean();
-	}
+	public abstract boolean getDynamicCalendars();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the elideSetAutoCommits.
 	 */
-	public boolean getElideSetAutoCommits() {
-		return this.elideSetAutoCommits.getValueAsBoolean();
-	}
+	public abstract boolean getElideSetAutoCommits();
 
-	public boolean getEmptyStringsConvertToZero() {
-		return this.emptyStringsConvertToZero.getValueAsBoolean();
-	}
+	public abstract boolean getEmptyStringsConvertToZero();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getEmulateLocators() {
-		return this.emulateLocators.getValueAsBoolean();
-	}
+	public abstract boolean getEmulateLocators();
 
 	/**
 	 * @return Returns the emulateUnsupportedPstmts.
 	 */
-	public boolean getEmulateUnsupportedPstmts() {
-		return this.emulateUnsupportedPstmts.getValueAsBoolean();
-	}
+	public abstract boolean getEmulateUnsupportedPstmts();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the enablePacketDebug.
 	 */
-	public boolean getEnablePacketDebug() {
-		return this.enablePacketDebug.getValueAsBoolean();
-	}
+	public abstract boolean getEnablePacketDebug();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public String getEncoding() {
-		return this.characterEncodingAsString;
-	}
+	public abstract String getEncoding();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the explainSlowQueries.
 	 */
-	public boolean getExplainSlowQueries() {
-		return this.explainSlowQueries.getValueAsBoolean();
-	}
+	public abstract boolean getExplainSlowQueries();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the failOverReadOnly.
 	 */
-	public boolean getFailOverReadOnly() {
-		return this.failOverReadOnly.getValueAsBoolean();
-	}
+	public abstract boolean getFailOverReadOnly();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the gatherPerformanceMetrics.
 	 */
-	public boolean getGatherPerformanceMetrics() {
-		return this.gatherPerformanceMetrics.getValueAsBoolean();
-	}
+	public abstract boolean getGatherPerformanceMetrics();
 
 	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return
-	 */
-	protected boolean getHighAvailability() {
-		return this.highAvailabilityAsBoolean;
-	}
-
-	/**
 	 * @return Returns the holdResultsOpenOverStatementClose.
 	 */
-	public boolean getHoldResultsOpenOverStatementClose() {
-		return holdResultsOpenOverStatementClose.getValueAsBoolean();
-	}
+	public abstract boolean getHoldResultsOpenOverStatementClose();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getIgnoreNonTxTables() {
-		return this.ignoreNonTxTables.getValueAsBoolean();
-	}
+	public abstract boolean getIgnoreNonTxTables();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getInitialTimeout() {
-		return this.initialTimeout.getValueAsInt();
-	}
+	public abstract int getInitialTimeout();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getInteractiveClient() {
-		return this.isInteractiveClient.getValueAsBoolean();
-	}
+	public abstract boolean getInteractiveClient();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the isInteractiveClient.
 	 */
-	public boolean getIsInteractiveClient() {
-		return this.isInteractiveClient.getValueAsBoolean();
-	}
+	public abstract boolean getIsInteractiveClient();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the jdbcCompliantTruncation.
 	 */
-	public boolean getJdbcCompliantTruncation() {
-		return this.jdbcCompliantTruncation.getValueAsBoolean();
-	}
+	public abstract boolean getJdbcCompliantTruncation();
 
 	/**
 	 * @return Returns the dontTrackOpenResources.
 	 */
-	public int getLocatorFetchBufferSize() {
-		return this.locatorFetchBufferSize.getValueAsInt();
-	}
+	public abstract int getLocatorFetchBufferSize();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public String getLogger() {
-		return this.loggerClassName.getValueAsString();
-	}
+	public abstract String getLogger();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the loggerClassName.
 	 */
-	public String getLoggerClassName() {
-		return this.loggerClassName.getValueAsString();
-	}
+	public abstract String getLoggerClassName();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the logSlowQueries.
 	 */
-	public boolean getLogSlowQueries() {
-		return this.logSlowQueries.getValueAsBoolean();
-	}
+	public abstract boolean getLogSlowQueries();
 
-	public boolean getMaintainTimeStats() {
-		return maintainTimeStatsAsBoolean;
-	}
+	public abstract boolean getMaintainTimeStats();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the maxQuerySizeToLog.
 	 */
-	public int getMaxQuerySizeToLog() {
-		return this.maxQuerySizeToLog.getValueAsInt();
-	}
+	public abstract int getMaxQuerySizeToLog();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getMaxReconnects() {
-		return this.maxReconnects.getValueAsInt();
-	}
+	public abstract int getMaxReconnects();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getMaxRows() {
-		return this.maxRowsAsInt;
-	}
+	public abstract int getMaxRows();
 
 	/**
 	 * Returns the number of queries that metadata can be cached if caching is
@@ -2026,631 +333,341 @@
 	 * 
 	 * @return the number of queries to cache metadata for.
 	 */
-	public int getMetadataCacheSize() {
-		return this.metadataCacheSize.getValueAsInt();
-	}
+	public abstract int getMetadataCacheSize();
 
 	/**
 	 * @return Returns the noDatetimeStringSync.
 	 */
-	public boolean getNoDatetimeStringSync() {
-		return this.noDatetimeStringSync.getValueAsBoolean();
-	}
+	public abstract boolean getNoDatetimeStringSync();
 
-	public boolean getNullCatalogMeansCurrent() {
-		return this.nullCatalogMeansCurrent.getValueAsBoolean();
-	}
+	public abstract boolean getNullCatalogMeansCurrent();
 
-	public boolean getNullNamePatternMatchesAll() {
-		return this.nullNamePatternMatchesAll.getValueAsBoolean();
-	}
+	public abstract boolean getNullNamePatternMatchesAll();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the packetDebugBufferSize.
 	 */
-	public int getPacketDebugBufferSize() {
-		return this.packetDebugBufferSize.getValueAsInt();
-	}
+	public abstract int getPacketDebugBufferSize();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getParanoid() {
-		return this.paranoid.getValueAsBoolean();
-	}
+	public abstract boolean getParanoid();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getPedantic() {
-		return this.pedantic.getValueAsBoolean();
-	}
+	public abstract boolean getPedantic();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the preparedStatementCacheSize.
 	 */
-	public int getPreparedStatementCacheSize() {
-		return ((Integer) this.preparedStatementCacheSize.getValueAsObject())
-				.intValue();
-	}
+	public abstract int getPreparedStatementCacheSize();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the preparedStatementCacheSqlLimit.
 	 */
-	public int getPreparedStatementCacheSqlLimit() {
-		return ((Integer) this.preparedStatementCacheSqlLimit
-				.getValueAsObject()).intValue();
-	}
+	public abstract int getPreparedStatementCacheSqlLimit();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getProfileSql() {
-		return this.profileSQLAsBoolean;
-	}
+	public abstract boolean getProfileSql();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the profileSQL flag
 	 */
-	public boolean getProfileSQL() {
-		return this.profileSQL.getValueAsBoolean();
-	}
+	public abstract boolean getProfileSQL();
 
 	/**
 	 * @return Returns the propertiesTransform.
 	 */
-	public String getPropertiesTransform() {
-		return this.propertiesTransform.getValueAsString();
-	}
+	public abstract String getPropertiesTransform();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getQueriesBeforeRetryMaster() {
-		return this.queriesBeforeRetryMaster.getValueAsInt();
-	}
+	public abstract int getQueriesBeforeRetryMaster();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getReconnectAtTxEnd() {
-		return this.reconnectTxAtEndAsBoolean;
-	}
+	public abstract boolean getReconnectAtTxEnd();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getRelaxAutoCommit() {
-		return this.relaxAutoCommit.getValueAsBoolean();
-	}
+	public abstract boolean getRelaxAutoCommit();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the reportMetricsIntervalMillis.
 	 */
-	public int getReportMetricsIntervalMillis() {
-		return this.reportMetricsIntervalMillis.getValueAsInt();
-	}
+	public abstract int getReportMetricsIntervalMillis();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getRequireSSL() {
-		return this.requireSSL.getValueAsBoolean();
-	}
+	public abstract boolean getRequireSSL();
 
-	protected boolean getRetainStatementAfterResultSetClose() {
-		return this.retainStatementAfterResultSetClose.getValueAsBoolean();
-	}
-
 	/**
 	 * @return Returns the rollbackOnPooledClose.
 	 */
-	public boolean getRollbackOnPooledClose() {
-		return this.rollbackOnPooledClose.getValueAsBoolean();
-	}
+	public abstract boolean getRollbackOnPooledClose();
 
 	/**
 	 * Returns whether or not hosts will be picked in a round-robin fashion.
 	 * 
 	 * @return Returns the roundRobinLoadBalance property.
 	 */
-	public boolean getRoundRobinLoadBalance() {
-		return this.roundRobinLoadBalance.getValueAsBoolean();
-	}
+	public abstract boolean getRoundRobinLoadBalance();
 
 	/**
 	 * @return Returns the runningCTS13.
 	 */
-	public boolean getRunningCTS13() {
-		return this.runningCTS13.getValueAsBoolean();
-	}
+	public abstract boolean getRunningCTS13();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getSecondsBeforeRetryMaster() {
-		return this.secondsBeforeRetryMaster.getValueAsInt();
-	}
+	public abstract int getSecondsBeforeRetryMaster();
 
 	/**
 	 * Returns the 'serverTimezone' property.
 	 * 
 	 * @return the configured server timezone property.
 	 */
-	public String getServerTimezone() {
-		return this.serverTimezone.getValueAsString();
-	}
+	public abstract String getServerTimezone();
 
 	/**
 	 * @return Returns the sessionVariables.
 	 */
-	public String getSessionVariables() {
-		return sessionVariables.getValueAsString();
-	}
+	public abstract String getSessionVariables();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the slowQueryThresholdMillis.
 	 */
-	public int getSlowQueryThresholdMillis() {
-		return this.slowQueryThresholdMillis.getValueAsInt();
-	}
+	public abstract int getSlowQueryThresholdMillis();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public String getSocketFactoryClassName() {
-		return this.socketFactoryClassName.getValueAsString();
-	}
+	public abstract String getSocketFactoryClassName();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public int getSocketTimeout() {
-		return this.socketTimeout.getValueAsInt();
-	}
+	public abstract int getSocketTimeout();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getStrictFloatingPoint() {
-		return this.strictFloatingPoint.getValueAsBoolean();
-	}
+	public abstract boolean getStrictFloatingPoint();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getStrictUpdates() {
-		return this.strictUpdates.getValueAsBoolean();
-	}
+	public abstract boolean getStrictUpdates();
 
 	/**
 	 * @return Returns the tinyInt1isBit.
 	 */
-	public boolean getTinyInt1isBit() {
-		return this.tinyInt1isBit.getValueAsBoolean();
-	}
+	public abstract boolean getTinyInt1isBit();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the logProtocol.
 	 */
-	public boolean getTraceProtocol() {
-		return this.traceProtocol.getValueAsBoolean();
-	}
+	public abstract boolean getTraceProtocol();
 
-	public boolean getTransformedBitIsBoolean() {
-		return this.transformedBitIsBoolean.getValueAsBoolean();
-	}
+	public abstract boolean getTransformedBitIsBoolean();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseCompression() {
-		return this.useCompression.getValueAsBoolean();
-	}
+	public abstract boolean getUseCompression();
 
 	/**
 	 * @return Returns the useFastIntParsing.
 	 */
-	public boolean getUseFastIntParsing() {
-		return this.useFastIntParsing.getValueAsBoolean();
-	}
+	public abstract boolean getUseFastIntParsing();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseHostsInPrivileges() {
-		return this.useHostsInPrivileges.getValueAsBoolean();
-	}
+	public abstract boolean getUseHostsInPrivileges();
 
-	public boolean getUseInformationSchema() {
-		return this.useInformationSchema.getValueAsBoolean();
-	}
+	public abstract boolean getUseInformationSchema();
 
 	/**
 	 * @return Returns the useLocalSessionState.
 	 */
-	public boolean getUseLocalSessionState() {
-		return this.useLocalSessionState.getValueAsBoolean();
-	}
+	public abstract boolean getUseLocalSessionState();
 
 	/**
 	 * @return Returns the useOldUTF8Behavior.
 	 */
-	public boolean getUseOldUTF8Behavior() {
-		return this.useOldUTF8BehaviorAsBoolean;
-	}
+	public abstract boolean getUseOldUTF8Behavior();
 
 	/**
 	 * @return Returns the useOnlyServerErrorMessages.
 	 */
-	public boolean getUseOnlyServerErrorMessages() {
-		return this.useOnlyServerErrorMessages.getValueAsBoolean();
-	}
+	public abstract boolean getUseOnlyServerErrorMessages();
 
 	/**
 	 * @return Returns the useReadAheadInput.
 	 */
-	public boolean getUseReadAheadInput() {
-		return this.useReadAheadInput.getValueAsBoolean();
-	}
+	public abstract boolean getUseReadAheadInput();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseServerPreparedStmts() {
-		return this.detectServerPreparedStmts.getValueAsBoolean();
-	}
+	public abstract boolean getUseServerPreparedStmts();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the useSqlStateCodes state.
 	 */
-	public boolean getUseSqlStateCodes() {
-		return this.useSqlStateCodes.getValueAsBoolean();
-	}
+	public abstract boolean getUseSqlStateCodes();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseSSL() {
-		return this.useSSL.getValueAsBoolean();
-	}
+	public abstract boolean getUseSSL();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseStreamLengthsInPrepStmts() {
-		return this.useStreamLengthsInPrepStmts.getValueAsBoolean();
-	}
+	public abstract boolean getUseStreamLengthsInPrepStmts();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseTimezone() {
-		return this.useTimezone.getValueAsBoolean();
-	}
+	public abstract boolean getUseTimezone();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseUltraDevWorkAround() {
-		return this.useUltraDevWorkAround.getValueAsBoolean();
-	}
+	public abstract boolean getUseUltraDevWorkAround();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the useUnbufferedInput.
 	 */
-	public boolean getUseUnbufferedInput() {
-		return this.useUnbufferedInput.getValueAsBoolean();
-	}
+	public abstract boolean getUseUnbufferedInput();
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return
 	 */
-	public boolean getUseUnicode() {
-		return this.useUnicodeAsBoolean;
-	}
+	public abstract boolean getUseUnicode();
 
 	/**
 	 * Returns whether or not the driver advises of proper usage.
 	 * 
 	 * @return the value of useUsageAdvisor
 	 */
-	public boolean getUseUsageAdvisor() {
-		return this.useUsageAdvisorAsBoolean;
-	}
+	public abstract boolean getUseUsageAdvisor();
 
-	public boolean getYearIsDateType() {
-		return this.yearIsDateType.getValueAsBoolean();
-	}
+	public abstract boolean getYearIsDateType();
 
 	/**
 	 * @return Returns the zeroDateTimeBehavior.
 	 */
-	public String getZeroDateTimeBehavior() {
-		return this.zeroDateTimeBehavior.getValueAsString();
-	}
+	public abstract String getZeroDateTimeBehavior();
 
 	/**
-	 * Initializes driver properties that come from a JNDI reference (in the
-	 * case of a javax.sql.DataSource bound into some name service that doesn't
-	 * handle Java objects directly).
-	 * 
-	 * @param ref
-	 *            The JNDI Reference that holds RefAddrs for all properties
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	protected void initializeFromRef(Reference ref) throws SQLException {
-		int numPropertiesToSet = PROPERTY_LIST.size();
-
-		for (int i = 0; i < numPropertiesToSet; i++) {
-			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
-					.get(i);
-
-			try {
-				ConnectionProperty propToSet = (ConnectionProperty) propertyField
-						.get(this);
-
-				if (ref != null) {
-					propToSet.initializeFrom(ref);
-				}
-			} catch (IllegalAccessException iae) {
-				throw SQLError.createSQLException("Internal properties failure",
-						SQLError.SQL_STATE_GENERAL_ERROR);
-			}
-		}
-
-		postInitialization();
-	}
-
-	/**
-	 * Initializes driver properties that come from URL or properties passed to
-	 * the driver manager.
-	 * 
-	 * @param info
-	 *            DOCUMENT ME!
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	protected void initializeProperties(Properties info) throws SQLException {
-		if (info != null) {
-			// For backwards-compatibility
-			String profileSqlLc = info.getProperty("profileSql");
-
-			if (profileSqlLc != null) {
-				info.put("profileSQL", profileSqlLc);
-			}
-
-			Properties infoCopy = (Properties) info.clone();
-
-			infoCopy.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
-			infoCopy.remove(NonRegisteringDriver.USER_PROPERTY_KEY);
-			infoCopy.remove(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
-			infoCopy.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
-			infoCopy.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);
-			infoCopy.remove("profileSql");
-
-			int numPropertiesToSet = PROPERTY_LIST.size();
-
-			for (int i = 0; i < numPropertiesToSet; i++) {
-				java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
-						.get(i);
-
-				try {
-					ConnectionProperty propToSet = (ConnectionProperty) propertyField
-							.get(this);
-
-					propToSet.initializeFrom(infoCopy);
-				} catch (IllegalAccessException iae) {
-					throw SQLError.createSQLException(
-							"Unable to initialize driver properties due to "
-									+ iae.toString(),
-							SQLError.SQL_STATE_GENERAL_ERROR);
-				}
-			}
-
-			// TODO -- Not yet
-			/*
-			 * int numUnknownProperties = infoCopy.size(); if
-			 * (numUnknownProperties > 0) { StringBuffer errorMessageBuf = new
-			 * StringBuffer( "Unknown connection ");
-			 * errorMessageBuf.append((numUnknownProperties == 1) ? "property " :
-			 * "properties "); Iterator propNamesItor =
-			 * infoCopy.keySet().iterator(); errorMessageBuf.append("'");
-			 * errorMessageBuf.append(propNamesItor.next().toString());
-			 * errorMessageBuf.append("'"); while (propNamesItor.hasNext()) {
-			 * errorMessageBuf.append(", '");
-			 * errorMessageBuf.append(propNamesItor.next().toString());
-			 * errorMessageBuf.append("'"); } throw new
-			 * SQLException(errorMessageBuf.toString(),
-			 * SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE); }
-			 */
-			postInitialization();
-		}
-	}
-
-	protected void postInitialization() throws SQLException {
-
-		// Support 'old' profileSql capitalization
-		if (this.profileSql.getValueAsObject() != null) {
-			this.profileSQL.initializeFrom(this.profileSql.getValueAsObject()
-					.toString());
-		}
-
-		this.reconnectTxAtEndAsBoolean = ((Boolean) this.reconnectAtTxEnd
-				.getValueAsObject()).booleanValue();
-
-		// Adjust max rows
-		if (this.getMaxRows() == 0) {
-			// adjust so that it will become MysqlDefs.MAX_ROWS
-			// in execSQL()
-			this.maxRows.setValueAsObject(new Integer(-1));
-		}
-
-		//
-		// Check character encoding
-		//
-		String testEncoding = this.getEncoding();
-
-		if (testEncoding != null) {
-			// Attempt to use the encoding, and bail out if it
-			// can't be used
-			try {
-				String testString = "abc";
-				testString.getBytes(testEncoding);
-			} catch (UnsupportedEncodingException UE) {
-				throw SQLError.createSQLException("Unsupported character " + "encoding '"
-						+ testEncoding + "'.", "0S100");
-			}
-		}
-
-		// Metadata caching is only supported on JDK-1.4 and newer
-		// because it relies on LinkedHashMap being present.
-		// Check (and disable) if not supported
-		if (((Boolean) this.cacheResultSetMetadata.getValueAsObject())
-				.booleanValue()) {
-			try {
-				Class.forName("java.util.LinkedHashMap");
-			} catch (ClassNotFoundException cnfe) {
-				this.cacheResultSetMetadata.setValue(false);
-			}
-		}
-
-		this.cacheResultSetMetaDataAsBoolean = this.cacheResultSetMetadata
-				.getValueAsBoolean();
-		this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean();
-		this.characterEncodingAsString = ((String) this.characterEncoding
-				.getValueAsObject());
-		this.highAvailabilityAsBoolean = this.autoReconnect.getValueAsBoolean();
-		this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools
-				.getValueAsBoolean();
-		this.maxRowsAsInt = ((Integer) this.maxRows.getValueAsObject())
-				.intValue();
-		this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean();
-		this.useUsageAdvisorAsBoolean = this.useUsageAdvisor
-				.getValueAsBoolean();
-		this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior
-				.getValueAsBoolean();
-		this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript
-				.getValueAsBoolean();
-		this.maintainTimeStatsAsBoolean = this.maintainTimeStats
-				.getValueAsBoolean();
-		this.jdbcCompliantTruncationForReads = getJdbcCompliantTruncation();
-	}
-
-	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setAllowLoadLocalInfile(boolean property) {
-		this.allowLoadLocalInfile.setValue(property);
-	}
+	public abstract void setAllowLoadLocalInfile(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setAllowMultiQueries(boolean property) {
-		this.allowMultiQueries.setValue(property);
-	}
+	public abstract void setAllowMultiQueries(boolean property);
 
 	/**
 	 * @param allowNanAndInf
 	 *            The allowNanAndInf to set.
 	 */
-	public void setAllowNanAndInf(boolean flag) {
-		this.allowNanAndInf.setValue(flag);
-	}
+	public abstract void setAllowNanAndInf(boolean flag);
 
 	/**
 	 * @param allowUrlInLocalInfile
 	 *            The allowUrlInLocalInfile to set.
 	 */
-	public void setAllowUrlInLocalInfile(boolean flag) {
-		this.allowUrlInLocalInfile.setValue(flag);
-	}
+	public abstract void setAllowUrlInLocalInfile(boolean flag);
 
 	/**
 	 * @param alwaysSendSetIsolation
 	 *            The alwaysSendSetIsolation to set.
 	 */
-	public void setAlwaysSendSetIsolation(boolean flag) {
-		this.alwaysSendSetIsolation.setValue(flag);
-	}
+	public abstract void setAlwaysSendSetIsolation(boolean flag);
 
 	/**
 	 * @param autoDeserialize
 	 *            The autoDeserialize to set.
 	 */
-	public void setAutoDeserialize(boolean flag) {
-		this.autoDeserialize.setValue(flag);
-	}
+	public abstract void setAutoDeserialize(boolean flag);
 
-	public void setAutoGenerateTestcaseScript(boolean flag) {
-		this.autoGenerateTestcaseScript.setValue(flag);
-		this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript
-				.getValueAsBoolean();
-	}
+	public abstract void setAutoGenerateTestcaseScript(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2658,20 +675,14 @@
 	 * @param flag
 	 *            The autoReconnect to set.
 	 */
-	public void setAutoReconnect(boolean flag) {
-		this.autoReconnect.setValue(flag);
-	}
+	public abstract void setAutoReconnect(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setAutoReconnectForConnectionPools(boolean property) {
-		this.autoReconnectForPools.setValue(property);
-		this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools
-				.getValueAsBoolean();
-	}
+	public abstract void setAutoReconnectForConnectionPools(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -2679,17 +690,13 @@
 	 * @param flag
 	 *            The autoReconnectForPools to set.
 	 */
-	public void setAutoReconnectForPools(boolean flag) {
-		this.autoReconnectForPools.setValue(flag);
-	}
+	public abstract void setAutoReconnectForPools(boolean flag);
 
 	/**
 	 * @param blobSendChunkSize
 	 *            The blobSendChunkSize to set.
 	 */
-	public void setBlobSendChunkSize(String value) throws SQLException {
-		this.blobSendChunkSize.setValue(value);
-	}
+	public abstract void setBlobSendChunkSize(String value) throws SQLException;
 
 	/**
 	 * DOCUMENT ME!
@@ -2697,9 +704,7 @@
 	 * @param flag
 	 *            The cacheCallableStatements to set.
 	 */
-	public void setCacheCallableStatements(boolean flag) {
-		this.cacheCallableStatements.setValue(flag);
-	}
+	public abstract void setCacheCallableStatements(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2707,28 +712,20 @@
 	 * @param flag
 	 *            The cachePreparedStatements to set.
 	 */
-	public void setCachePreparedStatements(boolean flag) {
-		this.cachePreparedStatements.setValue(flag);
-	}
+	public abstract void setCachePreparedStatements(boolean flag);
 
 	/**
 	 * Sets whether or not we should cache result set metadata.
 	 * 
 	 * @param property
 	 */
-	public void setCacheResultSetMetadata(boolean property) {
-		this.cacheResultSetMetadata.setValue(property);
-		this.cacheResultSetMetaDataAsBoolean = this.cacheResultSetMetadata
-				.getValueAsBoolean();
-	}
+	public abstract void setCacheResultSetMetadata(boolean property);
 
 	/**
 	 * @param cacheServerConfiguration
 	 *            The cacheServerConfiguration to set.
 	 */
-	public void setCacheServerConfiguration(boolean flag) {
-		this.cacheServerConfiguration.setValue(flag);
-	}
+	public abstract void setCacheServerConfiguration(boolean flag);
 
 	/**
 	 * Configures the number of callable statements to cache. (this is
@@ -2737,18 +734,14 @@
 	 * @param size
 	 *            The callableStatementCacheSize to set.
 	 */
-	public void setCallableStatementCacheSize(int size) {
-		this.callableStatementCacheSize.setValue(size);
-	}
+	public abstract void setCallableStatementCacheSize(int size);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setCapitalizeDBMDTypes(boolean property) {
-		this.capitalizeTypeNames.setValue(property);
-	}
+	public abstract void setCapitalizeDBMDTypes(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -2756,9 +749,7 @@
 	 * @param flag
 	 *            The capitalizeTypeNames to set.
 	 */
-	public void setCapitalizeTypeNames(boolean flag) {
-		this.capitalizeTypeNames.setValue(flag);
-	}
+	public abstract void setCapitalizeTypeNames(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2766,9 +757,7 @@
 	 * @param encoding
 	 *            The characterEncoding to set.
 	 */
-	public void setCharacterEncoding(String encoding) {
-		this.characterEncoding.setValue(encoding);
-	}
+	public abstract void setCharacterEncoding(String encoding);
 
 	/**
 	 * DOCUMENT ME!
@@ -2776,9 +765,7 @@
 	 * @param characterSet
 	 *            The characterSetResults to set.
 	 */
-	public void setCharacterSetResults(String characterSet) {
-		this.characterSetResults.setValue(characterSet);
-	}
+	public abstract void setCharacterSetResults(String characterSet);
 
 	/**
 	 * DOCUMENT ME!
@@ -2786,13 +773,9 @@
 	 * @param flag
 	 *            The clobberStreamingResults to set.
 	 */
-	public void setClobberStreamingResults(boolean flag) {
-		this.clobberStreamingResults.setValue(flag);
-	}
+	public abstract void setClobberStreamingResults(boolean flag);
 
-	public void setClobCharacterEncoding(String encoding) {
-		this.clobCharacterEncoding.setValue(encoding);
-	}
+	public abstract void setClobCharacterEncoding(String encoding);
 
 	/**
 	 * DOCUMENT ME!
@@ -2800,52 +783,38 @@
 	 * @param collation
 	 *            The connectionCollation to set.
 	 */
-	public void setConnectionCollation(String collation) {
-		this.connectionCollation.setValue(collation);
-	}
+	public abstract void setConnectionCollation(String collation);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param timeoutMs
 	 */
-	public void setConnectTimeout(int timeoutMs) {
-		this.connectTimeout.setValue(timeoutMs);
-	}
+	public abstract void setConnectTimeout(int timeoutMs);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setContinueBatchOnError(boolean property) {
-		this.continueBatchOnError.setValue(property);
-	}
+	public abstract void setContinueBatchOnError(boolean property);
 
-	public void setCreateDatabaseIfNotExist(boolean flag) {
-		this.createDatabaseIfNotExist.setValue(flag);
-	}
+	public abstract void setCreateDatabaseIfNotExist(boolean flag);
 
-	public void setDefaultFetchSize(int n) {
-		this.defaultFetchSize.setValue(n);
-	}
+	public abstract void setDefaultFetchSize(int n);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setDetectServerPreparedStmts(boolean property) {
-		this.detectServerPreparedStmts.setValue(property);
-	}
+	public abstract void setDetectServerPreparedStmts(boolean property);
 
 	/**
 	 * @param dontTrackOpenResources
 	 *            The dontTrackOpenResources to set.
 	 */
-	public void setDontTrackOpenResources(boolean flag) {
-		this.dontTrackOpenResources.setValue(flag);
-	}
+	public abstract void setDontTrackOpenResources(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2853,17 +822,13 @@
 	 * @param flag
 	 *            The dumpQueriesOnException to set.
 	 */
-	public void setDumpQueriesOnException(boolean flag) {
-		this.dumpQueriesOnException.setValue(flag);
-	}
+	public abstract void setDumpQueriesOnException(boolean flag);
 
 	/**
 	 * @param dynamicCalendars
 	 *            The dynamicCalendars to set.
 	 */
-	public void setDynamicCalendars(boolean flag) {
-		this.dynamicCalendars.setValue(flag);
-	}
+	public abstract void setDynamicCalendars(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2871,30 +836,22 @@
 	 * @param flag
 	 *            The elideSetAutoCommits to set.
 	 */
-	public void setElideSetAutoCommits(boolean flag) {
-		this.elideSetAutoCommits.setValue(flag);
-	}
+	public abstract void setElideSetAutoCommits(boolean flag);
 
-	public void setEmptyStringsConvertToZero(boolean flag) {
-		this.emptyStringsConvertToZero.setValue(flag);
-	}
+	public abstract void setEmptyStringsConvertToZero(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setEmulateLocators(boolean property) {
-		this.emulateLocators.setValue(property);
-	}
+	public abstract void setEmulateLocators(boolean property);
 
 	/**
 	 * @param emulateUnsupportedPstmts
 	 *            The emulateUnsupportedPstmts to set.
 	 */
-	public void setEmulateUnsupportedPstmts(boolean flag) {
-		this.emulateUnsupportedPstmts.setValue(flag);
-	}
+	public abstract void setEmulateUnsupportedPstmts(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2902,20 +859,14 @@
 	 * @param flag
 	 *            The enablePacketDebug to set.
 	 */
-	public void setEnablePacketDebug(boolean flag) {
-		this.enablePacketDebug.setValue(flag);
-	}
+	public abstract void setEnablePacketDebug(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setEncoding(String property) {
-		this.characterEncoding.setValue(property);
-		this.characterEncodingAsString = this.characterEncoding
-				.getValueAsString();
-	}
+	public abstract void setEncoding(String property);
 
 	/**
 	 * DOCUMENT ME!
@@ -2923,9 +874,7 @@
 	 * @param flag
 	 *            The explainSlowQueries to set.
 	 */
-	public void setExplainSlowQueries(boolean flag) {
-		this.explainSlowQueries.setValue(flag);
-	}
+	public abstract void setExplainSlowQueries(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2933,9 +882,7 @@
 	 * @param flag
 	 *            The failOverReadOnly to set.
 	 */
-	public void setFailOverReadOnly(boolean flag) {
-		this.failOverReadOnly.setValue(flag);
-	}
+	public abstract void setFailOverReadOnly(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -2943,54 +890,34 @@
 	 * @param flag
 	 *            The gatherPerformanceMetrics to set.
 	 */
-	public void setGatherPerformanceMetrics(boolean flag) {
-		this.gatherPerformanceMetrics.setValue(flag);
-	}
+	public abstract void setGatherPerformanceMetrics(boolean flag);
 
 	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @param property
-	 */
-	protected void setHighAvailability(boolean property) {
-		this.autoReconnect.setValue(property);
-		this.highAvailabilityAsBoolean = this.autoReconnect.getValueAsBoolean();
-	}
-
-	/**
 	 * @param holdResultsOpenOverStatementClose
 	 *            The holdResultsOpenOverStatementClose to set.
 	 */
-	public void setHoldResultsOpenOverStatementClose(boolean flag) {
-		this.holdResultsOpenOverStatementClose.setValue(flag);
-	}
+	public abstract void setHoldResultsOpenOverStatementClose(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setIgnoreNonTxTables(boolean property) {
-		this.ignoreNonTxTables.setValue(property);
-	}
+	public abstract void setIgnoreNonTxTables(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setInitialTimeout(int property) {
-		this.initialTimeout.setValue(property);
-	}
+	public abstract void setInitialTimeout(int property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setIsInteractiveClient(boolean property) {
-		this.isInteractiveClient.setValue(property);
-	}
+	public abstract void setIsInteractiveClient(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -2998,26 +925,21 @@
 	 * @param flag
 	 *            The jdbcCompliantTruncation to set.
 	 */
-	public void setJdbcCompliantTruncation(boolean flag) {
-		this.jdbcCompliantTruncation.setValue(flag);
-	}
+	public abstract void setJdbcCompliantTruncation(boolean flag);
 
 	/**
 	 * @param locatorFetchBufferSize
 	 *            The locatorFetchBufferSize to set.
 	 */
-	public void setLocatorFetchBufferSize(String value) throws SQLException {
-		this.locatorFetchBufferSize.setValue(value);
-	}
+	public abstract void setLocatorFetchBufferSize(String value)
+			throws SQLException;
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setLogger(String property) {
-		this.loggerClassName.setValueAsObject(property);
-	}
+	public abstract void setLogger(String property);
 
 	/**
 	 * DOCUMENT ME!
@@ -3025,9 +947,7 @@
 	 * @param className
 	 *            The loggerClassName to set.
 	 */
-	public void setLoggerClassName(String className) {
-		this.loggerClassName.setValue(className);
-	}
+	public abstract void setLoggerClassName(String className);
 
 	/**
 	 * DOCUMENT ME!
@@ -3035,15 +955,9 @@
 	 * @param flag
 	 *            The logSlowQueries to set.
 	 */
-	public void setLogSlowQueries(boolean flag) {
-		this.logSlowQueries.setValue(flag);
-	}
+	public abstract void setLogSlowQueries(boolean flag);
 
-	public void setMaintainTimeStats(boolean flag) {
-		this.maintainTimeStats.setValue(flag);
-		this.maintainTimeStatsAsBoolean = this.maintainTimeStats
-				.getValueAsBoolean();
-	}
+	public abstract void setMaintainTimeStats(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -3051,28 +965,21 @@
 	 * @param sizeInBytes
 	 *            The maxQuerySizeToLog to set.
 	 */
-	public void setMaxQuerySizeToLog(int sizeInBytes) {
-		this.maxQuerySizeToLog.setValue(sizeInBytes);
-	}
+	public abstract void setMaxQuerySizeToLog(int sizeInBytes);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setMaxReconnects(int property) {
-		this.maxReconnects.setValue(property);
-	}
+	public abstract void setMaxReconnects(int property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setMaxRows(int property) {
-		this.maxRows.setValue(property);
-		this.maxRowsAsInt = this.maxRows.getValueAsInt();
-	}
+	public abstract void setMaxRows(int property);
 
 	/**
 	 * Sets the number of queries that metadata can be cached if caching is
@@ -3081,25 +988,17 @@
 	 * @param value
 	 *            the number of queries to cache metadata for.
 	 */
-	public void setMetadataCacheSize(int value) {
-		this.metadataCacheSize.setValue(value);
-	}
+	public abstract void setMetadataCacheSize(int value);
 
 	/**
 	 * @param noDatetimeStringSync
 	 *            The noDatetimeStringSync to set.
 	 */
-	public void setNoDatetimeStringSync(boolean flag) {
-		this.noDatetimeStringSync.setValue(flag);
-	}
+	public abstract void setNoDatetimeStringSync(boolean flag);
 
-	public void setNullCatalogMeansCurrent(boolean value) {
-		this.nullCatalogMeansCurrent.setValue(value);
-	}
+	public abstract void setNullCatalogMeansCurrent(boolean value);
 
-	public void setNullNamePatternMatchesAll(boolean value) {
-		this.nullNamePatternMatchesAll.setValue(value);
-	}
+	public abstract void setNullNamePatternMatchesAll(boolean value);
 
 	/**
 	 * DOCUMENT ME!
@@ -3107,27 +1006,21 @@
 	 * @param size
 	 *            The packetDebugBufferSize to set.
 	 */
-	public void setPacketDebugBufferSize(int size) {
-		this.packetDebugBufferSize.setValue(size);
-	}
+	public abstract void setPacketDebugBufferSize(int size);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setParanoid(boolean property) {
-		this.paranoid.setValue(property);
-	}
+	public abstract void setParanoid(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setPedantic(boolean property) {
-		this.pedantic.setValue(property);
-	}
+	public abstract void setPedantic(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -3135,9 +1028,7 @@
 	 * @param cacheSize
 	 *            The preparedStatementCacheSize to set.
 	 */
-	public void setPreparedStatementCacheSize(int cacheSize) {
-		this.preparedStatementCacheSize.setValue(cacheSize);
-	}
+	public abstract void setPreparedStatementCacheSize(int cacheSize);
 
 	/**
 	 * DOCUMENT ME!
@@ -3145,19 +1036,14 @@
 	 * @param cacheSqlLimit
 	 *            The preparedStatementCacheSqlLimit to set.
 	 */
-	public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) {
-		this.preparedStatementCacheSqlLimit.setValue(cacheSqlLimit);
-	}
+	public abstract void setPreparedStatementCacheSqlLimit(int cacheSqlLimit);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setProfileSql(boolean property) {
-		this.profileSQL.setValue(property);
-		this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean();
-	}
+	public abstract void setProfileSql(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -3165,46 +1051,34 @@
 	 * @param flag
 	 *            The profileSQL to set.
 	 */
-	public void setProfileSQL(boolean flag) {
-		this.profileSQL.setValue(flag);
-	}
+	public abstract void setProfileSQL(boolean flag);
 
 	/**
 	 * @param propertiesTransform
 	 *            The propertiesTransform to set.
 	 */
-	public void setPropertiesTransform(String value) {
-		this.propertiesTransform.setValue(value);
-	}
+	public abstract void setPropertiesTransform(String value);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setQueriesBeforeRetryMaster(int property) {
-		this.queriesBeforeRetryMaster.setValue(property);
-	}
+	public abstract void setQueriesBeforeRetryMaster(int property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setReconnectAtTxEnd(boolean property) {
-		this.reconnectAtTxEnd.setValue(property);
-		this.reconnectTxAtEndAsBoolean = this.reconnectAtTxEnd
-				.getValueAsBoolean();
-	}
+	public abstract void setReconnectAtTxEnd(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setRelaxAutoCommit(boolean property) {
-		this.relaxAutoCommit.setValue(property);
-	}
+	public abstract void setRelaxAutoCommit(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -3212,30 +1086,22 @@
 	 * @param millis
 	 *            The reportMetricsIntervalMillis to set.
 	 */
-	public void setReportMetricsIntervalMillis(int millis) {
-		this.reportMetricsIntervalMillis.setValue(millis);
-	}
+	public abstract void setReportMetricsIntervalMillis(int millis);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setRequireSSL(boolean property) {
-		this.requireSSL.setValue(property);
-	}
+	public abstract void setRequireSSL(boolean property);
 
-	public void setRetainStatementAfterResultSetClose(boolean flag) {
-		this.retainStatementAfterResultSetClose.setValue(flag);
-	}
+	public abstract void setRetainStatementAfterResultSetClose(boolean flag);
 
 	/**
 	 * @param rollbackOnPooledClose
 	 *            The rollbackOnPooledClose to set.
 	 */
-	public void setRollbackOnPooledClose(boolean flag) {
-		this.rollbackOnPooledClose.setValue(flag);
-	}
+	public abstract void setRollbackOnPooledClose(boolean flag);
 
 	/**
 	 * Sets whether or not hosts will be picked in a round-robin fashion.
@@ -3243,26 +1109,20 @@
 	 * @param flag
 	 *            The roundRobinLoadBalance property to set.
 	 */
-	public void setRoundRobinLoadBalance(boolean flag) {
-		this.roundRobinLoadBalance.setValue(flag);
-	}
+	public abstract void setRoundRobinLoadBalance(boolean flag);
 
 	/**
 	 * @param runningCTS13
 	 *            The runningCTS13 to set.
 	 */
-	public void setRunningCTS13(boolean flag) {
-		this.runningCTS13.setValue(flag);
-	}
+	public abstract void setRunningCTS13(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setSecondsBeforeRetryMaster(int property) {
-		this.secondsBeforeRetryMaster.setValue(property);
-	}
+	public abstract void setSecondsBeforeRetryMaster(int property);
 
 	/**
 	 * DOCUMENT ME!
@@ -3270,17 +1130,13 @@
 	 * @param property
 	 *            DOCUMENT ME!
 	 */
-	public void setServerTimezone(String property) {
-		this.serverTimezone.setValue(property);
-	}
+	public abstract void setServerTimezone(String property);
 
 	/**
 	 * @param sessionVariables
 	 *            The sessionVariables to set.
 	 */
-	public void setSessionVariables(String variables) {
-		this.sessionVariables.setValue(variables);
-	}
+	public abstract void setSessionVariables(String variables);
 
 	/**
 	 * DOCUMENT ME!
@@ -3288,53 +1144,41 @@
 	 * @param millis
 	 *            The slowQueryThresholdMillis to set.
 	 */
-	public void setSlowQueryThresholdMillis(int millis) {
-		this.slowQueryThresholdMillis.setValue(millis);
-	}
+	public abstract void setSlowQueryThresholdMillis(int millis);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setSocketFactoryClassName(String property) {
-		this.socketFactoryClassName.setValue(property);
-	}
+	public abstract void setSocketFactoryClassName(String property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setSocketTimeout(int property) {
-		this.socketTimeout.setValue(property);
-	}
+	public abstract void setSocketTimeout(int property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setStrictFloatingPoint(boolean property) {
-		this.strictFloatingPoint.setValue(property);
-	}
+	public abstract void setStrictFloatingPoint(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setStrictUpdates(boolean property) {
-		this.strictUpdates.setValue(property);
-	}
+	public abstract void setStrictUpdates(boolean property);
 
 	/**
 	 * @param tinyInt1isBit
 	 *            The tinyInt1isBit to set.
 	 */
-	public void setTinyInt1isBit(boolean flag) {
-		this.tinyInt1isBit.setValue(flag);
-	}
+	public abstract void setTinyInt1isBit(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -3342,77 +1186,55 @@
 	 * @param flag
 	 *            The logProtocol to set.
 	 */
-	public void setTraceProtocol(boolean flag) {
-		this.traceProtocol.setValue(flag);
-	}
+	public abstract void setTraceProtocol(boolean flag);
 
-	public void setTransformedBitIsBoolean(boolean flag) {
-		this.transformedBitIsBoolean.setValue(flag);
-	}
+	public abstract void setTransformedBitIsBoolean(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setUseCompression(boolean property) {
-		this.useCompression.setValue(property);
-	}
+	public abstract void setUseCompression(boolean property);
 
 	/**
 	 * @param useFastIntParsing
 	 *            The useFastIntParsing to set.
 	 */
-	public void setUseFastIntParsing(boolean flag) {
-		this.useFastIntParsing.setValue(flag);
-	}
+	public abstract void setUseFastIntParsing(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setUseHostsInPrivileges(boolean property) {
-		this.useHostsInPrivileges.setValue(property);
-	}
+	public abstract void setUseHostsInPrivileges(boolean property);
 
-	public void setUseInformationSchema(boolean flag) {
-		this.useInformationSchema.setValue(flag);
-	}
+	public abstract void setUseInformationSchema(boolean flag);
 
 	/**
 	 * @param useLocalSessionState
 	 *            The useLocalSessionState to set.
 	 */
-	public void setUseLocalSessionState(boolean flag) {
-		this.useLocalSessionState.setValue(flag);
-	}
+	public abstract void setUseLocalSessionState(boolean flag);
 
 	/**
 	 * @param useOldUTF8Behavior
 	 *            The useOldUTF8Behavior to set.
 	 */
-	public void setUseOldUTF8Behavior(boolean flag) {
-		this.useOldUTF8Behavior.setValue(flag);
-		this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior
-				.getValueAsBoolean();
-	}
+	public abstract void setUseOldUTF8Behavior(boolean flag);
 
 	/**
 	 * @param useOnlyServerErrorMessages
 	 *            The useOnlyServerErrorMessages to set.
 	 */
-	public void setUseOnlyServerErrorMessages(boolean flag) {
-		this.useOnlyServerErrorMessages.setValue(flag);
-	}
+	public abstract void setUseOnlyServerErrorMessages(boolean flag);
 
 	/**
 	 * @param useReadAheadInput
 	 *            The useReadAheadInput to set.
 	 */
-	public void setUseReadAheadInput(boolean flag) {
-		this.useReadAheadInput.setValue(flag);
-	}
+	public abstract void setUseReadAheadInput(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -3420,9 +1242,7 @@
 	 * @param flag
 	 *            The detectServerPreparedStmts to set.
 	 */
-	public void setUseServerPreparedStmts(boolean flag) {
-		this.detectServerPreparedStmts.setValue(flag);
-	}
+	public abstract void setUseServerPreparedStmts(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -3430,45 +1250,35 @@
 	 * @param flag
 	 *            The useSqlStateCodes to set.
 	 */
-	public void setUseSqlStateCodes(boolean flag) {
-		this.useSqlStateCodes.setValue(flag);
-	}
+	public abstract void setUseSqlStateCodes(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setUseSSL(boolean property) {
-		this.useSSL.setValue(property);
-	}
+	public abstract void setUseSSL(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setUseStreamLengthsInPrepStmts(boolean property) {
-		this.useStreamLengthsInPrepStmts.setValue(property);
-	}
+	public abstract void setUseStreamLengthsInPrepStmts(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setUseTimezone(boolean property) {
-		this.useTimezone.setValue(property);
-	}
+	public abstract void setUseTimezone(boolean property);
 
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param property
 	 */
-	public void setUseUltraDevWorkAround(boolean property) {
-		this.useUltraDevWorkAround.setValue(property);
-	}
+	public abstract void setUseUltraDevWorkAround(boolean property);
 
 	/**
 	 * DOCUMENT ME!
@@ -3476,9 +1286,7 @@
 	 * @param flag
 	 *            The useUnbufferedInput to set.
 	 */
-	public void setUseUnbufferedInput(boolean flag) {
-		this.useUnbufferedInput.setValue(flag);
-	}
+	public abstract void setUseUnbufferedInput(boolean flag);
 
 	/**
 	 * DOCUMENT ME!
@@ -3486,10 +1294,7 @@
 	 * @param flag
 	 *            The useUnicode to set.
 	 */
-	public void setUseUnicode(boolean flag) {
-		this.useUnicode.setValue(flag);
-		this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean();
-	}
+	public abstract void setUseUnicode(boolean flag);
 
 	/**
 	 * Sets whether or not the driver advises of proper usage.
@@ -3497,224 +1302,276 @@
 	 * @param useUsageAdvisorFlag
 	 *            whether or not the driver advises of proper usage.
 	 */
-	public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) {
-		this.useUsageAdvisor.setValue(useUsageAdvisorFlag);
-		this.useUsageAdvisorAsBoolean = this.useUsageAdvisor
-				.getValueAsBoolean();
-	}
+	public abstract void setUseUsageAdvisor(boolean useUsageAdvisorFlag);
 
-	public void setYearIsDateType(boolean flag) {
-		this.yearIsDateType.setValue(flag);
-	}
+	public abstract void setYearIsDateType(boolean flag);
 
 	/**
 	 * @param zeroDateTimeBehavior
 	 *            The zeroDateTimeBehavior to set.
 	 */
-	public void setZeroDateTimeBehavior(String behavior) {
-		this.zeroDateTimeBehavior.setValue(behavior);
-	}
+	public abstract void setZeroDateTimeBehavior(String behavior);
 
-	protected void storeToRef(Reference ref) throws SQLException {
-		int numPropertiesToSet = PROPERTY_LIST.size();
-
-		for (int i = 0; i < numPropertiesToSet; i++) {
-			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
-					.get(i);
-
-			try {
-				ConnectionProperty propToStore = (ConnectionProperty) propertyField
-						.get(this);
-
-				if (ref != null) {
-					propToStore.storeTo(ref);
-				}
-			} catch (IllegalAccessException iae) {
-				throw SQLError.createSQLException("Huh?");
-			}
-		}
-	}
-
 	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @return Returns the useUnbufferedInput.
 	 */
-	public boolean useUnbufferedInput() {
-		return this.useUnbufferedInput.getValueAsBoolean();
-	}
+	public abstract boolean useUnbufferedInput();
 
-	public boolean getUseCursorFetch() {
-		return this.useCursorFetch.getValueAsBoolean();
-	}
+	public abstract boolean getUseCursorFetch();
 
-	public void setUseCursorFetch(boolean flag) {
-		this.useCursorFetch.setValue(flag);
-	}
+	public abstract void setUseCursorFetch(boolean flag);
 
-	public boolean getOverrideSupportsIntegrityEnhancementFacility() {
-		return this.overrideSupportsIntegrityEnhancementFacility.getValueAsBoolean();
-	}
+	public abstract boolean getOverrideSupportsIntegrityEnhancementFacility();
 
-	public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
-		this.overrideSupportsIntegrityEnhancementFacility.setValue(flag);	
-	}
-	
-	public boolean getNoTimezoneConversionForTimeType() {
-		return this.noTimezoneConversionForTimeType.getValueAsBoolean();
-	}
+	public abstract void setOverrideSupportsIntegrityEnhancementFacility(
+			boolean flag);
 
-	public void setNoTimezoneConversionForTimeType(boolean flag) {
-		this.noTimezoneConversionForTimeType.setValue(flag);
-	}
+	public abstract boolean getNoTimezoneConversionForTimeType();
 
-	public boolean getUseJDBCCompliantTimezoneShift() {
-		return this.useJDBCCompliantTimezoneShift.getValueAsBoolean();
-	}
+	public abstract void setNoTimezoneConversionForTimeType(boolean flag);
 
-	public void setUseJDBCCompliantTimezoneShift(boolean flag) {
-		this.useJDBCCompliantTimezoneShift.setValue(flag);
-	}
-	
-	public boolean getAutoClosePStmtStreams() {
-		return this.autoClosePStmtStreams.getValueAsBoolean();
-	}
+	public abstract boolean getUseJDBCCompliantTimezoneShift();
 
-	public void setAutoClosePStmtStreams(boolean flag) {
-		this.autoClosePStmtStreams.setValue(flag);
-	}
+	public abstract void setUseJDBCCompliantTimezoneShift(boolean flag);
 
-	public boolean getProcessEscapeCodesForPrepStmts() {
-		return this.processEscapeCodesForPrepStmts.getValueAsBoolean();
-	}
+	public abstract boolean getAutoClosePStmtStreams();
 
-	public void setProcessEscapeCodesForPrepStmts(boolean flag) {
-		this.processEscapeCodesForPrepStmts.setValue(flag);
-	}
+	public abstract void setAutoClosePStmtStreams(boolean flag);
 
-	public boolean getUseGmtMillisForDatetimes() {
-		return this.useGmtMillisForDatetimes.getValueAsBoolean();
-	}
+	public abstract boolean getProcessEscapeCodesForPrepStmts();
 
-	public void setUseGmtMillisForDatetimes(boolean flag) {
-		this.useGmtMillisForDatetimes.setValue(flag);
-	}
-	
-	public boolean getDumpMetadataOnColumnNotFound() {
-		return this.dumpMetadataOnColumnNotFound.getValueAsBoolean();
-	}
+	public abstract void setProcessEscapeCodesForPrepStmts(boolean flag);
 
-	public void setDumpMetadataOnColumnNotFound(boolean flag) {
-		this.dumpMetadataOnColumnNotFound.setValue(flag);
-	}
+	public abstract boolean getUseGmtMillisForDatetimes();
 
-	public String getResourceId() {
-		return this.resourceId.getValueAsString();
-	}
+	public abstract void setUseGmtMillisForDatetimes(boolean flag);
 
-	public void setResourceId(String resourceId) {
-		this.resourceId.setValue(resourceId);
-	}
+	public abstract boolean getDumpMetadataOnColumnNotFound();
+
+	public abstract void setDumpMetadataOnColumnNotFound(boolean flag);
+
+	public abstract String getResourceId();
+
+	public abstract void setResourceId(String resourceId);
+
+	public abstract boolean getRewriteBatchedStatements();
+
+	public abstract void setRewriteBatchedStatements(boolean flag);
+
+	public abstract boolean getJdbcCompliantTruncationForReads();
+
+	public abstract void setJdbcCompliantTruncationForReads(
+			boolean jdbcCompliantTruncationForReads);
+
+	public abstract boolean getUseJvmCharsetConverters();
+
+	public abstract void setUseJvmCharsetConverters(boolean flag);
+
+	public abstract boolean getPinGlobalTxToPhysicalConnection();
+
+	public abstract void setPinGlobalTxToPhysicalConnection(boolean flag);
+
+	public abstract void setGatherPerfMetrics(boolean flag);
+
+	public abstract boolean getGatherPerfMetrics();
+
+	public abstract void setUltraDevHack(boolean flag);
+
+	public abstract boolean getUltraDevHack();
+
+	public abstract void setInteractiveClient(boolean property);
+
+	public abstract void setSocketFactory(String name);
+
+	public abstract String getSocketFactory();
+
+	public abstract void setUseServerPrepStmts(boolean flag);
+
+	public abstract boolean getUseServerPrepStmts();
+
+	public abstract void setCacheCallableStmts(boolean flag);
+
+	public abstract boolean getCacheCallableStmts();
+
+	public abstract void setCachePrepStmts(boolean flag);
+
+	public abstract boolean getCachePrepStmts();
+
+	public abstract void setCallableStmtCacheSize(int cacheSize);
+
+	public abstract int getCallableStmtCacheSize();
+
+	public abstract void setPrepStmtCacheSize(int cacheSize);
+
+	public abstract int getPrepStmtCacheSize();
+
+	public abstract void setPrepStmtCacheSqlLimit(int sqlLimit);
+
+	public abstract int getPrepStmtCacheSqlLimit();
+
+	public abstract boolean getNoAccessToProcedureBodies();
+
+	public abstract void setNoAccessToProcedureBodies(boolean flag);
+
+	public abstract boolean getUseOldAliasMetadataBehavior();
+
+	public abstract void setUseOldAliasMetadataBehavior(boolean flag);
+
+	public abstract String getClientCertificateKeyStorePassword();
+
+	public abstract void setClientCertificateKeyStorePassword(String value);
+
+	public abstract String getClientCertificateKeyStoreType();
+
+	public abstract void setClientCertificateKeyStoreType(String value);
+
+	public abstract String getClientCertificateKeyStoreUrl();
+
+	public abstract void setClientCertificateKeyStoreUrl(String value);
+
+	public abstract String getTrustCertificateKeyStorePassword();
+
+	public abstract void setTrustCertificateKeyStorePassword(String value);
+
+	public abstract String getTrustCertificateKeyStoreType();
+
+	public abstract void setTrustCertificateKeyStoreType(String value);
+
+	public abstract String getTrustCertificateKeyStoreUrl();
+
+	public abstract void setTrustCertificateKeyStoreUrl(String value);
+
+	public abstract boolean getUseSSPSCompatibleTimezoneShift();
+
+	public abstract void setUseSSPSCompatibleTimezoneShift(boolean flag);
+
+	public abstract boolean getTreatUtilDateAsTimestamp();
+
+	public abstract void setTreatUtilDateAsTimestamp(boolean flag);
+
+	public abstract boolean getUseFastDateParsing();
+
+	public abstract void setUseFastDateParsing(boolean flag);
+
+	public abstract String getLocalSocketAddress();
+
+	public abstract void setLocalSocketAddress(String address);
+
+	public abstract void setUseConfigs(String configs);
+
+	public abstract String getUseConfigs();
+
+	public abstract boolean getGenerateSimpleParameterMetadata();
+
+	public abstract void setGenerateSimpleParameterMetadata(boolean flag);
+
+	public abstract boolean getLogXaCommands();
+
+	public abstract void setLogXaCommands(boolean flag);
+
+	public abstract int getResultSetSizeThreshold();
+
+	public abstract void setResultSetSizeThreshold(int threshold);
+
+	public abstract int getNetTimeoutForStreamingResults();
+
+	public abstract void setNetTimeoutForStreamingResults(int value);
+
+	public abstract boolean getEnableQueryTimeouts();
+
+	public abstract void setEnableQueryTimeouts(boolean flag);
+
+	public abstract boolean getPadCharsWithSpace();
+
+	public abstract void setPadCharsWithSpace(boolean flag);
+
+	public abstract boolean getUseDynamicCharsetInfo();
+
+	public abstract void setUseDynamicCharsetInfo(boolean flag);
+
+	public abstract String getClientInfoProvider();
+
+	public abstract void setClientInfoProvider(String classname);
 	
-	public boolean getRewriteBatchedStatements() {
-		return this.rewriteBatchedStatements.getValueAsBoolean();
-	}
+	public abstract boolean getPopulateInsertRowWithDefaultValues();
 
-	public void setRewriteBatchedStatements(boolean flag) {
-		this.rewriteBatchedStatements.setValue(flag);
-	}
+	public abstract void setPopulateInsertRowWithDefaultValues(boolean flag);
 	
-	public boolean getJdbcCompliantTruncationForReads() {
-		return this.jdbcCompliantTruncationForReads;
-	}
+	public abstract String getLoadBalanceStrategy();
 
-	public void setJdbcCompliantTruncationForReads(
-			boolean jdbcCompliantTruncationForReads) {
-		this.jdbcCompliantTruncationForReads = jdbcCompliantTruncationForReads;
-	}
+	public abstract void setLoadBalanceStrategy(String strategy);
+	
+	public abstract boolean getTcpNoDelay();
 
-	public boolean getUseJvmCharsetConverters() {
-		return this.useJvmCharsetConverters.getValueAsBoolean();
-	}
+	public abstract void setTcpNoDelay(boolean flag);
 
-	public void setUseJvmCharsetConverters(boolean flag) {
-		this.useJvmCharsetConverters.setValue(flag);
-	}
+	public abstract boolean getTcpKeepAlive();
 
-	public boolean getPinGlobalTxToPhysicalConnection() {
-		return this.pinGlobalTxToPhysicalConnection.getValueAsBoolean();
-	}
+	public abstract void setTcpKeepAlive(boolean flag);
 
-	public void setPinGlobalTxToPhysicalConnection(boolean flag) {
-		this.pinGlobalTxToPhysicalConnection.setValue(flag);
-	}
+	public abstract int getTcpRcvBuf();
+
+	public abstract void setTcpRcvBuf(int bufSize);
+
+	public abstract int getTcpSndBuf();
 	
-	/*
-	 * "Aliases" which match the property names to make using 
-	 * from datasources easier.
-	 */
+	public abstract void setTcpSndBuf(int bufSize);
+
+	public abstract int getTcpTrafficClass();
+
+	public abstract void setTcpTrafficClass(int classFlags);
 	
-	public void setUseServerPrepStmts(boolean flag) {
-		setUseServerPreparedStmts(flag);
-	}
+	public abstract boolean getUseNanosForElapsedTime();
 
-	public boolean getUseServerPrepStmts() {
-		return getUseServerPreparedStmts();
-	}
+	public abstract void setUseNanosForElapsedTime(boolean flag);
 
-	public void setCacheCallableStmts(boolean flag) {
-		setCacheCallableStatements(flag);
-	}
+	public abstract long getSlowQueryThresholdNanos();
 
-	public boolean getCacheCallableStmts() {
-		return getCacheCallableStatements();
-	}
+	public abstract void setSlowQueryThresholdNanos(long nanos);
+	
+	public abstract String getStatementInterceptors();
 
-	public void setCachePrepStmts(boolean flag) {
-		setCachePreparedStatements(flag);
-	}
+	public abstract void setStatementInterceptors(String value);
+	
+	public abstract boolean getUseDirectRowUnpack();
 
-	public boolean getCachePrepStmts() {
-		return getCachePreparedStatements();
-	}
+	public abstract void setUseDirectRowUnpack(boolean flag);
+	
+	public abstract String getLargeRowSizeThreshold();
 
-	public void setCallableStmtCacheSize(int cacheSize) {
-		setCallableStatementCacheSize(cacheSize);
-	}
+	public abstract void setLargeRowSizeThreshold(String value);
+	
+	public abstract boolean getUseBlobToStoreUTF8OutsideBMP();
 
-	public int getCallableStmtCacheSize() {
-		return getCallableStatementCacheSize();
-	}
+	public abstract void setUseBlobToStoreUTF8OutsideBMP(boolean flag);
+	
+	public abstract String getUtf8OutsideBmpExcludedColumnNamePattern();
 
-	public void setPrepStmtCacheSize(int cacheSize) {
-		setPreparedStatementCacheSize(cacheSize);
-	}
+	public abstract void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern);
 
-	public int getPrepStmtCacheSize() {
-		return getPreparedStatementCacheSize();
-	}
+	public abstract String getUtf8OutsideBmpIncludedColumnNamePattern();
 
-	public void setPrepStmtCacheSqlLimit(int sqlLimit) {
-		setPreparedStatementCacheSqlLimit(sqlLimit);
-	}
+	public abstract void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern);
+	
+	public abstract boolean getIncludeInnodbStatusInDeadlockExceptions();
+	
+	public abstract void setIncludeInnodbStatusInDeadlockExceptions(boolean flag);
+	
+	public abstract boolean getBlobsAreStrings();
 
-	public int getPrepStmtCacheSqlLimit() {
-		return getPreparedStatementCacheSqlLimit();
-	}
+	public abstract void setBlobsAreStrings(boolean flag);
 
-	public boolean getNoAccessToProcedureBodies() {
-		return this.noAccessToProcedureBodies.getValueAsBoolean();
-	}
+    public abstract boolean getFunctionsNeverReturnBlobs();
 
-	public void setNoAccessToProcedureBodies(boolean flag) {
-		this.noAccessToProcedureBodies.setValue(flag);
-	}
+    public abstract void setFunctionsNeverReturnBlobs(boolean flag);
+    
+	public abstract boolean getAutoSlowLog();
 
-	public boolean getUseOldAliasMetadataBehavior() {
-		return this.useOldAliasMetadataBehavior.getValueAsBoolean();
-	}
+	public abstract void setAutoSlowLog(boolean flag);
+	
+	public abstract String getConnectionLifecycleInterceptors();
 
-	public void setUseOldAliasMetadataBehavior(boolean flag) {
-		this.useOldAliasMetadataBehavior.setValue(flag);
-	}
+	public abstract void setConnectionLifecycleInterceptors(String interceptors);
 }

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionPropertiesImpl.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionPropertiesImpl.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ConnectionPropertiesImpl.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,4286 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.log.Log;
+import com.mysql.jdbc.log.StandardLogger;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+
+/**
+ * Represents configurable properties for Connections and DataSources. Can also
+ * expose properties as JDBC DriverPropertyInfo if required as well.
+ * 
+ * @author Mark Matthews
+ * @version $Id: ConnectionProperties.java,v 1.1.2.2 2005/05/17 14:58:56
+ *          mmatthews Exp $
+ */
+public class ConnectionPropertiesImpl implements Serializable, ConnectionProperties {
+	
+	private static final long serialVersionUID = 4257801713007640580L;
+
+	class BooleanConnectionProperty extends ConnectionProperty implements Serializable {
+	
+		private static final long serialVersionUID = 2540132501709159404L;
+
+		/**
+		 * DOCUMENT ME!
+		 * 
+		 * @param propertyNameToSet
+		 * @param defaultValueToSet
+		 * @param descriptionToSet
+		 *            DOCUMENT ME!
+		 * @param sinceVersionToSet
+		 *            DOCUMENT ME!
+		 */
+		BooleanConnectionProperty(String propertyNameToSet,
+				boolean defaultValueToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			super(propertyNameToSet, Boolean.valueOf(defaultValueToSet), null, 0,
+					0, descriptionToSet, sinceVersionToSet, category,
+					orderInCategory);
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#getAllowableValues()
+		 */
+		String[] getAllowableValues() {
+			return new String[] { "true", "false", "yes", "no" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+		}
+
+		boolean getValueAsBoolean() {
+			return ((Boolean) this.valueAsObject).booleanValue();
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#hasValueConstraints()
+		 */
+		boolean hasValueConstraints() {
+			return true;
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#initializeFrom(java.util.Properties)
+		 */
+		void initializeFrom(String extractedValue) throws SQLException {
+			if (extractedValue != null) {
+				validateStringValues(extractedValue);
+
+				this.valueAsObject = Boolean.valueOf(extractedValue
+						.equalsIgnoreCase("TRUE") //$NON-NLS-1$
+						|| extractedValue.equalsIgnoreCase("YES")); //$NON-NLS-1$
+			} else {
+				this.valueAsObject = this.defaultValue;
+			}
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#isRangeBased()
+		 */
+		boolean isRangeBased() {
+			return false;
+		}
+
+		void setValue(boolean valueFlag) {
+			this.valueAsObject = Boolean.valueOf(valueFlag);
+		}
+	}
+
+	abstract class ConnectionProperty implements Serializable {
+		String[] allowableValues;
+
+		String categoryName;
+
+		Object defaultValue;
+
+		int lowerBound;
+
+		int order;
+
+		String propertyName;
+
+		String sinceVersion;
+
+		int upperBound;
+
+		Object valueAsObject;
+
+		boolean required;
+		
+		String description;
+		
+		public ConnectionProperty() {}
+		
+		ConnectionProperty(String propertyNameToSet, Object defaultValueToSet,
+				String[] allowableValuesToSet, int lowerBoundToSet,
+				int upperBoundToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			
+			this.description = descriptionToSet;
+			this.propertyName = propertyNameToSet;
+			this.defaultValue = defaultValueToSet;
+			this.valueAsObject = defaultValueToSet;
+			this.allowableValues = allowableValuesToSet;
+			this.lowerBound = lowerBoundToSet;
+			this.upperBound = upperBoundToSet;
+			this.required = false;
+			this.sinceVersion = sinceVersionToSet;
+			this.categoryName = category;
+			this.order = orderInCategory;
+		}
+
+		String[] getAllowableValues() {
+			return this.allowableValues;
+		}
+
+		/**
+		 * @return Returns the categoryName.
+		 */
+		String getCategoryName() {
+			return this.categoryName;
+		}
+
+		Object getDefaultValue() {
+			return this.defaultValue;
+		}
+
+		int getLowerBound() {
+			return this.lowerBound;
+		}
+
+		/**
+		 * @return Returns the order.
+		 */
+		int getOrder() {
+			return this.order;
+		}
+
+		String getPropertyName() {
+			return this.propertyName;
+		}
+
+		int getUpperBound() {
+			return this.upperBound;
+		}
+
+		Object getValueAsObject() {
+			return this.valueAsObject;
+		}
+
+		abstract boolean hasValueConstraints();
+
+		void initializeFrom(Properties extractFrom) throws SQLException {
+			String extractedValue = extractFrom.getProperty(getPropertyName());
+			extractFrom.remove(getPropertyName());
+			initializeFrom(extractedValue);
+		}
+
+		void initializeFrom(Reference ref) throws SQLException {
+			RefAddr refAddr = ref.get(getPropertyName());
+
+			if (refAddr != null) {
+				String refContentAsString = (String) refAddr.getContent();
+
+				initializeFrom(refContentAsString);
+			}
+		}
+
+		abstract void initializeFrom(String extractedValue) throws SQLException;
+
+		abstract boolean isRangeBased();
+
+		/**
+		 * @param categoryName
+		 *            The categoryName to set.
+		 */
+		void setCategoryName(String categoryName) {
+			this.categoryName = categoryName;
+		}
+
+		/**
+		 * @param order
+		 *            The order to set.
+		 */
+		void setOrder(int order) {
+			this.order = order;
+		}
+
+		void setValueAsObject(Object obj) {
+			this.valueAsObject = obj;
+		}
+
+		void storeTo(Reference ref) {
+			if (getValueAsObject() != null) {
+				ref.add(new StringRefAddr(getPropertyName(), getValueAsObject()
+						.toString()));
+			}
+		}
+
+		DriverPropertyInfo getAsDriverPropertyInfo() {
+			DriverPropertyInfo dpi = new DriverPropertyInfo(this.propertyName, null);
+			dpi.choices = getAllowableValues();
+			dpi.value = (this.valueAsObject != null) ? this.valueAsObject.toString() : null;
+			dpi.required = this.required;
+			dpi.description = this.description;
+			
+			return dpi;
+		}
+		
+
+		void validateStringValues(String valueToValidate) throws SQLException {
+			String[] validateAgainst = getAllowableValues();
+
+			if (valueToValidate == null) {
+				return;
+			}
+
+			if ((validateAgainst == null) || (validateAgainst.length == 0)) {
+				return;
+			}
+
+			for (int i = 0; i < validateAgainst.length; i++) {
+				if ((validateAgainst[i] != null)
+						&& validateAgainst[i].equalsIgnoreCase(valueToValidate)) {
+					return;
+				}
+			}
+
+			StringBuffer errorMessageBuf = new StringBuffer();
+
+			errorMessageBuf.append("The connection property '"); //$NON-NLS-1$
+			errorMessageBuf.append(getPropertyName());
+			errorMessageBuf.append("' only accepts values of the form: "); //$NON-NLS-1$
+
+			if (validateAgainst.length != 0) {
+				errorMessageBuf.append("'"); //$NON-NLS-1$
+				errorMessageBuf.append(validateAgainst[0]);
+				errorMessageBuf.append("'"); //$NON-NLS-1$
+
+				for (int i = 1; i < (validateAgainst.length - 1); i++) {
+					errorMessageBuf.append(", "); //$NON-NLS-1$
+					errorMessageBuf.append("'"); //$NON-NLS-1$
+					errorMessageBuf.append(validateAgainst[i]);
+					errorMessageBuf.append(Messages.getString("'")); //$NON-NLS-1$
+				}
+
+				errorMessageBuf.append(" or '"); //$NON-NLS-1$
+				errorMessageBuf
+						.append(validateAgainst[validateAgainst.length - 1]);
+				errorMessageBuf.append("'"); //$NON-NLS-1$
+			}
+
+			errorMessageBuf.append(". The value '"); //$NON-NLS-1$
+			errorMessageBuf.append(valueToValidate);
+			errorMessageBuf.append("' is not in this set."); //$NON-NLS-1$
+
+			throw SQLError.createSQLException(errorMessageBuf.toString(),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	class IntegerConnectionProperty extends ConnectionProperty implements Serializable {
+
+		private static final long serialVersionUID = -3004305481796850832L;
+
+		public IntegerConnectionProperty(String propertyNameToSet,
+				Object defaultValueToSet, String[] allowableValuesToSet,
+				int lowerBoundToSet, int upperBoundToSet,
+				String descriptionToSet, String sinceVersionToSet,
+				String category, int orderInCategory) {
+			super(propertyNameToSet, defaultValueToSet, allowableValuesToSet,
+					lowerBoundToSet, upperBoundToSet, descriptionToSet, sinceVersionToSet,
+					category, orderInCategory);
+		}
+
+		int multiplier = 1;
+
+		IntegerConnectionProperty(String propertyNameToSet,
+				int defaultValueToSet, int lowerBoundToSet,
+				int upperBoundToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			super(propertyNameToSet, new Integer(defaultValueToSet), null,
+					lowerBoundToSet, upperBoundToSet, descriptionToSet,
+					sinceVersionToSet, category, orderInCategory);
+		}
+
+		/**
+		 * DOCUMENT ME!
+		 * 
+		 * @param propertyNameToSet
+		 * @param defaultValueToSet
+		 * @param descriptionToSet
+		 * @param sinceVersionToSet
+		 *            DOCUMENT ME!
+		 */
+
+		IntegerConnectionProperty(String propertyNameToSet,
+				int defaultValueToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			this(propertyNameToSet, defaultValueToSet, 0, 0, descriptionToSet,
+					sinceVersionToSet, category, orderInCategory);
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getAllowableValues()
+		 */
+		String[] getAllowableValues() {
+			return null;
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getLowerBound()
+		 */
+		int getLowerBound() {
+			return this.lowerBound;
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getUpperBound()
+		 */
+		int getUpperBound() {
+			return this.upperBound;
+		}
+
+		int getValueAsInt() {
+			return ((Integer) this.valueAsObject).intValue();
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#hasValueConstraints()
+		 */
+		boolean hasValueConstraints() {
+			return false;
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#initializeFrom(java.lang.String)
+		 */
+		void initializeFrom(String extractedValue) throws SQLException {
+			if (extractedValue != null) {
+				try {
+					// Parse decimals, too
+					int intValue = Double.valueOf(extractedValue).intValue();
+
+					/*
+					 * if (isRangeBased()) { if ((intValue < getLowerBound()) ||
+					 * (intValue > getUpperBound())) { throw new
+					 * SQLException("The connection property '" +
+					 * getPropertyName() + "' only accepts integer values in the
+					 * range of " + getLowerBound() + " - " + getUpperBound() + ",
+					 * the value '" + extractedValue + "' exceeds this range.",
+					 * SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } }
+					 */
+					this.valueAsObject = new Integer(intValue * multiplier);
+				} catch (NumberFormatException nfe) {
+					throw SQLError.createSQLException("The connection property '" //$NON-NLS-1$
+							+ getPropertyName()
+							+ "' only accepts integer values. The value '" //$NON-NLS-1$
+							+ extractedValue
+							+ "' can not be converted to an integer.", //$NON-NLS-1$
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+			} else {
+				this.valueAsObject = this.defaultValue;
+			}
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#isRangeBased()
+		 */
+		boolean isRangeBased() {
+			return getUpperBound() != getLowerBound();
+		}
+
+		void setValue(int valueFlag) {
+			this.valueAsObject = new Integer(valueFlag);
+		}
+	}
+	
+	public class LongConnectionProperty extends IntegerConnectionProperty {
+
+		private static final long serialVersionUID = 6068572984340480895L;
+
+		LongConnectionProperty(String propertyNameToSet,
+				long defaultValueToSet, long lowerBoundToSet,
+				long upperBoundToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			super(propertyNameToSet, new Long(defaultValueToSet), null,
+					(int)lowerBoundToSet, (int)upperBoundToSet, descriptionToSet,
+					sinceVersionToSet, category, orderInCategory);
+		}
+		
+
+		LongConnectionProperty(String propertyNameToSet,
+				long defaultValueToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			this(propertyNameToSet,
+				defaultValueToSet, 0,
+				0, descriptionToSet,
+				sinceVersionToSet, category, orderInCategory);
+		}
+		
+		void setValue(long value) {
+			this.valueAsObject = new Long(value);
+		}
+		
+		long getValueAsLong() {
+			return ((Long) this.valueAsObject).longValue();
+		}
+		
+		void initializeFrom(String extractedValue) throws SQLException {
+			if (extractedValue != null) {
+				try {
+					// Parse decimals, too
+					long longValue = Double.valueOf(extractedValue).longValue();
+
+					this.valueAsObject = new Long(longValue);
+				} catch (NumberFormatException nfe) {
+					throw SQLError.createSQLException("The connection property '" //$NON-NLS-1$
+							+ getPropertyName()
+							+ "' only accepts long integer values. The value '" //$NON-NLS-1$
+							+ extractedValue
+							+ "' can not be converted to a long integer.", //$NON-NLS-1$
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+			} else {
+				this.valueAsObject = this.defaultValue;
+			}
+		}
+	}
+	
+	class MemorySizeConnectionProperty extends IntegerConnectionProperty implements Serializable {
+
+		private static final long serialVersionUID = 7351065128998572656L;
+
+		private String valueAsString;
+		
+		MemorySizeConnectionProperty(String propertyNameToSet,
+				int defaultValueToSet, int lowerBoundToSet,
+				int upperBoundToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			super(propertyNameToSet, defaultValueToSet, lowerBoundToSet,
+					upperBoundToSet, descriptionToSet, sinceVersionToSet,
+					category, orderInCategory);
+			// TODO Auto-generated constructor stub
+		}
+
+		void initializeFrom(String extractedValue) throws SQLException {
+			valueAsString = extractedValue;
+			
+			if (extractedValue != null) {
+				if (extractedValue.endsWith("k") //$NON-NLS-1$
+						|| extractedValue.endsWith("K") //$NON-NLS-1$
+						|| extractedValue.endsWith("kb") //$NON-NLS-1$
+						|| extractedValue.endsWith("Kb") //$NON-NLS-1$
+						|| extractedValue.endsWith("kB")) { //$NON-NLS-1$
+					multiplier = 1024;
+					int indexOfK = StringUtils.indexOfIgnoreCase(
+							extractedValue, "k"); //$NON-NLS-1$
+					extractedValue = extractedValue.substring(0, indexOfK);
+				} else if (extractedValue.endsWith("m") //$NON-NLS-1$
+						|| extractedValue.endsWith("M") //$NON-NLS-1$
+						|| extractedValue.endsWith("G") //$NON-NLS-1$
+						|| extractedValue.endsWith("mb") //$NON-NLS-1$
+						|| extractedValue.endsWith("Mb") //$NON-NLS-1$
+						|| extractedValue.endsWith("mB")) { //$NON-NLS-1$
+					multiplier = 1024 * 1024;
+					int indexOfM = StringUtils.indexOfIgnoreCase(
+							extractedValue, "m"); //$NON-NLS-1$
+					extractedValue = extractedValue.substring(0, indexOfM);
+				} else if (extractedValue.endsWith("g") //$NON-NLS-1$
+						|| extractedValue.endsWith("G") //$NON-NLS-1$
+						|| extractedValue.endsWith("gb") //$NON-NLS-1$
+						|| extractedValue.endsWith("Gb") //$NON-NLS-1$
+						|| extractedValue.endsWith("gB")) { //$NON-NLS-1$
+					multiplier = 1024 * 1024 * 1024;
+					int indexOfG = StringUtils.indexOfIgnoreCase(
+							extractedValue, "g"); //$NON-NLS-1$
+					extractedValue = extractedValue.substring(0, indexOfG);
+				}
+			}
+
+			super.initializeFrom(extractedValue);
+		}
+
+		void setValue(String value) throws SQLException {
+			initializeFrom(value);
+		}
+		
+		String getValueAsString() {
+			return valueAsString;
+		}
+	}
+
+	class StringConnectionProperty extends ConnectionProperty implements Serializable {
+	
+		private static final long serialVersionUID = 5432127962785948272L;
+
+		StringConnectionProperty(String propertyNameToSet,
+				String defaultValueToSet, String descriptionToSet,
+				String sinceVersionToSet, String category, int orderInCategory) {
+			this(propertyNameToSet, defaultValueToSet, null, descriptionToSet,
+					sinceVersionToSet, category, orderInCategory);
+		}
+
+		/**
+		 * DOCUMENT ME!
+		 * 
+		 * @param propertyNameToSet
+		 * @param defaultValueToSet
+		 * @param allowableValuesToSet
+		 * @param descriptionToSet
+		 * @param sinceVersionToSet
+		 *            DOCUMENT ME!
+		 */
+		StringConnectionProperty(String propertyNameToSet,
+				String defaultValueToSet, String[] allowableValuesToSet,
+				String descriptionToSet, String sinceVersionToSet,
+				String category, int orderInCategory) {
+			super(propertyNameToSet, defaultValueToSet, allowableValuesToSet,
+					0, 0, descriptionToSet, sinceVersionToSet, category,
+					orderInCategory);
+		}
+
+		String getValueAsString() {
+			return (String) this.valueAsObject;
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#hasValueConstraints()
+		 */
+		boolean hasValueConstraints() {
+			return (this.allowableValues != null)
+					&& (this.allowableValues.length > 0);
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#initializeFrom(java.util.Properties)
+		 */
+		void initializeFrom(String extractedValue) throws SQLException {
+			if (extractedValue != null) {
+				validateStringValues(extractedValue);
+
+				this.valueAsObject = extractedValue;
+			} else {
+				this.valueAsObject = this.defaultValue;
+			}
+		}
+
+		/**
+		 * @see com.mysql.jdbc.ConnectionPropertiesImpl.ConnectionProperty#isRangeBased()
+		 */
+		boolean isRangeBased() {
+			return false;
+		}
+
+		void setValue(String valueFlag) {
+			this.valueAsObject = valueFlag;
+		}
+	}
+
+	private static final String CONNECTION_AND_AUTH_CATEGORY = Messages.getString("ConnectionProperties.categoryConnectionAuthentication"); //$NON-NLS-1$
+
+	private static final String NETWORK_CATEGORY = Messages.getString("ConnectionProperties.categoryNetworking"); //$NON-NLS-1$
+	
+	private static final String DEBUGING_PROFILING_CATEGORY = Messages.getString("ConnectionProperties.categoryDebuggingProfiling"); //$NON-NLS-1$
+
+	private static final String HA_CATEGORY = Messages.getString("ConnectionProperties.categorryHA"); //$NON-NLS-1$
+
+	private static final String MISC_CATEGORY = Messages.getString("ConnectionProperties.categoryMisc"); //$NON-NLS-1$
+
+	private static final String PERFORMANCE_CATEGORY = Messages.getString("ConnectionProperties.categoryPerformance"); //$NON-NLS-1$
+	
+	private static final String SECURITY_CATEGORY = Messages.getString("ConnectionProperties.categorySecurity"); //$NON-NLS-1$
+	
+	private static final String[] PROPERTY_CATEGORIES = new String[] {
+		CONNECTION_AND_AUTH_CATEGORY, NETWORK_CATEGORY,
+		HA_CATEGORY, SECURITY_CATEGORY,
+		PERFORMANCE_CATEGORY, DEBUGING_PROFILING_CATEGORY, MISC_CATEGORY };
+	
+	private static final ArrayList PROPERTY_LIST = new ArrayList();
+
+	//
+	// Yes, this looks goofy, but we're trying to avoid intern()ing here
+	//
+	private static final String STANDARD_LOGGER_NAME = StandardLogger.class.getName();
+
+	protected static final String ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL = "convertToNull"; //$NON-NLS-1$
+
+	protected static final String ZERO_DATETIME_BEHAVIOR_EXCEPTION = "exception"; //$NON-NLS-1$
+
+	protected static final String ZERO_DATETIME_BEHAVIOR_ROUND = "round"; //$NON-NLS-1$
+
+	static {
+		try {
+			java.lang.reflect.Field[] declaredFields = ConnectionPropertiesImpl.class
+					.getDeclaredFields();
+
+			for (int i = 0; i < declaredFields.length; i++) {
+				if (ConnectionPropertiesImpl.ConnectionProperty.class
+						.isAssignableFrom(declaredFields[i].getType())) {
+					PROPERTY_LIST.add(declaredFields[i]);
+				}
+			}
+		} catch (Exception ex) {
+			throw new RuntimeException(ex.toString());
+		}
+	}
+
+	/**
+	 * Exposes all ConnectionPropertyInfo instances as DriverPropertyInfo
+	 * 
+	 * @param info
+	 *            the properties to load into these ConnectionPropertyInfo
+	 *            instances
+	 * @param slotsToReserve
+	 *            the number of DPI slots to reserve for 'standard' DPI
+	 *            properties (user, host, password, etc)
+	 * @return a list of all ConnectionPropertyInfo instances, as
+	 *         DriverPropertyInfo
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected static DriverPropertyInfo[] exposeAsDriverPropertyInfo(
+			Properties info, int slotsToReserve) throws SQLException {
+		return (new ConnectionPropertiesImpl() {
+		}).exposeAsDriverPropertyInfoInternal(info, slotsToReserve);
+	}
+
+	private BooleanConnectionProperty allowLoadLocalInfile = new BooleanConnectionProperty(
+			"allowLoadLocalInfile", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.loadDataLocal"), //$NON-NLS-1$
+			"3.0.3", SECURITY_CATEGORY, Integer.MAX_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty allowMultiQueries = new BooleanConnectionProperty(
+			"allowMultiQueries", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.allowMultiQueries"), //$NON-NLS-1$
+			"3.1.1", SECURITY_CATEGORY, 1); //$NON-NLS-1$
+
+	private BooleanConnectionProperty allowNanAndInf = new BooleanConnectionProperty(
+			"allowNanAndInf", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.allowNANandINF"), //$NON-NLS-1$
+			"3.1.5", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty allowUrlInLocalInfile = new BooleanConnectionProperty(
+			"allowUrlInLocalInfile", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.allowUrlInLoadLocal"), //$NON-NLS-1$
+			"3.1.4", SECURITY_CATEGORY, Integer.MAX_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty alwaysSendSetIsolation = new BooleanConnectionProperty(
+			"alwaysSendSetIsolation", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.alwaysSendSetIsolation"), //$NON-NLS-1$
+			"3.1.7", PERFORMANCE_CATEGORY, Integer.MAX_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty autoClosePStmtStreams = new BooleanConnectionProperty(
+			"autoClosePStmtStreams",  //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.autoClosePstmtStreams"), //$NON-NLS-1$
+			"3.1.12", //$NON-NLS-1$
+			MISC_CATEGORY,
+			Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty autoDeserialize = new BooleanConnectionProperty(
+			"autoDeserialize", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.autoDeserialize"), //$NON-NLS-1$
+			"3.1.5", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty autoGenerateTestcaseScript = new BooleanConnectionProperty(
+			"autoGenerateTestcaseScript", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.autoGenerateTestcaseScript"), "3.1.9", //$NON-NLS-1$ //$NON-NLS-2$
+			DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+
+	private boolean autoGenerateTestcaseScriptAsBoolean = false;
+
+	private BooleanConnectionProperty autoReconnect = new BooleanConnectionProperty(
+			"autoReconnect", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.autoReconnect"), //$NON-NLS-1$
+			"1.1", HA_CATEGORY, 0); //$NON-NLS-1$
+
+	private BooleanConnectionProperty autoReconnectForPools = new BooleanConnectionProperty(
+			"autoReconnectForPools", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.autoReconnectForPools"), //$NON-NLS-1$
+			"3.1.3", HA_CATEGORY, 1); //$NON-NLS-1$
+
+	private boolean autoReconnectForPoolsAsBoolean = false;
+
+	private MemorySizeConnectionProperty blobSendChunkSize = new MemorySizeConnectionProperty(
+			"blobSendChunkSize", //$NON-NLS-1$
+			1024 * 1024,
+			1,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.blobSendChunkSize"), //$NON-NLS-1$
+			"3.1.9", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty autoSlowLog = new BooleanConnectionProperty(
+			"autoSlowLog", true,
+			Messages.getString("ConnectionProperties.autoSlowLog"),
+			"5.1.4", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty blobsAreStrings = new BooleanConnectionProperty(
+            "blobsAreStrings", false,
+            "Should the driver always treat BLOBs as Strings - specifically to work around dubious metadata "
+            + "returned by the server for GROUP BY clauses?",
+            "5.0.8", MISC_CATEGORY, Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty functionsNeverReturnBlobs = new BooleanConnectionProperty(
+            "functionsNeverReturnBlobs", false,
+            "Should the driver always treat data from functions returning BLOBs as Strings - specifically to work around dubious metadata "
+            + "returned by the server for GROUP BY clauses?",
+            "5.0.8", MISC_CATEGORY, Integer.MIN_VALUE);
+			
+	private BooleanConnectionProperty cacheCallableStatements = new BooleanConnectionProperty(
+			"cacheCallableStmts", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.cacheCallableStatements"), //$NON-NLS-1$
+			"3.1.2", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty cachePreparedStatements = new BooleanConnectionProperty(
+			"cachePrepStmts", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.cachePrepStmts"), //$NON-NLS-1$
+			"3.0.10", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty cacheResultSetMetadata = new BooleanConnectionProperty(
+			"cacheResultSetMetadata", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.cacheRSMetadata"), //$NON-NLS-1$
+			"3.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private boolean cacheResultSetMetaDataAsBoolean;
+
+	private BooleanConnectionProperty cacheServerConfiguration = new BooleanConnectionProperty(
+			"cacheServerConfiguration", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.cacheServerConfiguration"), //$NON-NLS-1$
+			"3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty callableStatementCacheSize = new IntegerConnectionProperty(
+			"callableStmtCacheSize", //$NON-NLS-1$
+			100,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.callableStmtCacheSize"), //$NON-NLS-1$
+			"3.1.2", PERFORMANCE_CATEGORY, 5); //$NON-NLS-1$
+
+	private BooleanConnectionProperty capitalizeTypeNames = new BooleanConnectionProperty(
+			"capitalizeTypeNames", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.capitalizeTypeNames"), //$NON-NLS-1$
+			"2.0.7", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private StringConnectionProperty characterEncoding = new StringConnectionProperty(
+			"characterEncoding", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.characterEncoding"), //$NON-NLS-1$
+			"1.1g", MISC_CATEGORY, 5); //$NON-NLS-1$
+
+	private String characterEncodingAsString = null;
+
+	private StringConnectionProperty characterSetResults = new StringConnectionProperty(
+			"characterSetResults", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.characterSetResults"), "3.0.13", //$NON-NLS-1$ //$NON-NLS-2$
+			MISC_CATEGORY, 6);
+	
+	private StringConnectionProperty clientInfoProvider = new StringConnectionProperty(
+			"clientInfoProvider", "com.mysql.jdbc.JDBC4CommentClientInfoProvider", //$NON-NLS-1$ //$NON-NLS-2$
+			Messages.getString("ConnectionProperties.clientInfoProvider"), //$NON-NLS-1$
+			"5.1.0", //$NON-NLS-1$
+			DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty clobberStreamingResults = new BooleanConnectionProperty(
+			"clobberStreamingResults", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.clobberStreamingResults"), //$NON-NLS-1$
+			"3.0.9", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private StringConnectionProperty clobCharacterEncoding = new StringConnectionProperty(
+			"clobCharacterEncoding", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.clobCharacterEncoding"), //$NON-NLS-1$
+			"5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private StringConnectionProperty connectionCollation = new StringConnectionProperty(
+			"connectionCollation", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.connectionCollation"), //$NON-NLS-1$
+			"3.0.13", MISC_CATEGORY, 7); //$NON-NLS-1$
+
+	private StringConnectionProperty connectionLifecycleInterceptors = new StringConnectionProperty(
+			"connectionLifecycleInterceptors", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.connectionLifecycleInterceptors"),
+			"5.1.4", CONNECTION_AND_AUTH_CATEGORY, Integer.MAX_VALUE);
+
+	private IntegerConnectionProperty connectTimeout = new IntegerConnectionProperty(
+			"connectTimeout", 0, 0, Integer.MAX_VALUE, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.connectTimeout"), //$NON-NLS-1$
+			"3.0.1", CONNECTION_AND_AUTH_CATEGORY, 9); //$NON-NLS-1$
+
+	private BooleanConnectionProperty continueBatchOnError = new BooleanConnectionProperty(
+			"continueBatchOnError", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.continueBatchOnError"), //$NON-NLS-1$
+			"3.0.3", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty createDatabaseIfNotExist = new BooleanConnectionProperty(
+			"createDatabaseIfNotExist", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.createDatabaseIfNotExist"), //$NON-NLS-1$
+			"3.1.9", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty defaultFetchSize = new IntegerConnectionProperty("defaultFetchSize", 0, Messages.getString("ConnectionProperties.defaultFetchSize"), "3.1.9", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+	private BooleanConnectionProperty detectServerPreparedStmts = new BooleanConnectionProperty(
+			"useServerPrepStmts", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useServerPrepStmts"), //$NON-NLS-1$
+			"3.1.0", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty dontTrackOpenResources = new BooleanConnectionProperty(
+			"dontTrackOpenResources", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.dontTrackOpenResources"), "3.1.7", PERFORMANCE_CATEGORY, //$NON-NLS-1$ //$NON-NLS-2$
+			Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty dumpQueriesOnException = new BooleanConnectionProperty(
+			"dumpQueriesOnException", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.dumpQueriesOnException"), //$NON-NLS-1$
+			"3.1.3", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty dynamicCalendars = new BooleanConnectionProperty(
+			"dynamicCalendars", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.dynamicCalendars"), //$NON-NLS-1$
+			"3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty elideSetAutoCommits = new BooleanConnectionProperty(
+			"elideSetAutoCommits", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.eliseSetAutoCommit"), //$NON-NLS-1$
+			"3.1.3", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty emptyStringsConvertToZero = new BooleanConnectionProperty(
+			"emptyStringsConvertToZero", true, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.emptyStringsConvertToZero"), "3.1.8", //$NON-NLS-1$ //$NON-NLS-2$
+			MISC_CATEGORY, Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty emulateLocators = new BooleanConnectionProperty(
+			"emulateLocators", false, Messages.getString("ConnectionProperties.emulateLocators"), "3.1.0", MISC_CATEGORY, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty emulateUnsupportedPstmts = new BooleanConnectionProperty(
+			"emulateUnsupportedPstmts", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.emulateUnsupportedPstmts"), //$NON-NLS-1$
+			"3.1.7", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty enablePacketDebug = new BooleanConnectionProperty(
+			"enablePacketDebug", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.enablePacketDebug"), //$NON-NLS-1$
+			"3.1.3", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty enableQueryTimeouts = new BooleanConnectionProperty(
+			"enableQueryTimeouts", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.enableQueryTimeouts"), //$NON-NLS-1$
+			"5.0.6", //$NON-NLS-1$
+			PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
+			
+	private BooleanConnectionProperty explainSlowQueries = new BooleanConnectionProperty(
+			"explainSlowQueries", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.explainSlowQueries"), //$NON-NLS-1$
+			"3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	/** When failed-over, set connection to read-only? */
+	private BooleanConnectionProperty failOverReadOnly = new BooleanConnectionProperty(
+			"failOverReadOnly", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.failoverReadOnly"), //$NON-NLS-1$
+			"3.0.12", HA_CATEGORY, 2); //$NON-NLS-1$
+
+	private BooleanConnectionProperty gatherPerformanceMetrics = new BooleanConnectionProperty(
+			"gatherPerfMetrics", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.gatherPerfMetrics"), //$NON-NLS-1$
+			"3.1.2", DEBUGING_PROFILING_CATEGORY, 1); //$NON-NLS-1$
+
+	private BooleanConnectionProperty generateSimpleParameterMetadata = new BooleanConnectionProperty(
+			"generateSimpleParameterMetadata", false, Messages.getString("ConnectionProperties.generateSimpleParameterMetadata"), "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private boolean highAvailabilityAsBoolean = false;
+
+	private BooleanConnectionProperty holdResultsOpenOverStatementClose = new BooleanConnectionProperty(
+			"holdResultsOpenOverStatementClose", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.holdRSOpenOverStmtClose"), //$NON-NLS-1$
+			"3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty includeInnodbStatusInDeadlockExceptions = new BooleanConnectionProperty(
+			"includeInnodbStatusInDeadlockExceptions",
+			false,
+			"Include the output of \"SHOW ENGINE INNODB STATUS\" in exception messages when deadlock exceptions are detected?",
+			"5.0.7", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty ignoreNonTxTables = new BooleanConnectionProperty(
+			"ignoreNonTxTables", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.ignoreNonTxTables"), //$NON-NLS-1$
+			"3.0.9", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty initialTimeout = new IntegerConnectionProperty(
+			"initialTimeout", 2, 1, Integer.MAX_VALUE, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.initialTimeout"), //$NON-NLS-1$
+			"1.1", HA_CATEGORY, 5); //$NON-NLS-1$
+
+	private BooleanConnectionProperty isInteractiveClient = new BooleanConnectionProperty(
+			"interactiveClient", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.interactiveClient"), //$NON-NLS-1$
+			"3.1.0", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty jdbcCompliantTruncation = new BooleanConnectionProperty(
+			"jdbcCompliantTruncation", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.jdbcCompliantTruncation"), "3.1.2", MISC_CATEGORY, //$NON-NLS-1$ //$NON-NLS-2$
+			Integer.MIN_VALUE);
+
+	private boolean jdbcCompliantTruncationForReads = 
+		this.jdbcCompliantTruncation.getValueAsBoolean();
+	
+	protected MemorySizeConnectionProperty largeRowSizeThreshold = new MemorySizeConnectionProperty("largeRowSizeThreshold",
+			2048, 0, Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.largeRowSizeThreshold"),
+			"5.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
+	
+	private StringConnectionProperty loadBalanceStrategy = new StringConnectionProperty(
+			"loadBalanceStrategy", //$NON-NLS-1$
+			"random", //$NON-NLS-1$
+			new String[] {"random", "bestResponseTime"}, //$NON-NLS-1$ //$NON-NLS-2$
+			Messages.getString("ConnectionProperties.loadBalanceStrategy"), //$NON-NLS-1$
+			"5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private StringConnectionProperty localSocketAddress = new StringConnectionProperty("localSocketAddress", //$NON-NLS-1$
+			null, Messages.getString("ConnectionProperties.localSocketAddress"), //$NON-NLS-1$
+			"5.0.5", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private MemorySizeConnectionProperty locatorFetchBufferSize = new MemorySizeConnectionProperty(
+			"locatorFetchBufferSize", //$NON-NLS-1$
+			1024 * 1024,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.locatorFetchBufferSize"), //$NON-NLS-1$
+			"3.2.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private StringConnectionProperty loggerClassName = new StringConnectionProperty(
+			"logger", STANDARD_LOGGER_NAME, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.logger", new Object[] {Log.class.getName(), STANDARD_LOGGER_NAME}), //$NON-NLS-1$
+					 "3.1.1", DEBUGING_PROFILING_CATEGORY, //$NON-NLS-1$ //$NON-NLS-2$
+			0);
+
+	private BooleanConnectionProperty logSlowQueries = new BooleanConnectionProperty(
+			"logSlowQueries", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.logSlowQueries"), //$NON-NLS-1$
+			"3.1.2", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty logXaCommands = new BooleanConnectionProperty(
+			"logXaCommands", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.logXaCommands"), //$NON-NLS-1$
+			"5.0.5", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty maintainTimeStats = new BooleanConnectionProperty(
+			"maintainTimeStats", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.maintainTimeStats"), "3.1.9", PERFORMANCE_CATEGORY, //$NON-NLS-1$ //$NON-NLS-2$
+			Integer.MAX_VALUE);
+
+	private boolean maintainTimeStatsAsBoolean = true;
+
+	private IntegerConnectionProperty maxQuerySizeToLog = new IntegerConnectionProperty(
+			"maxQuerySizeToLog", //$NON-NLS-1$
+			2048,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.maxQuerySizeToLog"), //$NON-NLS-1$
+			"3.1.3", DEBUGING_PROFILING_CATEGORY, 4); //$NON-NLS-1$
+
+	private IntegerConnectionProperty maxReconnects = new IntegerConnectionProperty(
+			"maxReconnects", //$NON-NLS-1$
+			3,
+			1,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.maxReconnects"), //$NON-NLS-1$
+			"1.1", HA_CATEGORY, 4); //$NON-NLS-1$
+
+	private IntegerConnectionProperty maxRows = new IntegerConnectionProperty(
+			"maxRows", -1, -1, Integer.MAX_VALUE, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.maxRows"), //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.allVersions"), MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private int maxRowsAsInt = -1;
+
+	private IntegerConnectionProperty metadataCacheSize = new IntegerConnectionProperty(
+			"metadataCacheSize", //$NON-NLS-1$
+			50,
+			1,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.metadataCacheSize"), //$NON-NLS-1$
+			"3.1.1", PERFORMANCE_CATEGORY, 5); //$NON-NLS-1$
+	
+	private IntegerConnectionProperty netTimeoutForStreamingResults = new IntegerConnectionProperty(
+			"netTimeoutForStreamingResults", 600, //$NON-NLS-1$
+			0, Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.netTimeoutForStreamingResults"), //$NON-NLS-1$
+			"5.1.0", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty noAccessToProcedureBodies = new BooleanConnectionProperty(
+			"noAccessToProcedureBodies",
+			false,
+			"When determining procedure parameter types for CallableStatements, and the connected user "
+			+ " can't access procedure bodies through \"SHOW CREATE PROCEDURE\" or select on mysql.proc "
+			+ " should the driver instead create basic metadata (all parameters reported as IN VARCHARs,"
+			+ " but allowing registerOutParameter() to be called on them anyway) instead "
+			+ " of throwing an exception?",
+			"5.0.3", MISC_CATEGORY, Integer.MIN_VALUE);
+			
+	private BooleanConnectionProperty noDatetimeStringSync = new BooleanConnectionProperty(
+			"noDatetimeStringSync", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.noDatetimeStringSync"), //$NON-NLS-1$
+			"3.1.7", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty noTimezoneConversionForTimeType = new BooleanConnectionProperty(
+			"noTimezoneConversionForTimeType", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.noTzConversionForTimeType"), //$NON-NLS-1$
+			"5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty nullCatalogMeansCurrent = new BooleanConnectionProperty(
+			"nullCatalogMeansCurrent", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.nullCatalogMeansCurrent"), //$NON-NLS-1$
+			"3.1.8", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty nullNamePatternMatchesAll = new BooleanConnectionProperty(
+			"nullNamePatternMatchesAll", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.nullNamePatternMatchesAll"), //$NON-NLS-1$
+			"3.1.8", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty packetDebugBufferSize = new IntegerConnectionProperty(
+			"packetDebugBufferSize", //$NON-NLS-1$
+			20,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.packetDebugBufferSize"), //$NON-NLS-1$
+			"3.1.3", DEBUGING_PROFILING_CATEGORY, 7); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty padCharsWithSpace = new BooleanConnectionProperty(
+			"padCharsWithSpace", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.padCharsWithSpace"), //$NON-NLS-1$
+			"5.0.6", //$NON-NLS-1$
+			MISC_CATEGORY,
+			Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty paranoid = new BooleanConnectionProperty(
+			"paranoid", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.paranoid"), //$NON-NLS-1$
+			"3.0.1", SECURITY_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty pedantic = new BooleanConnectionProperty(
+			"pedantic", false, Messages.getString("ConnectionProperties.pedantic"), "3.0.0", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			MISC_CATEGORY, Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty pinGlobalTxToPhysicalConnection = new BooleanConnectionProperty(
+			"pinGlobalTxToPhysicalConnection", false, Messages.getString("ConnectionProperties.pinGlobalTxToPhysicalConnection"), //$NON-NLS-1$
+			"5.0.1", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty populateInsertRowWithDefaultValues = new BooleanConnectionProperty(
+			"populateInsertRowWithDefaultValues", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.populateInsertRowWithDefaultValues"), //$NON-NLS-1$
+			"5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private IntegerConnectionProperty preparedStatementCacheSize = new IntegerConnectionProperty(
+			"prepStmtCacheSize", 25, 0, Integer.MAX_VALUE, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.prepStmtCacheSize"), //$NON-NLS-1$
+			"3.0.10", PERFORMANCE_CATEGORY, 10); //$NON-NLS-1$
+
+	private IntegerConnectionProperty preparedStatementCacheSqlLimit = new IntegerConnectionProperty(
+			"prepStmtCacheSqlLimit", //$NON-NLS-1$
+			256,
+			1,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.prepStmtCacheSqlLimit"), //$NON-NLS-1$
+			"3.0.10", PERFORMANCE_CATEGORY, 11); //$NON-NLS-1$
+
+	private BooleanConnectionProperty processEscapeCodesForPrepStmts = 
+		new BooleanConnectionProperty("processEscapeCodesForPrepStmts", //$NON-NLS-1$
+				true,
+				Messages.getString("ConnectionProperties.processEscapeCodesForPrepStmts"), //$NON-NLS-1$
+				"3.1.12", //$NON-NLS-1$
+				MISC_CATEGORY, Integer.MIN_VALUE);
+	
+	private StringConnectionProperty profileSql = new StringConnectionProperty(
+			"profileSql", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.profileSqlDeprecated"), //$NON-NLS-1$
+			"2.0.14", DEBUGING_PROFILING_CATEGORY, 3); //$NON-NLS-1$
+
+	private BooleanConnectionProperty profileSQL = new BooleanConnectionProperty(
+			"profileSQL", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.profileSQL"), //$NON-NLS-1$
+			"3.1.0", DEBUGING_PROFILING_CATEGORY, 1); //$NON-NLS-1$
+
+	private boolean profileSQLAsBoolean = false;
+
+	private StringConnectionProperty propertiesTransform = new StringConnectionProperty(
+			NonRegisteringDriver.PROPERTIES_TRANSFORM_KEY,
+			null,
+			Messages.getString("ConnectionProperties.connectionPropertiesTransform"), //$NON-NLS-1$
+			"3.1.4", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty queriesBeforeRetryMaster = new IntegerConnectionProperty(
+			"queriesBeforeRetryMaster", //$NON-NLS-1$
+			50,
+			1,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.queriesBeforeRetryMaster"), //$NON-NLS-1$
+			"3.0.2", HA_CATEGORY, 7); //$NON-NLS-1$
+
+	private BooleanConnectionProperty reconnectAtTxEnd = new BooleanConnectionProperty(
+			"reconnectAtTxEnd", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.reconnectAtTxEnd"), "3.0.10", //$NON-NLS-1$ //$NON-NLS-2$
+			HA_CATEGORY, 4);
+
+	private boolean reconnectTxAtEndAsBoolean = false;
+
+	private BooleanConnectionProperty relaxAutoCommit = new BooleanConnectionProperty(
+			"relaxAutoCommit", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.relaxAutoCommit"), //$NON-NLS-1$
+			"2.0.13", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty reportMetricsIntervalMillis = new IntegerConnectionProperty(
+			"reportMetricsIntervalMillis", //$NON-NLS-1$
+			30000,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.reportMetricsIntervalMillis"), //$NON-NLS-1$
+			"3.1.2", DEBUGING_PROFILING_CATEGORY, 3); //$NON-NLS-1$
+
+	private BooleanConnectionProperty requireSSL = new BooleanConnectionProperty(
+			"requireSSL", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.requireSSL"), //$NON-NLS-1$
+			"3.1.0", SECURITY_CATEGORY, 3); //$NON-NLS-1$
+
+	private StringConnectionProperty resourceId = new StringConnectionProperty(
+			"resourceId", //$NON-NLS-1$
+			null, Messages.getString("ConnectionProperties.resourceId"), //$NON-NLS-1$
+			"5.0.1", //$NON-NLS-1$
+			HA_CATEGORY,
+			Integer.MIN_VALUE);
+	
+	private IntegerConnectionProperty resultSetSizeThreshold = new IntegerConnectionProperty("resultSetSizeThreshold", 100, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.resultSetSizeThreshold"), "5.0.5", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$ //$NON-NLS-2$
+			
+	private BooleanConnectionProperty retainStatementAfterResultSetClose = new BooleanConnectionProperty(
+			"retainStatementAfterResultSetClose", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.retainStatementAfterResultSetClose"), //$NON-NLS-1$
+			"3.1.11", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty rewriteBatchedStatements = new BooleanConnectionProperty(
+			"rewriteBatchedStatements", //$NON-NLS-1$
+			false, 
+			Messages.getString("ConnectionProperties.rewriteBatchedStatements"), //$NON-NLS-1$
+			"3.1.13", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty rollbackOnPooledClose = new BooleanConnectionProperty(
+			"rollbackOnPooledClose", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.rollbackOnPooledClose"), //$NON-NLS-1$
+			"3.0.15", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty roundRobinLoadBalance = new BooleanConnectionProperty(
+			"roundRobinLoadBalance", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.roundRobinLoadBalance"), //$NON-NLS-1$
+			"3.1.2", HA_CATEGORY, 5); //$NON-NLS-1$
+
+	private BooleanConnectionProperty runningCTS13 = new BooleanConnectionProperty(
+			"runningCTS13", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.runningCTS13"), //$NON-NLS-1$
+			"3.1.7", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private IntegerConnectionProperty secondsBeforeRetryMaster = new IntegerConnectionProperty(
+			"secondsBeforeRetryMaster", //$NON-NLS-1$
+			30,
+			1,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.secondsBeforeRetryMaster"), //$NON-NLS-1$
+			"3.0.2", HA_CATEGORY, 8); //$NON-NLS-1$
+
+	private StringConnectionProperty serverTimezone = new StringConnectionProperty(
+			"serverTimezone", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.serverTimezone"), //$NON-NLS-1$
+			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private StringConnectionProperty sessionVariables = new StringConnectionProperty(
+			"sessionVariables", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.sessionVariables"), "3.1.8", //$NON-NLS-1$ //$NON-NLS-2$
+			MISC_CATEGORY, Integer.MAX_VALUE);
+
+	private IntegerConnectionProperty slowQueryThresholdMillis = new IntegerConnectionProperty(
+			"slowQueryThresholdMillis", //$NON-NLS-1$
+			2000,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.slowQueryThresholdMillis"), //$NON-NLS-1$
+			"3.1.2", DEBUGING_PROFILING_CATEGORY, 9); //$NON-NLS-1$
+	
+	private LongConnectionProperty slowQueryThresholdNanos = new LongConnectionProperty(
+			"slowQueryThresholdNanos", //$NON-NLS-1$
+			0,
+			Messages.getString("ConnectionProperties.slowQueryThresholdNanos"), //$NON-NLS-1$
+			"5.0.7", //$NON-NLS-1$
+			DEBUGING_PROFILING_CATEGORY,
+			10);
+	
+	private StringConnectionProperty socketFactoryClassName = new StringConnectionProperty(
+			"socketFactory", //$NON-NLS-1$
+			StandardSocketFactory.class.getName(),
+			Messages.getString("ConnectionProperties.socketFactory"), //$NON-NLS-1$
+			"3.0.3", CONNECTION_AND_AUTH_CATEGORY, 4); //$NON-NLS-1$
+
+	private IntegerConnectionProperty socketTimeout = new IntegerConnectionProperty(
+			"socketTimeout", //$NON-NLS-1$
+			0,
+			0,
+			Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.socketTimeout"), //$NON-NLS-1$
+			"3.0.1", CONNECTION_AND_AUTH_CATEGORY, 10); //$NON-NLS-1$
+	
+	private StringConnectionProperty statementInterceptors = new StringConnectionProperty("statementInterceptors", //$NON-NLS-1$
+			null, Messages.getString("ConnectionProperties.statementInterceptors"), "5.1.1", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$ //$NON-NLS-2$
+	
+	private BooleanConnectionProperty strictFloatingPoint = new BooleanConnectionProperty(
+			"strictFloatingPoint", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.strictFloatingPoint"), "3.0.0", //$NON-NLS-1$ //$NON-NLS-2$
+			MISC_CATEGORY, Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty strictUpdates = new BooleanConnectionProperty(
+			"strictUpdates", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.strictUpdates"), //$NON-NLS-1$
+			"3.0.4", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty overrideSupportsIntegrityEnhancementFacility =
+		new BooleanConnectionProperty("overrideSupportsIntegrityEnhancementFacility", //$NON-NLS-1$
+				false,
+				Messages.getString("ConnectionProperties.overrideSupportsIEF"), //$NON-NLS-1$
+				"3.1.12", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty tcpNoDelay = new BooleanConnectionProperty(
+			StandardSocketFactory.TCP_NO_DELAY_PROPERTY_NAME,
+			Boolean.valueOf(StandardSocketFactory.TCP_NO_DELAY_DEFAULT_VALUE).booleanValue(),
+			Messages.getString("ConnectionProperties.tcpNoDelay"), //$NON-NLS-1$
+			"5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty tcpKeepAlive = new BooleanConnectionProperty(
+			StandardSocketFactory.TCP_KEEP_ALIVE_PROPERTY_NAME,
+			Boolean.valueOf(StandardSocketFactory.TCP_KEEP_ALIVE_DEFAULT_VALUE).booleanValue(),
+			Messages.getString("ConnectionProperties.tcpKeepAlive"), //$NON-NLS-1$
+			"5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private IntegerConnectionProperty tcpRcvBuf = new IntegerConnectionProperty(
+			StandardSocketFactory.TCP_RCV_BUF_PROPERTY_NAME,
+			Integer.parseInt(StandardSocketFactory.TCP_RCV_BUF_DEFAULT_VALUE),
+			0, Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.tcpSoRcvBuf"), //$NON-NLS-1$
+			"5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private IntegerConnectionProperty tcpSndBuf = new IntegerConnectionProperty(
+			StandardSocketFactory.TCP_SND_BUF_PROPERTY_NAME,
+			Integer.parseInt(StandardSocketFactory.TCP_SND_BUF_DEFAULT_VALUE),
+			0, Integer.MAX_VALUE,
+			Messages.getString("ConnectionProperties.tcpSoSndBuf"), //$NON-NLS-1$
+			"5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+			
+	private IntegerConnectionProperty tcpTrafficClass = new IntegerConnectionProperty(
+			StandardSocketFactory.TCP_TRAFFIC_CLASS_PROPERTY_NAME,
+			Integer.parseInt(StandardSocketFactory.TCP_TRAFFIC_CLASS_DEFAULT_VALUE),
+			0, 255,
+			Messages.getString("ConnectionProperties.tcpTrafficClass"), //$NON-NLS-1$
+			"5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty tinyInt1isBit = new BooleanConnectionProperty(
+			"tinyInt1isBit", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.tinyInt1isBit"), //$NON-NLS-1$
+			"3.0.16", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty traceProtocol = new BooleanConnectionProperty(
+			"traceProtocol", false, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.traceProtocol"), "3.1.2", //$NON-NLS-1$ //$NON-NLS-2$
+			DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty treatUtilDateAsTimestamp = new BooleanConnectionProperty(
+			"treatUtilDateAsTimestamp", true, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.treatUtilDateAsTimestamp"), //$NON-NLS-1$
+			"5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty transformedBitIsBoolean = new BooleanConnectionProperty(
+			"transformedBitIsBoolean", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.transformedBitIsBoolean"), //$NON-NLS-1$
+			"3.1.9", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useBlobToStoreUTF8OutsideBMP = new BooleanConnectionProperty(
+			"useBlobToStoreUTF8OutsideBMP",
+			false,
+			Messages.getString("ConnectionProperties.useBlobToStoreUTF8OutsideBMP"), //$NON-NLS-1$
+			"5.1.3", MISC_CATEGORY, 128);
+	
+	private StringConnectionProperty utf8OutsideBmpExcludedColumnNamePattern = new StringConnectionProperty(
+			"utf8OutsideBmpExcludedColumnNamePattern",
+			null,
+			Messages.getString("ConnectionProperties.utf8OutsideBmpExcludedColumnNamePattern"), //$NON-NLS-1$
+			"5.1.3", MISC_CATEGORY, 129);
+	
+	private StringConnectionProperty utf8OutsideBmpIncludedColumnNamePattern = new StringConnectionProperty(
+			"utf8OutsideBmpIncludedColumnNamePattern",
+			null,
+			Messages.getString("ConnectionProperties.utf8OutsideBmpIncludedColumnNamePattern"), //$NON-NLS-1$
+			"5.1.3", MISC_CATEGORY, 129);
+	
+	private BooleanConnectionProperty useCompression = new BooleanConnectionProperty(
+			"useCompression", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useCompression"), //$NON-NLS-1$
+			"3.0.17", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private StringConnectionProperty useConfigs = new StringConnectionProperty(
+			"useConfigs", //$NON-NLS-1$
+			null,
+			Messages.getString("ConnectionProperties.useConfigs"), //$NON-NLS-1$
+			"3.1.5", CONNECTION_AND_AUTH_CATEGORY, Integer.MAX_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useCursorFetch = new BooleanConnectionProperty(
+			"useCursorFetch", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useCursorFetch"), //$NON-NLS-1$
+			"5.0.0", PERFORMANCE_CATEGORY, Integer.MAX_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty useDynamicCharsetInfo = new BooleanConnectionProperty(
+			"useDynamicCharsetInfo", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useDynamicCharsetInfo") //$NON-NLS-1$
+			, "5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty useDirectRowUnpack = new BooleanConnectionProperty(
+			"useDirectRowUnpack",
+			true, "Use newer result set row unpacking code that skips a copy from network buffers "
+			+ " to a MySQL packet instance and instead reads directly into the result set row data buffers.",
+			"5.1.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty useFastIntParsing = new BooleanConnectionProperty(
+			"useFastIntParsing", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useFastIntParsing"), //$NON-NLS-1$
+			"3.1.4", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useFastDateParsing = new BooleanConnectionProperty(
+			"useFastDateParsing", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useFastDateParsing"), //$NON-NLS-1$
+			"5.0.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty useHostsInPrivileges = new BooleanConnectionProperty(
+			"useHostsInPrivileges", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useHostsInPrivileges"), //$NON-NLS-1$
+			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	private BooleanConnectionProperty useInformationSchema = new BooleanConnectionProperty(
+			"useInformationSchema", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useInformationSchema"), //$NON-NLS-1$
+			"5.0.0", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	private BooleanConnectionProperty useJDBCCompliantTimezoneShift = new BooleanConnectionProperty(
+			"useJDBCCompliantTimezoneShift", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useJDBCCompliantTimezoneShift"), //$NON-NLS-1$
+			"5.0.0", //$NON-NLS-1$
+			MISC_CATEGORY, Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty useLocalSessionState = new BooleanConnectionProperty(
+			"useLocalSessionState", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useLocalSessionState"), //$NON-NLS-1$
+			"3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty useNanosForElapsedTime = new BooleanConnectionProperty(
+			"useNanosForElapsedTime", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useNanosForElapsedTime"), //$NON-NLS-1$
+			"5.0.7", //$NON-NLS-1$
+			DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty useOldAliasMetadataBehavior = new BooleanConnectionProperty(
+			"useOldAliasMetadataBehavior", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useOldAliasMetadataBehavior"), //$NON-NLS-1$
+		    "5.0.4", //$NON-NLS-1$
+		    MISC_CATEGORY,
+		    Integer.MIN_VALUE);
+	
+	private BooleanConnectionProperty useOldUTF8Behavior = new BooleanConnectionProperty(
+			"useOldUTF8Behavior", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useOldUtf8Behavior"), //$NON-NLS-1$
+			"3.1.6", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private boolean useOldUTF8BehaviorAsBoolean = false;
+
+	private BooleanConnectionProperty useOnlyServerErrorMessages = new BooleanConnectionProperty(
+			"useOnlyServerErrorMessages", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useOnlyServerErrorMessages"), //$NON-NLS-1$
+			"3.0.15", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useReadAheadInput = new BooleanConnectionProperty(
+			"useReadAheadInput", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useReadAheadInput"), //$NON-NLS-1$
+			"3.1.5", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useSqlStateCodes = new BooleanConnectionProperty(
+			"useSqlStateCodes", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useSqlStateCodes"), //$NON-NLS-1$
+			"3.1.3", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useSSL = new BooleanConnectionProperty(
+			"useSSL", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useSSL"), //$NON-NLS-1$
+			"3.0.2", SECURITY_CATEGORY, 2); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useSSPSCompatibleTimezoneShift = new BooleanConnectionProperty(
+			"useSSPSCompatibleTimezoneShift", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useSSPSCompatibleTimezoneShift"), //$NON-NLS-1$
+			"5.0.5", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+	
+	private BooleanConnectionProperty useStreamLengthsInPrepStmts = new BooleanConnectionProperty(
+			"useStreamLengthsInPrepStmts", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useStreamLengthsInPrepStmts"), //$NON-NLS-1$
+			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useTimezone = new BooleanConnectionProperty(
+			"useTimezone", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useTimezone"), //$NON-NLS-1$
+			"3.0.2", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useUltraDevWorkAround = new BooleanConnectionProperty(
+			"ultraDevHack", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.ultraDevHack"), //$NON-NLS-1$
+			"2.0.3", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useUnbufferedInput = new BooleanConnectionProperty(
+			"useUnbufferedInput", true, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.useUnbufferedInput"), //$NON-NLS-1$
+			"3.0.11", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private BooleanConnectionProperty useUnicode = new BooleanConnectionProperty(
+			"useUnicode", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.useUnicode"), //$NON-NLS-1$
+			"1.1g", MISC_CATEGORY, 0); //$NON-NLS-1$
+
+	// Cache these values, they are 'hot'
+	private boolean useUnicodeAsBoolean = true;
+
+	private BooleanConnectionProperty useUsageAdvisor = new BooleanConnectionProperty(
+			"useUsageAdvisor", //$NON-NLS-1$
+			false,
+			Messages.getString("ConnectionProperties.useUsageAdvisor"), //$NON-NLS-1$
+			"3.1.1", DEBUGING_PROFILING_CATEGORY, 10); //$NON-NLS-1$
+
+	private boolean useUsageAdvisorAsBoolean = false;
+
+	private BooleanConnectionProperty yearIsDateType = new BooleanConnectionProperty(
+			"yearIsDateType", //$NON-NLS-1$
+			true,
+			Messages.getString("ConnectionProperties.yearIsDateType"), //$NON-NLS-1$
+			"3.1.9", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$
+
+	private StringConnectionProperty zeroDateTimeBehavior = new StringConnectionProperty(
+			"zeroDateTimeBehavior", //$NON-NLS-1$
+			ZERO_DATETIME_BEHAVIOR_EXCEPTION,
+			new String[] { ZERO_DATETIME_BEHAVIOR_EXCEPTION,
+					ZERO_DATETIME_BEHAVIOR_ROUND,
+					ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL },
+			Messages.getString("ConnectionProperties.zeroDateTimeBehavior", new Object[] {ZERO_DATETIME_BEHAVIOR_EXCEPTION, ZERO_DATETIME_BEHAVIOR_ROUND,ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL}),  //$NON-NLS-1$
+			"3.1.4", //$NON-NLS-1$ //$NON-NLS-2$
+			MISC_CATEGORY, Integer.MIN_VALUE);
+
+	private BooleanConnectionProperty useJvmCharsetConverters = new BooleanConnectionProperty("useJvmCharsetConverters", //$NON-NLS-1$
+			false, Messages.getString("ConnectionProperties.useJvmCharsetConverters"), "5.0.1", PERFORMANCE_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$ //$NON-NLS-2$
+	
+	private BooleanConnectionProperty useGmtMillisForDatetimes = new BooleanConnectionProperty("useGmtMillisForDatetimes", false, Messages.getString("ConnectionProperties.useGmtMillisForDatetimes"), "3.1.12", MISC_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+	private BooleanConnectionProperty dumpMetadataOnColumnNotFound = new BooleanConnectionProperty("dumpMetadataOnColumnNotFound", false, Messages.getString("ConnectionProperties.dumpMetadataOnColumnNotFound"), "3.1.13", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+	// SSL Options
+	
+	private StringConnectionProperty clientCertificateKeyStoreUrl = new StringConnectionProperty(
+			"clientCertificateKeyStoreUrl", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.clientCertificateKeyStoreUrl"), "5.1.0", //$NON-NLS-1$ //$NON-NLS-2$
+			SECURITY_CATEGORY, Integer.MAX_VALUE);
+	
+	private StringConnectionProperty trustCertificateKeyStoreUrl = new StringConnectionProperty(
+			"trustCertificateKeyStoreUrl", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.trustCertificateKeyStoreUrl"), "5.1.0", //$NON-NLS-1$ //$NON-NLS-2$
+			SECURITY_CATEGORY, Integer.MAX_VALUE);
+	
+	private StringConnectionProperty clientCertificateKeyStoreType = new StringConnectionProperty(
+			"clientCertificateKeyStoreType", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.clientCertificateKeyStoreType"), "5.1.0", //$NON-NLS-1$ //$NON-NLS-2$
+			SECURITY_CATEGORY, Integer.MAX_VALUE);
+	
+	private StringConnectionProperty clientCertificateKeyStorePassword = new StringConnectionProperty(
+			"clientCertificateKeyStorePassword", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.clientCertificateKeyStorePassword"), "5.1.0", //$NON-NLS-1$ //$NON-NLS-2$
+			SECURITY_CATEGORY, Integer.MAX_VALUE);
+	
+	private StringConnectionProperty trustCertificateKeyStoreType = new StringConnectionProperty(
+			"trustCertificateKeyStoreType", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.trustCertificateKeyStoreType"), "5.1.0", //$NON-NLS-1$ //$NON-NLS-2$
+			SECURITY_CATEGORY, Integer.MAX_VALUE);
+	
+	private StringConnectionProperty trustCertificateKeyStorePassword = new StringConnectionProperty(
+			"trustCertificateKeyStorePassword", null, //$NON-NLS-1$
+			Messages.getString("ConnectionProperties.trustCertificateKeyStorePassword"), "5.1.0", //$NON-NLS-1$ //$NON-NLS-2$
+			SECURITY_CATEGORY, Integer.MAX_VALUE);
+	
+	protected DriverPropertyInfo[] exposeAsDriverPropertyInfoInternal(
+			Properties info, int slotsToReserve) throws SQLException {
+		initializeProperties(info);
+
+		int numProperties = PROPERTY_LIST.size();
+
+		int listSize = numProperties + slotsToReserve;
+
+		DriverPropertyInfo[] driverProperties = new DriverPropertyInfo[listSize];
+
+		for (int i = slotsToReserve; i < listSize; i++) {
+			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
+					.get(i - slotsToReserve);
+
+			try {
+				ConnectionProperty propToExpose = (ConnectionProperty) propertyField
+						.get(this);
+
+				if (info != null) {
+					propToExpose.initializeFrom(info);
+				}
+
+				
+				driverProperties[i] = propToExpose.getAsDriverPropertyInfo();
+			} catch (IllegalAccessException iae) {
+				throw SQLError.createSQLException(Messages.getString("ConnectionProperties.InternalPropertiesFailure"), //$NON-NLS-1$
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+
+		return driverProperties;
+	}
+
+	protected Properties exposeAsProperties(Properties info)
+			throws SQLException {
+		if (info == null) {
+			info = new Properties();
+		}
+
+		int numPropertiesToSet = PROPERTY_LIST.size();
+
+		for (int i = 0; i < numPropertiesToSet; i++) {
+			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
+					.get(i);
+
+			try {
+				ConnectionProperty propToGet = (ConnectionProperty) propertyField
+						.get(this);
+
+				Object propValue = propToGet.getValueAsObject();
+
+				if (propValue != null) {
+					info.setProperty(propToGet.getPropertyName(), propValue
+							.toString());
+				}
+			} catch (IllegalAccessException iae) {
+				throw SQLError.createSQLException("Internal properties failure", //$NON-NLS-1$
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+
+		return info;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#exposeAsXml()
+	 */
+	public String exposeAsXml() throws SQLException {
+		StringBuffer xmlBuf = new StringBuffer();
+		xmlBuf.append("<ConnectionProperties>"); //$NON-NLS-1$
+
+		int numPropertiesToSet = PROPERTY_LIST.size();
+
+		int numCategories = PROPERTY_CATEGORIES.length;
+
+		Map propertyListByCategory = new HashMap();
+
+		for (int i = 0; i < numCategories; i++) {
+			propertyListByCategory.put(PROPERTY_CATEGORIES[i], new Map[] {
+					new TreeMap(), new TreeMap() });
+		}
+
+		//
+		// The following properties are not exposed as 'normal' properties, but
+		// they are
+		// settable nonetheless, so we need to have them documented, make sure
+		// that they sort 'first' as #1 and #2 in the category
+		//
+		StringConnectionProperty userProp = new StringConnectionProperty(
+				NonRegisteringDriver.USER_PROPERTY_KEY, null,
+				Messages.getString("ConnectionProperties.Username"), Messages.getString("ConnectionProperties.allVersions"), CONNECTION_AND_AUTH_CATEGORY, //$NON-NLS-1$ //$NON-NLS-2$
+				Integer.MIN_VALUE + 1);
+		StringConnectionProperty passwordProp = new StringConnectionProperty(
+				NonRegisteringDriver.PASSWORD_PROPERTY_KEY, null,
+				Messages.getString("ConnectionProperties.Password"), Messages.getString("ConnectionProperties.allVersions"), //$NON-NLS-1$ //$NON-NLS-2$
+				CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE + 2);
+
+		Map[] connectionSortMaps = (Map[]) propertyListByCategory
+				.get(CONNECTION_AND_AUTH_CATEGORY);
+		TreeMap userMap = new TreeMap();
+		userMap.put(userProp.getPropertyName(), userProp);
+		
+		connectionSortMaps[0].put(new Integer(userProp.getOrder()), userMap);
+		
+		TreeMap passwordMap = new TreeMap();
+		passwordMap.put(passwordProp.getPropertyName(), passwordProp);
+		
+		connectionSortMaps[0]
+				.put(new Integer(passwordProp.getOrder()), passwordMap);
+
+		try {
+			for (int i = 0; i < numPropertiesToSet; i++) {
+				java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
+						.get(i);
+				ConnectionProperty propToGet = (ConnectionProperty) propertyField
+						.get(this);
+				Map[] sortMaps = (Map[]) propertyListByCategory.get(propToGet
+						.getCategoryName());
+				int orderInCategory = propToGet.getOrder();
+
+				if (orderInCategory == Integer.MIN_VALUE) {
+					sortMaps[1].put(propToGet.getPropertyName(), propToGet);
+				} else {
+					Integer order = new Integer(orderInCategory);
+					
+					Map orderMap = (Map)sortMaps[0].get(order);
+					
+					if (orderMap == null) {
+						orderMap = new TreeMap();
+						sortMaps[0].put(order, orderMap);
+					}
+					
+					orderMap.put(propToGet.getPropertyName(), propToGet);
+				}
+			}
+
+			for (int j = 0; j < numCategories; j++) {
+				Map[] sortMaps = (Map[]) propertyListByCategory
+						.get(PROPERTY_CATEGORIES[j]);
+				Iterator orderedIter = sortMaps[0].values().iterator();
+				Iterator alphaIter = sortMaps[1].values().iterator();
+
+				xmlBuf.append("\n <PropertyCategory name=\""); //$NON-NLS-1$
+				xmlBuf.append(PROPERTY_CATEGORIES[j]);
+				xmlBuf.append("\">"); //$NON-NLS-1$
+
+				while (orderedIter.hasNext()) {
+					Iterator orderedAlphaIter = ((Map)orderedIter.next()).values().iterator();
+					
+					while (orderedAlphaIter.hasNext()) {
+						ConnectionProperty propToGet = (ConnectionProperty) orderedAlphaIter
+								.next();
+						
+						xmlBuf.append("\n  <Property name=\""); //$NON-NLS-1$
+						xmlBuf.append(propToGet.getPropertyName());
+						xmlBuf.append("\" required=\""); //$NON-NLS-1$
+						xmlBuf.append(propToGet.required ? "Yes" : "No"); //$NON-NLS-1$ //$NON-NLS-2$
+	
+						xmlBuf.append("\" default=\""); //$NON-NLS-1$
+	
+						if (propToGet.getDefaultValue() != null) {
+							xmlBuf.append(propToGet.getDefaultValue());
+						}
+	
+						xmlBuf.append("\" sortOrder=\""); //$NON-NLS-1$
+						xmlBuf.append(propToGet.getOrder());
+						xmlBuf.append("\" since=\""); //$NON-NLS-1$
+						xmlBuf.append(propToGet.sinceVersion);
+						xmlBuf.append("\">\n"); //$NON-NLS-1$
+						xmlBuf.append("    "); //$NON-NLS-1$
+						xmlBuf.append(propToGet.description);
+						xmlBuf.append("\n  </Property>"); //$NON-NLS-1$
+					}
+				}
+
+				while (alphaIter.hasNext()) {
+					ConnectionProperty propToGet = (ConnectionProperty) alphaIter
+							.next();
+					
+					xmlBuf.append("\n  <Property name=\""); //$NON-NLS-1$
+					xmlBuf.append(propToGet.getPropertyName());
+					xmlBuf.append("\" required=\""); //$NON-NLS-1$
+					xmlBuf.append(propToGet.required ? "Yes" : "No"); //$NON-NLS-1$ //$NON-NLS-2$
+
+					xmlBuf.append("\" default=\""); //$NON-NLS-1$
+
+					if (propToGet.getDefaultValue() != null) {
+						xmlBuf.append(propToGet.getDefaultValue());
+					}
+
+					xmlBuf.append("\" sortOrder=\"alpha\" since=\""); //$NON-NLS-1$
+					xmlBuf.append(propToGet.sinceVersion);
+					xmlBuf.append("\">\n"); //$NON-NLS-1$
+					xmlBuf.append("    "); //$NON-NLS-1$
+					xmlBuf.append(propToGet.description);
+					xmlBuf.append("\n  </Property>"); //$NON-NLS-1$
+				}
+
+				xmlBuf.append("\n </PropertyCategory>"); //$NON-NLS-1$
+			}
+		} catch (IllegalAccessException iae) {
+			throw SQLError.createSQLException("Internal properties failure", //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		xmlBuf.append("\n</ConnectionProperties>"); //$NON-NLS-1$
+
+		return xmlBuf.toString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAllowLoadLocalInfile()
+	 */
+	public boolean getAllowLoadLocalInfile() {
+		return this.allowLoadLocalInfile.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAllowMultiQueries()
+	 */
+	public boolean getAllowMultiQueries() {
+		return this.allowMultiQueries.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAllowNanAndInf()
+	 */
+	public boolean getAllowNanAndInf() {
+		return allowNanAndInf.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAllowUrlInLocalInfile()
+	 */
+	public boolean getAllowUrlInLocalInfile() {
+		return this.allowUrlInLocalInfile.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAlwaysSendSetIsolation()
+	 */
+	public boolean getAlwaysSendSetIsolation() {
+		return this.alwaysSendSetIsolation.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAutoDeserialize()
+	 */
+	public boolean getAutoDeserialize() {
+		return autoDeserialize.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAutoGenerateTestcaseScript()
+	 */
+	public boolean getAutoGenerateTestcaseScript() {
+		return this.autoGenerateTestcaseScriptAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAutoReconnectForPools()
+	 */
+	public boolean getAutoReconnectForPools() {
+		return this.autoReconnectForPoolsAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getBlobSendChunkSize()
+	 */
+	public int getBlobSendChunkSize() {
+		return blobSendChunkSize.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCacheCallableStatements()
+	 */
+	public boolean getCacheCallableStatements() {
+		return this.cacheCallableStatements.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCachePreparedStatements()
+	 */
+	public boolean getCachePreparedStatements() {
+		return ((Boolean) this.cachePreparedStatements.getValueAsObject())
+				.booleanValue();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCacheResultSetMetadata()
+	 */
+	public boolean getCacheResultSetMetadata() {
+		return this.cacheResultSetMetaDataAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCacheServerConfiguration()
+	 */
+	public boolean getCacheServerConfiguration() {
+		return cacheServerConfiguration.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCallableStatementCacheSize()
+	 */
+	public int getCallableStatementCacheSize() {
+		return this.callableStatementCacheSize.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCapitalizeTypeNames()
+	 */
+	public boolean getCapitalizeTypeNames() {
+		return this.capitalizeTypeNames.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCharacterSetResults()
+	 */
+	public String getCharacterSetResults() {
+		return this.characterSetResults.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getClobberStreamingResults()
+	 */
+	public boolean getClobberStreamingResults() {
+		return this.clobberStreamingResults.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getClobCharacterEncoding()
+	 */
+	public String getClobCharacterEncoding() {
+		return this.clobCharacterEncoding.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getConnectionCollation()
+	 */
+	public String getConnectionCollation() {
+		return this.connectionCollation.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getConnectTimeout()
+	 */
+	public int getConnectTimeout() {
+		return this.connectTimeout.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getContinueBatchOnError()
+	 */
+	public boolean getContinueBatchOnError() {
+		return this.continueBatchOnError.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCreateDatabaseIfNotExist()
+	 */
+	public boolean getCreateDatabaseIfNotExist() {
+		return this.createDatabaseIfNotExist.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getDefaultFetchSize()
+	 */
+	public int getDefaultFetchSize() {
+		return this.defaultFetchSize.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getDontTrackOpenResources()
+	 */
+	public boolean getDontTrackOpenResources() {
+		return this.dontTrackOpenResources.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getDumpQueriesOnException()
+	 */
+	public boolean getDumpQueriesOnException() {
+		return this.dumpQueriesOnException.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getDynamicCalendars()
+	 */
+	public boolean getDynamicCalendars() {
+		return this.dynamicCalendars.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getElideSetAutoCommits()
+	 */
+	public boolean getElideSetAutoCommits() {
+		return this.elideSetAutoCommits.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getEmptyStringsConvertToZero()
+	 */
+	public boolean getEmptyStringsConvertToZero() {
+		return this.emptyStringsConvertToZero.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getEmulateLocators()
+	 */
+	public boolean getEmulateLocators() {
+		return this.emulateLocators.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getEmulateUnsupportedPstmts()
+	 */
+	public boolean getEmulateUnsupportedPstmts() {
+		return this.emulateUnsupportedPstmts.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getEnablePacketDebug()
+	 */
+	public boolean getEnablePacketDebug() {
+		return this.enablePacketDebug.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getEncoding()
+	 */
+	public String getEncoding() {
+		return this.characterEncodingAsString;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getExplainSlowQueries()
+	 */
+	public boolean getExplainSlowQueries() {
+		return this.explainSlowQueries.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getFailOverReadOnly()
+	 */
+	public boolean getFailOverReadOnly() {
+		return this.failOverReadOnly.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getGatherPerformanceMetrics()
+	 */
+	public boolean getGatherPerformanceMetrics() {
+		return this.gatherPerformanceMetrics.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	protected boolean getHighAvailability() {
+		return this.highAvailabilityAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getHoldResultsOpenOverStatementClose()
+	 */
+	public boolean getHoldResultsOpenOverStatementClose() {
+		return holdResultsOpenOverStatementClose.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getIgnoreNonTxTables()
+	 */
+	public boolean getIgnoreNonTxTables() {
+		return this.ignoreNonTxTables.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getInitialTimeout()
+	 */
+	public int getInitialTimeout() {
+		return this.initialTimeout.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getInteractiveClient()
+	 */
+	public boolean getInteractiveClient() {
+		return this.isInteractiveClient.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getIsInteractiveClient()
+	 */
+	public boolean getIsInteractiveClient() {
+		return this.isInteractiveClient.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getJdbcCompliantTruncation()
+	 */
+	public boolean getJdbcCompliantTruncation() {
+		return this.jdbcCompliantTruncation.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getLocatorFetchBufferSize()
+	 */
+	public int getLocatorFetchBufferSize() {
+		return this.locatorFetchBufferSize.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getLogger()
+	 */
+	public String getLogger() {
+		return this.loggerClassName.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getLoggerClassName()
+	 */
+	public String getLoggerClassName() {
+		return this.loggerClassName.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getLogSlowQueries()
+	 */
+	public boolean getLogSlowQueries() {
+		return this.logSlowQueries.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getMaintainTimeStats()
+	 */
+	public boolean getMaintainTimeStats() {
+		return maintainTimeStatsAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getMaxQuerySizeToLog()
+	 */
+	public int getMaxQuerySizeToLog() {
+		return this.maxQuerySizeToLog.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getMaxReconnects()
+	 */
+	public int getMaxReconnects() {
+		return this.maxReconnects.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getMaxRows()
+	 */
+	public int getMaxRows() {
+		return this.maxRowsAsInt;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getMetadataCacheSize()
+	 */
+	public int getMetadataCacheSize() {
+		return this.metadataCacheSize.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getNoDatetimeStringSync()
+	 */
+	public boolean getNoDatetimeStringSync() {
+		return this.noDatetimeStringSync.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getNullCatalogMeansCurrent()
+	 */
+	public boolean getNullCatalogMeansCurrent() {
+		return this.nullCatalogMeansCurrent.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getNullNamePatternMatchesAll()
+	 */
+	public boolean getNullNamePatternMatchesAll() {
+		return this.nullNamePatternMatchesAll.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPacketDebugBufferSize()
+	 */
+	public int getPacketDebugBufferSize() {
+		return this.packetDebugBufferSize.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getParanoid()
+	 */
+	public boolean getParanoid() {
+		return this.paranoid.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPedantic()
+	 */
+	public boolean getPedantic() {
+		return this.pedantic.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPreparedStatementCacheSize()
+	 */
+	public int getPreparedStatementCacheSize() {
+		return ((Integer) this.preparedStatementCacheSize.getValueAsObject())
+				.intValue();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPreparedStatementCacheSqlLimit()
+	 */
+	public int getPreparedStatementCacheSqlLimit() {
+		return ((Integer) this.preparedStatementCacheSqlLimit
+				.getValueAsObject()).intValue();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getProfileSql()
+	 */
+	public boolean getProfileSql() {
+		return this.profileSQLAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getProfileSQL()
+	 */
+	public boolean getProfileSQL() {
+		return this.profileSQL.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPropertiesTransform()
+	 */
+	public String getPropertiesTransform() {
+		return this.propertiesTransform.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getQueriesBeforeRetryMaster()
+	 */
+	public int getQueriesBeforeRetryMaster() {
+		return this.queriesBeforeRetryMaster.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getReconnectAtTxEnd()
+	 */
+	public boolean getReconnectAtTxEnd() {
+		return this.reconnectTxAtEndAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getRelaxAutoCommit()
+	 */
+	public boolean getRelaxAutoCommit() {
+		return this.relaxAutoCommit.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getReportMetricsIntervalMillis()
+	 */
+	public int getReportMetricsIntervalMillis() {
+		return this.reportMetricsIntervalMillis.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getRequireSSL()
+	 */
+	public boolean getRequireSSL() {
+		return this.requireSSL.getValueAsBoolean();
+	}
+
+	protected boolean getRetainStatementAfterResultSetClose() {
+		return this.retainStatementAfterResultSetClose.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getRollbackOnPooledClose()
+	 */
+	public boolean getRollbackOnPooledClose() {
+		return this.rollbackOnPooledClose.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getRoundRobinLoadBalance()
+	 */
+	public boolean getRoundRobinLoadBalance() {
+		return this.roundRobinLoadBalance.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getRunningCTS13()
+	 */
+	public boolean getRunningCTS13() {
+		return this.runningCTS13.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getSecondsBeforeRetryMaster()
+	 */
+	public int getSecondsBeforeRetryMaster() {
+		return this.secondsBeforeRetryMaster.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getServerTimezone()
+	 */
+	public String getServerTimezone() {
+		return this.serverTimezone.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getSessionVariables()
+	 */
+	public String getSessionVariables() {
+		return sessionVariables.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getSlowQueryThresholdMillis()
+	 */
+	public int getSlowQueryThresholdMillis() {
+		return this.slowQueryThresholdMillis.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getSocketFactoryClassName()
+	 */
+	public String getSocketFactoryClassName() {
+		return this.socketFactoryClassName.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getSocketTimeout()
+	 */
+	public int getSocketTimeout() {
+		return this.socketTimeout.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getStrictFloatingPoint()
+	 */
+	public boolean getStrictFloatingPoint() {
+		return this.strictFloatingPoint.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getStrictUpdates()
+	 */
+	public boolean getStrictUpdates() {
+		return this.strictUpdates.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTinyInt1isBit()
+	 */
+	public boolean getTinyInt1isBit() {
+		return this.tinyInt1isBit.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTraceProtocol()
+	 */
+	public boolean getTraceProtocol() {
+		return this.traceProtocol.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTransformedBitIsBoolean()
+	 */
+	public boolean getTransformedBitIsBoolean() {
+		return this.transformedBitIsBoolean.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseCompression()
+	 */
+	public boolean getUseCompression() {
+		return this.useCompression.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseFastIntParsing()
+	 */
+	public boolean getUseFastIntParsing() {
+		return this.useFastIntParsing.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseHostsInPrivileges()
+	 */
+	public boolean getUseHostsInPrivileges() {
+		return this.useHostsInPrivileges.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseInformationSchema()
+	 */
+	public boolean getUseInformationSchema() {
+		return this.useInformationSchema.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseLocalSessionState()
+	 */
+	public boolean getUseLocalSessionState() {
+		return this.useLocalSessionState.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseOldUTF8Behavior()
+	 */
+	public boolean getUseOldUTF8Behavior() {
+		return this.useOldUTF8BehaviorAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseOnlyServerErrorMessages()
+	 */
+	public boolean getUseOnlyServerErrorMessages() {
+		return this.useOnlyServerErrorMessages.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseReadAheadInput()
+	 */
+	public boolean getUseReadAheadInput() {
+		return this.useReadAheadInput.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseServerPreparedStmts()
+	 */
+	public boolean getUseServerPreparedStmts() {
+		return this.detectServerPreparedStmts.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseSqlStateCodes()
+	 */
+	public boolean getUseSqlStateCodes() {
+		return this.useSqlStateCodes.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseSSL()
+	 */
+	public boolean getUseSSL() {
+		return this.useSSL.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseStreamLengthsInPrepStmts()
+	 */
+	public boolean getUseStreamLengthsInPrepStmts() {
+		return this.useStreamLengthsInPrepStmts.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseTimezone()
+	 */
+	public boolean getUseTimezone() {
+		return this.useTimezone.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseUltraDevWorkAround()
+	 */
+	public boolean getUseUltraDevWorkAround() {
+		return this.useUltraDevWorkAround.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseUnbufferedInput()
+	 */
+	public boolean getUseUnbufferedInput() {
+		return this.useUnbufferedInput.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseUnicode()
+	 */
+	public boolean getUseUnicode() {
+		return this.useUnicodeAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseUsageAdvisor()
+	 */
+	public boolean getUseUsageAdvisor() {
+		return this.useUsageAdvisorAsBoolean;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getYearIsDateType()
+	 */
+	public boolean getYearIsDateType() {
+		return this.yearIsDateType.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getZeroDateTimeBehavior()
+	 */
+	public String getZeroDateTimeBehavior() {
+		return this.zeroDateTimeBehavior.getValueAsString();
+	}
+
+	/**
+	 * Initializes driver properties that come from a JNDI reference (in the
+	 * case of a javax.sql.DataSource bound into some name service that doesn't
+	 * handle Java objects directly).
+	 * 
+	 * @param ref
+	 *            The JNDI Reference that holds RefAddrs for all properties
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	protected void initializeFromRef(Reference ref) throws SQLException {
+		int numPropertiesToSet = PROPERTY_LIST.size();
+
+		for (int i = 0; i < numPropertiesToSet; i++) {
+			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
+					.get(i);
+
+			try {
+				ConnectionProperty propToSet = (ConnectionProperty) propertyField
+						.get(this);
+
+				if (ref != null) {
+					propToSet.initializeFrom(ref);
+				}
+			} catch (IllegalAccessException iae) {
+				throw SQLError.createSQLException("Internal properties failure", //$NON-NLS-1$
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+
+		postInitialization();
+	}
+
+	/**
+	 * Initializes driver properties that come from URL or properties passed to
+	 * the driver manager.
+	 * 
+	 * @param info
+	 *            DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	protected void initializeProperties(Properties info) throws SQLException {
+		if (info != null) {
+			// For backwards-compatibility
+			String profileSqlLc = info.getProperty("profileSql"); //$NON-NLS-1$
+
+			if (profileSqlLc != null) {
+				info.put("profileSQL", profileSqlLc); //$NON-NLS-1$
+			}
+
+			Properties infoCopy = (Properties) info.clone();
+
+			infoCopy.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
+			infoCopy.remove(NonRegisteringDriver.USER_PROPERTY_KEY);
+			infoCopy.remove(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
+			infoCopy.remove(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
+			infoCopy.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);
+			infoCopy.remove("profileSql"); //$NON-NLS-1$
+
+			int numPropertiesToSet = PROPERTY_LIST.size();
+
+			for (int i = 0; i < numPropertiesToSet; i++) {
+				java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
+						.get(i);
+
+				try {
+					ConnectionProperty propToSet = (ConnectionProperty) propertyField
+							.get(this);
+
+					propToSet.initializeFrom(infoCopy);
+				} catch (IllegalAccessException iae) {
+					throw SQLError.createSQLException(
+							Messages.getString("ConnectionProperties.unableToInitDriverProperties") //$NON-NLS-1$
+									+ iae.toString(),
+							SQLError.SQL_STATE_GENERAL_ERROR);
+				}
+			}
+
+			// TODO -- Not yet
+			/*
+			 * int numUnknownProperties = infoCopy.size(); if
+			 * (numUnknownProperties > 0) { StringBuffer errorMessageBuf = new
+			 * StringBuffer( "Unknown connection ");
+			 * errorMessageBuf.append((numUnknownProperties == 1) ? "property " :
+			 * "properties "); Iterator propNamesItor =
+			 * infoCopy.keySet().iterator(); errorMessageBuf.append("'");
+			 * errorMessageBuf.append(propNamesItor.next().toString());
+			 * errorMessageBuf.append("'"); while (propNamesItor.hasNext()) {
+			 * errorMessageBuf.append(", '");
+			 * errorMessageBuf.append(propNamesItor.next().toString());
+			 * errorMessageBuf.append("'"); } throw new
+			 * SQLException(errorMessageBuf.toString(),
+			 * SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE); }
+			 */
+			postInitialization();
+		}
+	}
+
+	protected void postInitialization() throws SQLException {
+	
+		// Support 'old' profileSql capitalization
+		if (this.profileSql.getValueAsObject() != null) {
+			this.profileSQL.initializeFrom(this.profileSql.getValueAsObject()
+					.toString());
+		}
+
+		this.reconnectTxAtEndAsBoolean = ((Boolean) this.reconnectAtTxEnd
+				.getValueAsObject()).booleanValue();
+
+		// Adjust max rows
+		if (this.getMaxRows() == 0) {
+			// adjust so that it will become MysqlDefs.MAX_ROWS
+			// in execSQL()
+			this.maxRows.setValueAsObject(Constants.integerValueOf(-1));
+		}
+
+		//
+		// Check character encoding
+		//
+		String testEncoding = this.getEncoding();
+
+		if (testEncoding != null) {
+			// Attempt to use the encoding, and bail out if it
+			// can't be used
+			try {
+				String testString = "abc"; //$NON-NLS-1$
+				testString.getBytes(testEncoding);
+			} catch (UnsupportedEncodingException UE) {
+				throw SQLError.createSQLException(Messages.getString("ConnectionProperties.unsupportedCharacterEncoding") + Messages.getString("ConnectionProperties.unsupportedCharacterEncoding.1") //$NON-NLS-1$ //$NON-NLS-2$
+						+ testEncoding + Messages.getString("ConnectionProperties.unsupportedCharacterEncoding.2"), "0S100"); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
+		// Metadata caching is only supported on JDK-1.4 and newer
+		// because it relies on LinkedHashMap being present.
+		// Check (and disable) if not supported
+		if (((Boolean) this.cacheResultSetMetadata.getValueAsObject())
+				.booleanValue()) {
+			try {
+				Class.forName("java.util.LinkedHashMap"); //$NON-NLS-1$
+			} catch (ClassNotFoundException cnfe) {
+				this.cacheResultSetMetadata.setValue(false);
+			}
+		}
+
+		this.cacheResultSetMetaDataAsBoolean = this.cacheResultSetMetadata
+				.getValueAsBoolean();
+		this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean();
+		this.characterEncodingAsString = ((String) this.characterEncoding
+				.getValueAsObject());
+		this.highAvailabilityAsBoolean = this.autoReconnect.getValueAsBoolean();
+		this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools
+				.getValueAsBoolean();
+		this.maxRowsAsInt = ((Integer) this.maxRows.getValueAsObject())
+				.intValue();
+		this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean();
+		this.useUsageAdvisorAsBoolean = this.useUsageAdvisor
+				.getValueAsBoolean();
+		this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior
+				.getValueAsBoolean();
+		this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript
+				.getValueAsBoolean();
+		this.maintainTimeStatsAsBoolean = this.maintainTimeStats
+				.getValueAsBoolean();
+		this.jdbcCompliantTruncationForReads = getJdbcCompliantTruncation();
+		
+		if (getUseCursorFetch()) {
+			// assume they want to use server-side prepared statements
+			// because they're required for this functionality
+			setDetectServerPreparedStmts(true);
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAllowLoadLocalInfile(boolean)
+	 */
+	public void setAllowLoadLocalInfile(boolean property) {
+		this.allowLoadLocalInfile.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAllowMultiQueries(boolean)
+	 */
+	public void setAllowMultiQueries(boolean property) {
+		this.allowMultiQueries.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAllowNanAndInf(boolean)
+	 */
+	public void setAllowNanAndInf(boolean flag) {
+		this.allowNanAndInf.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAllowUrlInLocalInfile(boolean)
+	 */
+	public void setAllowUrlInLocalInfile(boolean flag) {
+		this.allowUrlInLocalInfile.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAlwaysSendSetIsolation(boolean)
+	 */
+	public void setAlwaysSendSetIsolation(boolean flag) {
+		this.alwaysSendSetIsolation.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAutoDeserialize(boolean)
+	 */
+	public void setAutoDeserialize(boolean flag) {
+		this.autoDeserialize.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAutoGenerateTestcaseScript(boolean)
+	 */
+	public void setAutoGenerateTestcaseScript(boolean flag) {
+		this.autoGenerateTestcaseScript.setValue(flag);
+		this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAutoReconnect(boolean)
+	 */
+	public void setAutoReconnect(boolean flag) {
+		this.autoReconnect.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAutoReconnectForConnectionPools(boolean)
+	 */
+	public void setAutoReconnectForConnectionPools(boolean property) {
+		this.autoReconnectForPools.setValue(property);
+		this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAutoReconnectForPools(boolean)
+	 */
+	public void setAutoReconnectForPools(boolean flag) {
+		this.autoReconnectForPools.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setBlobSendChunkSize(java.lang.String)
+	 */
+	public void setBlobSendChunkSize(String value) throws SQLException {
+		this.blobSendChunkSize.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCacheCallableStatements(boolean)
+	 */
+	public void setCacheCallableStatements(boolean flag) {
+		this.cacheCallableStatements.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCachePreparedStatements(boolean)
+	 */
+	public void setCachePreparedStatements(boolean flag) {
+		this.cachePreparedStatements.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCacheResultSetMetadata(boolean)
+	 */
+	public void setCacheResultSetMetadata(boolean property) {
+		this.cacheResultSetMetadata.setValue(property);
+		this.cacheResultSetMetaDataAsBoolean = this.cacheResultSetMetadata
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCacheServerConfiguration(boolean)
+	 */
+	public void setCacheServerConfiguration(boolean flag) {
+		this.cacheServerConfiguration.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCallableStatementCacheSize(int)
+	 */
+	public void setCallableStatementCacheSize(int size) {
+		this.callableStatementCacheSize.setValue(size);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCapitalizeDBMDTypes(boolean)
+	 */
+	public void setCapitalizeDBMDTypes(boolean property) {
+		this.capitalizeTypeNames.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCapitalizeTypeNames(boolean)
+	 */
+	public void setCapitalizeTypeNames(boolean flag) {
+		this.capitalizeTypeNames.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCharacterEncoding(java.lang.String)
+	 */
+	public void setCharacterEncoding(String encoding) {
+		this.characterEncoding.setValue(encoding);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCharacterSetResults(java.lang.String)
+	 */
+	public void setCharacterSetResults(String characterSet) {
+		this.characterSetResults.setValue(characterSet);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setClobberStreamingResults(boolean)
+	 */
+	public void setClobberStreamingResults(boolean flag) {
+		this.clobberStreamingResults.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setClobCharacterEncoding(java.lang.String)
+	 */
+	public void setClobCharacterEncoding(String encoding) {
+		this.clobCharacterEncoding.setValue(encoding);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setConnectionCollation(java.lang.String)
+	 */
+	public void setConnectionCollation(String collation) {
+		this.connectionCollation.setValue(collation);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setConnectTimeout(int)
+	 */
+	public void setConnectTimeout(int timeoutMs) {
+		this.connectTimeout.setValue(timeoutMs);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setContinueBatchOnError(boolean)
+	 */
+	public void setContinueBatchOnError(boolean property) {
+		this.continueBatchOnError.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCreateDatabaseIfNotExist(boolean)
+	 */
+	public void setCreateDatabaseIfNotExist(boolean flag) {
+		this.createDatabaseIfNotExist.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setDefaultFetchSize(int)
+	 */
+	public void setDefaultFetchSize(int n) {
+		this.defaultFetchSize.setValue(n);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setDetectServerPreparedStmts(boolean)
+	 */
+	public void setDetectServerPreparedStmts(boolean property) {
+		this.detectServerPreparedStmts.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setDontTrackOpenResources(boolean)
+	 */
+	public void setDontTrackOpenResources(boolean flag) {
+		this.dontTrackOpenResources.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setDumpQueriesOnException(boolean)
+	 */
+	public void setDumpQueriesOnException(boolean flag) {
+		this.dumpQueriesOnException.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setDynamicCalendars(boolean)
+	 */
+	public void setDynamicCalendars(boolean flag) {
+		this.dynamicCalendars.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setElideSetAutoCommits(boolean)
+	 */
+	public void setElideSetAutoCommits(boolean flag) {
+		this.elideSetAutoCommits.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setEmptyStringsConvertToZero(boolean)
+	 */
+	public void setEmptyStringsConvertToZero(boolean flag) {
+		this.emptyStringsConvertToZero.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setEmulateLocators(boolean)
+	 */
+	public void setEmulateLocators(boolean property) {
+		this.emulateLocators.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setEmulateUnsupportedPstmts(boolean)
+	 */
+	public void setEmulateUnsupportedPstmts(boolean flag) {
+		this.emulateUnsupportedPstmts.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setEnablePacketDebug(boolean)
+	 */
+	public void setEnablePacketDebug(boolean flag) {
+		this.enablePacketDebug.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setEncoding(java.lang.String)
+	 */
+	public void setEncoding(String property) {
+		this.characterEncoding.setValue(property);
+		this.characterEncodingAsString = this.characterEncoding
+				.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setExplainSlowQueries(boolean)
+	 */
+	public void setExplainSlowQueries(boolean flag) {
+		this.explainSlowQueries.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setFailOverReadOnly(boolean)
+	 */
+	public void setFailOverReadOnly(boolean flag) {
+		this.failOverReadOnly.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setGatherPerformanceMetrics(boolean)
+	 */
+	public void setGatherPerformanceMetrics(boolean flag) {
+		this.gatherPerformanceMetrics.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	protected void setHighAvailability(boolean property) {
+		this.autoReconnect.setValue(property);
+		this.highAvailabilityAsBoolean = this.autoReconnect.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setHoldResultsOpenOverStatementClose(boolean)
+	 */
+	public void setHoldResultsOpenOverStatementClose(boolean flag) {
+		this.holdResultsOpenOverStatementClose.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setIgnoreNonTxTables(boolean)
+	 */
+	public void setIgnoreNonTxTables(boolean property) {
+		this.ignoreNonTxTables.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setInitialTimeout(int)
+	 */
+	public void setInitialTimeout(int property) {
+		this.initialTimeout.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setIsInteractiveClient(boolean)
+	 */
+	public void setIsInteractiveClient(boolean property) {
+		this.isInteractiveClient.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setJdbcCompliantTruncation(boolean)
+	 */
+	public void setJdbcCompliantTruncation(boolean flag) {
+		this.jdbcCompliantTruncation.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setLocatorFetchBufferSize(java.lang.String)
+	 */
+	public void setLocatorFetchBufferSize(String value) throws SQLException {
+		this.locatorFetchBufferSize.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setLogger(java.lang.String)
+	 */
+	public void setLogger(String property) {
+		this.loggerClassName.setValueAsObject(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setLoggerClassName(java.lang.String)
+	 */
+	public void setLoggerClassName(String className) {
+		this.loggerClassName.setValue(className);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setLogSlowQueries(boolean)
+	 */
+	public void setLogSlowQueries(boolean flag) {
+		this.logSlowQueries.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setMaintainTimeStats(boolean)
+	 */
+	public void setMaintainTimeStats(boolean flag) {
+		this.maintainTimeStats.setValue(flag);
+		this.maintainTimeStatsAsBoolean = this.maintainTimeStats
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setMaxQuerySizeToLog(int)
+	 */
+	public void setMaxQuerySizeToLog(int sizeInBytes) {
+		this.maxQuerySizeToLog.setValue(sizeInBytes);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setMaxReconnects(int)
+	 */
+	public void setMaxReconnects(int property) {
+		this.maxReconnects.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setMaxRows(int)
+	 */
+	public void setMaxRows(int property) {
+		this.maxRows.setValue(property);
+		this.maxRowsAsInt = this.maxRows.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setMetadataCacheSize(int)
+	 */
+	public void setMetadataCacheSize(int value) {
+		this.metadataCacheSize.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setNoDatetimeStringSync(boolean)
+	 */
+	public void setNoDatetimeStringSync(boolean flag) {
+		this.noDatetimeStringSync.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setNullCatalogMeansCurrent(boolean)
+	 */
+	public void setNullCatalogMeansCurrent(boolean value) {
+		this.nullCatalogMeansCurrent.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setNullNamePatternMatchesAll(boolean)
+	 */
+	public void setNullNamePatternMatchesAll(boolean value) {
+		this.nullNamePatternMatchesAll.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPacketDebugBufferSize(int)
+	 */
+	public void setPacketDebugBufferSize(int size) {
+		this.packetDebugBufferSize.setValue(size);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setParanoid(boolean)
+	 */
+	public void setParanoid(boolean property) {
+		this.paranoid.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPedantic(boolean)
+	 */
+	public void setPedantic(boolean property) {
+		this.pedantic.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPreparedStatementCacheSize(int)
+	 */
+	public void setPreparedStatementCacheSize(int cacheSize) {
+		this.preparedStatementCacheSize.setValue(cacheSize);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPreparedStatementCacheSqlLimit(int)
+	 */
+	public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) {
+		this.preparedStatementCacheSqlLimit.setValue(cacheSqlLimit);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setProfileSql(boolean)
+	 */
+	public void setProfileSql(boolean property) {
+		this.profileSQL.setValue(property);
+		this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setProfileSQL(boolean)
+	 */
+	public void setProfileSQL(boolean flag) {
+		this.profileSQL.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPropertiesTransform(java.lang.String)
+	 */
+	public void setPropertiesTransform(String value) {
+		this.propertiesTransform.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setQueriesBeforeRetryMaster(int)
+	 */
+	public void setQueriesBeforeRetryMaster(int property) {
+		this.queriesBeforeRetryMaster.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setReconnectAtTxEnd(boolean)
+	 */
+	public void setReconnectAtTxEnd(boolean property) {
+		this.reconnectAtTxEnd.setValue(property);
+		this.reconnectTxAtEndAsBoolean = this.reconnectAtTxEnd
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRelaxAutoCommit(boolean)
+	 */
+	public void setRelaxAutoCommit(boolean property) {
+		this.relaxAutoCommit.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setReportMetricsIntervalMillis(int)
+	 */
+	public void setReportMetricsIntervalMillis(int millis) {
+		this.reportMetricsIntervalMillis.setValue(millis);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRequireSSL(boolean)
+	 */
+	public void setRequireSSL(boolean property) {
+		this.requireSSL.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRetainStatementAfterResultSetClose(boolean)
+	 */
+	public void setRetainStatementAfterResultSetClose(boolean flag) {
+		this.retainStatementAfterResultSetClose.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRollbackOnPooledClose(boolean)
+	 */
+	public void setRollbackOnPooledClose(boolean flag) {
+		this.rollbackOnPooledClose.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRoundRobinLoadBalance(boolean)
+	 */
+	public void setRoundRobinLoadBalance(boolean flag) {
+		this.roundRobinLoadBalance.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRunningCTS13(boolean)
+	 */
+	public void setRunningCTS13(boolean flag) {
+		this.runningCTS13.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setSecondsBeforeRetryMaster(int)
+	 */
+	public void setSecondsBeforeRetryMaster(int property) {
+		this.secondsBeforeRetryMaster.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setServerTimezone(java.lang.String)
+	 */
+	public void setServerTimezone(String property) {
+		this.serverTimezone.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setSessionVariables(java.lang.String)
+	 */
+	public void setSessionVariables(String variables) {
+		this.sessionVariables.setValue(variables);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setSlowQueryThresholdMillis(int)
+	 */
+	public void setSlowQueryThresholdMillis(int millis) {
+		this.slowQueryThresholdMillis.setValue(millis);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setSocketFactoryClassName(java.lang.String)
+	 */
+	public void setSocketFactoryClassName(String property) {
+		this.socketFactoryClassName.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setSocketTimeout(int)
+	 */
+	public void setSocketTimeout(int property) {
+		this.socketTimeout.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setStrictFloatingPoint(boolean)
+	 */
+	public void setStrictFloatingPoint(boolean property) {
+		this.strictFloatingPoint.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setStrictUpdates(boolean)
+	 */
+	public void setStrictUpdates(boolean property) {
+		this.strictUpdates.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTinyInt1isBit(boolean)
+	 */
+	public void setTinyInt1isBit(boolean flag) {
+		this.tinyInt1isBit.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTraceProtocol(boolean)
+	 */
+	public void setTraceProtocol(boolean flag) {
+		this.traceProtocol.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTransformedBitIsBoolean(boolean)
+	 */
+	public void setTransformedBitIsBoolean(boolean flag) {
+		this.transformedBitIsBoolean.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseCompression(boolean)
+	 */
+	public void setUseCompression(boolean property) {
+		this.useCompression.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseFastIntParsing(boolean)
+	 */
+	public void setUseFastIntParsing(boolean flag) {
+		this.useFastIntParsing.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseHostsInPrivileges(boolean)
+	 */
+	public void setUseHostsInPrivileges(boolean property) {
+		this.useHostsInPrivileges.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseInformationSchema(boolean)
+	 */
+	public void setUseInformationSchema(boolean flag) {
+		this.useInformationSchema.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseLocalSessionState(boolean)
+	 */
+	public void setUseLocalSessionState(boolean flag) {
+		this.useLocalSessionState.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseOldUTF8Behavior(boolean)
+	 */
+	public void setUseOldUTF8Behavior(boolean flag) {
+		this.useOldUTF8Behavior.setValue(flag);
+		this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseOnlyServerErrorMessages(boolean)
+	 */
+	public void setUseOnlyServerErrorMessages(boolean flag) {
+		this.useOnlyServerErrorMessages.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseReadAheadInput(boolean)
+	 */
+	public void setUseReadAheadInput(boolean flag) {
+		this.useReadAheadInput.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseServerPreparedStmts(boolean)
+	 */
+	public void setUseServerPreparedStmts(boolean flag) {
+		this.detectServerPreparedStmts.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseSqlStateCodes(boolean)
+	 */
+	public void setUseSqlStateCodes(boolean flag) {
+		this.useSqlStateCodes.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseSSL(boolean)
+	 */
+	public void setUseSSL(boolean property) {
+		this.useSSL.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseStreamLengthsInPrepStmts(boolean)
+	 */
+	public void setUseStreamLengthsInPrepStmts(boolean property) {
+		this.useStreamLengthsInPrepStmts.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseTimezone(boolean)
+	 */
+	public void setUseTimezone(boolean property) {
+		this.useTimezone.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseUltraDevWorkAround(boolean)
+	 */
+	public void setUseUltraDevWorkAround(boolean property) {
+		this.useUltraDevWorkAround.setValue(property);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseUnbufferedInput(boolean)
+	 */
+	public void setUseUnbufferedInput(boolean flag) {
+		this.useUnbufferedInput.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseUnicode(boolean)
+	 */
+	public void setUseUnicode(boolean flag) {
+		this.useUnicode.setValue(flag);
+		this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseUsageAdvisor(boolean)
+	 */
+	public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) {
+		this.useUsageAdvisor.setValue(useUsageAdvisorFlag);
+		this.useUsageAdvisorAsBoolean = this.useUsageAdvisor
+				.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setYearIsDateType(boolean)
+	 */
+	public void setYearIsDateType(boolean flag) {
+		this.yearIsDateType.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setZeroDateTimeBehavior(java.lang.String)
+	 */
+	public void setZeroDateTimeBehavior(String behavior) {
+		this.zeroDateTimeBehavior.setValue(behavior);
+	}
+
+	protected void storeToRef(Reference ref) throws SQLException {
+		int numPropertiesToSet = PROPERTY_LIST.size();
+
+		for (int i = 0; i < numPropertiesToSet; i++) {
+			java.lang.reflect.Field propertyField = (java.lang.reflect.Field) PROPERTY_LIST
+					.get(i);
+
+			try {
+				ConnectionProperty propToStore = (ConnectionProperty) propertyField
+						.get(this);
+
+				if (ref != null) {
+					propToStore.storeTo(ref);
+				}
+			} catch (IllegalAccessException iae) {
+				throw SQLError.createSQLException(Messages.getString("ConnectionProperties.errorNotExpected")); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#useUnbufferedInput()
+	 */
+	public boolean useUnbufferedInput() {
+		return this.useUnbufferedInput.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseCursorFetch()
+	 */
+	public boolean getUseCursorFetch() {
+		return this.useCursorFetch.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseCursorFetch(boolean)
+	 */
+	public void setUseCursorFetch(boolean flag) {
+		this.useCursorFetch.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getOverrideSupportsIntegrityEnhancementFacility()
+	 */
+	public boolean getOverrideSupportsIntegrityEnhancementFacility() {
+		return this.overrideSupportsIntegrityEnhancementFacility.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setOverrideSupportsIntegrityEnhancementFacility(boolean)
+	 */
+	public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
+		this.overrideSupportsIntegrityEnhancementFacility.setValue(flag);	
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getNoTimezoneConversionForTimeType()
+	 */
+	public boolean getNoTimezoneConversionForTimeType() {
+		return this.noTimezoneConversionForTimeType.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setNoTimezoneConversionForTimeType(boolean)
+	 */
+	public void setNoTimezoneConversionForTimeType(boolean flag) {
+		this.noTimezoneConversionForTimeType.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseJDBCCompliantTimezoneShift()
+	 */
+	public boolean getUseJDBCCompliantTimezoneShift() {
+		return this.useJDBCCompliantTimezoneShift.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseJDBCCompliantTimezoneShift(boolean)
+	 */
+	public void setUseJDBCCompliantTimezoneShift(boolean flag) {
+		this.useJDBCCompliantTimezoneShift.setValue(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getAutoClosePStmtStreams()
+	 */
+	public boolean getAutoClosePStmtStreams() {
+		return this.autoClosePStmtStreams.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setAutoClosePStmtStreams(boolean)
+	 */
+	public void setAutoClosePStmtStreams(boolean flag) {
+		this.autoClosePStmtStreams.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getProcessEscapeCodesForPrepStmts()
+	 */
+	public boolean getProcessEscapeCodesForPrepStmts() {
+		return this.processEscapeCodesForPrepStmts.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setProcessEscapeCodesForPrepStmts(boolean)
+	 */
+	public void setProcessEscapeCodesForPrepStmts(boolean flag) {
+		this.processEscapeCodesForPrepStmts.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseGmtMillisForDatetimes()
+	 */
+	public boolean getUseGmtMillisForDatetimes() {
+		return this.useGmtMillisForDatetimes.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseGmtMillisForDatetimes(boolean)
+	 */
+	public void setUseGmtMillisForDatetimes(boolean flag) {
+		this.useGmtMillisForDatetimes.setValue(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getDumpMetadataOnColumnNotFound()
+	 */
+	public boolean getDumpMetadataOnColumnNotFound() {
+		return this.dumpMetadataOnColumnNotFound.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setDumpMetadataOnColumnNotFound(boolean)
+	 */
+	public void setDumpMetadataOnColumnNotFound(boolean flag) {
+		this.dumpMetadataOnColumnNotFound.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getResourceId()
+	 */
+	public String getResourceId() {
+		return this.resourceId.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setResourceId(java.lang.String)
+	 */
+	public void setResourceId(String resourceId) {
+		this.resourceId.setValue(resourceId);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getRewriteBatchedStatements()
+	 */
+	public boolean getRewriteBatchedStatements() {
+		return this.rewriteBatchedStatements.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setRewriteBatchedStatements(boolean)
+	 */
+	public void setRewriteBatchedStatements(boolean flag) {
+		this.rewriteBatchedStatements.setValue(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getJdbcCompliantTruncationForReads()
+	 */
+	public boolean getJdbcCompliantTruncationForReads() {
+		return this.jdbcCompliantTruncationForReads;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setJdbcCompliantTruncationForReads(boolean)
+	 */
+	public void setJdbcCompliantTruncationForReads(
+			boolean jdbcCompliantTruncationForReads) {
+		this.jdbcCompliantTruncationForReads = jdbcCompliantTruncationForReads;
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseJvmCharsetConverters()
+	 */
+	public boolean getUseJvmCharsetConverters() {
+		return this.useJvmCharsetConverters.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseJvmCharsetConverters(boolean)
+	 */
+	public void setUseJvmCharsetConverters(boolean flag) {
+		this.useJvmCharsetConverters.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPinGlobalTxToPhysicalConnection()
+	 */
+	public boolean getPinGlobalTxToPhysicalConnection() {
+		return this.pinGlobalTxToPhysicalConnection.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPinGlobalTxToPhysicalConnection(boolean)
+	 */
+	public void setPinGlobalTxToPhysicalConnection(boolean flag) {
+		this.pinGlobalTxToPhysicalConnection.setValue(flag);
+	}
+	
+	/*
+	 * "Aliases" which match the property names to make using 
+	 * from datasources easier.
+	 */
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setGatherPerfMetrics(boolean)
+	 */
+	public void setGatherPerfMetrics(boolean flag) {
+		setGatherPerformanceMetrics(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getGatherPerfMetrics()
+	 */
+	public boolean getGatherPerfMetrics() {
+		return getGatherPerformanceMetrics();
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUltraDevHack(boolean)
+	 */
+	public void setUltraDevHack(boolean flag) {
+		setUseUltraDevWorkAround(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUltraDevHack()
+	 */
+	public boolean getUltraDevHack() {
+		return getUseUltraDevWorkAround();
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setInteractiveClient(boolean)
+	 */
+	public void setInteractiveClient(boolean property) {
+		setIsInteractiveClient(property);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setSocketFactory(java.lang.String)
+	 */
+	public void setSocketFactory(String name) {
+		setSocketFactoryClassName(name);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getSocketFactory()
+	 */
+	public String getSocketFactory() {
+		return getSocketFactoryClassName();
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseServerPrepStmts(boolean)
+	 */
+	public void setUseServerPrepStmts(boolean flag) {
+		setUseServerPreparedStmts(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseServerPrepStmts()
+	 */
+	public boolean getUseServerPrepStmts() {
+		return getUseServerPreparedStmts();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCacheCallableStmts(boolean)
+	 */
+	public void setCacheCallableStmts(boolean flag) {
+		setCacheCallableStatements(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCacheCallableStmts()
+	 */
+	public boolean getCacheCallableStmts() {
+		return getCacheCallableStatements();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCachePrepStmts(boolean)
+	 */
+	public void setCachePrepStmts(boolean flag) {
+		setCachePreparedStatements(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCachePrepStmts()
+	 */
+	public boolean getCachePrepStmts() {
+		return getCachePreparedStatements();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setCallableStmtCacheSize(int)
+	 */
+	public void setCallableStmtCacheSize(int cacheSize) {
+		setCallableStatementCacheSize(cacheSize);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getCallableStmtCacheSize()
+	 */
+	public int getCallableStmtCacheSize() {
+		return getCallableStatementCacheSize();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPrepStmtCacheSize(int)
+	 */
+	public void setPrepStmtCacheSize(int cacheSize) {
+		setPreparedStatementCacheSize(cacheSize);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPrepStmtCacheSize()
+	 */
+	public int getPrepStmtCacheSize() {
+		return getPreparedStatementCacheSize();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPrepStmtCacheSqlLimit(int)
+	 */
+	public void setPrepStmtCacheSqlLimit(int sqlLimit) {
+		setPreparedStatementCacheSqlLimit(sqlLimit);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPrepStmtCacheSqlLimit()
+	 */
+	public int getPrepStmtCacheSqlLimit() {
+		return getPreparedStatementCacheSqlLimit();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getNoAccessToProcedureBodies()
+	 */
+	public boolean getNoAccessToProcedureBodies() {
+		return this.noAccessToProcedureBodies.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setNoAccessToProcedureBodies(boolean)
+	 */
+	public void setNoAccessToProcedureBodies(boolean flag) {
+		this.noAccessToProcedureBodies.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseOldAliasMetadataBehavior()
+	 */
+	public boolean getUseOldAliasMetadataBehavior() {
+		return this.useOldAliasMetadataBehavior.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseOldAliasMetadataBehavior(boolean)
+	 */
+	public void setUseOldAliasMetadataBehavior(boolean flag) {
+		this.useOldAliasMetadataBehavior.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getClientCertificateKeyStorePassword()
+	 */
+	public String getClientCertificateKeyStorePassword() {
+		return clientCertificateKeyStorePassword.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setClientCertificateKeyStorePassword(java.lang.String)
+	 */
+	public void setClientCertificateKeyStorePassword(
+			String value) {
+		this.clientCertificateKeyStorePassword.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getClientCertificateKeyStoreType()
+	 */
+	public String getClientCertificateKeyStoreType() {
+		return clientCertificateKeyStoreType.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setClientCertificateKeyStoreType(java.lang.String)
+	 */
+	public void setClientCertificateKeyStoreType(
+			String value) {
+		this.clientCertificateKeyStoreType.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getClientCertificateKeyStoreUrl()
+	 */
+	public String getClientCertificateKeyStoreUrl() {
+		return clientCertificateKeyStoreUrl.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setClientCertificateKeyStoreUrl(java.lang.String)
+	 */
+	public void setClientCertificateKeyStoreUrl(
+			String value) {
+		this.clientCertificateKeyStoreUrl.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTrustCertificateKeyStorePassword()
+	 */
+	public String getTrustCertificateKeyStorePassword() {
+		return trustCertificateKeyStorePassword.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTrustCertificateKeyStorePassword(java.lang.String)
+	 */
+	public void setTrustCertificateKeyStorePassword(
+			String value) {
+		this.trustCertificateKeyStorePassword.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTrustCertificateKeyStoreType()
+	 */
+	public String getTrustCertificateKeyStoreType() {
+		return trustCertificateKeyStoreType.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTrustCertificateKeyStoreType(java.lang.String)
+	 */
+	public void setTrustCertificateKeyStoreType(
+			String value) {
+		this.trustCertificateKeyStoreType.setValue(value);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTrustCertificateKeyStoreUrl()
+	 */
+	public String getTrustCertificateKeyStoreUrl() {
+		return trustCertificateKeyStoreUrl.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTrustCertificateKeyStoreUrl(java.lang.String)
+	 */
+	public void setTrustCertificateKeyStoreUrl(
+			String value) {
+		this.trustCertificateKeyStoreUrl.setValue(value);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseSSPSCompatibleTimezoneShift()
+	 */
+	public boolean getUseSSPSCompatibleTimezoneShift() {
+		return this.useSSPSCompatibleTimezoneShift.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseSSPSCompatibleTimezoneShift(boolean)
+	 */
+	public void setUseSSPSCompatibleTimezoneShift(boolean flag) {
+		this.useSSPSCompatibleTimezoneShift.setValue(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getTreatUtilDateAsTimestamp()
+	 */
+	public boolean getTreatUtilDateAsTimestamp() {
+		return this.treatUtilDateAsTimestamp.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setTreatUtilDateAsTimestamp(boolean)
+	 */
+	public void setTreatUtilDateAsTimestamp(boolean flag) {
+		this.treatUtilDateAsTimestamp.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseFastDateParsing()
+	 */
+	public boolean getUseFastDateParsing() {
+		return this.useFastDateParsing.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseFastDateParsing(boolean)
+	 */
+	public void setUseFastDateParsing(boolean flag) {
+		this.useFastDateParsing.setValue(flag);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getLocalSocketAddress()
+	 */
+	public String getLocalSocketAddress() {
+		return this.localSocketAddress.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setLocalSocketAddress(java.lang.String)
+	 */
+	public void setLocalSocketAddress(String address) {
+		this.localSocketAddress.setValue(address);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseConfigs(java.lang.String)
+	 */
+	public void setUseConfigs(String configs) {
+		this.useConfigs.setValue(configs);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseConfigs()
+	 */
+	public String getUseConfigs() {
+		return this.useConfigs.getValueAsString();
+	}
+	
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getGenerateSimpleParameterMetadata()
+	 */
+	public boolean getGenerateSimpleParameterMetadata() {
+		return this.generateSimpleParameterMetadata.getValueAsBoolean();
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setGenerateSimpleParameterMetadata(boolean)
+	 */
+	public void setGenerateSimpleParameterMetadata(boolean flag) {
+		this.generateSimpleParameterMetadata.setValue(flag);
+	}	
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getLogXaCommands()
+	 */
+	public boolean getLogXaCommands() {
+		return this.logXaCommands.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setLogXaCommands(boolean)
+	 */
+	public void setLogXaCommands(boolean flag) {
+		this.logXaCommands.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getResultSetSizeThreshold()
+	 */
+	public int getResultSetSizeThreshold() {
+		return this.resultSetSizeThreshold.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setResultSetSizeThreshold(int)
+	 */
+	public void setResultSetSizeThreshold(int threshold) {
+		this.resultSetSizeThreshold.setValue(threshold);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getNetTimeoutForStreamingResults()
+	 */
+	public int getNetTimeoutForStreamingResults() {
+		return this.netTimeoutForStreamingResults.getValueAsInt();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setNetTimeoutForStreamingResults(int)
+	 */
+	public void setNetTimeoutForStreamingResults(int value) {
+		this.netTimeoutForStreamingResults.setValue(value);
+	}
+	
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getEnableQueryTimeouts()
+	 */
+	public boolean getEnableQueryTimeouts() {
+		return this.enableQueryTimeouts.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setEnableQueryTimeouts(boolean)
+	 */
+	public void setEnableQueryTimeouts(boolean flag) {
+		this.enableQueryTimeouts.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getPadCharsWithSpace()
+	 */
+	public boolean getPadCharsWithSpace() {
+		return this.padCharsWithSpace.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setPadCharsWithSpace(boolean)
+	 */
+	public void setPadCharsWithSpace(boolean flag) {
+		this.padCharsWithSpace.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getUseDynamicCharsetInfo()
+	 */
+	public boolean getUseDynamicCharsetInfo() {
+		return this.useDynamicCharsetInfo.getValueAsBoolean();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setUseDynamicCharsetInfo(boolean)
+	 */
+	public void setUseDynamicCharsetInfo(boolean flag) {
+		this.useDynamicCharsetInfo.setValue(flag);
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#getClientInfoProvider()
+	 */
+	public String getClientInfoProvider() {
+		return this.clientInfoProvider.getValueAsString();
+	}
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IConnectionProperties#setClientInfoProvider(java.lang.String)
+	 */
+	public void setClientInfoProvider(String classname) {
+		this.clientInfoProvider.setValue(classname);
+	}
+	
+	public boolean getPopulateInsertRowWithDefaultValues() {
+		return this.populateInsertRowWithDefaultValues.getValueAsBoolean();
+	}
+
+	public void setPopulateInsertRowWithDefaultValues(boolean flag) {
+		this.populateInsertRowWithDefaultValues.setValue(flag);
+	}
+	
+	public String getLoadBalanceStrategy() {
+		return this.loadBalanceStrategy.getValueAsString();
+	}
+
+	public void setLoadBalanceStrategy(String strategy) {
+		this.loadBalanceStrategy.setValue(strategy);
+	}
+	
+	public boolean getTcpNoDelay() {
+		return this.tcpNoDelay.getValueAsBoolean();
+	}
+
+	public void setTcpNoDelay(boolean flag) {
+		this.tcpNoDelay.setValue(flag);
+	}
+
+	public boolean getTcpKeepAlive() {
+		return this.tcpKeepAlive.getValueAsBoolean();
+	}
+
+	public void setTcpKeepAlive(boolean flag) {
+		this.tcpKeepAlive.setValue(flag);
+	}
+
+	public int getTcpRcvBuf() {
+		return this.tcpRcvBuf.getValueAsInt();
+	}
+
+	public void setTcpRcvBuf(int bufSize) {
+		this.tcpRcvBuf.setValue(bufSize);
+	}
+
+	public int getTcpSndBuf() {
+		return this.tcpSndBuf.getValueAsInt();
+	}
+
+	public void setTcpSndBuf(int bufSize) {
+		this.tcpSndBuf.setValue(bufSize);
+	}
+
+	public int getTcpTrafficClass() {
+		return this.tcpTrafficClass.getValueAsInt();
+	}
+
+	public void setTcpTrafficClass(int classFlags) {
+		this.tcpTrafficClass.setValue(classFlags);
+	}
+	
+	public boolean getUseNanosForElapsedTime() {
+		return this.useNanosForElapsedTime.getValueAsBoolean();
+	}
+
+	public void setUseNanosForElapsedTime(boolean flag) {
+		this.useNanosForElapsedTime.setValue(flag);
+	}
+
+	public long getSlowQueryThresholdNanos() {
+		return this.slowQueryThresholdNanos.getValueAsLong();
+	}
+
+	public void setSlowQueryThresholdNanos(long nanos) {
+		this.slowQueryThresholdNanos.setValue(nanos);
+	}
+
+	public String getStatementInterceptors() {
+		return this.statementInterceptors.getValueAsString();
+	}
+
+	public void setStatementInterceptors(String value) {
+		this.statementInterceptors.setValue(value);
+	}
+
+	public boolean getUseDirectRowUnpack() {
+		return this.useDirectRowUnpack.getValueAsBoolean();
+	}
+
+	public void setUseDirectRowUnpack(boolean flag) {
+		this.useDirectRowUnpack.setValue(flag);
+	}
+
+	public String getLargeRowSizeThreshold() {
+		return this.largeRowSizeThreshold.getValueAsString();
+	}
+
+	public void setLargeRowSizeThreshold(String value) {
+		try {
+			this.largeRowSizeThreshold.setValue(value);
+		} catch (SQLException sqlEx) {
+			RuntimeException ex = new RuntimeException(sqlEx.getMessage());
+			ex.initCause(sqlEx);
+			
+			throw ex;
+		}
+	}
+
+	public boolean getUseBlobToStoreUTF8OutsideBMP() {
+		return this.useBlobToStoreUTF8OutsideBMP.getValueAsBoolean();
+	}
+
+	public void setUseBlobToStoreUTF8OutsideBMP(boolean flag) {
+		this.useBlobToStoreUTF8OutsideBMP.setValue(flag);
+	}
+
+	public String getUtf8OutsideBmpExcludedColumnNamePattern() {
+		return this.utf8OutsideBmpExcludedColumnNamePattern.getValueAsString();
+	}
+
+	public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) {
+		this.utf8OutsideBmpExcludedColumnNamePattern.setValue(regexPattern);
+	}
+
+	public String getUtf8OutsideBmpIncludedColumnNamePattern() {
+		return this.utf8OutsideBmpIncludedColumnNamePattern.getValueAsString();
+	}
+
+	public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) {
+		this.utf8OutsideBmpIncludedColumnNamePattern.setValue(regexPattern);
+	}
+	
+	public boolean getIncludeInnodbStatusInDeadlockExceptions() {
+		return this.includeInnodbStatusInDeadlockExceptions.getValueAsBoolean();
+	}
+
+	public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) {
+		this.includeInnodbStatusInDeadlockExceptions.setValue(flag);
+	}
+	
+	public boolean getBlobsAreStrings() {
+        return this.blobsAreStrings.getValueAsBoolean();
+    }
+
+	public void setBlobsAreStrings(boolean flag) {
+        this.blobsAreStrings.setValue(flag);
+    }
+
+    public boolean getFunctionsNeverReturnBlobs() {
+        return this.functionsNeverReturnBlobs.getValueAsBoolean();
+    }
+
+    public void setFunctionsNeverReturnBlobs(boolean flag) {
+        this.functionsNeverReturnBlobs.setValue(flag);
+    }
+
+	public boolean getAutoSlowLog() {
+		return this.autoSlowLog.getValueAsBoolean();
+	}
+
+	public void setAutoSlowLog(boolean flag) {
+		this.autoSlowLog.setValue(flag);
+	}
+
+	public String getConnectionLifecycleInterceptors() {
+		return this.connectionLifecycleInterceptors.getValueAsString();
+	}
+
+	public void setConnectionLifecycleInterceptors(String interceptors) {
+		this.connectionLifecycleInterceptors.setValue(interceptors);
+    }
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Constants.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Constants.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Constants.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -29,15 +29,115 @@
  * 
  * @author Mark Matthews
  * 
- * @version $Id: Constants.java 3726 2005-05-19 15:52:24Z mmatthews $
+ * @version $Id: Constants.java 6433 2007-05-18 18:38:56Z mmatthews $
  */
-class Constants {
+public class Constants {
 	/**
 	 * Avoids allocation of empty byte[] when representing 0-length strings.
 	 */
 	public final static byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
 	/**
+	 * I18N'd representation of the abbreviation for "ms"
+	 */
+	public final static String MILLIS_I18N = Messages.getString("Milliseconds");
+	
+	public final static byte[] SLASH_STAR_SPACE_AS_BYTES = new byte[] {
+			(byte) '/', (byte) '*', (byte) ' ' };
+
+	public final static byte[] SPACE_STAR_SLASH_SPACE_AS_BYTES = new byte[] {
+			(byte) ' ', (byte) '*', (byte) '/', (byte) ' ' };
+
+	/*
+	 * We're still stuck on JDK-1.4.2, but want the Number.valueOf() methods
+	 */
+	private static final Character[] CHARACTER_CACHE = new Character[128];
+
+	private static final int BYTE_CACHE_OFFSET = 128;
+
+	private static final Byte[] BYTE_CACHE = new Byte[256];
+
+	private static final int INTEGER_CACHE_OFFSET = 128;
+
+	private static final Integer[] INTEGER_CACHE = new Integer[256];
+
+	private static final int SHORT_CACHE_OFFSET = 128;
+
+	private static final Short[] SHORT_CACHE = new Short[256];
+
+	private static final Long[] LONG_CACHE = new Long[256];
+
+	private static final int LONG_CACHE_OFFSET = 128;
+
+	static {
+		for (int i = 0; i < CHARACTER_CACHE.length; i++) {
+			CHARACTER_CACHE[i] = new Character((char) i);
+		}
+
+		for (int i = 0; i < INTEGER_CACHE.length; i++) {
+			INTEGER_CACHE[i] = new Integer(i - 128);
+		}
+
+		for (int i = 0; i < SHORT_CACHE.length; i++) {
+			SHORT_CACHE[i] = new Short((short) (i - 128));
+		}
+
+		for (int i = 0; i < LONG_CACHE.length; i++) {
+			LONG_CACHE[i] = new Long(i - 128);
+		}
+
+		for (int i = 0; i < BYTE_CACHE.length; i++)
+			BYTE_CACHE[i] = new Byte((byte) (i - BYTE_CACHE_OFFSET));
+	}
+
+	/** Same behavior as JDK-1.5's Constants.characterValueOf(int) */
+	
+	public static Character characterValueOf(char c) {
+		if (c <= 127) {
+			return CHARACTER_CACHE[c];
+		}
+
+		return new Character(c);
+	}
+
+	/** Same behavior as JDK-1.5's Byte.valueOf(int) */
+
+	public static final Byte byteValueOf(byte b) {
+		return BYTE_CACHE[b + BYTE_CACHE_OFFSET];
+	}
+
+	/** Same behavior as JDK-1.5's Integer.valueOf(int) */
+
+	public static final Integer integerValueOf(int i) {
+		if (i >= -128 && i <= 127) {
+			return INTEGER_CACHE[i + INTEGER_CACHE_OFFSET];
+		}
+
+		return new Integer(i);
+	}
+
+	/** Same behavior as JDK-1.5's Constants.shortValueOf(int) */
+	
+	public static Short shortValueOf(short s) {
+
+		if (s >= -128 && s <= 127) {
+			return SHORT_CACHE[s + SHORT_CACHE_OFFSET];
+		}
+		
+		return new Short(s);
+	}
+
+	/** Same behavior as JDK-1.5's Long.valueOf(int) */
+	
+	public static final Long longValueOf(long l) {
+		if (l >= -128 && l <= 127) {
+			return LONG_CACHE[(int) l + LONG_CACHE_OFFSET];
+		}
+
+		return new Long(l);
+	}
+
+	/**
 	 * Prevents instantiation
 	 */
 	private Constants() {

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaData.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaData.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaData.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -25,6 +25,8 @@
 package com.mysql.jdbc;
 
 import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -38,6 +40,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Properties;
 import java.util.StringTokenizer;
 import java.util.TreeMap;
 
@@ -63,26 +66,7 @@
  *          Exp $
  */
 public class DatabaseMetaData implements java.sql.DatabaseMetaData {
-	protected abstract class IterateBlock {
-		IteratorWithCleanup iterator;
 
-		IterateBlock(IteratorWithCleanup i) {
-			iterator = i;
-		}
-
-		public void doForAll() throws SQLException {
-			try {
-				while (iterator.hasNext()) {
-					forEach(iterator.next());
-				}
-			} finally {
-				iterator.close();
-			}
-		}
-
-		abstract void forEach(Object each) throws SQLException;
-	}
-
 	protected abstract class IteratorWithCleanup {
 		abstract void close() throws SQLException;
 
@@ -168,11 +152,11 @@
 
 		int charOctetLength;
 
-		int columnSize;
+		Integer columnSize;
 
 		short dataType;
 
-		int decimalDigits;
+		Integer decimalDigits;
 
 		String isNullable;
 
@@ -184,6 +168,11 @@
 
 		TypeDescriptor(String typeInfo, String nullabilityInfo)
 				throws SQLException {
+			if (typeInfo == null) {
+				throw SQLError.createSQLException("NULL typeinfo not supported.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+			
 			String mysqlType = "";
 			String fullMysqlType = null;
 
@@ -204,8 +193,11 @@
 			// Add unsigned to typename reported to enduser as 'native type', if
 			// present
 
+			boolean isUnsigned = false;
+			
 			if (StringUtils.indexOfIgnoreCase(typeInfo, "unsigned") != -1) {
 				fullMysqlType = mysqlType + " unsigned";
+				isUnsigned = true;
 			} else {
 				fullMysqlType = mysqlType;
 			}
@@ -219,138 +211,185 @@
 			this.typeName = fullMysqlType;
 
 			// Figure Out the Size
-			if (typeInfo != null) {
-				if (StringUtils.startsWithIgnoreCase(typeInfo, "enum")) {
-					String temp = typeInfo.substring(typeInfo.indexOf("("),
-							typeInfo.lastIndexOf(")"));
-					java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
-							temp, ",");
-					int maxLength = 0;
+			
+			if (StringUtils.startsWithIgnoreCase(typeInfo, "enum")) {
+				String temp = typeInfo.substring(typeInfo.indexOf("("),
+						typeInfo.lastIndexOf(")"));
+				java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
+						temp, ",");
+				int maxLength = 0;
 
-					while (tokenizer.hasMoreTokens()) {
-						maxLength = Math.max(maxLength, (tokenizer.nextToken()
-								.length() - 2));
-					}
+				while (tokenizer.hasMoreTokens()) {
+					maxLength = Math.max(maxLength, (tokenizer.nextToken()
+							.length() - 2));
+				}
 
-					this.columnSize = maxLength;
-					this.decimalDigits = 0;
-				} else if (StringUtils.startsWithIgnoreCase(typeInfo, "set")) {
-					String temp = typeInfo.substring(typeInfo.indexOf("("),
-							typeInfo.lastIndexOf(")"));
-					java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
-							temp, ",");
-					int maxLength = 0;
+				this.columnSize = Constants.integerValueOf(maxLength);
+				this.decimalDigits = null;
+			} else if (StringUtils.startsWithIgnoreCase(typeInfo, "set")) {
+				String temp = typeInfo.substring(typeInfo.indexOf("("),
+						typeInfo.lastIndexOf(")"));
+				java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
+						temp, ",");
+				int maxLength = 0;
 
-					while (tokenizer.hasMoreTokens()) {
-						String setMember = tokenizer.nextToken().trim();
-						
-						if (setMember.startsWith("'") && setMember.endsWith("'")) {
-							maxLength += setMember.length() - 2;
-						} else {
-							maxLength += setMember.length();
-						}
+				while (tokenizer.hasMoreTokens()) {
+					String setMember = tokenizer.nextToken().trim();
+
+					if (setMember.startsWith("'")
+							&& setMember.endsWith("'")) {
+						maxLength += setMember.length() - 2;
+					} else {
+						maxLength += setMember.length();
 					}
+				}
 
-					this.columnSize = maxLength;
-					this.decimalDigits = 0;
-				} else if (typeInfo.indexOf(",") != -1) {
-					// Numeric with decimals
-					this.columnSize = Integer.parseInt(typeInfo.substring(
-							(typeInfo.indexOf("(") + 1),
-							(typeInfo.indexOf(","))));
-					this.decimalDigits = Integer.parseInt(typeInfo.substring(
-							(typeInfo.indexOf(",") + 1),
-							(typeInfo.indexOf(")"))));
-				} else {
-					this.columnSize = 0;
+				this.columnSize = Constants.integerValueOf(maxLength);
+				this.decimalDigits = null;
+			} else if (typeInfo.indexOf(",") != -1) {
+				// Numeric with decimals
+				this.columnSize = Integer.valueOf(typeInfo.substring((typeInfo
+						.indexOf("(") + 1), (typeInfo.indexOf(","))).trim());
+				this.decimalDigits = Integer.valueOf(typeInfo.substring(
+						(typeInfo.indexOf(",") + 1),
+						(typeInfo.indexOf(")"))).trim());
+			} else {
+				this.columnSize = null;
+				this.decimalDigits = null;
 
-					/* If the size is specified with the DDL, use that */
-					if (typeInfo.indexOf("(") != -1) {
-						int endParenIndex = typeInfo.indexOf(")");
+				/* If the size is specified with the DDL, use that */
+				if ((StringUtils.indexOfIgnoreCase(typeInfo, "char") != -1
+						|| StringUtils.indexOfIgnoreCase(typeInfo, "text") != -1
+						|| StringUtils.indexOfIgnoreCase(typeInfo, "blob") != -1
+						|| StringUtils
+								.indexOfIgnoreCase(typeInfo, "binary") != -1 || StringUtils
+						.indexOfIgnoreCase(typeInfo, "bit") != -1)
+						&& typeInfo.indexOf("(") != -1) {
+					int endParenIndex = typeInfo.indexOf(")");
 
-						if (endParenIndex == -1) {
-							endParenIndex = typeInfo.length();
-						}
+					if (endParenIndex == -1) {
+						endParenIndex = typeInfo.length();
+					}
 
-						this.columnSize = Integer.parseInt(typeInfo.substring(
-								(typeInfo.indexOf("(") + 1), endParenIndex));
+					this.columnSize = Integer.valueOf(typeInfo.substring(
+							(typeInfo.indexOf("(") + 1), endParenIndex).trim());
 
-						// Adjust for pseudo-boolean
-						if (conn.getTinyInt1isBit()
-								&& this.columnSize == 1
-								&& StringUtils.startsWithIgnoreCase(typeInfo,
-										0, "tinyint")) {
-							if (conn.getTransformedBitIsBoolean()) {
-								this.dataType = Types.BOOLEAN;
-								this.typeName = "BOOLEAN";
-							} else {
-								this.dataType = Types.BIT;
-								this.typeName = "BIT";
-							}
+					// Adjust for pseudo-boolean
+					if (conn.getTinyInt1isBit()
+							&& this.columnSize.intValue() == 1
+							&& StringUtils.startsWithIgnoreCase(typeInfo,
+									0, "tinyint")) {
+						if (conn.getTransformedBitIsBoolean()) {
+							this.dataType = Types.BOOLEAN;
+							this.typeName = "BOOLEAN";
+						} else {
+							this.dataType = Types.BIT;
+							this.typeName = "BIT";
 						}
-					} else if (typeInfo.equalsIgnoreCase("tinyint")) {
-						this.columnSize = 1;
-					} else if (typeInfo.equalsIgnoreCase("smallint")) {
-						this.columnSize = 6;
-					} else if (typeInfo.equalsIgnoreCase("mediumint")) {
-						this.columnSize = 6;
-					} else if (typeInfo.equalsIgnoreCase("int")) {
-						this.columnSize = 11;
-					} else if (typeInfo.equalsIgnoreCase("integer")) {
-						this.columnSize = 11;
-					} else if (typeInfo.equalsIgnoreCase("bigint")) {
-						this.columnSize = 25;
-					} else if (typeInfo.equalsIgnoreCase("int24")) {
-						this.columnSize = 25;
-					} else if (typeInfo.equalsIgnoreCase("real")) {
-						this.columnSize = 12;
-					} else if (typeInfo.equalsIgnoreCase("float")) {
-						this.columnSize = 12;
-					} else if (typeInfo.equalsIgnoreCase("decimal")) {
-						this.columnSize = 12;
-					} else if (typeInfo.equalsIgnoreCase("numeric")) {
-						this.columnSize = 12;
-					} else if (typeInfo.equalsIgnoreCase("double")) {
-						this.columnSize = 22;
-					} else if (typeInfo.equalsIgnoreCase("char")) {
-						this.columnSize = 1;
-					} else if (typeInfo.equalsIgnoreCase("varchar")) {
-						this.columnSize = 255;
-					} else if (typeInfo.equalsIgnoreCase("date")) {
-						this.columnSize = 10;
-					} else if (typeInfo.equalsIgnoreCase("time")) {
-						this.columnSize = 8;
-					} else if (typeInfo.equalsIgnoreCase("timestamp")) {
-						this.columnSize = 19;
-					} else if (typeInfo.equalsIgnoreCase("datetime")) {
-						this.columnSize = 19;
-					} else if (typeInfo.equalsIgnoreCase("tinyblob")) {
-						this.columnSize = 255;
-					} else if (typeInfo.equalsIgnoreCase("blob")) {
-						this.columnSize = 65535;
-					} else if (typeInfo.equalsIgnoreCase("mediumblob")) {
-						this.columnSize = 16277215;
-					} else if (typeInfo.equalsIgnoreCase("longblob")) {
-						this.columnSize = Integer.MAX_VALUE;
-					} else if (typeInfo.equalsIgnoreCase("tinytext")) {
-						this.columnSize = 255;
-					} else if (typeInfo.equalsIgnoreCase("text")) {
-						this.columnSize = 65535;
-					} else if (typeInfo.equalsIgnoreCase("mediumtext")) {
-						this.columnSize = 16277215;
-					} else if (typeInfo.equalsIgnoreCase("longtext")) {
-						this.columnSize = Integer.MAX_VALUE;
-					} else if (typeInfo.equalsIgnoreCase("enum")) {
-						this.columnSize = 255;
-					} else if (typeInfo.equalsIgnoreCase("set")) {
-						this.columnSize = 255;
 					}
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"tinyint")) {
+					if (conn.getTinyInt1isBit() && typeInfo.indexOf("(1)") != -1) {
+						if (conn.getTransformedBitIsBoolean()) {
+							this.dataType = Types.BOOLEAN;
+							this.typeName = "BOOLEAN";
+						} else {
+							this.dataType = Types.BIT;
+							this.typeName = "BIT";
+						}
+					} else {
+						this.columnSize = Constants.integerValueOf(3);
+						this.decimalDigits = Constants.integerValueOf(0);
+					}
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"smallint")) {
+					this.columnSize = Constants.integerValueOf(5);
+					this.decimalDigits = Constants.integerValueOf(0);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"mediumint")) {
+					this.columnSize = Constants.integerValueOf(isUnsigned ? 8 : 7);
+					this.decimalDigits = Constants.integerValueOf(0);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"int")) {
+					this.columnSize = Constants.integerValueOf(10);
+					this.decimalDigits = Constants.integerValueOf(0);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"integer")) {
+					this.columnSize = Constants.integerValueOf(10);
+					this.decimalDigits = Constants.integerValueOf(0);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"bigint")) {
+					this.columnSize = Constants.integerValueOf(isUnsigned ? 20 : 19);
+					this.decimalDigits = Constants.integerValueOf(0);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"int24")) {
+					this.columnSize = Constants.integerValueOf(19);
+					this.decimalDigits = Constants.integerValueOf(0);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"real")) {
+					this.columnSize = Constants.integerValueOf(12);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"float")) {
+					this.columnSize = Constants.integerValueOf(12);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"decimal")) {
+					this.columnSize = Constants.integerValueOf(12);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"numeric")) {
+					this.columnSize = Constants.integerValueOf(12);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"double")) {
+					this.columnSize = Constants.integerValueOf(22);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"char")) {
+					this.columnSize = Constants.integerValueOf(1);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"varchar")) {
+					this.columnSize = Constants.integerValueOf(255);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"date")) {
+					this.columnSize = null;
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"time")) {
+					this.columnSize = null;
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"timestamp")) {
+					this.columnSize = null;
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"datetime")) {
+					this.columnSize = null;
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"tinyblob")) {
+					this.columnSize = Constants.integerValueOf(255);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"blob")) {
+					this.columnSize = Constants.integerValueOf(65535);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"mediumblob")) {
+					this.columnSize = Constants.integerValueOf(16777215);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"longblob")) {
+					this.columnSize = Constants.integerValueOf(Integer.MAX_VALUE);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"tinytext")) {
+					this.columnSize = Constants.integerValueOf(255);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"text")) {
+					this.columnSize = Constants.integerValueOf(65535);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"mediumtext")) {
+					this.columnSize = Constants.integerValueOf(16777215);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"longtext")) {
+					this.columnSize = Constants.integerValueOf(Integer.MAX_VALUE);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"enum")) {
+					this.columnSize = Constants.integerValueOf(255);
+				} else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo,
+						"set")) {
+					this.columnSize = Constants.integerValueOf(255);
+				}
 
-					this.decimalDigits = 0;
-				}
-			} else {
-				this.decimalDigits = 0;
-				this.columnSize = 0;
 			}
 
 			// BUFFER_LENGTH
@@ -376,7 +415,11 @@
 			}
 		}
 	}
-
+       
+	private static String mysqlKeywordsThatArentSQL92;
+	
+	protected static final int MAX_IDENTIFIER_LENGTH = 64;
+	
 	private static final int DEFERRABILITY = 13;
 
 	private static final int DELETE_RULE = 10;
@@ -415,9 +458,157 @@
 	private static final int UPDATE_RULE = 9;
 
 	private static final byte[] VIEW_AS_BYTES = "VIEW".getBytes();
+	
+	private static final Constructor JDBC_4_DBMD_SHOW_CTOR;
+	
+	private static final Constructor JDBC_4_DBMD_IS_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_DBMD_SHOW_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4DatabaseMetaData").getConstructor(
+						new Class[] { com.mysql.jdbc.ConnectionImpl.class,
+								String.class });
+				JDBC_4_DBMD_IS_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4DatabaseMetaDataUsingInfoSchema")
+						.getConstructor(
+								new Class[] { com.mysql.jdbc.ConnectionImpl.class,
+										String.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_DBMD_IS_CTOR = null;
+			JDBC_4_DBMD_SHOW_CTOR = null;
+		}
+		
+		// Current as-of MySQL-5.1.16
+		String[] allMySQLKeywords = new String[] { "ACCESSIBLE", "ADD", "ALL",
+				"ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", "BEFORE",
+				"BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", "CALL",
+				"CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK",
+				"COLLATE", "COLUMN", "CONDITION", "CONNECTION", "CONSTRAINT",
+				"CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE",
+				"CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR",
+				"DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND",
+				"DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", "DECLARE",
+				"DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE",
+				"DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE",
+				"DROP", "DUAL", "EACH", "ELSE", "ELSEIF", "ENCLOSED",
+				"ESCAPED", "EXISTS", "EXIT", "EXPLAIN", "FALSE", "FETCH",
+				"FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM",
+				"FULLTEXT", "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY",
+				"HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF",
+				"IGNORE", "IN", "INDEX", "INFILE", "INNER", "INOUT",
+				"INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4",
+				"INT8", "INTEGER", "INTERVAL", "INTO", "IS", "ITERATE", "JOIN",
+				"KEY", "KEYS", "KILL", "LEADING", "LEAVE", "LEFT", "LIKE",
+				"LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME",
+				"LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT",
+				"LOOP", "LOW_PRIORITY", "MATCH", "MEDIUMBLOB", "MEDIUMINT",
+				"MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND",
+				"MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT",
+				"NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON", "OPTIMIZE",
+				"OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER",
+				"OUTFILE", "PRECISION", "PRIMARY", "PROCEDURE", "PURGE",
+				"RANGE", "READ", "READS", "READ_ONLY", "READ_WRITE", "REAL",
+				"REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPEAT",
+				"REPLACE", "REQUIRE", "RESTRICT", "RETURN", "REVOKE", "RIGHT",
+				"RLIKE", "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", "SELECT",
+				"SENSITIVE", "SEPARATOR", "SET", "SHOW", "SMALLINT", "SPATIAL",
+				"SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING",
+				"SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT",
+				"SSL", "STARTING", "STRAIGHT_JOIN", "TABLE", "TERMINATED",
+				"THEN", "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING",
+				"TRIGGER", "TRUE", "UNDO", "UNION", "UNIQUE", "UNLOCK",
+				"UNSIGNED", "UPDATE", "USAGE", "USE", "USING", "UTC_DATE",
+				"UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR",
+				"VARCHARACTER", "VARYING", "WHEN", "WHERE", "WHILE", "WITH",
+				"WRITE", "X509", "XOR", "YEAR_MONTH", "ZEROFILL" };
 
+		String[] sql92Keywords = new String[] { "ABSOLUTE", "EXEC", "OVERLAPS",
+				"ACTION", "EXECUTE", "PAD", "ADA", "EXISTS", "PARTIAL", "ADD",
+				"EXTERNAL", "PASCAL", "ALL", "EXTRACT", "POSITION", "ALLOCATE",
+				"FALSE", "PRECISION", "ALTER", "FETCH", "PREPARE", "AND",
+				"FIRST", "PRESERVE", "ANY", "FLOAT", "PRIMARY", "ARE", "FOR",
+				"PRIOR", "AS", "FOREIGN", "PRIVILEGES", "ASC", "FORTRAN",
+				"PROCEDURE", "ASSERTION", "FOUND", "PUBLIC", "AT", "FROM",
+				"READ", "AUTHORIZATION", "FULL", "REAL", "AVG", "GET",
+				"REFERENCES", "BEGIN", "GLOBAL", "RELATIVE", "BETWEEN", "GO",
+				"RESTRICT", "BIT", "GOTO", "REVOKE", "BIT_LENGTH", "GRANT",
+				"RIGHT", "BOTH", "GROUP", "ROLLBACK", "BY", "HAVING", "ROWS",
+				"CASCADE", "HOUR", "SCHEMA", "CASCADED", "IDENTITY", "SCROLL",
+				"CASE", "IMMEDIATE", "SECOND", "CAST", "IN", "SECTION",
+				"CATALOG", "INCLUDE", "SELECT", "CHAR", "INDEX", "SESSION",
+				"CHAR_LENGTH", "INDICATOR", "SESSION_USER", "CHARACTER",
+				"INITIALLY", "SET", "CHARACTER_LENGTH", "INNER", "SIZE",
+				"CHECK", "INPUT", "SMALLINT", "CLOSE", "INSENSITIVE", "SOME",
+				"COALESCE", "INSERT", "SPACE", "COLLATE", "INT", "SQL",
+				"COLLATION", "INTEGER", "SQLCA", "COLUMN", "INTERSECT",
+				"SQLCODE", "COMMIT", "INTERVAL", "SQLERROR", "CONNECT", "INTO",
+				"SQLSTATE", "CONNECTION", "IS", "SQLWARNING", "CONSTRAINT",
+				"ISOLATION", "SUBSTRING", "CONSTRAINTS", "JOIN", "SUM",
+				"CONTINUE", "KEY", "SYSTEM_USER", "CONVERT", "LANGUAGE",
+				"TABLE", "CORRESPONDING", "LAST", "TEMPORARY", "COUNT",
+				"LEADING", "THEN", "CREATE", "LEFT", "TIME", "CROSS", "LEVEL",
+				"TIMESTAMP", "CURRENT", "LIKE", "TIMEZONE_HOUR",
+				"CURRENT_DATE", "LOCAL", "TIMEZONE_MINUTE", "CURRENT_TIME",
+				"LOWER", "TO", "CURRENT_TIMESTAMP", "MATCH", "TRAILING",
+				"CURRENT_USER", "MAX", "TRANSACTION", "CURSOR", "MIN",
+				"TRANSLATE", "DATE", "MINUTE", "TRANSLATION", "DAY", "MODULE",
+				"TRIM", "DEALLOCATE", "MONTH", "TRUE", "DEC", "NAMES", "UNION",
+				"DECIMAL", "NATIONAL", "UNIQUE", "DECLARE", "NATURAL",
+				"UNKNOWN", "DEFAULT", "NCHAR", "UPDATE", "DEFERRABLE", "NEXT",
+				"UPPER", "DEFERRED", "NO", "USAGE", "DELETE", "NONE", "USER",
+				"DESC", "NOT", "USING", "DESCRIBE", "NULL", "VALUE",
+				"DESCRIPTOR", "NULLIF", "VALUES", "DIAGNOSTICS", "NUMERIC",
+				"VARCHAR", "DISCONNECT", "OCTET_LENGTH", "VARYING", "DISTINCT",
+				"OF", "VIEW", "DOMAIN", "ON", "WHEN", "DOUBLE", "ONLY",
+				"WHENEVER", "DROP", "OPEN", "WHERE", "ELSE", "OPTION", "WITH",
+				"END", "OR", "WORK", "END-EXEC", "ORDER", "WRITE", "ESCAPE",
+				"OUTER", "YEAR", "EXCEPT", "OUTPUT", "ZONE", "EXCEPTION" };
+		
+		TreeMap mySQLKeywordMap = new TreeMap();
+		
+		for (int i = 0; i < allMySQLKeywords.length; i++) {
+			mySQLKeywordMap.put(allMySQLKeywords[i], null);
+		}
+		
+		HashMap sql92KeywordMap = new HashMap(sql92Keywords.length);
+		
+		for (int i = 0; i < sql92Keywords.length; i++) {
+			sql92KeywordMap.put(sql92Keywords[i], null);
+		}
+		
+		Iterator it = sql92KeywordMap.keySet().iterator();
+		
+		while (it.hasNext()) {
+			mySQLKeywordMap.remove(it.next());
+		}
+		
+		StringBuffer keywordBuf = new StringBuffer();
+		
+		it = mySQLKeywordMap.keySet().iterator();
+		
+		if (it.hasNext()) {
+			keywordBuf.append(it.next().toString());
+		}
+		
+		while (it.hasNext()) {
+			keywordBuf.append(",");
+			keywordBuf.append(it.next().toString());
+		}
+	
+		mysqlKeywordsThatArentSQL92 = keywordBuf.toString();
+	}
+	
 	/** The connection to the database */
-	protected Connection conn;
+	protected ConnectionImpl conn;
 
 	/** The 'current' database name being used */
 	protected String database = null;
@@ -425,6 +616,34 @@
 	/** What character to use when quoting identifiers */
 	protected String quotedId = null;
 
+	// We need to provide factory-style methods so we can support both JDBC3 (and older)
+	// and JDBC4 runtimes, otherwise the class verifier complains...
+	
+	protected static DatabaseMetaData getInstance(
+			ConnectionImpl connToSet, String databaseToSet)
+			throws SQLException {
+		if (!Util.isJdbc4()) {
+			if (connToSet != null && connToSet.getUseInformationSchema()
+					&& connToSet.versionMeetsMinimum(5, 0, 7)) {
+				return new DatabaseMetaDataUsingInfoSchema(connToSet,
+						databaseToSet);
+			}
+
+			return new DatabaseMetaData(connToSet, databaseToSet);
+		}
+
+		if (connToSet != null && connToSet.getUseInformationSchema()
+				&& connToSet.versionMeetsMinimum(5, 0, 7)) {
+
+			return (DatabaseMetaData) Util.handleNewInstance(
+					JDBC_4_DBMD_IS_CTOR, new Object[] { connToSet,
+							databaseToSet });
+		}
+
+		return (DatabaseMetaData) Util.handleNewInstance(JDBC_4_DBMD_SHOW_CTOR,
+				new Object[] { connToSet, databaseToSet });
+	}
+	
 	/**
 	 * Creates a new DatabaseMetaData object.
 	 * 
@@ -433,7 +652,7 @@
 	 * @param databaseToSet
 	 *            DOCUMENT ME!
 	 */
-	public DatabaseMetaData(Connection connToSet, String databaseToSet) {
+	protected DatabaseMetaData(ConnectionImpl connToSet, String databaseToSet) {
 		this.conn = connToSet;
 		this.database = databaseToSet;
 
@@ -477,7 +696,7 @@
 	}
 	
 	static java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields,
-			java.util.ArrayList rows, Connection c) throws SQLException {
+			java.util.ArrayList rows, ConnectionImpl c) throws SQLException {
 		int fieldsLength = fields.length;
 
 		for (int i = 0; i < fieldsLength; i++) {
@@ -485,13 +704,14 @@
 			fields[i].setUseOldNameMetadata(true);
 		}
 
-		return new com.mysql.jdbc.ResultSet(c.getCatalog(), fields,
-				new RowDataStatic(rows), c, null);
+		return com.mysql.jdbc.ResultSetImpl.getInstance(c.getCatalog(), fields,
+				new RowDataStatic(rows), c, null, false);
 	}
 
 	private void convertToJdbcFunctionList(String catalog,
 			ResultSet proceduresRs, boolean needsClientFiltering, String db,
-			Map procedureRowsOrderedByName, int nameIndex) throws SQLException {
+			Map procedureRowsOrderedByName, int nameIndex,
+			Field[] fields) throws SQLException {
 		while (proceduresRs.next()) {
 			boolean shouldAdd = true;
 
@@ -502,28 +722,49 @@
 
 				if (db == null && procDb == null) {
 					shouldAdd = true;
-				} else if (db != null & db.equals(procDb)) {
+				} else if (db != null && db.equals(procDb)) {
 					shouldAdd = true;
 				}
 			}
 
 			if (shouldAdd) {
 				String functionName = proceduresRs.getString(nameIndex);
-				byte[][] rowData = new byte[8][];
-				rowData[0] = catalog == null ? null : s2b(catalog);
-				rowData[1] = null;
-				rowData[2] = s2b(functionName);
-				rowData[3] = null;
-				rowData[4] = null;
-				rowData[5] = null;
-				rowData[6] = null;
-				rowData[7] = s2b(Integer.toString(procedureReturnsResult));
+				
+				byte[][] rowData = null;
+				
+				if (fields != null && fields.length == 9) {
+					
+					rowData = new byte[8][];
+					rowData[0] = catalog == null ? null : s2b(catalog);         // PROCEDURE_CAT
+					rowData[1] = null;                                          // PROCEDURE_SCHEM
+					rowData[2] = s2b(functionName);                             // PROCEDURE_NAME
+					rowData[3] = null;                                          // reserved1
+					rowData[4] = null;                                          // reserved2
+					rowData[5] = null;                                          // reserved3
+					rowData[6] = s2b(proceduresRs.getString("comment"));        // REMARKS
+					rowData[7] = s2b(Integer.toString(procedureReturnsResult)); // PROCEDURE_TYPE
+					rowData[8] = s2b(functionName);
+				} else {
+					
+					rowData = new byte[6][];
+					
+					rowData[0] = catalog == null ? null : s2b(catalog);  // FUNCTION_CAT
+					rowData[1] = null;                                   // FUNCTION_SCHEM
+					rowData[2] = s2b(functionName);                      // FUNCTION_NAME
+					rowData[3] = s2b(proceduresRs.getString("comment")); // REMARKS
+					rowData[4] = s2b(Integer.toString(getJDBC4FunctionNoTableConstant())); // FUNCTION_TYPE
+					rowData[5] = s2b(functionName);                      // SPECFIC NAME
+				}
 
-				procedureRowsOrderedByName.put(functionName, rowData);
+				procedureRowsOrderedByName.put(functionName, new ByteArrayRow(rowData));
 			}
 		}
 	}
-
+	
+	protected int getJDBC4FunctionNoTableConstant() {
+		return 0;
+	}
+	
 	private void convertToJdbcProcedureList(boolean fromSelect, String catalog,
 			ResultSet proceduresRs, boolean needsClientFiltering, String db,
 			Map procedureRowsOrderedByName, int nameIndex) throws SQLException {
@@ -537,14 +778,14 @@
 
 				if (db == null && procDb == null) {
 					shouldAdd = true;
-				} else if (db != null & db.equals(procDb)) {
+				} else if (db != null && db.equals(procDb)) {
 					shouldAdd = true;
 				}
 			}
 
 			if (shouldAdd) {
 				String procedureName = proceduresRs.getString(nameIndex);
-				byte[][] rowData = new byte[8][];
+				byte[][] rowData = new byte[9][];
 				rowData[0] = catalog == null ? null : s2b(catalog);
 				rowData[1] = null;
 				rowData[2] = s2b(procedureName);
@@ -560,21 +801,30 @@
 						.toString(procedureReturnsResult) : Integer
 						.toString(procedureResultUnknown));
 
-				procedureRowsOrderedByName.put(procedureName, rowData);
+				rowData[8] = s2b(procedureName);
+				
+				procedureRowsOrderedByName.put(procedureName, new ByteArrayRow(rowData));
 			}
 		}
 	}
 
-	private byte[][] convertTypeDescriptorToProcedureRow(
+	private ResultSetRow convertTypeDescriptorToProcedureRow(
 			byte[] procNameAsBytes, String paramName, boolean isOutParam,
-			boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc)
+			boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc,
+			boolean forGetFunctionColumns,
+			int ordinal)
 			throws SQLException {
-		byte[][] row = new byte[14][];
+		byte[][] row = forGetFunctionColumns ? new byte[17][] : new byte[14][];
 		row[0] = null; // PROCEDURE_CAT
 		row[1] = null; // PROCEDURE_SCHEM
 		row[2] = procNameAsBytes; // PROCEDURE/NAME
 		row[3] = s2b(paramName); // COLUMN_NAME
 		// COLUMN_TYPE
+		
+		// NOTE: For JDBC-4.0, we luck out here for functions
+		// because the values are the same for functionColumn....
+		// and they're not using Enumerations....
+		
 		if (isInParam && isOutParam) {
 			row[4] = s2b(String.valueOf(procedureColumnInOut));
 		} else if (isInParam) {
@@ -588,24 +838,24 @@
 		}
 		row[5] = s2b(Short.toString(typeDesc.dataType)); // DATA_TYPE
 		row[6] = s2b(typeDesc.typeName); // TYPE_NAME
-		row[7] = s2b(Integer.toString(typeDesc.columnSize)); // PRECISION
+		row[7] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize.toString()); // PRECISION
 		row[8] = s2b(Integer.toString(typeDesc.bufferLength)); // LENGTH
-		row[9] = s2b(Integer.toString(typeDesc.decimalDigits)); // SCALE
+		row[9] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString()); // SCALE
 		row[10] = s2b(Integer.toString(typeDesc.numPrecRadix)); // RADIX
 		// Map 'column****' to 'procedure****'
 		switch (typeDesc.nullability) {
 		case columnNoNulls:
-			row[11] = s2b(Integer.toString(procedureNoNulls)); // NULLABLE
+			row[11] = s2b(String.valueOf(procedureNoNulls)); // NULLABLE
 
 			break;
 
 		case columnNullable:
-			row[11] = s2b(Integer.toString(procedureNullable)); // NULLABLE
+			row[11] = s2b(String.valueOf(procedureNullable)); // NULLABLE
 
 			break;
 
 		case columnNullableUnknown:
-			row[11] = s2b(Integer.toString(procedureNullableUnknown)); // nullable
+			row[11] = s2b(String.valueOf(procedureNullableUnknown)); // nullable
 
 			break;
 
@@ -614,8 +864,23 @@
 					"Internal error while parsing callable statement metadata (unknown nullability value fount)",
 					SQLError.SQL_STATE_GENERAL_ERROR);
 		}
+		
 		row[12] = null;
-		return row;
+		
+		if (forGetFunctionColumns) {
+			// CHAR_OCTECT_LENGTH
+			row[13] = null;
+			
+			// ORDINAL_POSITION
+			row[14] = s2b(String.valueOf(ordinal));
+			
+			// IS_NULLABLE
+			row[15] = Constants.EMPTY_BYTE_ARRAY;
+			
+			row[16] = s2b(paramName);
+		}
+		
+		return new ByteArrayRow(row);
 	}
 
 	/**
@@ -820,7 +1085,7 @@
 		}
 	
 		row[2] = s2b(commentBuf.toString());
-		rows.add(row);
+		rows.add(new ByteArrayRow(row));
 	
 		return rows;
 	}
@@ -888,7 +1153,20 @@
 						quoteChar).append(catalog).append(quoteChar)
 						.append(".").append(quoteChar).append(tableToExtract)
 						.append(quoteChar).toString();
-				rs = stmt.executeQuery(query);
+				
+				try {
+					rs = stmt.executeQuery(query);
+				} catch (SQLException sqlEx) {
+					// Table might've disappeared on us, not really an error
+					String sqlState = sqlEx.getSQLState();
+					
+					if (!"42S02".equals(sqlState) && 
+							sqlEx.getErrorCode() != MysqlErrorNumbers.ER_NO_SUCH_TABLE) {
+						throw sqlEx;
+					}
+					
+					continue;
+				}
 
 				while (rs.next()) {
 					extractForeignKeyForTable(rows, rs, catalog);
@@ -1099,7 +1377,7 @@
 													java.sql.DatabaseMetaData.bestRowNotPseudo)
 											.getBytes();
 
-									rows.add(rowVal);
+									rows.add(new ByteArrayRow(rowVal));
 								}
 							}
 						}
@@ -1163,6 +1441,13 @@
 	 */
 	private void getCallStmtParameterTypes(String catalog, String procName,
 			String parameterNamePattern, List resultRows) throws SQLException {
+		getCallStmtParameterTypes(catalog, procName, 
+				parameterNamePattern, resultRows, false);
+	}
+	
+	private void getCallStmtParameterTypes(String catalog, String procName,
+			String parameterNamePattern, List resultRows, 
+			boolean forGetFunctionColumns) throws SQLException {
 		java.sql.Statement paramRetrievalStmt = null;
 		java.sql.ResultSet paramRetrievalRs = null;
 
@@ -1188,11 +1473,11 @@
 
 		String quoteChar = getIdentifierQuoteString();
 
-		String storageDefnDelims = "(" + quoteChar;
-		String storageDefnClosures = ")" + quoteChar;
-
-		// First try 'select from mysql.proc, as this is easier to parse...
 		String parameterDef = null;
+	
+		boolean isProcedureInAnsiMode = false;
+		String storageDefnDelims = null;
+		String storageDefnClosures = null;
 		
 		try {
 			paramRetrievalStmt = this.conn.getMetadataSafeStatement();
@@ -1292,93 +1577,86 @@
 				String procedureDef = parsingFunction ? paramRetrievalRs
 						.getString("Create Function") : paramRetrievalRs
 						.getString("Create Procedure");
+						
+				if (procedureDef == null || procedureDef.length() == 0) {
+					throw SQLError.createSQLException("User does not have access to metadata required to determine " +
+							"stored procedure parameter types. If rights can not be granted, configure connection with \"noAccessToProcedureBodies=true\" " +
+							"to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.",
+							SQLError.SQL_STATE_GENERAL_ERROR);		
+				}
 
+				try {
+					String sqlMode = paramRetrievalRs.getString("sql_mode");
+					
+					if (StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1) {
+						isProcedureInAnsiMode = true;
+					}
+				} catch (SQLException sqlEx) {
+					// doesn't exist
+				}
+
+				String identifierMarkers = isProcedureInAnsiMode ? "`\"" : "`";
+				String identifierAndStringMarkers = "'" + identifierMarkers;
+				storageDefnDelims = "(" + identifierMarkers;
+				storageDefnClosures = ")" + identifierMarkers;
+				
+				// sanitize/normalize by stripping out comments
+				procedureDef = StringUtils.stripComments(procedureDef, 
+						identifierAndStringMarkers, identifierAndStringMarkers, true, false, true, true);
+				
 				int openParenIndex = StringUtils
-						.indexOfIgnoreCaseRespectQuotes(0, procedureDef, "(",
-								quoteChar.charAt(0), !this.conn
-										.isNoBackslashEscapesSet());
+				.indexOfIgnoreCaseRespectQuotes(0, procedureDef, "(",
+						quoteChar.charAt(0), !this.conn
+						.isNoBackslashEscapesSet());
+				int endOfParamDeclarationIndex = 0;
 
-				String beforeBegin = null;
+				endOfParamDeclarationIndex = endPositionOfParameterDeclaration(
+						openParenIndex, procedureDef, quoteChar);
 
-				// Try and fudge this with the 'begin' statement
-				int beginIndex = 0;
+				if (parsingFunction) {
 
-				if (!parsingFunction) {
-					beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
-							procedureDef, "\nbegin", quoteChar.charAt(0),
-							!this.conn.isNoBackslashEscapesSet());
-				} else {
-					// Grab the return column first, since it needs
+					// Grab the return column since it needs
 					// to go first in the output result set
 					int returnsIndex = StringUtils
-							.indexOfIgnoreCaseRespectQuotes(0, procedureDef,
-									" RETURNS ", quoteChar.charAt(0),
-									!this.conn.isNoBackslashEscapesSet());
+					.indexOfIgnoreCaseRespectQuotes(0, procedureDef,
+							" RETURNS ", quoteChar.charAt(0),
+							!this.conn.isNoBackslashEscapesSet());
 
-					beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(
-							returnsIndex, procedureDef, "\nbegin", quoteChar
-									.charAt(0), !this.conn
-									.isNoBackslashEscapesSet());
+					int endReturnsDef = findEndOfReturnsClause(procedureDef,
+							quoteChar, returnsIndex);
 
-					if (beginIndex == -1) {
-						beginIndex = StringUtils
-								.indexOfIgnoreCaseRespectQuotes(0,
-										procedureDef, "\n",
-										quoteChar.charAt(0), !this.conn
-												.isNoBackslashEscapesSet());
-					}
+					// Trim off whitespace after "RETURNS"
 
-					// Okay, give up...
+					int declarationStart = returnsIndex + "RETURNS ".length();
 
-					if (beginIndex == -1) {
-						throw SQLError.createSQLException(
-								"Driver requires declaration of procedure to either contain a '\\nbegin' or '\\n' to follow argument declaration, or SELECT privilege on mysql.proc to parse column types.",
-								SQLError.SQL_STATE_GENERAL_ERROR);
+					while (declarationStart < procedureDef.length()) {
+						if (Character.isWhitespace(procedureDef.charAt(declarationStart))) {
+							declarationStart++;
+						} else {
+							break;
+						}
 					}
 
-					String returnsDefn = procedureDef.substring(returnsIndex
-							+ "RETURNS ".length(), beginIndex);
+					String returnsDefn = procedureDef.substring(declarationStart, endReturnsDef).trim();
 					TypeDescriptor returnDescriptor = new TypeDescriptor(
 							returnsDefn, null);
 
 					resultRows.add(convertTypeDescriptorToProcedureRow(
 							procNameAsBytes, "", false, false, true,
-							returnDescriptor));
-
-					beginIndex = returnsIndex; // further processing needs to
-					// look before "RETURNS" token
+							returnDescriptor, forGetFunctionColumns, 0));
 				}
 
-				// Bah, we _really_ need information schema here
-
-				if (beginIndex != -1) {
-					beforeBegin = procedureDef.substring(0, beginIndex);
-				} else {
-					beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
-							procedureDef, "\n", quoteChar.charAt(0), !this.conn
-									.isNoBackslashEscapesSet());
-
-					if (beginIndex != -1) {
-						beforeBegin = procedureDef.substring(0, beginIndex);
-					} else {
-						throw SQLError.createSQLException(
-								"Driver requires declaration of procedure to either contain a '\\nbegin' or '\\n' to follow argument declaration, or SELECT privilege on mysql.proc to parse column types.",
-								SQLError.SQL_STATE_GENERAL_ERROR);
-					}
-
-				}
-
-				int endParenIndex = beforeBegin.lastIndexOf(')');
-
-				if ((openParenIndex == -1) || (endParenIndex == -1)) {
+				if ((openParenIndex == -1)
+						|| (endOfParamDeclarationIndex == -1)) {
 					// parse error?
-					throw SQLError.createSQLException(
+					throw SQLError
+					.createSQLException(
 							"Internal error when parsing callable statement metadata",
 							SQLError.SQL_STATE_GENERAL_ERROR);
 				}
 
 				parameterDef = procedureDef.substring(openParenIndex + 1,
-						endParenIndex);
+						endOfParamDeclarationIndex);
 			}
 		} finally {
 			SQLException sqlExRethrow = null;
@@ -1409,6 +1687,8 @@
 		}
 
 		if (parameterDef != null) {
+			int ordinal = 1;
+			
 			List parseList = StringUtils.split(parameterDef, ",",
 					storageDefnDelims, storageDefnClosures, true);
 
@@ -1490,13 +1770,19 @@
 								SQLError.SQL_STATE_GENERAL_ERROR);
 					}
 
+					if ((paramName.startsWith("`") && paramName.endsWith("`")) || 
+							(isProcedureInAnsiMode && paramName.startsWith("\"") && paramName.endsWith("\""))) {
+						paramName = paramName.substring(1, paramName.length() - 1);
+					}
+
 					int wildCompareRes = StringUtils.wildCompare(paramName,
 							parameterNamePattern);
 
 					if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
-						byte[][] row = convertTypeDescriptorToProcedureRow(
+						ResultSetRow row = convertTypeDescriptorToProcedureRow(
 								procNameAsBytes, paramName, isOutParam,
-								isInParam, false, typeDesc);
+								isInParam, false, typeDesc, forGetFunctionColumns,
+								ordinal++);
 
 						resultRows.add(row);
 					}
@@ -1512,8 +1798,121 @@
 			// exist, is it an error....
 		}
 	}
+	/**
+	 * Finds the end of the parameter declaration from the output of "SHOW
+	 * CREATE PROCEDURE".
+	 * 
+	 * @param beginIndex
+	 *            should be the index of the procedure body that contains the
+	 *            first "(".
+	 * @param procedureDef
+	 *            the procedure body
+	 * @param quoteChar
+	 *            the identifier quote character in use
+	 * @return the ending index of the parameter declaration, not including the
+	 *         closing ")"
+	 * @throws SQLException
+	 *             if a parse error occurs.
+	 */
+	private int endPositionOfParameterDeclaration(int beginIndex,
+			String procedureDef, String quoteChar) throws SQLException {
+		int currentPos = beginIndex + 1;
+		int parenDepth = 1; // counting the first openParen
 
+		while (parenDepth > 0 && currentPos < procedureDef.length()) {
+			int closedParenIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(
+					currentPos, procedureDef, ")", quoteChar.charAt(0),
+					!this.conn.isNoBackslashEscapesSet());
+
+			if (closedParenIndex != -1) {
+				int nextOpenParenIndex = StringUtils
+						.indexOfIgnoreCaseRespectQuotes(currentPos,
+								procedureDef, "(", quoteChar.charAt(0),
+								!this.conn.isNoBackslashEscapesSet());
+
+				if (nextOpenParenIndex != -1
+						&& nextOpenParenIndex < closedParenIndex) {
+					parenDepth++;
+					currentPos = closedParenIndex + 1; // set after closed
+														// paren that increases
+														// depth
+				} else {
+					parenDepth--;
+					currentPos = closedParenIndex; // start search from same
+													// position
+				}
+			} else {
+				// we should always get closed paren of some sort
+				throw SQLError
+						.createSQLException(
+								"Internal error when parsing callable statement metadata",
+								SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+
+		return currentPos;
+	}
+
 	/**
+	 * Finds the end of the RETURNS clause for SQL Functions by using any of the
+	 * keywords allowed after the RETURNS clause, or a label.
+	 * 
+	 * @param procedureDefn
+	 *            the function body containing the definition of the function
+	 * @param quoteChar
+	 *            the identifier quote string in use
+	 * @param positionOfReturnKeyword
+	 *            the position of "RETRUNS" in the definition
+	 * @return the end of the returns clause
+	 * @throws SQLException
+	 *             if a parse error occurs
+	 */
+	private int findEndOfReturnsClause(String procedureDefn, String quoteChar,
+			int positionOfReturnKeyword) throws SQLException {
+		/*
+		 * characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL |
+		 * NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY {
+		 * DEFINER | INVOKER } | COMMENT 'string'
+		 */
+
+		String[] tokens = new String[] { "LANGUAGE", "NOT", "DETERMINISTIC",
+				"CONTAINS", "NO", "READ", "MODIFIES", "SQL", "COMMENT", "BEGIN", 
+				"RETURN" };
+
+		int startLookingAt = positionOfReturnKeyword + "RETURNS".length() + 1;
+
+		for (int i = 0; i < tokens.length; i++) {
+			int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes(
+					startLookingAt, procedureDefn, tokens[i], quoteChar
+							.charAt(0), !this.conn.isNoBackslashEscapesSet());
+
+			if (endOfReturn != -1) {
+				return endOfReturn;
+			}
+		}
+
+		// Label?
+		int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes(
+				startLookingAt, procedureDefn, ":", quoteChar.charAt(0),
+				!this.conn.isNoBackslashEscapesSet());
+
+		if (endOfReturn != -1) {
+			// seek back until whitespace
+			for (int i = endOfReturn; i > 0; i--) {
+				if (Character.isWhitespace(procedureDefn.charAt(i))) {
+					return i;
+				}
+			}
+		}
+
+		// We can't parse it.
+
+		throw SQLError.createSQLException(
+				"Internal error when parsing callable statement metadata",
+				SQLError.SQL_STATE_GENERAL_ERROR);
+	}
+	
+	/**
 	 * Parses the cascade option string and returns the DBMD constant that
 	 * represents it (for deletes)
 	 * 
@@ -1624,7 +2023,7 @@
 			while (results.next()) {
 				byte[][] rowVal = new byte[1][];
 				rowVal[0] = results.getBytes(1);
-				tuples.add(rowVal);
+				tuples.add(new ByteArrayRow(rowVal));
 			}
 
 			return buildResultSet(fields, tuples);
@@ -1798,7 +2197,7 @@
 						tuple[5] = s2b(fullUser.toString());
 						tuple[6] = s2b(privilege);
 						tuple[7] = null;
-						grantRows.add(tuple);
+						grantRows.add(new ByteArrayRow(tuple));
 					}
 				}
 			}
@@ -1900,7 +2299,7 @@
 
 		final String colPattern = columnNamePattern;
 
-		Field[] fields = new Field[18];
+		Field[] fields = new Field[23];
 		fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 255);
 		fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 0);
 		fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 255);
@@ -1921,6 +2320,11 @@
 				.toString(Integer.MAX_VALUE).length());
 		fields[16] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 10);
 		fields[17] = new Field("", "IS_NULLABLE", Types.CHAR, 3);
+		fields[18] = new Field("", "SCOPE_CATALOG", Types.CHAR, 255);
+		fields[19] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 255);
+		fields[20] = new Field("", "SCOPE_TABLE", Types.CHAR, 255);
+		fields[21] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 10);
+		fields[22] = new Field("", "IS_AUTOINCREMENT", Types.CHAR, 3);
 
 		final ArrayList rows = new ArrayList();
 		final Statement stmt = this.conn.getMetadataSafeStatement();
@@ -2049,7 +2453,7 @@
 											.getString("Field");
 
 									ordinalFixUpMap.put(fullOrdColName,
-											new Integer(fullOrdinalPos++));
+											Constants.integerValueOf(fullOrdinalPos++));
 								}
 							}
 
@@ -2058,7 +2462,7 @@
 							int ordPos = 1;
 
 							while (results.next()) {
-								byte[][] rowVal = new byte[18][];
+								byte[][] rowVal = new byte[23][];
 								rowVal[0] = s2b(catalog); // TABLE_CAT
 								rowVal[1] = null; // TABLE_SCHEM (No schemas
 								// in MySQL)
@@ -2076,12 +2480,9 @@
 								// DATA_TYPE (jdbc)
 								rowVal[5] = s2b(typeDesc.typeName); // TYPE_NAME
 								// (native)
-								rowVal[6] = s2b(Integer
-										.toString(typeDesc.columnSize));
-								rowVal[7] = s2b(Integer
-										.toString(typeDesc.bufferLength));
-								rowVal[8] = s2b(Integer
-										.toString(typeDesc.decimalDigits));
+								rowVal[6] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize.toString());
+								rowVal[7] = s2b(Integer.toString(typeDesc.bufferLength));
+								rowVal[8] = typeDesc.decimalDigits == null ? null : s2b(typeDesc.decimalDigits.toString());
 								rowVal[9] = s2b(Integer
 										.toString(typeDesc.numPrecRadix));
 								rowVal[10] = s2b(Integer
@@ -2110,7 +2511,15 @@
 
 								rowVal[13] = new byte[] { (byte) '0' }; // SQL_DATA_TYPE
 								rowVal[14] = new byte[] { (byte) '0' }; // SQL_DATE_TIME_SUB
-								rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH
+								
+								if (StringUtils.indexOfIgnoreCase(typeDesc.typeName, "CHAR") != -1 ||
+										StringUtils.indexOfIgnoreCase(typeDesc.typeName, "BLOB") != -1 ||
+										StringUtils.indexOfIgnoreCase(typeDesc.typeName, "TEXT") != -1 ||
+										StringUtils.indexOfIgnoreCase(typeDesc.typeName, "BINARY") != -1) {
+									rowVal[15] = rowVal[6]; // CHAR_OCTET_LENGTH
+								} else {
+									rowVal[15] = null;
+								}
 
 								// ORDINAL_POSITION
 								if (!fixUpOrdinalsRequired) {
@@ -2133,8 +2542,25 @@
 								}
 
 								rowVal[17] = s2b(typeDesc.isNullable);
-
-								rows.add(rowVal);
+								
+								// We don't support REF or DISTINCT types
+								rowVal[18] = null;
+								rowVal[19] = null;
+								rowVal[20] = null;
+								rowVal[21] = null;
+								
+								rowVal[22] = s2b("");
+								
+								String extra = results.getString("Extra");
+								
+								if (extra != null) {
+									rowVal[22] = s2b(StringUtils
+											.indexOfIgnoreCase(extra,
+													"auto_increment") != -1 ? "YES"
+											: "NO");
+								}
+								
+								rows.add(new ByteArrayRow(rowVal));
 							}
 						} finally {
 							if (results != null) {
@@ -2391,7 +2817,7 @@
 														.toString(
 																java.sql.DatabaseMetaData.importedKeyNotDeferrable)
 														.getBytes();
-												tuples.add(tuple);
+												tuples.add(new ByteArrayRow(tuple));
 												keySeq++;
 											}
 										}
@@ -2515,7 +2941,7 @@
 	 *             DOCUMENT ME!
 	 */
 	public String getDriverVersion() throws java.sql.SQLException {
-		return "@MYSQL_CJ_FULL_PROD_NAME@ ( $Date: 2006-10-19 17:47:48 +0200 (Thu, 19 Oct 2006) $, $Revision: 5908 $ )";
+		return "@MYSQL_CJ_FULL_PROD_NAME@ ( Revision: @MYSQL_CJ_REVISION@ )";
 	}
 
 	/**
@@ -3136,11 +3562,11 @@
 
 							if (unique) {
 								if (indexIsUnique) {
-									rows.add(row);
+									rows.add(new ByteArrayRow(row));
 								}
 							} else {
 								// All rows match
-								rows.add(row);
+								rows.add(new ByteArrayRow(row));
 							}
 						}
 					} finally {
@@ -3477,7 +3903,6 @@
 
 						rs = stmt.executeQuery(queryBuf.toString());
 
-						ArrayList tuples = new ArrayList();
 						TreeMap sortMap = new TreeMap();
 
 						while (rs.next()) {
@@ -3506,7 +3931,7 @@
 						Iterator sortedIterator = sortMap.values().iterator();
 
 						while (sortedIterator.hasNext()) {
-							rows.add(sortedIterator.next());
+							rows.add(new ByteArrayRow((byte[][])sortedIterator.next()));
 						}
 
 					} finally {
@@ -3602,7 +4027,6 @@
 	public java.sql.ResultSet getProcedureColumns(String catalog,
 			String schemaPattern, String procedureNamePattern,
 			String columnNamePattern) throws SQLException {
-
 		Field[] fields = new Field[13];
 
 		fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
@@ -3618,21 +4042,36 @@
 		fields[10] = new Field("", "RADIX", Types.SMALLINT, 0);
 		fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0);
 		fields[12] = new Field("", "REMARKS", Types.CHAR, 0);
+		
+		return getProcedureOrFunctionColumns(
+				fields, catalog, schemaPattern,
+				procedureNamePattern, columnNamePattern,
+				true, true);
+	}
+	
+	protected java.sql.ResultSet getProcedureOrFunctionColumns(
+			Field[] fields, String catalog, String schemaPattern,
+			String procedureOrFunctionNamePattern,
+			String columnNamePattern, boolean returnProcedures,
+			boolean returnFunctions) throws SQLException {
 
 		List proceduresToExtractList = new ArrayList();
 
 		if (supportsStoredProcedures()) {
-			if ((procedureNamePattern.indexOf("%") == -1)
-					&& (procedureNamePattern.indexOf("?") == -1)) {
-				proceduresToExtractList.add(procedureNamePattern);
+			if ((procedureOrFunctionNamePattern.indexOf("%") == -1)
+					&& (procedureOrFunctionNamePattern.indexOf("?") == -1)) {
+				proceduresToExtractList.add(procedureOrFunctionNamePattern);
 			} else {
 				
 				ResultSet procedureNameRs = null;
 
 				try {
 
-					procedureNameRs = getProcedures(catalog, schemaPattern,
-							procedureNamePattern);
+					procedureNameRs = getProceduresAndOrFunctions(
+							createFieldMetadataForGetProcedures(),
+							catalog, schemaPattern,
+							procedureOrFunctionNamePattern, returnProcedures,
+							returnFunctions);
 
 					while (procedureNameRs.next()) {
 						proceduresToExtractList.add(procedureNameRs
@@ -3668,7 +4107,8 @@
 			String procName = (String) iter.next();
 
 			getCallStmtParameterTypes(catalog, procName, columnNamePattern,
-					resultRows);
+					resultRows, 
+					fields.length == 17 /* for getFunctionColumns */);
 		}
 
 		return buildResultSet(fields, resultRows);
@@ -3717,11 +4157,29 @@
 	public java.sql.ResultSet getProcedures(String catalog,
 			String schemaPattern, String procedureNamePattern)
 			throws SQLException {
-		return getProceduresAndOrFunctions(catalog, schemaPattern,
+		Field[] fields = createFieldMetadataForGetProcedures();
+		
+		return getProceduresAndOrFunctions(fields, catalog, schemaPattern,
 				procedureNamePattern, true, true);
 	}
+
+	private Field[] createFieldMetadataForGetProcedures() {
+		Field[] fields = new Field[9];
+		fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 255);
+		fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 255);
+		fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 255);
+		fields[3] = new Field("", "reserved1", Types.CHAR, 0);
+		fields[4] = new Field("", "reserved2", Types.CHAR, 0);
+		fields[5] = new Field("", "reserved3", Types.CHAR, 0);
+		fields[6] = new Field("", "REMARKS", Types.CHAR, 255);
+		fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 6);
+		fields[8] = new Field("", "SPECIFIC_NAME", Types.CHAR, 255);
+		
+		return fields;
+	}
 	
 	protected java.sql.ResultSet getProceduresAndOrFunctions(
+			final Field[] fields,
 			String catalog,
 			String schemaPattern,
 			String procedureNamePattern,
@@ -3738,16 +4196,6 @@
 			}
 		}
 
-		Field[] fields = new Field[8];
-		fields[0] = new Field("", "PROCEDURE_CAT", Types.CHAR, 0);
-		fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
-		fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
-		fields[3] = new Field("", "reserved1", Types.CHAR, 0);
-		fields[4] = new Field("", "reserved2", Types.CHAR, 0);
-		fields[5] = new Field("", "reserved3", Types.CHAR, 0);
-		fields[6] = new Field("", "REMARKS", Types.CHAR, 0);
-		fields[7] = new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0);
-
 		final ArrayList procedureRows = new ArrayList();
 
 		if (supportsStoredProcedures()) {
@@ -3762,8 +4210,8 @@
 					boolean fromSelect = false;
 					ResultSet proceduresRs = null;
 					boolean needsClientFiltering = true;
-					PreparedStatement proceduresStmt = conn
-							.clientPrepareStatement("SELECT name, type FROM mysql.proc WHERE name like ? and db <=> ? ORDER BY name");
+					PreparedStatement proceduresStmt = (PreparedStatement) conn
+							.clientPrepareStatement("SELECT name, type, comment FROM mysql.proc WHERE name like ? and db <=> ? ORDER BY name");
 
 					try {
 						//
@@ -3809,7 +4257,7 @@
 								nameIndex = 1;
 							}
 
-							proceduresStmt = conn
+							proceduresStmt = (PreparedStatement) conn
 									.clientPrepareStatement("SHOW PROCEDURE STATUS LIKE ?");
 
 							if (proceduresStmt.getMaxRows() != 0) {
@@ -3833,7 +4281,7 @@
 								proceduresStmt.close();
 							}
 
-							proceduresStmt = conn
+							proceduresStmt = (PreparedStatement) conn
 									.clientPrepareStatement("SHOW FUNCTION STATUS LIKE ?");
 
 							if (proceduresStmt.getMaxRows() != 0) {
@@ -3847,7 +4295,8 @@
 							if (returnFunctions) {
 								convertToJdbcFunctionList(db, proceduresRs,
 									needsClientFiltering, db,
-									procedureRowsOrderedByName, nameIndex);
+									procedureRowsOrderedByName, nameIndex,
+									fields);
 							}
 						}
 
@@ -3889,6 +4338,7 @@
 		return buildResultSet(fields, procedureRows);
 	}
 
+
 	/**
 	 * What's the database vendor's preferred term for "procedure"?
 	 * 
@@ -3956,7 +4406,7 @@
 			tuple[PK_NAME] = null; // not available from show table status
 			tuple[DEFERRABILITY] = s2b(Integer
 					.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable));
-			tuples.add(tuple);
+			tuples.add(new ByteArrayRow(tuple));
 		}
 	}
 
@@ -3976,8 +4426,9 @@
 	 *             DOCUMENT ME!
 	 */
 	public java.sql.ResultSet getSchemas() throws SQLException {
-		Field[] fields = new Field[1];
-		fields[0] = new Field("", "TABLE_SCHEM", java.sql.Types.CHAR, 0);
+		Field[] fields = new Field[2];
+	    fields[0] = new Field("", "TABLE_SCHEM", java.sql.Types.CHAR, 0);
+	    fields[1] = new Field("", "TABLE_CATALOG", java.sql.Types.CHAR, 0);
 
 		ArrayList tuples = new ArrayList();
 		java.sql.ResultSet results = buildResultSet(fields, tuples);
@@ -4023,22 +4474,7 @@
 	 *             DOCUMENT ME!
 	 */
 	public String getSQLKeywords() throws SQLException {
-		return "AUTO_INCREMENT,BINARY,BLOB,ENUM,INFILE,LOAD,MEDIUMINT,OPTION,OUTFILE,REPLACE,SET,TEXT,UNSIGNED,ZEROFILL";
-
-		/*
-		 * [20:44] root at test> select GROUP_CONCAT(reserved.a) from reserved left
-		 * join sql92 on (reserved.a=sql92.a) where sql92.a IS NULL GROUP BY
-		 * (reserved.b) \G ************************** 1. row
-		 * *************************** GROUP_CONCAT(reserved.a):
-		 * RETURN,REQUIRE,REPLACE,REPEAT,RENAME,
-		 * REGEXP,PURGE,SPECIFIC,SPATIAL,SONAME,SHOW,SEPARATOR,SENSITIVE,
-		 * SECOND_MICROSECOND,RLIKE,MOD,MINUTE_SECOND,MINUTE_MICROSECOND,
-		 * MIDDLEINT,MEDIUMTEXT,MEDIUMINT,MEDIUMBLOB,MASTER_SERVER_ID,
-		 * LOW_PRIORITY,LOOP,LONGTEXT,OUTFILE,OUT,OPTIONALLY,OPTIMIZE,
-		 * NO_WRITE_TO_BINLOG,LONGBLOB,ZEROFILL,UTC_DATE,USER_RESOURCES,USE,
-		 * UNSIGNED,UNLOCK,UNDO,UTC_TIME,UTC_TIMESTAMP,YEAR_MONTH,XOR,WHILE,
-		 * VARCHARACTER,VARBINARY,TINYTEXT,SQL_T
-		 */
+		return mysqlKeywordsThatArentSQL92;
 	}
 
 	/**
@@ -4251,7 +4687,7 @@
 								tuple[4] = s2b(fullUser.toString());
 								tuple[5] = s2b(privilege);
 								tuple[6] = null;
-								grantRows.add(tuple);
+								grantRows.add(new ByteArrayRow(tuple));
 							}
 						} finally {
 							if (columnResults != null) {
@@ -4509,7 +4945,7 @@
 									.iterator();
 
 							while (tablesIter.hasNext()) {
-								tuples.add(tablesIter.next());
+								tuples.add(new ByteArrayRow((byte[][])tablesIter.next()));
 							}
 						}
 
@@ -4518,7 +4954,7 @@
 									.iterator();
 
 							while (viewsIter.hasNext()) {
-								tuples.add(viewsIter.next());
+								tuples.add(new ByteArrayRow((byte[][])viewsIter.next()));
 							}
 						}
 
@@ -4571,17 +5007,17 @@
 
 		byte[][] tableTypeRow = new byte[1][];
 		tableTypeRow[0] = TABLE_AS_BYTES;
-		tuples.add(tableTypeRow);
+		tuples.add(new ByteArrayRow(tableTypeRow));
 
 		if (this.conn.versionMeetsMinimum(5, 0, 1)) {
 			byte[][] viewTypeRow = new byte[1][];
 			viewTypeRow[0] = VIEW_AS_BYTES;
-			tuples.add(viewTypeRow);
+			tuples.add(new ByteArrayRow(viewTypeRow));
 		}
 
 		byte[][] tempTypeRow = new byte[1][];
 		tempTypeRow[0] = s2b("LOCAL TEMPORARY");
-		tuples.add(tempTypeRow);
+		tuples.add(new ByteArrayRow(tempTypeRow));
 
 		return buildResultSet(fields, tuples);
 	}
@@ -4760,7 +5196,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: BOOL (silently converted to TINYINT(1)) JDBC Type: BIT
@@ -4792,7 +5228,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: TINYINT JDBC Type: TINYINT
@@ -4824,8 +5260,37 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
+		
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TINYINT UNSIGNED");
+		rowVal[1] = Integer.toString(java.sql.Types.TINYINT).getBytes();
 
+		// JDBC Data type
+		rowVal[2] = s2b("3"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M)] [UNSIGNED] [ZEROFILL]"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("false"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("true"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("TINYINT UNSIGNED"); // Locale Type Name
+		rowVal[13] = s2b("0"); // Minimum Scale
+		rowVal[14] = s2b("0"); // Maximum Scale
+		rowVal[15] = s2b("0"); // SQL Data Type (not used)
+		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
+		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
+		tuples.add(new ByteArrayRow(rowVal));
+
 		/*
 		 * MySQL Type: BIGINT JDBC Type: BIGINT
 		 */
@@ -4856,8 +5321,37 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
+		
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("BIGINT UNSIGNED");
+		rowVal[1] = Integer.toString(java.sql.Types.BIGINT).getBytes();
 
+		// JDBC Data type
+		rowVal[2] = s2b("20"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("false"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("true"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("BIGINT UNSIGNED"); // Locale Type Name
+		rowVal[13] = s2b("0"); // Minimum Scale
+		rowVal[14] = s2b("0"); // Maximum Scale
+		rowVal[15] = s2b("0"); // SQL Data Type (not used)
+		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
+		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
+		tuples.add(new ByteArrayRow(rowVal));
+
 		/*
 		 * MySQL Type: LONG VARBINARY JDBC Type: LONGVARBINARY
 		 */
@@ -4888,7 +5382,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: MEDIUMBLOB JDBC Type: LONGVARBINARY
@@ -4920,7 +5414,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: LONGBLOB JDBC Type: LONGVARBINARY
@@ -4954,7 +5448,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: BLOB JDBC Type: LONGVARBINARY
@@ -4986,7 +5480,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: TINYBLOB JDBC Type: LONGVARBINARY
@@ -5018,7 +5512,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: VARBINARY (sliently converted to VARCHAR(M) BINARY) JDBC
@@ -5051,7 +5545,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: BINARY (silently converted to CHAR(M) BINARY) JDBC Type:
@@ -5084,7 +5578,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: LONG VARCHAR JDBC Type: LONGVARCHAR
@@ -5116,7 +5610,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: MEDIUMTEXT JDBC Type: LONGVARCHAR
@@ -5148,7 +5642,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: LONGTEXT JDBC Type: LONGVARCHAR
@@ -5182,7 +5676,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: TEXT JDBC Type: LONGVARCHAR
@@ -5214,7 +5708,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: TINYTEXT JDBC Type: LONGVARCHAR
@@ -5246,7 +5740,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: CHAR JDBC Type: CHAR
@@ -5278,8 +5772,20 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
+		// The maximum number of digits for DECIMAL or NUMERIC is 65 (64 from MySQL 5.0.3 to 5.0.5). 
+		
+		int decimalPrecision = 254;
+		
+		if (this.conn.versionMeetsMinimum(5,0,3)) {
+			if (this.conn.versionMeetsMinimum(5, 0, 6)) {
+				decimalPrecision = 65;
+			} else {
+				decimalPrecision = 64;
+			}
+		}
+		
 		/*
 		 * MySQL Type: NUMERIC (silently converted to DECIMAL) JDBC Type:
 		 * NUMERIC
@@ -5289,7 +5795,7 @@
 		rowVal[1] = Integer.toString(java.sql.Types.NUMERIC).getBytes();
 
 		// JDBC Data type
-		rowVal[2] = s2b("17"); // Precision
+		rowVal[2] = s2b(String.valueOf(decimalPrecision)); // Precision
 		rowVal[3] = s2b(""); // Literal Prefix
 		rowVal[4] = s2b(""); // Literal Suffix
 		rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params
@@ -5311,7 +5817,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: DECIMAL JDBC Type: DECIMAL
@@ -5321,7 +5827,7 @@
 		rowVal[1] = Integer.toString(java.sql.Types.DECIMAL).getBytes();
 
 		// JDBC Data type
-		rowVal[2] = s2b("17"); // Precision
+		rowVal[2] = s2b(String.valueOf(decimalPrecision)); // Precision
 		rowVal[3] = s2b(""); // Literal Prefix
 		rowVal[4] = s2b(""); // Literal Suffix
 		rowVal[5] = s2b("[(M[,D])] [ZEROFILL]"); // Create Params
@@ -5343,7 +5849,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: INTEGER JDBC Type: INTEGER
@@ -5375,8 +5881,37 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
+		
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("INTEGER UNSIGNED");
+		rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
 
+		// JDBC Data type
+		rowVal[2] = s2b("10"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("false"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("true"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("INTEGER UNSIGNED"); // Locale Type Name
+		rowVal[13] = s2b("0"); // Minimum Scale
+		rowVal[14] = s2b("0"); // Maximum Scale
+		rowVal[15] = s2b("0"); // SQL Data Type (not used)
+		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
+		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
+		tuples.add(new ByteArrayRow(rowVal));
+
 		/*
 		 * MySQL Type: INT JDBC Type: INTEGER
 		 */
@@ -5407,8 +5942,37 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
+		
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("INT UNSIGNED");
+		rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
 
+		// JDBC Data type
+		rowVal[2] = s2b("10"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("false"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("true"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("INT UNSIGNED"); // Locale Type Name
+		rowVal[13] = s2b("0"); // Minimum Scale
+		rowVal[14] = s2b("0"); // Maximum Scale
+		rowVal[15] = s2b("0"); // SQL Data Type (not used)
+		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
+		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
+		tuples.add(new ByteArrayRow(rowVal));
+
 		/*
 		 * MySQL Type: MEDIUMINT JDBC Type: INTEGER
 		 */
@@ -5439,8 +6003,37 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("MEDIUMINT UNSIGNED");
+		rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("8"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("false"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("true"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("MEDIUMINT UNSIGNED"); // Locale Type Name
+		rowVal[13] = s2b("0"); // Minimum Scale
+		rowVal[14] = s2b("0"); // Maximum Scale
+		rowVal[15] = s2b("0"); // SQL Data Type (not used)
+		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
+		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
+		tuples.add(new ByteArrayRow(rowVal));
+		
 		/*
 		 * MySQL Type: SMALLINT JDBC Type: SMALLINT
 		 */
@@ -5471,8 +6064,37 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
+		
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("SMALLINT UNSIGNED");
+		rowVal[1] = Integer.toString(java.sql.Types.SMALLINT).getBytes();
 
+		// JDBC Data type
+		rowVal[2] = s2b("5"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M)] [ZEROFILL]"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("false"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("true"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("SMALLINT UNSIGNED"); // Locale Type Name
+		rowVal[13] = s2b("0"); // Minimum Scale
+		rowVal[14] = s2b("0"); // Maximum Scale
+		rowVal[15] = s2b("0"); // SQL Data Type (not used)
+		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
+		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
+		tuples.add(new ByteArrayRow(rowVal));
+
 		/*
 		 * MySQL Type: FLOAT JDBC Type: REAL (this is the SINGLE PERCISION
 		 * floating point type)
@@ -5504,7 +6126,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: DOUBLE JDBC Type: DOUBLE
@@ -5536,7 +6158,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: DOUBLE PRECISION JDBC Type: DOUBLE
@@ -5568,7 +6190,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: REAL (does not map to Types.REAL) JDBC Type: DOUBLE
@@ -5600,7 +6222,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: VARCHAR JDBC Type: VARCHAR
@@ -5632,7 +6254,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: ENUM JDBC Type: VARCHAR
@@ -5664,7 +6286,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: SET JDBC Type: VARCHAR
@@ -5696,7 +6318,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: DATE JDBC Type: DATE
@@ -5728,7 +6350,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: TIME JDBC Type: TIME
@@ -5760,7 +6382,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: DATETIME JDBC Type: TIMESTAMP
@@ -5792,7 +6414,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		/*
 		 * MySQL Type: TIMESTAMP JDBC Type: TIMESTAMP
@@ -5824,7 +6446,7 @@
 		rowVal[15] = s2b("0"); // SQL Data Type (not used)
 		rowVal[16] = s2b("0"); // SQL DATETIME SUB (not used)
 		rowVal[17] = s2b("10"); // NUM_PREC_RADIX (2 or 10)
-		tuples.add(rowVal);
+		tuples.add(new ByteArrayRow(rowVal));
 
 		return buildResultSet(fields, tuples);
 	}
@@ -6317,7 +6939,7 @@
 	 *            DOCUMENT ME!
 	 * @return DOCUMENT ME!
 	 */
-	private byte[] s2b(String s) throws SQLException {
+	protected byte[] s2b(String s) throws SQLException {
 		return StringUtils.s2b(s, this.conn);
 	}
 	
@@ -7393,4 +8015,60 @@
 	public boolean usesLocalFiles() throws SQLException {
 		return false;
 	}
+	
+	//
+	// JDBC-4.0 functions that aren't reliant on Java6
+    /**
+     * Retrieves a description of the given catalog's system or user 
+     * function parameters and return type.
+     *
+ 	 * @see java.sql.DatabaseMetaData#getFunctionColumns(String, String, String, String)
+     * @since 1.6
+     */
+    public ResultSet getFunctionColumns(String catalog, 
+    		String schemaPattern, 
+    		String functionNamePattern, 
+    		String columnNamePattern) throws SQLException {
+    	Field[] fields = {
+    			new Field("", "FUNCTION_CAT", Types.VARCHAR, 0),
+    			new Field("", "FUNCTION_SCHEM", Types.VARCHAR, 0),
+    			new Field("", "FUNCTION_NAME", Types.VARCHAR, 0),
+    			new Field("", "COLUMN_NAME", Types.VARCHAR, 0),
+    			new Field("", "COLUMN_TYPE", Types.VARCHAR, 0),
+    			new Field("", "DATA_TYPE", Types.SMALLINT, 0),
+    			new Field("", "TYPE_NAME", Types.VARCHAR, 0),
+    			new Field("", "PRECISION", Types.INTEGER, 0),
+    			new Field("", "LENGTH", Types.INTEGER, 0),
+    			new Field("", "SCALE", Types.SMALLINT, 0),
+    			new Field("", "RADIX", Types.SMALLINT, 0),
+    			new Field("", "NULLABLE", Types.SMALLINT, 0),
+    			new Field("", "REMARKS", Types.VARCHAR, 0),
+    			new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 0),
+    			new Field("", "ORDINAL_POSITION", Types.INTEGER, 0),
+    			new Field("", "IS_NULLABLE", Types.VARCHAR, 3),
+    			new Field("", "SPECIFIC_NAME", Types.VARCHAR, 0)};
+
+		return getProcedureOrFunctionColumns(
+				fields, catalog, schemaPattern,
+				functionNamePattern, columnNamePattern,
+				false, true);
+	}
+
+	public boolean providesQueryObjectGenerator() throws SQLException {
+		return false;
+	}
+	
+	public ResultSet getSchemas(String catalog, 
+			String schemaPattern) throws SQLException {
+		Field[] fields = {
+				new Field("", "TABLE_SCHEM", Types.VARCHAR, 255),
+				new Field("", "TABLE_CATALOG", Types.VARCHAR, 255)
+		};
+		
+		return buildResultSet(fields, new ArrayList());
+	}
+
+	public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
+		return true;
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2005 MySQL AB
+ Copyright (C) 2005-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -35,15 +35,20 @@
  */
 public class DatabaseMetaDataUsingInfoSchema extends DatabaseMetaData {
 
-	public DatabaseMetaDataUsingInfoSchema(Connection connToSet,
-			String databaseToSet) {
+	private boolean hasReferentialConstraintsView;
+
+	protected DatabaseMetaDataUsingInfoSchema(ConnectionImpl connToSet,
+			String databaseToSet) throws SQLException {
 		super(connToSet, databaseToSet);
+		
+		this.hasReferentialConstraintsView = 
+			this.conn.versionMeetsMinimum(5, 1, 10);
 	}
 
 	private ResultSet executeMetadataQuery(PreparedStatement pStmt)
 			throws SQLException {
 		ResultSet rs = pStmt.executeQuery();
-		((com.mysql.jdbc.ResultSet) rs).setOwningStatement(null);
+		((com.mysql.jdbc.ResultSetInternalMethods) rs).setOwningStatement(null);
 
 		return rs;
 	}
@@ -97,18 +102,15 @@
 		}
 
 		if (catalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-
-			catalog = this.database;
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				catalog = this.database;
+			}	
 		}
 		
 		String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME,"
 			 +"COLUMN_NAME, NULL AS GRANTOR, GRANTEE, PRIVILEGE_TYPE AS PRIVILEGE, IS_GRANTABLE FROM "
 			 + "INFORMATION_SCHEMA.COLUMN_PRIVILEGES WHERE "
-			 + "TABLE_SCHEMA = ? AND "
+			 + "TABLE_SCHEMA LIKE ? AND "
 			 + "TABLE_NAME =? AND COLUMN_NAME LIKE ? ORDER BY " 
 			 + "COLUMN_NAME, PRIVILEGE_TYPE";
 		
@@ -116,12 +118,18 @@
 		
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
-			pStmt.setString(1, catalog);
+			
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, table);
 			pStmt.setString(3, columnNamePattern);
 			
 			ResultSet rs = executeMetadataQuery(pStmt);
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "TABLE_CAT", Types.CHAR, 64),
 					new Field("", "TABLE_SCHEM", Types.CHAR, 1),
 					new Field("", "TABLE_NAME", Types.CHAR, 64),
@@ -196,12 +204,9 @@
 		}
 
 		if (catalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				catalog = this.database;
 			}
-
-			catalog = this.database;
 		}
 
 		StringBuffer sqlBuf = new StringBuffer("SELECT "
@@ -221,32 +226,44 @@
 				.append("CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN NUMERIC_PRECISION ELSE CASE WHEN CHARACTER_MAXIMUM_LENGTH > " 
 						+ Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + 
 						" ELSE CHARACTER_MAXIMUM_LENGTH END END AS COLUMN_SIZE, "
-						+ " NULL AS BUFFER_LENGTH,"
+						+ MysqlIO.getMaxBuf() + " AS BUFFER_LENGTH,"
 						+ "NUMERIC_SCALE AS DECIMAL_DIGITS,"
 						+ "10 AS NUM_PREC_RADIX,"
-						+ "NULL AS NULLABLE,"
+						+ "CASE WHEN IS_NULLABLE='NO' THEN " + columnNoNulls + " ELSE CASE WHEN IS_NULLABLE='YES' THEN " + columnNullable + " ELSE " + columnNullableUnknown + " END END AS NULLABLE,"
 						+ "COLUMN_COMMENT AS REMARKS,"
 						+ "COLUMN_DEFAULT AS COLUMN_DEF,"
-						+ "NULL AS SQL_DATA_TYPE,"
-						+ "NULL AS SQL_DATETIME_SUB,"
-						+ "CHARACTER_OCTET_LENGTH AS CHAR_OCTET_LENGTH,"
+						+ "0 AS SQL_DATA_TYPE,"
+						+ "0 AS SQL_DATETIME_SUB,"
+						+ "CASE WHEN CHARACTER_OCTET_LENGTH > " + Integer.MAX_VALUE + " THEN " + Integer.MAX_VALUE + " ELSE CHARACTER_OCTET_LENGTH END AS CHAR_OCTET_LENGTH,"
 						+ "ORDINAL_POSITION,"
-						+ "IS_NULLABLE "
+						+ "IS_NULLABLE,"
+						+ "NULL AS SCOPE_CATALOG,"
+						+ "NULL AS SCOPE_SCHEMA,"
+						+ "NULL AS SCOPE_TABLE,"
+						+ "NULL AS SOURCE_DATA_TYPE,"
+						+ "IF (EXTRA LIKE '%auto_increment%','YES','NO') AS IS_AUTOINCREMENT "
 						+ "FROM INFORMATION_SCHEMA.COLUMNS WHERE "
-						+ "TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND COLUMN_NAME LIKE ? "
+						+ "TABLE_SCHEMA LIKE ? AND "
+						+ "TABLE_NAME LIKE ? AND COLUMN_NAME LIKE ? "
 						+ "ORDER BY TABLE_SCHEMA, TABLE_NAME, ORDINAL_POSITION");
 
 		PreparedStatement pStmt = null;
 
 		try {
 			pStmt = prepareMetaDataSafeStatement(sqlBuf.toString());
-			pStmt.setString(1, catalog);
+			
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, tableName);
 			pStmt.setString(3, columnNamePattern);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
 
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "TABLE_CAT", Types.CHAR, 255),
 					new Field("", "TABLE_SCHEM", Types.CHAR, 0),
 					new Field("", "TABLE_NAME", Types.CHAR, 255),
@@ -266,8 +283,12 @@
 					new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, Integer
 							.toString(Integer.MAX_VALUE).length()),
 					new Field("", "ORDINAL_POSITION", Types.INTEGER, 10),
-					new Field("", "IS_NULLABLE", Types.CHAR, 3) });
-
+					new Field("", "IS_NULLABLE", Types.CHAR, 3),
+					new Field("", "SCOPE_CATALOG", Types.CHAR, 255),
+					new Field("", "SCOPE_SCHEMA", Types.CHAR, 255),
+					new Field("", "SCOPE_TABLE", Types.CHAR, 255),
+					new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 10),
+					new Field("", "IS_AUTOINCREMENT", Types.CHAR, 3) });
 			return rs;
 		} finally {
 			if (pStmt != null) {
@@ -352,21 +373,15 @@
 		}
 
 		if (primaryCatalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				primaryCatalog = this.database;	
 			}
-
-			primaryCatalog = this.database;
 		}
 
 		if (foreignCatalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				foreignCatalog = this.database;
 			}
-
-			foreignCatalog = this.database;
 		}
 
 		Field[] fields = new Field[14];
@@ -395,36 +410,52 @@
 				+ "A.TABLE_NAME AS FKTABLE_NAME, "
 				+ "A.COLUMN_NAME AS FKCOLUMN_NAME, "
 				+ "A.ORDINAL_POSITION AS KEY_SEQ,"
-				+ importedKeyRestrict
+				+ generateUpdateRuleClause()
 				+ " AS UPDATE_RULE,"
-				+ importedKeyRestrict
+				+ generateDeleteRuleClause()
 				+ " AS DELETE_RULE,"
 				+ "A.CONSTRAINT_NAME AS FK_NAME,"
-				+ "NULL AS PK_NAME,"
+				+ "(SELECT CONSTRAINT_NAME FROM"
+				+ " INFORMATION_SCHEMA.TABLE_CONSTRAINTS"
+				+ " WHERE TABLE_SCHEMA = REFERENCED_TABLE_SCHEMA AND"
+				+ " TABLE_NAME = REFERENCED_TABLE_NAME AND"
+				+ " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)"
+				+ " AS PK_NAME,"
 				+ importedKeyNotDeferrable
 				+ " AS DEFERRABILITY "
 				+ "FROM "
-				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A,"
+				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A JOIN "
 				+ "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B "
+				+ "USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) "
+				+ generateOptionalRefContraintsJoin()
 				+ "WHERE "
-				+ "A.TABLE_SCHEMA=B.TABLE_SCHEMA AND A.TABLE_NAME=B.TABLE_NAME "
-				+ "AND "
-				+ "A.CONSTRAINT_NAME=B.CONSTRAINT_NAME AND B.CONSTRAINT_TYPE IS NOT NULL "
-				+ "AND A.REFERENCED_TABLE_SCHEMA=? AND A.REFERENCED_TABLE_NAME=? "
-				+ "AND A.TABLE_SCHEMA=? AND A.TABLE_NAME=? " + "ORDER BY "
+				+ "B.CONSTRAINT_TYPE = 'FOREIGN KEY' "
+				+ "AND A.REFERENCED_TABLE_SCHEMA LIKE ? AND A.REFERENCED_TABLE_NAME=? "
+				+ "AND A.TABLE_SCHEMA LIKE ? AND A.TABLE_NAME=? " + "ORDER BY "
 				+ "A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION";
 
 		PreparedStatement pStmt = null;
 
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
-			pStmt.setString(1, primaryCatalog);
+			if (primaryCatalog != null) {
+				pStmt.setString(1, primaryCatalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, primaryTable);
-			pStmt.setString(3, foreignCatalog);
+			
+			if (foreignCatalog != null) {
+				pStmt.setString(3, foreignCatalog);
+			} else {
+				pStmt.setString(3, "%");
+			}
+			
 			pStmt.setString(4, foreignTable);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "PKTABLE_CAT", Types.CHAR, 255),
 					new Field("", "PKTABLE_SCHEM", Types.CHAR, 0),
 					new Field("", "PKTABLE_NAME", Types.CHAR, 255),
@@ -517,13 +548,12 @@
 		}
 
 		if (catalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
-
-			catalog = this.database;
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				catalog = this.database;
+			}	
 		}
+		
+		//CASCADE, SET NULL, SET DEFAULT, RESTRICT, NO ACTION
 
 		String sql = "SELECT "
 				+ "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT,"
@@ -535,34 +565,45 @@
 				+ "A.TABLE_NAME AS FKTABLE_NAME,"
 				+ "A.COLUMN_NAME AS FKCOLUMN_NAME, "
 				+ "A.ORDINAL_POSITION AS KEY_SEQ,"
-				+ importedKeyRestrict
+				+ generateUpdateRuleClause()
 				+ " AS UPDATE_RULE,"
-				+ importedKeyRestrict
+				+ generateDeleteRuleClause()
 				+ " AS DELETE_RULE,"
 				+ "A.CONSTRAINT_NAME AS FK_NAME,"
-				+ "NULL AS PK_NAME,"
+				+ "(SELECT CONSTRAINT_NAME FROM"
+				+ " INFORMATION_SCHEMA.TABLE_CONSTRAINTS"
+				+ " WHERE TABLE_SCHEMA = REFERENCED_TABLE_SCHEMA AND"
+				+ " TABLE_NAME = REFERENCED_TABLE_NAME AND"
+				+ " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)"
+				+ " AS PK_NAME,"
 				+ importedKeyNotDeferrable
 				+ " AS DEFERRABILITY "
 				+ "FROM "
-				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A,"
+				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A JOIN "
 				+ "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B "
+				+ "USING (TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_NAME) "
+				+ generateOptionalRefContraintsJoin()
 				+ "WHERE "
-				+ "A.TABLE_SCHEMA=B.TABLE_SCHEMA AND A.TABLE_NAME=B.TABLE_NAME "
-				+ "AND "
-				+ "A.CONSTRAINT_NAME=B.CONSTRAINT_NAME AND B.CONSTRAINT_TYPE IS NOT NULL "
-				+ "AND A.REFERENCED_TABLE_SCHEMA=? AND A.REFERENCED_TABLE_NAME=? "
+				+ "B.CONSTRAINT_TYPE = 'FOREIGN KEY' "
+				+ "AND A.REFERENCED_TABLE_SCHEMA LIKE ? AND A.REFERENCED_TABLE_NAME=? "
 				+ "ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION";
 
 		PreparedStatement pStmt = null;
 
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
-			pStmt.setString(1, catalog);
+			
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, table);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
 
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "PKTABLE_CAT", Types.CHAR, 255),
 					new Field("", "PKTABLE_SCHEM", Types.CHAR, 0),
 					new Field("", "PKTABLE_NAME", Types.CHAR, 255),
@@ -587,32 +628,34 @@
 
 	}
 
-	/*
-	 * 
-	 * getTablePrivileges
-	 * 
-	 * if (getMysqlVersion() > 49999) { if (!strcasecmp("localhost",
-	 * m_pSettings->pConnection->host)) { sprintf(user, "A.GRANTEE =
-	 * \"'%s'@'localhost'\" OR A.GRANTEE LIKE \"'%'@'localhost'\"",
-	 * m_pSettings->pConnection->user, m_pSettings->pConnection->user); } else {
-	 * sprintf(user, "\"'%s'@'%s'\" LIKE A.GRANTEE",
-	 * m_pSettings->pConnection->user, m_pSettings->pConnection->host); }
-	 * 
-	 * sprintf(query, "SELECT DISTINCT A.TABLE_CATALOG, B.TABLE_SCHEMA,
-	 * B.TABLE_NAME, CURRENT_USER(), " \ "A.PRIVILEGE_TYPE FROM
-	 * INFORMATION_SCHEMA.USER_PRIVILEGES A, INFORMATION_SCHEMA.TABLES B " \
-	 * "WHERE B.TABLE_SCHEMA LIKE '%s' AND B.TABLE_NAME LIKE '%s' AND (%s) " \
-	 * "UNION " \ "SELECT DISTINCT A.TABLE_CATALOG, B.TABLE_SCHEMA,
-	 * B.TABLE_NAME, CURRENT_USER(), A.PRIVILEGE_TYPE " \ "FROM
-	 * INFORMATION_SCHEMA.SCHEMA_PRIVILEGES A, INFORMATION_SCHEMA.TABLES B WHERE " \
-	 * "B.TABLE_SCHEMA LIKE '%s' AND B.TABLE_NAME LIKE '%s' AND (%s) " \ "UNION "\
-	 * "SELECT DISTINCT A.TABLE_CATALOG, A.TABLE_SCHEMA, A.TABLE_NAME,
-	 * CURRENT_USER, A.PRIVILEGE_TYPE FROM " \
-	 * "INFORMATION_SCHEMA.TABLE_PRIVILEGES A WHERE A.TABLE_SCHEMA LIKE '%s' AND
-	 * A.TABLE_NAME LIKE '%s' " \ "AND (%s)", schemaName, tableName, user,
-	 * schemaName, tableName, user, schemaName, tableName, user );
-	 */
+	private String generateOptionalRefContraintsJoin() {
+		return ((this.hasReferentialConstraintsView) ? "JOIN " 
+				+ "INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R "
+				+ "ON (R.CONSTRAINT_NAME = B.CONSTRAINT_NAME "
+				+ "AND R.TABLE_NAME = B.TABLE_NAME AND "
+				+ "R.CONSTRAINT_SCHEMA = B.TABLE_SCHEMA) " : "");
+	}
 
+	private String generateDeleteRuleClause() {
+		return ((this.hasReferentialConstraintsView) ? 
+				"CASE WHEN R.DELETE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) 
+				+ " WHEN R.DELETE_RULE='SET NULL' THEN " + String.valueOf(importedKeySetNull)  
+				+ " WHEN R.DELETE_RULE='SET DEFAULT' THEN " + String.valueOf(importedKeySetDefault) 
+				+ " WHEN R.DELETE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict)
+				+ " WHEN R.DELETE_RULE='NO ACTION' THEN " + String.valueOf(importedKeyNoAction)
+				+ " ELSE " + String.valueOf(importedKeyNoAction) + " END " : String.valueOf(importedKeyRestrict));
+	}
+
+	private String generateUpdateRuleClause() {
+		return ((this.hasReferentialConstraintsView) ? 
+				"CASE WHEN R.UPDATE_RULE='CASCADE' THEN " + String.valueOf(importedKeyCascade) 
+				+ " WHEN R.UPDATE_RULE='SET NULL' THEN " + String.valueOf(importedKeySetNull)  
+				+ " WHEN R.UPDATE_RULE='SET DEFAULT' THEN " + String.valueOf(importedKeySetDefault) 
+				+ " WHEN R.UPDATE_RULE='RESTRICT' THEN " + String.valueOf(importedKeyRestrict)
+				+ " WHEN R.UPDATE_RULE='NO ACTION' THEN " + String.valueOf(importedKeyNoAction)
+				+ " ELSE " + String.valueOf(importedKeyNoAction) + " END " : String.valueOf(importedKeyRestrict));
+	}
+
 	/**
 	 * Get a description of the primary key columns that are referenced by a
 	 * table's foreign key columns (the primary keys imported by a table). They
@@ -680,12 +723,9 @@
 		}
 
 		if (catalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				catalog = this.database;
 			}
-
-			catalog = this.database;
 		}
 
 		String sql = "SELECT "
@@ -698,21 +738,30 @@
 				+ "A.TABLE_NAME AS FKTABLE_NAME, "
 				+ "A.COLUMN_NAME AS FKCOLUMN_NAME, "
 				+ "A.ORDINAL_POSITION AS KEY_SEQ,"
-				+ importedKeyRestrict
+				+ generateUpdateRuleClause()
 				+ " AS UPDATE_RULE,"
-				+ importedKeyRestrict
+				+ generateDeleteRuleClause()
 				+ " AS DELETE_RULE,"
 				+ "A.CONSTRAINT_NAME AS FK_NAME,"
-				+ "NULL AS PK_NAME, "
+				+ "(SELECT CONSTRAINT_NAME FROM"
+				+ " INFORMATION_SCHEMA.TABLE_CONSTRAINTS"
+				+ " WHERE TABLE_SCHEMA = REFERENCED_TABLE_SCHEMA AND"
+				+ " TABLE_NAME = REFERENCED_TABLE_NAME AND"
+				+ " CONSTRAINT_TYPE IN ('UNIQUE','PRIMARY KEY') LIMIT 1)"
+				+ " AS PK_NAME,"
 				+ importedKeyNotDeferrable
 				+ " AS DEFERRABILITY "
 				+ "FROM "
-				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A, "
-				+ "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B WHERE A.TABLE_SCHEMA=? "
-				+ "AND A.CONSTRAINT_NAME=B.CONSTRAINT_NAME AND A.TABLE_NAME=? "
-				+ "AND "
-				+ "B.TABLE_NAME=? AND A.REFERENCED_TABLE_SCHEMA IS NOT NULL "
-				+ " ORDER BY "
+				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A "
+				+ "JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS B USING "
+				+ "(CONSTRAINT_NAME, TABLE_NAME) "
+				+ generateOptionalRefContraintsJoin()
+				+ "WHERE "
+				+ "B.CONSTRAINT_TYPE = 'FOREIGN KEY' "
+				+ "AND A.TABLE_SCHEMA LIKE ? "
+				+ "AND A.TABLE_NAME=? "
+				+ "AND A.REFERENCED_TABLE_SCHEMA IS NOT NULL "
+				+ "ORDER BY "
 				+ "A.REFERENCED_TABLE_SCHEMA, A.REFERENCED_TABLE_NAME, "
 				+ "A.ORDINAL_POSITION";
 
@@ -720,13 +769,18 @@
 
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
-			pStmt.setString(1, catalog);
+			
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, table);
-			pStmt.setString(3, table);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
 
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "PKTABLE_CAT", Types.CHAR, 255),
 					new Field("", "PKTABLE_SCHEM", Types.CHAR, 0),
 					new Field("", "PKTABLE_NAME", Types.CHAR, 255),
@@ -830,14 +884,25 @@
 		PreparedStatement pStmt = null;
 
 		try {
+			if (catalog == null) {
+				if (this.conn.getNullCatalogMeansCurrent()) {
+					catalog = this.database;
+				}
+			}
+			
 			pStmt = prepareMetaDataSafeStatement(sqlBuf.toString());
 
-			pStmt.setString(1, catalog);
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, table);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
 
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "TABLE_CAT", Types.CHAR, 255),
 					new Field("", "TABLE_SCHEM", Types.CHAR, 0),
 					new Field("", "TABLE_NAME", Types.CHAR, 255),
@@ -889,12 +954,9 @@
 			String table) throws SQLException {
 
 		if (catalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				catalog = this.database;
 			}
-
-			catalog = this.database;
 		}
 
 		if (table == null) {
@@ -912,11 +974,16 @@
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
 
-			pStmt.setString(1, catalog);
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, table);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "TABLE_CAT", Types.CHAR, 255),
 					new Field("", "TABLE_SCHEM", Types.CHAR, 0),
 					new Field("", "TABLE_NAME", Types.CHAR, 255),
@@ -989,17 +1056,9 @@
 		String db = null;
 
 		if (catalog == null) {
-			db = this.database;
-		} else if (catalog.length() > 0) {
-			db = catalog;
-		} else {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				db = this.database;
 			}
-
-			catalog = null;
-			db = null;
 		}
 
 		String sql = "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, "
@@ -1019,11 +1078,17 @@
 
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
-			pStmt.setString(1, db);
+			
+			if (db != null) {
+				pStmt.setString(1, db);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, procedureNamePattern);
 
 			ResultSet rs = executeMetadataQuery(pStmt);
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "PROCEDURE_CAT", Types.CHAR, 0),
 					new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0),
 					new Field("", "PROCEDURE_NAME", Types.CHAR, 0),
@@ -1080,12 +1145,9 @@
 	public ResultSet getTables(String catalog, String schemaPattern,
 			String tableNamePattern, String[] types) throws SQLException {
 		if (catalog == null) {
-			if (!this.conn.getNullCatalogMeansCurrent()) {
-				throw SQLError.createSQLException("'catalog' parameter can not be null",
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			if (this.conn.getNullCatalogMeansCurrent()) {
+				catalog = this.database;
 			}
-
-			catalog = this.database;
 		}
 
 		if (tableNamePattern == null) {
@@ -1109,7 +1171,13 @@
 				+ "ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME";
 		try {
 			pStmt = prepareMetaDataSafeStatement(sql);
-			pStmt.setString(1, catalog);
+			
+			if (catalog != null) {
+				pStmt.setString(1, catalog);
+			} else {
+				pStmt.setString(1, "%");
+			}
+			
 			pStmt.setString(2, tableNamePattern);
 
 			// This overloading of IN (...) allows us to cache this
@@ -1140,7 +1208,7 @@
 
 			ResultSet rs = executeMetadataQuery(pStmt);
 
-			((com.mysql.jdbc.ResultSet) rs).redefineFieldsForDBMD(new Field[] {
+			((com.mysql.jdbc.ResultSetInternalMethods) rs).redefineFieldsForDBMD(new Field[] {
 					new Field("", "TABLE_CAT", java.sql.Types.VARCHAR,
 							(catalog == null) ? 0 : catalog.length()),
 					new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0),
@@ -1160,7 +1228,7 @@
 			throws SQLException {
 		// Can't use server-side here as we coerce a lot of types to match
 		// the spec.
-		PreparedStatement pStmt = this.conn.clientPrepareStatement(sql);
+		PreparedStatement pStmt = (PreparedStatement) this.conn.clientPrepareStatement(sql);
 
 		if (pStmt.getMaxRows() != 0) {
 			pStmt.setMaxRows(0);

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/DocsConnectionPropsHelper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/DocsConnectionPropsHelper.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/DocsConnectionPropsHelper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -12,7 +12,7 @@
  * To change the template for this generated type comment go to Window -
  * Preferences - Java - Code Generation - Code and Comments
  */
-public class DocsConnectionPropsHelper extends ConnectionProperties {
+public class DocsConnectionPropsHelper extends ConnectionPropertiesImpl {
 
 	public static void main(String[] args) throws Exception {
 		System.out.println(new DocsConnectionPropsHelper().exposeAsXml());

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessor.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -101,7 +101,7 @@
 	 */
 	public static final Object escapeSQL(String sql,
 			boolean serverSupportsConvertFn, 
-			Connection conn) throws java.sql.SQLException {
+			ConnectionImpl conn) throws java.sql.SQLException {
 		boolean replaceEscapeSequence = false;
 		String escapeSequence = null;
 
@@ -125,7 +125,7 @@
 
 		EscapeTokenizer escapeTokenizer = new EscapeTokenizer(sql);
 
-		byte usesVariables = Statement.USES_VARIABLES_FALSE;
+		byte usesVariables = StatementImpl.USES_VARIABLES_FALSE;
 		boolean callingStoredFunction = false;
 
 		while (escapeTokenizer.hasMoreTokens()) {
@@ -157,7 +157,7 @@
 							} else {
 								remaining = ((EscapeProcessorResult) remainingResults).escapedSql;
 
-								if (usesVariables != Statement.USES_VARIABLES_TRUE) {
+								if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) {
 									usesVariables = ((EscapeProcessorResult) remainingResults).usesVariables;
 								}
 							}
@@ -186,18 +186,16 @@
 							escapeSequence = st.nextToken();
 
 							if (escapeSequence.length() < 3) {
-								throw SQLError.createSQLException(
-										"Syntax error for escape sequence '"
-												+ token + "'", "42000");
-							}
+								newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
+							} else {
+							
 
-							escapeSequence = escapeSequence.substring(1,
+								escapeSequence = escapeSequence.substring(1,
 									escapeSequence.length() - 1);
-							replaceEscapeSequence = true;
+								replaceEscapeSequence = true;
+							}
 						} catch (java.util.NoSuchElementException e) {
-							throw SQLError.createSQLException(
-									"Syntax error for escape sequence '"
-											+ token + "'", "42000");
+							newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
 						}
 					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 							"{fn")) {
@@ -222,148 +220,146 @@
 						int endPos = token.lastIndexOf('\''); // no }
 
 						if ((startPos == -1) || (endPos == -1)) {
-							throw SQLError.createSQLException(
-									"Syntax error for DATE escape sequence '"
-											+ token + "'", "42000");
+							newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
+						} else {
+	
+							String argument = token.substring(startPos, endPos);
+	
+							try {
+								StringTokenizer st = new StringTokenizer(argument,
+										" -");
+								String year4 = st.nextToken();
+								String month2 = st.nextToken();
+								String day2 = st.nextToken();
+								String dateString = "'" + year4 + "-" + month2
+										+ "-" + day2 + "'";
+								newSql.append(dateString);
+							} catch (java.util.NoSuchElementException e) {
+								throw SQLError.createSQLException(
+										"Syntax error for DATE escape sequence '"
+												+ argument + "'", "42000");
+							}
 						}
-
-						String argument = token.substring(startPos, endPos);
-
-						try {
-							StringTokenizer st = new StringTokenizer(argument,
-									" -");
-							String year4 = st.nextToken();
-							String month2 = st.nextToken();
-							String day2 = st.nextToken();
-							String dateString = "'" + year4 + "-" + month2
-									+ "-" + day2 + "'";
-							newSql.append(dateString);
-						} catch (java.util.NoSuchElementException e) {
-							throw SQLError.createSQLException(
-									"Syntax error for DATE escape sequence '"
-											+ argument + "'", "42000");
-						}
 					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 							"{ts")) {
 						int startPos = token.indexOf('\'') + 1;
 						int endPos = token.lastIndexOf('\''); // no }
 
 						if ((startPos == -1) || (endPos == -1)) {
-							throw SQLError.createSQLException(
-									"Syntax error for TIMESTAMP escape sequence '"
-											+ token + "'", "42000");
-						}
+							newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
+						} else {
 
-						String argument = token.substring(startPos, endPos);
-
-						try {
-							StringTokenizer st = new StringTokenizer(argument,
-									" .-:");
-							String year4 = st.nextToken();
-							String month2 = st.nextToken();
-							String day2 = st.nextToken();
-							String hour = st.nextToken();
-							String minute = st.nextToken();
-							String second = st.nextToken();
-
-							/*
-							 * For now, we get the fractional seconds part, but
-							 * we don't use it, as MySQL doesn't support it in
-							 * it's TIMESTAMP data type
-							 * 
-							 * String fractionalSecond = "";
-							 * 
-							 * if (st.hasMoreTokens()) { fractionalSecond =
-							 * st.nextToken(); }
-							 */
-							/*
-							 * Use the full format because number format will
-							 * not work for "between" clauses.
-							 * 
-							 * Ref. Mysql Docs
-							 * 
-							 * You can specify DATETIME, DATE and TIMESTAMP
-							 * values using any of a common set of formats:
-							 * 
-							 * As a string in either 'YYYY-MM-DD HH:MM:SS' or
-							 * 'YY-MM-DD HH:MM:SS' format.
-							 * 
-							 * Thanks to Craig Longman for pointing out this bug
-							 */
-							if (!conn.getUseTimezone() && !conn.getUseJDBCCompliantTimezoneShift()) {
-								newSql.append("'").append(year4).append("-")
-									.append(month2).append("-").append(day2)
-									.append(" ").append(hour).append(":")
-									.append(minute).append(":").append(second)
-									.append("'");
-							} else {
-								Calendar sessionCalendar;
-								
-								if (conn != null) {
-									sessionCalendar = conn.getCalendarInstanceForSessionOrNew();
+							String argument = token.substring(startPos, endPos);
+	
+							try {
+								StringTokenizer st = new StringTokenizer(argument,
+										" .-:");
+								String year4 = st.nextToken();
+								String month2 = st.nextToken();
+								String day2 = st.nextToken();
+								String hour = st.nextToken();
+								String minute = st.nextToken();
+								String second = st.nextToken();
+	
+								/*
+								 * For now, we get the fractional seconds part, but
+								 * we don't use it, as MySQL doesn't support it in
+								 * it's TIMESTAMP data type
+								 * 
+								 * String fractionalSecond = "";
+								 * 
+								 * if (st.hasMoreTokens()) { fractionalSecond =
+								 * st.nextToken(); }
+								 */
+								/*
+								 * Use the full format because number format will
+								 * not work for "between" clauses.
+								 * 
+								 * Ref. Mysql Docs
+								 * 
+								 * You can specify DATETIME, DATE and TIMESTAMP
+								 * values using any of a common set of formats:
+								 * 
+								 * As a string in either 'YYYY-MM-DD HH:MM:SS' or
+								 * 'YY-MM-DD HH:MM:SS' format.
+								 * 
+								 * Thanks to Craig Longman for pointing out this bug
+								 */
+								if (!conn.getUseTimezone() && !conn.getUseJDBCCompliantTimezoneShift()) {
+									newSql.append("'").append(year4).append("-")
+										.append(month2).append("-").append(day2)
+										.append(" ").append(hour).append(":")
+										.append(minute).append(":").append(second)
+										.append("'");
 								} else {
-									sessionCalendar = new GregorianCalendar();
-									sessionCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
-								}
-								
-								try {
-									int year4Int = Integer.parseInt(year4);
-									int month2Int = Integer.parseInt(month2);
-									int day2Int = Integer.parseInt(day2);
-									int hourInt = Integer.parseInt(hour);
-									int minuteInt = Integer.parseInt(minute);
-									int secondInt = Integer.parseInt(second);
+									Calendar sessionCalendar;
 									
-									synchronized (sessionCalendar) {
-										boolean useGmtMillis = conn.getUseGmtMillisForDatetimes();
-										
-										Timestamp toBeAdjusted = TimeUtil.fastTimestampCreate(useGmtMillis,
-												useGmtMillis ? Calendar.getInstance(TimeZone.getTimeZone("GMT")): null,
-											sessionCalendar,
-											year4Int,
-											month2Int,
-											day2Int,
-											hourInt,
-											minuteInt,
-											secondInt,
-											0);
+									if (conn != null) {
+										sessionCalendar = conn.getCalendarInstanceForSessionOrNew();
+									} else {
+										sessionCalendar = new GregorianCalendar();
+										sessionCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
+									}
 									
-										Timestamp inServerTimezone = TimeUtil.changeTimezone(
-												conn,
+									try {
+										int year4Int = Integer.parseInt(year4);
+										int month2Int = Integer.parseInt(month2);
+										int day2Int = Integer.parseInt(day2);
+										int hourInt = Integer.parseInt(hour);
+										int minuteInt = Integer.parseInt(minute);
+										int secondInt = Integer.parseInt(second);
+										
+										synchronized (sessionCalendar) {
+											boolean useGmtMillis = conn.getUseGmtMillisForDatetimes();
+											
+											Timestamp toBeAdjusted = TimeUtil.fastTimestampCreate(useGmtMillis,
+													useGmtMillis ? Calendar.getInstance(TimeZone.getTimeZone("GMT")): null,
 												sessionCalendar,
-												null,
-												toBeAdjusted,
-												sessionCalendar.getTimeZone(),
-												conn.getServerTimezoneTZ(),
-												false);
+												year4Int,
+												month2Int,
+												day2Int,
+												hourInt,
+												minuteInt,
+												secondInt,
+												0);
 										
-										
-										newSql.append("'");
-										
-										String timezoneLiteral = inServerTimezone.toString();
-										
-										int indexOfDot = timezoneLiteral.indexOf(".");
-										
-										if (indexOfDot != -1) {
-											timezoneLiteral = timezoneLiteral.substring(0, indexOfDot);
+											Timestamp inServerTimezone = TimeUtil.changeTimezone(
+													conn,
+													sessionCalendar,
+													null,
+													toBeAdjusted,
+													sessionCalendar.getTimeZone(),
+													conn.getServerTimezoneTZ(),
+													false);
+											
+											
+											newSql.append("'");
+											
+											String timezoneLiteral = inServerTimezone.toString();
+											
+											int indexOfDot = timezoneLiteral.indexOf(".");
+											
+											if (indexOfDot != -1) {
+												timezoneLiteral = timezoneLiteral.substring(0, indexOfDot);
+											}
+											
+											newSql.append(timezoneLiteral);
 										}
 										
-										newSql.append(timezoneLiteral);
-									}
+										newSql.append("'");	
+										
 									
-									newSql.append("'");	
-									
-								
-								} catch (NumberFormatException nfe) {
-									throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" 
-										+ token + "'.",
-										SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+									} catch (NumberFormatException nfe) {
+										throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" 
+											+ token + "'.",
+											SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+									}
 								}
+							} catch (java.util.NoSuchElementException e) {
+								throw SQLError.createSQLException(
+										"Syntax error for TIMESTAMP escape sequence '"
+												+ argument + "'", "42000");
 							}
-						} catch (java.util.NoSuchElementException e) {
-							throw SQLError.createSQLException(
-									"Syntax error for TIMESTAMP escape sequence '"
-											+ argument + "'", "42000");
 						}
 					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 							"{t")) {
@@ -371,69 +367,68 @@
 						int endPos = token.lastIndexOf('\''); // no }
 
 						if ((startPos == -1) || (endPos == -1)) {
-							throw SQLError.createSQLException(
-									"Syntax error for TIME escape sequence '"
-											+ token + "'", "42000");
-						}
+							newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders
+						} else {
 
-						String argument = token.substring(startPos, endPos);
-
-						try {
-							StringTokenizer st = new StringTokenizer(argument,
-									" :");
-							String hour = st.nextToken();
-							String minute = st.nextToken();
-							String second = st.nextToken();
-							
-							if (!conn.getUseTimezone()) {
-								String timeString = "'" + hour + ":" + minute + ":"
-									+ second + "'";
-								newSql.append(timeString);
-							} else {
-								Calendar sessionCalendar = null;
+							String argument = token.substring(startPos, endPos);
+	
+							try {
+								StringTokenizer st = new StringTokenizer(argument,
+										" :");
+								String hour = st.nextToken();
+								String minute = st.nextToken();
+								String second = st.nextToken();
 								
-								if (conn != null) {
-									sessionCalendar = conn.getCalendarInstanceForSessionOrNew();
+								if (!conn.getUseTimezone()) {
+									String timeString = "'" + hour + ":" + minute + ":"
+										+ second + "'";
+									newSql.append(timeString);
 								} else {
-									sessionCalendar = new GregorianCalendar();
-								}
-
-								try {
-									int hourInt = Integer.parseInt(hour);
-									int minuteInt = Integer.parseInt(minute);
-									int secondInt = Integer.parseInt(second);
+									Calendar sessionCalendar = null;
 									
-									synchronized (sessionCalendar) {
-										Time toBeAdjusted = TimeUtil.fastTimeCreate(
-												sessionCalendar,
-												hourInt,
-												minuteInt,
-												secondInt);
+									if (conn != null) {
+										sessionCalendar = conn.getCalendarInstanceForSessionOrNew();
+									} else {
+										sessionCalendar = new GregorianCalendar();
+									}
+	
+									try {
+										int hourInt = Integer.parseInt(hour);
+										int minuteInt = Integer.parseInt(minute);
+										int secondInt = Integer.parseInt(second);
 										
-										Time inServerTimezone = TimeUtil.changeTimezone(
-												conn,
-												sessionCalendar,
-												null,
-												toBeAdjusted,
-												sessionCalendar.getTimeZone(),
-												conn.getServerTimezoneTZ(),
-												false);
-										
-										newSql.append("'");
-										newSql.append(inServerTimezone.toString());
-										newSql.append("'");		
+										synchronized (sessionCalendar) {
+											Time toBeAdjusted = TimeUtil.fastTimeCreate(
+													sessionCalendar,
+													hourInt,
+													minuteInt,
+													secondInt);
+											
+											Time inServerTimezone = TimeUtil.changeTimezone(
+													conn,
+													sessionCalendar,
+													null,
+													toBeAdjusted,
+													sessionCalendar.getTimeZone(),
+													conn.getServerTimezoneTZ(),
+													false);
+											
+											newSql.append("'");
+											newSql.append(inServerTimezone.toString());
+											newSql.append("'");		
+										}
+									
+									} catch (NumberFormatException nfe) {
+										throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" 
+											+ token + "'.",
+											SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 									}
-								
-								} catch (NumberFormatException nfe) {
-									throw SQLError.createSQLException("Syntax error in TIMESTAMP escape sequence '" 
-										+ token + "'.",
-										SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 								}
+							} catch (java.util.NoSuchElementException e) {
+								throw SQLError.createSQLException(
+										"Syntax error for escape sequence '"
+												+ argument + "'", "42000");
 							}
-						} catch (java.util.NoSuchElementException e) {
-							throw SQLError.createSQLException(
-									"Syntax error for escape sequence '"
-											+ argument + "'", "42000");
 						}
 					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
 							"{call")
@@ -505,11 +500,11 @@
 		epr.escapedSql = escapedSql;
 		epr.callingStoredFunction = callingStoredFunction;
 
-		if (usesVariables != Statement.USES_VARIABLES_TRUE) {
+		if (usesVariables != StatementImpl.USES_VARIABLES_TRUE) {
 			if (escapeTokenizer.sawVariableUse()) {
-				epr.usesVariables = Statement.USES_VARIABLES_TRUE;
+				epr.usesVariables = StatementImpl.USES_VARIABLES_TRUE;
 			} else {
-				epr.usesVariables = Statement.USES_VARIABLES_FALSE;
+				epr.usesVariables = StatementImpl.USES_VARIABLES_FALSE;
 			}
 		}
 

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessorResult.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessorResult.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeProcessorResult.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -39,5 +39,5 @@
 
 	String escapedSql;
 
-	byte usesVariables = Statement.USES_VARIABLES_FALSE;
+	byte usesVariables = StatementImpl.USES_VARIABLES_FALSE;
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeTokenizer.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeTokenizer.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/EscapeTokenizer.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -139,7 +139,7 @@
 				tokenBuf.append(c);
 			} else if (c == '-') {
 				if ((this.lastChar == '-')
-						&& ((this.lastLastChar != '\\') & !this.inQuotes)) {
+						&& ((this.lastLastChar != '\\') && !this.inQuotes)) {
 					this.inComment = true;
 				}
 

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/ExportControlled.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ExportControlled.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ExportControlled.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -27,7 +27,21 @@
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.sql.SQLException;
 
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManagerFactory;
+
 /**
  * Holds functionality that falls under export-control regulations.
  * 
@@ -37,6 +51,8 @@
  *          Exp $
  */
 public class ExportControlled {
+	private static final String SQL_STATE_BAD_SSL_PARAMS = "08000";
+
 	protected static boolean enabled() {
 		// we may wish to un-static-ify this class
 		// this static method call may be removed entirely by the compiler
@@ -57,9 +73,8 @@
 	 *             perform the handshake.
 	 */
 	protected static void transformSocketToSSLSocket(MysqlIO mysqlIO)
-			throws CommunicationsException {
-		javax.net.ssl.SSLSocketFactory sslFact = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
-				.getDefault();
+			throws SQLException {
+		javax.net.ssl.SSLSocketFactory sslFact = getSSLSocketFactoryDefaultOrConfigured(mysqlIO);
 
 		try {
 			mysqlIO.mysqlConnection = sslFact.createSocket(
@@ -84,11 +99,135 @@
 
 			mysqlIO.mysqlOutput.flush();
 		} catch (IOException ioEx) {
-			throw new CommunicationsException(mysqlIO.connection,
+			throw SQLError.createCommunicationsException(mysqlIO.connection,
 					mysqlIO.lastPacketSentTimeMs, ioEx);
 		}
 	}
 
 	private ExportControlled() { /* prevent instantiation */
 	}
+
+	private static SSLSocketFactory getSSLSocketFactoryDefaultOrConfigured(
+			MysqlIO mysqlIO) throws SQLException {
+		String clientCertificateKeyStoreUrl = mysqlIO.connection
+				.getClientCertificateKeyStoreUrl();
+		String trustCertificateKeyStoreUrl = mysqlIO.connection
+				.getTrustCertificateKeyStoreUrl();
+		String clientCertificateKeyStoreType = mysqlIO.connection
+				.getClientCertificateKeyStoreType();
+		String clientCertificateKeyStorePassword = mysqlIO.connection
+				.getClientCertificateKeyStorePassword();
+		String trustCertificateKeyStoreType = mysqlIO.connection
+				.getTrustCertificateKeyStoreType();
+		String trustCertificateKeyStorePassword = mysqlIO.connection
+				.getTrustCertificateKeyStorePassword();
+
+		if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)
+				&& StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
+			return (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory
+					.getDefault();
+		}
+
+		TrustManagerFactory tmf = null;
+		KeyManagerFactory kmf = null;
+
+		try {
+			tmf = TrustManagerFactory.getInstance(TrustManagerFactory
+					.getDefaultAlgorithm());
+			kmf = KeyManagerFactory.getInstance(KeyManagerFactory
+					.getDefaultAlgorithm());
+		} catch (NoSuchAlgorithmException nsae) {
+			throw SQLError
+					.createSQLException(
+							"Default algorithm definitions for TrustManager and/or KeyManager are invalid.  Check java security properties file.",
+							SQL_STATE_BAD_SSL_PARAMS, 0, false);
+		}
+
+		if (StringUtils.isNullOrEmpty(clientCertificateKeyStoreUrl)) {
+			try {
+				KeyStore clientKeyStore = KeyStore
+						.getInstance(clientCertificateKeyStoreType);
+				URL ksURL = new URL(clientCertificateKeyStoreUrl);
+				char[] password = (clientCertificateKeyStorePassword == null) ? new char[0]
+						: clientCertificateKeyStorePassword.toCharArray();
+				clientKeyStore.load(ksURL.openStream(), password);
+				kmf.init(clientKeyStore, password);
+			} catch (UnrecoverableKeyException uke) {
+				throw SQLError
+						.createSQLException(
+								"Could not recover keys from client keystore.  Check password?",
+								SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			} catch (NoSuchAlgorithmException nsae) {
+				throw SQLError.createSQLException(
+						"Unsupported keystore algorithm [" + nsae.getMessage()
+								+ "]", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			} catch (KeyStoreException kse) {
+				throw SQLError.createSQLException(
+						"Could not create KeyStore instance ["
+								+ kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			} catch (CertificateException nsae) {
+				throw SQLError.createSQLException("Could not load client"
+						+ clientCertificateKeyStoreType + " keystore from "
+						+ clientCertificateKeyStoreUrl);
+			} catch (MalformedURLException mue) {
+				throw SQLError.createSQLException(clientCertificateKeyStoreUrl
+						+ " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0,
+						false);
+			} catch (IOException ioe) {
+				throw SQLError.createSQLException("Cannot open "
+						+ clientCertificateKeyStoreUrl + " ["
+						+ ioe.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			}
+		}
+
+		if (StringUtils.isNullOrEmpty(trustCertificateKeyStoreUrl)) {
+
+			try {
+				KeyStore trustKeyStore = KeyStore
+						.getInstance(trustCertificateKeyStoreType);
+				URL ksURL = new URL(trustCertificateKeyStoreUrl);
+
+				char[] password = (trustCertificateKeyStorePassword == null) ? new char[0]
+						: trustCertificateKeyStorePassword.toCharArray();
+				trustKeyStore.load(ksURL.openStream(), password);
+				tmf.init(trustKeyStore);
+			} catch (NoSuchAlgorithmException nsae) {
+				throw SQLError.createSQLException(
+						"Unsupported keystore algorithm [" + nsae.getMessage()
+								+ "]", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			} catch (KeyStoreException kse) {
+				throw SQLError.createSQLException(
+						"Could not create KeyStore instance ["
+								+ kse.getMessage() + "]", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			} catch (CertificateException nsae) {
+				throw SQLError.createSQLException("Could not load trust"
+						+ trustCertificateKeyStoreType + " keystore from "
+						+ trustCertificateKeyStoreUrl, SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			} catch (MalformedURLException mue) {
+				throw SQLError.createSQLException(trustCertificateKeyStoreUrl
+						+ " does not appear to be a valid URL.", SQL_STATE_BAD_SSL_PARAMS, 0,
+						false);
+			} catch (IOException ioe) {
+				throw SQLError.createSQLException("Cannot open "
+						+ trustCertificateKeyStoreUrl + " [" + ioe.getMessage()
+						+ "]", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+			}
+		}
+
+		SSLContext sslContext = null;
+
+		try {
+			sslContext = SSLContext.getInstance("TLS");
+			sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+			return sslContext.getSocketFactory();
+		} catch (NoSuchAlgorithmException nsae) {
+			throw SQLError.createSQLException("TLS"
+					+ " is not a valid SSL protocol.", SQL_STATE_BAD_SSL_PARAMS, 0, false);
+		} catch (KeyManagementException kme) {
+			throw SQLError.createSQLException("KeyManagementException: "
+					+ kme.getMessage(), SQL_STATE_BAD_SSL_PARAMS, 0, false);
+		}
+	}
+
 }
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/Extension.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Extension.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Extension.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+public interface Extension {
+
+	/**
+	 * Called once per connection that wants to use the extension
+	 * 
+	 * The properties are the same ones passed in in the URL or arguments to
+	 * Driver.connect() or DriverManager.getConnection().
+	 * 
+	 * @param conn the connection for which this extension is being created
+	 * @param props configuration values as passed to the connection. Note that
+	 * in order to support javax.sql.DataSources, configuration properties specific
+	 * to an interceptor <strong>must</strong> be passed via setURL() on the
+	 * DataSource. Extension properties are not exposed via 
+	 * accessor/mutator methods on DataSources.
+	 * 
+	 * @throws SQLException should be thrown if the the Extension
+	 * can not initialize itself.
+	 */
+	
+	public abstract void init(Connection conn, Properties props) throws SQLException;
+	
+	/**
+	 * Called by the driver when this extension should release any resources
+	 * it is holding and cleanup internally before the connection is
+	 * closed.
+	 */
+	public abstract void destroy();
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Field.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Field.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Field.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -2,12 +2,12 @@
  Copyright (C) 2002-2004 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as 
+ it under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.
 
- There are special exceptions to the terms and conditions of the GPL 
- as it is applied to this software. View the full text of the 
- exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
  software distribution.
 
  This program is distributed in the hope that it will be useful,
@@ -27,24 +27,20 @@
 import java.io.UnsupportedEncodingException;
 import java.sql.SQLException;
 import java.sql.Types;
+import java.util.regex.PatternSyntaxException;
 
 /**
  * Field is a class used to describe fields in a ResultSet
- * 
+ *
  * @author Mark Matthews
- * @version $Id: Field.java 5639 2006-08-16 14:18:23Z mmatthews $
+ * @version $Id: Field.java 6577 2007-09-07 16:12:04Z mmatthews $
  */
 public class Field {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
 
 	private static final int AUTO_INCREMENT_FLAG = 512;
 
 	private static final int NO_CHARSET_INFO = -1;
 
-	// ~ Instance fields
-	// --------------------------------------------------------
-
 	private byte[] buffer;
 
 	private int charsetIndex = 0;
@@ -57,7 +53,7 @@
 
 	private String collationName = null;
 
-	private Connection connection = null;
+	private ConnectionImpl connection = null;
 
 	private String databaseName = null;
 
@@ -110,18 +106,17 @@
 	private int tableNameLength;
 
 	private int tableNameStart;
-	
+
 	private boolean useOldNameMetadata = false;
 
 	private boolean isSingleBit;
 
-	// ~ Constructors
-	// -----------------------------------------------------------
+	private int maxBytesPerChar;
 
 	/**
 	 * Constructor used when communicating with 4.1 and newer servers
 	 */
-	Field(Connection conn, byte[] buffer, int databaseNameStart,
+	Field(ConnectionImpl conn, byte[] buffer, int databaseNameStart,
 			int databaseNameLength, int tableNameStart, int tableNameLength,
 			int originalTableNameStart, int originalTableNameLength,
 			int nameStart, int nameLength, int originalColumnNameStart,
@@ -156,17 +151,29 @@
 		// charset
 		this.charsetIndex = charsetIndex;
 
-		
+
 		// Map MySqlTypes to java.sql Types
 		this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
-		
+
+        checkForImplicitTemporaryTable();
 		// Re-map to 'real' blob type, if we're a BLOB
 
 		if (this.mysqlType == MysqlDefs.FIELD_TYPE_BLOB) {
-			if (this.charsetIndex == 63 || 
+		    boolean isFromFunction = this.originalTableNameLength == 0;
+
+		    if (this.connection != null && this.connection.getBlobsAreStrings() ||
+		            (this.connection.getFunctionsNeverReturnBlobs() && isFromFunction)) {
+		        this.sqlType = Types.VARCHAR;
+		        this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR;
+		    } else if (this.charsetIndex == 63 ||
 					!this.connection.versionMeetsMinimum(4, 1, 0)) {
-				setBlobTypeBasedOnLength();
-				this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
+				if (this.connection.getUseBlobToStoreUTF8OutsideBMP() 
+						&& shouldSetupForUtf8StringInBlob()) {
+					setupForUtf8StringInBlob();
+				} else {
+					setBlobTypeBasedOnLength();
+					this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
+				}
 			} else {
 				// *TEXT masquerading as blob
 				this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
@@ -186,28 +193,28 @@
 			}
 
 		}
-		
+
 		if (!isNativeNumericType() && !isNativeDateTimeType()) {
 			this.charsetName = this.connection
 				.getCharsetNameForIndex(this.charsetIndex);
 
-			
+
 			// Handle VARBINARY/BINARY (server doesn't have a different type
 			// for this
-	
+
 			boolean isBinary = isBinary();
-	
+
 			if (this.connection.versionMeetsMinimum(4, 1, 0) &&
-					this.mysqlType == MysqlDefs.FIELD_TYPE_VAR_STRING && 
+					this.mysqlType == MysqlDefs.FIELD_TYPE_VAR_STRING &&
 					isBinary &&
 					this.charsetIndex == 63) {
 				if (this.isOpaqueBinary()) {
 					this.sqlType = Types.VARBINARY;
 				}
-			} 
-			
+			}
+
 			if (this.connection.versionMeetsMinimum(4, 1, 0) &&
-					this.mysqlType == MysqlDefs.FIELD_TYPE_STRING && 
+					this.mysqlType == MysqlDefs.FIELD_TYPE_STRING &&
 					isBinary && this.charsetIndex == 63) {
 				//
 				// Okay, this is a hack, but there's currently no way
@@ -215,31 +222,32 @@
 				// from the "BINARY" column type, other than looking
 				// at the original column name.
 				//
-				
-				if (isOpaqueBinary()) {
+
+				if (isOpaqueBinary() && !this.connection.getBlobsAreStrings()) {
 					this.sqlType = Types.BINARY;
 				}
 			}
-	
-			
-	
+
+
+
 			if (this.mysqlType == MysqlDefs.FIELD_TYPE_BIT) {
 				this.isSingleBit = (this.length == 0);
-				
+
 				if (this.connection != null && (this.connection.versionMeetsMinimum(5, 0, 21) ||
 						this.connection.versionMeetsMinimum(5, 1, 10)) && this.length == 1) {
 					this.isSingleBit = true;
 				}
-				
+
 				if (this.isSingleBit) {
 					this.sqlType = Types.BIT;
 				} else {
 					this.sqlType = Types.VARBINARY;
 					this.colFlag |= 128; // we need to pretend this is a full
 					this.colFlag |= 16; // binary blob
+					isBinary = true;
 				}
 			}
-	
+
 			//
 			// Handle TEXT type (special case), Fix proposed by Peter McKeown
 			//
@@ -248,8 +256,6 @@
 			} else if ((this.sqlType == java.sql.Types.VARBINARY) && !isBinary) {
 				this.sqlType = java.sql.Types.VARCHAR;
 			}
-			
-			checkForImplicitTemporaryTable();
 		} else {
 			this.charsetName = "US-ASCII";
 		}
@@ -281,10 +287,71 @@
 		}
 	}
 
+	private boolean shouldSetupForUtf8StringInBlob() throws SQLException {
+		String includePattern = this.connection
+				.getUtf8OutsideBmpIncludedColumnNamePattern();
+		String excludePattern = this.connection
+				.getUtf8OutsideBmpExcludedColumnNamePattern();
+
+		if (excludePattern != null
+				&& !StringUtils.isEmptyOrWhitespaceOnly(excludePattern)) {
+			try {
+				if (getOriginalName().matches(excludePattern)) {
+					if (includePattern != null
+							&& !StringUtils.isEmptyOrWhitespaceOnly(includePattern)) {
+						try {
+							if (getOriginalName().matches(includePattern)) {
+								return true;
+							}
+						} catch (PatternSyntaxException pse) {
+							SQLException sqlEx = SQLError
+									.createSQLException(
+											"Illegal regex specified for \"utf8OutsideBmpIncludedColumnNamePattern\"",
+											SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+
+							if (!this.connection.getParanoid()) {
+								sqlEx.initCause(pse);
+							}
+
+							throw sqlEx;
+						}
+					}
+					
+					return false;
+				}
+			} catch (PatternSyntaxException pse) {
+				SQLException sqlEx = SQLError
+						.createSQLException(
+								"Illegal regex specified for \"utf8OutsideBmpExcludedColumnNamePattern\"",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+
+				if (!this.connection.getParanoid()) {
+					sqlEx.initCause(pse);
+				}
+
+				throw sqlEx;
+			}
+		}
+
+		return true;
+	}
+
+	private void setupForUtf8StringInBlob() {
+		if (this.length == MysqlDefs.LENGTH_TINYBLOB || this.length == MysqlDefs.LENGTH_BLOB) {
+			this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR;
+			this.sqlType = Types.VARCHAR;
+		}  else {
+			this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
+			this.sqlType = Types.LONGVARCHAR;
+		}
+		
+		this.charsetIndex = 33;	
+	}
+
 	/**
 	 * Constructor used when communicating with pre 4.1 servers
 	 */
-	Field(Connection conn, byte[] buffer, int nameStart, int nameLength,
+	Field(ConnectionImpl conn, byte[] buffer, int nameStart, int nameLength,
 			int tableNameStart, int tableNameLength, int length, int mysqlType,
 			short colFlag, int colDecimals) throws SQLException {
 		this(conn, buffer, -1, -1, tableNameStart, tableNameLength, -1, -1,
@@ -303,10 +370,34 @@
 		this.colFlag = 0;
 		this.colDecimals = 0;
 	}
-
-	// ~ Methods
-	// ----------------------------------------------------------------
-
+	
+	/**
+	 * Used by prepared statements to re-use result set data conversion methods
+	 * when generating bound parmeter retrieval instance for statement
+	 * interceptors.
+	 * 
+	 * @param tableName
+	 *            not used
+	 * @param columnName
+	 *            not used
+	 * @param charsetIndex
+	 *            the MySQL collation/character set index
+	 * @param jdbcType
+	 *            from java.sql.Types
+	 * @param length
+	 *            length in characters or bytes (for BINARY data).
+	 */
+	Field(String tableName, String columnName, int charsetIndex, int jdbcType,
+			int length) {
+		this.tableName = tableName;
+		this.name = columnName;
+		this.length = length;
+		this.sqlType = jdbcType;
+		this.colFlag = 0;
+		this.colDecimals = 0;
+		this.charsetIndex = charsetIndex;
+	}
+	
 	private void checkForImplicitTemporaryTable() {
 		this.isImplicitTempTable = this.tableNameLength > 5
 				&& this.buffer[tableNameStart] == (byte) '#'
@@ -318,7 +409,7 @@
 
 	/**
 	 * Returns the character set (if known) for this field.
-	 * 
+	 *
 	 * @return the character set
 	 */
 	public String getCharacterSet() throws SQLException {
@@ -329,82 +420,83 @@
 		if (this.collationName == null) {
 			if (this.connection != null) {
 				if (this.connection.versionMeetsMinimum(4, 1, 0)) {
-					java.sql.DatabaseMetaData dbmd = this.connection
-							.getMetaData();
+					if (this.connection.getUseDynamicCharsetInfo()) {
+						java.sql.DatabaseMetaData dbmd = this.connection
+								.getMetaData();
 
-					String quotedIdStr = dbmd.getIdentifierQuoteString();
+						String quotedIdStr = dbmd.getIdentifierQuoteString();
 
-					if (" ".equals(quotedIdStr)) { //$NON-NLS-1$
-						quotedIdStr = ""; //$NON-NLS-1$
-					}
+						if (" ".equals(quotedIdStr)) { //$NON-NLS-1$
+							quotedIdStr = ""; //$NON-NLS-1$
+						}
 
-					String csCatalogName = getDatabaseName();
-					String csTableName = getOriginalTableName();
-					String csColumnName = getOriginalName();
+						String csCatalogName = getDatabaseName();
+						String csTableName = getOriginalTableName();
+						String csColumnName = getOriginalName();
 
-					if (csCatalogName != null && csCatalogName.length() != 0
-							&& csTableName != null && csTableName.length() != 0
-							&& csColumnName != null
-							&& csColumnName.length() != 0) {
-						StringBuffer queryBuf = new StringBuffer(csCatalogName
-								.length()
-								+ csTableName.length() + 28);
-						queryBuf.append("SHOW FULL COLUMNS FROM "); //$NON-NLS-1$
-						queryBuf.append(quotedIdStr);
-						queryBuf.append(csCatalogName);
-						queryBuf.append(quotedIdStr);
-						queryBuf.append("."); //$NON-NLS-1$
-						queryBuf.append(quotedIdStr);
-						queryBuf.append(csTableName);
-						queryBuf.append(quotedIdStr);
+						if (csCatalogName != null && csCatalogName.length() != 0
+								&& csTableName != null && csTableName.length() != 0
+								&& csColumnName != null
+								&& csColumnName.length() != 0) {
+							StringBuffer queryBuf = new StringBuffer(csCatalogName
+									.length()
+									+ csTableName.length() + 28);
+							queryBuf.append("SHOW FULL COLUMNS FROM "); //$NON-NLS-1$
+							queryBuf.append(quotedIdStr);
+							queryBuf.append(csCatalogName);
+							queryBuf.append(quotedIdStr);
+							queryBuf.append("."); //$NON-NLS-1$
+							queryBuf.append(quotedIdStr);
+							queryBuf.append(csTableName);
+							queryBuf.append(quotedIdStr);
 
-						java.sql.Statement collationStmt = null;
-						java.sql.ResultSet collationRs = null;
+							java.sql.Statement collationStmt = null;
+							java.sql.ResultSet collationRs = null;
 
-						try {
-							collationStmt = this.connection.createStatement();
+							try {
+								collationStmt = this.connection.createStatement();
 
-							collationRs = collationStmt.executeQuery(queryBuf
-									.toString());
+								collationRs = collationStmt.executeQuery(queryBuf
+										.toString());
 
-							while (collationRs.next()) {
-								if (csColumnName.equals(collationRs
-										.getString("Field"))) { //$NON-NLS-1$
-									this.collationName = collationRs
-											.getString("Collation"); //$NON-NLS-1$
+								while (collationRs.next()) {
+									if (csColumnName.equals(collationRs
+											.getString("Field"))) { //$NON-NLS-1$
+										this.collationName = collationRs
+												.getString("Collation"); //$NON-NLS-1$
 
-									break;
+										break;
+									}
 								}
-							}
-						} finally {
-							if (collationRs != null) {
-								collationRs.close();
-								collationRs = null;
-							}
+							} finally {
+								if (collationRs != null) {
+									collationRs.close();
+									collationRs = null;
+								}
 
-							if (collationStmt != null) {
-								collationStmt.close();
-								collationStmt = null;
+								if (collationStmt != null) {
+									collationStmt.close();
+									collationStmt = null;
+								}
 							}
 						}
-
+					} else {
+						this.collationName = CharsetMapping.INDEX_TO_COLLATION[charsetIndex];
 					}
 				}
-
 			}
-
 		}
 
 		return this.collationName;
 	}
-
+	
 	public String getColumnLabel() throws SQLException {
 		return getName(); // column name if not aliased, alias if used
 	}
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getDatabaseName() throws SQLException {
@@ -423,7 +515,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getFullName() throws SQLException {
@@ -444,7 +536,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getFullOriginalName() throws SQLException {
@@ -472,20 +564,24 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public long getLength() {
 		return this.length;
 	}
 
-	public int getMaxBytesPerCharacter() throws SQLException {
-		return this.connection.getMaxBytesPerChar(getCharacterSet());
+	public synchronized int getMaxBytesPerCharacter() throws SQLException {
+		if (this.maxBytesPerChar == 0) {
+			this.maxBytesPerChar = this.connection.getMaxBytesPerChar(getCharacterSet());
+		}
+
+		return this.maxBytesPerChar;
 	}
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public int getMysqlType() {
@@ -494,7 +590,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getName() throws SQLException {
@@ -509,18 +605,18 @@
 		if (this.useOldNameMetadata) {
 			return getName();
 		}
-		
-		if (this.connection != null && 
+
+		if (this.connection != null &&
 				this.connection.versionMeetsMinimum(4, 1, 0)) {
 			return getOriginalName();
 		}
-		
+
 		return getName();
 	}
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getOriginalName() throws SQLException {
@@ -536,7 +632,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getOriginalTableName() throws SQLException {
@@ -553,9 +649,9 @@
 	/**
 	 * Returns amount of correction that should be applied to the precision
 	 * value.
-	 * 
+	 *
 	 * Different versions of MySQL report different precision values.
-	 * 
+	 *
 	 * @return the amount to adjust precision value by.
 	 */
 	public int getPrecisionAdjustFactor() {
@@ -564,7 +660,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public int getSQLType() {
@@ -642,7 +738,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getTable() throws SQLException {
@@ -651,7 +747,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public String getTableName() throws SQLException {
@@ -667,13 +763,13 @@
 		if (this.connection.versionMeetsMinimum(4, 1, 0)) {
 			return getOriginalTableName();
 		}
-			
+
 		return getTableName(); // pre-4.1, no aliases returned
 	}
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isAutoIncrement() {
@@ -682,7 +778,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isBinary() {
@@ -691,7 +787,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isBlob() {
@@ -700,7 +796,7 @@
 
 	/**
 	 * Is this field owned by a server-created temporary table?
-	 * 
+	 *
 	 * @return
 	 */
 	private boolean isImplicitTemporaryTable() {
@@ -709,7 +805,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isMultipleKey() {
@@ -731,7 +827,8 @@
 				&& (this.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING ||
 				this.getMysqlType() == MysqlDefs.FIELD_TYPE_VAR_STRING)) {
 
-			if (this.originalTableNameLength == 0) {
+			if (this.originalTableNameLength == 0 && (
+					this.connection != null && !this.connection.versionMeetsMinimum(5, 0, 25))) {
 				return false; // Probably from function
 			}
 
@@ -748,7 +845,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isPrimaryKey() {
@@ -757,7 +854,7 @@
 
 	/**
 	 * Is this field _definitely_ not writable?
-	 * 
+	 *
 	 * @return true if this field can not be written to in an INSERT/UPDATE
 	 *         statement.
 	 */
@@ -775,7 +872,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isUniqueKey() {
@@ -784,7 +881,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isUnsigned() {
@@ -793,7 +890,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @return DOCUMENT ME!
 	 */
 	public boolean isZeroFill() {
@@ -816,14 +913,14 @@
 			this.mysqlType = MysqlDefs.FIELD_TYPE_LONG_BLOB;
 		}
 	}
-	
+
 	private boolean isNativeNumericType() {
-		return ((this.mysqlType >= MysqlDefs.FIELD_TYPE_TINY && 
+		return ((this.mysqlType >= MysqlDefs.FIELD_TYPE_TINY &&
 					this.mysqlType <= MysqlDefs.FIELD_TYPE_DOUBLE) ||
 					this.mysqlType == MysqlDefs.FIELD_TYPE_LONGLONG ||
 					this.mysqlType == MysqlDefs.FIELD_TYPE_YEAR);
 	}
-	
+
 	private boolean isNativeDateTimeType() {
 		return (this.mysqlType == MysqlDefs.FIELD_TYPE_DATE ||
 				this.mysqlType == MysqlDefs.FIELD_TYPE_NEWDATE ||
@@ -834,11 +931,11 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @param conn
 	 *            DOCUMENT ME!
 	 */
-	public void setConnection(Connection conn) {
+	public void setConnection(ConnectionImpl conn) {
 		this.connection = conn;
 
 		this.charsetName = this.connection.getEncoding();
@@ -857,26 +954,70 @@
 		try {
 			StringBuffer asString = new StringBuffer();
 			asString.append(super.toString());
-
-			asString.append("\n  catalog: ");
+			asString.append("[");
+			asString.append("catalog=");
 			asString.append(this.getDatabaseName());
-			asString.append("\n  table name: ");
+			asString.append(",tableName=");
 			asString.append(this.getTableName());
-			asString.append("\n  original table name: ");
+			asString.append(",originalTableName=");
 			asString.append(this.getOriginalTableName());
-			asString.append("\n  column name: ");
+			asString.append(",columnName=");
 			asString.append(this.getName());
-			asString.append("\n  original column name: ");
+			asString.append(",originalColumnName=");
 			asString.append(this.getOriginalName());
-			asString.append("\n  MySQL data type: ");
+			asString.append(",mysqlType=");
 			asString.append(getMysqlType());
-
-			if (this.buffer != null) {
-				asString.append("\n\nData as received from server:\n\n");
-				asString.append(StringUtils.dumpAsHex(this.buffer,
-						this.buffer.length));
+			asString.append("(");
+			asString.append(MysqlDefs.typeToName(getMysqlType()));
+			asString.append(")");
+			asString.append(",flags=");
+			
+			if (isAutoIncrement()) {
+				asString.append(" AUTO_INCREMENT");
 			}
+			
+			if (isPrimaryKey()) {
+				asString.append(" PRIMARY_KEY");
+			}
+			
+			if (isUniqueKey()) {
+				asString.append(" UNIQUE_KEY");
+			}
+			
+			if (isBinary()) {
+				asString.append(" BINARY");
+			}
+			
+			if (isBlob()) {
+				asString.append(" BLOB");
+			}
+			
+			if (isMultipleKey()) {
+				asString.append(" MULTI_KEY");
+			}
+			
+			if (isUnsigned()) {
+				asString.append(" UNSIGNED");
+			}
+			
+			if (isZeroFill()) {
+				asString.append(" ZEROFILL");
+			}
 
+			asString.append(", charsetIndex=");
+			asString.append(this.charsetIndex);
+			asString.append(", charsetName=");
+			asString.append(this.charsetName);
+			
+			
+			//if (this.buffer != null) {
+			//	asString.append("\n\nData as received from server:\n\n");
+			//	asString.append(StringUtils.dumpAsHex(this.buffer,
+			//			this.buffer.length));
+			//}
+
+			asString.append("]");
+			
 			return asString.toString();
 		} catch (Throwable t) {
 			return super.toString();

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/IterateBlock.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/IterateBlock.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/IterateBlock.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,75 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.Iterator;
+
+import com.mysql.jdbc.DatabaseMetaData.IteratorWithCleanup;
+
+public abstract class IterateBlock {
+	IteratorWithCleanup iteratorWithCleanup;
+	Iterator javaIterator;
+	boolean stopIterating = false;
+	
+	IterateBlock(IteratorWithCleanup i) {
+		this.iteratorWithCleanup = i;
+		this.javaIterator = null;
+	}
+	
+	IterateBlock(Iterator i) {
+		this.javaIterator = i;
+		this.iteratorWithCleanup = null;
+	}
+
+	public void doForAll() throws SQLException {
+		if (this.iteratorWithCleanup != null) {
+			try {
+				while (this.iteratorWithCleanup.hasNext()) {
+					forEach(this.iteratorWithCleanup.next());
+					
+					if (this.stopIterating) {
+						break;
+					}
+				}
+			} finally {
+				this.iteratorWithCleanup.close();
+			}
+		} else {
+			while (this.javaIterator.hasNext()) {
+				forEach(this.javaIterator.next());
+				
+				if (this.stopIterating) {
+					break;
+				}
+			}
+		}
+	}
+
+	abstract void forEach(Object each) throws SQLException;
+	
+	public final boolean fullIteration() {
+		return !this.stopIterating;
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CallableStatement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CallableStatement.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CallableStatement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,283 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.SQLException;
+import java.sql.RowId;
+import java.sql.SQLXML;
+import java.sql.NClob;
+
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4CallableStatement extends CallableStatement {
+
+	public JDBC4CallableStatement(ConnectionImpl conn,
+			CallableStatementParamInfo paramInfo) throws SQLException {
+		super(conn, paramInfo);
+	}
+
+	public JDBC4CallableStatement(ConnectionImpl conn, String sql,
+			String catalog, boolean isFunctionCall) throws SQLException {
+		super(conn, sql, catalog, isFunctionCall);
+	}
+
+	
+	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+		JDBC4PreparedStatementHelper.setRowId(this, parameterIndex, x);
+	}
+
+	public void setRowId(String parameterName, RowId x) throws SQLException {
+		JDBC4PreparedStatementHelper.setRowId(this, getNamedParamIndex(
+				parameterName, false), x);
+	}
+
+	public void setSQLXML(int parameterIndex, SQLXML xmlObject)
+			throws SQLException {
+		JDBC4PreparedStatementHelper.setSQLXML(this, parameterIndex, xmlObject);
+	}
+
+	public void setSQLXML(String parameterName, SQLXML xmlObject)
+			throws SQLException {
+		JDBC4PreparedStatementHelper.setSQLXML(this, getNamedParamIndex(
+				parameterName, false), xmlObject);
+
+	}
+
+	public SQLXML getSQLXML(int parameterIndex) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
+
+		SQLXML retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getSQLXML(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+
+	}
+
+	public SQLXML getSQLXML(String parameterName) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely
+																// not going to
+																// be
+		// from ?=
+
+		SQLXML retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getSQLXML(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	public RowId getRowId(int parameterIndex) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
+
+		RowId retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getRowId(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	public RowId getRowId(String parameterName) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely
+																// not going to
+																// be
+		// from ?=
+
+		RowId retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getRowId(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * JDBC 4.0 Set a NCLOB parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setNClob(int parameterIndex, NClob value) throws SQLException {
+		JDBC4PreparedStatementHelper.setNClob(this, parameterIndex, value);
+	}
+
+	public void setNClob(String parameterName, NClob value) throws SQLException {
+		JDBC4PreparedStatementHelper.setNClob(this, getNamedParamIndex(
+				parameterName, false), value);
+
+	}
+
+	public void setNClob(String parameterName, Reader reader)
+			throws SQLException {
+		setNClob(getNamedParamIndex(parameterName, false), reader);
+
+	}
+
+	public void setNClob(String parameterName, Reader reader, long length)
+			throws SQLException {
+		setNClob(getNamedParamIndex(parameterName, false), reader, length);
+
+	}
+
+	public void setNString(String parameterName, String value)
+			throws SQLException {
+		setNString(getNamedParamIndex(parameterName, false), value);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getCharacterStream(int)
+	 */
+	public Reader getCharacterStream(int parameterIndex) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
+
+		Reader retValue = rs
+				.getCharacterStream(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getCharacterStream(java.lang.String)
+	 */
+	public Reader getCharacterStream(String parameterName) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely
+																// not going to
+																// be
+		// from ?=
+
+		Reader retValue = rs
+				.getCharacterStream(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNCharacterStream(int)
+	 */
+	public Reader getNCharacterStream(int parameterIndex) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
+
+		Reader retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getNCharacterStream(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNCharacterStream(java.lang.String)
+	 */
+	public Reader getNCharacterStream(String parameterName) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely
+																// not going to
+																// be
+		// from ?=
+
+		Reader retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getNCharacterStream(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNClob(int)
+	 */
+	public NClob getNClob(int parameterIndex) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
+
+		NClob retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getNClob(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNClob(java.lang.String)
+	 */
+	public NClob getNClob(String parameterName) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely
+																// not going to
+																// be
+		// from ?=
+
+		NClob retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getNClob(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNString(int)
+	 */
+	public String getNString(int parameterIndex) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(parameterIndex);
+
+		String retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getNString(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNString(java.lang.String)
+	 */
+	public String getNString(String parameterName) throws SQLException {
+		ResultSetInternalMethods rs = getOutputParameters(0); // definitely
+																// not going to
+																// be
+		// from ?=
+
+		String retValue = ((com.mysql.jdbc.JDBC4ResultSet) rs)
+				.getNString(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProvider.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,137 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.sql.SQLClientInfoException;
+import java.util.Properties;
+
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+/**
+ * Classes that implement this interface and provide a no-args constructor
+ * can be used by the driver to store and retrieve client information and/or
+ * labels.
+ * 
+ * The driver will create an instance for each Connection instance, and call
+ * initialize() once and only once. When the connection is closed, destroy()
+ * will be called, and the provider is expected to clean up any resources at
+ * this time.
+ *
+ * @version $Id: $
+ */
+public interface JDBC4ClientInfoProvider {
+	/**
+	 * Called once by the driver when it needs to configure the provider.
+	 * 
+	 * @param conn the connection that the provider belongs too.
+	 * @param configurationProps a java.util.Properties instance that contains
+	 * configuration information for the connection. 
+	 * @throws SQLException if initialization fails.
+	 */
+	public void initialize(java.sql.Connection conn, Properties configurationProps) throws SQLException;
+	
+	/**
+	 * Called once by the driver when the connection this provider instance
+	 * belongs to is being closed.
+	 * 
+	 * Implementations are expected to clean up and resources at this point
+	 * in time.
+	 * 
+	 * @throws SQLException if an error occurs.
+	 */
+	public void destroy() throws SQLException;
+	
+	/**
+	 * Returns the client info for the connection that this provider
+	 * instance belongs to. The connection instance is passed as an argument
+	 * for convenience's sake.
+	 * 
+	 * Providers can use the connection to communicate with the database,
+	 * but it will be within the scope of any ongoing transactions, so therefore
+	 * implementations should not attempt to change isolation level, autocommit settings
+	 * or call rollback() or commit() on the connection.
+	 * 
+	 * @param conn
+	 * @return 
+	 * @throws SQLException
+	 * 
+	 * @see java.sql.Connection#getClientInfo()
+	 */
+	public Properties getClientInfo(java.sql.Connection conn) throws SQLException;
+
+	/**
+	 * Returns the client info for the connection that this provider
+	 * instance belongs to. The connection instance is passed as an argument
+	 * for convenience's sake.
+	 * 
+	 * Providers can use the connection to communicate with the database,
+	 * but it will be within the scope of any ongoing transactions, so therefore
+	 * implementations should not attempt to change isolation level, autocommit settings
+	 * or call rollback() or commit() on the connection.
+	 * 
+	 * @param conn
+	 * @return 
+	 * @throws SQLException
+	 * 
+	 * @see java.sql.Connection#getClientInfo(java.lang.String)
+	 */
+	public String getClientInfo(java.sql.Connection conn, String name) throws SQLException;
+	
+	/**
+	 * Sets the client info for the connection that this provider
+	 * instance belongs to. The connection instance is passed as an argument
+	 * for convenience's sake.
+	 * 
+	 * Providers can use the connection to communicate with the database,
+	 * but it will be within the scope of any ongoing transactions, so therefore
+	 * implementations should not attempt to change isolation level, autocommit settings
+	 * or call rollback() or commit() on the connection.
+	 * 
+	 * @param conn
+	 * @return 
+	 * @throws SQLException
+	 * 
+	 * @see java.sql.Connection#setClientInfo(java.util.Properties)
+	 */
+	public void setClientInfo(java.sql.Connection conn, Properties properties) throws SQLClientInfoException;
+
+	/**
+	 * Sets the client info for the connection that this provider
+	 * instance belongs to. The connection instance is passed as an argument
+	 * for convenience's sake.
+	 * 
+	 * Providers can use the connection to communicate with the database,
+	 * but it will be within the scope of any ongoing transactions, so therefore
+	 * implementations should not attempt to change isolation level, autocommit settings
+	 * or call rollback() or commit() on the connection.
+	 * 
+	 * @param conn
+	 * @return 
+	 * @throws SQLException
+	 * 
+	 * @see java.sql.Connection#setClientInfo(java.lang.String,java.lang.String)
+	 */
+	public void setClientInfo(java.sql.Connection conn, String name, String value) throws SQLClientInfoException;
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ClientInfoProviderSP.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,168 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLClientInfoException;
+import java.util.Enumeration;
+import java.util.Properties;
+
+public class JDBC4ClientInfoProviderSP implements JDBC4ClientInfoProvider {
+	PreparedStatement setClientInfoSp;
+
+	PreparedStatement getClientInfoSp;
+
+	PreparedStatement getClientInfoBulkSp;
+
+	public synchronized void initialize(java.sql.Connection conn,
+			Properties configurationProps) throws SQLException {
+		String identifierQuote = conn.getMetaData().getIdentifierQuoteString();
+		String setClientInfoSpName = configurationProps.getProperty(
+				"clientInfoSetSPName", "setClientInfo");
+		String getClientInfoSpName = configurationProps.getProperty(
+				"clientInfoGetSPName", "getClientInfo");
+		String getClientInfoBulkSpName = configurationProps.getProperty(
+				"clientInfoGetBulkSPName", "getClientInfoBulk");
+		String clientInfoCatalog = configurationProps.getProperty(
+				"clientInfoCatalog", ""); // "" means use current from
+											// connection
+
+		String catalog = "".equals(clientInfoCatalog) ? conn.getCatalog()
+				: clientInfoCatalog;
+
+		this.setClientInfoSp = ((com.mysql.jdbc.Connection) conn)
+				.clientPrepareStatement("CALL " + identifierQuote + catalog
+						+ identifierQuote + "." + identifierQuote
+						+ setClientInfoSpName + identifierQuote + "(?, ?)");
+		
+		this.getClientInfoSp = ((com.mysql.jdbc.Connection) conn)
+				.clientPrepareStatement("CALL" + identifierQuote + catalog
+						+ identifierQuote + "." + identifierQuote
+						+ getClientInfoSpName + identifierQuote + "(?)");
+		
+		this.getClientInfoBulkSp = ((com.mysql.jdbc.Connection) conn)
+				.clientPrepareStatement("CALL " + identifierQuote + catalog
+						+ identifierQuote + "." + identifierQuote
+						+ getClientInfoBulkSpName + identifierQuote + "()");
+	}
+
+	public synchronized void destroy() throws SQLException {
+		if (this.setClientInfoSp != null) {
+			this.setClientInfoSp.close();
+			this.setClientInfoSp = null;
+		}
+
+		if (this.getClientInfoSp != null) {
+			this.getClientInfoSp.close();
+			this.getClientInfoSp = null;
+		}
+
+		if (this.getClientInfoBulkSp != null) {
+			this.getClientInfoBulkSp.close();
+			this.getClientInfoBulkSp = null;
+		}
+	}
+
+	public synchronized Properties getClientInfo(java.sql.Connection conn)
+			throws SQLException {
+		ResultSet rs = null;
+
+		Properties props = new Properties();
+		
+		try {
+			this.getClientInfoBulkSp.execute();
+
+			rs = this.getClientInfoBulkSp.getResultSet();
+
+			while (rs.next()) {
+				props.setProperty(rs.getString(1), rs.getString(2));
+			}
+		} finally {
+			if (rs != null) {
+				rs.close();
+			}
+		}
+		
+		return props;
+	}
+
+	public synchronized String getClientInfo(java.sql.Connection conn,
+			String name) throws SQLException {
+		ResultSet rs = null;
+
+		String clientInfo = null;
+		
+		try {
+			this.getClientInfoSp.setString(1, name);
+			this.getClientInfoSp.execute();
+
+			rs = this.getClientInfoSp.getResultSet();
+
+			if (rs.next()) {
+				clientInfo = rs.getString(1);
+			}
+		} finally {
+			if (rs != null) {
+				rs.close();
+			}
+		}
+		
+		return clientInfo;
+	}
+
+	public synchronized void setClientInfo(java.sql.Connection conn,
+			Properties properties) throws SQLClientInfoException {
+		try {
+			Enumeration propNames = properties.propertyNames();
+
+			while (propNames.hasMoreElements()) {
+				String name = (String) propNames.nextElement();
+				String value = properties.getProperty(name);
+
+				setClientInfo(conn, name, value);
+			}
+		} catch (SQLException sqlEx) {
+			SQLClientInfoException clientInfoEx = new SQLClientInfoException();
+			clientInfoEx.initCause(sqlEx);
+
+			throw clientInfoEx;
+		}
+	}
+
+	public synchronized void setClientInfo(java.sql.Connection conn,
+			String name, String value) throws SQLClientInfoException {
+		try {
+			this.setClientInfoSp.setString(1, name);
+			this.setClientInfoSp.setString(2, value);
+			this.setClientInfoSp.execute();
+		} catch (SQLException sqlEx) {
+			SQLClientInfoException clientInfoEx = new SQLClientInfoException();
+			clientInfoEx.initCause(sqlEx);
+
+			throw clientInfoEx;
+		}
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4CommentClientInfoProvider.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLClientInfoException;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * An implementation of JDBC4ClientInfoProvider that exposes
+ * the client info as a comment prepended to all statements issued
+ * by the driver.
+ * 
+ * Client information is <i>never</i> read from the server with this
+ * implementation, it is always cached locally.
+ * 
+ * @version $Id: $
+ */
+
+public class JDBC4CommentClientInfoProvider implements JDBC4ClientInfoProvider {
+	private Properties clientInfo;
+	
+	public synchronized void initialize(java.sql.Connection conn,
+			Properties configurationProps) throws SQLException {
+		this.clientInfo = new Properties();
+	}
+
+	public synchronized void destroy() throws SQLException {
+		this.clientInfo = null;
+	}
+
+	public synchronized Properties getClientInfo(java.sql.Connection conn)
+			throws SQLException {
+		return this.clientInfo;
+	}
+
+	public synchronized String getClientInfo(java.sql.Connection conn,
+			String name) throws SQLException {
+		return this.clientInfo.getProperty(name);
+	}
+
+	public synchronized void setClientInfo(java.sql.Connection conn,
+			Properties properties) throws SQLClientInfoException {
+		this.clientInfo = new Properties();
+		
+		Enumeration propNames = properties.propertyNames();
+		
+		while (propNames.hasMoreElements()) {
+			String name = (String)propNames.nextElement();
+			
+			this.clientInfo.put(name, properties.getProperty(name));
+		}
+		
+		setComment(conn);
+	}
+
+	public synchronized void setClientInfo(java.sql.Connection conn,
+			String name, String value) throws SQLClientInfoException {
+		this.clientInfo.setProperty(name, value);
+		setComment(conn);
+	}
+	
+	private synchronized void setComment(java.sql.Connection conn) {
+		StringBuffer commentBuf = new StringBuffer();
+		Iterator elements = this.clientInfo.entrySet().iterator();
+		
+		while (elements.hasNext()) {
+			if (commentBuf.length() > 0) {
+				commentBuf.append(", ");
+			}
+			
+			Map.Entry entry = (Map.Entry)elements.next();
+			commentBuf.append("" + entry.getKey());
+			commentBuf.append("=");
+			commentBuf.append("" + entry.getValue());
+		}
+		
+		((com.mysql.jdbc.Connection)conn).setStatementComment(
+				commentBuf.toString());
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4Connection.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4Connection.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4Connection.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,265 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.NClob;
+import java.sql.Struct;
+import java.util.Properties;
+import java.util.TimerTask;
+
+
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.Messages;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4Connection extends ConnectionImpl {
+	private JDBC4ClientInfoProvider infoProvider;
+	
+	public JDBC4Connection(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException {
+		super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url);
+		// TODO Auto-generated constructor stub
+	}
+
+	public SQLXML createSQLXML() throws SQLException {
+		return new JDBC4MysqlSQLXML();
+	}
+	
+	public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	public Properties getClientInfo() throws SQLException {
+		return getClientInfoProviderImpl().getClientInfo(this);
+	}
+
+	public String getClientInfo(String name) throws SQLException {
+		return getClientInfoProviderImpl().getClientInfo(this, name);
+	}
+
+	/**
+	 * Returns true if the connection has not been closed and is still valid.  
+	 * The driver shall submit a query on the connection or use some other 
+	 * mechanism that positively verifies the connection is still valid when 
+	 * this method is called.
+	 * <p>
+	 * The query submitted by the driver to validate the connection shall be 
+	 * executed in the context of the current transaction.
+	 * 
+	 * @param timeout -		The time in seconds to wait for the database operation 
+	 * 						used to validate the connection to complete.  If 
+	 * 						the timeout period expires before the operation 
+	 * 						completes, this method returns false.  A value of 
+	 * 						0 indicates a timeout is not applied to the 
+	 * 						database operation.
+	 * <p>
+	 * @return true if the connection is valid, false otherwise
+         * @exception SQLException if the value supplied for <code>timeout</code> 
+         * is less then 0
+         * @since 1.6
+	 */
+	public synchronized boolean isValid(int timeout) throws SQLException {
+		if (isClosed()) {
+			return false;
+		}
+		
+		TimerTask timeoutTask = null;
+		
+		if (timeout != 0) {
+			getCancelTimer().schedule(new TimerTask() { 
+				public void run() {
+					new Thread() {
+						public void run() {
+							try {
+								abortInternal();
+							} catch (Throwable t) {
+								throw new RuntimeException(t);
+							}
+						}
+					}.start();	
+				}
+				}, timeout * 1000);
+		}
+		
+		try {
+			synchronized (getMutex()) {
+				try {
+					pingInternal(false);
+					
+					if (timeoutTask != null) {
+						timeoutTask.cancel();
+					}
+					
+					timeoutTask = null;
+				} catch (Throwable t) {
+					try {
+						abortInternal();
+					} catch (Throwable ignoreThrown) {
+						// we're dead now anyway
+					}
+					
+					return false;
+				} finally {
+					if (timeoutTask != null) {
+						timeoutTask.cancel();
+					}
+				}
+			}
+		} catch (Throwable t) {
+			return false;
+		}
+		
+		return true;
+	}
+
+	public void setClientInfo(Properties properties) throws SQLClientInfoException {
+		try {
+			getClientInfoProviderImpl().setClientInfo(this, properties);
+		} catch (SQLClientInfoException ciEx) {
+			throw ciEx;
+		} catch (SQLException sqlEx) {
+			SQLClientInfoException clientInfoEx = new SQLClientInfoException();
+			clientInfoEx.initCause(sqlEx);
+
+			throw clientInfoEx;
+		}
+	}
+
+	public void setClientInfo(String name, String value) throws SQLClientInfoException {
+		try {
+			getClientInfoProviderImpl().setClientInfo(this, name, value);
+		} catch (SQLClientInfoException ciEx) {
+			throw ciEx;
+		} catch (SQLException sqlEx) {
+			SQLClientInfoException clientInfoEx = new SQLClientInfoException();
+			clientInfoEx.initCause(sqlEx);
+
+			throw clientInfoEx;
+		}
+	}
+
+    /**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		checkClosed();
+		
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface 
+     * @since 1.6
+     */
+    public <T> T unwrap(java.lang.Class<T> iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+            return iface.cast(this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
+    
+	/**
+	 * @see java.sql.Connection#createBlob()
+	 */
+	public Blob createBlob() {
+	    return new com.mysql.jdbc.Blob();
+	}
+
+	/**
+	 * @see java.sql.Connection#createClob()
+	 */
+	public Clob createClob() {
+	    return new com.mysql.jdbc.Clob();
+	}
+
+	/**
+	 * @see java.sql.Connection#createNClob()
+	 */
+	public NClob createNClob() {
+	    return new com.mysql.jdbc.JDBC4NClob();
+	}
+	
+	protected synchronized JDBC4ClientInfoProvider getClientInfoProviderImpl() throws SQLException {
+		if (this.infoProvider == null) {
+			try {
+				try {
+					this.infoProvider = (JDBC4ClientInfoProvider)Util.getInstance(getClientInfoProvider(), 
+							new Class[0], new Object[0]);
+				} catch (SQLException sqlEx) {
+					if (sqlEx.getCause() instanceof ClassCastException) {
+						// try with package name prepended
+						this.infoProvider = (JDBC4ClientInfoProvider)Util.getInstance(
+								"com.mysql.jdbc." + getClientInfoProvider(), 
+								new Class[0], new Object[0]);
+					}
+				}
+			} catch (ClassCastException cce) {
+				throw SQLError.createSQLException(Messages
+						.getString("JDBC4Connection.ClientInfoNotImplemented", new Object[] {getClientInfoProvider()}), 
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+			
+			this.infoProvider.initialize(this, this.props);
+		}
+		
+		return this.infoProvider;
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaData.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,204 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.ResultSet;
+import java.sql.RowIdLifetime;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+
+import java.util.List;
+
+import com.mysql.jdbc.Field;
+
+public class JDBC4DatabaseMetaData extends DatabaseMetaData {
+	public JDBC4DatabaseMetaData(ConnectionImpl connToSet, String databaseToSet) {
+		super(connToSet, databaseToSet);
+	}
+
+	public RowIdLifetime getRowIdLifetime() throws SQLException {
+		return RowIdLifetime.ROWID_UNSUPPORTED;
+	}
+
+	/**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface 
+     * @since 1.6
+     */
+    public <T> T unwrap(java.lang.Class<T> iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+            return iface.cast(this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
+
+    /**
+	 * Retrieves a list of the client info properties 
+	 * that the driver supports.  The result set contains the following columns
+	 * <p>
+         * <ol>
+	 * <li><b>NAME</b> String=> The name of the client info property<br>
+	 * <li><b>MAX_LEN</b> int=> The maximum length of the value for the property<br>
+	 * <li><b>DEFAULT_VALUE</b> String=> The default value of the property<br>
+	 * <li><b>DESCRIPTION</b> String=> A description of the property.  This will typically 
+	 * 						contain information as to where this property is 
+	 * 						stored in the database.
+	 * </ol>
+         * <p>
+	 * The <code>ResultSet</code> is sorted by the NAME column
+	 * <p>
+	 * @return	A <code>ResultSet</code> object; each row is a supported client info
+         * property
+	 * <p>
+	 *  @exception SQLException if a database access error occurs
+	 * <p>
+	 * @since 1.6
+	 */
+	public ResultSet getClientInfoProperties()
+		throws SQLException {
+		// We don't have any built-ins, we actually support whatever
+		// the client wants to provide, however we don't have a way
+		// to express this with the interface given
+		Field[] fields = new Field[4];
+		fields[0] = new Field("", "NAME", Types.VARCHAR, 255);
+		fields[1] = new Field("", "MAX_LEN", Types.INTEGER, 10);
+		fields[2] = new Field("", "DEFAULT_VALUE", Types.VARCHAR, 255);
+		fields[3] = new Field("", "DESCRIPTION", Types.VARCHAR, 255);
+		
+		ArrayList tuples = new ArrayList();
+		
+		return buildResultSet(fields, tuples, this.conn);
+	}
+	
+    public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
+    	return false;
+    }
+    
+    /**
+     * Retrieves a description of the  system and user functions available 
+     * in the given catalog.
+     * <P>
+     * Only system and user function descriptions matching the schema and
+     * function name criteria are returned.  They are ordered by
+     * <code>FUNCTION_CAT</code>, <code>FUNCTION_SCHEM</code>,
+     * <code>FUNCTION_NAME</code> and 
+     * <code>SPECIFIC_ NAME</code>.
+     *
+     * <P>Each function description has the the following columns:
+     *  <OL>
+     *	<LI><B>FUNCTION_CAT</B> String => function catalog (may be <code>null</code>)
+     *	<LI><B>FUNCTION_SCHEM</B> String => function schema (may be <code>null</code>)
+     *	<LI><B>FUNCTION_NAME</B> String => function name.  This is the name 
+     * used to invoke the function
+     *	<LI><B>REMARKS</B> String => explanatory comment on the function
+     * <LI><B>FUNCTION_TYPE</B> short => kind of function:
+     *      <UL>
+     *      <LI>functionResultUnknown - Cannot determine if a return value
+     *       or table will be returned
+     *      <LI> functionNoTable- Does not return a table
+     *      <LI> functionReturnsTable - Returns a table
+     *      </UL>
+     *	<LI><B>SPECIFIC_NAME</B> String  => the name which uniquely identifies 
+     *  this function within its schema.  This is a user specified, or DBMS
+     * generated, name that may be different then the <code>FUNCTION_NAME</code> 
+     * for example with overload functions
+     *  </OL>
+     * <p>
+     * A user may not have permission to execute any of the functions that are
+     * returned by <code>getFunctions</code>
+     *
+     * @param catalog a catalog name; must match the catalog name as it
+     *        is stored in the database; "" retrieves those without a catalog;
+     *        <code>null</code> means that the catalog name should not be used to narrow
+     *        the search
+     * @param schemaPattern a schema name pattern; must match the schema name
+     *        as it is stored in the database; "" retrieves those without a schema;
+     *        <code>null</code> means that the schema name should not be used to narrow
+     *        the search
+     * @param functionNamePattern a function name pattern; must match the
+     *        function name as it is stored in the database 
+     * @return <code>ResultSet</code> - each row is a function description 
+     * @exception SQLException if a database access error occurs
+     * @see #getSearchStringEscape 
+     * @since 1.6
+     */
+    public java.sql.ResultSet getFunctions(String catalog, String schemaPattern,
+			    String functionNamePattern) throws SQLException {
+    	Field[] fields = new Field[6];
+    	
+    	fields[0] = new Field("", "FUNCTION_CAT", Types.CHAR, 255);
+		fields[1] = new Field("", "FUNCTION_SCHEM", Types.CHAR, 255);
+		fields[2] = new Field("", "FUNCTION_NAME", Types.CHAR, 255);
+		fields[3] = new Field("", "REMARKS", Types.CHAR, 255);
+		fields[4] = new Field("", "FUNCTION_TYPE", Types.SMALLINT, 6);
+		fields[5] = new Field("", "SPECIFIC_NAME", Types.CHAR, 255);
+		
+		return getProceduresAndOrFunctions(
+				fields,
+				catalog,
+				schemaPattern,
+				functionNamePattern,
+				false,
+				true);
+    }
+    
+	protected int getJDBC4FunctionNoTableConstant() {
+		return functionNoTable;
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4DatabaseMetaDataUsingInfoSchema.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,92 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.ResultSet;
+import java.sql.RowIdLifetime;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+
+import java.util.List;
+
+public class JDBC4DatabaseMetaDataUsingInfoSchema extends DatabaseMetaDataUsingInfoSchema {
+	public JDBC4DatabaseMetaDataUsingInfoSchema(ConnectionImpl connToSet, String databaseToSet) throws SQLException {
+		super(connToSet, databaseToSet);
+	}
+
+	public RowIdLifetime getRowIdLifetime() throws SQLException {
+		return RowIdLifetime.ROWID_UNSUPPORTED;
+	}
+
+	/**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface 
+     * @since 1.6
+     */
+    public <T> T unwrap(java.lang.Class<T> iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+            return iface.cast(this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
+
+	protected int getJDBC4FunctionNoTableConstant() {
+		return functionNoTable;
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4MysqlSQLXML.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,833 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLXML;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stax.StAXResult;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.SAXException;
+
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4MysqlSQLXML implements SQLXML {
+
+	private XMLInputFactory inputFactory;
+
+	private XMLOutputFactory outputFactory;
+
+	private String stringRep;
+
+	private ResultSetInternalMethods owningResultSet;
+
+	private int columnIndexOfXml;
+
+	private boolean fromResultSet;
+
+	private boolean isClosed = false;
+
+	private boolean workingWithResult;
+
+	private DOMResult asDOMResult;
+
+	private SAXResult asSAXResult;
+
+	private SimpleSaxToReader saxToReaderConverter;
+
+	private StringWriter asStringWriter;
+
+	private ByteArrayOutputStream asByteArrayOutputStream;
+
+	protected JDBC4MysqlSQLXML(ResultSetInternalMethods owner, int index) {
+		this.owningResultSet = owner;
+		this.columnIndexOfXml = index;
+		this.fromResultSet = true;
+	}
+
+	protected JDBC4MysqlSQLXML() {
+		this.fromResultSet = false;
+	}
+
+	public synchronized void free() throws SQLException {
+		this.stringRep = null;
+		this.asDOMResult = null;
+		this.asSAXResult = null;
+		this.inputFactory = null;
+		this.outputFactory = null;
+		this.owningResultSet = null;
+		this.workingWithResult = false;
+		this.isClosed = true;
+
+	}
+
+	public synchronized String getString() throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		if (this.fromResultSet) {
+			return this.owningResultSet.getString(this.columnIndexOfXml);
+		}
+
+		return this.stringRep;
+	}
+
+	private synchronized void checkClosed() throws SQLException {
+		if (this.isClosed) {
+			throw SQLError
+					.createSQLException("SQLXMLInstance has been free()d");
+		}
+	}
+
+	private synchronized void checkWorkingWithResult() throws SQLException {
+		if (this.workingWithResult) {
+			throw SQLError
+					.createSQLException(
+							"Can't perform requested operation after getResult() has been called to write XML data",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	/**
+	 * Sets the XML value designated by this SQLXML instance to the given String
+	 * representation. The format of this String is defined by
+	 * org.xml.sax.InputSource, where the characters in the stream represent the
+	 * unicode code points for XML according to section 2 and appendix B of the
+	 * XML 1.0 specification. Although an encoding declaration other than
+	 * unicode may be present, the encoding of the String is unicode. The
+	 * behavior of this method is the same as ResultSet.updateString() when the
+	 * designated column of the ResultSet has a type java.sql.Types of SQLXML.
+	 * <p>
+	 * The SQL XML object becomes not writeable when this method is called and
+	 * may also become not readable depending on implementation.
+	 * 
+	 * @param value
+	 *            the XML value
+	 * @throws SQLException
+	 *             if there is an error processing the XML value. The getCause()
+	 *             method of the exception may provide a more detailed
+	 *             exception, for example, if the stream does not contain valid
+	 *             characters. An exception is thrown if the state is not
+	 *             writable.
+	 * @exception SQLFeatureNotSupportedException
+	 *                if the JDBC driver does not support this method
+	 * @since 1.6
+	 */
+
+	public synchronized void setString(String str) throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		this.stringRep = str;
+		this.fromResultSet = false;
+	}
+
+	public synchronized boolean isEmpty() throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		if (!this.fromResultSet) {
+			return this.stringRep == null || this.stringRep.length() == 0;
+		}
+
+		return false;
+	}
+
+	public synchronized InputStream getBinaryStream() throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		return this.owningResultSet.getBinaryStream(this.columnIndexOfXml);
+	}
+
+	/**
+	 * Retrieves the XML value designated by this SQLXML instance as a
+	 * java.io.Reader object. The format of this stream is defined by
+	 * org.xml.sax.InputSource, where the characters in the stream represent the
+	 * unicode code points for XML according to section 2 and appendix B of the
+	 * XML 1.0 specification. Although an encoding declaration other than
+	 * unicode may be present, the encoding of the stream is unicode. The
+	 * behavior of this method is the same as ResultSet.getCharacterStream()
+	 * when the designated column of the ResultSet has a type java.sql.Types of
+	 * SQLXML.
+	 * <p>
+	 * The SQL XML object becomes not readable when this method is called and
+	 * may also become not writable depending on implementation.
+	 * 
+	 * @return a stream containing the XML data.
+	 * @throws SQLException
+	 *             if there is an error processing the XML value. The getCause()
+	 *             method of the exception may provide a more detailed
+	 *             exception, for example, if the stream does not contain valid
+	 *             characters. An exception is thrown if the state is not
+	 *             readable.
+	 * @exception SQLFeatureNotSupportedException
+	 *                if the JDBC driver does not support this method
+	 * @since 1.6
+	 */
+	public synchronized Reader getCharacterStream() throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		return this.owningResultSet.getCharacterStream(this.columnIndexOfXml);
+	}
+
+	/**
+	 * Returns a Source for reading the XML value designated by this SQLXML
+	 * instance. Sources are used as inputs to XML parsers and XSLT
+	 * transformers.
+	 * <p>
+	 * Sources for XML parsers will have namespace processing on by default. The
+	 * systemID of the Source is implementation dependent.
+	 * <p>
+	 * The SQL XML object becomes not readable when this method is called and
+	 * may also become not writable depending on implementation.
+	 * <p>
+	 * Note that SAX is a callback architecture, so a returned SAXSource should
+	 * then be set with a content handler that will receive the SAX events from
+	 * parsing. The content handler will receive callbacks based on the contents
+	 * of the XML.
+	 * 
+	 * <pre>
+	 * SAXSource saxSource = sqlxml.getSource(SAXSource.class);
+	 * XMLReader xmlReader = saxSource.getXMLReader();
+	 * xmlReader.setContentHandler(myHandler);
+	 * xmlReader.parse(saxSource.getInputSource());
+	 * </pre>
+	 * 
+	 * @param sourceClass
+	 *            The class of the source, or null. If the class is null, a
+	 *            vendor specifc Source implementation will be returned. The
+	 *            following classes are supported at a minimum:
+	 * 
+	 * (MySQL returns a SAXSource if sourceClass == null)
+	 * 
+	 * <pre>
+	 *    javax.xml.transform.dom.DOMSource - returns a DOMSource
+	 *    javax.xml.transform.sax.SAXSource - returns a SAXSource
+	 *    javax.xml.transform.stax.StAXSource - returns a StAXSource
+	 *    javax.xml.transform.stream.StreamSource - returns a StreamSource
+	 * </pre>
+	 * 
+	 * @return a Source for reading the XML value.
+	 * @throws SQLException
+	 *             if there is an error processing the XML value or if this
+	 *             feature is not supported. The getCause() method of the
+	 *             exception may provide a more detailed exception, for example,
+	 *             if an XML parser exception occurs. An exception is thrown if
+	 *             the state is not readable.
+	 * @exception SQLFeatureNotSupportedException
+	 *                if the JDBC driver does not support this method
+	 * @since 1.6
+	 */
+	public synchronized Source getSource(Class clazz) throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		// Note that we try and use streams here wherever possible
+		// for the day that the server actually supports streaming
+		// from server -> client (futureproofing)
+
+		if (clazz == null || clazz.equals(SAXSource.class)) {
+
+			InputSource inputSource = null;
+
+			if (this.fromResultSet) {
+				inputSource = new InputSource(this.owningResultSet
+						.getCharacterStream(this.columnIndexOfXml));
+			} else {
+				inputSource = new InputSource(new StringReader(this.stringRep));
+			}
+
+			return new SAXSource(inputSource);
+		} else if (clazz.equals(DOMSource.class)) {
+			try {
+				DocumentBuilderFactory builderFactory = DocumentBuilderFactory
+						.newInstance();
+				builderFactory.setNamespaceAware(true);
+				DocumentBuilder builder = builderFactory.newDocumentBuilder();
+
+				InputSource inputSource = null;
+
+				if (this.fromResultSet) {
+					inputSource = new InputSource(this.owningResultSet
+							.getCharacterStream(this.columnIndexOfXml));
+				} else {
+					inputSource = new InputSource(new StringReader(
+							this.stringRep));
+				}
+
+				return new DOMSource(builder.parse(inputSource));
+			} catch (Throwable t) {
+				SQLException sqlEx = SQLError.createSQLException(t
+						.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				sqlEx.initCause(t);
+				
+				throw sqlEx;
+			}
+
+		} else if (clazz.equals(StreamSource.class)) {
+			Reader reader = null;
+
+			if (this.fromResultSet) {
+				reader = this.owningResultSet
+						.getCharacterStream(this.columnIndexOfXml);
+			} else {
+				reader = new StringReader(this.stringRep);
+			}
+
+			return new StreamSource(reader);
+		} else if (clazz.equals(StAXSource.class)) {
+			try {
+				Reader reader = null;
+
+				if (this.fromResultSet) {
+					reader = this.owningResultSet
+							.getCharacterStream(this.columnIndexOfXml);
+				} else {
+					reader = new StringReader(this.stringRep);
+				}
+
+				return new StAXSource(this.inputFactory
+						.createXMLStreamReader(reader));
+			} catch (XMLStreamException ex) {
+				SQLException sqlEx = SQLError.createSQLException(ex
+						.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				sqlEx.initCause(ex);
+				
+				throw sqlEx;
+			}
+		} else {
+			throw SQLError.createSQLException("XML Source of type \""
+					+ clazz.toString() + "\" Not supported.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	/**
+	 * Retrieves a stream that can be used to write the XML value that this
+	 * SQLXML instance represents. The stream begins at position 0. The bytes of
+	 * the stream are interpreted according to appendix F of the XML 1.0
+	 * specification The behavior of this method is the same as
+	 * ResultSet.updateBinaryStream() when the designated column of the
+	 * ResultSet has a type java.sql.Types of SQLXML.
+	 * <p>
+	 * The SQL XML object becomes not writeable when this method is called and
+	 * may also become not readable depending on implementation.
+	 * 
+	 * @return a stream to which data can be written.
+	 * @throws SQLException
+	 *             if there is an error processing the XML value. An exception
+	 *             is thrown if the state is not writable.
+	 * @exception SQLFeatureNotSupportedException
+	 *                if the JDBC driver does not support this method
+	 * @since 1.6
+	 */
+	public synchronized OutputStream setBinaryStream() throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		this.workingWithResult = true;
+
+		return setBinaryStreamInternal();
+	}
+
+	private synchronized OutputStream setBinaryStreamInternal()
+			throws SQLException {
+		this.asByteArrayOutputStream = new ByteArrayOutputStream();
+
+		return this.asByteArrayOutputStream;
+	}
+
+	/**
+	 * Retrieves a stream to be used to write the XML value that this SQLXML
+	 * instance represents. The format of this stream is defined by
+	 * org.xml.sax.InputSource, where the characters in the stream represent the
+	 * unicode code points for XML according to section 2 and appendix B of the
+	 * XML 1.0 specification. Although an encoding declaration other than
+	 * unicode may be present, the encoding of the stream is unicode. The
+	 * behavior of this method is the same as ResultSet.updateCharacterStream()
+	 * when the designated column of the ResultSet has a type java.sql.Types of
+	 * SQLXML.
+	 * <p>
+	 * The SQL XML object becomes not writeable when this method is called and
+	 * may also become not readable depending on implementation.
+	 * 
+	 * @return a stream to which data can be written.
+	 * @throws SQLException
+	 *             if there is an error processing the XML value. The getCause()
+	 *             method of the exception may provide a more detailed
+	 *             exception, for example, if the stream does not contain valid
+	 *             characters. An exception is thrown if the state is not
+	 *             writable.
+	 * @exception SQLFeatureNotSupportedException
+	 *                if the JDBC driver does not support this method
+	 * @since 1.6
+	 */
+	public synchronized Writer setCharacterStream() throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		this.workingWithResult = true;
+
+		return setCharacterStreamInternal();
+	}
+
+	private synchronized Writer setCharacterStreamInternal()
+			throws SQLException {
+		this.asStringWriter = new StringWriter();
+
+		return this.asStringWriter;
+	}
+
+	/**
+	 * Returns a Result for setting the XML value designated by this SQLXML
+	 * instance.
+	 * <p>
+	 * The systemID of the Result is implementation dependent.
+	 * <p>
+	 * The SQL XML object becomes not writeable when this method is called and
+	 * may also become not readable depending on implementation.
+	 * <p>
+	 * Note that SAX is a callback architecture and the returned SAXResult has a
+	 * content handler assigned that will receive the SAX events based on the
+	 * contents of the XML. Call the content handler with the contents of the
+	 * XML document to assign the values.
+	 * 
+	 * <pre>
+	 * SAXResult saxResult = sqlxml.setResult(SAXResult.class);
+	 * ContentHandler contentHandler = saxResult.getXMLReader().getContentHandler();
+	 * contentHandler.startDocument();
+	 * // set the XML elements and attributes into the result
+	 * contentHandler.endDocument();
+	 * </pre>
+	 * 
+	 * @param resultClass
+	 *            The class of the result, or null. If resultClass is null, a
+	 *            vendor specific Result implementation will be returned. The
+	 *            following classes are supported at a minimum:
+	 * 
+	 * <pre>
+	 *    javax.xml.transform.dom.DOMResult - returns a DOMResult
+	 *    javax.xml.transform.sax.SAXResult - returns a SAXResult
+	 *    javax.xml.transform.stax.StAXResult - returns a StAXResult
+	 *    javax.xml.transform.stream.StreamResult - returns a StreamResult
+	 * </pre>
+	 * 
+	 * @return Returns a Result for setting the XML value.
+	 * @throws SQLException
+	 *             if there is an error processing the XML value or if this
+	 *             feature is not supported. The getCause() method of the
+	 *             exception may provide a more detailed exception, for example,
+	 *             if an XML parser exception occurs. An exception is thrown if
+	 *             the state is not writable.
+	 * @exception SQLFeatureNotSupportedException
+	 *                if the JDBC driver does not support this method
+	 * @since 1.6
+	 */
+	public synchronized Result setResult(Class clazz) throws SQLException {
+		checkClosed();
+		checkWorkingWithResult();
+
+		this.workingWithResult = true;
+		this.asDOMResult = null;
+		this.asSAXResult = null;
+		this.saxToReaderConverter = null;
+		this.stringRep = null;
+		this.asStringWriter = null;
+		this.asByteArrayOutputStream = null;
+
+		if (clazz == null || clazz.equals(SAXResult.class)) {
+			this.saxToReaderConverter = new SimpleSaxToReader();
+
+			this.asSAXResult = new SAXResult(this.saxToReaderConverter);
+
+			return this.asSAXResult;
+		} else if (clazz.equals(DOMResult.class)) {
+
+			this.asDOMResult = new DOMResult();
+			return this.asDOMResult;
+
+		} else if (clazz.equals(StreamResult.class)) {
+			return new StreamResult(setCharacterStreamInternal());
+		} else if (clazz.equals(StAXResult.class)) {
+			try {
+				if (this.outputFactory == null) {
+					this.outputFactory = XMLOutputFactory.newInstance();
+				}
+
+				return new StAXResult(this.outputFactory
+						.createXMLEventWriter(setCharacterStreamInternal()));
+			} catch (XMLStreamException ex) {
+				SQLException sqlEx = SQLError.createSQLException(ex
+						.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				sqlEx.initCause(ex);
+				
+				throw sqlEx;
+			}
+		} else {
+			throw SQLError.createSQLException("XML Result of type \""
+					+ clazz.toString() + "\" Not supported.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	private Reader binaryInputStreamStreamToReader(ByteArrayOutputStream out) {
+
+		try {
+			// There's got to be an easier way to do this, but
+			// I don't feel like coding up Appendix F of the XML Spec
+			// myself, when there's a reusable way to do it, and we
+			// can warn folks away from BINARY xml streams that have
+			// to be parsed to determine the character encoding :P
+
+			String encoding = "UTF-8";
+
+			try {
+				ByteArrayInputStream bIn = new ByteArrayInputStream(out
+						.toByteArray());
+				XMLStreamReader reader = this.inputFactory
+						.createXMLStreamReader(bIn);
+
+				int eventType = 0;
+
+				while ((eventType = reader.next()) != XMLStreamReader.END_DOCUMENT) {
+					if (eventType == XMLStreamReader.START_DOCUMENT) {
+						String possibleEncoding = reader.getEncoding();
+
+						if (possibleEncoding != null) {
+							encoding = possibleEncoding;
+						}
+
+						break;
+					}
+				}
+			} catch (Throwable t) {
+				// ignore, dealt with later when the string can't be parsed
+				// into valid XML
+			}
+
+			return new StringReader(new String(out.toByteArray(), encoding));
+		} catch (UnsupportedEncodingException badEnc) {
+			throw new RuntimeException(badEnc);
+		}
+	}
+
+	protected String readerToString(Reader reader) throws SQLException {
+		StringBuffer buf = new StringBuffer();
+		
+		int charsRead = 0;
+		
+		char[] charBuf = new char[512];
+		
+		try {
+			while ((charsRead = reader.read(charBuf)) != -1) {
+				buf.append(charBuf, 0, charsRead);
+			}
+		} catch (IOException ioEx) {
+			SQLException sqlEx = SQLError.createSQLException(ioEx
+					.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			sqlEx.initCause(ioEx);
+			
+			throw sqlEx;
+		}
+		
+		return buf.toString();
+	}
+	
+	protected synchronized Reader serializeAsCharacterStream()
+			throws SQLException {
+		checkClosed();
+		if (this.workingWithResult) {
+			// figure out what kind of result
+			if (this.stringRep != null) {
+				return new StringReader(this.stringRep);
+			}
+
+			if (this.asDOMResult != null) {
+				return new StringReader(domSourceToString());
+			}
+
+			if (this.asStringWriter != null) { // stax result
+				return new StringReader(this.asStringWriter.toString());
+			}
+			
+			if (this.asSAXResult != null) {
+				return this.saxToReaderConverter.toReader();
+			}
+
+			if (this.asByteArrayOutputStream != null) {
+				return binaryInputStreamStreamToReader(this.asByteArrayOutputStream);
+			}
+		}
+
+		return this.owningResultSet.getCharacterStream(this.columnIndexOfXml);
+	}
+
+	protected String domSourceToString() throws SQLException {
+		try {
+			DOMSource source = new DOMSource(this.asDOMResult.getNode());
+			Transformer identity = TransformerFactory.newInstance()
+					.newTransformer();
+			StringWriter stringOut = new StringWriter();
+			Result result = new StreamResult(stringOut);
+			identity.transform(source, result);
+	
+			return stringOut.toString();
+		} catch (Throwable t) {
+			SQLException sqlEx = SQLError.createSQLException(t
+					.getMessage(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			sqlEx.initCause(t);
+			
+			throw sqlEx;
+		}
+	}
+	
+	protected synchronized String serializeAsString() throws SQLException {
+		checkClosed();
+		if (this.workingWithResult) {
+			// figure out what kind of result
+			if (this.stringRep != null) {
+				return this.stringRep;
+			}
+
+			if (this.asDOMResult != null) {
+				return domSourceToString();
+			}
+			
+			if (this.asStringWriter != null) { // stax result
+				return this.asStringWriter.toString();
+			}
+			
+			if (this.asSAXResult != null) {
+				return readerToString(this.saxToReaderConverter.toReader());
+			}
+
+			if (this.asByteArrayOutputStream != null) {
+				return readerToString(
+						binaryInputStreamStreamToReader(this.asByteArrayOutputStream));
+			}
+		}
+
+		return this.owningResultSet.getString(this.columnIndexOfXml);
+	}
+
+	/*
+	 * The SimpleSaxToReader class is an adaptation of the SAX "Writer"
+	 * example from the Apache XercesJ-2 Project. The license for this
+	 * code is as follows:
+	 *
+	 * Licensed to the Apache Software Foundation (ASF) under one or more
+	 * contributor license agreements.  See the NOTICE file distributed with
+	 * this work for additional information regarding copyright ownership.
+	 * The ASF licenses this file to You under the Apache License, Version 2.0
+	 * (the "License"); you may not use this file except in compliance with
+	 * the License.  You may obtain a copy of the License at
+	 * 
+	 *      http://www.apache.org/licenses/LICENSE-2.0
+	 * 
+	 * Unless required by applicable law or agreed to in writing, software
+	 * distributed under the License is distributed on an "AS IS" BASIS,
+	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+	 * See the License for the specific language governing permissions and
+	 * limitations under the License.
+	 */
+
+	class SimpleSaxToReader extends DefaultHandler {
+		StringBuffer buf = new StringBuffer();
+
+		public void startDocument() throws SAXException {
+			buf.append("<?xml version='1.0' encoding='UTF-8'?>");
+		}
+
+		public void endDocument() throws SAXException {
+			// Do we need to override this?
+		}
+
+		public void startElement(String namespaceURI, String sName,
+				String qName, Attributes attrs) throws SAXException {
+
+			this.buf.append("<");
+			this.buf.append(qName);
+
+			if (attrs != null) {
+				for (int i = 0; i < attrs.getLength(); i++) {
+					this.buf.append(" ");
+					this.buf.append(attrs.getQName(i)).append("=\"");
+					escapeCharsForXml(attrs.getValue(i), true);
+					this.buf.append("\"");
+				}
+			}
+
+			this.buf.append(">");
+		}
+
+		public void characters(char buf[], int offset, int len)
+				throws SAXException {
+			if (!this.inCDATA) {
+				escapeCharsForXml(buf, offset, len, false);
+			} else {
+				this.buf.append(buf, offset, len);
+			}
+		}
+
+		public void ignorableWhitespace(char ch[], int start, int length)
+				throws SAXException {
+			characters(ch, start, length);
+		}
+
+		private boolean inCDATA = false;
+
+		public void startCDATA() throws SAXException {
+			this.buf.append("<![CDATA[");
+			this.inCDATA = true;
+		}
+
+		public void endCDATA() throws SAXException {
+			this.inCDATA = false;
+			this.buf.append("]]>");
+		}
+
+		public void comment(char ch[], int start, int length)
+				throws SAXException {
+			// if (!fCanonical && fElementDepth > 0) {
+			this.buf.append("<!--");
+			for (int i = 0; i < length; ++i) {
+				this.buf.append(ch[start + i]);
+			}
+			this.buf.append("-->");
+			// }
+		}
+
+		Reader toReader() {
+			return new StringReader(this.buf.toString());
+		}
+
+		private void escapeCharsForXml(String str, boolean isAttributeData) {
+			if (str == null) {
+				return;
+			}
+
+			int strLen = str.length();
+
+			for (int i = 0; i < strLen; i++) {
+				escapeCharsForXml(str.charAt(i), isAttributeData);
+			}
+		}
+
+		private void escapeCharsForXml(char[] buf, int offset, int len,
+				boolean isAttributeData) {
+
+			if (buf == null) {
+				return;
+			}
+
+			for (int i = 0; i < len; i++) {
+				escapeCharsForXml(buf[offset + i], isAttributeData);
+			}
+		}
+
+		private void escapeCharsForXml(char c, boolean isAttributeData) {
+			switch (c) {
+			case '<': 
+				this.buf.append("&lt;");
+				break;
+			
+			case '>': 
+				this.buf.append("&gt;");
+				break;
+			
+			case '&':
+				this.buf.append("&amp;");
+				break;
+			
+			case '"': 
+
+				if (!isAttributeData) {
+					this.buf.append("\"");
+				}
+				else {
+					this.buf.append("&quot;");
+				}
+
+				break;
+		
+			case '\r': 
+				this.buf.append("&#xD;");
+				break;
+			
+
+			default: 
+
+				if (((c >= 0x01 && c <= 0x1F && c != 0x09 && c != 0x0A) 
+						|| (c >= 0x7F && c <= 0x9F) || c == 0x2028)
+						|| isAttributeData && (c == 0x09 || c == 0x0A)) {
+					this.buf.append("&#x");
+					this.buf.append(Integer.toHexString(c).toUpperCase());
+					this.buf.append(";");
+				}
+				else {
+					this.buf.append(c);
+				}        		
+			}
+		}
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4NClob.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4NClob.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4NClob.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+
+/**
+ * Simplistic implementation of java.sql.NClob for MySQL Connector/J
+ * 
+ * @author Tetsuro Ikeda
+ * @version $Id: NClob.java 4963 2006-02-21 13:28:14Z tikeda $
+ */
+public class JDBC4NClob extends Clob implements java.sql.NClob {
+
+	JDBC4NClob() {
+        super();
+    }
+	
+	JDBC4NClob(String charDataInit) {
+        super(charDataInit);
+    }
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatement.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,78 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.sql.NClob;
+import java.sql.RowId;
+import java.sql.SQLXML;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.PreparedStatement;
+import com.mysql.jdbc.PreparedStatement.ParseInfo;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4PreparedStatement extends PreparedStatement {
+
+	public JDBC4PreparedStatement(ConnectionImpl conn, String catalog) throws SQLException {
+		super(conn, catalog);
+	}
+	
+	public JDBC4PreparedStatement(ConnectionImpl conn, String sql, String catalog)
+		throws SQLException {
+		super(conn, sql, catalog);
+	}
+	
+	public JDBC4PreparedStatement(ConnectionImpl conn, String sql, String catalog,
+			ParseInfo cachedParseInfo) throws SQLException {
+		super(conn, sql, catalog, cachedParseInfo);
+	}
+
+	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+		JDBC4PreparedStatementHelper.setRowId(this, parameterIndex, x);
+	}
+	
+	/**
+	 * JDBC 4.0 Set a NCLOB parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setNClob(int parameterIndex, NClob value) throws SQLException {
+		JDBC4PreparedStatementHelper.setNClob(this, parameterIndex, value);
+	}
+
+	public void setSQLXML(int parameterIndex, SQLXML xmlObject)
+			throws SQLException {
+		JDBC4PreparedStatementHelper.setSQLXML(this, parameterIndex, xmlObject);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4PreparedStatementHelper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,77 @@
+package com.mysql.jdbc;
+
+import java.io.Reader;
+import java.sql.NClob;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Types;
+
+import com.mysql.jdbc.PreparedStatement;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4PreparedStatementHelper {
+	private JDBC4PreparedStatementHelper() {
+		
+	}
+	
+	static void setRowId(PreparedStatement pstmt, int parameterIndex, RowId x) throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+
+	/**
+	 * JDBC 4.0 Set a NCLOB parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	static void setNClob(PreparedStatement pstmt, int parameterIndex, NClob value) throws SQLException {
+		if (value == null) {
+			pstmt.setNull(parameterIndex, java.sql.Types.NCLOB);
+	    } else {
+	    	pstmt.setNCharacterStream(parameterIndex, value.getCharacterStream(), value.length());
+	    }
+	}
+
+	static void setNClob(PreparedStatement pstmt, int parameterIndex, Reader reader) throws SQLException {
+		pstmt.setNCharacterStream(parameterIndex, reader);
+	}
+
+	/**
+	 * JDBC 4.0 Set a NCLOB parameter.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param reader
+	 *            the java reader which contains the UNICODE data
+	 * @param length
+	 *            the number of characters in the stream
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	static void setNClob(PreparedStatement pstmt, int parameterIndex, Reader reader, long length)
+			throws SQLException {
+	    if (reader == null) {
+	    	pstmt.setNull(parameterIndex, java.sql.Types.NCLOB);
+	    } else {
+	    	pstmt.setNCharacterStream(parameterIndex, reader, length);
+	    }
+	}
+
+	static void setSQLXML(PreparedStatement pstmt, int parameterIndex, SQLXML xmlObject)
+			throws SQLException {
+		if (xmlObject == null) {
+			pstmt.setNull(parameterIndex, Types.SQLXML);
+		} else {
+			// FIXME: Won't work for Non-MYSQL SQLXMLs
+			pstmt.setCharacterStream(parameterIndex, ((JDBC4MysqlSQLXML)xmlObject).serializeAsCharacterStream());	
+		}
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ResultSet.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ResultSet.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ResultSet.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,526 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.sql.NClob;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.Field;
+import com.mysql.jdbc.NotUpdatable;
+import com.mysql.jdbc.ResultSetImpl;
+import com.mysql.jdbc.RowData;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.Statement;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4ResultSet extends ResultSetImpl {
+
+	public JDBC4ResultSet(long updateCount, long updateID, 
+			ConnectionImpl conn, StatementImpl creatorStmt) {
+		super(updateCount, updateID, conn, creatorStmt);
+	}
+	
+	public JDBC4ResultSet(String catalog, Field[] fields, RowData tuples,
+			ConnectionImpl conn, StatementImpl creatorStmt) throws SQLException {
+		super(catalog, fields, tuples, conn, creatorStmt);
+	}
+	
+	/**
+	 * JDBC 4.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the column to get the value from
+	 * 
+	 * @return the value in the column as a java.io.Reader.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public Reader getNCharacterStream(int columnIndex) throws SQLException {
+		checkColumnBounds(columnIndex);
+		
+		String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+		if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+			throw new SQLException(
+					"Can not call getNCharacterStream() when field's charset isn't UTF-8");
+		}
+		return getCharacterStream(columnIndex);
+	}
+
+	/**
+	 * JDBC 4.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnName
+	 *            the column name to retrieve the value from
+	 * 
+	 * @return the value as a java.io.Reader
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public Reader getNCharacterStream(String columnName) throws SQLException {
+		return getNCharacterStream(findColumn(columnName));
+	}
+
+	/**
+	 * JDBC 4.0 Get a NCLOB column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public NClob getNClob(int columnIndex) throws SQLException {
+		checkColumnBounds(columnIndex);
+		
+		String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+		if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+			throw new SQLException(
+					"Can not call getNClob() when field's charset isn't UTF-8");
+		}
+		if (!this.isBinaryEncoded) {
+			String asString = getStringForNClob(columnIndex);
+
+			if (asString == null) {
+				return null;
+			}
+
+			return new com.mysql.jdbc.JDBC4NClob(asString);
+		}
+
+		return getNativeNClob(columnIndex);
+	}
+
+	/**
+	 * JDBC 4.0 Get a NCLOB column.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * 
+	 * @return an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public NClob getNClob(String columnName) throws SQLException {
+		return getNClob(findColumn(columnName));
+	}
+	
+	/**
+	 * JDBC 4.0 Get a NCLOB column.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected java.sql.NClob getNativeNClob(int columnIndex)
+			throws SQLException {
+		String stringVal = getStringForNClob(columnIndex);
+
+		if (stringVal == null) {
+			return null;
+		}
+
+		return getNClobFromString(stringVal, columnIndex);
+	}
+	
+	private String getStringForNClob(int columnIndex) throws SQLException {
+		String asString = null;
+
+		String forcedEncoding = "UTF-8";
+
+		try {
+			byte[] asBytes = null;
+
+			if (!this.isBinaryEncoded) {
+				asBytes = getBytes(columnIndex);
+			} else {
+				asBytes = getNativeBytes(columnIndex, true);
+			}
+
+			if (asBytes != null) {
+				asString = new String(asBytes, forcedEncoding);
+			}
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException("Unsupported character encoding "
+					+ forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		return asString;
+	}
+	
+	private final java.sql.NClob getNClobFromString(String stringVal,
+			int columnIndex) throws SQLException {
+		return new com.mysql.jdbc.JDBC4NClob(stringVal);
+	}
+	
+	/**
+	 * JDBC 4.0
+	 * 
+	 * Get the value of a column in the current row as a Java String
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value, null for SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getNString(int columnIndex) throws SQLException {
+		checkColumnBounds(columnIndex);
+		
+		String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+		if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+			throw new SQLException(
+					"Can not call getNString() when field's charset isn't UTF-8");
+		}
+		return getString(columnIndex);
+	}
+	
+	/**
+	 * JDBC 4.0
+	 * 
+	 * The following routines simply convert the columnName into a columnIndex
+	 * and then call the appropriate routine above.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * 
+	 * @return the column value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getNString(String columnName) throws SQLException {
+		return getNString(findColumn(columnName));
+	}
+	
+	/**
+	 * JDBC 4.0 Update a column with a character stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            the length of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateNCharacterStream(int columnIndex, Reader x, int length)
+			throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 4.0 Update a column with a character stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param reader
+	 *            the stream to update the column with
+	 * @param length
+	 *            of the stream
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs
+	 */
+	public void updateNCharacterStream(String columnName, Reader reader,
+			int length) throws SQLException {
+		updateNCharacterStream(findColumn(columnName), reader, length);
+	}
+
+	/**
+	 * @see ResultSet#updateNClob(String, NClob)
+	 */
+	public void updateNClob(String columnName, NClob nClob) throws SQLException {
+		updateNClob(findColumn(columnName), nClob);
+	}
+	
+	public void updateRowId(int columnIndex, RowId x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	public void updateRowId(String columnName, RowId x) throws SQLException {
+		updateRowId(findColumn(columnName), x);
+	}
+
+	public int getHoldability() throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	public RowId getRowId(int columnIndex) throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	public RowId getRowId(String columnLabel) throws SQLException {
+		return getRowId(findColumn(columnLabel));
+	}
+
+	public SQLXML getSQLXML(int columnIndex) throws SQLException {
+		checkColumnBounds(columnIndex);
+		
+		return new JDBC4MysqlSQLXML(this, columnIndex);
+	}
+
+	public SQLXML getSQLXML(String columnLabel) throws SQLException {
+		return getSQLXML(findColumn(columnLabel));
+	}
+
+	public synchronized boolean isClosed() throws SQLException {
+		return this.isClosed;
+	}
+
+	public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
+		updateAsciiStream(findColumn(columnLabel), x);
+		
+	}
+
+	public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
+		updateAsciiStream(findColumn(columnLabel), x, length);
+	}
+
+	public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
+		updateBinaryStream(findColumn(columnLabel), x);
+	}
+
+	public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
+		updateBinaryStream(findColumn(columnLabel), x, length);
+	}
+
+	public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
+		updateBlob(findColumn(columnLabel), inputStream);
+	}
+
+	public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException {
+		updateBlob(findColumn(columnLabel), inputStream, length);
+	}
+
+	public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
+		updateCharacterStream(findColumn(columnLabel), reader);
+	}
+
+	public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+		updateCharacterStream(findColumn(columnLabel), reader, length);
+	}
+
+	public void updateClob(int columnIndex, Reader reader) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateClob(String columnLabel, Reader reader) throws SQLException {
+		updateClob(findColumn(columnLabel), reader);
+	}
+
+	public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
+		updateClob(findColumn(columnLabel), reader, length);
+	}
+
+	public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
+		updateNCharacterStream(findColumn(columnLabel), reader);
+		
+	}
+
+	public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+		updateNCharacterStream(findColumn(columnLabel), reader, length);
+	}
+
+	public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNClob(int columnIndex, Reader reader) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNClob(String columnLabel, Reader reader) throws SQLException {
+		updateNClob(findColumn(columnLabel), reader);
+		
+	}
+
+	public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
+		updateNClob(findColumn(columnLabel), reader, length);
+	}
+
+	public void updateNString(int columnIndex, String nString) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNString(String columnLabel, String nString) throws SQLException {
+		updateNString(findColumn(columnLabel), nString);
+	}
+
+	public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
+		updateSQLXML(findColumn(columnLabel), xmlObject);
+		
+	}
+
+	/**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		checkClosed();
+		
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface 
+     * @since 1.6
+     */
+    public <T> T unwrap(java.lang.Class<T> iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+            return iface.cast(this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4ServerPreparedStatement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.io.Reader;
+import java.sql.NClob;
+import java.sql.RowId;
+import java.sql.SQLXML;
+import java.sql.SQLException;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.MysqlDefs;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.ServerPreparedStatement;
+import com.mysql.jdbc.ServerPreparedStatement.BindValue;
+
+public class JDBC4ServerPreparedStatement extends ServerPreparedStatement {
+
+	public JDBC4ServerPreparedStatement(ConnectionImpl conn, String sql,
+			String catalog, int resultSetType, int resultSetConcurrency)
+			throws SQLException {
+		super(conn, sql, catalog, resultSetType, resultSetConcurrency);
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setNCharacterStream(int, java.io.Reader,
+	 *      long)
+	 */
+	public void setNCharacterStream(int parameterIndex, Reader reader,
+			long length) throws SQLException {
+		// can't take if characterEncoding isn't utf8
+		if (!this.charEncoding.equalsIgnoreCase("UTF-8")
+				&& !this.charEncoding.equalsIgnoreCase("utf8")) {
+			throw SQLError
+					.createSQLException("Can not call setNCharacterStream() when connection character set isn't UTF-8");
+		}
+
+		checkClosed();
+
+		if (reader == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = reader;
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = length;
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setNClob(int, java.sql.NClob)
+	 */
+	public void setNClob(int parameterIndex, NClob x) throws SQLException {
+		setNClob(parameterIndex, x.getCharacterStream(), this.connection
+				.getUseStreamLengthsInPrepStmts() ? x.length() : -1);
+	}
+
+	/**
+	 * JDBC 4.0 Set a NCLOB parameter.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param reader
+	 *            the java reader which contains the UNICODE data
+	 * @param length
+	 *            the number of characters in the stream
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setNClob(int parameterIndex, Reader reader, long length)
+			throws SQLException {
+		// can't take if characterEncoding isn't utf8
+		if (!this.charEncoding.equalsIgnoreCase("UTF-8")
+				&& !this.charEncoding.equalsIgnoreCase("utf8")) {
+			throw SQLError
+					.createSQLException("Can not call setNClob() when connection character set isn't UTF-8");
+		}
+
+		checkClosed();
+
+		if (reader == null) {
+			setNull(parameterIndex, java.sql.Types.NCLOB);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = reader;
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = length;
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setNString(int, java.lang.String)
+	 */
+	public void setNString(int parameterIndex, String x) throws SQLException {
+		if (this.charEncoding.equalsIgnoreCase("UTF-8")
+				|| this.charEncoding.equalsIgnoreCase("utf8")) {
+			setString(parameterIndex, x);
+		} else {
+			throw SQLError
+					.createSQLException("Can not call setNString() when connection character set isn't UTF-8");
+		}
+	}
+
+	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+		JDBC4PreparedStatementHelper.setRowId(this, parameterIndex, x);
+	}
+
+	public void setSQLXML(int parameterIndex, SQLXML xmlObject)
+			throws SQLException {
+		JDBC4PreparedStatementHelper.setSQLXML(this, parameterIndex, xmlObject);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/JDBC4UpdatableResultSet.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,613 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.sql.NClob;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.Field;
+import com.mysql.jdbc.NotUpdatable;
+import com.mysql.jdbc.RowData;
+import com.mysql.jdbc.Statement;
+import com.mysql.jdbc.StringUtils;
+import com.mysql.jdbc.UpdatableResultSet;
+
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
+public class JDBC4UpdatableResultSet extends UpdatableResultSet {
+	public JDBC4UpdatableResultSet(String catalog, Field[] fields, RowData tuples, 
+			ConnectionImpl conn, StatementImpl creatorStmt) throws SQLException {
+		super(catalog, fields, tuples, conn, creatorStmt);
+	}
+
+	public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+	
+	public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+
+	public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateClob(int columnIndex, Reader reader) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+		updateNCharacterStream(columnIndex, x, (int) length);
+		
+	}
+	
+
+	public void updateNClob(int columnIndex, Reader reader) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+
+	public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
+		throw new NotUpdatable();
+		
+	}
+	
+	public void updateRowId(int columnIndex, RowId x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
+		updateAsciiStream(findColumn(columnLabel), x);	
+	}
+
+	public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException {
+		updateAsciiStream(findColumn(columnLabel), x, length);
+	}
+
+	public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
+		updateBinaryStream(findColumn(columnLabel), x);
+	}
+
+	public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException {
+		updateBinaryStream(findColumn(columnLabel), x, length);
+	}
+
+	public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
+		updateBlob(findColumn(columnLabel), inputStream);
+	}
+
+	public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException {
+		updateBlob(findColumn(columnLabel), inputStream, length);
+	}
+
+	public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
+		updateCharacterStream(findColumn(columnLabel), reader);
+	}
+
+	public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+		updateCharacterStream(findColumn(columnLabel), reader, length);
+	}
+	
+	public void updateClob(String columnLabel, Reader reader) throws SQLException {
+		updateClob(findColumn(columnLabel), reader);
+	}
+
+	public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
+		updateClob(findColumn(columnLabel), reader, length);
+	}
+	
+	public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException {
+		updateNCharacterStream(findColumn(columnLabel), reader);
+		
+	}
+
+	public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException {
+		updateNCharacterStream(findColumn(columnLabel), reader, length);
+	}
+
+
+	public void updateNClob(String columnLabel, Reader reader) throws SQLException {
+		updateNClob(findColumn(columnLabel), reader);
+		
+	}
+
+	public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
+		updateNClob(findColumn(columnLabel), reader, length);
+	}
+
+	public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
+		updateSQLXML(findColumn(columnLabel), xmlObject);
+		
+	}
+
+	/**
+	 * JDBC 4.0 Update a column with a character stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            the length of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public synchronized void updateNCharacterStream(int columnIndex,
+	        java.io.Reader x, int length) throws SQLException {
+	    String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+	    if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+	        throw new SQLException(
+	                "Can not call updateNCharacterStream() when field's character set isn't UTF-8");
+	    }
+	    
+	    if (!this.onInsertRow) {
+	        if (!this.doingUpdates) {
+	            this.doingUpdates = true;
+	            syncUpdate();
+	        }
+	
+	        ((com.mysql.jdbc.JDBC4PreparedStatement)this.updater).setNCharacterStream(columnIndex, x, length);
+	    } else {
+	    	((com.mysql.jdbc.JDBC4PreparedStatement)this.inserter).setNCharacterStream(columnIndex, x, length);
+	
+	        if (x == null) {
+	            this.thisRow.setColumnValue(columnIndex, null);
+	        } else {
+	        	 this.thisRow.setColumnValue(columnIndex, STREAM_DATA_MARKER);
+	        }
+	    }
+	}
+
+	/**
+	 * JDBC 4.0 Update a column with a character stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param reader
+	 *            the new column value
+	 * @param length
+	 *            of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public synchronized void updateNCharacterStream(String columnName,
+	        java.io.Reader reader, int length) throws SQLException {
+	    updateNCharacterStream(findColumn(columnName), reader, length);
+	}
+
+	/**
+	 * @see ResultSet#updateNClob(int, NClob)
+	 */
+	public void updateNClob(int columnIndex, java.sql.NClob nClob)
+	        throws SQLException {
+	    String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+	    if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+	        throw new SQLException("Can not call updateNClob() when field's character set isn't UTF-8");
+	    }
+	    
+	    if (nClob == null) {
+	        updateNull(columnIndex);
+	    } else {
+	        updateNCharacterStream(columnIndex, nClob.getCharacterStream(),
+	                (int) nClob.length());
+	    }
+	}
+
+	/**
+	 * @see ResultSet#updateClob(int, Clob)
+	 */
+	public void updateNClob(String columnName, java.sql.NClob nClob)
+	        throws SQLException {
+	    updateNClob(findColumn(columnName), nClob);
+	}
+
+	/**
+	 * JDBC 4.0 Update a column with NATIONAL CHARACTER. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public synchronized void updateNString(int columnIndex, String x)
+	        throws SQLException {
+	    String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+	    if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+	        throw new SQLException("Can not call updateNString() when field's character set isn't UTF-8");
+	    }
+	    
+	    if (!this.onInsertRow) {
+	        if (!this.doingUpdates) {
+	            this.doingUpdates = true;
+	            syncUpdate();
+	        }
+	
+	        ((com.mysql.jdbc.JDBC4PreparedStatement)this.updater).setNString(columnIndex, x);
+	    } else {
+	    	((com.mysql.jdbc.JDBC4PreparedStatement)this.inserter).setNString(columnIndex, x);
+	
+	        if (x == null) {
+	        	 this.thisRow.setColumnValue(columnIndex, null);
+	        } else {
+	        	 this.thisRow.setColumnValue(columnIndex, StringUtils.getBytes(x,
+	                        this.charConverter, fieldEncoding,
+	                        this.connection.getServerCharacterEncoding(),
+	                        this.connection.parserKnowsUnicode()));
+	        }
+	    }
+	}
+
+	/**
+	 * JDBC 4.0 Update a column with NATIONAL CHARACTER. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public synchronized void updateNString(String columnName, String x)
+	        throws SQLException {
+	    updateNString(findColumn(columnName), x);
+	}
+
+	public int getHoldability() throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	/**
+	 * JDBC 4.0 Get a NCLOB column.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected java.sql.NClob getNativeNClob(int columnIndex)
+			throws SQLException {
+		String stringVal = getStringForNClob(columnIndex);
+	
+		if (stringVal == null) {
+			return null;
+		}
+	
+		return getNClobFromString(stringVal, columnIndex);
+	}
+
+	/**
+	 * JDBC 4.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the column to get the value from
+	 * 
+	 * @return the value in the column as a java.io.Reader.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public Reader getNCharacterStream(int columnIndex) throws SQLException {
+		String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+		if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+			throw new SQLException(
+					"Can not call getNCharacterStream() when field's charset isn't UTF-8");
+		}
+		
+		return getCharacterStream(columnIndex);
+	}
+
+	/**
+	 * JDBC 4.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnName
+	 *            the column name to retrieve the value from
+	 * 
+	 * @return the value as a java.io.Reader
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public Reader getNCharacterStream(String columnName) throws SQLException {
+		return getNCharacterStream(findColumn(columnName));
+	}
+
+	/**
+	 * JDBC 4.0 Get a NCLOB column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public NClob getNClob(int columnIndex) throws SQLException {
+		String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+		
+		if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+			throw new SQLException(
+					"Can not call getNClob() when field's charset isn't UTF-8");
+		}
+		
+		if (!this.isBinaryEncoded) {
+			String asString = getStringForNClob(columnIndex);
+	
+			if (asString == null) {
+				return null;
+			}
+	
+			return new com.mysql.jdbc.JDBC4NClob(asString);
+		}
+	
+		return getNativeNClob(columnIndex);
+	}
+
+	/**
+	 * JDBC 4.0 Get a NCLOB column.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * 
+	 * @return an object representing a NCLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public NClob getNClob(String columnName) throws SQLException {
+		return getNClob(findColumn(columnName));
+	}
+
+	private final java.sql.NClob getNClobFromString(String stringVal,
+			int columnIndex) throws SQLException {
+		return new com.mysql.jdbc.JDBC4NClob(stringVal);
+	}
+
+	/**
+	 * JDBC 4.0
+	 * 
+	 * Get the value of a column in the current row as a Java String
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value, null for SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getNString(int columnIndex) throws SQLException {
+		String fieldEncoding = this.fields[columnIndex - 1].getCharacterSet();
+		
+		if (fieldEncoding == null || !fieldEncoding.equals("UTF-8")) {
+			throw new SQLException(
+					"Can not call getNString() when field's charset isn't UTF-8");
+		}
+		
+		return getString(columnIndex);
+	}
+
+	/**
+	 * JDBC 4.0
+	 * 
+	 * The following routines simply convert the columnName into a columnIndex
+	 * and then call the appropriate routine above.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * 
+	 * @return the column value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getNString(String columnName) throws SQLException {
+		return getNString(findColumn(columnName));
+	}
+
+	public RowId getRowId(int columnIndex) throws SQLException {
+		throw new NotYetImplementedException();
+	}
+
+	public RowId getRowId(String columnLabel) throws SQLException {
+		return getRowId(findColumn(columnLabel));
+	}
+
+	public SQLXML getSQLXML(int columnIndex) throws SQLException {
+		return new JDBC4MysqlSQLXML(this, columnIndex);
+	}
+
+	public SQLXML getSQLXML(String columnLabel) throws SQLException {
+		return getSQLXML(findColumn(columnLabel));
+	}
+
+	private String getStringForNClob(int columnIndex) throws SQLException {
+		String asString = null;
+	
+		String forcedEncoding = "UTF-8";
+	
+		try {
+			byte[] asBytes = null;
+	
+			if (!this.isBinaryEncoded) {
+				asBytes = getBytes(columnIndex);
+			} else {
+				asBytes = getNativeBytes(columnIndex, true);
+			}
+	
+			if (asBytes != null) {
+				asString = new String(asBytes, forcedEncoding);
+			}
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException("Unsupported character encoding "
+					+ forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	
+		return asString;
+	}
+
+	public synchronized boolean isClosed() throws SQLException {
+		return this.isClosed;
+	}
+
+	/**
+	 * Returns true if this either implements the interface argument or is
+	 * directly or indirectly a wrapper for an object that does. Returns false
+	 * otherwise. If this implements the interface then return true, else if
+	 * this is a wrapper then return the result of recursively calling
+	 * <code>isWrapperFor</code> on the wrapped object. If this does not
+	 * implement the interface and is not a wrapper, return false. This method
+	 * should be implemented as a low-cost operation compared to
+	 * <code>unwrap</code> so that callers can use this method to avoid
+	 * expensive <code>unwrap</code> calls that may fail. If this method
+	 * returns true then calling <code>unwrap</code> with the same argument
+	 * should succeed.
+	 * 
+	 * @param interfaces
+	 *            a Class defining an interface.
+	 * @return true if this implements the interface or directly or indirectly
+	 *         wraps an object that does.
+	 * @throws java.sql.SQLException
+	 *             if an error occurs while determining whether this is a
+	 *             wrapper for an object with the given interface.
+	 * @since 1.6
+	 */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		checkClosed();
+		
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+	 * Returns an object that implements the given interface to allow access to
+	 * non-standard methods, or standard methods not exposed by the proxy. The
+	 * result may be either the object found to implement the interface or a
+	 * proxy for that object. If the receiver implements the interface then that
+	 * is the object. If the receiver is a wrapper and the wrapped object
+	 * implements the interface then that is the object. Otherwise the object is
+	 * the result of calling <code>unwrap</code> recursively on the wrapped
+	 * object. If the receiver is not a wrapper and does not implement the
+	 * interface, then an <code>SQLException</code> is thrown.
+	 * 
+	 * @param iface
+	 *            A Class defining an interface that the result must implement.
+	 * @return an object that implements the interface. May be a proxy for the
+	 *         actual implementing object.
+	 * @throws java.sql.SQLException
+	 *             If no object found that implements the interface
+	 * @since 1.6
+	 */
+    public <T> T unwrap(java.lang.Class<T> iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+            return iface.cast(this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,532 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+
+package com.mysql.jdbc;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * An implementation of java.sql.Connection that load balances requests across a
+ * series of MySQL JDBC connections, where the balancing takes place at
+ * transaction commit.
+ * 
+ * Therefore, for this to work (at all), you must use transactions, even if only
+ * reading data.
+ * 
+ * This implementation will invalidate connections that it detects have had
+ * communication errors when processing a request. A new connection to the
+ * problematic host will be attempted the next time it is selected by the load
+ * balancing algorithm.
+ * 
+ * This implementation is thread-safe, but it's questionable whether sharing a
+ * connection instance amongst threads is a good idea, given that transactions
+ * are scoped to connections in JDBC.
+ * 
+ * @version $Id: $
+ * 
+ */
+public class LoadBalancingConnectionProxy implements InvocationHandler, PingTarget {
+
+	private static Method getLocalTimeMethod;
+
+	static {
+		try {
+			getLocalTimeMethod = System.class.getMethod("nanoTime",
+					new Class[0]);
+		} catch (SecurityException e) {
+			// ignore
+		} catch (NoSuchMethodException e) {
+			// ignore
+		}
+	}
+
+	interface BalanceStrategy {
+		abstract Connection pickConnection() throws SQLException;
+	}
+
+	class BestResponseTimeBalanceStrategy implements BalanceStrategy {
+
+		public Connection pickConnection() throws SQLException {
+			long minResponseTime = Long.MAX_VALUE;
+
+			int bestHostIndex = 0;
+
+			long[] localResponseTimes = new long[responseTimes.length];
+			
+			synchronized (responseTimes) {
+				System.arraycopy(responseTimes, 0, localResponseTimes, 0, responseTimes.length);
+			}
+			
+			SQLException ex = null;
+			
+			for (int attempts = 0; attempts < 1200 /* 5 minutes */; attempts++) {
+				for (int i = 0; i < localResponseTimes.length; i++) {
+					long candidateResponseTime = localResponseTimes[i];
+	
+					if (candidateResponseTime < minResponseTime) {
+						if (candidateResponseTime == 0) {
+							bestHostIndex = i;
+	
+							break;
+						}
+	
+						bestHostIndex = i;
+						minResponseTime = candidateResponseTime;
+					}
+				}
+	
+				if (bestHostIndex == localResponseTimes.length - 1) {
+					// try again, assuming that the previous list was mostly
+					// correct as far as distribution of response times went
+					
+					synchronized (responseTimes) {
+						System.arraycopy(responseTimes, 0, localResponseTimes, 0, responseTimes.length);
+					}
+					
+					continue;
+				}
+				String bestHost = (String) hostList.get(bestHostIndex);
+	
+				Connection conn = (Connection) liveConnections.get(bestHost);
+	
+				if (conn == null) {
+					try {
+						conn = createConnectionForHost(bestHost);
+					} catch (SQLException sqlEx) {
+						ex = sqlEx;
+						
+						if (sqlEx instanceof CommunicationsException || "08S01".equals(sqlEx.getSQLState())) {
+							localResponseTimes[bestHostIndex] = Long.MAX_VALUE;
+							
+							try {
+								Thread.sleep(250);
+							} catch (InterruptedException e) {
+							}
+							
+							continue;
+						} else {
+							throw sqlEx;
+						}
+					}
+				}
+	
+				return conn;
+			}
+			
+			if (ex != null) {
+				throw ex;
+			}
+			
+			return null; // we won't get here, compiler can't tell
+		}
+	}
+
+	// Lifted from C/J 5.1's JDBC-2.0 connection pool classes, let's merge this
+	// if/when this gets into 5.1
+	protected class ConnectionErrorFiringInvocationHandler implements
+			InvocationHandler {
+		Object invokeOn = null;
+
+		public ConnectionErrorFiringInvocationHandler(Object toInvokeOn) {
+			invokeOn = toInvokeOn;
+		}
+
+		public Object invoke(Object proxy, Method method, Object[] args)
+				throws Throwable {
+			Object result = null;
+
+			try {
+				result = method.invoke(invokeOn, args);
+
+				if (result != null) {
+					result = proxyIfInterfaceIsJdbc(result, result.getClass());
+				}
+			} catch (InvocationTargetException e) {
+				dealWithInvocationException(e);
+			}
+
+			return result;
+		}
+	}
+
+	class RandomBalanceStrategy implements BalanceStrategy {
+
+		public Connection pickConnection() throws SQLException {
+			int random = (int) (Math.random() * hostList.size());
+
+			if (random == hostList.size()) {
+				random--;
+			}
+
+			String hostPortSpec = (String) hostList.get(random);
+
+			SQLException ex = null;
+			
+			for (int attempts = 0; attempts < 1200 /* 5 minutes */; attempts++) {
+				Connection conn = (Connection) liveConnections.get(hostPortSpec);
+				
+				if (conn == null) {
+					try {
+						conn = createConnectionForHost(hostPortSpec);
+					} catch (SQLException sqlEx) {
+						ex = sqlEx;
+						
+						if (sqlEx instanceof CommunicationsException || "08S01".equals(sqlEx.getSQLState())) {
+							
+							try {
+								Thread.sleep(250);
+							} catch (InterruptedException e) {
+							}
+							
+							continue;
+						} else {
+							throw sqlEx;
+						}
+					}
+				}
+	
+				return conn;
+			}
+			
+			if (ex != null) {
+				throw ex;
+			}
+			
+			return null; // we won't get here, compiler can't tell
+		}
+
+	}
+
+	private Connection currentConn;
+
+	private List hostList;
+
+	private Map liveConnections;
+
+	private Map connectionsToHostsMap;
+
+	private long[] responseTimes;
+
+	private Map hostsToListIndexMap;
+
+	boolean inTransaction = false;
+
+	long transactionStartTime = 0;
+
+	Properties localProps;
+
+	boolean isClosed = false;
+
+	BalanceStrategy balancer;
+
+	/**
+	 * Creates a proxy for java.sql.Connection that routes requests between the
+	 * given list of host:port and uses the given properties when creating
+	 * connections.
+	 * 
+	 * @param hosts
+	 * @param props
+	 * @throws SQLException
+	 */
+	LoadBalancingConnectionProxy(List hosts, Properties props)
+			throws SQLException {
+		this.hostList = hosts;
+
+		int numHosts = this.hostList.size();
+
+		this.liveConnections = new HashMap(numHosts);
+		this.connectionsToHostsMap = new HashMap(numHosts);
+		this.responseTimes = new long[numHosts];
+		this.hostsToListIndexMap = new HashMap(numHosts);
+
+		for (int i = 0; i < numHosts; i++) {
+			this.hostsToListIndexMap.put(this.hostList.get(i), new Integer(i));
+		}
+
+		this.localProps = (Properties) props.clone();
+		this.localProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
+		this.localProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);
+		this.localProps.setProperty("useLocalSessionState", "true");
+
+		String strategy = this.localProps.getProperty("loadBalanceStrategy",
+				"random");
+
+		if ("random".equals(strategy)) {
+			this.balancer = new RandomBalanceStrategy();
+		} else if ("bestResponseTime".equals(strategy)) {
+			this.balancer = new BestResponseTimeBalanceStrategy();
+		} else {
+			throw SQLError.createSQLException(Messages.getString(
+					"InvalidLoadBalanceStrategy", new Object[] { strategy }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		pickNewConnection();
+	}
+
+	/**
+	 * Creates a new physical connection for the given host:port and updates
+	 * required internal mappings and statistics for that connection.
+	 * 
+	 * @param hostPortSpec
+	 * @return
+	 * @throws SQLException
+	 */
+	private synchronized Connection createConnectionForHost(String hostPortSpec)
+			throws SQLException {
+		Properties connProps = (Properties) this.localProps.clone();
+
+		String[] hostPortPair = NonRegisteringDriver
+				.parseHostPortPair(hostPortSpec);
+
+		if (hostPortPair[1] == null) {
+			hostPortPair[1] = "3306";
+		}
+
+		connProps.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
+				hostPortSpec);
+		connProps.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY,
+				hostPortPair[1]);
+
+		Connection conn = ConnectionImpl.getInstance(hostPortSpec, Integer
+				.parseInt(hostPortPair[1]), connProps, connProps
+				.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY),
+				"jdbc:mysql://" + hostPortPair[0] + ":" + hostPortPair[1] + "/");
+
+		this.liveConnections.put(hostPortSpec, conn);
+		this.connectionsToHostsMap.put(conn, hostPortSpec);
+
+		return conn;
+	}
+
+	/**
+	 * @param e
+	 * @throws SQLException
+	 * @throws Throwable
+	 * @throws InvocationTargetException
+	 */
+	void dealWithInvocationException(InvocationTargetException e)
+			throws SQLException, Throwable, InvocationTargetException {
+		Throwable t = e.getTargetException();
+
+		if (t != null) {
+			if (t instanceof SQLException) {
+				String sqlState = ((SQLException) t).getSQLState();
+
+				if (sqlState != null) {
+					if (sqlState.startsWith("08")) {
+						// connection error, close up shop on current
+						// connection
+						invalidateCurrentConnection();
+					}
+				}
+			}
+
+			throw t;
+		}
+
+		throw e;
+	}
+
+	/**
+	 * Closes current connection and removes it from required mappings.
+	 * 
+	 * @throws SQLException
+	 */
+	synchronized void invalidateCurrentConnection() throws SQLException {
+		try {
+			if (!this.currentConn.isClosed()) {
+				this.currentConn.close();
+			}
+
+		} finally {
+			this.liveConnections.remove(this.connectionsToHostsMap
+					.get(this.currentConn));
+			this.connectionsToHostsMap.remove(this.currentConn);
+		}
+	}
+
+	/**
+	 * Proxies method invocation on the java.sql.Connection interface, trapping
+	 * "close", "isClosed" and "commit/rollback" (to switch connections for load
+	 * balancing).
+	 */
+	public Object invoke(Object proxy, Method method, Object[] args)
+			throws Throwable {
+
+		String methodName = method.getName();
+
+		if ("close".equals(methodName)) {
+			synchronized (this.liveConnections) {
+				// close all underlying connections
+				Iterator allConnections = this.liveConnections.values()
+						.iterator();
+
+				while (allConnections.hasNext()) {
+					((Connection) allConnections.next()).close();
+				}
+
+				this.liveConnections.clear();
+				this.connectionsToHostsMap.clear();
+			}
+
+			return null;
+		}
+
+		if ("isClosed".equals(methodName)) {
+			return Boolean.valueOf(this.isClosed);
+		}
+
+		if (this.isClosed) {
+			throw SQLError.createSQLException(
+					"No operations allowed after connection closed.",
+					SQLError.SQL_STATE_CONNECTION_NOT_OPEN);
+		}
+
+		if (!inTransaction) {
+			this.inTransaction = true;
+			this.transactionStartTime = getLocalTimeBestResolution();
+		}
+
+		Object result = null;
+
+		try {
+			result = method.invoke(this.currentConn, args);
+
+			if (result != null) {
+				if (result instanceof com.mysql.jdbc.Statement) {
+					((com.mysql.jdbc.Statement)result).setPingTarget(this);
+				}
+				
+				result = proxyIfInterfaceIsJdbc(result, result.getClass());
+			}
+		} catch (InvocationTargetException e) {
+			dealWithInvocationException(e);
+		} finally {
+			if ("commit".equals(methodName) || "rollback".equals(methodName)) {
+				this.inTransaction = false;
+
+				// Update stats
+				int hostIndex = ((Integer) this.hostsToListIndexMap
+						.get(this.connectionsToHostsMap.get(this.currentConn)))
+						.intValue();
+
+				synchronized (this.responseTimes) {
+					this.responseTimes[hostIndex] = getLocalTimeBestResolution()
+							- this.transactionStartTime;
+				}
+
+				pickNewConnection();
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * Picks the "best" connection to use for the next transaction based on the
+	 * BalanceStrategy in use.
+	 * 
+	 * @throws SQLException
+	 */
+	private synchronized void pickNewConnection() throws SQLException {
+		if (this.currentConn == null) {
+			this.currentConn = this.balancer.pickConnection();
+
+			return;
+		}
+
+		Connection newConn = this.balancer.pickConnection();
+
+		newConn.setTransactionIsolation(this.currentConn
+				.getTransactionIsolation());
+		newConn.setAutoCommit(this.currentConn.getAutoCommit());
+		this.currentConn = newConn;
+	}
+
+	/**
+	 * Recursively checks for interfaces on the given object to determine if it
+	 * implements a java.sql interface, and if so, proxies the instance so that
+	 * we can catch and fire SQL errors.
+	 * 
+	 * @param toProxy
+	 * @param clazz
+	 * @return
+	 */
+	Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) {
+		Class[] interfaces = clazz.getInterfaces();
+
+		for (int i = 0; i < interfaces.length; i++) {
+			String packageName = interfaces[i].getPackage().getName();
+
+			if ("java.sql".equals(packageName)
+					|| "javax.sql".equals(packageName)) {
+				return Proxy.newProxyInstance(toProxy.getClass()
+						.getClassLoader(), interfaces,
+						new ConnectionErrorFiringInvocationHandler(toProxy));
+			}
+
+			return proxyIfInterfaceIsJdbc(toProxy, interfaces[i]);
+		}
+
+		return toProxy;
+	}
+
+	/**
+	 * Returns best-resolution representation of local time, using nanoTime() if
+	 * availble, otherwise defaulting to currentTimeMillis().
+	 */
+	private static long getLocalTimeBestResolution() {
+		if (getLocalTimeMethod != null) {
+			try {
+				return ((Long) getLocalTimeMethod.invoke(null, null))
+						.longValue();
+			} catch (IllegalArgumentException e) {
+				// ignore - we fall through to currentTimeMillis()
+			} catch (IllegalAccessException e) {
+				// ignore - we fall through to currentTimeMillis()
+			} catch (InvocationTargetException e) {
+				// ignore - we fall through to currentTimeMillis()
+			}
+		}
+
+		return System.currentTimeMillis();
+	}
+
+	public synchronized void doPing() throws SQLException {
+		Iterator allConns = this.liveConnections.values().iterator();
+		
+		while (allConns.hasNext()) {
+			((Connection)allConns.next()).ping();
+		}
+	}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/LocalizedErrorMessages.properties
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2007-11-30 10:46:51 UTC (rev 4921)
@@ -5,7 +5,8 @@
 ResultSet.Retrieved__1=Retrieved 
 ResultSet.Bad_format_for_BigDecimal=Bad format for BigDecimal ''{0}'' in column {1}.
 ResultSet.Bad_format_for_BigInteger=Bad format for BigInteger ''{0}'' in column {1}.
-ResultSet.Column_Index_out_of_range=Column Index out of range, {0} > {1}. 
+ResultSet.Column_Index_out_of_range_low=Column Index out of range, {0} < 1.
+ResultSet.Column_Index_out_of_range_high=Column Index out of range, {0} > {1}. 
 ResultSet.Value_is_out_of_range=Value ''{0}'' is out of range [{1}, {2}].
 ResultSet.Positioned_Update_not_supported=Positioned Update not supported.
 ResultSet.Bad_format_for_Date=Bad format for DATE ''{0}'' in column {1}.
@@ -99,18 +100,17 @@
 ResultSet.Unsupported_character_encoding____135=Unsupported character encoding \'
 ResultSet.Unsupported_character_encoding____138=Unsupported character encoding \'
 
-ResultSet.ResultSet_implicitly_closed_by_driver._150=ResultSet implicitly closed by driver.
-ResultSet._n_nYou_should_close_ResultSets_explicitly_from_your_code_to_free_up_resources_in_a_more_efficient_manner._151=\n\nYou should close ResultSets explicitly from your code to free up resources in a more efficient manner.
+#
+# Usage advisor messages for ResultSets
+#
 
-ResultSet.Possible_incomplete_traversal_of_result_set._Cursor_was_left_on_row__154=Possible incomplete traversal of result set. Cursor was left on row 
-ResultSet._of__155=\ of 
-ResultSet._rows_when_it_was_closed._156=\ rows when it was closed.
-ResultSet._n_nYou_should_consider_re-formulating_your_query_to_return_only_the_rows_you_are_interested_in_using._157=\n\nYou should consider re-formulating your query to return only the rows you are interested in using.
+ResultSet.ResultSet_implicitly_closed_by_driver=ResultSet implicitly closed by driver.\n\nYou should close ResultSets explicitly from your code to free up resources in a more efficient manner.
+ResultSet.Possible_incomplete_traversal_of_result_set=Possible incomplete traversal of result set. Cursor was left on row {0} of {1} rows when it was closed.\n\nYou should consider re-formulating your query to return only the rows you are interested in using.
+ResultSet.The_following_columns_were_never_referenced=The following columns were part of the SELECT statement for this result set, but were never referenced: 
+ResultSet.Too_Large_Result_Set=Result set size of {0} rows is larger than \"resultSetSizeThreshold\" of {1} rows. Application may be requesting more data than it is using. Consider reformulating the query.
+ResultSet.CostlyConversion=ResultSet type conversion via parsing detected when calling {0} for column {1} (column named '{2}') in table '{3}'{4}\n\nJava class of column type is '{5}', MySQL field type is '{6}'.\n\nTypes that could be converted directly without parsing are:\n{7}
+ResultSet.CostlyConversionCreatedFromQuery= created from query:\n\n
 
-ResultSet.The_following_columns_were__160=The following columns were 
-ResultSet._part_of_the_SELECT_statement_for_this_result_set,_but_were_161=\ part of the SELECT statement for this result set, but were
-ResultSet._never_referenced___162=\ never referenced: 
-
 ResultSet.Value____173=Value \'
 ResultSetMetaData.46=Column index out of range.
 ResultSet.___is_out_of_range_[-127,127]_174=\' is out of range [-127,127]
@@ -173,8 +173,10 @@
 CommunicationsException.17=the operating system. This limit is usually configurable. \n\n
 CommunicationsException.18=For Unix-based platforms, see the manual page for the 'ulimit' command. Kernel or system reconfiguration may also be required.
 CommunicationsException.19=\n\nFor Windows-based platforms, see Microsoft Knowledge Base Article 196271 (Q196271).
+CommunicationsException.19a=The configuration parameter \"localSocketAddress\" has been set to a network interface not available for use by the JVM.
 CommunicationsException.20=Communications link failure
 CommunicationsException.21=\ due to underlying exception: 
+CommunicationsException.ClientWasStreaming=Application was streaming results when the connection failed. Consider raising value of 'net_write_timeout' on the server.
 NonRegisteringDriver.3=Hostname of MySQL Server
 NonRegisteringDriver.7=Port number of MySQL Server
 NonRegisteringDriver.13=Username to authenticate as
@@ -283,9 +285,9 @@
 MysqlIO.22=\ any active result sets before attempting more queries.
 MysqlIO.23=Can not use streaming results with multiple result statements
 MysqlIO.25=\ ... (truncated)
-MysqlIO.26=Slow query (exceeded 
-MysqlIO.26a=\ ms., duration:\ 
-MysqlIO.27=\ ms): 
+MysqlIO.SlowQuery=Slow query (exceeded {0} {1}, duration: {2} {1}): 
+Nanoseconds=ns
+Milliseconds=ms
 MysqlIO.28=Not issuing EXPLAIN for query of size > 
 MysqlIO.29=\ bytes.
 MysqlIO.33=The following query was executed with a bad index, use 'EXPLAIN' for more details: 
@@ -343,6 +345,10 @@
 MysqlIO.99=\ of 
 MysqlIO.100=\ in binary-encoded result set.
 MysqlIO.102=, underlying cause: 
+MysqlIO.EOF=Can not read response from server. Expected to read {0} bytes, read {1} bytes before connection was unexpectedly lost.
+MysqlIO.NoInnoDBStatusFound=No InnoDB status output returned by server.
+MysqlIO.InnoDBStatusFailed=Couldn't retrieve InnoDB status due to underlying exception: 
+MysqlIO.LoadDataLocalNotAllowed=Server asked for stream in response to LOAD DATA LOCAL INFILE but functionality is disabled at client by 'allowLoadLocalInfile' being set to 'false'.
 NotImplemented.0=Feature not implemented
 PreparedStatement.0=SQL String can not be NULL
 PreparedStatement.1=SQL String can not be NULL
@@ -399,6 +405,201 @@
 NotUpdatable.0=Result Set not updatable.
 NotUpdatable.1=This result set must come from a statement 
 NotUpdatable.2=that was created with a result set type of ResultSet.CONCUR_UPDATABLE, 
-NotUpdatable.3=the query must select only one table, and must 
+NotUpdatable.3=the query must select only one table, can not use functions and must 
 NotUpdatable.4=select all primary keys from that table. See the JDBC 2.1 API Specification, 
 NotUpdatable.5=section 5.6 for more details.
+NotUpdatableReason.0=Result Set not updatable (references more than one table).
+NotUpdatableReason.1=Result Set not updatable (references more than one database).
+NotUpdatableReason.2=Result Set not updatable (references no tables).
+NotUpdatableReason.3=Result Set not updatable (references computed values or doesn't reference any columns or tables).
+NotUpdatableReason.4=Result Set not updatable (references no primary keys).
+NotUpdatableReason.5=Result Set not updatable (referenced table has no primary keys).
+NotUpdatableReason.6=Result Set not updatable (references unknown primary key {0}).
+NotUpdatableReason.7=Result Set not updatable (does not reference all primary keys).
+
+JDBC4Connection.ClientInfoNotImplemented=Configured clientInfoProvider class '{0}' does not implement com.mysql.jdbc.JDBC4ClientInfoProvider.
+
+InvalidLoadBalanceStrategy=Invalid load balancing strategy '{0}'.
+Connection.Connection.BadValueInServerVariables=Invalid value '{1}' for server variable named '{0}', falling back to sane default of '{2}'.
+
+#
+# ConnectionProperty Categories
+#
+
+ConnectionProperties.categoryConnectionAuthentication=Connection/Authentication
+ConnectionProperties.categoryNetworking=Networking
+ConnectionProperties.categoryDebuggingProfiling=Debugging/Profiling
+ConnectionProperties.categorryHA=High Availability and Clustering
+ConnectionProperties.categoryMisc=Miscellaneous
+ConnectionProperties.categoryPerformance=Performance Extensions
+ConnectionProperties.categorySecurity=Security
+
+#
+# ConnectionProperty Descriptions
+#
+
+ConnectionProperties.loadDataLocal=Should the driver allow use of 'LOAD DATA LOCAL INFILE...' (defaults to 'true').
+ConnectionProperties.allowMultiQueries=Allow the use of ';' to delimit multiple queries during one statement (true/false), defaults to 'false'
+ConnectionProperties.allowNANandINF=Should the driver allow NaN or +/- INF values in PreparedStatement.setDouble()?
+ConnectionProperties.allowUrlInLoadLocal=Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements?
+ConnectionProperties.alwaysSendSetIsolation=Should the driver always communicate with the database when Connection.setTransactionIsolation() is called? If set to false, the driver will only communicate with the database when the requested transaction isolation is different than the whichever is newer, the last value that was set via Connection.setTransactionIsolation(), or the value that was read from the server when the connection was established.
+ConnectionProperties.autoClosePstmtStreams=Should the driver automatically call .close() on streams/readers passed as arguments via set*() methods?
+ConnectionProperties.autoDeserialize=Should the driver automatically detect and de-serialize objects stored in BLOB fields?
+ConnectionProperties.autoGenerateTestcaseScript=Should the driver dump the SQL it is executing, including server-side prepared statements to STDERR?
+ConnectionProperties.autoReconnect=Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. Alternatively, investigate setting the MySQL server variable "wait_timeout" to some high value rather than the default of 8 hours.
+ConnectionProperties.autoReconnectForPools=Use a reconnection strategy appropriate for connection pools (defaults to 'false')
+ConnectionProperties.autoSlowLog=Instead of using slowQueryThreshold* to determine if a query is slow enough to be logged, maintain statistics that allow the driver to determine queries that are outside the 99th percentile?
+ConnectionProperties.blobsAreStrings=Should the driver always treat BLOBs as Strings - specifically to work around dubious metadata returned by the server for GROUP BY clauses?
+ConnectionProperties.functionsNeverReturnBlobs=Should the driver always treat data from functions returning BLOBs as Strings - specifically to work around dubious metadata returned by the server for GROUP BY clauses?
+ConnectionProperties.blobSendChunkSize=Chunk to use when sending BLOB/CLOBs via ServerPreparedStatements
+ConnectionProperties.cacheCallableStatements=Should the driver cache the parsing stage of CallableStatements
+ConnectionProperties.cachePrepStmts=Should the driver cache the parsing stage of PreparedStatements of client-side prepared statements, the "check" for suitability of server-side prepared and server-side prepared statements themselves?
+ConnectionProperties.cacheRSMetadata=Should the driver cache ResultSetMetaData for Statements and PreparedStatements? (Req. JDK-1.4+, true/false, default 'false')
+ConnectionProperties.cacheServerConfiguration=Should the driver cache the results of 'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis?
+ConnectionProperties.callableStmtCacheSize=If 'cacheCallableStmts' is enabled, how many callable statements should be cached?
+ConnectionProperties.capitalizeTypeNames=Capitalize type names in DatabaseMetaData? (usually only useful when using WebObjects, true/false, defaults to 'false')
+ConnectionProperties.characterEncoding=If 'useUnicode' is set to true, what character encoding should the driver use when dealing with strings? (defaults is to 'autodetect')
+ConnectionProperties.characterSetResults=Character set to tell the server to return results as.
+ConnectionProperties.clientInfoProvider=The name of a class that implements the com.mysql.jdbc.JDBC4ClientInfoProvider interface in order to support JDBC-4.0's Connection.get/setClientInfo() methods
+ConnectionProperties.clobberStreamingResults=This will cause a 'streaming' ResultSet to be automatically closed, and any outstanding data still streaming from the server to be discarded if another query is executed before all the data has been read from the server.
+ConnectionProperties.clobCharacterEncoding=The character encoding to use for sending and retrieving TEXT, MEDIUMTEXT and LONGTEXT values instead of the configured connection characterEncoding
+ConnectionProperties.connectionCollation=If set, tells the server to use this collation via 'set collation_connection'
+ConnectionProperties.connectionLifecycleInterceptors=A comma-delimited list of classes that implement "com.mysql.jdbc.ConnectionLifecycleInterceptor" that should notified of connection lifecycle events (creation, destruction, commit, rollback, setCatalog and setAutoCommit) and potentially alter the execution of these commands. ConnectionLifecycleInterceptors are "stackable", more than one interceptor may be specified via the configuration property as a comma-delimited list, with the interceptors executed in order from left to right.
+ConnectionProperties.connectTimeout=Timeout for socket connect (in milliseconds), with 0 being no timeout. Only works on JDK-1.4 or newer. Defaults to '0'.
+ConnectionProperties.continueBatchOnError=Should the driver continue processing batch commands if one statement fails. The JDBC spec allows either way (defaults to 'true').
+ConnectionProperties.createDatabaseIfNotExist=Creates the database given in the URL if it doesn't yet exist. Assumes the configured user has permissions to create databases.
+ConnectionProperties.defaultFetchSize=The driver will call setFetchSize(n) with this value on all newly-created Statements
+ConnectionProperties.useServerPrepStmts=Use server-side prepared statements if the server supports them?
+ConnectionProperties.dontTrackOpenResources=The JDBC specification requires the driver to automatically track and close resources, however if your application doesn't do a good job of explicitly calling close() on statements or result sets, this can cause memory leakage. Setting this property to true relaxes this constraint, and can be more memory efficient for some applications.
+ConnectionProperties.dumpQueriesOnException=Should the driver dump the contents of the query sent to the server in the message for SQLExceptions?
+ConnectionProperties.dynamicCalendars=Should the driver retrieve the default calendar when required, or cache it per connection/session?
+ConnectionProperties.eliseSetAutoCommit=If using MySQL-4.1 or newer, should the driver only issue 'set autocommit=n' queries when the server's state doesn't match the requested state by Connection.setAutoCommit(boolean)?
+ConnectionProperties.emptyStringsConvertToZero=Should the driver allow conversions from empty string fields to numeric values of '0'?
+ConnectionProperties.emulateLocators=Should the driver emulate java.sql.Blobs with locators? With this feature enabled, the driver will delay loading the actual Blob data until the one of the retrieval methods (getInputStream(), getBytes(), and so forth) on the blob data stream has been accessed. For this to work, you must use a column alias with the value of the column to the actual name of the Blob. The feature also has the following restrictions: The SELECT that created the result set must reference only one table, the table must have a primary key; the SELECT must alias the original blob column name, specified as a string, to an alternate name; the SELECT must cover all columns that make up the primary key. 
+ConnectionProperties.emulateUnsupportedPstmts=Should the driver detect prepared statements that are not supported by the server, and replace them with client-side emulated versions?
+ConnectionProperties.enablePacketDebug=When enabled, a ring-buffer of 'packetDebugBufferSize' packets will be kept, and dumped when exceptions are thrown in key areas in the driver's code
+ConnectionProperties.enableQueryTimeouts=When enabled, query timeouts set via Statement.setQueryTimeout() use a shared java.util.Timer instance for scheduling. Even if the timeout doesn't expire before the query is processed, there will be memory used by the TimerTask for the given timeout which won't be reclaimed until the time the timeout would have expired if it hadn't been cancelled by the driver. High-load environments might want to consider disabling this functionality.
+ConnectionProperties.explainSlowQueries=If 'logSlowQueries' is enabled, should the driver automatically issue an 'EXPLAIN' on the server and send the results to the configured log at a WARN level?
+ConnectionProperties.failoverReadOnly=When failing over in autoReconnect mode, should the connection be set to 'read-only'?
+ConnectionProperties.gatherPerfMetrics=Should the driver gather performance metrics, and report them via the configured logger every 'reportMetricsIntervalMillis' milliseconds?
+ConnectionProperties.generateSimpleParameterMetadata=Should the driver generate simplified parameter metadata for PreparedStatements when no metadata is available either because the server couldn't support preparing the statement, or server-side prepared statements are disabled?
+ConnectionProperties.holdRSOpenOverStmtClose=Should the driver close result sets on Statement.close() as required by the JDBC specification?
+ConnectionProperties.ignoreNonTxTables=Ignore non-transactional table warning for rollback? (defaults to 'false').
+ConnectionProperties.initialTimeout=If autoReconnect is enabled, the initial time to wait between re-connect attempts (in seconds, defaults to '2').
+ConnectionProperties.interactiveClient=Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout connections based on INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT
+ConnectionProperties.jdbcCompliantTruncation=Should the driver throw java.sql.DataTruncation exceptions when data is truncated as is required by the JDBC specification when connected to a server that supports warnings (MySQL 4.1.0 and newer)? This property has no effect if the server sql-mode includes STRICT_TRANS_TABLES.
+ConnectionProperties.largeRowSizeThreshold=What size result set row should the JDBC driver consider "large", and thus use a more memory-efficient way of representing the row internally?
+ConnectionProperties.loadBalanceStrategy=If using a load-balanced connection to connect to SQL nodes in a MySQL Cluster/NDB configuration (by using the URL prefix "jdbc:mysql:loadbalance://"), which load balancing algorithm should the driver use: (1) "random" - the driver will pick a random host for each request. This tends to work better than round-robin, as the randomness will somewhat account for spreading loads where requests vary in response time, while round-robin can sometimes lead to overloaded nodes if there are variations in response times across the workload. (2) "bestResponseTime" - the driver will route the request to the host that had the best response time for the previous transaction.
+ConnectionProperties.localSocketAddress=Hostname or IP address given to explicitly configure the interface that the driver will bind the client side of the TCP/IP connection to when connecting.
+ConnectionProperties.locatorFetchBufferSize=If 'emulateLocators' is configured to 'true', what size buffer should be used when fetching BLOB data for getBinaryInputStream?
+ConnectionProperties.logger=The name of a class that implements \"{0}\"  that will be used to log messages to. (default is \"{1}\", which logs to STDERR)
+ConnectionProperties.logSlowQueries=Should queries that take longer than 'slowQueryThresholdMillis' be logged?
+ConnectionProperties.logXaCommands=Should the driver log XA commands sent by MysqlXaConnection to the server, at the DEBUG level of logging?
+ConnectionProperties.maintainTimeStats=Should the driver maintain various internal timers to enable idle time calculations as well as more verbose error messages when the connection to the server fails? Setting this property to false removes at least two calls to System.getCurrentTimeMillis() per query.
+ConnectionProperties.maxQuerySizeToLog=Controls the maximum length/size of a query that will get logged when profiling or tracing
+ConnectionProperties.maxReconnects=Maximum number of reconnects to attempt if autoReconnect is true, default is '3'.
+ConnectionProperties.maxRows=The maximum number of rows to return (0, the default means return all rows).
+ConnectionProperties.allVersions=all versions
+ConnectionProperties.metadataCacheSize=The number of queries to cache ResultSetMetadata for if cacheResultSetMetaData is set to 'true' (default 50)
+ConnectionProperties.netTimeoutForStreamingResults=What value should the driver automatically set the server setting 'net_write_timeout' to when the streaming result sets feature is in use? (value has unit of seconds, the value '0' means the driver will not try and adjust this value)
+ConnectionProperties.noAccessToProcedureBodies=When determining procedure parameter types for CallableStatements, and the connected user can't access procedure bodies through "SHOW CREATE PROCEDURE" or select on mysql.proc should the driver instead create basic metadata (all parameters reported as INOUT VARCHARs) instead of throwing an exception?
+ConnectionProperties.noDatetimeStringSync=Don't ensure that ResultSet.getDatetimeType().toString().equals(ResultSet.getString())
+ConnectionProperties.noTzConversionForTimeType=Don't convert TIME values using the server timezone if 'useTimezone'='true'
+ConnectionProperties.nullCatalogMeansCurrent=When DatabaseMetadataMethods ask for a 'catalog' parameter, does the value null mean use the current catalog? (this is not JDBC-compliant, but follows legacy behavior from earlier versions of the driver)
+ConnectionProperties.nullNamePatternMatchesAll=Should DatabaseMetaData methods that accept *pattern parameters treat null the same as '%' (this is not JDBC-compliant, however older versions of the driver accepted this departure from the specification)
+ConnectionProperties.packetDebugBufferSize=The maximum number of packets to retain when 'enablePacketDebug' is true
+ConnectionProperties.padCharsWithSpace=If a result set column has the CHAR type and the value does not fill the amount of characters specified in the DDL for the column, should the driver pad the remaining characters with space (for ANSI compliance)?
+ConnectionProperties.paranoid=Take measures to prevent exposure sensitive information in error messages and clear data structures holding sensitive data when possible? (defaults to 'false')
+ConnectionProperties.pedantic=Follow the JDBC spec to the letter.
+ConnectionProperties.pinGlobalTxToPhysicalConnection=When using XAConnections, should the driver ensure that operations on a given XID are always routed to the same physical connection? This allows the XAConnection to support "XA START ... JOIN" after "XA END" has been called
+ConnectionProperties.populateInsertRowWithDefaultValues=When using ResultSets that are CONCUR_UPDATABLE, should the driver pre-populate the "insert" row with default values from the DDL for the table used in the query so those values are immediately available for ResultSet accessors? This functionality requires a call to the database for metadata each time a result set of this type is created. If disabled (the default), the default values will be populated by the an internal call to refreshRow() which pulls back default values and/or values changed by triggers.
+ConnectionProperties.prepStmtCacheSize=If prepared statement caching is enabled, how many prepared statements should be cached?
+ConnectionProperties.prepStmtCacheSqlLimit=If prepared statement caching is enabled, what's the largest SQL the driver will cache the parsing for?
+ConnectionProperties.processEscapeCodesForPrepStmts=Should the driver process escape codes in queries that are prepared?
+ConnectionProperties.profileSqlDeprecated=Deprecated, use 'profileSQL' instead. Trace queries and their execution/fetch times on STDERR (true/false) defaults to 'false'
+ConnectionProperties.profileSQL=Trace queries and their execution/fetch times to the configured logger (true/false) defaults to 'false'
+ConnectionProperties.connectionPropertiesTransform=An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that the driver will use to modify URL properties passed to the driver before attempting a connection
+ConnectionProperties.queriesBeforeRetryMaster=Number of queries to issue before falling back to master when failed over (when using multi-host failover). Whichever condition is met first, 'queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an attempt to be made to reconnect to the master. Defaults to 50.
+ConnectionProperties.reconnectAtTxEnd=If autoReconnect is set to true, should the driver attempt reconnections at the end of every transaction?
+ConnectionProperties.relaxAutoCommit=If the version of MySQL the driver connects to does not support transactions, still allow calls to commit(), rollback() and setAutoCommit() (true/false, defaults to 'false')?
+ConnectionProperties.reportMetricsIntervalMillis=If 'gatherPerfMetrics' is enabled, how often should they be logged (in ms)?
+ConnectionProperties.requireSSL=Require SSL connection if useSSL=true? (defaults to 'false').
+ConnectionProperties.resourceId=A globally unique name that identifies the resource that this datasource or connection is connected to, used for XAResource.isSameRM() when the driver can't determine this value based on hostnames used in the URL
+ConnectionProperties.resultSetSizeThreshold=If the usage advisor is enabled, how many rows should a result set contain before the driver warns that it is suspiciously large?
+ConnectionProperties.retainStatementAfterResultSetClose=Should the driver retain the Statement reference in a ResultSet after ResultSet.close() has been called. This is not JDBC-compliant after JDBC-4.0.
+ConnectionProperties.rewriteBatchedStatements=Should the driver use multiqueries (irregardless of the setting of "allowMultiQueries") as well as rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() is called? Notice that this has the potential for SQL injection if using plain java.sql.Statements and your code doesn't sanitize input correctly. Notice that for prepared statements, server-side prepared statements can not currently take advantage of this rewrite option, and that if you don't specify stream lengths when using PreparedStatement.set*Stream(), the driver won't be able to determine the optimum number of parameters per batch and you might receive an error from the driver that the resultant packet is too large. Statement.getGeneratedKeys() for these rewritten statements only works when the entire batch includes INSERT statements.
+ConnectionProperties.rollbackOnPooledClose=Should the driver issue a rollback() when the logical connection in a pool is closed?
+ConnectionProperties.roundRobinLoadBalance=When autoReconnect is enabled, and failoverReadonly is false, should we pick hosts to connect to on a round-robin basis?
+ConnectionProperties.runningCTS13=Enables workarounds for bugs in Sun's JDBC compliance testsuite version 1.3
+ConnectionProperties.secondsBeforeRetryMaster=How long should the driver wait, when failed over, before attempting 
+ConnectionProperties.secondsBeforeRetryMaster.1=to reconnect to the master server? Whichever condition is met first, 
+ConnectionProperties.secondsBeforeRetryMaster.2='queriesBeforeRetryMaster' or 'secondsBeforeRetryMaster' will cause an 
+ConnectionProperties.secondsBeforeRetryMaster.3=attempt to be made to reconnect to the master. Time in seconds, defaults to 30
+ConnectionProperties.serverTimezone=Override detection/mapping of timezone. Used when timezone from server doesn't map to Java timezone
+ConnectionProperties.sessionVariables=A comma-separated list of name/value pairs to be sent as SET SESSION ... to the server when the driver connects.
+ConnectionProperties.slowQueryThresholdMillis=If 'logSlowQueries' is enabled, how long should a query (in ms) before it is logged as 'slow'?
+ConnectionProperties.slowQueryThresholdNanos=If 'useNanosForElapsedTime' is set to true, and this property is set to a non-zero value, the driver will use this threshold (in nanosecond units) to determine if a query was slow.
+ConnectionProperties.socketFactory=The name of the class that the driver should use for creating socket connections to the server. This class must implement the interface 'com.mysql.jdbc.SocketFactory' and have public no-args constructor.
+ConnectionProperties.socketTimeout=Timeout on network socket operations (0, the default means no timeout).
+ConnectionProperties.statementInterceptors=A comma-delimited list of classes that implement "com.mysql.jdbc.StatementInterceptor" that should be placed "in between" query execution to influence the results. StatementInterceptors are "chainable", the results returned by the "current" interceptor will be passed on to the next in in the chain, from left-to-right order, as specified in this property. 
+ConnectionProperties.strictFloatingPoint=Used only in older versions of compliance test
+ConnectionProperties.strictUpdates=Should the driver do strict checking (all primary keys selected) of updatable result sets (true, false, defaults to 'true')?
+ConnectionProperties.overrideSupportsIEF=Should the driver return "true" for DatabaseMetaData.supportsIntegrityEnhancementFacility() even if the database doesn't support it to workaround applications that require this method to return "true" to signal support of foreign keys, even though the SQL specification states that this facility contains much more than just foreign key support (one such application being OpenOffice)?
+ConnectionProperties.tcpNoDelay=If connecting using TCP/IP, should the driver set SO_TCP_NODELAY (disabling the Nagle Algorithm)?
+ConnectionProperties.tcpKeepAlive=If connecting using TCP/IP, should the driver set SO_KEEPALIVE?
+ConnectionProperties.tcpSoRcvBuf=If connecting using TCP/IP, should the driver set SO_RCV_BUF to the given value? The default value of '0', means use the platform default value for this property)
+ConnectionProperties.tcpSoSndBuf=If connecting using TCP/IP, shuold the driver set SO_SND_BUF to the given value? The default value of '0', means use the platform default value for this property)
+ConnectionProperties.tcpTrafficClass=If connecting using TCP/IP, should the driver set traffic class or type-of-service fields ?See the documentation for java.net.Socket.setTrafficClass() for more information.
+ConnectionProperties.tinyInt1isBit=Should the driver treat the datatype TINYINT(1) as the BIT type (because the server silently converts BIT -> TINYINT(1) when creating tables)?
+ConnectionProperties.traceProtocol=Should trace-level network protocol be logged?
+ConnectionProperties.treatUtilDateAsTimestamp=Should the driver treat java.util.Date as a TIMESTAMP for the purposes of PreparedStatement.setObject()?
+ConnectionProperties.transformedBitIsBoolean=If the driver converts TINYINT(1) to a different type, should it use BOOLEAN instead of BIT for future compatibility with MySQL-5.0, as MySQL-5.0 has a BIT type?
+ConnectionProperties.useCompression=Use zlib compression when communicating with the server (true/false)? Defaults to 'false'.
+ConnectionProperties.useConfigs=Load the comma-delimited list of configuration properties before parsing the URL or applying user-specified properties. These configurations are explained in the 'Configurations' of the documentation.
+ConnectionProperties.useCursorFetch=If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a statement, should that statement use cursor-based fetching to retrieve rows?
+ConnectionProperties.useDynamicCharsetInfo=Should the driver use a per-connection cache of character set information queried from the server when necessary, or use a built-in static mapping that is more efficient, but isn't aware of custom character sets or character sets implemented after the release of the JDBC driver?
+ConnectionProperties.useFastIntParsing=Use internal String->Integer conversion routines to avoid excessive object creation?
+ConnectionProperties.useFastDateParsing=Use internal String->Date/Time/Timestamp conversion routines to avoid excessive object creation?
+ConnectionProperties.useHostsInPrivileges=Add '@hostname' to users in DatabaseMetaData.getColumn/TablePrivileges() (true/false), defaults to 'true'.
+ConnectionProperties.useInformationSchema=When connected to MySQL-5.0.7 or newer, should the driver use the INFORMATION_SCHEMA to derive information used by DatabaseMetaData?
+ConnectionProperties.useJDBCCompliantTimezoneShift=Should the driver use JDBC-compliant rules when converting TIME/TIMESTAMP/DATETIME values' timezone information for those JDBC arguments which take a java.util.Calendar argument? (Notice that this option is exclusive of the "useTimezone=true" configuration option.)
+ConnectionProperties.useLocalSessionState=Should the driver refer to the internal values of autocommit and transaction isolation that are set by Connection.setAutoCommit() and Connection.setTransactionIsolation() and transaction state as maintained by the protocol, rather than querying the database or blindly sending commands to the database for commit() or rollback() method calls?
+ConnectionProperties.useNanosForElapsedTime=For profiling/debugging functionality that measures elapsed time, should the driver try to use nanoseconds resolution if available (JDK >= 1.5)?
+ConnectionProperties.useOldAliasMetadataBehavior=Should the driver use the legacy behavior for "AS" clauses on columns and tables, and only return aliases (if any) for ResultSetMetaData.getColumnName() or ResultSetMetaData.getTableName() rather than the original column/table name?
+ConnectionProperties.useOldUtf8Behavior=Use the UTF-8 behavior the driver did when communicating with 4.0 and older servers
+ConnectionProperties.useOnlyServerErrorMessages=Don't prepend 'standard' SQLState error messages to error messages returned by the server.
+ConnectionProperties.useReadAheadInput=Use newer, optimized non-blocking, buffered input stream when reading from the server?
+ConnectionProperties.useSqlStateCodes=Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes (true/false), default is 'true'
+ConnectionProperties.useSSL=Use SSL when communicating with the server (true/false), defaults to 'false'
+ConnectionProperties.useSSPSCompatibleTimezoneShift=If migrating from an environment that was using server-side prepared statements, and the configuration property "useJDBCCompliantTimeZoneShift" set to "true", use compatible behavior when not using server-side prepared statements when sending TIMESTAMP values to the MySQL server.
+ConnectionProperties.useStreamLengthsInPrepStmts=Honor stream length parameter in PreparedStatement/ResultSet.setXXXStream() method calls (true/false, defaults to 'true')?
+ConnectionProperties.useTimezone=Convert time/date types between client and server timezones (true/false, defaults to 'false')?
+ConnectionProperties.ultraDevHack=Create PreparedStatements for prepareCall() when required, because UltraDev is broken and issues a prepareCall() for _all_ statements? (true/false, defaults to 'false')
+ConnectionProperties.useUnbufferedInput=Don't use BufferedInputStream for reading data from the server
+ConnectionProperties.useUnicode=Should the driver use Unicode character encodings when handling strings? Should only be used when the driver can't determine the character set mapping, or you are trying to 'force' the driver to use a character set that MySQL either doesn't natively support (such as UTF-8), true/false, defaults to 'true'
+ConnectionProperties.useUsageAdvisor=Should the driver issue 'usage' warnings advising proper and efficient usage of JDBC and MySQL Connector/J to the log (true/false, defaults to 'false')?
+ConnectionProperties.yearIsDateType=Should the JDBC driver treat the MySQL type "YEAR" as a java.sql.Date, or as a SHORT?
+ConnectionProperties.zeroDateTimeBehavior=What should happen when the driver encounters DATETIME values that are composed entirely of zeroes (used by MySQL to represent invalid dates)? Valid values are \"{0}\", \"{1}\" and \"{2}\".
+ConnectionProperties.useJvmCharsetConverters=Always use the character encoding routines built into the JVM, rather than using lookup tables for single-byte character sets?
+ConnectionProperties.useGmtMillisForDatetimes=Convert between session timezone and GMT before creating Date and Timestamp instances (value of "false" is legacy behavior, "true" leads to more JDBC-compliant behavior.
+ConnectionProperties.dumpMetadataOnColumnNotFound=Should the driver dump the field-level metadata of a result set into the exception message when ResultSet.findColumn() fails?
+ConnectionProperties.clientCertificateKeyStoreUrl=URL to the client certificate KeyStore (if not specified, use defaults)
+ConnectionProperties.trustCertificateKeyStoreUrl=URL to the trusted root certificate KeyStore (if not specified, use defaults)
+ConnectionProperties.clientCertificateKeyStoreType=KeyStore type for client certificates (NULL or empty means use default, standard keystore types supported by the JVM are "JKS" and "PKCS12", your environment may have more available depending on what security products are installed and available to the JVM.
+ConnectionProperties.clientCertificateKeyStorePassword=Password for the client certificates KeyStore
+ConnectionProperties.trustCertificateKeyStoreType=KeyStore type for trusted root certificates (NULL or empty means use default, standard keystore types supported by the JVM are "JKS" and "PKCS12", your environment may have more available depending on what security products are installed and available to the JVM.
+ConnectionProperties.trustCertificateKeyStorePassword=Password for the trusted root certificates KeyStore
+ConnectionProperties.Username=The user to connect as
+ConnectionProperties.Password=The password to use when connecting
+ConnectionProperties.useBlobToStoreUTF8OutsideBMP=Tells the driver to treat [MEDIUM/LONG]BLOB columns as [LONG]VARCHAR columns holding text encoded in UTF-8 that has characters outside the BMP (4-byte encodings), which MySQL server can't handle natively.
+ConnectionProperties.utf8OutsideBmpExcludedColumnNamePattern=When "useBlobToStoreUTF8OutsideBMP" is set to "true", column names matching the given regex will still be treated as BLOBs unless they match the regex specified for "utf8OutsideBmpIncludedColumnNamePattern". The regex must follow the patterns used for the java.util.regex package.
+ConnectionProperties.utf8OutsideBmpIncludedColumnNamePattern=Used to specify exclusion rules to "utf8OutsideBmpExcludedColumnNamePattern". The regex must follow the patterns used for the java.util.regex package.
+
+# 
+# Error Messages for Connection Properties
+#
+
+ConnectionProperties.unableToInitDriverProperties=Unable to initialize driver properties due to 
+ConnectionProperties.unsupportedCharacterEncoding=Unsupported character encoding '${0}'.
+ConnectionProperties.errorNotExpected=Huh?
+ConnectionProperties.InternalPropertiesFailure=Internal properties failure

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlDefs.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlDefs.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlDefs.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -25,7 +25,6 @@
 package com.mysql.jdbc;
 
 import java.sql.Types;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -35,9 +34,9 @@
  * MySQL server.
  * 
  * @author Mark Matthews
- * @version $Id: MysqlDefs.java 4724 2005-12-20 23:27:01Z mmatthews $
+ * @version $Id: MysqlDefs.java 6376 2007-04-05 21:56:05Z mmatthews $
  */
-final class MysqlDefs {
+public final class MysqlDefs {
 	// ~ Static fields/initializers
 	// ---------------------------------------------
 
@@ -81,7 +80,7 @@
 
 	static final int FIELD_TYPE_BIT = 16;
 
-	static final int FIELD_TYPE_BLOB = 252;
+	public static final int FIELD_TYPE_BLOB = 252;
 
 	static final int FIELD_TYPE_DATE = 10;
 
@@ -488,67 +487,67 @@
 	private static Map mysqlToJdbcTypesMap = new HashMap();
 
 	static {
-		mysqlToJdbcTypesMap.put("BIT", new Integer(
+		mysqlToJdbcTypesMap.put("BIT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_BIT)));
 
-		mysqlToJdbcTypesMap.put("TINYINT", new Integer(
+		mysqlToJdbcTypesMap.put("TINYINT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_TINY)));
-		mysqlToJdbcTypesMap.put("SMALLINT", new Integer(
+		mysqlToJdbcTypesMap.put("SMALLINT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_SHORT)));
-		mysqlToJdbcTypesMap.put("MEDIUMINT", new Integer(
+		mysqlToJdbcTypesMap.put("MEDIUMINT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_INT24)));
-		mysqlToJdbcTypesMap.put("INT", new Integer(
+		mysqlToJdbcTypesMap.put("INT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_LONG)));
-		mysqlToJdbcTypesMap.put("INTEGER", new Integer(
+		mysqlToJdbcTypesMap.put("INTEGER", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_LONG)));
-		mysqlToJdbcTypesMap.put("BIGINT", new Integer(
+		mysqlToJdbcTypesMap.put("BIGINT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_LONGLONG)));
-		mysqlToJdbcTypesMap.put("INT24", new Integer(
+		mysqlToJdbcTypesMap.put("INT24", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_INT24)));
-		mysqlToJdbcTypesMap.put("REAL", new Integer(
+		mysqlToJdbcTypesMap.put("REAL", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_DOUBLE)));
-		mysqlToJdbcTypesMap.put("FLOAT", new Integer(
+		mysqlToJdbcTypesMap.put("FLOAT", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_FLOAT)));
-		mysqlToJdbcTypesMap.put("DECIMAL", new Integer(
+		mysqlToJdbcTypesMap.put("DECIMAL", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_DECIMAL)));
-		mysqlToJdbcTypesMap.put("NUMERIC", new Integer(
+		mysqlToJdbcTypesMap.put("NUMERIC", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_DECIMAL)));
-		mysqlToJdbcTypesMap.put("DOUBLE", new Integer(
+		mysqlToJdbcTypesMap.put("DOUBLE", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_DOUBLE)));
-		mysqlToJdbcTypesMap.put("CHAR", new Integer(
+		mysqlToJdbcTypesMap.put("CHAR", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_STRING)));
-		mysqlToJdbcTypesMap.put("VARCHAR", new Integer(
+		mysqlToJdbcTypesMap.put("VARCHAR", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_VAR_STRING)));
-		mysqlToJdbcTypesMap.put("DATE", new Integer(
+		mysqlToJdbcTypesMap.put("DATE", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_DATE)));
-		mysqlToJdbcTypesMap.put("TIME", new Integer(
+		mysqlToJdbcTypesMap.put("TIME", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_TIME)));
-		mysqlToJdbcTypesMap.put("YEAR", new Integer(
+		mysqlToJdbcTypesMap.put("YEAR", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_YEAR)));
-		mysqlToJdbcTypesMap.put("TIMESTAMP", new Integer(
+		mysqlToJdbcTypesMap.put("TIMESTAMP", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_TIMESTAMP)));
-		mysqlToJdbcTypesMap.put("DATETIME", new Integer(
+		mysqlToJdbcTypesMap.put("DATETIME", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_DATETIME)));
-		mysqlToJdbcTypesMap.put("TINYBLOB", new Integer(java.sql.Types.BINARY));
-		mysqlToJdbcTypesMap.put("BLOB", new Integer(
+		mysqlToJdbcTypesMap.put("TINYBLOB", Constants.integerValueOf(java.sql.Types.BINARY));
+		mysqlToJdbcTypesMap.put("BLOB", Constants.integerValueOf(
 				java.sql.Types.LONGVARBINARY));
-		mysqlToJdbcTypesMap.put("MEDIUMBLOB", new Integer(
+		mysqlToJdbcTypesMap.put("MEDIUMBLOB", Constants.integerValueOf(
 				java.sql.Types.LONGVARBINARY));
-		mysqlToJdbcTypesMap.put("LONGBLOB", new Integer(
+		mysqlToJdbcTypesMap.put("LONGBLOB", Constants.integerValueOf(
 				java.sql.Types.LONGVARBINARY));
 		mysqlToJdbcTypesMap
-				.put("TINYTEXT", new Integer(java.sql.Types.VARCHAR));
+				.put("TINYTEXT", Constants.integerValueOf(java.sql.Types.VARCHAR));
 		mysqlToJdbcTypesMap
-				.put("TEXT", new Integer(java.sql.Types.LONGVARCHAR));
-		mysqlToJdbcTypesMap.put("MEDIUMTEXT", new Integer(
+				.put("TEXT", Constants.integerValueOf(java.sql.Types.LONGVARCHAR));
+		mysqlToJdbcTypesMap.put("MEDIUMTEXT", Constants.integerValueOf(
 				java.sql.Types.LONGVARCHAR));
-		mysqlToJdbcTypesMap.put("LONGTEXT", new Integer(
+		mysqlToJdbcTypesMap.put("LONGTEXT", Constants.integerValueOf(
 				java.sql.Types.LONGVARCHAR));
-		mysqlToJdbcTypesMap.put("ENUM", new Integer(
+		mysqlToJdbcTypesMap.put("ENUM", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_ENUM)));
-		mysqlToJdbcTypesMap.put("SET", new Integer(
+		mysqlToJdbcTypesMap.put("SET", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_SET)));
-		mysqlToJdbcTypesMap.put("GEOMETRY", new Integer(
+		mysqlToJdbcTypesMap.put("GEOMETRY", Constants.integerValueOf(
 				mysqlToJavaType(FIELD_TYPE_GEOMETRY)));
 	}
 
@@ -557,8 +556,8 @@
 		buf.append("CASE ");
 		Map typesMap = new HashMap();
 		typesMap.putAll(mysqlToJdbcTypesMap);
-		typesMap.put("BINARY", new Integer(Types.BINARY));
-		typesMap.put("VARBINARY", new Integer(Types.VARBINARY));
+		typesMap.put("BINARY", Constants.integerValueOf(Types.BINARY));
+		typesMap.put("VARBINARY", Constants.integerValueOf(Types.VARBINARY));
 		
 		Iterator mysqlTypes = typesMap.keySet().iterator();
 		

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlIO.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlIO.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlIO.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
-      Copyright (C) 2002-2004 MySQL AB
+      Copyright (C) 2002-2007 MySQL AB
 
       This program is free software; you can redistribute it and/or modify
       it under the terms of version 2 of the GNU General Public License as
@@ -24,11 +24,6 @@
  */
 package com.mysql.jdbc;
 
-import com.mysql.jdbc.profiler.ProfileEventSink;
-import com.mysql.jdbc.profiler.ProfilerEvent;
-import com.mysql.jdbc.util.ReadAheadInputStream;
-import com.mysql.jdbc.util.ResultSetUtil;
-
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
@@ -37,23 +32,15 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStreamWriter;
-
 import java.lang.ref.SoftReference;
-
 import java.math.BigInteger;
-
-import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
 import java.net.Socket;
 import java.net.URL;
-
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-
 import java.security.NoSuchAlgorithmException;
-
+import java.sql.ResultSet;
 import java.sql.SQLException;
-
+import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Iterator;
@@ -62,20 +49,28 @@
 import java.util.Properties;
 import java.util.zip.Deflater;
 
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+import com.mysql.jdbc.util.ReadAheadInputStream;
+import com.mysql.jdbc.util.ResultSetUtil;
 
+
 /**
  * This class is used by Connection for communicating with the MySQL server.
  *
  * @author Mark Matthews
- * @version $Id: MysqlIO.java 5417 2006-06-20 21:33:56Z mmatthews $
+ * @version $Id: MysqlIO.java 6613 2007-10-04 16:56:05Z mmatthews $
  *
  * @see java.sql.Connection
  */
 class MysqlIO {
-    protected static final int NULL_LENGTH = ~0;
+    private static final int UTF8_CHARSET_INDEX = 33;
+    private static final String CODE_PAGE_1252 = "Cp1252";
+	protected static final int NULL_LENGTH = ~0;
     protected static final int COMP_HEADER_LENGTH = 3;
     protected static final int MIN_COMPRESS_LEN = 50;
     protected static final int HEADER_LENGTH = 4;
+    protected static final int AUTH_411_OVERHEAD = 33;
     private static int maxBufferSize = 65535;
     private static final int CLIENT_COMPRESS = 32; /* Can use compression
     protcol */
@@ -106,17 +101,12 @@
     private static final String FALSE_SCRAMBLE = "xxxxxxxx"; //$NON-NLS-1$
     protected static final int MAX_QUERY_SIZE_TO_LOG = 1024; // truncate logging of queries at 1K
     protected static final int MAX_QUERY_SIZE_TO_EXPLAIN = 1024 * 1024; // don't explain queries above 1MB
-
+    protected static final int INITIAL_PACKET_SIZE = 1024;
     /**
      * We store the platform 'encoding' here, only used to avoid munging
      * filenames for LOAD DATA LOCAL INFILE...
      */
     private static String jvmPlatformCharset = null;
-    
-    /**
-     * Are we using packed or unpacked binary result set rows?
-     */
-    private boolean binaryResultsAreUnpacked = true;
 
     /**
      * We need to have a 'marker' for all-zero datetimes so that ResultSet
@@ -163,7 +153,7 @@
 
     /** Data to the server */
     protected BufferedOutputStream mysqlOutput = null;
-    protected com.mysql.jdbc.Connection connection;
+    protected ConnectionImpl connection;
     private Deflater deflater = null;
     protected InputStream mysqlInput = null;
     private LinkedList packetDebugRingBuffer = null;
@@ -218,7 +208,7 @@
     private byte packetSequence = 0;
     private byte readPacketSequence = -1;
     private boolean checkPacketSequence = false;
-    byte protocolVersion = 0;
+    private byte protocolVersion = 0;
     private int maxAllowedPacket = 1024 * 1024;
     protected int maxThreeBytes = 255 * 255 * 255;
     protected int port = 3306;
@@ -232,12 +222,17 @@
     protected long lastPacketSentTimeMs = 0;
     private boolean traceProtocol = false;
     private boolean enablePacketDebug = false;
-    private ByteBuffer channelClearBuf;
     private Calendar sessionCalendar;
 	private boolean useConnectWithDb;
 	private boolean needToGrabQueryFromPacket;
 	private boolean autoGenerateTestcaseScript;
 	private long threadId;
+	private boolean useNanosForElapsedTime;
+	private long slowQueryThreshold;
+	private String queryTimingUnits;
+	private List statementInterceptors;
+	private boolean useDirectRowUnpack = true;
+	private int useBufferRowSizeThreshold;
 
     /**
      * Constructor:  Connect to the MySQL server and setup a stream connection.
@@ -254,69 +249,93 @@
      * @throws SQLException if a database access error occurs.
      */
     public MysqlIO(String host, int port, Properties props,
-        String socketFactoryClassName, com.mysql.jdbc.Connection conn,
-        int socketTimeout) throws IOException, SQLException {
+        String socketFactoryClassName, ConnectionImpl conn,
+        int socketTimeout, int useBufferRowSizeThreshold) throws IOException, SQLException {
         this.connection = conn;
 
         if (this.connection.getEnablePacketDebug()) {
             this.packetDebugRingBuffer = new LinkedList();
         }
 
+        this.useAutoSlowLog = this.connection.getAutoSlowLog();
+        
+        this.useBufferRowSizeThreshold = useBufferRowSizeThreshold;
+        this.useDirectRowUnpack = this.connection.getUseDirectRowUnpack();
+
         this.logSlowQueries = this.connection.getLogSlowQueries();
 
-        this.reusablePacket = new Buffer(
-        		this.connection.getNetBufferLength());
+        this.reusablePacket = new Buffer(INITIAL_PACKET_SIZE);
+        this.sendPacket = new Buffer(INITIAL_PACKET_SIZE);
 
         this.port = port;
         this.host = host;
 
         this.socketFactoryClassName = socketFactoryClassName;
         this.socketFactory = createSocketFactory();
-        
+
         this.mysqlConnection = this.socketFactory.connect(this.host,
         		this.port, props);
-        
+
         if (socketTimeout != 0) {
         	try {
         		this.mysqlConnection.setSoTimeout(socketTimeout);
         	} catch (Exception ex) {
         		/* Ignore if the platform does not support it */
-        		;
         	}
         }
-        
+
         this.mysqlConnection = this.socketFactory.beforeHandshake();
-        
+
         if (this.connection.getUseReadAheadInput()) {
-        	this.mysqlInput = new ReadAheadInputStream(this.mysqlConnection.getInputStream(), 16384, 
+        	this.mysqlInput = new ReadAheadInputStream(this.mysqlConnection.getInputStream(), 16384,
         			this.connection.getTraceProtocol(),
-        			this.connection.getLog());	
+        			this.connection.getLog());
         } else if (this.connection.useUnbufferedInput()) {
         	this.mysqlInput = this.mysqlConnection.getInputStream();
         } else {
         	this.mysqlInput = new BufferedInputStream(this.mysqlConnection.getInputStream(),
         			16384);
         }
-        
+
         this.mysqlOutput = new BufferedOutputStream(this.mysqlConnection.getOutputStream(),
         		16384);
-        
 
+
         this.isInteractiveClient = this.connection.getInteractiveClient();
         this.profileSql = this.connection.getProfileSql();
         this.sessionCalendar = Calendar.getInstance();
         this.autoGenerateTestcaseScript = this.connection.getAutoGenerateTestcaseScript();
-        
-        this.needToGrabQueryFromPacket = (this.profileSql || 
-        		this.logSlowQueries || 
+
+        this.needToGrabQueryFromPacket = (this.profileSql ||
+        		this.logSlowQueries ||
         		this.autoGenerateTestcaseScript);
+
+        if (this.connection.getUseNanosForElapsedTime()
+				&& Util.nanoTimeAvailable()) {
+			this.useNanosForElapsedTime = true;
+
+			this.queryTimingUnits = Messages.getString("Nanoseconds");
+		} else {
+			this.queryTimingUnits = Messages.getString("Milliseconds");
+		}
+
+		if (this.connection.getLogSlowQueries()) {
+			calculateSlowQueryThreshold();
+		}
     }
 
+    protected void initializeStatementInterceptors(String interceptorClasses,
+			Properties props) throws SQLException {
+		this.statementInterceptors = Util.loadExtensions(this.connection, props, 
+				interceptorClasses,
+				"MysqlIo.BadStatementInterceptor");
+	}
+
     /**
-     * Does the server send back extra column info?
-     *
-     * @return true if so
-     */
+	 * Does the server send back extra column info?
+	 * 
+	 * @return true if so
+	 */
     public boolean hasLongColumnInfo() {
         return this.hasLongColumnInfo;
     }
@@ -325,7 +344,7 @@
         try {
             return this.mysqlInput.available() > 0;
         } catch (IOException ioEx) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, ioEx);
         }
     }
@@ -360,95 +379,99 @@
      *
      * @throws SQLException if a database access error occurs
      */
-    protected ResultSet getResultSet(Statement callingStatement,
+    protected ResultSetImpl getResultSet(StatementImpl callingStatement,
         long columnCount, int maxRows, int resultSetType,
         int resultSetConcurrency, boolean streamResults, String catalog,
-        boolean isBinaryEncoded, boolean unpackFieldInfo)
+        boolean isBinaryEncoded, Field[] metadataFromCache)
         throws SQLException {
         Buffer packet; // The packet from the server
         Field[] fields = null;
 
-        if (unpackFieldInfo) {
+        // Read in the column information
+
+        if (metadataFromCache == null /* we want the metadata from the server */) {
             fields = new Field[(int) columnCount];
-        }
 
-        // Read in the column information
-        for (int i = 0; i < columnCount; i++) {
-            Buffer fieldPacket = null;
+            for (int i = 0; i < columnCount; i++) {
+            	Buffer fieldPacket = null;
 
-            fieldPacket = readPacket();
-            
-            if (unpackFieldInfo) {
+                fieldPacket = readPacket();
                 fields[i] = unpackField(fieldPacket, false);
             }
+        } else {
+        	for (int i = 0; i < columnCount; i++) {
+        		skipPacket();
+        	}
         }
 
         packet = reuseAndReadPacket(this.reusablePacket);
 
         readServerStatusForResultSets(packet);
-		
+
 		//
 		// Handle cursor-based fetch first
 		//
-		
+
 		if (this.connection.versionMeetsMinimum(5, 0, 2)
+				&& this.connection.getUseCursorFetch()
 				&& isBinaryEncoded
 				&& callingStatement != null
 				&& callingStatement.getFetchSize() != 0
 				&& callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY) {
 			ServerPreparedStatement prepStmt = (com.mysql.jdbc.ServerPreparedStatement) callingStatement;
-	
-			Field[] fieldMetadata = ((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields;
 
 			boolean usingCursor = true;
-			
+
 			//
 			// Server versions 5.0.5 or newer will only open
 			// a cursor and set this flag if they can, otherwise
 			// they punt and go back to mysql_store_results() behavior
 			//
-			
+
 			if (this.connection.versionMeetsMinimum(5, 0, 5)) {
-				usingCursor = (this.serverStatus & 
+				usingCursor = (this.serverStatus &
 						SERVER_STATUS_CURSOR_EXISTS) != 0;
 			}
-			
+
 			if (usingCursor) {
-				RowData rows = new CursorRowProvider(
+				RowData rows = new RowDataCursor(
 					this,
 					prepStmt,
-					((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields);
+					fields);
 
-				ResultSet rs = buildResultSetWithRows(
+				ResultSetImpl rs = buildResultSetWithRows(
 					callingStatement,
 					catalog,
-					((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields,
+					fields,
 					rows, resultSetType, resultSetConcurrency, isBinaryEncoded);
-				
+
 				if (usingCursor) {
 		        	rs.setFetchSize(callingStatement.getFetchSize());
 		        }
-				
+
 				return rs;
 			}
 		}
-		
+
         RowData rowData = null;
-       
+
         if (!streamResults) {
             rowData = readSingleRowSet(columnCount, maxRows,
-                    resultSetConcurrency, isBinaryEncoded, fields);
+                    resultSetConcurrency, isBinaryEncoded,
+                    (metadataFromCache == null) ? fields : metadataFromCache);
         } else {
-            rowData = new RowDataDynamic(this, (int) columnCount, fields,
+            rowData = new RowDataDynamic(this, (int) columnCount,
+            		(metadataFromCache == null) ? fields : metadataFromCache,
                     isBinaryEncoded);
             this.streamingData = rowData;
         }
 
-        ResultSet rs = buildResultSetWithRows(callingStatement, catalog, fields,
+        ResultSetImpl rs = buildResultSetWithRows(callingStatement, catalog,
+        		(metadataFromCache == null) ? fields : metadataFromCache,
             rowData, resultSetType, resultSetConcurrency, isBinaryEncoded);
- 
-        
-        
+
+
+
         return rs;
     }
 
@@ -488,6 +511,64 @@
     }
 
     /**
+     * Reads and discards a single MySQL packet from the input stream.
+     *
+     * @throws SQLException if the network fails while skipping the
+     * packet.
+     */
+    protected final void skipPacket() throws SQLException {
+		try {
+
+			int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf,
+					0, 4);
+
+			if (lengthRead < 4) {
+				forceClose();
+				throw new IOException(Messages.getString("MysqlIO.1")); //$NON-NLS-1$
+			}
+
+			int packetLength = (this.packetHeaderBuf[0] & 0xff)
+					+ ((this.packetHeaderBuf[1] & 0xff) << 8)
+					+ ((this.packetHeaderBuf[2] & 0xff) << 16);
+
+			if (this.traceProtocol) {
+				StringBuffer traceMessageBuf = new StringBuffer();
+
+				traceMessageBuf.append(Messages.getString("MysqlIO.2")); //$NON-NLS-1$
+				traceMessageBuf.append(packetLength);
+				traceMessageBuf.append(Messages.getString("MysqlIO.3")); //$NON-NLS-1$
+				traceMessageBuf.append(StringUtils.dumpAsHex(
+						this.packetHeaderBuf, 4));
+
+				this.connection.getLog().logTrace(traceMessageBuf.toString());
+			}
+
+			byte multiPacketSeq = this.packetHeaderBuf[3];
+
+			if (!this.packetSequenceReset) {
+				if (this.enablePacketDebug && this.checkPacketSequence) {
+					checkPacketSequencing(multiPacketSeq);
+				}
+			} else {
+				this.packetSequenceReset = false;
+			}
+
+			this.readPacketSequence = multiPacketSeq;
+
+			skipFully(this.mysqlInput, packetLength);
+		} catch (IOException ioEx) {
+			throw SQLError.createCommunicationsException(this.connection,
+					this.lastPacketSentTimeMs, ioEx);
+		} catch (OutOfMemoryError oom) {
+			try {
+				this.connection.realClose(false, false, true, oom);
+			} finally {
+				throw oom;
+			}
+		}
+	}
+
+    /**
      * Read one packet from the MySQL server
      *
      * @return the packet from the server.
@@ -497,7 +578,7 @@
      */
     protected final Buffer readPacket() throws SQLException {
         try {
-            
+
             int lengthRead = readFully(this.mysqlInput,
                     this.packetHeaderBuf, 0, 4);
 
@@ -566,7 +647,7 @@
 
             return packet;
         } catch (IOException ioEx) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, ioEx);
         } catch (OutOfMemoryError oom) {
         	try {
@@ -607,7 +688,7 @@
             int tableNameStart = packet.getPosition() + 1;
             int tableNameLength = packet.fastSkipLenString();
             tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength);
-            
+
             // orgTableName is never used so skip
             int originalTableNameStart = packet.getPosition() + 1;
             int originalTableNameLength = packet.fastSkipLenString();
@@ -616,7 +697,7 @@
             // we only store the position again...
             int nameStart = packet.getPosition() + 1;
             int nameLength = packet.fastSkipLenString();
-            
+
             nameStart = adjustStartForFieldLength(nameStart, nameLength);
 
             // orgColName is not required so skip...
@@ -670,11 +751,11 @@
         int tableNameStart = packet.getPosition() + 1;
         int tableNameLength = packet.fastSkipLenString();
         tableNameStart = adjustStartForFieldLength(tableNameStart, tableNameLength);
-        
+
         int nameStart = packet.getPosition() + 1;
         int nameLength = packet.fastSkipLenString();
         nameStart = adjustStartForFieldLength(nameStart, nameLength);
-        
+
         int colLength = packet.readnBytes();
         int colType = packet.readnBytes();
         packet.readByte(); // We know it's currently 2
@@ -704,15 +785,15 @@
     	if (nameLength < 251) {
     		return nameStart;
     	}
-    	
+
 		if (nameLength >= 251 && nameLength < 65536) {
 			return nameStart + 2;
 		}
-		
+
 		if (nameLength >= 65536 && nameLength < 16777216) {
 			return nameStart + 3;
 		}
-		
+
 		return nameStart + 8;
 	}
 
@@ -721,7 +802,7 @@
             boolean autoCommitModeOnServer = ((this.serverStatus &
                 SERVER_STATUS_AUTOCOMMIT) != 0);
 
-            if (!autoCommitFlag) {
+            if (!autoCommitFlag && versionMeetsMinimum(5, 0, 0)) {
                 // Just to be safe, check if a transaction is in progress on the server....
                 // if so, then we must be in autoCommit == false
                 // therefore return the opposite of transaction status
@@ -731,12 +812,16 @@
                 return !inTransactionOnServer;
             }
 
-            return !autoCommitModeOnServer;
+            return autoCommitModeOnServer != autoCommitFlag;
         }
 
         return true;
     }
 
+    protected boolean inTransactionOnServer() {
+    	return (this.serverStatus & SERVER_STATUS_IN_TRANS) != 0;
+    }
+
     /**
      * Re-authenticates as the given user and password
      *
@@ -751,14 +836,11 @@
         this.packetSequence = -1;
 
         int passwordLength = 16;
-        int userLength = 0;
+        int userLength = (userName != null) ? userName.length() : 0;
+        int databaseLength = (database != null) ? database.length() : 0;
 
-        if (userName != null) {
-            userLength = userName.length();
-        }
+        int packLength = ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD;
 
-        int packLength = (userLength + passwordLength) + 7 + HEADER_LENGTH;
-
         if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
             Buffer changeUserPacket = new Buffer(packLength + 1);
             changeUserPacket.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
@@ -784,16 +866,16 @@
                 packet.writeString(Util.oldCrypt(password, this.seed));
             }
 
-			boolean localUseConnectWithDb = this.useConnectWithDb && 
+			boolean localUseConnectWithDb = this.useConnectWithDb &&
 				(database != null && database.length() > 0);
-			
+
             if (localUseConnectWithDb) {
                 packet.writeString(database);
             }
 
             send(packet, packet.getPosition());
             checkErrorPacket();
-			
+
 			if (!localUseConnectWithDb) {
 				changeDatabaseTo(database);
 			}
@@ -833,7 +915,7 @@
     }
 
     protected void clearInputStream() throws SQLException {
-    
+
         try {
             int len = this.mysqlInput.available();
 
@@ -842,7 +924,7 @@
                 len = this.mysqlInput.available();
             }
         } catch (IOException ioEx) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, ioEx);
         }
     }
@@ -886,7 +968,7 @@
             java.sql.ResultSet rs = null;
 
             try {
-                stmt = this.connection.clientPrepareStatement("EXPLAIN ?"); //$NON-NLS-1$
+                stmt = (PreparedStatement) this.connection.clientPrepareStatement("EXPLAIN ?"); //$NON-NLS-1$
                 stmt.setBytesNoEscapeNoQuotes(1, querySQL);
                 rs = stmt.executeQuery();
 
@@ -977,7 +1059,7 @@
             try {
                 this.mysqlConnection.close();
             } catch (Exception e) {
-                ; // ignore
+                // ignore
             }
 
             int errno = 2000;
@@ -1001,26 +1083,26 @@
         this.serverVersion = buf.readString();
 
         // Parse the server version into major/minor/subminor
-        int point = this.serverVersion.indexOf("."); //$NON-NLS-1$
+        int point = this.serverVersion.indexOf('.'); //$NON-NLS-1$
 
         if (point != -1) {
             try {
                 int n = Integer.parseInt(this.serverVersion.substring(0, point));
                 this.serverMajorVersion = n;
             } catch (NumberFormatException NFE1) {
-                ;
+                // ignore
             }
 
             String remaining = this.serverVersion.substring(point + 1,
                     this.serverVersion.length());
-            point = remaining.indexOf("."); //$NON-NLS-1$
+            point = remaining.indexOf('.'); //$NON-NLS-1$
 
             if (point != -1) {
                 try {
                     int n = Integer.parseInt(remaining.substring(0, point));
                     this.serverMinorVersion = n;
                 } catch (NumberFormatException nfe) {
-                    ;
+                    // ignore
                 }
 
                 remaining = remaining.substring(point + 1, remaining.length());
@@ -1040,7 +1122,7 @@
                     int n = Integer.parseInt(remaining.substring(0, pos));
                     this.serverSubMinorVersion = n;
                 } catch (NumberFormatException nfe) {
-                    ;
+                    // ignore
                 }
             }
         }
@@ -1057,7 +1139,7 @@
         this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog
         this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5);
 
-        threadId = buf.readLong(); 
+        threadId = buf.readLong();
         this.seed = buf.readString();
 
         this.serverCapabilities = 0;
@@ -1086,10 +1168,10 @@
             this.clientParam |= CLIENT_COMPRESS;
         }
 
-		this.useConnectWithDb = (database != null) && 
+		this.useConnectWithDb = (database != null) &&
 			(database.length() > 0) &&
 			!this.connection.getCreateDatabaseIfNotExist();
-		
+
         if (this.useConnectWithDb) {
             this.clientParam |= CLIENT_CONNECT_WITH_DB;
         }
@@ -1159,19 +1241,11 @@
         }
 
         int passwordLength = 16;
-        int userLength = 0;
-        int databaseLength = 0;
+        int userLength = (user != null) ? user.length() : 0;
+        int databaseLength = (database != null) ? database.length() : 0;
 
-        if (user != null) {
-            userLength = user.length();
-        }
+        int packLength = ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD;
 
-        if (database != null) {
-            databaseLength = database.length();
-        }
-
-        int packLength = (userLength + passwordLength + databaseLength) + 7 +
-            HEADER_LENGTH;
         Buffer packet = null;
 
         if (!this.connection.getUseSSL()) {
@@ -1210,16 +1284,16 @@
                 }
 
                 // User/Password data
-                packet.writeString(user, "Cp1252", this.connection);
+                packet.writeString(user, CODE_PAGE_1252, this.connection);
 
                 if (this.protocolVersion > 9) {
-                    packet.writeString(Util.newCrypt(password, this.seed), "Cp1252", this.connection);
+                    packet.writeString(Util.newCrypt(password, this.seed), CODE_PAGE_1252, this.connection);
                 } else {
-                    packet.writeString(Util.oldCrypt(password, this.seed), "Cp1252", this.connection);
+                    packet.writeString(Util.oldCrypt(password, this.seed), CODE_PAGE_1252, this.connection);
                 }
 
                 if (this.useConnectWithDb) {
-                    packet.writeString(database, "Cp1252", this.connection);
+                    packet.writeString(database, CODE_PAGE_1252, this.connection);
                 }
 
                 send(packet, packet.getPosition());
@@ -1253,11 +1327,11 @@
         }
     }
 
-	private void changeDatabaseTo(String database) throws SQLException, CommunicationsException {
+	private void changeDatabaseTo(String database) throws SQLException {
 		if (database == null || database.length() == 0) {
 			return;
 		}
-		
+
 		try {
 		    sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
 		} catch (Exception ex) {
@@ -1267,7 +1341,7 @@
 					null, false, null);
 				sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
 			} else {
-				throw new CommunicationsException(this.connection,
+				throw SQLError.createCommunicationsException(this.connection,
 						this.lastPacketSentTimeMs, ex);
 			}
 		}
@@ -1282,18 +1356,44 @@
     * @param columnCount DOCUMENT ME!
     * @param isBinaryEncoded DOCUMENT ME!
     * @param resultSetConcurrency DOCUMENT ME!
+     * @param b
     *
     * @return DOCUMENT ME!
     *
     * @throws SQLException DOCUMENT ME!
     */
-    final Object[] nextRow(Field[] fields, int columnCount,
-        boolean isBinaryEncoded, int resultSetConcurrency)
+    final ResultSetRow nextRow(Field[] fields, int columnCount,
+        boolean isBinaryEncoded, int resultSetConcurrency,
+        boolean useBufferRowIfPossible,
+        boolean useBufferRowExplicit,
+        boolean canReuseRowPacketForBufferRow, Buffer existingRowPacket)
         throws SQLException {
-        // Get the next incoming packet, re-using the packet because
-        // all the data we need gets copied out of it.
-        Buffer rowPacket = checkErrorPacket();
 
+    	if (this.useDirectRowUnpack && existingRowPacket == null
+				&& !isBinaryEncoded && !useBufferRowIfPossible
+				&& !useBufferRowExplicit) {
+			return nextRowFast(fields, columnCount, isBinaryEncoded, resultSetConcurrency,
+					useBufferRowIfPossible, useBufferRowExplicit, canReuseRowPacketForBufferRow);
+		}
+
+        Buffer rowPacket = null;
+
+        if (existingRowPacket == null) {
+        	rowPacket = checkErrorPacket();
+
+        	if (!useBufferRowExplicit && useBufferRowIfPossible) {
+        		if (rowPacket.getBufLength() > this.useBufferRowSizeThreshold) {
+        			useBufferRowExplicit = true;
+        		}
+        	}
+        } else {
+        	// We attempted to do nextRowFast(), but the packet was a
+        	// multipacket, so we couldn't unpack it directly
+        	rowPacket = existingRowPacket;
+        	checkErrorPacket(existingRowPacket);
+        }
+
+
         if (!isBinaryEncoded) {
             //
             // Didn't read an error, so re-position to beginning
@@ -1302,15 +1402,24 @@
             rowPacket.setPosition(rowPacket.getPosition() - 1);
 
             if (!rowPacket.isLastDataPacket()) {
-                byte[][] rowData = new byte[columnCount][];
+            	if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE
+            			|| (!useBufferRowIfPossible && !useBufferRowExplicit)) {
 
-                int offset = 0;
+            		byte[][] rowData = new byte[columnCount][];
 
-                for (int i = 0; i < columnCount; i++) {
-                    rowData[i] = rowPacket.readLenByteArray(offset);
-                }
+            		for (int i = 0; i < columnCount; i++) {
+            			rowData[i] = rowPacket.readLenByteArray(0);
+            		}
 
-                return rowData;
+            		return new ByteArrayRow(rowData);
+            	}
+
+            	if (!canReuseRowPacketForBufferRow) {
+            		this.reusablePacket = new Buffer(rowPacket.getBufLength());
+            	}
+
+            	return new BufferRow(rowPacket, fields, false);
+
             }
 
             readServerStatusForResultSets(rowPacket);
@@ -1318,13 +1427,22 @@
             return null;
         }
 
-        // 
-        // Handle binary-encoded data for server-side   
+        //
+        // Handle binary-encoded data for server-side
         // PreparedStatements...
         //
         if (!rowPacket.isLastDataPacket()) {
-            return unpackBinaryResultSetRow(fields, rowPacket,
-                resultSetConcurrency);
+        	if (resultSetConcurrency == ResultSet.CONCUR_UPDATABLE
+        			|| (!useBufferRowIfPossible && !useBufferRowExplicit)) {
+        		return unpackBinaryResultSetRow(fields, rowPacket,
+        				resultSetConcurrency);
+        	}
+
+        	if (!canReuseRowPacketForBufferRow) {
+        		this.reusablePacket = new Buffer(rowPacket.getBufLength());
+        	}
+
+            return new BufferRow(rowPacket, fields, true);
         }
 
         rowPacket.setPosition(rowPacket.getPosition() - 1);
@@ -1333,6 +1451,159 @@
         return null;
     }
 
+    final ResultSetRow nextRowFast(Field[] fields, int columnCount,
+            boolean isBinaryEncoded, int resultSetConcurrency,
+            boolean useBufferRowIfPossible,
+            boolean useBufferRowExplicit, boolean canReuseRowPacket)
+			throws SQLException {
+		try {
+			int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf,
+					0, 4);
+
+			if (lengthRead < 4) {
+				forceClose();
+				throw new RuntimeException(Messages.getString("MysqlIO.43")); //$NON-NLS-1$
+			}
+
+			int packetLength = (this.packetHeaderBuf[0] & 0xff)
+					+ ((this.packetHeaderBuf[1] & 0xff) << 8)
+					+ ((this.packetHeaderBuf[2] & 0xff) << 16);
+
+			// Have we stumbled upon a multi-packet?
+			if (packetLength == this.maxThreeBytes) {
+				reuseAndReadPacket(this.reusablePacket, packetLength);
+
+				// Go back to "old" way which uses packets
+				return nextRow(fields, columnCount, isBinaryEncoded, resultSetConcurrency,
+						useBufferRowIfPossible, useBufferRowExplicit,
+						canReuseRowPacket, this.reusablePacket);
+			}
+
+			// Does this go over the threshold where we should use a BufferRow?
+
+			if (packetLength > this.useBufferRowSizeThreshold) {
+				reuseAndReadPacket(this.reusablePacket, packetLength);
+
+				// Go back to "old" way which uses packets
+				return nextRow(fields, columnCount, isBinaryEncoded, resultSetConcurrency,
+						true, true,
+						false, this.reusablePacket);
+			}
+
+			int remaining = packetLength;
+
+			boolean firstTime = true;
+
+			byte[][] rowData = null;
+
+			for (int i = 0; i < columnCount; i++) {
+
+				int sw = this.mysqlInput.read() & 0xff;
+				remaining--;
+
+				if (firstTime) {
+					if (sw == 255) {
+						// error packet
+						Buffer errorPacket = new Buffer(packetLength);
+						errorPacket.writeByte((byte)sw);
+						readFully(this.mysqlInput, errorPacket.getByteBuffer(), 1, packetLength - 1);
+
+						checkErrorPacket(errorPacket);
+					}
+
+					if (sw == 254 && packetLength < 9) {
+						this.warningCount = (this.mysqlInput.read() & 0xff)
+						| ((this.mysqlInput.read() & 0xff) << 8);
+						remaining -= 2;
+
+			            if (this.warningCount > 0) {
+			                this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
+			            }
+
+			            this.serverStatus = (this.mysqlInput.read() & 0xff)
+						| ((this.mysqlInput.read() & 0xff) << 8);
+						remaining -= 2;
+
+						if (remaining > 0) {
+							skipFully(this.mysqlInput, remaining);
+						}
+
+						return null; // last data packet
+					}
+
+					rowData = new byte[columnCount][];
+
+					firstTime = false;
+				}
+
+				int len = 0;
+
+				switch (sw) {
+				case 251:
+					len = NULL_LENGTH;
+					break;
+
+				case 252:
+					len = (this.mysqlInput.read() & 0xff)
+							| ((this.mysqlInput.read() & 0xff) << 8);
+					remaining -= 2;
+					break;
+
+				case 253:
+					len = (this.mysqlInput.read() & 0xff)
+							| ((this.mysqlInput.read() & 0xff) << 8)
+							| ((this.mysqlInput.read() & 0xff) << 16);
+
+					remaining -= 3;
+					break;
+
+				case 254:
+					len = (int) ((this.mysqlInput.read() & 0xff)
+							| ((long) (this.mysqlInput.read() & 0xff) << 8)
+							| ((long) (this.mysqlInput.read() & 0xff) << 16)
+							| ((long) (this.mysqlInput.read() & 0xff) << 24)
+							| ((long) (this.mysqlInput.read() & 0xff) << 32)
+							| ((long) (this.mysqlInput.read() & 0xff) << 40)
+							| ((long) (this.mysqlInput.read() & 0xff) << 48)
+							| ((long) (this.mysqlInput.read() & 0xff) << 56));
+					remaining -= 8;
+					break;
+
+				default:
+					len = sw;
+				}
+
+				if (len == NULL_LENGTH) {
+					rowData[i] = null;
+				} else if (len == 0) {
+					rowData[i] = Constants.EMPTY_BYTE_ARRAY;
+				} else {
+					rowData[i] = new byte[len];
+
+					int bytesRead = readFully(this.mysqlInput, rowData[i], 0,
+							len);
+
+					if (bytesRead != len) {
+						throw SQLError.createCommunicationsException(this.connection,
+			    				this.lastPacketSentTimeMs,
+			    				new IOException(Messages.getString("MysqlIO.43")));
+					}
+
+					remaining -= bytesRead;
+				}
+			}
+
+			if (remaining > 0) {
+				skipFully(this.mysqlInput, remaining);
+			}
+
+			return new ByteArrayRow(rowData);
+		} catch (IOException ioEx) {
+			throw SQLError.createCommunicationsException(this.connection,
+    				this.lastPacketSentTimeMs, ioEx);
+		}
+	}
+
     /**
      * Log-off of the MySQL server and close the socket.
      *
@@ -1354,8 +1625,7 @@
      */
     Buffer getSharedSendPacket() {
         if (this.sharedSendPacket == null) {
-        	this.sharedSendPacket = new Buffer(
-        			this.connection.getNetBufferLength());
+        	this.sharedSendPacket = new Buffer(INITIAL_PACKET_SIZE);
         }
 
         return this.sharedSendPacket;
@@ -1377,19 +1647,19 @@
         this.streamingData = null;
     }
 
-    ResultSet readAllResults(Statement callingStatement, int maxRows,
+    ResultSetImpl readAllResults(StatementImpl callingStatement, int maxRows,
         int resultSetType, int resultSetConcurrency, boolean streamResults,
         String catalog, Buffer resultPacket, boolean isBinaryEncoded,
-        long preSentColumnCount, boolean unpackFieldInfo)
+        long preSentColumnCount, Field[] metadataFromCache)
         throws SQLException {
         resultPacket.setPosition(resultPacket.getPosition() - 1);
 
-        ResultSet topLevelResultSet = readResultsForQueryOrUpdate(callingStatement,
+        ResultSetImpl topLevelResultSet = readResultsForQueryOrUpdate(callingStatement,
                 maxRows, resultSetType, resultSetConcurrency, streamResults,
                 catalog, resultPacket, isBinaryEncoded, preSentColumnCount,
-                unpackFieldInfo);
+                metadataFromCache);
 
-        ResultSet currentResultSet = topLevelResultSet;
+        ResultSetImpl currentResultSet = topLevelResultSet;
 
         boolean checkForMoreResults = ((this.clientParam &
             CLIENT_MULTI_RESULTS) != 0);
@@ -1412,17 +1682,11 @@
         while (moreRowSetsExist) {
         	Buffer fieldPacket = checkErrorPacket();
             fieldPacket.setPosition(0);
-            
-            if ((fieldPacket.readByte(0) == 0) &&
-                    (fieldPacket.readByte(1) == 0) &&
-                    (fieldPacket.readByte(2) == 0)) {
-                break;
-            }
 
-            ResultSet newResultSet = readResultsForQueryOrUpdate(callingStatement,
+            ResultSetImpl newResultSet = readResultsForQueryOrUpdate(callingStatement,
                     maxRows, resultSetType, resultSetConcurrency,
                     streamResults, catalog, fieldPacket, isBinaryEncoded,
-                    preSentColumnCount, unpackFieldInfo);
+                    preSentColumnCount, metadataFromCache);
 
             currentResultSet.setNextResultSet(newResultSet);
 
@@ -1466,7 +1730,7 @@
      *
      * @throws SQLException if an I/O error or SQL error occurs
      */
-   
+
     final Buffer sendCommand(int command, String extraData, Buffer queryPacket,
         boolean skipCheck, String extraDataCharEncoding)
         throws SQLException {
@@ -1480,11 +1744,11 @@
         this.readPacketSequence = 0;
 
         try {
-        	
+
             checkForOutstandingStreamingData();
-           
+
             // Clear serverStatus...this value is guarded by an
-            // external mutex, as you can only ever be processing 
+            // external mutex, as you can only ever be processing
             // one command at a time
             this.serverStatus = 0;
             this.hadWarnings = false;
@@ -1544,7 +1808,7 @@
                                 this.connection.parserKnowsUnicode(), this.connection);
                         }
                     } else if (command == MysqlDefs.PROCESS_KILL) {
-                        long id = new Long(extraData).longValue();
+                        long id = Long.parseLong(extraData);
                         this.sendPacket.writeLong(id);
                     }
 
@@ -1557,7 +1821,7 @@
                 // don't wrap SQLExceptions
                 throw sqlEx;
             } catch (Exception ex) {
-                throw new CommunicationsException(this.connection,
+                throw SQLError.createCommunicationsException(this.connection,
                     this.lastPacketSentTimeMs, ex);
             }
 
@@ -1575,11 +1839,14 @@
 
             return returnPacket;
         } catch (IOException ioEx) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, ioEx);
         }
     }
 
+    private int statementExecutionDepth = 0;
+	private boolean useAutoSlowLog;
+
     /**
      * Send a query stored in a packet directly to the server.
      *
@@ -1599,222 +1866,374 @@
      *
      * @throws Exception DOCUMENT ME!
      */
-    final ResultSet sqlQueryDirect(Statement callingStatement, String query,
-        String characterEncoding, Buffer queryPacket, int maxRows,
-        Connection conn, int resultSetType, int resultSetConcurrency,
-        boolean streamResults, String catalog, boolean unpackFieldInfo)
-        throws Exception {
-        long queryStartTime = 0;
-        long queryEndTime = 0;
+    final ResultSetInternalMethods sqlQueryDirect(StatementImpl callingStatement, String query,
+    		String characterEncoding, Buffer queryPacket, int maxRows,
+    		int resultSetType, int resultSetConcurrency,
+    		boolean streamResults, String catalog, Field[] cachedMetadata)
+    throws Exception {
+    	this.statementExecutionDepth++;
 
-        if (query != null) {
-        	
-        	
-            // We don't know exactly how many bytes we're going to get
-            // from the query. Since we're dealing with Unicode, the
-            // max is 2, so pad it (2 * query) + space for headers
-            int packLength = HEADER_LENGTH + 1 + (query.length() * 2) + 2;
+    	try {
+	    	if (this.statementInterceptors != null) {
+	    		ResultSetInternalMethods interceptedResults =
+	    			invokeStatementInterceptorsPre(query, callingStatement);
 
-            if (this.sendPacket == null) {
-            	this.sendPacket = new Buffer(packLength);
-            } else {
-                this.sendPacket.clear();
-            }
+	    		if (interceptedResults != null) {
+	    			return interceptedResults;
+	    		}
+	    	}
 
-            this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
+	    	long queryStartTime = 0;
+	    	long queryEndTime = 0;
 
-            if (characterEncoding != null) {
-                if (this.platformDbCharsetMatches) {
-                    this.sendPacket.writeStringNoNull(query, characterEncoding,
-                        this.connection.getServerCharacterEncoding(),
-                        this.connection.parserKnowsUnicode(),
-                        this.connection);
-                } else {
-                    if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) { //$NON-NLS-1$
-                        this.sendPacket.writeBytesNoNull(query.getBytes());
-                    } else {
-                        this.sendPacket.writeStringNoNull(query,
-                            characterEncoding,
-                            this.connection.getServerCharacterEncoding(),
-                            this.connection.parserKnowsUnicode(),
-                            this.connection);
-                    }
-                }
-            } else {
-                this.sendPacket.writeStringNoNull(query);
-            }
+	    	if (query != null) {
 
-            queryPacket = this.sendPacket;
-        }
+	    		// We don't know exactly how many bytes we're going to get
+	    		// from the query. Since we're dealing with Unicode, the
+	    		// max is 2, so pad it (2 * query) + space for headers
+	    		int packLength = HEADER_LENGTH + 1 + (query.length() * 2) + 2;
 
-        byte[] queryBuf = null;
-        int oldPacketPosition = 0;
+	    		String statementComment = this.connection.getStatementComment();
 
-        
-        
-        if (needToGrabQueryFromPacket) {
-            queryBuf = queryPacket.getByteBuffer();
+	    		byte[] commentAsBytes = null;
 
-            // save the packet position
-            oldPacketPosition = queryPacket.getPosition();
+	    		if (statementComment != null) {
+	    			commentAsBytes = StringUtils.getBytes(statementComment, null,
+	    					characterEncoding, this.connection
+	    					.getServerCharacterEncoding(),
+	    					this.connection.parserKnowsUnicode());
 
-            queryStartTime = System.currentTimeMillis();
-        }
+	    			packLength += commentAsBytes.length;
+	    			packLength += 6; // for /*[space] [space]*/
+	    		}
 
-        // Send query command and sql query string
-        Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket,
-                false, null);
+	    		if (this.sendPacket == null) {
+	    			this.sendPacket = new Buffer(packLength);
+	    		} else {
+	    			this.sendPacket.clear();
+	    		}
 
-        long fetchBeginTime = 0;
-        long fetchEndTime = 0;
+	    		this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
 
-        String profileQueryToLog = null;
+	    		if (commentAsBytes != null) {
+	    			this.sendPacket.writeBytesNoNull(Constants.SLASH_STAR_SPACE_AS_BYTES);
+	    			this.sendPacket.writeBytesNoNull(commentAsBytes);
+	    			this.sendPacket.writeBytesNoNull(Constants.SPACE_STAR_SLASH_SPACE_AS_BYTES);
+	    		}
 
-        boolean queryWasSlow = false;
+	    		if (characterEncoding != null) {
+	    			if (this.platformDbCharsetMatches) {
+	    				this.sendPacket.writeStringNoNull(query, characterEncoding,
+	    						this.connection.getServerCharacterEncoding(),
+	    						this.connection.parserKnowsUnicode(),
+	    						this.connection);
+	    			} else {
+	    				if (StringUtils.startsWithIgnoreCaseAndWs(query, "LOAD DATA")) { //$NON-NLS-1$
+	    					this.sendPacket.writeBytesNoNull(query.getBytes());
+	    				} else {
+	    					this.sendPacket.writeStringNoNull(query,
+	    							characterEncoding,
+	    							this.connection.getServerCharacterEncoding(),
+	    							this.connection.parserKnowsUnicode(),
+	    							this.connection);
+	    				}
+	    			}
+	    		} else {
+	    			this.sendPacket.writeStringNoNull(query);
+	    		}
 
-        if (this.profileSql || this.logSlowQueries) {
-            queryEndTime = System.currentTimeMillis();
+	    		queryPacket = this.sendPacket;
+	    	}
 
-            boolean shouldExtractQuery = false;
+	    	byte[] queryBuf = null;
+	    	int oldPacketPosition = 0;
 
-            if (this.profileSql) {
-                shouldExtractQuery = true;
-            } else if (this.logSlowQueries &&
-                    ((queryEndTime - queryStartTime) > this.connection.getSlowQueryThresholdMillis())) {
-                shouldExtractQuery = true;
-                queryWasSlow = true;
-            }
+	    	if (needToGrabQueryFromPacket) {
+	    		queryBuf = queryPacket.getByteBuffer();
 
-            if (shouldExtractQuery) {
-                // Extract the actual query from the network packet 
-                boolean truncated = false;
+	    		// save the packet position
+	    		oldPacketPosition = queryPacket.getPosition();
 
-                int extractPosition = oldPacketPosition;
+	    		queryStartTime = getCurrentTimeNanosOrMillis();
+	    	}
 
-                if (oldPacketPosition > this.connection.getMaxQuerySizeToLog()) {
-                    extractPosition = this.connection.getMaxQuerySizeToLog() + 5;
-                    truncated = true;
-                }
+	    	// Send query command and sql query string
+	    	Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket,
+	    			false, null);
 
-                profileQueryToLog = new String(queryBuf, 5,
-                        (extractPosition - 5));
+	    	long fetchBeginTime = 0;
+	    	long fetchEndTime = 0;
 
-                if (truncated) {
-                    profileQueryToLog += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
-                }
-            }
+	    	String profileQueryToLog = null;
 
-            fetchBeginTime = queryEndTime;
-        }
-        
-        if (this.autoGenerateTestcaseScript) {
-        	String testcaseQuery = null;
-        	
-        	if (query != null) {
-        		testcaseQuery = query;
-        	} else {
-        		testcaseQuery = new String(queryBuf, 5,
-                        (oldPacketPosition - 5));
-        	}
-        	
-    		StringBuffer debugBuf = new StringBuffer(testcaseQuery.length() + 32);
-    		this.connection.generateConnectionCommentBlock(debugBuf);
-    		debugBuf.append(testcaseQuery);
-    		debugBuf.append(';');
-    		this.connection.dumpTestcaseQuery(debugBuf.toString());
-    	}
-        
-        ResultSet rs = readAllResults(callingStatement, maxRows, resultSetType,
-                resultSetConcurrency, streamResults, catalog, resultPacket,
-                false, -1L, unpackFieldInfo);
+	    	boolean queryWasSlow = false;
 
-        if (queryWasSlow) {
-            StringBuffer mesgBuf = new StringBuffer(48 +
-                    profileQueryToLog.length());
-            mesgBuf.append(Messages.getString("MysqlIO.26")); //$NON-NLS-1$
-            mesgBuf.append(this.connection.getSlowQueryThresholdMillis());
-            mesgBuf.append(Messages.getString("MysqlIO.26a"));
-            mesgBuf.append((queryEndTime - queryStartTime));
-            mesgBuf.append(Messages.getString("MysqlIO.27")); //$NON-NLS-1$
-            mesgBuf.append(profileQueryToLog);
+	    	if (this.profileSql || this.logSlowQueries) {
+	    		queryEndTime = System.currentTimeMillis();
 
-            ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
+	    		boolean shouldExtractQuery = false;
 
-            eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY,
-                    "", catalog, this.connection.getId(), //$NON-NLS-1$
-                    (callingStatement != null) ? callingStatement.getId() : 999,
-                    rs.resultId, System.currentTimeMillis(),
-                    (int) (queryEndTime - queryStartTime), null,
-                    new Throwable(), profileQueryToLog));
-            
-            //this.connection.getLog().logWarn(mesgBuf.toString());
+	    		if (this.profileSql) {
+	    			shouldExtractQuery = true;
+	    		} else if (this.logSlowQueries) {
+	    			long queryTime = queryEndTime - queryStartTime;
+	    			
+	    			boolean logSlow = false;
+	    			
+	    			if (this.useAutoSlowLog) {
+	    				logSlow = queryTime > this.connection.getSlowQueryThresholdMillis();
+	    			} else {
+	    				logSlow = this.connection.isAbonormallyLongQuery(queryTime);
+	    				
+	    				this.connection.reportQueryTime(queryTime);
+	    			}
+	    			
+	    			if (logSlow) {
+	    				shouldExtractQuery = true;
+	    				queryWasSlow = true;
+	    			}
+	    		}
 
-            if (this.connection.getExplainSlowQueries()) {
-                if (oldPacketPosition < MAX_QUERY_SIZE_TO_EXPLAIN) {
-                    explainSlowQuery(queryPacket.getBytes(5,
-                            (oldPacketPosition - 5)), profileQueryToLog);
-                } else {
-                    this.connection.getLog().logWarn(Messages.getString(
-                            "MysqlIO.28") //$NON-NLS-1$
-                         +MAX_QUERY_SIZE_TO_EXPLAIN +
-                        Messages.getString("MysqlIO.29")); //$NON-NLS-1$
-                }
-            }
-        }
+	    		if (shouldExtractQuery) {
+	    			// Extract the actual query from the network packet
+	    			boolean truncated = false;
 
-        if (this.profileSql) {
-            fetchEndTime = System.currentTimeMillis();
+	    			int extractPosition = oldPacketPosition;
 
-            ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
+	    			if (oldPacketPosition > this.connection.getMaxQuerySizeToLog()) {
+	    				extractPosition = this.connection.getMaxQuerySizeToLog() + 5;
+	    				truncated = true;
+	    			}
 
-            eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY,
-                    "", catalog, this.connection.getId(), //$NON-NLS-1$
-                    (callingStatement != null) ? callingStatement.getId() : 999,
-                    rs.resultId, System.currentTimeMillis(),
-                    (int) (queryEndTime - queryStartTime), null,
-                    new Throwable(), profileQueryToLog));
+	    			profileQueryToLog = new String(queryBuf, 5,
+	    					(extractPosition - 5));
 
-            eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_FETCH,
-                    "", catalog, this.connection.getId(), //$NON-NLS-1$
-                    (callingStatement != null) ? callingStatement.getId() : 999,
-                    rs.resultId, System.currentTimeMillis(),
-                    (int) (fetchEndTime - fetchBeginTime), null,
-                    new Throwable(), null));
+	    			if (truncated) {
+	    				profileQueryToLog += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
+	    			}
+	    		}
 
-            if (this.queryBadIndexUsed) {
-                eventSink.consumeEvent(new ProfilerEvent(
-                        ProfilerEvent.TYPE_WARN, "", catalog, //$NON-NLS-1$
-                        this.connection.getId(),
-                        (callingStatement != null) ? callingStatement.getId()
-                                                   : 999, rs.resultId,
-                        System.currentTimeMillis(),
-                        (int) (queryEndTime - queryStartTime), null,
-                        new Throwable(),
-                        Messages.getString("MysqlIO.33") //$NON-NLS-1$
-                         +profileQueryToLog));
-            }
+	    		fetchBeginTime = queryEndTime;
+	    	}
 
-            if (this.queryNoIndexUsed) {
-                eventSink.consumeEvent(new ProfilerEvent(
-                        ProfilerEvent.TYPE_WARN, "", catalog, //$NON-NLS-1$
-                        this.connection.getId(),
-                        (callingStatement != null) ? callingStatement.getId()
-                                                   : 999, rs.resultId,
-                        System.currentTimeMillis(),
-                        (int) (queryEndTime - queryStartTime), null,
-                        new Throwable(),
-                        Messages.getString("MysqlIO.35") //$NON-NLS-1$
-                         +profileQueryToLog));
-            }
-        }
+	    	if (this.autoGenerateTestcaseScript) {
+	    		String testcaseQuery = null;
 
-        if (this.hadWarnings) {
-            scanForAndThrowDataTruncation();
-        }
+	    		if (query != null) {
+	    			testcaseQuery = query;
+	    		} else {
+	    			testcaseQuery = new String(queryBuf, 5,
+	    					(oldPacketPosition - 5));
+	    		}
 
-        return rs;
+	    		StringBuffer debugBuf = new StringBuffer(testcaseQuery.length() + 32);
+	    		this.connection.generateConnectionCommentBlock(debugBuf);
+	    		debugBuf.append(testcaseQuery);
+	    		debugBuf.append(';');
+	    		this.connection.dumpTestcaseQuery(debugBuf.toString());
+	    	}
+
+	    	ResultSetInternalMethods rs = readAllResults(callingStatement, maxRows, resultSetType,
+	    			resultSetConcurrency, streamResults, catalog, resultPacket,
+	    			false, -1L, cachedMetadata);
+
+	    	if (queryWasSlow) {
+	    		StringBuffer mesgBuf = new StringBuffer(48 +
+	    				profileQueryToLog.length());
+
+	    		mesgBuf.append(Messages.getString("MysqlIO.SlowQuery",
+	    				new Object[] {new Long(this.slowQueryThreshold),
+	    				queryTimingUnits,
+	    				new Long(queryEndTime - queryStartTime)}));
+	    		mesgBuf.append(profileQueryToLog);
+
+	    		ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
+
+	    		eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY,
+	    				"", catalog, this.connection.getId(), //$NON-NLS-1$
+	    				(callingStatement != null) ? callingStatement.getId() : 999,
+	    						((ResultSetImpl)rs).resultId, System.currentTimeMillis(),
+	    						(int) (queryEndTime - queryStartTime), queryTimingUnits, null,
+	    						new Throwable(), mesgBuf.toString()));
+
+	    		if (this.connection.getExplainSlowQueries()) {
+	    			if (oldPacketPosition < MAX_QUERY_SIZE_TO_EXPLAIN) {
+	    				explainSlowQuery(queryPacket.getBytes(5,
+	    						(oldPacketPosition - 5)), profileQueryToLog);
+	    			} else {
+	    				this.connection.getLog().logWarn(Messages.getString(
+	    						"MysqlIO.28") //$NON-NLS-1$
+	    						+MAX_QUERY_SIZE_TO_EXPLAIN +
+	    						Messages.getString("MysqlIO.29")); //$NON-NLS-1$
+	    			}
+	    		}
+	    	}
+
+	    	if (this.logSlowQueries) {
+
+	    		ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
+
+	    		if (this.queryBadIndexUsed) {
+	    			eventSink.consumeEvent(new ProfilerEvent(
+	    					ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, //$NON-NLS-1$
+	    					this.connection.getId(),
+	    					(callingStatement != null) ? callingStatement.getId()
+	    							: 999, ((ResultSetImpl)rs).resultId,
+	    							System.currentTimeMillis(),
+	    							(queryEndTime - queryStartTime), this.queryTimingUnits,
+	    							null,
+	    							new Throwable(),
+	    							Messages.getString("MysqlIO.33") //$NON-NLS-1$
+	    							+profileQueryToLog));
+	    		}
+
+	    		if (this.queryNoIndexUsed) {
+	    			eventSink.consumeEvent(new ProfilerEvent(
+	    					ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, //$NON-NLS-1$
+	    					this.connection.getId(),
+	    					(callingStatement != null) ? callingStatement.getId()
+	    							: 999, ((ResultSetImpl)rs).resultId,
+	    							System.currentTimeMillis(),
+	    							(queryEndTime - queryStartTime), this.queryTimingUnits,
+	    							null,
+	    							new Throwable(),
+	    							Messages.getString("MysqlIO.35") //$NON-NLS-1$
+	    							+profileQueryToLog));
+	    		}
+	    	}
+
+	    	if (this.profileSql) {
+	    		fetchEndTime = getCurrentTimeNanosOrMillis();
+
+	    		ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection);
+
+	    		eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY,
+	    				"", catalog, this.connection.getId(), //$NON-NLS-1$
+	    				(callingStatement != null) ? callingStatement.getId() : 999,
+	    						((ResultSetImpl)rs).resultId, System.currentTimeMillis(),
+	    						(queryEndTime - queryStartTime), this.queryTimingUnits,
+	    						null,
+	    						new Throwable(), profileQueryToLog));
+
+	    		eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_FETCH,
+	    				"", catalog, this.connection.getId(), //$NON-NLS-1$
+	    				(callingStatement != null) ? callingStatement.getId() : 999,
+	    						((ResultSetImpl)rs).resultId, System.currentTimeMillis(),
+	    						(fetchEndTime - fetchBeginTime), this.queryTimingUnits,
+	    						null,
+	    						new Throwable(), null));
+	    	}
+
+	    	if (this.hadWarnings) {
+	    		scanForAndThrowDataTruncation();
+	    	}
+
+	    	if (this.statementInterceptors != null) {
+	    		ResultSetInternalMethods interceptedResults = invokeStatementInterceptorsPost(
+	    				query, callingStatement, rs);
+
+	    		if (interceptedResults != null) {
+	    			rs = interceptedResults;
+	    		}
+	    	}
+
+	    	return rs;
+    	} finally {
+    		this.statementExecutionDepth--;
+    	}
     }
 
+    private ResultSetInternalMethods invokeStatementInterceptorsPre(String sql,
+			Statement interceptedStatement) throws SQLException {
+		ResultSetInternalMethods previousResultSet = null;
+
+		Iterator interceptors = this.statementInterceptors.iterator();
+
+		while (interceptors.hasNext()) {
+			StatementInterceptor interceptor = ((StatementInterceptor) interceptors
+					.next());
+
+			boolean executeTopLevelOnly = interceptor.executeTopLevelOnly();
+			boolean shouldExecute = (executeTopLevelOnly && this.statementExecutionDepth == 1)
+					|| (!executeTopLevelOnly);
+
+			if (shouldExecute) {
+				String sqlToInterceptor = sql;
+
+				if (interceptedStatement instanceof PreparedStatement) {
+					sqlToInterceptor = ((PreparedStatement) interceptedStatement)
+							.asSql();
+				}
+
+				ResultSetInternalMethods interceptedResultSet = interceptor
+						.preProcess(sqlToInterceptor, interceptedStatement,
+								this.connection);
+
+				if (interceptedResultSet != null) {
+					previousResultSet = interceptedResultSet;
+				}
+			}
+		}
+
+		return previousResultSet;
+	}
+
+	private ResultSetInternalMethods invokeStatementInterceptorsPost(
+			String sql, Statement interceptedStatement,
+			ResultSetInternalMethods originalResultSet) throws SQLException {
+		Iterator interceptors = this.statementInterceptors.iterator();
+
+		while (interceptors.hasNext()) {
+			StatementInterceptor interceptor = ((StatementInterceptor) interceptors
+					.next());
+
+			boolean executeTopLevelOnly = interceptor.executeTopLevelOnly();
+			boolean shouldExecute = (executeTopLevelOnly && this.statementExecutionDepth == 1)
+					|| (!executeTopLevelOnly);
+
+			if (shouldExecute) {
+				String sqlToInterceptor = sql;
+
+				if (interceptedStatement instanceof PreparedStatement) {
+					sqlToInterceptor = ((PreparedStatement) interceptedStatement)
+							.asSql();
+				}
+
+				ResultSetInternalMethods interceptedResultSet = interceptor
+						.postProcess(sqlToInterceptor, interceptedStatement,
+								originalResultSet, this.connection);
+
+				if (interceptedResultSet != null) {
+					originalResultSet = interceptedResultSet;
+				}
+			}
+		}
+
+		return originalResultSet;
+	}
+
+	private void calculateSlowQueryThreshold() {
+		this.slowQueryThreshold = this.connection.getSlowQueryThresholdMillis();
+
+		if (this.connection.getUseNanosForElapsedTime()) {
+			long nanosThreshold = this.connection.getSlowQueryThresholdNanos();
+
+			if (nanosThreshold != 0) {
+				this.slowQueryThreshold = nanosThreshold;
+			} else {
+				this.slowQueryThreshold *= 1000000; // 1 million millis in a nano
+			}
+		}
+	}
+
+	protected long getCurrentTimeNanosOrMillis() {
+		if (this.useNanosForElapsedTime) {
+			return Util.getCurrentTimeNanosOrMillis();
+		}
+
+		return System.currentTimeMillis();
+	}
+
     /**
      * Returns the host this IO is connected to
      *
@@ -1867,7 +2286,7 @@
                 return false;
             }
 
-            // newer than major  
+            // newer than major
             return true;
         }
 
@@ -1910,7 +2329,8 @@
             int count = in.read(b, off + n, len - n);
 
             if (count < 0) {
-                throw new EOFException();
+                throw new EOFException(Messages.getString("MysqlIO.EOF",
+                		new Object[] {new Integer(len), new Integer(n)}));
             }
 
             n += count;
@@ -1919,6 +2339,27 @@
         return n;
     }
 
+    private final long skipFully(InputStream in, long len) throws IOException {
+    	if (len < 0) {
+    		throw new IOException("Negative skip length not allowed");
+    	}
+
+    	long n = 0;
+
+    	while (n < len) {
+    		long count = in.skip(len - n);
+
+    		if (count < 0) {
+    			throw new EOFException(Messages.getString("MysqlIO.EOF",
+                		new Object[] {new Long(len), new Long(n)}));
+    		}
+
+    		n += count;
+    	}
+
+    	return n;
+    }
+
     /**
      * Reads one result set off of the wire, if the result is actually an
      * update count, creates an update-count only result set.
@@ -1939,11 +2380,11 @@
      *
      * @throws SQLException if an error occurs while reading the rows
      */
-    private final ResultSet readResultsForQueryOrUpdate(
-        Statement callingStatement, int maxRows, int resultSetType,
+    private final ResultSetImpl readResultsForQueryOrUpdate(
+        StatementImpl callingStatement, int maxRows, int resultSetType,
         int resultSetConcurrency, boolean streamResults, String catalog,
         Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount,
-        boolean unpackFieldInfo) throws SQLException {
+        Field[] metadataFromCache) throws SQLException {
         long columnCount = resultPacket.readFieldLength();
 
         if (columnCount == 0) {
@@ -1967,9 +2408,10 @@
 
             return sendFileToServer(callingStatement, fileName);
         } else {
-            com.mysql.jdbc.ResultSet results = getResultSet(callingStatement,
+            com.mysql.jdbc.ResultSetImpl results = getResultSet(callingStatement,
                     columnCount, maxRows, resultSetType, resultSetConcurrency,
-                    streamResults, catalog, isBinaryEncoded, unpackFieldInfo);
+                    streamResults, catalog, isBinaryEncoded,
+                    metadataFromCache);
 
             return results;
         }
@@ -1979,17 +2421,17 @@
         return ((((a) + (l)) - 1) & ~((l) - 1));
     }
 
-    private com.mysql.jdbc.ResultSet buildResultSetWithRows(
-        Statement callingStatement, String catalog,
+    private com.mysql.jdbc.ResultSetImpl buildResultSetWithRows(
+        StatementImpl callingStatement, String catalog,
         com.mysql.jdbc.Field[] fields, RowData rows, int resultSetType,
         int resultSetConcurrency, boolean isBinaryEncoded)
         throws SQLException {
-        ResultSet rs = null;
+        ResultSetImpl rs = null;
 
         switch (resultSetConcurrency) {
         case java.sql.ResultSet.CONCUR_READ_ONLY:
-            rs = new com.mysql.jdbc.ResultSet(catalog, fields, rows,
-                    this.connection, callingStatement);
+            rs = com.mysql.jdbc.ResultSetImpl.getInstance(catalog, fields, rows,
+                    this.connection, callingStatement, false);
 
             if (isBinaryEncoded) {
                 rs.setBinaryEncoded();
@@ -1998,14 +2440,14 @@
             break;
 
         case java.sql.ResultSet.CONCUR_UPDATABLE:
-            rs = new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
-                    this.connection, callingStatement);
+            rs = com.mysql.jdbc.ResultSetImpl.getInstance(catalog, fields, rows,
+                    this.connection, callingStatement, true);
 
             break;
 
         default:
-            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
-                this.connection, callingStatement);
+            return com.mysql.jdbc.ResultSetImpl.getInstance(catalog, fields, rows,
+                this.connection, callingStatement, false);
         }
 
         rs.setResultSetType(resultSetType);
@@ -2014,8 +2456,8 @@
         return rs;
     }
 
-    private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
-        Statement callingStatement, Buffer resultPacket)
+    private com.mysql.jdbc.ResultSetImpl buildResultSetWithUpdates(
+        StatementImpl callingStatement, Buffer resultPacket)
         throws SQLException {
         long updateCount = -1;
         long updateID = -1;
@@ -2058,19 +2500,21 @@
                  +ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
         }
 
-        ResultSet updateRs = new com.mysql.jdbc.ResultSet(updateCount,
+        ResultSetInternalMethods updateRs = com.mysql.jdbc.ResultSetImpl.getInstance(updateCount,
                 updateID, this.connection, callingStatement);
 
         if (info != null) {
-            updateRs.setServerInfo(info);
+            ((com.mysql.jdbc.ResultSetImpl)updateRs).setServerInfo(info);
         }
 
-        return updateRs;
+        return (com.mysql.jdbc.ResultSetImpl)updateRs;
     }
 
     private void checkForOutstandingStreamingData() throws SQLException {
         if (this.streamingData != null) {
-            if (!this.connection.getClobberStreamingResults()) {
+        	boolean shouldClobber = this.connection.getClobberStreamingResults();
+
+            if (!shouldClobber) {
                 throw SQLError.createSQLException(Messages.getString("MysqlIO.39") //$NON-NLS-1$
                      +this.streamingData +
                     Messages.getString("MysqlIO.40") //$NON-NLS-1$
@@ -2156,7 +2600,7 @@
             }
         }
     }
-    
+
     private SocketFactory createSocketFactory() throws SQLException {
         try {
             if (this.socketFactoryClassName == null) {
@@ -2240,31 +2684,33 @@
 
         this.packetDebugRingBuffer.addLast(packetDump);
     }
-    
+
     private RowData readSingleRowSet(long columnCount, int maxRows,
         int resultSetConcurrency, boolean isBinaryEncoded, Field[] fields)
         throws SQLException {
         RowData rowData;
         ArrayList rows = new ArrayList();
 
+        boolean useBufferRowExplicit = useBufferRowExplicit(fields);
+
         // Now read the data
-        Object rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
-                resultSetConcurrency);
-        
+        ResultSetRow row = nextRow(fields, (int) columnCount, isBinaryEncoded,
+                resultSetConcurrency, false, useBufferRowExplicit, false, null);
+
         int rowCount = 0;
 
-        if (rowBytes != null) {
-            rows.add(rowBytes);
+        if (row != null) {
+            rows.add(row);
             rowCount = 1;
         }
 
-        while (rowBytes != null) {
-            rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
-                    resultSetConcurrency);
+        while (row != null) {
+        	row = nextRow(fields, (int) columnCount, isBinaryEncoded,
+                    resultSetConcurrency, false, useBufferRowExplicit, false, null);
 
-            if (rowBytes != null) {
+            if (row != null) {
             	if ((maxRows == -1) || (rowCount < maxRows)) {
-            		rows.add(rowBytes);
+            		rows.add(row);
             		rowCount++;
             	}
             }
@@ -2275,13 +2721,31 @@
         return rowData;
     }
 
-    /**
+    public static boolean useBufferRowExplicit(Field[] fields) {
+    	if (fields == null) {
+    		return false;
+    	}
+
+		for (int i = 0; i < fields.length; i++) {
+			switch (fields[i].getSQLType()) {
+			case Types.BLOB:
+			case Types.CLOB:
+			case Types.LONGVARBINARY:
+			case Types.LONGVARCHAR:
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	/**
      * Don't hold on to overly-large packets
      */
     private void reclaimLargeReusablePacket() {
         if ((this.reusablePacket != null) &&
                 (this.reusablePacket.getCapacity() > 1048576)) {
-            this.reusablePacket = new Buffer(this.connection.getNetBufferLength());
+            this.reusablePacket = new Buffer(INITIAL_PACKET_SIZE);
         }
     }
 
@@ -2295,38 +2759,47 @@
      * @throws SQLException DOCUMENT ME!
      * @throws SQLException DOCUMENT ME!
      */
-    private final Buffer reuseAndReadPacket(Buffer reuse)
+    private final Buffer reuseAndReadPacket(Buffer reuse) throws SQLException {
+    	return reuseAndReadPacket(reuse, -1);
+    }
+
+    private final Buffer reuseAndReadPacket(Buffer reuse, int existingPacketLength)
         throws SQLException {
-        
+
     	try {
     		reuse.setWasMultiPacket(false);
-    		
-    		int lengthRead = readFully(this.mysqlInput,
-    				this.packetHeaderBuf, 0, 4);
-    		
-    		if (lengthRead < 4) {
-    			forceClose();
-    			throw new IOException(Messages.getString("MysqlIO.43")); //$NON-NLS-1$
+    		int packetLength = 0;
+
+    		if (existingPacketLength == -1) {
+	    		int lengthRead = readFully(this.mysqlInput,
+	    				this.packetHeaderBuf, 0, 4);
+
+	    		if (lengthRead < 4) {
+	    			forceClose();
+	    			throw new IOException(Messages.getString("MysqlIO.43")); //$NON-NLS-1$
+	    		}
+
+	    		packetLength = (this.packetHeaderBuf[0] & 0xff) +
+	    		((this.packetHeaderBuf[1] & 0xff) << 8) +
+	    		((this.packetHeaderBuf[2] & 0xff) << 16);
+    		} else {
+    			packetLength = existingPacketLength;
     		}
-    		
-    		int packetLength = (this.packetHeaderBuf[0] & 0xff) +
-    		((this.packetHeaderBuf[1] & 0xff) << 8) +
-    		((this.packetHeaderBuf[2] & 0xff) << 16);
-    		
+
     		if (this.traceProtocol) {
     			StringBuffer traceMessageBuf = new StringBuffer();
-    			
+
     			traceMessageBuf.append(Messages.getString("MysqlIO.44")); //$NON-NLS-1$
     			traceMessageBuf.append(packetLength);
     			traceMessageBuf.append(Messages.getString("MysqlIO.45")); //$NON-NLS-1$
     			traceMessageBuf.append(StringUtils.dumpAsHex(
     					this.packetHeaderBuf, 4));
-    			
+
     			this.connection.getLog().logTrace(traceMessageBuf.toString());
     		}
-    		
+
     		byte multiPacketSeq = this.packetHeaderBuf[3];
-    		
+
     		if (!this.packetSequenceReset) {
     			if (this.enablePacketDebug && this.checkPacketSequence) {
     				checkPacketSequencing(multiPacketSeq);
@@ -2334,12 +2807,12 @@
     		} else {
     			this.packetSequenceReset = false;
     		}
-    		
+
     		this.readPacketSequence = multiPacketSeq;
-    		
+
     		// Set the Buffer to it's original state
     		reuse.setPosition(0);
-    		
+
     		// Do we need to re-alloc the byte buffer?
     		//
     		// Note: We actually check the length of the buffer,
@@ -2349,170 +2822,55 @@
     		if (reuse.getByteBuffer().length <= packetLength) {
     			reuse.setByteBuffer(new byte[packetLength + 1]);
     		}
-    		
+
     		// Set the new length
     		reuse.setBufLength(packetLength);
-    		
+
     		// Read the data from the server
     		int numBytesRead = readFully(this.mysqlInput,
     				reuse.getByteBuffer(), 0, packetLength);
-    		
+
     		if (numBytesRead != packetLength) {
     			throw new IOException("Short read, expected " +
     					packetLength + " bytes, only read " + numBytesRead);
     		}
-    		
+
     		if (this.traceProtocol) {
     			StringBuffer traceMessageBuf = new StringBuffer();
-    			
+
     			traceMessageBuf.append(Messages.getString("MysqlIO.46")); //$NON-NLS-1$
     			traceMessageBuf.append(getPacketDumpToLog(reuse,
     					packetLength));
-    			
+
     			this.connection.getLog().logTrace(traceMessageBuf.toString());
     		}
-    		
+
     		if (this.enablePacketDebug) {
     			enqueuePacketForDebugging(false, true, 0,
     					this.packetHeaderBuf, reuse);
     		}
-    		
+
     		boolean isMultiPacket = false;
-    		
+
     		if (packetLength == this.maxThreeBytes) {
     			reuse.setPosition(this.maxThreeBytes);
-    			
+
     			int packetEndPoint = packetLength;
-    			
+
     			// it's multi-packet
     			isMultiPacket = true;
-    			
-    			lengthRead = readFully(this.mysqlInput,
-    					this.packetHeaderBuf = new byte[4], 0, 4);
-    			
-    			if (lengthRead < 4) {
-    				forceClose();
-    				throw new IOException(Messages.getString("MysqlIO.47")); //$NON-NLS-1$
-    			}
-    			
-    			packetLength = (this.packetHeaderBuf[0] & 0xff) +
-    				((this.packetHeaderBuf[1] & 0xff) << 8) +
-    				((this.packetHeaderBuf[2] & 0xff) << 16);
-    			
-    			Buffer multiPacket = new Buffer(packetLength);
-    			boolean firstMultiPkt = true;
-    			
-    			while (true) {
-    				if (!firstMultiPkt) {
-    					lengthRead = readFully(this.mysqlInput,
-    							this.packetHeaderBuf = new byte[4], 0, 4);
-    					
-    					if (lengthRead < 4) {
-    						forceClose();
-    						throw new IOException(Messages.getString(
-    						"MysqlIO.48")); //$NON-NLS-1$
-    					}
-    					
-    					packetLength = (this.packetHeaderBuf[0] & 0xff) +
-    					((this.packetHeaderBuf[1] & 0xff) << 8) +
-    					((this.packetHeaderBuf[2] & 0xff) << 16);
-    				} else {
-    					firstMultiPkt = false;
-    				}
-    				
-    				if (!this.useNewLargePackets && (packetLength == 1)) {
-    					clearInputStream();
-    					
-    					break;
-    				} else if (packetLength < this.maxThreeBytes) {
-    					byte newPacketSeq = this.packetHeaderBuf[3];
-    					
-    					if (newPacketSeq != (multiPacketSeq + 1)) {
-    						throw new IOException(Messages.getString(
-    						"MysqlIO.49")); //$NON-NLS-1$
-    					}
-    					
-    					multiPacketSeq = newPacketSeq;
-    					
-    					// Set the Buffer to it's original state
-    					multiPacket.setPosition(0);
-    					
-    					// Set the new length
-    					multiPacket.setBufLength(packetLength);
-    					
-    					// Read the data from the server
-    					byte[] byteBuf = multiPacket.getByteBuffer();
-    					int lengthToWrite = packetLength;
-    					
-    					int bytesRead = readFully(this.mysqlInput, byteBuf,
-    							0, packetLength);
-    					
-    					if (bytesRead != lengthToWrite) {
-    						throw new CommunicationsException(this.connection,
-    								this.lastPacketSentTimeMs,
-    								SQLError.createSQLException(Messages.getString(
-    								"MysqlIO.50") //$NON-NLS-1$
-    								+lengthToWrite +
-    								Messages.getString("MysqlIO.51") +
-    								bytesRead //$NON-NLS-1$
-    								+".")); //$NON-NLS-1$
-    					}
-    					
-    					reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
-    					
-    					packetEndPoint += lengthToWrite;
-    					
-    					break; // end of multipacket sequence
-    				}
-    				
-    				byte newPacketSeq = this.packetHeaderBuf[3];
-    				
-    				if (newPacketSeq != (multiPacketSeq + 1)) {
-    					throw new IOException(Messages.getString(
-    					"MysqlIO.53")); //$NON-NLS-1$
-    				}
-    				
-    				multiPacketSeq = newPacketSeq;
-    				
-    				// Set the Buffer to it's original state
-    				multiPacket.setPosition(0);
-    				
-    				// Set the new length
-    				multiPacket.setBufLength(packetLength);
-    				
-    				// Read the data from the server
-    				byte[] byteBuf = multiPacket.getByteBuffer();
-    				int lengthToWrite = packetLength;
-    				
-    				int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
-    						packetLength);
-    				
-    				if (bytesRead != lengthToWrite) {
-    					throw new CommunicationsException(this.connection,
-    							this.lastPacketSentTimeMs,
-    							SQLError.createSQLException(Messages.getString(
-    							"MysqlIO.54") //$NON-NLS-1$
-    							+lengthToWrite +
-    							Messages.getString("MysqlIO.55") //$NON-NLS-1$
-    							+bytesRead + ".")); //$NON-NLS-1$
-    				}
-    				
-    				reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
-    				
-    				packetEndPoint += lengthToWrite;
-    			}
-    			
-    			reuse.setPosition(0);
-    			reuse.setWasMultiPacket(true);
+
+    			packetLength = readRemainingMultiPackets(reuse, multiPacketSeq,
+						packetEndPoint);
     		}
-    		
+
     		if (!isMultiPacket) {
     			reuse.getByteBuffer()[packetLength] = 0; // Null-termination
     		}
-    		
+
     		return reuse;
     	} catch (IOException ioEx) {
-    		throw new CommunicationsException(this.connection,
+    		throw SQLError.createCommunicationsException(this.connection,
     				this.lastPacketSentTimeMs, ioEx);
     	} catch (OutOfMemoryError oom) {
     		try {
@@ -2526,24 +2884,148 @@
     			}
     		}
     	}
-    	
+
     }
 
+	private int readRemainingMultiPackets(Buffer reuse, byte multiPacketSeq,
+			int packetEndPoint) throws IOException, SQLException {
+		int lengthRead;
+		int packetLength;
+		lengthRead = readFully(this.mysqlInput,
+				this.packetHeaderBuf, 0, 4);
+
+		if (lengthRead < 4) {
+			forceClose();
+			throw new IOException(Messages.getString("MysqlIO.47")); //$NON-NLS-1$
+		}
+
+		packetLength = (this.packetHeaderBuf[0] & 0xff) +
+			((this.packetHeaderBuf[1] & 0xff) << 8) +
+			((this.packetHeaderBuf[2] & 0xff) << 16);
+
+		Buffer multiPacket = new Buffer(packetLength);
+		boolean firstMultiPkt = true;
+
+		while (true) {
+			if (!firstMultiPkt) {
+				lengthRead = readFully(this.mysqlInput,
+						this.packetHeaderBuf, 0, 4);
+
+				if (lengthRead < 4) {
+					forceClose();
+					throw new IOException(Messages.getString(
+					"MysqlIO.48")); //$NON-NLS-1$
+				}
+
+				packetLength = (this.packetHeaderBuf[0] & 0xff) +
+				((this.packetHeaderBuf[1] & 0xff) << 8) +
+				((this.packetHeaderBuf[2] & 0xff) << 16);
+			} else {
+				firstMultiPkt = false;
+			}
+
+			if (!this.useNewLargePackets && (packetLength == 1)) {
+				clearInputStream();
+
+				break;
+			} else if (packetLength < this.maxThreeBytes) {
+				byte newPacketSeq = this.packetHeaderBuf[3];
+
+				if (newPacketSeq != (multiPacketSeq + 1)) {
+					throw new IOException(Messages.getString(
+					"MysqlIO.49")); //$NON-NLS-1$
+				}
+
+				multiPacketSeq = newPacketSeq;
+
+				// Set the Buffer to it's original state
+				multiPacket.setPosition(0);
+
+				// Set the new length
+				multiPacket.setBufLength(packetLength);
+
+				// Read the data from the server
+				byte[] byteBuf = multiPacket.getByteBuffer();
+				int lengthToWrite = packetLength;
+
+				int bytesRead = readFully(this.mysqlInput, byteBuf,
+						0, packetLength);
+
+				if (bytesRead != lengthToWrite) {
+					throw SQLError.createCommunicationsException(this.connection,
+							this.lastPacketSentTimeMs,
+							SQLError.createSQLException(Messages.getString(
+							"MysqlIO.50") //$NON-NLS-1$
+							+lengthToWrite +
+							Messages.getString("MysqlIO.51") +
+							bytesRead //$NON-NLS-1$
+							+".")); //$NON-NLS-1$
+				}
+
+				reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
+
+				packetEndPoint += lengthToWrite;
+
+				break; // end of multipacket sequence
+			}
+
+			byte newPacketSeq = this.packetHeaderBuf[3];
+
+			if (newPacketSeq != (multiPacketSeq + 1)) {
+				throw new IOException(Messages.getString(
+				"MysqlIO.53")); //$NON-NLS-1$
+			}
+
+			multiPacketSeq = newPacketSeq;
+
+			// Set the Buffer to it's original state
+			multiPacket.setPosition(0);
+
+			// Set the new length
+			multiPacket.setBufLength(packetLength);
+
+			// Read the data from the server
+			byte[] byteBuf = multiPacket.getByteBuffer();
+			int lengthToWrite = packetLength;
+
+			int bytesRead = readFully(this.mysqlInput, byteBuf, 0,
+					packetLength);
+
+			if (bytesRead != lengthToWrite) {
+				throw SQLError.createCommunicationsException(this.connection,
+						this.lastPacketSentTimeMs,
+						SQLError.createSQLException(Messages.getString(
+						"MysqlIO.54") //$NON-NLS-1$
+						+lengthToWrite +
+						Messages.getString("MysqlIO.55") //$NON-NLS-1$
+						+bytesRead + ".")); //$NON-NLS-1$
+			}
+
+			reuse.writeBytesNoNull(byteBuf, 0, lengthToWrite);
+
+			packetEndPoint += lengthToWrite;
+		}
+
+		reuse.setPosition(0);
+		reuse.setWasMultiPacket(true);
+		return packetLength;
+	}
+
     /**
          * @param multiPacketSeq
          * @throws CommunicationsException
          */
     private void checkPacketSequencing(byte multiPacketSeq)
-        throws CommunicationsException {
+        throws SQLException {
         if ((multiPacketSeq == -128) && (this.readPacketSequence != 127)) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs,
                 new IOException("Packets out of order, expected packet # -128, but received packet # " +
                     multiPacketSeq));
         }
 
         if ((this.readPacketSequence == -1) && (multiPacketSeq != 0)) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs,
                 new IOException("Packets out of order, expected packet # -1, but received packet # " +
                     multiPacketSeq));
@@ -2551,7 +3033,7 @@
 
         if ((multiPacketSeq != -128) && (this.readPacketSequence != -1) &&
                 (multiPacketSeq != (this.readPacketSequence + 1))) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs,
                 new IOException("Packets out of order, expected packet # " +
                     (this.readPacketSequence + 1) + ", but received packet # " +
@@ -2561,22 +3043,22 @@
 
     void enableMultiQueries() throws SQLException {
     	Buffer buf = getSharedSendPacket();
-    	
+
     	buf.clear();
     	buf.writeByte((byte)MysqlDefs.COM_SET_OPTION);
     	buf.writeInt(0);
     	sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null);
     }
-    
+
     void disableMultiQueries() throws SQLException {
     	Buffer buf = getSharedSendPacket();
-    	
+
     	buf.clear();
     	buf.writeByte((byte)MysqlDefs.COM_SET_OPTION);
     	buf.writeInt(1);
     	sendCommand(MysqlDefs.COM_SET_OPTION, null, buf, false, null);
     }
-    
+
     private final void send(Buffer packet, int packetLen)
         throws SQLException {
         try {
@@ -2631,10 +3113,10 @@
                     }
                 }
 
-                
+
                 this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
                 		packetLen);
-                this.mysqlOutput.flush();   
+                this.mysqlOutput.flush();
             }
 
             if (this.enablePacketDebug) {
@@ -2642,14 +3124,14 @@
                     this.packetHeaderBuf, packet);
             }
 
-            // 
+            //
             // Don't hold on to large packets
             //
             if (packet == this.sharedSendPacket) {
                 reclaimLargeSharedSendPacket();
             }
         } catch (IOException ioEx) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, ioEx);
         }
     }
@@ -2664,9 +3146,9 @@
      *
      * @throws SQLException DOCUMENT ME!
      */
-    private final ResultSet sendFileToServer(Statement callingStatement,
+    private final ResultSetImpl sendFileToServer(StatementImpl callingStatement,
         String fileName) throws SQLException {
-    	
+
         Buffer filePacket = (this.loadFileBufRef == null) ? null
                                                           : (Buffer) (this.loadFileBufRef.get());
 
@@ -2674,12 +3156,12 @@
                 (HEADER_LENGTH * 3),
                 alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096) -
                 (HEADER_LENGTH * 3));
-        
+
         int oneMeg = 1024 * 1024;
-        
-        int smallerPacketSizeAligned = Math.min(oneMeg - (HEADER_LENGTH * 3), 
+
+        int smallerPacketSizeAligned = Math.min(oneMeg - (HEADER_LENGTH * 3),
         		alignPacketSize(oneMeg - 16, 4096) - (HEADER_LENGTH * 3));
-        
+
         int packetLength = Math.min(smallerPacketSizeAligned, bigPacketLength);
 
         if (filePacket == null) {
@@ -2687,11 +3169,11 @@
         		filePacket = new Buffer((packetLength + HEADER_LENGTH));
         		this.loadFileBufRef = new SoftReference(filePacket);
         	} catch (OutOfMemoryError oom) {
-        		throw SQLError.createSQLException("Could not allocate packet of " + packetLength 
-        				+ " bytes required for LOAD DATA LOCAL INFILE operation." 
+        		throw SQLError.createSQLException("Could not allocate packet of " + packetLength
+        				+ " bytes required for LOAD DATA LOCAL INFILE operation."
 						+ " Try increasing max heap allocation for JVM or decreasing server variable "
 						+ "'max_allowed_packet'", SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE);
-				
+
         	}
         }
 
@@ -2703,11 +3185,25 @@
         BufferedInputStream fileIn = null;
 
         try {
-            if (!this.connection.getAllowUrlInLocalInfile()) {
+        	if (!this.connection.getAllowLoadLocalInfile()) {
+        		throw SQLError.createSQLException(
+        				Messages.getString("MysqlIO.LoadDataLocalNotAllowed"),
+        				SQLError.SQL_STATE_GENERAL_ERROR);
+        	}
+
+        	InputStream hookedStream = null;
+
+        	if (callingStatement != null) {
+        	    hookedStream = callingStatement.getLocalInfileInputStream();
+        	}
+
+        	if (hookedStream != null) {
+        	    fileIn = new BufferedInputStream(hookedStream);
+        	} else if (!this.connection.getAllowUrlInLocalInfile()) {
                 fileIn = new BufferedInputStream(new FileInputStream(fileName));
             } else {
                 // First look for ':'
-                if (fileName.indexOf(":") != -1) {
+                if (fileName.indexOf(':') != -1) {
                     try {
                         URL urlFromFileName = new URL(fileName);
                         fileIn = new BufferedInputStream(urlFromFileName.openStream());
@@ -2801,15 +3297,23 @@
             // exception chain and let someone higher up decide
             // what to do (barf, reconnect, etc).
             resultPacket = reuseAndReadPacket(this.reusablePacket);
-            statusCode = resultPacket.readByte();
         } catch (SQLException sqlEx) {
             // Don't wrap SQL Exceptions
             throw sqlEx;
         } catch (Exception fallThru) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, fallThru);
         }
 
+        checkErrorPacket(resultPacket);
+
+        return resultPacket;
+    }
+
+    private void checkErrorPacket(Buffer resultPacket) throws SQLException {
+
+        int statusCode = resultPacket.readByte();
+
         // Error handling
         if (statusCode == (byte) 0xff) {
             String serverErrorMessage;
@@ -2820,10 +3324,10 @@
 
                 String xOpen = null;
 
-                serverErrorMessage = 
+                serverErrorMessage =
                 	resultPacket.readString(this.connection.getErrorMessageEncoding());
 
-                if (serverErrorMessage.startsWith("#")) { //$NON-NLS-1$
+                if (serverErrorMessage.charAt(0) == '#') { //$NON-NLS-1$
 
                     // we have an SQLState
                     if (serverErrorMessage.length() > 6) {
@@ -2863,9 +3367,11 @@
                         errorBuf.append("\""); //$NON-NLS-1$
                     }
                 }
-                
+
+                appendInnodbStatusInformation(xOpen, errorBuf);
+
                 if (xOpen != null && xOpen.startsWith("22")) {
-                	throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0); 
+                	throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0);
                 } else {
                 	throw SQLError.createSQLException(errorBuf.toString(), xOpen, errno);
                 }
@@ -2892,10 +3398,43 @@
                     SQLError.SQL_STATE_GENERAL_ERROR) + ", " //$NON-NLS-1$
                  +errorBuf.toString(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
         }
-
-        return resultPacket;
     }
 
+    private void appendInnodbStatusInformation(String xOpen,
+			StringBuffer errorBuf) throws SQLException {
+		if (this.connection.getIncludeInnodbStatusInDeadlockExceptions()
+				&& xOpen != null
+				&& (xOpen.startsWith("40") || xOpen.startsWith("41"))
+				&& this.streamingData == null) {
+			ResultSet rs = null;
+
+			try {
+				rs = sqlQueryDirect(null, "SHOW ENGINE INNODB STATUS",
+						this.connection.getEncoding(), null, -1,
+						ResultSet.TYPE_FORWARD_ONLY,
+						ResultSet.CONCUR_READ_ONLY, false, this.connection
+								.getCatalog(), null);
+
+				if (rs.next()) {
+					errorBuf.append("\n\n");
+					errorBuf.append(rs.getString(1));
+				} else {
+					errorBuf.append(Messages
+							.getString("MysqlIO.NoInnoDBStatusFound"));
+				}
+			} catch (Exception ex) {
+				errorBuf.append(Messages
+						.getString("MysqlIO.InnoDBStatusFailed"));
+				errorBuf.append("\n\n");
+				errorBuf.append(Util.stackTraceToString(ex));
+			} finally {
+				if (rs != null) {
+					rs.close();
+				}
+			}
+		}
+	}
+
     /**
      * Sends a large packet to the server as a series of smaller packets
      *
@@ -3009,7 +3548,7 @@
                 this.mysqlOutput.flush();
             }
         } catch (IOException ioEx) {
-            throw new CommunicationsException(this.connection,
+            throw SQLError.createCommunicationsException(this.connection,
                 this.lastPacketSentTimeMs, ioEx);
         }
     }
@@ -3017,17 +3556,17 @@
     private void reclaimLargeSharedSendPacket() {
         if ((this.sharedSendPacket != null) &&
                 (this.sharedSendPacket.getCapacity() > 1048576)) {
-            this.sharedSendPacket = new Buffer(this.connection.getNetBufferLength());
+            this.sharedSendPacket = new Buffer(INITIAL_PACKET_SIZE);
         }
     }
 
     boolean hadWarnings() {
     	return this.hadWarnings;
     }
-    
+
     void scanForAndThrowDataTruncation() throws SQLException {
         if ((this.streamingData == null) && versionMeetsMinimum(4, 1, 0) &&
-                this.connection.getJdbcCompliantTruncation()) {
+                this.connection.getJdbcCompliantTruncation() && this.warningCount > 0) {
             SQLError.convertShowWarningsToSQLWarnings(this.connection,
                 this.warningCount, true);
         }
@@ -3077,18 +3616,18 @@
         }
 
         // User/Password data
-        packet.writeString(user, "Cp1252", this.connection);
+        packet.writeString(user, CODE_PAGE_1252, this.connection);
 
         if (password.length() != 0) {
             /* Prepare false scramble  */
-            packet.writeString(FALSE_SCRAMBLE, "Cp1252", this.connection);
+            packet.writeString(FALSE_SCRAMBLE, CODE_PAGE_1252, this.connection);
         } else {
             /* For empty password*/
-            packet.writeString("", "Cp1252", this.connection); //$NON-NLS-1$
+            packet.writeString("", CODE_PAGE_1252, this.connection); //$NON-NLS-1$
         }
 
         if (this.useConnectWithDb) {
-            packet.writeString(database, "Cp1252", this.connection);
+            packet.writeString(database, CODE_PAGE_1252, this.connection);
         }
 
         send(packet, packet.getPosition());
@@ -3166,7 +3705,7 @@
                                     mysqlScrambleBuff), password);
 
                         Buffer packet2 = new Buffer(packLength);
-                        packet2.writeString(scrambledPassword, "Cp1252", this.connection);
+                        packet2.writeString(scrambledPassword, CODE_PAGE_1252, this.connection);
                         this.packetSequence++;
 
                         send(packet2, 24);
@@ -3223,10 +3762,10 @@
                     packet.writeLong(this.clientParam);
                     packet.writeLong(this.maxThreeBytes);
 
-                    // charset, JDBC will connect as 'latin1',
+                    // charset, JDBC will connect as 'utf8',
                     // and use 'SET NAMES' to change to the desired
                     // charset after the connection is established.
-                    packet.writeByte((byte) 8);
+                    packet.writeByte((byte) UTF8_CHARSET_INDEX);
 
                     // Set of bytes reserved for future use.
                     packet.writeBytesNoNull(new byte[23]);
@@ -3241,7 +3780,7 @@
         }
 
         // User/Password data
-        packet.writeString(user);
+        packet.writeString(user, "utf-8", this.connection);
 
         if (password.length() != 0) {
             packet.writeByte((byte) 0x14);
@@ -3259,7 +3798,7 @@
         }
 
         if (this.useConnectWithDb) {
-            packet.writeString(database);
+            packet.writeString(database, "utf-8", this.connection);
         }
 
         send(packet, packet.getPosition());
@@ -3296,11 +3835,11 @@
      *
      * @throws SQLException DOCUMENT ME!
      */
-    private final Object[] unpackBinaryResultSetRow(Field[] fields,
+    private final ResultSetRow unpackBinaryResultSetRow(Field[] fields,
         Buffer binaryData, int resultSetConcurrency) throws SQLException {
         int numFields = fields.length;
 
-        Object[] unpackedRowData = new Object[numFields];
+        byte[][] unpackedRowData = new byte[numFields][];
 
         //
         // Unpack the null bitmask, first
@@ -3317,25 +3856,25 @@
 
         int nullMaskPos = 0;
         int bit = 4; // first two bits are reserved for future use
-       
+
         //
         // TODO: Benchmark if moving check for updatable result
         //       sets out of loop is worthwhile?
         //
-        
+
         for (int i = 0; i < numFields; i++) {
             if ((nullBitMask[nullMaskPos] & bit) != 0) {
                 unpackedRowData[i] = null;
             } else {
-            	if (resultSetConcurrency != ResultSet.CONCUR_UPDATABLE) {
-            		extractNativeEncodedColumn(binaryData, fields, i, 
+            	if (resultSetConcurrency != ResultSetInternalMethods.CONCUR_UPDATABLE) {
+            		extractNativeEncodedColumn(binaryData, fields, i,
             				unpackedRowData);
             	} else {
-            		unpackNativeEncodedColumn(binaryData, fields, i, 
+            		unpackNativeEncodedColumn(binaryData, fields, i,
             				unpackedRowData);
-            	}   
+            	}
             }
-            
+
         	if (((bit <<= 1) & 255) == 0) {
         		bit = 1; /* To next byte */
 
@@ -3343,31 +3882,31 @@
         	}
         }
 
-        return unpackedRowData;
+        return new ByteArrayRow(unpackedRowData);
     }
 
-        
-    private final void extractNativeEncodedColumn(Buffer binaryData, 
-    		Field[] fields, int columnIndex, Object[] unpackedRowData) throws SQLException {
+
+    private final void extractNativeEncodedColumn(Buffer binaryData,
+    		Field[] fields, int columnIndex, byte[][] unpackedRowData) throws SQLException {
     	Field curField = fields[columnIndex];
-    	
+
     	switch (curField.getMysqlType()) {
     	case MysqlDefs.FIELD_TYPE_NULL:
     		break; // for dummy binds
-    	
+
     	case MysqlDefs.FIELD_TYPE_TINY:
 
     		unpackedRowData[columnIndex] = new byte[] {binaryData.readByte()};
     		break;
-    	
+
     	case MysqlDefs.FIELD_TYPE_SHORT:
     	case MysqlDefs.FIELD_TYPE_YEAR:
-    		
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(2);
     		break;
     	case MysqlDefs.FIELD_TYPE_LONG:
     	case MysqlDefs.FIELD_TYPE_INT24:
-    		
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(4);
     		break;
     	case MysqlDefs.FIELD_TYPE_LONGLONG:
@@ -3375,31 +3914,31 @@
     		unpackedRowData[columnIndex] = binaryData.getBytes(8);
     		break;
     	case MysqlDefs.FIELD_TYPE_FLOAT:
-    		
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(4);
-    		break;   	
+    		break;
     	case MysqlDefs.FIELD_TYPE_DOUBLE:
-    		
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(8);
     		break;
     	case MysqlDefs.FIELD_TYPE_TIME:
-    		
+
     		int length = (int) binaryData.readFieldLength();
-    	
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(length);
 
     		break;
     	case MysqlDefs.FIELD_TYPE_DATE:
-    		
+
     		length = (int) binaryData.readFieldLength();
-    	
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(length);
 
     		break;
     	case MysqlDefs.FIELD_TYPE_DATETIME:
     	case MysqlDefs.FIELD_TYPE_TIMESTAMP:
     		length = (int) binaryData.readFieldLength();
-    	
+
     		unpackedRowData[columnIndex] = binaryData.getBytes(length);
     		break;
     	case MysqlDefs.FIELD_TYPE_TINY_BLOB:
@@ -3413,11 +3952,11 @@
     	case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
     	case MysqlDefs.FIELD_TYPE_GEOMETRY:
     		unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
-    	
+
     		break;
     	case MysqlDefs.FIELD_TYPE_BIT:
     		unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
-    		
+
     		break;
     	default:
     		throw SQLError.createSQLException(Messages.getString("MysqlIO.97") //$NON-NLS-1$
@@ -3429,53 +3968,53 @@
     	}
     }
 
-    private final void unpackNativeEncodedColumn(Buffer binaryData, 
-    		Field[] fields, int columnIndex, Object[] unpackedRowData) 
+    private final void unpackNativeEncodedColumn(Buffer binaryData,
+    		Field[] fields, int columnIndex, byte[][] unpackedRowData)
     throws SQLException {
     	Field curField = fields[columnIndex];
-    	
+
     	switch (curField.getMysqlType()) {
     	case MysqlDefs.FIELD_TYPE_NULL:
     		break; // for dummy binds
-    		
+
     	case MysqlDefs.FIELD_TYPE_TINY:
-    		
+
     		byte tinyVal = binaryData.readByte();
-    		
-    		if (!curField.isUnsigned()) {			
+
+    		if (!curField.isUnsigned()) {
     			unpackedRowData[columnIndex] = String.valueOf(tinyVal)
-    			.getBytes();  			
+    			.getBytes();
     		} else {
     			short unsignedTinyVal = (short) (tinyVal & 0xff);
- 
+
     			unpackedRowData[columnIndex] = String.valueOf(unsignedTinyVal)
-    			.getBytes();   			
+    			.getBytes();
     		}
-    		
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_SHORT:
     	case MysqlDefs.FIELD_TYPE_YEAR:
-    		
+
     		short shortVal = (short) binaryData.readInt();
-    		
+
     		if (!curField.isUnsigned()) {
     			unpackedRowData[columnIndex] = String.valueOf(shortVal)
-    			.getBytes();	
+    			.getBytes();
     		} else {
     			int unsignedShortVal = shortVal & 0xffff;
 
     			unpackedRowData[columnIndex] = String.valueOf(unsignedShortVal)
-    			.getBytes();	
+    			.getBytes();
     		}
-    		
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_LONG:
     	case MysqlDefs.FIELD_TYPE_INT24:
-    		
+
     		int intVal = (int) binaryData.readLong();
-    		
+
     		if (!curField.isUnsigned()) {
     			unpackedRowData[columnIndex] = String.valueOf(intVal)
     			.getBytes();
@@ -3483,276 +4022,276 @@
     			long longVal = intVal & 0xffffffffL;
 
     			unpackedRowData[columnIndex] = String.valueOf(longVal)
-    			.getBytes();	
+    			.getBytes();
     		}
-    		
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_LONGLONG:
-    		
+
     		long longVal = binaryData.readLongLong();
-    		
+
     		if (!curField.isUnsigned()) {
     			unpackedRowData[columnIndex] = String.valueOf(longVal)
     			.getBytes();
     		} else {
-    			BigInteger asBigInteger = ResultSet.convertLongToUlong(longVal);
+    			BigInteger asBigInteger = ResultSetImpl.convertLongToUlong(longVal);
 
     			unpackedRowData[columnIndex] = asBigInteger.toString()
-    			.getBytes();	
+    			.getBytes();
     		}
-    		
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_FLOAT:
-    		
+
     		float floatVal = Float.intBitsToFloat(binaryData.readIntAsLong());
-    		
+
     		unpackedRowData[columnIndex] = String.valueOf(floatVal).getBytes();
 
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_DOUBLE:
-    		
+
     		double doubleVal = Double.longBitsToDouble(binaryData.readLongLong());
 
     		unpackedRowData[columnIndex] = String.valueOf(doubleVal).getBytes();
 
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_TIME:
-    		
+
     		int length = (int) binaryData.readFieldLength();
-    		
+
     		int hour = 0;
     		int minute = 0;
     		int seconds = 0;
-    		
+
     		if (length != 0) {
     			binaryData.readByte(); // skip tm->neg
     			binaryData.readLong(); // skip daysPart
     			hour = binaryData.readByte();
     			minute = binaryData.readByte();
     			seconds = binaryData.readByte();
-    			
+
     			if (length > 8) {
     				binaryData.readLong(); // ignore 'secondsPart'
     			}
     		}
-    		
-    		
+
+
     		byte[] timeAsBytes = new byte[8];
-    		
+
     		timeAsBytes[0] = (byte) Character.forDigit(hour / 10, 10);
     		timeAsBytes[1] = (byte) Character.forDigit(hour % 10, 10);
-    		
+
     		timeAsBytes[2] = (byte) ':';
-    		
+
     		timeAsBytes[3] = (byte) Character.forDigit(minute / 10,
     				10);
     		timeAsBytes[4] = (byte) Character.forDigit(minute % 10,
     				10);
-    		
+
     		timeAsBytes[5] = (byte) ':';
-    		
+
     		timeAsBytes[6] = (byte) Character.forDigit(seconds / 10,
     				10);
     		timeAsBytes[7] = (byte) Character.forDigit(seconds % 10,
     				10);
-    		
+
     		unpackedRowData[columnIndex] = timeAsBytes;
-    		
-    		
+
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_DATE:
     		length = (int) binaryData.readFieldLength();
-    		
+
     		int year = 0;
     		int month = 0;
     		int day = 0;
-    		
+
     		hour = 0;
     		minute = 0;
     		seconds = 0;
-    		
+
     		if (length != 0) {
     			year = binaryData.readInt();
     			month = binaryData.readByte();
     			day = binaryData.readByte();
     		}
-    		
+
     		if ((year == 0) && (month == 0) && (day == 0)) {
-    			if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(
+    			if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(
     					this.connection.getZeroDateTimeBehavior())) {
     				unpackedRowData[columnIndex] = null;
-    				
+
     				break;
-    			} else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(
+    			} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(
     					this.connection.getZeroDateTimeBehavior())) {
     				throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Date",
     						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
     			}
-    			
+
     			year = 1;
     			month = 1;
     			day = 1;
     		}
-    		
-    		
+
+
     		byte[] dateAsBytes = new byte[10];
-    		
+
     		dateAsBytes[0] = (byte) Character.forDigit(year / 1000,
     				10);
-    		
+
     		int after1000 = year % 1000;
-    		
+
     		dateAsBytes[1] = (byte) Character.forDigit(after1000 / 100,
     				10);
-    		
+
     		int after100 = after1000 % 100;
-    		
+
     		dateAsBytes[2] = (byte) Character.forDigit(after100 / 10,
     				10);
     		dateAsBytes[3] = (byte) Character.forDigit(after100 % 10,
     				10);
-    		
+
     		dateAsBytes[4] = (byte) '-';
-    		
+
     		dateAsBytes[5] = (byte) Character.forDigit(month / 10,
     				10);
     		dateAsBytes[6] = (byte) Character.forDigit(month % 10,
     				10);
-    		
+
     		dateAsBytes[7] = (byte) '-';
-    		
+
     		dateAsBytes[8] = (byte) Character.forDigit(day / 10, 10);
     		dateAsBytes[9] = (byte) Character.forDigit(day % 10, 10);
-    		
+
     		unpackedRowData[columnIndex] = dateAsBytes;
-    		
-    		
+
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_DATETIME:
     	case MysqlDefs.FIELD_TYPE_TIMESTAMP:
     		length = (int) binaryData.readFieldLength();
-    		
+
     		year = 0;
     		month = 0;
     		day = 0;
-    		
+
     		hour = 0;
     		minute = 0;
     		seconds = 0;
-    		
+
     		int nanos = 0;
-    		
+
     		if (length != 0) {
     			year = binaryData.readInt();
     			month = binaryData.readByte();
     			day = binaryData.readByte();
-    			
+
     			if (length > 4) {
     				hour = binaryData.readByte();
     				minute = binaryData.readByte();
     				seconds = binaryData.readByte();
     			}
-    			
+
     			//if (length > 7) {
     			//    nanos = (int)binaryData.readLong();
     			//}
     		}
-    		
+
     		if ((year == 0) && (month == 0) && (day == 0)) {
-    			if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(
+    			if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL.equals(
     					this.connection.getZeroDateTimeBehavior())) {
     				unpackedRowData[columnIndex] = null;
-    				
+
     				break;
-    			} else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(
+    			} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION.equals(
     					this.connection.getZeroDateTimeBehavior())) {
     				throw SQLError.createSQLException("Value '0000-00-00' can not be represented as java.sql.Timestamp",
     						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
     			}
-    			
+
     			year = 1;
     			month = 1;
     			day = 1;
     		}
-    		
-    		
+
+
     		int stringLength = 19;
-    		
+
     		byte[] nanosAsBytes = Integer.toString(nanos).getBytes();
-    		
+
     		stringLength += (1 + nanosAsBytes.length); // '.' + # of digits
-    		
+
     		byte[] datetimeAsBytes = new byte[stringLength];
-    		
+
     		datetimeAsBytes[0] = (byte) Character.forDigit(year / 1000,
     				10);
-    		
+
     		after1000 = year % 1000;
-    		
+
     		datetimeAsBytes[1] = (byte) Character.forDigit(after1000 / 100,
     				10);
-    		
+
     		after100 = after1000 % 100;
-    		
+
     		datetimeAsBytes[2] = (byte) Character.forDigit(after100 / 10,
     				10);
     		datetimeAsBytes[3] = (byte) Character.forDigit(after100 % 10,
     				10);
-    		
+
     		datetimeAsBytes[4] = (byte) '-';
-    		
+
     		datetimeAsBytes[5] = (byte) Character.forDigit(month / 10,
     				10);
     		datetimeAsBytes[6] = (byte) Character.forDigit(month % 10,
     				10);
-    		
+
     		datetimeAsBytes[7] = (byte) '-';
-    		
+
     		datetimeAsBytes[8] = (byte) Character.forDigit(day / 10,
     				10);
     		datetimeAsBytes[9] = (byte) Character.forDigit(day % 10,
     				10);
-    		
+
     		datetimeAsBytes[10] = (byte) ' ';
-    		
+
     		datetimeAsBytes[11] = (byte) Character.forDigit(hour / 10,
     				10);
     		datetimeAsBytes[12] = (byte) Character.forDigit(hour % 10,
     				10);
-    		
+
     		datetimeAsBytes[13] = (byte) ':';
-    		
+
     		datetimeAsBytes[14] = (byte) Character.forDigit(minute / 10,
     				10);
     		datetimeAsBytes[15] = (byte) Character.forDigit(minute % 10,
     				10);
-    		
+
     		datetimeAsBytes[16] = (byte) ':';
-    		
+
     		datetimeAsBytes[17] = (byte) Character.forDigit(seconds / 10,
     				10);
     		datetimeAsBytes[18] = (byte) Character.forDigit(seconds % 10,
     				10);
-    		
+
     		datetimeAsBytes[19] = (byte) '.';
-    		
+
     		int nanosOffset = 20;
-    		
+
     		for (int j = 0; j < nanosAsBytes.length; j++) {
     			datetimeAsBytes[nanosOffset + j] = nanosAsBytes[j];
     		}
-    		
+
     		unpackedRowData[columnIndex] = datetimeAsBytes;
-    		
-    		
+
+
     		break;
-    		
+
     	case MysqlDefs.FIELD_TYPE_TINY_BLOB:
     	case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
     	case MysqlDefs.FIELD_TYPE_LONG_BLOB:
@@ -3764,9 +4303,9 @@
     	case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
     	case MysqlDefs.FIELD_TYPE_BIT:
     		unpackedRowData[columnIndex] = binaryData.readLenByteArray(0);
-    		
+
     		break;
-    		
+
     	default:
     		throw SQLError.createSQLException(Messages.getString("MysqlIO.97") //$NON-NLS-1$
     				+curField.getMysqlType() +
@@ -3776,20 +4315,8 @@
     				SQLError.SQL_STATE_GENERAL_ERROR);
     	}
     }
-    
+
     /**
-     * Optimization to only use one calendar per-session, or calculate it
-     * for each call, depending on user configuration
-     */
-    private Calendar getCalendarInstanceForSessionOrNew() {
-    	if (this.connection.getDynamicCalendars()) {
-    		return Calendar.getInstance();
-    	} else {
-    		return this.sessionCalendar;
-    	}
-    }
-    
-    /**
      * Negotiates the SSL communications channel used when connecting
      * to a MySQL server that understands SSL.
      *
@@ -3802,7 +4329,7 @@
      */
     private void negotiateSSLConnection(String user, String password,
         String database, int packLength)
-        throws SQLException, CommunicationsException {
+        throws SQLException {
         if (!ExportControlled.enabled()) {
             throw new ConnectionFeatureNotAvailableException(this.connection,
                 this.lastPacketSentTimeMs, null);
@@ -3819,7 +4346,7 @@
 
         Buffer packet = new Buffer(packLength);
 
-        if ((this.clientParam & CLIENT_RESERVED) != 0) {
+        if (this.use41Extensions) {
             packet.writeLong(this.clientParam);
         } else {
             packet.writeInt((int) this.clientParam);
@@ -3838,7 +4365,7 @@
                 secureAuth411(null, packLength, user, password, database, true);
             }
         } else {
-            if ((this.clientParam & CLIENT_RESERVED) != 0) {
+            if (this.use41Extensions) {
                 packet.writeLong(this.clientParam);
                 packet.writeLong(this.maxThreeBytes);
             } else {
@@ -3865,38 +4392,50 @@
     }
 
 	protected int getServerStatus() {
-		return serverStatus;
+		return this.serverStatus;
 	}
 
 	protected List fetchRowsViaCursor(List fetchedRows, long statementId,
-			Field[] columnTypes, int fetchSize) throws SQLException {
-		
+			Field[] columnTypes, int fetchSize, boolean useBufferRowExplicit) throws SQLException {
+
 		if (fetchedRows == null) {
 			fetchedRows = new ArrayList(fetchSize);
 		} else {
 			fetchedRows.clear();
 		}
-	
+
 		this.sharedSendPacket.clear();
-	
+
 		this.sharedSendPacket.writeByte((byte) MysqlDefs.COM_FETCH);
 		this.sharedSendPacket.writeLong(statementId);
 		this.sharedSendPacket.writeLong(fetchSize);
-	
+
 		sendCommand(MysqlDefs.COM_FETCH, null, this.sharedSendPacket, true,
 				null);
-	
-		Object[] row = null;
-	
+
+		ResultSetRow row = null;
+
 		while ((row = nextRow(columnTypes, columnTypes.length, true,
-				ResultSet.CONCUR_READ_ONLY)) != null) {
+				ResultSet.CONCUR_READ_ONLY, false, useBufferRowExplicit, false, null)) != null) {
 			fetchedRows.add(row);
 		}
-	
+
 		return fetchedRows;
 	}
 
 	protected long getThreadId() {
-		return threadId;
+		return this.threadId;
 	}
+
+	protected boolean useNanosForElapsedTime() {
+		return this.useNanosForElapsedTime;
+	}
+
+	protected long getSlowQueryThreshold() {
+		return this.slowQueryThreshold;
+	}
+
+	protected String getQueryTimingUnits() {
+		return this.queryTimingUnits;
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlParameterMetadata.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlParameterMetadata.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/MysqlParameterMetadata.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,98 +1,213 @@
 /*
-   Copyright (C) 2005 MySQL AB
+ Copyright (C) 2005 MySQL AB
 
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of version 2 of the GNU General Public License as 
-   published by the Free Software Foundation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
 
-   There are special exceptions to the terms and conditions of the GPL 
-   as it is applied to this software. View the full text of the 
-   exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
-   software distribution.
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
 
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-*/
+ */
 
 package com.mysql.jdbc;
 
 import java.sql.ParameterMetaData;
 import java.sql.SQLException;
+import java.sql.Types;
 
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
 public class MysqlParameterMetadata implements ParameterMetaData {
-	
+	boolean returnSimpleMetadata = false;
+
 	ResultSetMetaData metadata = null;
+
 	int parameterCount = 0;
-	
-	
+
 	MysqlParameterMetadata(Field[] fieldInfo, int parameterCount) {
 		this.metadata = new ResultSetMetaData(fieldInfo, false);
-		
+
 		this.parameterCount = parameterCount;
 	}
-	
+
+	/**
+	 * Used for "fake" basic metadata for client-side prepared statements when
+	 * we don't know the parameter types.
+	 * 
+	 * @param parameterCount
+	 */
+	MysqlParameterMetadata(int count) {
+		this.parameterCount = count;
+		this.returnSimpleMetadata = true;
+	}
+
 	public int getParameterCount() throws SQLException {
 		return this.parameterCount;
 	}
 
 	public int isNullable(int arg0) throws SQLException {
 		checkAvailable();
-		
+
 		return this.metadata.isNullable(arg0);
 	}
 
 	private void checkAvailable() throws SQLException {
-		if (this.metadata == null) {
+		if (this.metadata == null || this.metadata.fields == null) {
 			throw SQLError.createSQLException(
-				"Parameter metadata not available for the given statement",
-				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+					"Parameter metadata not available for the given statement",
+					SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
 		}
 	}
 
 	public boolean isSigned(int arg0) throws SQLException {
+		if (this.returnSimpleMetadata) {
+			checkBounds(arg0);
+
+			return false;
+		}
+
 		checkAvailable();
-		
+
 		return (this.metadata.isSigned(arg0));
 	}
 
 	public int getPrecision(int arg0) throws SQLException {
+		if (this.returnSimpleMetadata) {
+			checkBounds(arg0);
+
+			return 0;
+		}
+
 		checkAvailable();
-		
+
 		return (this.metadata.getPrecision(arg0));
 	}
 
 	public int getScale(int arg0) throws SQLException {
+		if (this.returnSimpleMetadata) {
+			checkBounds(arg0);
+
+			return 0;
+		}
+
 		checkAvailable();
-		
+
 		return (this.metadata.getScale(arg0));
 	}
 
 	public int getParameterType(int arg0) throws SQLException {
+		if (this.returnSimpleMetadata) {
+			checkBounds(arg0);
+
+			return Types.VARCHAR;
+		}
+
 		checkAvailable();
-		
+
 		return (this.metadata.getColumnType(arg0));
 	}
 
 	public String getParameterTypeName(int arg0) throws SQLException {
+		if (this.returnSimpleMetadata) {
+			checkBounds(arg0);
+
+			return "VARCHAR";
+		}
+
 		checkAvailable();
-		
+
 		return (this.metadata.getColumnTypeName(arg0));
 	}
 
 	public String getParameterClassName(int arg0) throws SQLException {
+		if (this.returnSimpleMetadata) {
+			checkBounds(arg0);
+
+			return "java.lang.String";
+		}
+
 		checkAvailable();
-		
+
 		return (this.metadata.getColumnClassName(arg0));
 	}
 
 	public int getParameterMode(int arg0) throws SQLException {
 		return parameterModeIn;
 	}
+
+	private void checkBounds(int paramNumber) throws SQLException {
+		if (paramNumber < 1) {
+			throw SQLError.createSQLException("Parameter index of '"
+					+ paramNumber + "' is invalid.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (paramNumber > this.parameterCount) {
+			throw SQLError.createSQLException("Parameter index of '"
+					+ paramNumber
+					+ "' is greater than number of parameters, which is '"
+					+ this.parameterCount + "'.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+
+		}
+	}
+	
+	/**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class iface) throws SQLException {
+
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface 
+     * @since 1.6
+     */
+	public Object unwrap(Class iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+    		return Util.cast(iface, this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/NonRegisteringDriver.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/NonRegisteringDriver.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/NonRegisteringDriver.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as
@@ -31,11 +31,11 @@
 import java.sql.DriverPropertyInfo;
 import java.sql.SQLException;
 
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Properties;
 import java.util.StringTokenizer;
-
 /**
  * The Java SQL framework allows for multiple database drivers. Each driver
  * should supply a class that implements the Driver interface
@@ -66,6 +66,14 @@
  * @see java.sql.Driver
  */
 public class NonRegisteringDriver implements java.sql.Driver {
+	private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://";
+
+	private static final String URL_PREFIX = "jdbc:mysql://";
+
+	private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://";
+
+	private static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://";
+
 	/**
 	 * Key used to retreive the database value from the properties instance
 	 * passed to the driver.
@@ -256,6 +264,15 @@
 	 */
 	public java.sql.Connection connect(String url, Properties info)
 			throws SQLException {
+		if (url != null) {
+			if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) {
+				return connectLoadBalanced(url, info);
+			} else if (StringUtils.startsWithIgnoreCase(url,
+					REPLICATION_URL_PREFIX)) {
+				return connectReplicationConnection(url, info);
+			}
+		}
+
 		Properties props = null;
 
 		if ((props = parseURL(url, info)) == null) {
@@ -263,8 +280,8 @@
 		}
 
 		try {
-			Connection newConn = new com.mysql.jdbc.Connection(host(props),
-					port(props), props, database(props), url);
+			Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(
+					host(props), port(props), props, database(props), url);
 
 			return newConn;
 		} catch (SQLException sqlEx) {
@@ -280,6 +297,107 @@
 		}
 	}
 
+	private java.sql.Connection connectLoadBalanced(String url, Properties info)
+			throws SQLException {
+		Properties parsedProps = parseURL(url, info);
+
+		if (parsedProps == null) {
+			return null;
+		}
+
+		String hostValues = parsedProps.getProperty(HOST_PROPERTY_KEY);
+
+		List hostList = null;
+
+		if (hostValues != null) {
+			hostList = StringUtils.split(hostValues, ",", true);
+		}
+
+		if (hostList == null) {
+			hostList = new ArrayList();
+			hostList.add("localhost:3306");
+		}
+
+		LoadBalancingConnectionProxy proxyBal = new LoadBalancingConnectionProxy(
+				hostList, parsedProps);
+
+		return (java.sql.Connection) java.lang.reflect.Proxy.newProxyInstance(this
+				.getClass().getClassLoader(),
+				new Class[] { java.sql.Connection.class }, proxyBal);
+	}
+
+	private java.sql.Connection connectReplicationConnection(String url, Properties info)
+			throws SQLException {
+		Properties parsedProps = parseURL(url, info);
+
+		if (parsedProps == null) {
+			return null;
+		}
+
+		Properties masterProps = (Properties) parsedProps.clone();
+		Properties slavesProps = (Properties) parsedProps.clone();
+
+		// Marker used for further testing later on, also when
+		// debugging
+		slavesProps.setProperty("com.mysql.jdbc.ReplicationConnection.isSlave",
+				"true");
+
+		String hostValues = parsedProps.getProperty(HOST_PROPERTY_KEY);
+
+		if (hostValues != null) {
+			StringTokenizer st = new StringTokenizer(hostValues, ",");
+
+			StringBuffer masterHost = new StringBuffer();
+			StringBuffer slaveHosts = new StringBuffer();
+
+			if (st.hasMoreTokens()) {
+				String[] hostPortPair = parseHostPortPair(st.nextToken());
+
+				if (hostPortPair[HOST_NAME_INDEX] != null) {
+					masterHost.append(hostPortPair[HOST_NAME_INDEX]);
+				}
+
+				if (hostPortPair[PORT_NUMBER_INDEX] != null) {
+					masterHost.append(":");
+					masterHost.append(hostPortPair[PORT_NUMBER_INDEX]);
+				}
+			}
+
+			boolean firstSlaveHost = true;
+
+			while (st.hasMoreTokens()) {
+				String[] hostPortPair = parseHostPortPair(st.nextToken());
+
+				if (!firstSlaveHost) {
+					slaveHosts.append(",");
+				} else {
+					firstSlaveHost = false;
+				}
+
+				if (hostPortPair[HOST_NAME_INDEX] != null) {
+					slaveHosts.append(hostPortPair[HOST_NAME_INDEX]);
+				}
+
+				if (hostPortPair[PORT_NUMBER_INDEX] != null) {
+					slaveHosts.append(":");
+					slaveHosts.append(hostPortPair[PORT_NUMBER_INDEX]);
+				}
+			}
+
+			if (slaveHosts.length() == 0) {
+				throw SQLError
+						.createSQLException(
+								"Must specify at least one slave host to connect to for master/slave replication load-balancing functionality",
+								SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+			}
+
+			masterProps.setProperty(HOST_PROPERTY_KEY, masterHost.toString());
+			slavesProps.setProperty(HOST_PROPERTY_KEY, slaveHosts.toString());
+		}
+
+		return new ReplicationConnection(masterProps, slavesProps);
+	}
+
 	/**
 	 * Returns the database property from <code>props</code>
 	 * 
@@ -342,7 +460,7 @@
 			info = new Properties();
 		}
 
-		if ((url != null) && url.startsWith("jdbc:mysql://")) { //$NON-NLS-1$
+		if ((url != null) && url.startsWith(URL_PREFIX)) { //$NON-NLS-1$
 			info = parseURL(url, info);
 		}
 
@@ -373,7 +491,7 @@
 		passwordProp.description = Messages
 				.getString("NonRegisteringDriver.16"); //$NON-NLS-1$
 
-		DriverPropertyInfo[] dpi = ConnectionProperties
+		DriverPropertyInfo[] dpi = ConnectionPropertiesImpl
 				.exposeAsDriverPropertyInfo(info, 5);
 
 		dpi[0] = hostProp;
@@ -418,19 +536,6 @@
 		return false;
 	}
 
-	/**
-	 * 
-	 * 
-	 * @param url
-	 *            ...
-	 * @param defaults
-	 *            ...
-	 * 
-	 * @return ...
-	 * 
-	 * @throws java.sql.SQLException
-	 *             ...
-	 */
 	public Properties parseURL(String url, Properties defaults)
 			throws java.sql.SQLException {
 		Properties urlProps = (defaults != null) ? new Properties(defaults)
@@ -440,21 +545,25 @@
 			return null;
 		}
 
-		if (!StringUtils.startsWithIgnoreCase(url, "jdbc:mysql://") && 
-				!StringUtils.startsWithIgnoreCase(url, "jdbc:mysql:mxj://")) { //$NON-NLS-1$
+		if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX)
+				&& !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)
+				&& !StringUtils.startsWithIgnoreCase(url,
+						LOADBALANCE_URL_PREFIX)
+				&& !StringUtils.startsWithIgnoreCase(url,
+						REPLICATION_URL_PREFIX)) { //$NON-NLS-1$
 
 			return null;
 		}
 
-		int beginningOfSlashes = 13;
-		
-		if (StringUtils.startsWithIgnoreCase(url, "jdbc:mysql:mxj://")) {
-			beginningOfSlashes = 17;
+		int beginningOfSlashes = url.indexOf("//");
+
+		if (StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)) {
 			
-			urlProps.setProperty("socketFactory", 
-				"com.mysql.management.driverlaunched.ServerLauncherSocketFactory");
+			urlProps
+					.setProperty("socketFactory",
+							"com.mysql.management.driverlaunched.ServerLauncherSocketFactory");
 		}
-		
+
 		/*
 		 * Parse parameters after the ? in the URL and remove them from the
 		 * original URL.
@@ -494,13 +603,13 @@
 						urlProps.put(parameter, URLDecoder.decode(value));
 					} catch (NoSuchMethodError nsme) {
 						// punt again
-						urlProps.put(parameter,  URLDecoder.decode(value));
+						urlProps.put(parameter, URLDecoder.decode(value));
 					}
 				}
 			}
 		}
 
-		url = url.substring(beginningOfSlashes);
+		url = url.substring(beginningOfSlashes + 2);
 
 		String hostStuff = null;
 
@@ -514,7 +623,7 @@
 						url.substring((slashIndex + 1), url.length()));
 			}
 		} else {
-			return null;
+			hostStuff = url;
 		}
 
 		if ((hostStuff != null) && (hostStuff.length() > 0)) {
@@ -554,6 +663,22 @@
 			}
 		}
 
+		if (Util.isColdFusion() &&
+				urlProps.getProperty("autoConfigureForColdFusion", "true").equalsIgnoreCase("true")) {
+			String configs = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY);
+			
+			StringBuffer newConfigs = new StringBuffer();
+			
+			if (configs != null) {
+				newConfigs.append(configs);
+				newConfigs.append(",");
+			}
+			
+			newConfigs.append("coldFusion");
+			
+			urlProps.setProperty(USE_CONFIG_PROPERTY_KEY, newConfigs.toString());
+		}
+		
 		// If we use a config, it actually should get overridden by anything in
 		// the URL or passed-in properties
 
@@ -583,10 +708,11 @@
 									"configs/" + configName + ".properties");
 
 					if (configAsStream == null) {
-						throw SQLError.createSQLException(
-								"Can't find configuration template named '"
-										+ configName + "'",
-								SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+						throw SQLError
+								.createSQLException(
+										"Can't find configuration template named '"
+												+ configName + "'",
+										SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
 					}
 					configProps.load(configAsStream);
 				} catch (IOException ioEx) {

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/NotUpdatable.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/NotUpdatable.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/NotUpdatable.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -32,9 +32,9 @@
  * @author Mark Matthews
  */
 public class NotUpdatable extends SQLException {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
 
+	private static final long serialVersionUID = 8084742846039782258L;
+
 	/**
 	 * The message to use when result set is not updatable.
 	 * 
@@ -49,13 +49,24 @@
 			+ Messages.getString("NotUpdatable.4") //$NON-NLS-1$
 			+ Messages.getString("NotUpdatable.5"); //$NON-NLS-1$
 
-	// ~ Constructors
-	// -----------------------------------------------------------
-
 	/**
 	 * Creates a new NotUpdatable exception.
 	 */
 	public NotUpdatable() {
-		super(NOT_UPDATEABLE_MESSAGE, SQLError.SQL_STATE_GENERAL_ERROR);
+		this(NOT_UPDATEABLE_MESSAGE);
 	}
-}
+
+	/**
+	 * Append the given reason to the not updatable message if the reason is not
+	 * null.
+	 */
+	public NotUpdatable(String reason) {
+		super(reason
+				+ Messages.getString("NotUpdatable.1")
+				+ Messages.getString("NotUpdatable.2")
+				+ Messages.getString("NotUpdatable.3")
+				+ Messages.getString("NotUpdatable.4")
+				+ Messages.getString("NotUpdatable.5"),
+				SQLError.SQL_STATE_GENERAL_ERROR);
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ParameterBindings.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ParameterBindings.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ParameterBindings.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+/**
+ * Interface to allow PreparedStatement implementations to expose
+ * their parameter bindings to StatementInterceptors.
+ * 
+ * @version $Id: $
+ */
+public interface ParameterBindings {
+
+	public abstract Array getArray(int parameterIndex) throws SQLException;
+
+	public abstract InputStream getAsciiStream(int parameterIndex) throws SQLException;
+
+	public abstract BigDecimal getBigDecimal(int parameterIndex) throws SQLException;
+
+	public abstract InputStream getBinaryStream(int parameterIndex) throws SQLException;
+
+	public abstract java.sql.Blob getBlob(int parameterIndex) throws SQLException;
+
+	public abstract boolean getBoolean(int parameterIndex) throws SQLException;
+	
+	public abstract byte getByte(int parameterIndex) throws SQLException;
+
+	public abstract byte[] getBytes(int parameterIndex) throws SQLException;
+
+	public abstract Reader getCharacterStream(int parameterIndex) throws SQLException;
+
+	public abstract Clob getClob(int parameterIndex) throws SQLException;
+	
+	public abstract Date getDate(int parameterIndex) throws SQLException;
+	
+	public abstract double getDouble(int parameterIndex) throws SQLException;
+
+	public abstract float getFloat(int parameterIndex) throws SQLException;
+
+	public abstract int getInt(int parameterIndex) throws SQLException;
+	
+	public abstract long getLong(int parameterIndex) throws SQLException;
+
+	public abstract Reader getNCharacterStream(int parameterIndex) throws SQLException;
+	
+	public abstract Reader getNClob(int parameterIndex) throws SQLException;
+	
+	public abstract Object getObject(int parameterIndex) throws SQLException;
+
+	public abstract Ref getRef(int parameterIndex) throws SQLException;
+
+	public abstract short getShort(int parameterIndex) throws SQLException;
+
+	public abstract String getString(int parameterIndex) throws SQLException;
+
+	public abstract Time getTime(int parameterIndex) throws SQLException;
+
+	public abstract Timestamp getTimestamp(int parameterIndex) throws SQLException;
+
+	public abstract URL getURL(int parameterIndex) throws SQLException;
+
+	public abstract boolean isNull(int parameterIndex) throws SQLException;
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/PingTarget.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/PingTarget.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/PingTarget.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+ 
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+public interface PingTarget {
+	public void doPing() throws SQLException;
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/PreparedStatement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/PreparedStatement.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/PreparedStatement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -32,13 +32,18 @@
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.net.URL;
 import java.sql.Array;
+import java.sql.BatchUpdateException;
 import java.sql.Clob;
+import java.sql.Date;
 import java.sql.ParameterMetaData;
 import java.sql.Ref;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Timestamp;
@@ -47,10 +52,14 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.List;
 import java.util.Locale;
+import java.util.Properties;
 import java.util.TimeZone;
 
-import com.mysql.jdbc.Statement.CancelTask;
+import com.mysql.jdbc.StatementImpl.CancelTask;
+import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
 import com.mysql.jdbc.exceptions.MySQLTimeoutException;
 import com.mysql.jdbc.profiler.ProfilerEvent;
 
@@ -78,8 +87,43 @@
  * @see java.sql.ResultSet
  * @see java.sql.PreparedStatement
  */
-public class PreparedStatement extends com.mysql.jdbc.Statement implements
+public class PreparedStatement extends com.mysql.jdbc.StatementImpl implements
 		java.sql.PreparedStatement {
+	private static final Constructor JDBC_4_PSTMT_2_ARG_CTOR;
+	private static final Constructor JDBC_4_PSTMT_3_ARG_CTOR;
+	private static final Constructor JDBC_4_PSTMT_4_ARG_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_PSTMT_2_ARG_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4PreparedStatement")
+						.getConstructor(
+								new Class[] { ConnectionImpl.class, String.class });
+				JDBC_4_PSTMT_3_ARG_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4PreparedStatement")
+						.getConstructor(
+								new Class[] { ConnectionImpl.class, String.class,
+										String.class });
+				JDBC_4_PSTMT_4_ARG_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4PreparedStatement")
+						.getConstructor(
+								new Class[] { ConnectionImpl.class, String.class,
+										String.class, ParseInfo.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_PSTMT_2_ARG_CTOR = null;
+			JDBC_4_PSTMT_3_ARG_CTOR = null;
+			JDBC_4_PSTMT_4_ARG_CTOR = null;
+		}
+	}
+	
 	class BatchParams {
 		boolean[] isNull = null;
 
@@ -136,168 +180,223 @@
 
 		int statementLength = 0;
 
+		int statementStartPos = 0;
+
 		byte[][] staticSql = null;
 
 		/**
-		 * 
+		 * Represents the "parsed" state of a client-side
+		 * prepared statement, with the statement broken up into
+		 * it's static and dynamic (where parameters are bound) 
+		 * parts.
 		 */
-		public ParseInfo(String sql, Connection conn,
+		public ParseInfo(String sql, ConnectionImpl conn,
 				java.sql.DatabaseMetaData dbmd, String encoding,
 				SingleByteCharsetConverter converter) throws SQLException {
-			if (sql == null) {
-				throw SQLError.createSQLException(Messages
-						.getString("PreparedStatement.61"), //$NON-NLS-1$
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
+			try {
+				if (sql == null) {
+					throw SQLError.createSQLException(Messages
+							.getString("PreparedStatement.61"), //$NON-NLS-1$
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
 
-			this.lastUsed = System.currentTimeMillis();
+				this.lastUsed = System.currentTimeMillis();
 
-			String quotedIdentifierString = dbmd.getIdentifierQuoteString();
+				String quotedIdentifierString = dbmd.getIdentifierQuoteString();
 
-			char quotedIdentifierChar = 0;
+				char quotedIdentifierChar = 0;
 
-			if ((quotedIdentifierString != null)
-					&& !quotedIdentifierString.equals(" ") //$NON-NLS-1$
-					&& (quotedIdentifierString.length() > 0)) {
-				quotedIdentifierChar = quotedIdentifierString.charAt(0);
-			}
+				if ((quotedIdentifierString != null)
+						&& !quotedIdentifierString.equals(" ") //$NON-NLS-1$
+						&& (quotedIdentifierString.length() > 0)) {
+					quotedIdentifierChar = quotedIdentifierString.charAt(0);
+				}
 
-			this.statementLength = sql.length();
+				this.statementLength = sql.length();
 
-			ArrayList endpointList = new ArrayList();
-			boolean inQuotes = false;
-			char quoteChar = 0;
-			boolean inQuotedId = false;
-			int lastParmEnd = 0;
-			int i;
+				ArrayList endpointList = new ArrayList();
+				boolean inQuotes = false;
+				char quoteChar = 0;
+				boolean inQuotedId = false;
+				int lastParmEnd = 0;
+				int i;
 
-			int stopLookingForLimitClause = this.statementLength - 5;
+				int stopLookingForLimitClause = this.statementLength - 5;
 
-			this.foundLimitClause = false;
-			
-			boolean noBackslashEscapes = connection.isNoBackslashEscapesSet();
-			
-			for (i = 0; i < this.statementLength; ++i) {
-				char c = sql.charAt(i);
+				this.foundLimitClause = false;
 
-				if ((this.firstStmtChar == 0) && !Character.isWhitespace(c)) {
-					// Determine what kind of statement we're doing (_S_elect,
-					// _I_nsert, etc.)
-					this.firstStmtChar = Character.toUpperCase(c);
-				}
+				boolean noBackslashEscapes = connection.isNoBackslashEscapesSet();
 
-				if (!noBackslashEscapes &&
-						c == '\\' && i < (this.statementLength - 1)) {
-					i++;
-					continue; // next character is escaped
-				}
-				
-				// are we in a quoted identifier?
-				// (only valid when the id is not inside a 'string')
-				if (!inQuotes && (quotedIdentifierChar != 0)
-						&& (c == quotedIdentifierChar)) {
-					inQuotedId = !inQuotedId;
-				} else if (!inQuotedId) {
-					//	only respect quotes when not in a quoted identifier
-					
-					if (inQuotes) {
-						if (((c == '\'') || (c == '"')) && c == quoteChar) {
-							if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) {
-								i++; 
-								continue; // inline quote escape
+				// we're not trying to be real pedantic here, but we'd like to 
+				// skip comments at the beginning of statements, as frameworks
+				// such as Hibernate use them to aid in debugging
+
+				statementStartPos = findStartOfStatement(sql);
+
+				for (i = statementStartPos; i < this.statementLength; ++i) {
+					char c = sql.charAt(i);
+
+					if ((this.firstStmtChar == 0) && Character.isLetter(c)) {
+						// Determine what kind of statement we're doing (_S_elect,
+						// _I_nsert, etc.)
+						this.firstStmtChar = Character.toUpperCase(c);
+					}
+
+					if (!noBackslashEscapes &&
+							c == '\\' && i < (this.statementLength - 1)) {
+						i++;
+						continue; // next character is escaped
+					}
+
+					// are we in a quoted identifier?
+					// (only valid when the id is not inside a 'string')
+					if (!inQuotes && (quotedIdentifierChar != 0)
+							&& (c == quotedIdentifierChar)) {
+						inQuotedId = !inQuotedId;
+					} else if (!inQuotedId) {
+						//	only respect quotes when not in a quoted identifier
+
+						if (inQuotes) {
+							if (((c == '\'') || (c == '"')) && c == quoteChar) {
+								if (i < (this.statementLength - 1) && sql.charAt(i + 1) == quoteChar) {
+									i++; 
+									continue; // inline quote escape
+								}
+
+								inQuotes = !inQuotes;
+								quoteChar = 0;
+							} else if (((c == '\'') || (c == '"')) && c == quoteChar) {
+								inQuotes = !inQuotes;
+								quoteChar = 0;
 							}
-							
-							inQuotes = !inQuotes;
-							quoteChar = 0;
-						} else if (((c == '\'') || (c == '"')) && c == quoteChar) {
-							inQuotes = !inQuotes;
-							quoteChar = 0;
+						} else {
+							if (c == '#'
+								|| (c == '-' && (i + 1) < this.statementLength && sql
+										.charAt(i + 1) == '-')) {
+								// run out to end of statement, or newline,
+								// whichever comes first
+								int endOfStmt = this.statementLength - 1;
+
+								for (; i < endOfStmt; i++) {
+									c = sql.charAt(i);
+
+									if (c == '\r' || c == '\n') {
+										break;
+									}
+								}
+
+								continue;
+							} else if (c == '/' && (i + 1) < this.statementLength) {
+								// Comment?
+								char cNext = sql.charAt(i + 1);
+
+								if (cNext == '*') {
+									i+= 2;
+
+									for (int j = i; j < this.statementLength; j++) {
+										i++;
+										cNext = sql.charAt(j);
+
+										if (cNext == '*' && (j + 1) < this.statementLength) {
+											if (sql.charAt(j + 1) == '/') {
+												i++;
+
+												if (i < this.statementLength) {
+													c = sql.charAt(i);
+												}
+
+												break; // comment done
+											}
+										}
+									}
+								}
+							} else if ((c == '\'') || (c == '"')) {
+								inQuotes = true;
+								quoteChar = c;
+							}
 						}
-					} else {
-						if ((c == '\'') || (c == '"')) {
-							inQuotes = true;
-							quoteChar = c;
-						} else if ((c == '\'') || (c == '"')) {
-							inQuotes = true;
-							quoteChar = c;
-						}
 					}
-				}
 
-				if ((c == '?') && !inQuotes && !inQuotedId) {
-					endpointList.add(new int[] { lastParmEnd, i });
-					lastParmEnd = i + 1;
-				}
+					if ((c == '?') && !inQuotes && !inQuotedId) {
+						endpointList.add(new int[] { lastParmEnd, i });
+						lastParmEnd = i + 1;
+					}
 
-				if (!inQuotes && (i < stopLookingForLimitClause)) {
-					if ((c == 'L') || (c == 'l')) {
-						char posI1 = sql.charAt(i + 1);
+					if (!inQuotes && (i < stopLookingForLimitClause)) {
+						if ((c == 'L') || (c == 'l')) {
+							char posI1 = sql.charAt(i + 1);
 
-						if ((posI1 == 'I') || (posI1 == 'i')) {
-							char posM = sql.charAt(i + 2);
+							if ((posI1 == 'I') || (posI1 == 'i')) {
+								char posM = sql.charAt(i + 2);
 
-							if ((posM == 'M') || (posM == 'm')) {
-								char posI2 = sql.charAt(i + 3);
+								if ((posM == 'M') || (posM == 'm')) {
+									char posI2 = sql.charAt(i + 3);
 
-								if ((posI2 == 'I') || (posI2 == 'i')) {
-									char posT = sql.charAt(i + 4);
+									if ((posI2 == 'I') || (posI2 == 'i')) {
+										char posT = sql.charAt(i + 4);
 
-									if ((posT == 'T') || (posT == 't')) {
-										foundLimitClause = true;
+										if ((posT == 'T') || (posT == 't')) {
+											foundLimitClause = true;
+										}
 									}
 								}
 							}
 						}
 					}
 				}
-			}
 
-			if (this.firstStmtChar == 'L') {
-				if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
-					this.foundLoadData = true;
+				if (this.firstStmtChar == 'L') {
+					if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
+						this.foundLoadData = true;
+					} else {
+						this.foundLoadData = false;
+					}
 				} else {
 					this.foundLoadData = false;
 				}
-			} else {
-				this.foundLoadData = false;
-			}
 
-			endpointList.add(new int[] { lastParmEnd, this.statementLength });
-			this.staticSql = new byte[endpointList.size()][];
-			char[] asCharArray = sql.toCharArray();
+				endpointList.add(new int[] { lastParmEnd, this.statementLength });
+				this.staticSql = new byte[endpointList.size()][];
+				char[] asCharArray = sql.toCharArray();
 
-			for (i = 0; i < this.staticSql.length; i++) {
-				int[] ep = (int[]) endpointList.get(i);
-				int end = ep[1];
-				int begin = ep[0];
-				int len = end - begin;
+				for (i = 0; i < this.staticSql.length; i++) {
+					int[] ep = (int[]) endpointList.get(i);
+					int end = ep[1];
+					int begin = ep[0];
+					int len = end - begin;
 
-				if (this.foundLoadData) {
-					String temp = new String(asCharArray, begin, len);
-					this.staticSql[i] = temp.getBytes();
-				} else if (encoding == null) {
-					byte[] buf = new byte[len];
+					if (this.foundLoadData) {
+						String temp = new String(asCharArray, begin, len);
+						this.staticSql[i] = temp.getBytes();
+					} else if (encoding == null) {
+						byte[] buf = new byte[len];
 
-					for (int j = 0; j < len; j++) {
-						buf[j] = (byte) sql.charAt(begin + j);
-					}
+						for (int j = 0; j < len; j++) {
+							buf[j] = (byte) sql.charAt(begin + j);
+						}
 
-					this.staticSql[i] = buf;
-				} else {
-					if (converter != null) {
-						this.staticSql[i] = StringUtils.getBytes(sql,
-								converter, encoding, connection
-										.getServerCharacterEncoding(), begin,
-								len, connection.parserKnowsUnicode());
+						this.staticSql[i] = buf;
 					} else {
-						String temp = new String(asCharArray, begin, len);
+						if (converter != null) {
+							this.staticSql[i] = StringUtils.getBytes(sql,
+									converter, encoding, connection
+									.getServerCharacterEncoding(), begin,
+									len, connection.parserKnowsUnicode());
+						} else {
+							String temp = new String(asCharArray, begin, len);
 
-						this.staticSql[i] = StringUtils.getBytes(temp,
-								encoding, connection
-										.getServerCharacterEncoding(),
-								connection.parserKnowsUnicode(), conn);
+							this.staticSql[i] = StringUtils.getBytes(temp,
+									encoding, connection
+									.getServerCharacterEncoding(),
+									connection.parserKnowsUnicode(), conn);
+						}
 					}
 				}
+			} catch (StringIndexOutOfBoundsException oobEx) {
+				SQLException sqlEx = new SQLException("Parse error for " + sql);
+				sqlEx.initCause(oobEx);
+
+				throw sqlEx;
 			}
 		}
 	}
@@ -323,7 +422,7 @@
 	 * @throws IOException
 	 *             DOCUMENT ME!
 	 */
-	private static int readFully(Reader reader, char[] buf, int length)
+	protected static int readFully(Reader reader, char[] buf, int length)
 			throws IOException {
 		int numCharsRead = 0;
 
@@ -380,6 +479,12 @@
 
 	private byte[][] parameterValues = null;
 
+	/**
+	 * Only used by statement interceptors at the moment to
+	 * provide introspection of bound values
+	 */
+	protected int[] parameterTypes = null;
+	
 	private ParseInfo parseInfo;
 
 	private java.sql.ResultSetMetaData pstmtResultMetaData;
@@ -397,11 +502,80 @@
 	 */
 	protected boolean useTrueBoolean = false;
 
-	private boolean usingAnsiMode;
+	protected boolean usingAnsiMode;
 
-	private String batchedValuesClause;
+	protected String batchedValuesClause;
 
+	/** Where does the statement text actually start? */
+	
+	private int statementAfterCommentsPos;
+
 	/**
+	 * have we checked whether we can rewrite this statement as a multi-value
+	 * insert?
+	 */
+
+	private boolean hasCheckedForRewrite = false;
+
+	/** Can we actually rewrite this statement as a multi-value insert? */
+
+	private boolean canRewrite = false;
+
+	private boolean doPingInstead;
+	
+	/**
+	 * Creates a prepared statement instance -- We need to provide factory-style
+	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
+	 * otherwise the class verifier complains when it tries to load JDBC4-only
+	 * interface classes that are present in JDBC4 method signatures.
+	 */
+
+	protected static PreparedStatement getInstance(ConnectionImpl conn,
+			String catalog) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new PreparedStatement(conn, catalog);
+		}
+
+		return (PreparedStatement) Util.handleNewInstance(
+				JDBC_4_PSTMT_2_ARG_CTOR, new Object[] { conn, catalog });
+	}
+
+	/**
+	 * Creates a prepared statement instance -- We need to provide factory-style
+	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
+	 * otherwise the class verifier complains when it tries to load JDBC4-only
+	 * interface classes that are present in JDBC4 method signatures.
+	 */
+
+	protected static PreparedStatement getInstance(ConnectionImpl conn, String sql,
+			String catalog) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new PreparedStatement(conn, sql, catalog);
+		}
+
+		return (PreparedStatement) Util.handleNewInstance(
+				JDBC_4_PSTMT_3_ARG_CTOR, new Object[] { conn, sql, catalog });
+	}
+
+	/**
+	 * Creates a prepared statement instance -- We need to provide factory-style
+	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
+	 * otherwise the class verifier complains when it tries to load JDBC4-only
+	 * interface classes that are present in JDBC4 method signatures.
+	 */
+
+	protected static PreparedStatement getInstance(ConnectionImpl conn, String sql,
+			String catalog, ParseInfo cachedParseInfo) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new PreparedStatement(conn, sql, catalog, cachedParseInfo);
+		}
+
+		return (PreparedStatement) Util.handleNewInstance(
+				JDBC_4_PSTMT_4_ARG_CTOR, new Object[] { conn, sql, catalog,
+						cachedParseInfo });
+	}
+	
+	/**
 	 * Constructor used by server-side prepared statements
 	 * 
 	 * @param conn
@@ -412,7 +586,7 @@
 	 * @throws SQLException
 	 *             if an error occurs
 	 */
-	protected PreparedStatement(Connection conn, String catalog)
+	public PreparedStatement(ConnectionImpl conn, String catalog)
 			throws SQLException {
 		super(conn, catalog);
 	}
@@ -430,7 +604,7 @@
 	 * @throws SQLException
 	 *             if a database error occurs.
 	 */
-	public PreparedStatement(Connection conn, String sql, String catalog)
+	public PreparedStatement(ConnectionImpl conn, String sql, String catalog)
 			throws SQLException {
 		super(conn, catalog);
 
@@ -441,6 +615,12 @@
 
 		this.originalSql = sql;
 
+		if (this.originalSql.startsWith(PING_MARKER)) {
+			this.doPingInstead = true;
+		} else {
+			this.doPingInstead = false;
+		}
+		
 		this.dbmd = this.connection.getMetaData();
 
 		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
@@ -466,7 +646,7 @@
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
-	public PreparedStatement(Connection conn, String sql, String catalog,
+	public PreparedStatement(ConnectionImpl conn, String sql, String catalog,
 			ParseInfo cachedParseInfo) throws SQLException {
 		super(conn, catalog);
 
@@ -494,7 +674,7 @@
 	 * @exception SQLException
 	 *                if a database-access error occurs.
 	 * 
-	 * @see Statement#addBatch
+	 * @see StatementImpl#addBatch
 	 */
 	public void addBatch() throws SQLException {
 		if (this.batchedArgs == null) {
@@ -611,6 +791,7 @@
 			this.parameterStreams[i] = null;
 			this.isStream[i] = false;
 			this.isNull[i] = false;
+			this.parameterTypes[i] = Types.NULL;
 		}
 	}
 
@@ -713,35 +894,70 @@
 	 *                if a database access error occurs
 	 */
 	public boolean execute() throws SQLException {
-		if (this.connection.isReadOnly() && (this.firstCharOfStmt != 'S')) {
+		checkClosed();
+		
+		ConnectionImpl locallyScopedConn = this.connection;
+		
+		if (locallyScopedConn.isReadOnly() && (this.firstCharOfStmt != 'S')) {
 			throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") //$NON-NLS-1$
 					+ Messages.getString("PreparedStatement.21"), //$NON-NLS-1$
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 		}
+		
+		ResultSetInternalMethods rs = null;
 
-		checkClosed();
+		CachedResultSetMetaData cachedMetadata = null;
 
-		ResultSet rs = null;
-
-		synchronized (this.connection.getMutex()) {
+		synchronized (locallyScopedConn.getMutex()) {
+			boolean doStreaming = createStreamingResultSet();
+			
 			clearWarnings();
 
+			// Adjust net_write_timeout to a higher value if we're
+			// streaming result sets. More often than not, someone runs into
+			// an issue where they blow net_write_timeout when using this
+			// feature, and if they're willing to hold a result set open
+			// for 30 seconds or more, one more round-trip isn't going to hurt
+			//
+			// This is reset by RowDataDynamic.close().
+			
+			if (doStreaming
+					&& this.connection.getNetTimeoutForStreamingResults() > 0) {
+				executeSimpleNonQuery(locallyScopedConn,
+						"SET net_write_timeout="
+								+ this.connection
+										.getNetTimeoutForStreamingResults());
+			}
+			
 			this.batchedGeneratedKeys = null;
 
 			Buffer sendPacket = fillSendPacket();
 
 			String oldCatalog = null;
 
-			if (!this.connection.getCatalog().equals(this.currentCatalog)) {
-				oldCatalog = this.connection.getCatalog();
-				this.connection.setCatalog(this.currentCatalog);
+			if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
+				oldCatalog = locallyScopedConn.getCatalog();
+				locallyScopedConn.setCatalog(this.currentCatalog);
 			}
 
+			//
+			// Check if we have cached metadata for this query...
+			//
+			if (locallyScopedConn.getCacheResultSetMetadata()) {
+				cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
+			}
+
+			Field[] metadataFromCache = null;
+			
+			if (cachedMetadata != null) {
+				metadataFromCache = cachedMetadata.fields;
+			}
+			
 			boolean oldInfoMsgState = false;
 
 			if (this.retrieveGeneratedKeys) {
-				oldInfoMsgState = this.connection.isReadInfoMsgEnabled();
-				this.connection.setReadInfoMsgEnabled(true);
+				oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
 			}
 
 			// If there isn't a limit clause in the SQL
@@ -753,7 +969,7 @@
 			//
 			// Only apply max_rows to selects
 			//
-			if (this.connection.useMaxRows()) {
+			if (locallyScopedConn.useMaxRows()) {
 				int rowLimit = -1;
 
 				if (this.firstCharOfStmt == 'S') {
@@ -761,53 +977,51 @@
 						rowLimit = this.maxRows;
 					} else {
 						if (this.maxRows <= 0) {
-							this.connection.execSQL(this,
-									"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, //$NON-NLS-1$
-									null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
-									java.sql.ResultSet.CONCUR_READ_ONLY, false,
-									this.currentCatalog, true);
+							executeSimpleNonQuery(locallyScopedConn,
+									"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 						} else {
-							this.connection
-									.execSQL(
-											this,
-											"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, -1, //$NON-NLS-1$
-											null,
-											java.sql.ResultSet.TYPE_FORWARD_ONLY,
-											java.sql.ResultSet.CONCUR_READ_ONLY,
-											false, this.currentCatalog,
-											true);
+							executeSimpleNonQuery(locallyScopedConn,
+									"SET OPTION SQL_SELECT_LIMIT="
+											+ this.maxRows);
 						}
 					}
 				} else {
-					this.connection.execSQL(this,
-							"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
-							java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.currentCatalog, true);
+					executeSimpleNonQuery(locallyScopedConn,
+							"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 				}
 
 				// Finally, execute the query
 				rs = executeInternal(rowLimit, sendPacket,
-						createStreamingResultSet(),
-						(this.firstCharOfStmt == 'S'), true, false);
+						doStreaming,
+						(this.firstCharOfStmt == 'S'), metadataFromCache, false);
 			} else {
 				rs = executeInternal(-1, sendPacket,
-						createStreamingResultSet(),
-						(this.firstCharOfStmt == 'S'), true, false);
+						doStreaming,
+						(this.firstCharOfStmt == 'S'), metadataFromCache, false);
 			}
 
+			if (cachedMetadata != null) {
+				locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
+						cachedMetadata, this.results);
+			} else {
+				if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) {
+					locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
+							null /* will be created */, rs);
+				}
+			}
+			
 			if (this.retrieveGeneratedKeys) {
-				this.connection.setReadInfoMsgEnabled(oldInfoMsgState);
-				rs.setFirstCharOfQuery('R');
+				locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
+				rs.setFirstCharOfQuery(this.firstCharOfStmt);
 			}
 
 			if (oldCatalog != null) {
-				this.connection.setCatalog(oldCatalog);
+				locallyScopedConn.setCatalog(oldCatalog);
 			}
 
-			this.lastInsertId = rs.getUpdateID();
-
 			if (rs != null) {
+				this.lastInsertId = rs.getUpdateID();
+				
 				this.results = rs;
 			}
 		}
@@ -839,25 +1053,273 @@
 		}
 
 		synchronized (this.connection.getMutex()) {
+			if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
+                return new int[0];
+            }
+
+			// we timeout the entire batch, not individual statements
+			int batchTimeout = this.timeoutInMillis;
+			this.timeoutInMillis = 0;
+		
+			resetCancelledState();
+			
 			try {
 				clearWarnings();
 
 				if (!this.batchHasPlainStatements
 						&& this.connection.getRewriteBatchedStatements()) {
-					if (StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
-							"INSERT")) {
-						return executeBatchedInserts();
+					
+					
+					if (canRewriteAsMultivalueInsertStatement()) {
+						return executeBatchedInserts(batchTimeout);
 					}
+					
+					if (this.connection.versionMeetsMinimum(4, 1, 0) 
+							&& !this.batchHasPlainStatements
+							&& this.batchedArgs != null 
+							&& this.batchedArgs.size() > 3 /* cost of option setting rt-wise */) {
+						return executePreparedBatchAsMultiStatement(batchTimeout);
+					}
 				}
 
-				return executeBatchSerially();
+				return executeBatchSerially(batchTimeout);
 			} finally {
 				clearBatch();
 			}
 		}
 	}
 
+	public synchronized boolean canRewriteAsMultivalueInsertStatement() {
+		if (!this.hasCheckedForRewrite) {
+			// Needs to be INSERT, can't have INSERT ... SELECT or
+			// INSERT ... ON DUPLICATE KEY UPDATE
+			//
+			// We're not smart enough to re-write to
+			//
+			//    INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6)
+			//    ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
+			//
+			// (yet)
+
+			this.canRewrite = StringUtils.startsWithIgnoreCaseAndWs(
+					this.originalSql, "INSERT", this.statementAfterCommentsPos) 
+			&& StringUtils.indexOfIgnoreCaseRespectMarker(this.statementAfterCommentsPos, this.originalSql, "SELECT", "\"'`", "\"'`", false) == -1 
+			&& StringUtils.indexOfIgnoreCaseRespectMarker(this.statementAfterCommentsPos, this.originalSql, "UPDATE", "\"'`", "\"'`", false) == -1;
+			
+			this.hasCheckedForRewrite = true;
+		}
+
+		return this.canRewrite;
+	}
+
 	/**
+	 * Rewrites the already prepared statement into a multi-statement
+	 * query of 'statementsPerBatch' values and executes the entire batch
+	 * using this new statement.
+	 * 
+	 * @return update counts in the same fashion as executeBatch()
+	 * 
+	 * @throws SQLException
+	 */
+	
+	protected int[] executePreparedBatchAsMultiStatement(int batchTimeout) throws SQLException {
+		synchronized (this.connection.getMutex()) {
+			// This is kind of an abuse, but it gets the job done
+			if (this.batchedValuesClause == null) {
+				this.batchedValuesClause = this.originalSql + ";";
+			}
+			
+			ConnectionImpl locallyScopedConn = this.connection;
+			
+			boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
+			CancelTask timeoutTask = null;
+			
+			try {
+				clearWarnings();
+				
+				int numBatchedArgs = this.batchedArgs.size();
+				
+				if (this.retrieveGeneratedKeys) {
+					this.batchedGeneratedKeys = new ArrayList(numBatchedArgs);
+				}
+
+				int numValuesPerBatch = computeBatchSize(numBatchedArgs);
+
+				if (numBatchedArgs < numValuesPerBatch) {
+					numValuesPerBatch = numBatchedArgs;
+				}
+
+				java.sql.PreparedStatement batchedStatement = null;
+
+				int batchedParamIndex = 1;
+				int numberToExecuteAsMultiValue = 0;
+				int batchCounter = 0;
+				int updateCountCounter = 0;
+				int[] updateCounts = new int[numBatchedArgs];
+				SQLException sqlEx = null;
+				
+				try {
+					if (!multiQueriesEnabled) {
+						locallyScopedConn.getIO().enableMultiQueries();
+					}
+					
+					if (this.retrieveGeneratedKeys) {
+						batchedStatement = locallyScopedConn.prepareStatement(
+								generateMultiStatementForBatch(numValuesPerBatch),
+								RETURN_GENERATED_KEYS);
+					} else {
+						batchedStatement = locallyScopedConn
+								.prepareStatement(generateMultiStatementForBatch(numValuesPerBatch));
+					}
+
+					if (locallyScopedConn.getEnableQueryTimeouts() &&
+							batchTimeout != 0
+							&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
+						timeoutTask = new CancelTask((StatementImpl)batchedStatement);
+						ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+								batchTimeout);
+					}
+					
+					if (numBatchedArgs < numValuesPerBatch) {
+						numberToExecuteAsMultiValue = numBatchedArgs;
+					} else {
+						numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch;
+					}
+			
+					int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch;
+			
+					for (int i = 0; i < numberArgsToExecute; i++) {
+						if (i != 0 && i % numValuesPerBatch == 0) {
+							try {
+								batchedStatement.execute();
+							} catch (SQLException ex) {
+								sqlEx = handleExceptionForBatch(batchCounter, numValuesPerBatch, 
+										updateCounts, ex);
+							}
+							
+							updateCountCounter = processMultiCountsAndKeys(
+									(StatementImpl)batchedStatement, updateCountCounter,
+									updateCounts);
+							
+							batchedStatement.clearParameters();
+							batchedParamIndex = 1;
+						}
+			
+						batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
+								batchedParamIndex, this.batchedArgs
+								.get(batchCounter++));
+					}
+			
+					try {
+						batchedStatement.execute();
+					} catch (SQLException ex) {
+						sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, 
+								updateCounts, ex);
+					}
+					
+					updateCountCounter = processMultiCountsAndKeys(
+							(StatementImpl)batchedStatement, updateCountCounter,
+							updateCounts);
+					
+					batchedStatement.clearParameters();
+			
+					numValuesPerBatch = numBatchedArgs - batchCounter;
+				} finally {
+					if (batchedStatement != null) {
+						batchedStatement.close();
+					}
+				}
+				
+				try {
+					if (numValuesPerBatch > 0) {
+			
+						if (this.retrieveGeneratedKeys) {
+							batchedStatement = locallyScopedConn.prepareStatement(
+									generateMultiStatementForBatch(numValuesPerBatch),
+								RETURN_GENERATED_KEYS);
+						} else {
+							batchedStatement = locallyScopedConn.prepareStatement(
+									generateMultiStatementForBatch(numValuesPerBatch));
+						}
+						
+						if (timeoutTask != null) {
+							timeoutTask.toCancel = (StatementImpl)batchedStatement;
+						}
+						
+						batchedParamIndex = 1;
+			
+						while (batchCounter < numBatchedArgs) {
+							batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
+									batchedParamIndex, this.batchedArgs
+									.get(batchCounter++));
+						}
+			
+						try {
+							batchedStatement.execute();
+						} catch (SQLException ex) {
+							sqlEx = handleExceptionForBatch(batchCounter - 1, numValuesPerBatch, 
+									updateCounts, ex);
+						}
+						
+						updateCountCounter = processMultiCountsAndKeys(
+								(StatementImpl)batchedStatement, updateCountCounter,
+								updateCounts);
+						
+						batchedStatement.clearParameters();
+					}
+			
+					if (timeoutTask != null) {
+						if (timeoutTask.caughtWhileCancelling != null) {
+							throw timeoutTask.caughtWhileCancelling;
+						}
+
+						timeoutTask.cancel();
+						timeoutTask = null;
+					}
+					
+					if (sqlEx != null) {
+						throw new java.sql.BatchUpdateException(sqlEx
+								.getMessage(), sqlEx.getSQLState(), sqlEx
+								.getErrorCode(), updateCounts);
+					}
+					
+					return updateCounts;
+				} finally {
+					if (batchedStatement != null) {
+						batchedStatement.close();
+					}
+				}
+			} finally {
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
+				
+				resetCancelledState();
+				
+				if (!multiQueriesEnabled) {
+					locallyScopedConn.getIO().disableMultiQueries();
+				}
+				
+				clearBatch();
+			}
+		}
+	}
+	
+	private String generateMultiStatementForBatch(int numBatches) {
+		StringBuffer newStatementSql = new StringBuffer((this.originalSql
+				.length() + 1) * numBatches);
+				
+		newStatementSql.append(this.originalSql);
+
+		for (int i = 0; i < numBatches - 1; i++) {
+			newStatementSql.append(';');
+			newStatementSql.append(this.originalSql);
+		}
+
+		return newStatementSql.toString();
+	}
+	
+	/**
 	 * Rewrites the already prepared statement into a multi-value insert
 	 * statement of 'statementsPerBatch' values and executes the entire batch
 	 * using this new statement.
@@ -866,17 +1328,17 @@
 	 * 
 	 * @throws SQLException
 	 */
-	private int[] executeBatchedInserts() throws SQLException {
+	protected int[] executeBatchedInserts(int batchTimeout) throws SQLException {
 		String valuesClause = extractValuesClause();
 
 		Connection locallyScopedConn = this.connection;
-		
+
 		if (valuesClause == null) {
-			return executeBatchSerially();
+			return executeBatchSerially(batchTimeout);
 		}
 
 		int numBatchedArgs = this.batchedArgs.size();
-		
+
 		if (this.retrieveGeneratedKeys) {
 			this.batchedGeneratedKeys = new ArrayList(numBatchedArgs);
 		}
@@ -889,80 +1351,171 @@
 
 		java.sql.PreparedStatement batchedStatement = null;
 
-		if (this.retrieveGeneratedKeys) {
-			batchedStatement = locallyScopedConn.prepareStatement(
-					generateBatchedInsertSQL(valuesClause, numValuesPerBatch),
-					RETURN_GENERATED_KEYS);
-		} else {
-			batchedStatement = locallyScopedConn
-					.prepareStatement(generateBatchedInsertSQL(valuesClause,
-							numValuesPerBatch));
-		}
-
 		int batchedParamIndex = 1;
 		int updateCountRunningTotal = 0;
 		int numberToExecuteAsMultiValue = 0;
 		int batchCounter = 0;
+		CancelTask timeoutTask = null;
+		SQLException sqlEx = null;
+		
+		int[] updateCounts = new int[numBatchedArgs];
 
-		if (numBatchedArgs < numValuesPerBatch) {
-			numberToExecuteAsMultiValue = numBatchedArgs;
-		} else {
-			numberToExecuteAsMultiValue = numBatchedArgs / numValuesPerBatch;
+		for (int i = 0; i < this.batchedArgs.size(); i++) {
+			updateCounts[i] = 1;
 		}
+		
+		try {
+			try {
+				if (this.retrieveGeneratedKeys) {
+					batchedStatement = locallyScopedConn.prepareStatement(
+							generateBatchedInsertSQL(valuesClause,
+									numValuesPerBatch), RETURN_GENERATED_KEYS);
+				} else {
+					batchedStatement = locallyScopedConn
+							.prepareStatement(generateBatchedInsertSQL(
+									valuesClause, numValuesPerBatch));
+				}
 
-		int numberArgsToExecute = numberToExecuteAsMultiValue * numValuesPerBatch;
+				if (this.connection.getEnableQueryTimeouts()
+						&& batchTimeout != 0
+						&& this.connection.versionMeetsMinimum(5, 0, 0)) {
+					timeoutTask = new CancelTask(
+							(StatementImpl) batchedStatement);
+					ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+							batchTimeout);
+				}
 
-		for (int i = 0; i < numberArgsToExecute; i++) {
-			if (i != 0 && i % numValuesPerBatch == 0) {
-				updateCountRunningTotal += batchedStatement.executeUpdate();
+				if (numBatchedArgs < numValuesPerBatch) {
+					numberToExecuteAsMultiValue = numBatchedArgs;
+				} else {
+					numberToExecuteAsMultiValue = numBatchedArgs
+							/ numValuesPerBatch;
+				}
 
+				int numberArgsToExecute = numberToExecuteAsMultiValue
+						* numValuesPerBatch;
+
+				for (int i = 0; i < numberArgsToExecute; i++) {
+					if (i != 0 && i % numValuesPerBatch == 0) {
+						try {
+							updateCountRunningTotal += batchedStatement
+								.executeUpdate();
+						} catch (SQLException ex) {
+							sqlEx = handleExceptionForBatch(batchCounter - 1,
+									numValuesPerBatch, updateCounts, ex);
+						}
+
+						getBatchedGeneratedKeys(batchedStatement);
+						batchedStatement.clearParameters();
+						batchedParamIndex = 1;
+
+					}
+
+					batchedParamIndex = setOneBatchedParameterSet(
+							batchedStatement, batchedParamIndex,
+							this.batchedArgs.get(batchCounter++));
+				}
+
+				try {
+					updateCountRunningTotal += batchedStatement.executeUpdate();
+				} catch (SQLException ex) {
+					sqlEx = handleExceptionForBatch(batchCounter - 1,
+							numValuesPerBatch, updateCounts, ex);
+				}
+				
 				getBatchedGeneratedKeys(batchedStatement);
-				batchedStatement.clearParameters();
-				batchedParamIndex = 1;
 
+				numValuesPerBatch = numBatchedArgs - batchCounter;
+			} finally {
+				if (batchedStatement != null) {
+					batchedStatement.close();
+				}
 			}
 
-			BatchParams paramArg = (BatchParams) this.batchedArgs
-					.get(batchCounter++);
+			try {
+				if (numValuesPerBatch > 0) {
 
-			batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
-					batchedParamIndex, paramArg);
-		}
+					if (this.retrieveGeneratedKeys) {
+						batchedStatement = locallyScopedConn.prepareStatement(
+								generateBatchedInsertSQL(valuesClause,
+										numValuesPerBatch),
+								RETURN_GENERATED_KEYS);
+					} else {
+						batchedStatement = locallyScopedConn
+								.prepareStatement(generateBatchedInsertSQL(
+										valuesClause, numValuesPerBatch));
+					}
 
-		updateCountRunningTotal += batchedStatement.executeUpdate();
-		getBatchedGeneratedKeys(batchedStatement);
+					if (timeoutTask != null) {
+						timeoutTask.toCancel = (StatementImpl) batchedStatement;
+					}
 
-		numValuesPerBatch = numBatchedArgs - batchCounter;
+					batchedParamIndex = 1;
 
-		if (numValuesPerBatch > 0) {
+					while (batchCounter < numBatchedArgs) {
+						batchedParamIndex = setOneBatchedParameterSet(
+								batchedStatement, batchedParamIndex,
+								this.batchedArgs.get(batchCounter++));
+					}
 
-			batchedStatement = locallyScopedConn.prepareStatement(
-					generateBatchedInsertSQL(valuesClause, numValuesPerBatch),
-					RETURN_GENERATED_KEYS);
-			batchedParamIndex = 1;
+					try {
+						updateCountRunningTotal += batchedStatement.executeUpdate();
+					} catch (SQLException ex) {
+						sqlEx = handleExceptionForBatch(batchCounter - 1,
+								numValuesPerBatch, updateCounts, ex);
+					}
+					
+					getBatchedGeneratedKeys(batchedStatement);
+				}
 
-			while (batchCounter < numBatchedArgs) {
-
-				BatchParams paramArg = (BatchParams) this.batchedArgs
-						.get(batchCounter++);
-				batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
-						batchedParamIndex, paramArg);
+				if (sqlEx != null) {
+					throw new java.sql.BatchUpdateException(sqlEx
+							.getMessage(), sqlEx.getSQLState(), sqlEx
+							.getErrorCode(), updateCounts);
+				}
+				
+				return updateCounts;
+			} finally {
+				if (batchedStatement != null) {
+					batchedStatement.close();
+				}
 			}
+		} finally {
+			if (timeoutTask != null) {
+				timeoutTask.cancel();
+			}
 
-			updateCountRunningTotal += batchedStatement.executeUpdate();
-			getBatchedGeneratedKeys(batchedStatement);
+			resetCancelledState();
 		}
+	}
 
-		int[] updateCounts = new int[this.batchedArgs.size()];
-
-		for (int i = 0; i < this.batchedArgs.size(); i++) {
-			updateCounts[i] = 1;
+	/**
+	 * Computes the optimum number of batched parameter lists to send
+	 * without overflowing max_allowed_packet.
+	 * 
+	 * @param numBatchedArgs
+	 * @return
+	 */
+	protected int computeBatchSize(int numBatchedArgs) {
+		long[] combinedValues = computeMaxParameterSetSizeAndBatchSize(numBatchedArgs);
+		
+		long maxSizeOfParameterSet = combinedValues[0];
+		long sizeOfEntireBatch = combinedValues[1];
+		
+		int maxAllowedPacket = this.connection.getMaxAllowedPacket();
+		
+		if (sizeOfEntireBatch < maxAllowedPacket - this.originalSql.length()) {
+			return numBatchedArgs;
 		}
-
-		return updateCounts;
+		
+		return (int)Math.max(1, (maxAllowedPacket - this.originalSql.length()) / maxSizeOfParameterSet);
 	}
-
-	protected int computeBatchSize(int numBatchedArgs) {
+	
+	/** 
+	 *  Computes the maximum parameter set size, and entire batch size given 
+	 *  the number of arguments in the batch.
+	 */
+	protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) {
 		long sizeOfEntireBatch = 0;
 		long maxSizeOfParameterSet = 0;
 		
@@ -988,8 +1541,10 @@
 							sizeOfParameterSet += paramLength;
 						}
 					} else {
-						sizeOfParameterSet += 4; // for NULL literal in SQL 
+						sizeOfParameterSet += paramArg.parameterStrings[j].length;
 					}
+				} else {
+					sizeOfParameterSet += 4; // for NULL literal in SQL 
 				}
 			}
 			
@@ -1001,23 +1556,21 @@
 			// anyway
 			//
 			
-			sizeOfParameterSet += this.batchedValuesClause.length() + 1; 
+			if (this.batchedValuesClause != null) {
+				sizeOfParameterSet += this.batchedValuesClause.length() + 1;
+			}
+			
 			sizeOfEntireBatch += sizeOfParameterSet;
 			
 			if (sizeOfParameterSet > maxSizeOfParameterSet) {
 				maxSizeOfParameterSet = sizeOfParameterSet;
 			}
-		}
+		}	
 		
-		int maxAllowedPacket = this.connection.getMaxAllowedPacket();
-		
-		if (sizeOfEntireBatch < maxAllowedPacket - this.originalSql.length()) {
-			return numBatchedArgs;
-		}
-		
-		return (int)Math.max(1, (maxAllowedPacket - this.originalSql.length()) / maxSizeOfParameterSet);
+		return new long[] {maxSizeOfParameterSet, sizeOfEntireBatch};
 	}
 
+
 	/**
 	 * Executes the current batch of statements by executing them one-by-one.
 	 * 
@@ -1025,7 +1578,7 @@
 	 * @throws SQLException
 	 *             if an error occurs
 	 */
-	protected int[] executeBatchSerially() throws SQLException {
+	protected int[] executeBatchSerially(int batchTimeout) throws SQLException {
 		
 		Connection locallyScopedConn = this.connection;
 		
@@ -1047,65 +1600,86 @@
 
 			int commandIndex = 0;
 
-			if (this.retrieveGeneratedKeys) {
-				this.batchedGeneratedKeys = new ArrayList(nbrCommands);
-			}
-
-			for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
-				Object arg = this.batchedArgs.get(commandIndex);
-
-				if (arg instanceof String) {
-					updateCounts[commandIndex] = executeUpdate((String) arg);
-				} else {
-					BatchParams paramArg = (BatchParams) arg;
-
-					try {
-						updateCounts[commandIndex] = executeUpdate(
-								paramArg.parameterStrings,
-								paramArg.parameterStreams, paramArg.isStream,
-								paramArg.streamLengths, paramArg.isNull, true);
-
-						if (this.retrieveGeneratedKeys) {
-							java.sql.ResultSet rs = null;
-
-							try {
-								rs = getGeneratedKeysInternal();
-
-								while (rs.next()) {
-									this.batchedGeneratedKeys
-											.add(new byte[][] { rs.getBytes(1) });
+			CancelTask timeoutTask = null;
+			
+			try {
+				if (this.connection.getEnableQueryTimeouts() &&
+						batchTimeout != 0
+						&& this.connection.versionMeetsMinimum(5, 0, 0)) {
+					timeoutTask = new CancelTask(this);
+					ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+							batchTimeout);
+				}
+				
+				if (this.retrieveGeneratedKeys) {
+					this.batchedGeneratedKeys = new ArrayList(nbrCommands);
+				}
+	
+				for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
+					Object arg = this.batchedArgs.get(commandIndex);
+	
+					if (arg instanceof String) {
+						updateCounts[commandIndex] = executeUpdate((String) arg);
+					} else {
+						BatchParams paramArg = (BatchParams) arg;
+	
+						try {
+							updateCounts[commandIndex] = executeUpdate(
+									paramArg.parameterStrings,
+									paramArg.parameterStreams, paramArg.isStream,
+									paramArg.streamLengths, paramArg.isNull, true);
+	
+							if (this.retrieveGeneratedKeys) {
+								java.sql.ResultSet rs = null;
+	
+								try {
+									rs = getGeneratedKeysInternal();
+	
+									while (rs.next()) {
+										this.batchedGeneratedKeys
+												.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }));
+									}
+								} finally {
+									if (rs != null) {
+										rs.close();
+									}
 								}
-							} finally {
-								if (rs != null) {
-									rs.close();
-								}
 							}
+						} catch (SQLException ex) {
+							updateCounts[commandIndex] = EXECUTE_FAILED;
+	
+							if (this.continueBatchOnError && 
+									!(ex instanceof MySQLTimeoutException) && 
+									!(ex instanceof MySQLStatementCancelledException)) {
+								sqlEx = ex;
+							} else {
+								int[] newUpdateCounts = new int[commandIndex];
+								System.arraycopy(updateCounts, 0,
+										newUpdateCounts, 0, commandIndex);
+	
+								throw new java.sql.BatchUpdateException(ex
+										.getMessage(), ex.getSQLState(), ex
+										.getErrorCode(), newUpdateCounts);
+							}
 						}
-					} catch (SQLException ex) {
-						updateCounts[commandIndex] = EXECUTE_FAILED;
-
-						if (this.continueBatchOnError) {
-							sqlEx = ex;
-						} else {
-							int[] newUpdateCounts = new int[commandIndex];
-							System.arraycopy(updateCounts, 0, newUpdateCounts,
-									0, commandIndex);
-
-							throw new java.sql.BatchUpdateException(ex
-									.getMessage(), ex.getSQLState(), ex
-									.getErrorCode(), newUpdateCounts);
-						}
 					}
 				}
+	
+				if (sqlEx != null) {
+					throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
+							sqlEx.getSQLState(), sqlEx.getErrorCode(), updateCounts);
+				}
+			} finally {
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
+				
+				resetCancelledState();
 			}
-
-			if (sqlEx != null) {
-				throw new java.sql.BatchUpdateException(sqlEx.getMessage(),
-						sqlEx.getSQLState(), sqlEx.getErrorCode(), updateCounts);
-			}
 		}
-
+	
 		return (updateCounts != null) ? updateCounts : new int[0];
+		
 	}
 
 	/**
@@ -1128,49 +1702,82 @@
 	 * @throws SQLException
 	 *             if an error occurs.
 	 */
-	protected ResultSet executeInternal(int maxRowsToRetrieve,
+	protected ResultSetInternalMethods executeInternal(int maxRowsToRetrieve,
 			Buffer sendPacket, boolean createStreamingResultSet,
-			boolean queryIsSelectOnly, boolean unpackFields, boolean isBatch)
+			boolean queryIsSelectOnly, Field[] metadataFromCache,
+			boolean isBatch)
 			throws SQLException {
-		this.wasCancelled = false;
-		
-		Connection locallyScopedConnection= this.connection;
-		
-		this.numberOfExecutions++;
-
-		ResultSet rs;
-		
-		CancelTask timeoutTask = null;
-
 		try {
-			if (this.timeoutInMillis != 0
-					&& locallyScopedConnection.versionMeetsMinimum(5, 0, 0)) {
-				timeoutTask = new CancelTask();
-				Connection.getCancelTimer().schedule(timeoutTask, 
-						this.timeoutInMillis);
+			
+			resetCancelledState();
+			
+			ConnectionImpl locallyScopedConnection = this.connection;
+			
+			this.numberOfExecutions++;
+	
+			if (this.doPingInstead) {
+				doPingInstead();
+				
+				return this.results;
 			}
 			
-			rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket,
-				this.resultSetType, this.resultSetConcurrency,
-				createStreamingResultSet, this.currentCatalog,
-				unpackFields, isBatch);
+			ResultSetInternalMethods rs;
 			
-			if (timeoutTask != null) {
-				timeoutTask.cancel();
-				timeoutTask = null;
+			CancelTask timeoutTask = null;
+	
+			try {
+				if (locallyScopedConnection.getEnableQueryTimeouts() &&
+						this.timeoutInMillis != 0
+						&& locallyScopedConnection.versionMeetsMinimum(5, 0, 0)) {
+					timeoutTask = new CancelTask(this);
+					ConnectionImpl.getCancelTimer().schedule(timeoutTask, 
+							this.timeoutInMillis);
+				}
+				
+				rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket,
+					this.resultSetType, this.resultSetConcurrency,
+					createStreamingResultSet, this.currentCatalog,
+					metadataFromCache, isBatch);
+				
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+					
+					if (timeoutTask.caughtWhileCancelling != null) {
+						throw timeoutTask.caughtWhileCancelling;
+					}
+					
+					timeoutTask = null;
+				}
+			
+				synchronized (this.cancelTimeoutMutex) {
+					if (this.wasCancelled) {
+						SQLException cause = null;
+						
+						if (this.wasCancelledByTimeout) {
+							cause = new MySQLTimeoutException();
+						} else {
+							cause = new MySQLStatementCancelledException();
+						}
+						
+						resetCancelledState();
+						
+						throw cause;
+					}
+				}
+			} finally {
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
 			}
-		
-			if (this.wasCancelled) {
-				this.wasCancelled = false;
-				throw new MySQLTimeoutException();
-			}
-		} finally {
-			if (timeoutTask != null) {
-				timeoutTask.cancel();
-			}
+			
+			return rs;
+		} catch (NullPointerException npe) {
+			checkClosed(); // we can't synchronize ourselves against async connection-close
+			               // due to deadlock issues, so this is the next best thing for
+			 			   // this particular corner case.
+			
+			throw npe;
 		}
-
-		return rs;
 	}
 
 	/**
@@ -1185,7 +1792,7 @@
 	public java.sql.ResultSet executeQuery() throws SQLException {
 		checkClosed();
 		
-		Connection locallyScopedConn = this.connection;
+		ConnectionImpl locallyScopedConn = this.connection;
 		
 		checkForDml(this.originalSql, this.firstCharOfStmt);
 
@@ -1198,8 +1805,27 @@
 		synchronized (locallyScopedConn.getMutex()) {
 			clearWarnings();
 
+			boolean doStreaming = createStreamingResultSet();
+			
 			this.batchedGeneratedKeys = null;
 
+			// Adjust net_write_timeout to a higher value if we're
+			// streaming result sets. More often than not, someone runs into
+			// an issue where they blow net_write_timeout when using this
+			// feature, and if they're willing to hold a result set open
+			// for 30 seconds or more, one more round-trip isn't going to hurt
+			//
+			// This is reset by RowDataDynamic.close().
+			
+			if (doStreaming
+					&& this.connection.getNetTimeoutForStreamingResults() > 0) {
+				locallyScopedConn.execSQL(this, "SET net_write_timeout="
+						+ this.connection.getNetTimeoutForStreamingResults(),
+						-1, null, ResultSet.TYPE_FORWARD_ONLY,
+						ResultSet.CONCUR_READ_ONLY, false, this.currentCatalog,
+						null, false);
+			}
+			
 			Buffer sendPacket = fillSendPacket();
 
 			if (this.results != null) {
@@ -1221,9 +1847,15 @@
 			// Check if we have cached metadata for this query...
 			//
 			if (locallyScopedConn.getCacheResultSetMetadata()) {
-				cachedMetadata = getCachedMetaData(this.originalSql);
+				cachedMetadata = locallyScopedConn.getCachedMetaData(this.originalSql);
 			}
 
+			Field[] metadataFromCache = null;
+			
+			if (cachedMetadata != null) {
+				metadataFromCache = cachedMetadata.fields;
+			}
+			
 			if (locallyScopedConn.useMaxRows()) {
 				// If there isn't a limit clause in the SQL
 				// then limit the number of rows to return in
@@ -1234,29 +1866,19 @@
 				if (this.hasLimitClause) {
 					this.results = executeInternal(this.maxRows, sendPacket,
 							createStreamingResultSet(), true,
-							(cachedMetadata == null), false);
+							metadataFromCache, false);
 				} else {
 					if (this.maxRows <= 0) {
-						locallyScopedConn
-								.execSQL(
-										this,
-										"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
-										java.sql.ResultSet.TYPE_FORWARD_ONLY,
-										java.sql.ResultSet.CONCUR_READ_ONLY,
-										false, this.currentCatalog, true);
+						executeSimpleNonQuery(locallyScopedConn,
+								"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 					} else {
-						locallyScopedConn
-								.execSQL(
-										this,
-										"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, -1, null, //$NON-NLS-1$
-										java.sql.ResultSet.TYPE_FORWARD_ONLY,
-										java.sql.ResultSet.CONCUR_READ_ONLY,
-										false, this.currentCatalog, true);
+						executeSimpleNonQuery(locallyScopedConn,
+								"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows);
 					}
 
 					this.results = executeInternal(-1, sendPacket,
-							createStreamingResultSet(), true,
-							(cachedMetadata == null), false);
+							doStreaming, true,
+							metadataFromCache, false);
 
 					if (oldCatalog != null) {
 						this.connection.setCatalog(oldCatalog);
@@ -1264,30 +1886,30 @@
 				}
 			} else {
 				this.results = executeInternal(-1, sendPacket,
-						createStreamingResultSet(), true,
-						(cachedMetadata == null), false);
+						doStreaming, true,
+						metadataFromCache, false);
 			}
 
 			if (oldCatalog != null) {
 				locallyScopedConn.setCatalog(oldCatalog);
 			}
+			
+			if (cachedMetadata != null) {
+				locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
+						cachedMetadata, this.results);
+			} else {
+				if (locallyScopedConn.getCacheResultSetMetadata()) {
+					locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql,
+							null /* will be created */, this.results);
+				}
+			}
 		}
 
 		this.lastInsertId = this.results.getUpdateID();
 
-		if (cachedMetadata != null) {
-			initializeResultsMetadataFromCache(this.originalSql,
-					cachedMetadata, this.results);
-		} else {
-			if (this.connection.getCacheResultSetMetadata()) {
-				initializeResultsMetadataFromCache(this.originalSql,
-						null /* will be created */, this.results);
-			}
-		}
-
 		return this.results;
 	}
-
+	
 	/**
 	 * Execute a SQL INSERT, UPDATE or DELETE statement. In addition, SQL
 	 * statements that return nothing such as SQL DDL statements can be
@@ -1345,7 +1967,7 @@
 
 		checkClosed();
 
-		Connection locallyScopedConn = this.connection;
+		ConnectionImpl locallyScopedConn = this.connection;
 		
 		if (locallyScopedConn.isReadOnly()) {
 			throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") //$NON-NLS-1$
@@ -1354,8 +1976,7 @@
 		}
 
 		if ((this.firstCharOfStmt == 'S')
-				&& StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
-						"SELECT")) { //$NON-NLS-1$
+				&& isSelectQuery()) { //$NON-NLS-1$
 			throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), //$NON-NLS-1$
 					"01S03"); //$NON-NLS-1$
 		}
@@ -1366,7 +1987,7 @@
 			}
 		}
 
-		ResultSet rs = null;
+		ResultSetInternalMethods rs = null;
 
 		// The checking and changing of catalogs
 		// must happen in sequence, so synchronize
@@ -1387,11 +2008,8 @@
 			// Only apply max_rows to selects
 			//
 			if (locallyScopedConn.useMaxRows()) {
-				locallyScopedConn.execSQL(this,
-						"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
-						java.sql.ResultSet.TYPE_FORWARD_ONLY,
-						java.sql.ResultSet.CONCUR_READ_ONLY, false,
-						this.currentCatalog, true);
+				executeSimpleNonQuery(locallyScopedConn,
+						"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
 			}
 
 			boolean oldInfoMsgState = false;
@@ -1401,7 +2019,8 @@
 				locallyScopedConn.setReadInfoMsgEnabled(true);
 			}
 
-			rs = executeInternal(-1, sendPacket, false, false, true, isReallyBatch);
+			rs = executeInternal(-1, sendPacket, false, false, null, 
+					isReallyBatch);
 
 			if (this.retrieveGeneratedKeys) {
 				locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
@@ -1438,10 +2057,12 @@
 			int indexOfValues = -1;
 	
 			if (quoteCharStr.length() > 0) {
-				indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
+				indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(
+						this.statementAfterCommentsPos,
 						this.originalSql, "VALUES ", quoteCharStr.charAt(0), false);
 			} else {
-				indexOfValues = StringUtils.indexOfIgnoreCase(0, this.originalSql,
+				indexOfValues = StringUtils.indexOfIgnoreCase(this.statementAfterCommentsPos, 
+						this.originalSql,
 						"VALUES ");
 			}
 	
@@ -1518,6 +2139,24 @@
 		//
 		int ensurePacketSize = 0;
 
+		String statementComment = this.connection.getStatementComment();
+		
+		byte[] commentAsBytes = null;
+		
+		if (statementComment != null) {
+			if (this.charConverter != null) {
+				commentAsBytes = this.charConverter.toBytes(statementComment);
+			} else {
+				commentAsBytes = StringUtils.getBytes(statementComment, this.charConverter,
+						this.charEncoding, this.connection
+								.getServerCharacterEncoding(), this.connection
+								.parserKnowsUnicode());
+			}
+			
+			ensurePacketSize += commentAsBytes.length;
+			ensurePacketSize += 6; // for /*[space] [space]*/
+		}
+	
 		for (int i = 0; i < batchedParameterStrings.length; i++) {
 			if (batchedIsStream[i] && useStreamLengths) {
 				ensurePacketSize += batchedStreamLengths[i];
@@ -1528,6 +2167,12 @@
 			sendPacket.ensureCapacity(ensurePacketSize);
 		}
 
+		if (commentAsBytes != null) {
+			sendPacket.writeBytesNoNull(Constants.SLASH_STAR_SPACE_AS_BYTES);
+			sendPacket.writeBytesNoNull(commentAsBytes);
+			sendPacket.writeBytesNoNull(Constants.SPACE_STAR_SLASH_SPACE_AS_BYTES);
+		}
+		
 		for (int i = 0; i < batchedParameterStrings.length; i++) {
 			if ((batchedParameterStrings[i] == null)
 					&& (batchedParameterStreams[i] == null)) {
@@ -1666,16 +2311,16 @@
 		ArrayList vecRemovelist = new ArrayList();
 		Object[] nv = new Object[3];
 		Object[] v;
-		nv[0] = new Character('y');
+		nv[0] = Constants.characterValueOf('y');
 		nv[1] = new StringBuffer();
-		nv[2] = new Integer(0);
+		nv[2] = Constants.integerValueOf(0);
 		vec.add(nv);
 
 		if (toTime) {
 			nv = new Object[3];
-			nv[0] = new Character('h');
+			nv[0] = Constants.characterValueOf('h');
 			nv[1] = new StringBuffer();
-			nv[2] = new Integer(0);
+			nv[2] = Constants.integerValueOf(0);
 			vec.add(nv);
 		}
 
@@ -1695,7 +2340,7 @@
 						((StringBuffer) v[1]).append(separator);
 
 						if ((c == 'X') || (c == 'Y')) {
-							v[2] = new Integer(4);
+							v[2] = Constants.integerValueOf(4);
 						}
 					}
 				} else {
@@ -1704,26 +2349,26 @@
 						nv = new Object[3];
 						nv[1] = (new StringBuffer(((StringBuffer) v[1])
 								.toString())).append('M');
-						nv[0] = new Character('M');
-						nv[2] = new Integer(1);
+						nv[0] = Constants.characterValueOf('M');
+						nv[2] = Constants.integerValueOf(1);
 						vec.add(nv);
 					} else if (c == 'Y') {
 						c = 'M';
 						nv = new Object[3];
 						nv[1] = (new StringBuffer(((StringBuffer) v[1])
 								.toString())).append('d');
-						nv[0] = new Character('d');
-						nv[2] = new Integer(1);
+						nv[0] = Constants.characterValueOf('d');
+						nv[2] = Constants.integerValueOf(1);
 						vec.add(nv);
 					}
 
 					((StringBuffer) v[1]).append(c);
 
 					if (c == ((Character) v[0]).charValue()) {
-						v[2] = new Integer(n + 1);
+						v[2] = Constants.integerValueOf(n + 1);
 					} else {
-						v[0] = new Character(c);
-						v[2] = new Integer(1);
+						v[0] = Constants.characterValueOf(c);
+						v[2] = Constants.integerValueOf(1);
 					}
 				}
 			}
@@ -1783,8 +2428,18 @@
 	public java.sql.ResultSetMetaData getMetaData()
 			throws SQLException {
 
-		if (!StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(
-				this.originalSql, "SELECT")) {
+		//
+		// We could just tack on a LIMIT 0 here no matter what the 
+		// statement, and check if a result set was returned or not,
+		// but I'm not comfortable with that, myself, so we take
+		// the "safer" road, and only allow metadata for _actual_
+		// SELECTS (but not SHOWs).
+		// 
+		// CALL's are trapped further up and you end up with a 
+		// CallableStatement anyway.
+		//
+		
+		if (!isSelectQuery()) {
 			return null;
 		}
 
@@ -1847,18 +2502,29 @@
 		return this.pstmtResultMetaData;
 	}
 
+	protected boolean isSelectQuery() {
+		return StringUtils.startsWithIgnoreCaseAndWs(
+				StringUtils.stripComments(this.originalSql,
+						"'\"", "'\"", true, false, true, true), 
+						"SELECT");
+	}
+
 	/**
 	 * @see PreparedStatement#getParameterMetaData()
 	 */
 	public ParameterMetaData getParameterMetaData() 
 		throws SQLException {
-		if (this.parameterMetaData == null) {
+	if (this.parameterMetaData == null) {
+		if (this.connection.getGenerateSimpleParameterMetadata()) {
+			this.parameterMetaData = new MysqlParameterMetadata(this.parameterCount);
+		} else {
 			this.parameterMetaData = new MysqlParameterMetadata(
-					null, this.parameterCount);
+				null, this.parameterCount);
 		}
-		
-		return this.parameterMetaData;
 	}
+	
+	return this.parameterMetaData;
+}
 
 	ParseInfo getParseInfo() {
 		return this.parseInfo;
@@ -1914,12 +2580,15 @@
 		this.isStream = new boolean[this.parameterCount];
 		this.streamLengths = new int[this.parameterCount];
 		this.isNull = new boolean[this.parameterCount];
-
+		this.parameterTypes = new int[this.parameterCount];
+	
 		clearParameters();
 
 		for (int j = 0; j < this.parameterCount; j++) {
 			this.isStream[j] = false;
 		}
+		
+		this.statementAfterCommentsPos = this.parseInfo.statementStartPos;
 	}
 
 	boolean isNull(int paramIndex) {
@@ -1969,7 +2638,8 @@
 				this.eventSink.consumeEvent(new ProfilerEvent(
 						ProfilerEvent.TYPE_WARN, "", this.currentCatalog, //$NON-NLS-1$
 						this.connectionId, this.getId(), -1, System
-								.currentTimeMillis(), 0, null,
+								.currentTimeMillis(), 0, Constants.MILLIS_I18N,
+								null,
 						this.pointOfOrigin, message));
 			}
 		}
@@ -1985,6 +2655,7 @@
 		this.streamLengths = null;
 		this.isNull = null;
 		this.streamConvertBuf = null;
+		this.parameterTypes = null;
 	}
 
 	/**
@@ -2054,6 +2725,8 @@
 		} else {
 			setInternal(parameterIndex, StringUtils
 					.fixDecimalExponent(StringUtils.consistentToString(x)));
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DECIMAL;
 		}
 	}
 
@@ -2101,9 +2774,15 @@
 			this.isStream[parameterIndex - 1 + parameterIndexOffset] = true;
 			this.streamLengths[parameterIndex - 1 + parameterIndexOffset] = length;
 			this.isNull[parameterIndex - 1 + parameterIndexOffset] = false;
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BLOB;
 		}
 	}
 
+	public void setBlob(int parameterIndex, InputStream inputStream, long length)
+			throws SQLException {
+		setBinaryStream(parameterIndex, inputStream, (int)length);
+	}
+
 	/**
 	 * JDBC 2.0 Set a BLOB parameter.
 	 * 
@@ -2127,6 +2806,8 @@
 			bytesOut.write('\'');
 
 			setInternal(i, bytesOut.toByteArray());
+			
+			this.parameterTypes[i - 1 + getParameterIndexOffset()] = Types.BLOB;
 		}
 	}
 
@@ -2147,6 +2828,8 @@
 			setInternal(parameterIndex, x ? "1" : "0"); //$NON-NLS-1$ //$NON-NLS-2$
 		} else {
 			setInternal(parameterIndex, x ? "'t'" : "'f'"); //$NON-NLS-1$ //$NON-NLS-2$
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BOOLEAN;
 		}
 	}
 
@@ -2164,6 +2847,8 @@
 	 */
 	public void setByte(int parameterIndex, byte x) throws SQLException {
 		setInternal(parameterIndex, String.valueOf(x));
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TINYINT;
 	}
 
 	/**
@@ -2181,6 +2866,10 @@
 	 */
 	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
 		setBytes(parameterIndex, x, true, true);
+		
+		if (x != null) {
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BINARY;
+		}
 	}
 
 	protected void setBytes(int parameterIndex, byte[] x,
@@ -2407,6 +3096,8 @@
 						}
 					}
 				}
+				
+				this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB;
 			}
 		} catch (java.io.IOException ioEx) {
 			throw SQLError.createSQLException(ioEx.toString(),
@@ -2428,22 +3119,23 @@
 	public void setClob(int i, Clob x) throws SQLException {
 		if (x == null) {
 			setNull(i, Types.CLOB);
+		} else {
 
-			return;
-		}
-
-		String forcedEncoding = this.connection.getClobCharacterEncoding();
+			String forcedEncoding = this.connection.getClobCharacterEncoding();
+			
+			if (forcedEncoding == null) {
+				setString(i, x.getSubString(1L, (int) x.length()));
+			} else {
+				try {
+					setBytes(i, x.getSubString(1L, 
+							(int)x.length()).getBytes(forcedEncoding));
+				} catch (UnsupportedEncodingException uee) {
+					throw SQLError.createSQLException("Unsupported character encoding " + 
+							forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+			}
 		
-		if (forcedEncoding == null) {
-			setString(i, x.getSubString(1L, (int) x.length()));
-		} else {
-			try {
-				setBytes(i, x.getSubString(1L, 
-						(int)x.length()).getBytes(forcedEncoding));
-			} catch (UnsupportedEncodingException uee) {
-				throw SQLError.createSQLException("Unsupported character encoding " + 
-						forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
-			}
+			this.parameterTypes[i - 1 + getParameterIndexOffset()] = Types.CLOB;
 		}
 	}
 
@@ -2469,6 +3161,8 @@
 			SimpleDateFormat dateFormatter = new SimpleDateFormat(
 					"''yyyy-MM-dd''", Locale.US); //$NON-NLS-1$
 			setInternal(parameterIndex, dateFormatter.format(x));
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DATE;
 		}
 	}
 
@@ -2516,6 +3210,8 @@
 
 		setInternal(parameterIndex, StringUtils.fixDecimalExponent(String
 				.valueOf(x)));
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DOUBLE;
 	}
 
 	/**
@@ -2533,6 +3229,8 @@
 	public void setFloat(int parameterIndex, float x) throws SQLException {
 		setInternal(parameterIndex, StringUtils.fixDecimalExponent(String
 				.valueOf(x)));
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.FLOAT;
 	}
 
 	/**
@@ -2549,17 +3247,29 @@
 	 */
 	public void setInt(int parameterIndex, int x) throws SQLException {
 		setInternal(parameterIndex, String.valueOf(x));
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.INTEGER;
 	}
 
-	private final void setInternal(int paramIndex, byte[] val)
+	protected final void setInternal(int paramIndex, byte[] val)
 			throws SQLException {
 		if (this.isClosed) {
 			throw SQLError.createSQLException(Messages.getString("PreparedStatement.48"), //$NON-NLS-1$
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 		}
-		
+
 		int parameterIndexOffset = getParameterIndexOffset();
 		
+		checkBounds(paramIndex, parameterIndexOffset);
+
+		this.isStream[paramIndex - 1 + parameterIndexOffset] = false;
+		this.isNull[paramIndex - 1 + parameterIndexOffset] = false;
+		this.parameterStreams[paramIndex - 1 + parameterIndexOffset] = null;
+		this.parameterValues[paramIndex - 1 + parameterIndexOffset] = val;
+	}
+
+	private void checkBounds(int paramIndex, int parameterIndexOffset)
+			throws SQLException {
 		if ((paramIndex < 1)) {
 			throw SQLError.createSQLException(
 					Messages.getString("PreparedStatement.49") //$NON-NLS-1$
@@ -2575,14 +3285,9 @@
 			throw SQLError.createSQLException("Can't set IN parameter for return value of stored function call.", 
 					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
 		}
-
-		this.isStream[paramIndex - 1 + parameterIndexOffset] = false;
-		this.isNull[paramIndex - 1 + parameterIndexOffset] = false;
-		this.parameterStreams[paramIndex - 1 + parameterIndexOffset] = null;
-		this.parameterValues[paramIndex - 1 + parameterIndexOffset] = val;
 	}
 
-	private final void setInternal(int paramIndex, String val)
+	protected final void setInternal(int paramIndex, String val)
 			throws SQLException {
 		checkClosed();
 		
@@ -2614,6 +3319,8 @@
 	 */
 	public void setLong(int parameterIndex, long x) throws SQLException {
 		setInternal(parameterIndex, String.valueOf(x));
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.BIGINT;
 	}
 
 	/**
@@ -2635,6 +3342,8 @@
 	public void setNull(int parameterIndex, int sqlType) throws SQLException {
 		setInternal(parameterIndex, "null"); //$NON-NLS-1$
 		this.isNull[parameterIndex - 1] = true;
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.NULL;
 	}
 
 	/**
@@ -2657,6 +3366,8 @@
 	public void setNull(int parameterIndex, int sqlType, String arg)
 			throws SQLException {
 		setNull(parameterIndex, sqlType);
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.NULL;
 	}
 
 	private void setNumericObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException {
@@ -2664,7 +3375,7 @@
 
 		if (parameterObj instanceof Boolean) {
 			parameterAsNum = ((Boolean) parameterObj)
-					.booleanValue() ? new Integer(1) : new Integer(
+					.booleanValue() ? Constants.integerValueOf(1) : Constants.integerValueOf(
 					0);
 		} else if (parameterObj instanceof String) {
 			switch (targetSqlType) {
@@ -2672,8 +3383,8 @@
 				boolean parameterAsBoolean = "true"
 						.equalsIgnoreCase((String) parameterObj);
 
-				parameterAsNum = parameterAsBoolean ? new Integer(1)
-						: new Integer(0);
+				parameterAsNum = parameterAsBoolean ? Constants.integerValueOf(1)
+						: Constants.integerValueOf(0);
 
 				break;
 
@@ -2829,9 +3540,10 @@
 				setBlob(parameterIndex, (java.sql.Blob) parameterObj);
 			} else if (parameterObj instanceof java.sql.Clob) {
 				setClob(parameterIndex, (java.sql.Clob) parameterObj);
-			} else if (parameterObj instanceof java.util.Date) {
+			} else if (this.connection.getTreatUtilDateAsTimestamp() && 
+				parameterObj instanceof java.util.Date) {
 				setTimestamp(parameterIndex, new Timestamp(
-						((java.util.Date) parameterObj).getTime()));
+				((java.util.Date) parameterObj).getTime()));
 			} else if (parameterObj instanceof BigInteger) {
 				setString(parameterIndex, parameterObj.toString());
 			} else {
@@ -3084,9 +3796,11 @@
 		}
 	}
 
-	private int setOneBatchedParameterSet(
+	protected int setOneBatchedParameterSet(
 			java.sql.PreparedStatement batchedStatement, int batchedParamIndex,
-			BatchParams paramArg) throws SQLException {
+			Object paramSet) throws SQLException {
+		BatchParams paramArg = (BatchParams)paramSet;
+		
 		boolean[] isNullBatch = paramArg.isNull;
 		boolean[] isStreamBatch = paramArg.isStream;
 
@@ -3108,7 +3822,7 @@
 
 		return batchedParamIndex;
 	}
-
+	
 	/**
 	 * JDBC 2.0 Set a REF(&lt;structured-type&gt;) parameter.
 	 * 
@@ -3155,6 +3869,8 @@
 		this.retrieveGeneratedKeys = retrieveGeneratedKeys;
 	}
 
+
+
 	/**
 	 * Sets the value for the placeholder as a serialized Java object (used by
 	 * various forms of setObject()
@@ -3181,6 +3897,7 @@
 			byte[] buf = bytesOut.toByteArray();
 			ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf);
 			setBinaryStream(parameterIndex, bytesIn, buf.length);
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.JAVA_OBJECT;
 		} catch (Exception ex) {
 			throw SQLError.createSQLException(Messages.getString("PreparedStatement.54") //$NON-NLS-1$
 					+ ex.getClass().getName(),
@@ -3202,6 +3919,8 @@
 	 */
 	public void setShort(int parameterIndex, short x) throws SQLException {
 		setInternal(parameterIndex, String.valueOf(x));
+		
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.SMALLINT;
 	}
 
 	/**
@@ -3229,53 +3948,9 @@
 			if (this.connection.isNoBackslashEscapesSet()) {
 				// Scan for any nasty chars
 
-				boolean needsHexEscape = false;
+				boolean needsHexEscape = isEscapeNeededForString(x,
+						stringLength);
 
-				for (int i = 0; i < stringLength; ++i) {
-					char c = x.charAt(i);
-
-					switch (c) {
-					case 0: /* Must be escaped for 'mysql' */
-
-						needsHexEscape = true;
-						break;
-
-					case '\n': /* Must be escaped for logs */
-						needsHexEscape = true;
-
-						break;
-
-					case '\r':
-						needsHexEscape = true;
-						break;
-
-					case '\\':
-						needsHexEscape = true;
-
-						break;
-
-					case '\'':
-						needsHexEscape = true;
-
-						break;
-
-					case '"': /* Better safe than sorry */
-						needsHexEscape = true;
-
-						break;
-
-					case '\032': /* This gives problems on Win32 */
-						needsHexEscape = true;
-						break;
-					}
-
-					if (needsHexEscape) {
-						break; // no need to scan more
-					}
-				}
-
-				
-
 				if (!needsHexEscape) {
 					byte[] parameterAsBytes = null;
 
@@ -3314,88 +3989,153 @@
 				return;
 			}
 
-			StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
-			buf.append('\'');
+			String parameterAsString = x;
+			boolean needsQuoted = true;
+			
+			if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
+				needsQuoted = false; // saves an allocation later
+				
+				StringBuffer buf = new StringBuffer((int) (x.length() * 1.1));
+				
+				buf.append('\'');
+	
+				//
+				// Note: buf.append(char) is _faster_ than
+				// appending in blocks, because the block
+				// append requires a System.arraycopy()....
+				// go figure...
+				//
+	
+				for (int i = 0; i < stringLength; ++i) {
+					char c = x.charAt(i);
+	
+					switch (c) {
+					case 0: /* Must be escaped for 'mysql' */
+						buf.append('\\');
+						buf.append('0');
+	
+						break;
+	
+					case '\n': /* Must be escaped for logs */
+						buf.append('\\');
+						buf.append('n');
+	
+						break;
+	
+					case '\r':
+						buf.append('\\');
+						buf.append('r');
+	
+						break;
+	
+					case '\\':
+						buf.append('\\');
+						buf.append('\\');
+	
+						break;
+	
+					case '\'':
+						buf.append('\\');
+						buf.append('\'');
+	
+						break;
+	
+					case '"': /* Better safe than sorry */
+						if (this.usingAnsiMode) {
+							buf.append('\\');
+						}
+	
+						buf.append('"');
+	
+						break;
+	
+					case '\032': /* This gives problems on Win32 */
+						buf.append('\\');
+						buf.append('Z');
+	
+						break;
+	
+					default:
+						buf.append(c);
+					}
+				}
+	
+				buf.append('\'');
+	
+				parameterAsString = buf.toString();
+			}
 
-			//
-			// Note: buf.append(char) is _faster_ than
-			// appending in blocks, because the block
-			// append requires a System.arraycopy()....
-			// go figure...
-			//
+			byte[] parameterAsBytes = null;
 
-			for (int i = 0; i < stringLength; ++i) {
-				char c = x.charAt(i);
+			if (!this.isLoadDataQuery) {
+				if (needsQuoted) {
+					parameterAsBytes = StringUtils.getBytesWrapped(parameterAsString,
+						'\'', '\'', this.charConverter, this.charEncoding, this.connection
+								.getServerCharacterEncoding(), this.connection
+								.parserKnowsUnicode());
+				} else {
+					parameterAsBytes = StringUtils.getBytes(parameterAsString,
+							this.charConverter, this.charEncoding, this.connection
+									.getServerCharacterEncoding(), this.connection
+									.parserKnowsUnicode());
+				}
+			} else {
+				// Send with platform character encoding
+				parameterAsBytes = parameterAsString.getBytes();
+			}
 
-				switch (c) {
-				case 0: /* Must be escaped for 'mysql' */
-					buf.append('\\');
-					buf.append('0');
+			setInternal(parameterIndex, parameterAsBytes);
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.VARCHAR;
+		}
+	}
 
-					break;
+	private boolean isEscapeNeededForString(String x, int stringLength) {
+		boolean needsHexEscape = false;
 
-				case '\n': /* Must be escaped for logs */
-					buf.append('\\');
-					buf.append('n');
+		for (int i = 0; i < stringLength; ++i) {
+			char c = x.charAt(i);
 
-					break;
+			switch (c) {
+			case 0: /* Must be escaped for 'mysql' */
 
-				case '\r':
-					buf.append('\\');
-					buf.append('r');
+				needsHexEscape = true;
+				break;
 
-					break;
+			case '\n': /* Must be escaped for logs */
+				needsHexEscape = true;
 
-				case '\\':
-					buf.append('\\');
-					buf.append('\\');
+				break;
 
-					break;
+			case '\r':
+				needsHexEscape = true;
+				break;
 
-				case '\'':
-					buf.append('\\');
-					buf.append('\'');
+			case '\\':
+				needsHexEscape = true;
 
-					break;
+				break;
 
-				case '"': /* Better safe than sorry */
-					if (this.usingAnsiMode) {
-						buf.append('\\');
-					}
+			case '\'':
+				needsHexEscape = true;
 
-					buf.append('"');
+				break;
 
-					break;
+			case '"': /* Better safe than sorry */
+				needsHexEscape = true;
 
-				case '\032': /* This gives problems on Win32 */
-					buf.append('\\');
-					buf.append('Z');
+				break;
 
-					break;
-
-				default:
-					buf.append(c);
-				}
+			case '\032': /* This gives problems on Win32 */
+				needsHexEscape = true;
+				break;
 			}
 
-			buf.append('\'');
-
-			String parameterAsString = buf.toString();
-
-			byte[] parameterAsBytes = null;
-
-			if (!this.isLoadDataQuery) {
-				parameterAsBytes = StringUtils.getBytes(parameterAsString,
-						this.charConverter, this.charEncoding, this.connection
-								.getServerCharacterEncoding(), this.connection
-								.parserKnowsUnicode());
-			} else {
-				// Send with platform character encoding
-				parameterAsBytes = parameterAsString.getBytes();
+			if (needsHexEscape) {
+				break; // no need to scan more
 			}
-
-			setInternal(parameterIndex, parameterAsBytes);
 		}
+		return needsHexEscape;
 	}
 
 	/**
@@ -3431,7 +4171,7 @@
 	 */
 	public void setTime(int parameterIndex, Time x)
 			throws java.sql.SQLException {
-		setTimeInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+		setTimeInternal(parameterIndex, x, null, Util.getDefaultTimeZone(), false);
 	}
 
 	/**
@@ -3468,6 +4208,8 @@
 			}
 			
 			setInternal(parameterIndex, "'" + x.toString() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIME;
 		}
 	}
 
@@ -3504,7 +4246,7 @@
 	 */
 	public void setTimestamp(int parameterIndex, Timestamp x)
 			throws java.sql.SQLException {
-		setTimestampInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+		setTimestampInternal(parameterIndex, x, null, Util.getDefaultTimeZone(), false);
 	}
 
 	/**
@@ -3543,15 +4285,96 @@
 					.getServerTimezoneTZ(), rollForward);
 			}
 
-			if (this.tsdf == null) {
-				this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US); //$NON-NLS-1$
+			if (this.connection.getUseSSPSCompatibleTimezoneShift()) {
+				doSSPSCompatibleTimezoneShift(parameterIndex, x, sessionCalendar);
+			} else {
+				
+				if (this.tsdf == null) {
+					this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US); //$NON-NLS-1$
+				}
+				
+				timestampString = this.tsdf.format(x);
+
+				setInternal(parameterIndex, timestampString); // SimpleDateFormat is not
+															  // thread-safe
 			}
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIMESTAMP;
+		}
+	}
 
-			timestampString = this.tsdf.format(x);
+	private void doSSPSCompatibleTimezoneShift(int parameterIndex, Timestamp x, Calendar sessionCalendar) throws SQLException {
+		Calendar sessionCalendar2 = (this.connection
+				.getUseJDBCCompliantTimezoneShift()) ? this.connection
+				.getUtcCalendar()
+				: getCalendarInstanceForSessionOrNew();
 
-			setInternal(parameterIndex, timestampString); // SimpleDateFormat
-			// is not
-			// thread-safe
+		synchronized (sessionCalendar2) {
+			java.util.Date oldTime = sessionCalendar2.getTime();
+
+			try {
+				sessionCalendar2.setTime(x);
+
+				int year = sessionCalendar2.get(Calendar.YEAR);
+				int month = sessionCalendar2.get(Calendar.MONTH) + 1;
+				int date = sessionCalendar2.get(Calendar.DAY_OF_MONTH);
+
+				int hour = sessionCalendar2.get(Calendar.HOUR_OF_DAY);
+				int minute = sessionCalendar2.get(Calendar.MINUTE);
+				int seconds = sessionCalendar2.get(Calendar.SECOND);
+
+				StringBuffer tsBuf = new StringBuffer();
+
+				tsBuf.append('\'');
+				tsBuf.append(year);
+
+				tsBuf.append("-");
+
+				if (month < 10) {
+					tsBuf.append('0');
+				}
+
+				tsBuf.append(month);
+
+				tsBuf.append('-');
+
+				if (date < 10) {
+					tsBuf.append('0');
+				}
+
+				tsBuf.append(date);
+
+				tsBuf.append(' ');
+
+				if (hour < 10) {
+					tsBuf.append('0');
+				}
+
+				tsBuf.append(hour);
+
+				tsBuf.append(':');
+
+				if (minute < 10) {
+					tsBuf.append('0');
+				}
+
+				tsBuf.append(minute);
+
+				tsBuf.append(':');
+
+				if (seconds < 10) {
+					tsBuf.append('0');
+				}
+
+				tsBuf.append(seconds);
+
+				tsBuf.append('\'');
+
+				setInternal(parameterIndex, tsBuf.toString());
+
+			} finally {
+				sessionCalendar.setTime(oldTime);
+			}
 		}
 	}
 
@@ -3585,6 +4408,8 @@
 			setNull(parameterIndex, java.sql.Types.VARCHAR);
 		} else {
 			setBinaryStream(parameterIndex, x, length);
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB;
 		}
 	}
 
@@ -3594,6 +4419,8 @@
 	public void setURL(int parameterIndex, URL arg) throws SQLException {
 		if (arg != null) {
 			setString(parameterIndex, arg.toString());
+			
+			this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.DATALINK;
 		} else {
 			setNull(parameterIndex, Types.CHAR);
 		}
@@ -3694,17 +4521,17 @@
 
 			int lengthLeftToRead = streamLength - bc;
 
-			if (this.connection.versionMeetsMinimum(4, 1, 0)) {
-				bytesOut.write('_');
-				bytesOut.write('b');
-				bytesOut.write('i');
-				bytesOut.write('n');
-				bytesOut.write('a');
-				bytesOut.write('r');
-				bytesOut.write('y');
-			}
-
 			if (escape) {
+				if (this.connection.versionMeetsMinimum(4, 1, 0)) {
+					bytesOut.write('_');
+					bytesOut.write('b');
+					bytesOut.write('i');
+					bytesOut.write('n');
+					bytesOut.write('a');
+					bytesOut.write('r');
+					bytesOut.write('y');
+				}
+				
 				bytesOut.write('\'');
 			}
 
@@ -3743,7 +4570,7 @@
 			}
 		}
 	}
-
+	
 	/**
 	 * Returns this PreparedStatement represented as a string.
 	 * 
@@ -3762,6 +4589,12 @@
 
 		return buf.toString();
 	}
+
+
+
+	public synchronized boolean isClosed() throws SQLException {
+		return this.isClosed;
+	}
 	
 	/** 
 	 * For calling stored functions, this will be -1 as we don't really count
@@ -3773,4 +4606,436 @@
 	protected int getParameterIndexOffset() {
 		return 0;
 	}
+	
+	public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
+		setAsciiStream(parameterIndex, x, -1);
+	}
+
+	public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
+		setAsciiStream(parameterIndex, x, (int)length);
+		this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.CLOB;
+	}
+
+	public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
+		setBinaryStream(parameterIndex, x, -1);
+	}
+
+	public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
+		setBinaryStream(parameterIndex, x, (int)length);	
+	}
+
+	public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
+		setBinaryStream(parameterIndex, inputStream);
+	}
+
+	public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
+		setCharacterStream(parameterIndex, reader, -1);
+	}
+
+	public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
+		setCharacterStream(parameterIndex, reader, (int)length);
+		
+	}
+
+	public void setClob(int parameterIndex, Reader reader) throws SQLException {
+		setCharacterStream(parameterIndex, reader);
+		
+	}
+
+	public void setClob(int parameterIndex, Reader reader, long length)
+			throws SQLException {
+		setCharacterStream(parameterIndex, reader, length);
+	}
+	
+	public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
+		setNCharacterStream(parameterIndex, value, -1);
+	}
+	
+	/**
+	 * Set a parameter to a Java String value. The driver converts this to a SQL
+	 * VARCHAR or LONGVARCHAR value with introducer _utf8 (depending on the 
+	 * arguments size relative to the driver's limits on VARCHARs) when it sends 
+	 * it to the database. If charset is set as utf8, this method just call setString.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setNString(int parameterIndex, String x) throws SQLException {
+	    if (this.charEncoding.equalsIgnoreCase("UTF-8")
+	            || this.charEncoding.equalsIgnoreCase("utf8")) {
+	        setString(parameterIndex, x);
+	        return;
+	    }
+	
+	    // if the passed string is null, then set this column to null
+	    if (x == null) {
+	        setNull(parameterIndex, java.sql.Types.CHAR); 
+	    } else {
+	        int stringLength = x.length();
+	        // Ignore sql_mode=NO_BACKSLASH_ESCAPES in current implementation.
+	
+	        // Add introducer _utf8 for NATIONAL CHARACTER
+	        StringBuffer buf = new StringBuffer((int) (x.length() * 1.1 + 4));
+	        buf.append("_utf8");
+	        buf.append('\'');
+	
+	        //
+	        // Note: buf.append(char) is _faster_ than
+	        // appending in blocks, because the block
+	        // append requires a System.arraycopy()....
+	        // go figure...
+	        //
+	
+	        for (int i = 0; i < stringLength; ++i) {
+	            char c = x.charAt(i);
+	
+	            switch (c) {
+	            case 0: /* Must be escaped for 'mysql' */
+	                buf.append('\\');
+	                buf.append('0');
+	
+	                break;
+	
+	            case '\n': /* Must be escaped for logs */
+	                buf.append('\\');
+	                buf.append('n');
+	
+	                break;
+	
+	            case '\r':
+	                buf.append('\\');
+	                buf.append('r');
+	
+	                break;
+	
+	            case '\\':
+	                buf.append('\\');
+	                buf.append('\\');
+	
+	                break;
+	
+	            case '\'':
+	                buf.append('\\');
+	                buf.append('\'');
+	
+	                break;
+	
+	            case '"': /* Better safe than sorry */
+	                if (this.usingAnsiMode) {
+	                    buf.append('\\');
+	                }
+	
+	                buf.append('"');
+	
+	                break;
+	
+	            case '\032': /* This gives problems on Win32 */
+	                buf.append('\\');
+	                buf.append('Z');
+	
+	                break;
+	
+	            default:
+	                buf.append(c);
+	            }
+	        }
+	
+	        buf.append('\'');
+	
+	        String parameterAsString = buf.toString();
+	
+	        byte[] parameterAsBytes = null;
+	
+	        if (!this.isLoadDataQuery) {
+	            parameterAsBytes = StringUtils.getBytes(parameterAsString,
+	                    this.connection.getCharsetConverter("UTF-8"), "UTF-8", 
+	                            this.connection.getServerCharacterEncoding(),
+	                            this.connection.parserKnowsUnicode());
+	        } else {
+	            // Send with platform character encoding
+	            parameterAsBytes = parameterAsString.getBytes();
+	        }
+	
+	        setInternal(parameterIndex, parameterAsBytes);
+	        
+	        this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = -9; /* Types.NVARCHAR */
+	    }
+	}
+	
+	/**
+	 * JDBC 2.0 When a very large UNICODE value is input to a LONGVARCHAR
+	 * parameter, it may be more practical to send it via a java.io.Reader. JDBC
+	 * will read the data from the stream as needed, until it reaches
+	 * end-of-file. The JDBC driver will do any necessary conversion from
+	 * UNICODE to the database char format.
+	 * 
+	 * <P>
+	 * <B>Note:</B> This stream object can either be a standard Java stream
+	 * object or your own subclass that implements the standard interface.
+	 * </p>
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param reader
+	 *            the java reader which contains the UNICODE data
+	 * @param length
+	 *            the number of characters in the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public void setNCharacterStream(int parameterIndex, Reader reader,
+			long length) throws SQLException {
+	    try {
+	        if (reader == null) {
+	            setNull(parameterIndex, java.sql.Types.LONGVARCHAR);
+	            
+	        } else {
+	            char[] c = null;
+	            int len = 0;
+	
+	            boolean useLength = this.connection
+	                    .getUseStreamLengthsInPrepStmts();
+	            
+	            // Ignore "clobCharacterEncoding" because utf8 should be used this time.
+	
+	            if (useLength && (length != -1)) {
+	                c = new char[(int) length];  // can't take more than Integer.MAX_VALUE
+	
+	                int numCharsRead = readFully(reader, c, (int) length); // blocks
+	                // until
+	                // all
+	                // read
+	                setNString(parameterIndex, new String(c, 0, numCharsRead));
+	
+	            } else {
+	                c = new char[4096];
+	
+	                StringBuffer buf = new StringBuffer();
+	
+	                while ((len = reader.read(c)) != -1) {
+	                    buf.append(c, 0, len);
+	                }
+	
+	                setNString(parameterIndex, buf.toString());
+	            }
+	            
+	            this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = 2011; /* Types.NCLOB */
+	        }
+	    } catch (java.io.IOException ioEx) {
+	        throw SQLError.createSQLException(ioEx.toString(),
+	                SQLError.SQL_STATE_GENERAL_ERROR);
+	    }
+	}
+	
+	public void setNClob(int parameterIndex, Reader reader) throws SQLException {
+		setNCharacterStream(parameterIndex, reader);		
+	}
+
+	/**
+	 * JDBC 4.0 Set a NCLOB parameter.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param reader
+	 *            the java reader which contains the UNICODE data
+	 * @param length
+	 *            the number of characters in the stream
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setNClob(int parameterIndex, Reader reader, long length)
+			throws SQLException {
+	    if (reader == null) {
+	        setNull(parameterIndex, java.sql.Types.LONGVARCHAR);
+	    } else {
+	        setNCharacterStream(parameterIndex, reader, length);
+	    }
+	}
+	
+	public ParameterBindings getParameterBindings() throws SQLException {
+		return new EmulatedPreparedStatementBindings();
+	}
+	
+	class EmulatedPreparedStatementBindings implements ParameterBindings {
+
+		private ResultSetImpl bindingsAsRs;
+		private boolean[] parameterIsNull;
+		
+		public EmulatedPreparedStatementBindings() throws SQLException {
+			List rows = new ArrayList();
+			parameterIsNull = new boolean[parameterCount];
+			System
+					.arraycopy(isNull, 0, this.parameterIsNull, 0,
+							parameterCount);
+			byte[][] rowData = new byte[parameterCount][];
+			Field[] typeMetadata = new Field[parameterCount];
+
+			for (int i = 0; i < parameterCount; i++) {
+				rowData[i] = getBytesRepresentation(i);
+
+				int charsetIndex = 0;
+
+				if (parameterTypes[i] == Types.BINARY
+						|| parameterTypes[i] == Types.BLOB) {
+					charsetIndex = 63;
+				} else {
+					String mysqlEncodingName = CharsetMapping
+							.getMysqlEncodingForJavaEncoding(connection
+									.getEncoding(), connection);
+					charsetIndex = CharsetMapping
+							.getCharsetIndexForMysqlEncodingName(mysqlEncodingName);
+				}
+
+				Field parameterMetadata = new Field(null, "parameter_"
+						+ (i + 1), charsetIndex, parameterTypes[i],
+						rowData[i].length);
+				parameterMetadata.setConnection(connection);
+				typeMetadata[i] = parameterMetadata;
+			}
+
+			rows.add(new ByteArrayRow(rowData));
+
+			this.bindingsAsRs = new ResultSetImpl(connection.getCatalog(),
+					typeMetadata, new RowDataStatic(rows), connection, null);
+			this.bindingsAsRs.next();
+		}
+		
+		public Array getArray(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getArray(parameterIndex);
+		}
+
+		public InputStream getAsciiStream(int parameterIndex)
+				throws SQLException {
+			return this.bindingsAsRs.getAsciiStream(parameterIndex);
+		}
+
+		public BigDecimal getBigDecimal(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getBigDecimal(parameterIndex);
+		}
+
+		public InputStream getBinaryStream(int parameterIndex)
+				throws SQLException {
+			return this.bindingsAsRs.getBinaryStream(parameterIndex);
+		}
+
+		public java.sql.Blob getBlob(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getBlob(parameterIndex);
+		}
+
+		public boolean getBoolean(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getBoolean(parameterIndex);
+		}
+
+		public byte getByte(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getByte(parameterIndex);
+		}
+
+		public byte[] getBytes(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getBytes(parameterIndex);
+		}
+
+		public Reader getCharacterStream(int parameterIndex)
+				throws SQLException {
+			return this.bindingsAsRs.getCharacterStream(parameterIndex);
+		}
+
+		public java.sql.Clob getClob(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getClob(parameterIndex);
+		}
+
+		public Date getDate(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getDate(parameterIndex);
+		}
+
+		public double getDouble(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getDouble(parameterIndex);
+		}
+
+		public float getFloat(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getFloat(parameterIndex);
+		}
+
+		public int getInt(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getInt(parameterIndex);
+		}
+
+		public long getLong(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getLong(parameterIndex);
+		}
+
+		public Reader getNCharacterStream(int parameterIndex)
+				throws SQLException {
+			return this.bindingsAsRs.getCharacterStream(parameterIndex);
+		}
+
+		public Reader getNClob(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getCharacterStream(parameterIndex);
+		}
+
+		public Object getObject(int parameterIndex) throws SQLException {
+			checkBounds(parameterIndex, 0);
+			
+			if (parameterIsNull[parameterIndex - 1]) {
+				return null;
+			}
+			
+			// we can't rely on the default mapping for JDBC's 
+			// ResultSet.getObject() for numerics, they're not one-to-one with
+			// PreparedStatement.setObject
+			
+			switch (parameterTypes[parameterIndex - 1]) {
+			case Types.TINYINT:
+				return new Byte(getByte(parameterIndex));
+			case Types.SMALLINT:
+				return new Short(getShort(parameterIndex));
+			case Types.INTEGER:
+				return new Integer(getInt(parameterIndex));
+			case Types.BIGINT:
+				return new Long(getLong(parameterIndex));
+			case Types.FLOAT:
+				return new Float(getFloat(parameterIndex));
+			case Types.DOUBLE: 
+				return new Double(getDouble(parameterIndex));
+			default:
+				return this.bindingsAsRs.getObject(parameterIndex);
+			}
+		}
+
+		public Ref getRef(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getRef(parameterIndex);
+		}
+
+		public short getShort(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getShort(parameterIndex);
+		}
+
+		public String getString(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getString(parameterIndex);
+		}
+
+		public Time getTime(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getTime(parameterIndex);
+		}
+
+		public Timestamp getTimestamp(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getTimestamp(parameterIndex);
+		}
+
+		public URL getURL(int parameterIndex) throws SQLException {
+			return this.bindingsAsRs.getURL(parameterIndex);
+		}
+
+		public boolean isNull(int parameterIndex) throws SQLException {
+			checkBounds(parameterIndex, 0);
+			
+			return this.parameterIsNull[parameterIndex -1];
+		}	
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/ReplicationConnection.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ReplicationConnection.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ReplicationConnection.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2004 MySQL AB
+ Copyright (C) 2004-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -21,13 +21,17 @@
  */
 package com.mysql.jdbc;
 
+import java.sql.Array;
+import java.sql.Blob;
 import java.sql.CallableStatement;
+import java.sql.Clob;
 import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.sql.Savepoint;
 import java.sql.Statement;
+import java.sql.Struct;
 import java.util.Map;
 import java.util.Properties;
 
@@ -39,12 +43,12 @@
  * @version $Id: ReplicationConnection.java,v 1.1.2.1 2005/05/13 18:58:38
  *          mmatthews Exp $
  */
-public class ReplicationConnection implements java.sql.Connection {
-	private Connection currentConnection;
+public class ReplicationConnection implements java.sql.Connection, PingTarget {
+	protected Connection currentConnection;
 
-	private Connection masterConnection;
+	protected Connection masterConnection;
 
-	private Connection slavesConnection;
+	protected Connection slavesConnection;
 
 	public ReplicationConnection(Properties masterProperties,
 			Properties slaveProperties) throws SQLException {
@@ -127,7 +131,10 @@
 	 * @see java.sql.Connection#createStatement()
 	 */
 	public Statement createStatement() throws SQLException {
-		return this.currentConnection.createStatement();
+		Statement stmt = this.currentConnection.createStatement();
+		((com.mysql.jdbc.Statement) stmt).setPingTarget(this);
+		
+		return stmt;
 	}
 
 	/*
@@ -137,8 +144,12 @@
 	 */
 	public synchronized Statement createStatement(int resultSetType,
 			int resultSetConcurrency) throws SQLException {
-		return this.currentConnection.createStatement(resultSetType,
+		Statement stmt = this.currentConnection.createStatement(resultSetType,
 				resultSetConcurrency);
+		
+		((com.mysql.jdbc.Statement) stmt).setPingTarget(this);
+		
+		return stmt;
 	}
 
 	/*
@@ -149,8 +160,12 @@
 	public synchronized Statement createStatement(int resultSetType,
 			int resultSetConcurrency, int resultSetHoldability)
 			throws SQLException {
-		return this.currentConnection.createStatement(resultSetType,
+		Statement stmt = this.currentConnection.createStatement(resultSetType,
 				resultSetConcurrency, resultSetHoldability);
+		
+		((com.mysql.jdbc.Statement) stmt).setPingTarget(this);
+		
+		return stmt;
 	}
 
 	/*
@@ -293,7 +308,11 @@
 	 * @see java.sql.Connection#prepareStatement(java.lang.String)
 	 */
 	public PreparedStatement prepareStatement(String sql) throws SQLException {
-		return this.currentConnection.prepareStatement(sql);
+		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql);
+
+		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
+		
+		return pstmt;
 	}
 
 	/*
@@ -303,7 +322,11 @@
 	 */
 	public synchronized PreparedStatement prepareStatement(String sql,
 			int autoGeneratedKeys) throws SQLException {
-		return this.currentConnection.prepareStatement(sql, autoGeneratedKeys);
+		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, autoGeneratedKeys);
+
+		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
+		
+		return pstmt;
 	}
 
 	/*
@@ -313,8 +336,12 @@
 	 */
 	public synchronized PreparedStatement prepareStatement(String sql,
 			int resultSetType, int resultSetConcurrency) throws SQLException {
-		return this.currentConnection.prepareStatement(sql, resultSetType,
+		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, resultSetType,
 				resultSetConcurrency);
+		
+		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
+		
+		return pstmt;
 	}
 
 	/*
@@ -326,8 +353,12 @@
 	public synchronized PreparedStatement prepareStatement(String sql,
 			int resultSetType, int resultSetConcurrency,
 			int resultSetHoldability) throws SQLException {
-		return this.currentConnection.prepareStatement(sql, resultSetType,
+		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, resultSetType,
 				resultSetConcurrency, resultSetHoldability);
+
+		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
+		
+		return pstmt;
 	}
 
 	/*
@@ -337,7 +368,11 @@
 	 */
 	public synchronized PreparedStatement prepareStatement(String sql,
 			int[] columnIndexes) throws SQLException {
-		return this.currentConnection.prepareStatement(sql, columnIndexes);
+		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, columnIndexes);
+		
+		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
+		
+		return pstmt;
 	}
 
 	/*
@@ -348,7 +383,11 @@
 	 */
 	public synchronized PreparedStatement prepareStatement(String sql,
 			String[] columnNames) throws SQLException {
-		return this.currentConnection.prepareStatement(sql, columnNames);
+		PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, columnNames);
+
+		((com.mysql.jdbc.Statement) pstmt).setPingTarget(this);
+		
+		return pstmt;
 	}
 
 	/*
@@ -512,4 +551,14 @@
 		
 		this.currentConnection = switchToConnection;
 	}
+
+	public synchronized void doPing() throws SQLException {
+		if (this.masterConnection != null) {
+			this.masterConnection.ping();
+		}
+		
+		if (this.slavesConnection != null) {
+			this.slavesConnection.ping();
+		}
+	}
 }

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetImpl.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetImpl.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetImpl.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,8675 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.PreparedStatement.ParseInfo;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+
+import java.lang.reflect.Constructor;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import java.sql.Array;
+import java.sql.DataTruncation;
+import java.sql.Date;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.TreeMap;
+
+/**
+ * A ResultSet provides access to a table of data generated by executing a
+ * Statement. The table rows are retrieved in sequence. Within a row its column
+ * values can be accessed in any order.
+ * 
+ * <P>
+ * A ResultSet maintains a cursor pointing to its current row of data. Initially
+ * the cursor is positioned before the first row. The 'next' method moves the
+ * cursor to the next row.
+ * </p>
+ * 
+ * <P>
+ * The getXXX methods retrieve column values for the current row. You can
+ * retrieve values either using the index number of the column, or by using the
+ * name of the column. In general using the column index will be more efficient.
+ * Columns are numbered from 1.
+ * </p>
+ * 
+ * <P>
+ * For maximum portability, ResultSet columns within each row should be read in
+ * left-to-right order and each column should be read only once.
+ * </p>
+ * 
+ * <P>
+ * For the getXXX methods, the JDBC driver attempts to convert the underlying
+ * data to the specified Java type and returns a suitable Java value. See the
+ * JDBC specification for allowable mappings from SQL types to Java types with
+ * the ResultSet getXXX methods.
+ * </p>
+ * 
+ * <P>
+ * Column names used as input to getXXX methods are case insenstive. When
+ * performing a getXXX using a column name, if several columns have the same
+ * name, then the value of the first matching column will be returned. The
+ * column name option is designed to be used when column names are used in the
+ * SQL Query. For columns that are NOT explicitly named in the query, it is best
+ * to use column numbers. If column names were used there is no way for the
+ * programmer to guarentee that they actually refer to the intended columns.
+ * </p>
+ * 
+ * <P>
+ * A ResultSet is automatically closed by the Statement that generated it when
+ * that Statement is closed, re-executed, or is used to retrieve the next result
+ * from a sequence of multiple results.
+ * </p>
+ * 
+ * <P>
+ * The number, types and properties of a ResultSet's columns are provided by the
+ * ResultSetMetaData object returned by the getMetaData method.
+ * </p>
+ * 
+ * @author Mark Matthews
+ * @version $Id: ResultSetImpl.java 6588 2007-09-15 21:01:29Z mmatthews $
+ * 
+ * @see ResultSetMetaData
+ * @see java.sql.ResultSet
+ */
+public class ResultSetImpl implements ResultSetInternalMethods {
+
+	private static final Constructor JDBC_4_RS_4_ARG_CTOR;
+	private static final Constructor JDBC_4_RS_6_ARG_CTOR;;
+	private static final Constructor JDBC_4_UPD_RS_6_ARG_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_RS_4_ARG_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4ResultSet").getConstructor(
+						new Class[] { Long.TYPE, Long.TYPE,
+								ConnectionImpl.class,
+								com.mysql.jdbc.StatementImpl.class });
+				JDBC_4_RS_6_ARG_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4ResultSet").getConstructor(
+						new Class[] { String.class, Field[].class,
+								RowData.class, 
+								ConnectionImpl.class,
+								com.mysql.jdbc.StatementImpl.class });
+				JDBC_4_UPD_RS_6_ARG_CTOR = Class.forName(
+						"com.mysql.jdbc.JDBC4UpdatableResultSet")
+						.getConstructor(
+								new Class[] { String.class, Field[].class,
+										RowData.class,
+										ConnectionImpl.class,
+										com.mysql.jdbc.StatementImpl.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_RS_4_ARG_CTOR = null;
+			JDBC_4_RS_6_ARG_CTOR = null;
+			JDBC_4_UPD_RS_6_ARG_CTOR = null;
+		}
+	}
+	
+	/**
+	 * Epsillon between Float.MIN_VALUE and the double representation of said value.
+	 */
+    protected static final double MIN_DIFF_PREC = Float.parseFloat(Float.toString(Float.MIN_VALUE))
+        - Double.parseDouble(Float.toString(Float.MIN_VALUE));
+    
+    /**
+	 * Epsillon between Float.MAX_VALUE and the double representation of said value.
+	 */
+    protected static final double MAX_DIFF_PREC = Float.parseFloat(Float.toString(Float.MAX_VALUE))
+        - Double.parseDouble(Float.toString(Float.MAX_VALUE));
+    
+	/** Counter used to generate IDs for profiling. */
+	protected static int resultCounter = 1;
+
+	/**
+	 * Converts the given value as a java long, to an 'unsigned' long, using the
+	 * java.math.BigInteger class.
+	 */
+	protected static BigInteger convertLongToUlong(long longVal) {
+		byte[] asBytes = new byte[8];
+		asBytes[7] = (byte) (longVal & 0xff);
+		asBytes[6] = (byte) (longVal >>> 8);
+		asBytes[5] = (byte) (longVal >>> 16);
+		asBytes[4] = (byte) (longVal >>> 24);
+		asBytes[3] = (byte) (longVal >>> 32);
+		asBytes[2] = (byte) (longVal >>> 40);
+		asBytes[1] = (byte) (longVal >>> 48);
+		asBytes[0] = (byte) (longVal >>> 56);
+
+		return new BigInteger(1, asBytes);
+	}
+
+	/** The catalog that was in use when we were created */
+	protected String catalog = null;
+
+	/** Map column names (and all of their permutations) to column indices */
+	protected Map columnNameToIndex = null;
+
+	/** Keep track of columns accessed */
+	protected boolean[] columnUsed = null;
+
+	/** The Connection instance that created us */
+	protected ConnectionImpl connection; // The connection that
+				   					     // created us
+
+	protected long connectionId = 0;
+	
+	/** The current row #, -1 == before start of result set */
+	protected int currentRow = -1; // Cursor to current row;
+
+	TimeZone defaultTimeZone;
+
+	/** Are we in the middle of doing updates to the current row? */
+	protected boolean doingUpdates = false;
+
+	protected ProfileEventSink eventSink = null;
+
+	Calendar fastDateCal = null;
+
+	/** The direction to fetch rows (always FETCH_FORWARD) */
+	protected int fetchDirection = FETCH_FORWARD;
+
+	/** The number of rows to fetch in one go... */
+	protected int fetchSize = 0;
+
+	/** The fields for this result set */
+	protected Field[] fields; // The fields
+
+	/**
+	 * First character of the query that created this result set...Used to
+	 * determine whether or not to parse server info messages in certain
+	 * circumstances.
+	 */
+	protected char firstCharOfQuery;
+
+	/** Map of fully-specified column names to column indices */
+	protected Map fullColumnNameToIndex = null;
+
+	protected boolean hasBuiltIndexMapping = false;
+
+	/**
+	 * Is the data stored as strings (default) or natively (which is the case
+	 * with results from PrepStmts)
+	 */
+	protected boolean isBinaryEncoded = false;
+
+	/** Has this result set been closed? */
+	protected boolean isClosed = false;
+
+	protected ResultSetInternalMethods nextResultSet = null;
+
+	/** Are we on the insert row? */
+	protected boolean onInsertRow = false;
+
+	/** The statement that created us */
+	protected com.mysql.jdbc.StatementImpl owningStatement;
+
+	/**
+	 * StackTrace generated where ResultSet was created... used when profiling
+	 */
+	protected Throwable pointOfOrigin;
+
+	/** Are we tracking items for profileSql? */
+	protected boolean profileSql = false;
+
+	/**
+	 * Do we actually contain rows, or just information about
+	 * UPDATE/INSERT/DELETE?
+	 */
+	protected boolean reallyResult = false;
+
+	/** The id (used when profiling) to identify us */
+	protected int resultId;
+
+	/** Are we read-only or updatable? */
+	protected int resultSetConcurrency = 0;
+
+	/** Are we scroll-sensitive/insensitive? */
+	protected int resultSetType = 0;
+
+	/** The actual rows */
+	protected RowData rowData; // The results
+
+	/**
+	 * Any info message from the server that was created while generating this
+	 * result set (if 'info parsing' is enabled for the connection).
+	 */
+	protected String serverInfo = null;
+
+	PreparedStatement statementUsedForFetchingRows;
+
+	/** Pointer to current row data */
+	protected ResultSetRow thisRow = null; // Values for current row
+
+	// These are longs for
+	// recent versions of the MySQL server.
+	//
+	// They get reduced to ints via the JDBC API,
+	// but can be retrieved through a MySQLStatement
+	// in their entirety.
+	//
+
+	/** How many rows were affected by UPDATE/INSERT/DELETE? */
+	protected long updateCount;
+
+	/** Value generated for AUTO_INCREMENT columns */
+	protected long updateId = -1;
+
+	private boolean useStrictFloatingPoint = false;
+
+	protected boolean useUsageAdvisor = false;
+
+	/** The warning chain */
+	protected java.sql.SQLWarning warningChain = null;
+
+	/** Did the previous value retrieval find a NULL? */
+	protected boolean wasNullFlag = false;
+
+	protected java.sql.Statement wrapperStatement;
+
+	protected boolean retainOwningStatement;
+
+	protected Calendar gmtCalendar = null;
+
+	protected boolean useFastDateParsing = false;
+
+	private boolean padCharsWithSpace = false;
+
+	private boolean jdbcCompliantTruncationForReads;
+	
+	private boolean useFastIntParsing = true;
+	
+	protected final static char[] EMPTY_SPACE = new char[255];
+	
+	static {
+		for (int i = 0; i < EMPTY_SPACE.length; i++) {
+			EMPTY_SPACE[i] = ' ';
+		}
+	}
+	
+	protected static ResultSetImpl getInstance(long updateCount, long updateID,
+			ConnectionImpl conn, StatementImpl creatorStmt) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new ResultSetImpl(updateCount, updateID, conn, creatorStmt);
+		}
+
+		return (ResultSetImpl) Util.handleNewInstance(JDBC_4_RS_4_ARG_CTOR,
+				new Object[] { Constants.longValueOf(updateCount), Constants.longValueOf(updateID), conn,
+						creatorStmt });
+	}
+
+	/**
+	 * Creates a result set instance that represents a query result -- We need
+	 * to provide factory-style methods so we can support both JDBC3 (and older)
+	 * and JDBC4 runtimes, otherwise the class verifier complains when it tries
+	 * to load JDBC4-only interface classes that are present in JDBC4 method
+	 * signatures.
+	 */
+
+	protected static ResultSetImpl getInstance(String catalog, Field[] fields,
+			RowData tuples, ConnectionImpl conn, StatementImpl creatorStmt,
+			boolean isUpdatable) throws SQLException {
+		if (!Util.isJdbc4()) {
+			if (!isUpdatable) {
+				return new ResultSetImpl(catalog, fields, tuples, conn, creatorStmt);
+			}
+
+			return new UpdatableResultSet(catalog, fields, tuples, conn,
+					creatorStmt);
+		}
+
+		if (!isUpdatable) {
+			return (ResultSetImpl) Util
+					.handleNewInstance(JDBC_4_RS_6_ARG_CTOR, new Object[] {
+							catalog, fields, tuples, conn, creatorStmt });
+		}
+
+		return (ResultSetImpl) Util.handleNewInstance(JDBC_4_UPD_RS_6_ARG_CTOR,
+				new Object[] { catalog, fields, tuples, conn, creatorStmt });
+	}
+
+	/**
+	 * Create a result set for an executeUpdate statement.
+	 * 
+	 * @param updateCount
+	 *            the number of rows affected by the update
+	 * @param updateID
+	 *            the autoincrement value (if any)
+	 * @param conn
+	 *            DOCUMENT ME!
+	 * @param creatorStmt
+	 *            DOCUMENT ME!
+	 */
+	public ResultSetImpl(long updateCount, long updateID, ConnectionImpl conn,
+			StatementImpl creatorStmt) {
+		this.updateCount = updateCount;
+		this.updateId = updateID;
+		this.reallyResult = false;
+		this.fields = new Field[0];
+
+		this.connection = conn;
+		this.owningStatement = creatorStmt;
+		
+		this.retainOwningStatement = false;
+		
+		if (this.connection != null) {
+			this.retainOwningStatement = 
+				this.connection.getRetainStatementAfterResultSetClose();
+			
+			this.connectionId = this.connection.getId();
+		}
+	}
+
+	/**
+	 * Creates a new ResultSet object.
+	 * 
+	 * @param catalog
+	 *            the database in use when we were created
+	 * @param fields
+	 *            an array of Field objects (basically, the ResultSet MetaData)
+	 * @param tuples
+	 *            actual row data
+	 * @param conn
+	 *            the Connection that created us.
+	 * @param creatorStmt
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public ResultSetImpl(String catalog, Field[] fields, RowData tuples,
+			ConnectionImpl conn, StatementImpl creatorStmt) throws SQLException {
+		this.connection = conn;
+
+		this.retainOwningStatement = false;
+		
+		if (this.connection != null) {
+			this.useStrictFloatingPoint = this.connection
+					.getStrictFloatingPoint();
+			this.setDefaultTimeZone(this.connection.getDefaultTimeZone());
+			this.connectionId = this.connection.getId();
+			this.useFastDateParsing = this.connection.getUseFastDateParsing();
+			this.profileSql = this.connection.getProfileSql();
+			this.retainOwningStatement = 
+				this.connection.getRetainStatementAfterResultSetClose();
+			this.jdbcCompliantTruncationForReads = this.connection.getJdbcCompliantTruncationForReads();
+			this.useFastIntParsing = this.connection.getUseFastIntParsing();
+		}
+
+		this.owningStatement = creatorStmt;
+
+		this.catalog = catalog;
+
+		this.fields = fields;
+		this.rowData = tuples;
+		this.updateCount = this.rowData.size();
+
+		if (Driver.DEBUG) {
+			System.out.println(Messages.getString("ResultSet.Retrieved__1")
+					+ this.updateCount + " rows"); //$NON-NLS-1$
+		}
+
+		this.reallyResult = true;
+
+		// Check for no results
+		if (this.rowData.size() > 0) {
+			if (this.updateCount == 1) {
+				if (this.thisRow == null) {
+					this.rowData.close(); // empty result set
+					this.updateCount = -1;
+				}
+			}
+		} else {
+			this.thisRow = null;
+		}
+
+		this.rowData.setOwner(this);
+
+		if (this.fields != null) {
+			initializeWithMetadata();
+		} // else called by Connection.initializeResultsMetadataFromCache() when cached
+	}
+
+	public void initializeWithMetadata() throws SQLException {
+		this.rowData.setMetadata(this.fields);
+		
+		if (this.profileSql || this.connection.getUseUsageAdvisor()) {
+			this.columnUsed = new boolean[this.fields.length];
+			this.pointOfOrigin = new Throwable();
+			this.resultId = resultCounter++;
+			this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
+			this.eventSink = ProfileEventSink.getInstance(this.connection);
+		}
+
+		if (this.connection.getGatherPerformanceMetrics()) {
+			this.connection.incrementNumberOfResultSetsCreated();
+
+			Map tableNamesMap = new HashMap();
+
+			for (int i = 0; i < this.fields.length; i++) {
+				Field f = this.fields[i];
+
+				String tableName = f.getOriginalTableName();
+
+				if (tableName == null) {
+					tableName = f.getTableName();
+				}
+
+				if (tableName != null) {
+					if (this.connection.lowerCaseTableNames()) {
+						tableName = tableName.toLowerCase(); // on windows, table
+						// names are not case-sens.
+					}
+
+					tableNamesMap.put(tableName, null);
+				}
+			}
+
+			this.connection.reportNumberOfTablesAccessed(tableNamesMap.size());
+		}
+	}
+
+	private synchronized void createCalendarIfNeeded() {
+		if (this.fastDateCal == null) {
+			this.fastDateCal = new GregorianCalendar(Locale.US);
+			this.fastDateCal.setTimeZone(this.getDefaultTimeZone());
+		}
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Move to an absolute row number in the result set.
+	 * </p>
+	 * 
+	 * <p>
+	 * If row is positive, moves to an absolute row with respect to the
+	 * beginning of the result set. The first row is row 1, the second is row 2,
+	 * etc.
+	 * </p>
+	 * 
+	 * <p>
+	 * If row is negative, moves to an absolute row position with respect to the
+	 * end of result set. For example, calling absolute(-1) positions the cursor
+	 * on the last row, absolute(-2) indicates the next-to-last row, etc.
+	 * </p>
+	 * 
+	 * <p>
+	 * An attempt to position the cursor beyond the first/last row in the result
+	 * set, leaves the cursor before/after the first/last row, respectively.
+	 * </p>
+	 * 
+	 * <p>
+	 * Note: Calling absolute(1) is the same as calling first(). Calling
+	 * absolute(-1) is the same as calling last().
+	 * </p>
+	 * 
+	 * @param row
+	 *            the row number to move to
+	 * 
+	 * @return true if on the result set, false if off.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or row is 0, or result
+	 *                set type is TYPE_FORWARD_ONLY.
+	 */
+	public boolean absolute(int row) throws SQLException {
+		checkClosed();
+
+		boolean b;
+
+		if (this.rowData.size() == 0) {
+			b = false;
+		} else {
+			if (row == 0) {
+				throw SQLError.createSQLException(
+						Messages
+								.getString("ResultSet.Cannot_absolute_position_to_row_0_110"), //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			if (this.onInsertRow) {
+				this.onInsertRow = false;
+			}
+
+			if (this.doingUpdates) {
+				this.doingUpdates = false;
+			}
+
+			if (this.thisRow != null) {
+				this.thisRow.closeOpenStreams();
+			}
+			
+			if (row == 1) {
+				b = first();
+			} else if (row == -1) {
+				b = last();
+			} else if (row > this.rowData.size()) {
+				afterLast();
+				b = false;
+			} else {
+				if (row < 0) {
+					// adjust to reflect after end of result set
+					int newRowPosition = this.rowData.size() + row + 1;
+
+					if (newRowPosition <= 0) {
+						beforeFirst();
+						b = false;
+					} else {
+						b = absolute(newRowPosition);
+					}
+				} else {
+					row--; // adjust for index difference
+					this.rowData.setCurrentRow(row);
+					this.thisRow = this.rowData.getAt(row);
+					b = true;
+				}
+			}
+		}
+
+		setRowPositionValidity();
+		
+		return b;
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Moves to the end of the result set, just after the last row. Has no
+	 * effect if the result set contains no rows.
+	 * </p>
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or result set type is
+	 *                TYPE_FORWARD_ONLY.
+	 */
+	public void afterLast() throws SQLException {
+		checkClosed();
+
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+		}
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+		}
+
+		if (this.thisRow != null) {
+			this.thisRow.closeOpenStreams();
+		}
+		
+		if (this.rowData.size() != 0) {
+			this.rowData.afterLast();
+			this.thisRow = null;
+		}
+		
+		setRowPositionValidity();
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Moves to the front of the result set, just before the first row. Has no
+	 * effect if the result set contains no rows.
+	 * </p>
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or result set type is
+	 *                TYPE_FORWARD_ONLY
+	 */
+	public void beforeFirst() throws SQLException {
+		checkClosed();
+
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+		}
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+		}
+
+		if (this.rowData.size() == 0) {
+			return;
+		}
+
+		if (this.thisRow != null) {
+			this.thisRow.closeOpenStreams();
+		}
+		
+		this.rowData.beforeFirst();
+		this.thisRow = null;
+		
+		setRowPositionValidity();
+	}
+
+	// ---------------------------------------------------------------------
+	// Traversal/Positioning
+	// ---------------------------------------------------------------------
+
+	/**
+	 * Builds a hash between column names and their indices for fast retrieval.
+	 */
+	public void buildIndexMapping() throws SQLException {
+		int numFields = this.fields.length;
+		this.columnNameToIndex = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+		this.fullColumnNameToIndex = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+		
+		
+		// We do this in reverse order, so that the 'first' column
+		// with a given name ends up as the final mapping in the
+		// hashtable...
+		//
+		// Quoting the JDBC Spec:
+		//
+		// "Column names used as input to getter
+		// methods are case insensitive. When a getter method is called with a
+		// column
+		// name and several columns have the same name, the value of the first
+		// matching column will be returned. "
+		//
+		for (int i = numFields - 1; i >= 0; i--) {
+			Integer index = Constants.integerValueOf(i);
+			String columnName = this.fields[i].getName();
+			String fullColumnName = this.fields[i].getFullName();
+
+			if (columnName != null) {			
+				this.columnNameToIndex.put(columnName, index);
+			}
+
+			if (fullColumnName != null) {
+				this.fullColumnNameToIndex.put(fullColumnName, index);
+			}
+		}
+
+		// set the flag to prevent rebuilding...
+		this.hasBuiltIndexMapping = true;
+	}
+
+	/**
+	 * JDBC 2.0 The cancelRowUpdates() method may be called after calling an
+	 * updateXXX() method(s) and before calling updateRow() to rollback the
+	 * updates made to a row. If no updates have been made or updateRow() has
+	 * already been called, then this method has no effect.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or if called when on
+	 *                the insert row.
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void cancelRowUpdates() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * Ensures that the result set is not closed
+	 * 
+	 * @throws SQLException
+	 *             if the result set is closed
+	 */
+	protected final void checkClosed() throws SQLException {
+		if (this.isClosed) {
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	/**
+	 * Checks if columnIndex is within the number of columns in this result set.
+	 * 
+	 * @param columnIndex
+	 *            the index to check
+	 * 
+	 * @throws SQLException
+	 *             if the index is out of bounds
+	 */
+	protected final void checkColumnBounds(int columnIndex) throws SQLException {
+		if ((columnIndex < 1)) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Column_Index_out_of_range_low", new Object[] {
+							Constants.integerValueOf(columnIndex),
+							Constants.integerValueOf(this.fields.length) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		} else if ((columnIndex > this.fields.length)) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Column_Index_out_of_range_high", new Object[] {
+							Constants.integerValueOf(columnIndex),
+							Constants.integerValueOf(this.fields.length) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		if (this.profileSql || this.useUsageAdvisor) {
+			this.columnUsed[columnIndex - 1] = true;
+		}
+	}
+
+	/**
+	 * Ensures that the cursor is positioned on a valid row and that the result
+	 * set is not closed
+	 * 
+	 * @throws SQLException
+	 *             if the result set is not in a valid state for traversal
+	 */
+	protected void checkRowPos() throws SQLException {
+		checkClosed();
+
+		if (!this.onValidRow) {
+			throw SQLError.createSQLException(this.invalidRowReason, 
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+	
+	private boolean onValidRow = false;
+	private String invalidRowReason = null;
+	
+	private void setRowPositionValidity() throws SQLException {
+		if (!this.rowData.isDynamic() && (this.rowData.size() == 0)) {
+			this.invalidRowReason = Messages
+			.getString("ResultSet.Illegal_operation_on_empty_result_set");//$NON-NLS-1$
+			this.onValidRow = false;
+		} else if (this.rowData.isBeforeFirst()) {
+			this.invalidRowReason = Messages
+					.getString("ResultSet.Before_start_of_result_set_146"); //$NON-NLS-1$
+			this.onValidRow = false;
+		} else if (this.rowData.isAfterLast()) {
+			this.invalidRowReason = Messages
+					.getString("ResultSet.After_end_of_result_set_148"); //$NON-NLS-1$
+			this.onValidRow = false;
+		} else {
+			this.onValidRow = true;
+			this.invalidRowReason = null;
+		}
+	}
+
+	/**
+	 * We can't do this ourselves, otherwise the contract for
+	 * Statement.getMoreResults() won't work correctly.
+	 */
+	public void clearNextResult() {
+		this.nextResultSet = null;
+	}
+
+	/**
+	 * After this call, getWarnings returns null until a new warning is reported
+	 * for this ResultSet
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void clearWarnings() throws SQLException {
+		this.warningChain = null;
+	}
+
+	/**
+	 * In some cases, it is desirable to immediately release a ResultSet
+	 * database and JDBC resources instead of waiting for this to happen when it
+	 * is automatically closed. The close method provides this immediate
+	 * release.
+	 * 
+	 * <p>
+	 * <B>Note:</B> A ResultSet is automatically closed by the Statement the
+	 * Statement that generated it when that Statement is closed, re-executed,
+	 * or is used to retrieve the next result from a sequence of multiple
+	 * results. A ResultSet is also automatically closed when it is garbage
+	 * collected.
+	 * </p>
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void close() throws SQLException {
+		realClose(true);
+	}
+
+	/**
+	 * @return
+	 */
+	private int convertToZeroWithEmptyCheck() throws SQLException {
+		if (this.connection.getEmptyStringsConvertToZero()) {
+			return 0;
+		}
+
+		throw SQLError.createSQLException("Can't convert empty string ('') to numeric",
+				SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST);
+	}
+	
+	private String convertToZeroLiteralStringWithEmptyCheck()
+		throws SQLException {
+		
+		if (this.connection.getEmptyStringsConvertToZero()) {
+			return "0";
+		}
+
+		throw SQLError.createSQLException("Can't convert empty string ('') to numeric",
+				SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST);
+	}
+
+	//
+	// Note, row data is linked between these two result sets
+	//
+	public ResultSetInternalMethods copy() throws SQLException {
+		ResultSetInternalMethods rs = ResultSetImpl.getInstance(this.catalog, this.fields, this.rowData,
+				this.connection, this.owningStatement, false); // note, doesn't work for updatable result sets
+
+		return rs;
+	}
+
+	public void redefineFieldsForDBMD(Field[] f) {
+		this.fields = f;
+		
+		for (int i = 0; i < this.fields.length; i++) {
+			this.fields[i].setUseOldNameMetadata(true);
+			this.fields[i].setConnection(this.connection);
+		}
+	}
+	
+	public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData)
+		throws SQLException {
+		cachedMetaData.fields = this.fields;
+		cachedMetaData.columnNameToIndex = this.columnNameToIndex;
+		cachedMetaData.fullColumnNameToIndex = this.fullColumnNameToIndex;
+		cachedMetaData.metadata = getMetaData();
+	}
+	
+	public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData) {
+		this.fields = cachedMetaData.fields;
+		this.columnNameToIndex = cachedMetaData.columnNameToIndex;
+		this.fullColumnNameToIndex = cachedMetaData.fullColumnNameToIndex;
+		this.hasBuiltIndexMapping = true;
+	}
+	
+
+	/**
+	 * JDBC 2.0 Delete the current row from the result set and the underlying
+	 * database. Cannot be called when on the insert row.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or if called when on
+	 *                the insert row.
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void deleteRow() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * @param columnIndex
+	 * @param stringVal
+	 * @param mysqlType
+	 * @return
+	 * @throws SQLException
+	 */
+	private String extractStringFromNativeColumn(int columnIndex, int mysqlType)
+			throws SQLException {
+		int columnIndexMinusOne = columnIndex - 1;
+
+		this.wasNullFlag = false;
+		
+		if (this.thisRow.isNull(columnIndexMinusOne)) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+
+		this.wasNullFlag = false;
+
+		String encoding = this.fields[columnIndexMinusOne]
+		      						.getCharacterSet();
+		
+		return this.thisRow.getString(columnIndex - 1, encoding, this.connection);
+	}
+
+	protected synchronized Date fastDateCreate(Calendar cal, int year, int month,
+			int day) {
+		if (cal == null) {
+			createCalendarIfNeeded();
+			cal = this.fastDateCal;
+		}
+
+		boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();
+						
+		return TimeUtil.fastDateCreate(useGmtMillis,
+				useGmtMillis ? getGmtCalendar() : null,
+				cal, year, month, day);
+	}
+
+	protected synchronized Time fastTimeCreate(Calendar cal, int hour,
+			int minute, int second) throws SQLException {
+		if (cal == null) {
+			createCalendarIfNeeded();
+			cal = this.fastDateCal;
+		}
+
+		return TimeUtil.fastTimeCreate(cal, hour, minute, second);
+	}
+
+	protected synchronized Timestamp fastTimestampCreate(Calendar cal, int year,
+			int month, int day, int hour, int minute, int seconds,
+			int secondsPart) {
+		if (cal == null) {
+			createCalendarIfNeeded();
+			cal = this.fastDateCal;
+		}
+
+		boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();
+		
+		return TimeUtil.fastTimestampCreate(useGmtMillis,
+				useGmtMillis ? getGmtCalendar() : null,
+				cal, year, month, day, hour,
+				minute, seconds, secondsPart);
+	}
+
+	/*
+	/**
+	 * Required by JDBC spec
+	 */
+	/*
+	protected void finalize() throws Throwable {
+		if (!this.isClosed) {
+			realClose(false);
+		}
+	}
+	*/
+
+	// --------------------------JDBC 2.0-----------------------------------
+	// ---------------------------------------------------------------------
+	// Getter's and Setter's
+	// ---------------------------------------------------------------------
+
+
+	/**
+	 * Map a ResultSet column name to a ResultSet column index
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * 
+	 * @return the column index
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public synchronized int findColumn(String columnName) throws SQLException {
+		Integer index;
+
+		if (!this.hasBuiltIndexMapping) {
+			buildIndexMapping();
+		}
+
+		index = (Integer) this.columnNameToIndex.get(columnName);
+
+		if (index == null) {
+			index = (Integer) this.fullColumnNameToIndex.get(columnName);
+		}
+
+		if (index != null) {
+			return index.intValue() + 1;
+		}
+
+		// Try this inefficient way, now
+
+		for (int i = 0; i < this.fields.length; i++) {
+			if (this.fields[i].getName().equalsIgnoreCase(columnName)) {
+				return i + 1;
+			} else if (this.fields[i].getFullName()
+					.equalsIgnoreCase(columnName)) {
+				return i + 1;
+			}
+		}
+
+		throw SQLError.createSQLException(Messages.getString("ResultSet.Column____112")
+				+ columnName
+				+ Messages.getString("ResultSet.___not_found._113"), //$NON-NLS-1$ //$NON-NLS-2$
+				SQLError.SQL_STATE_COLUMN_NOT_FOUND);
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Moves to the first row in the result set.
+	 * </p>
+	 * 
+	 * @return true if on a valid row, false if no rows in the result set.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or result set type is
+	 *                TYPE_FORWARD_ONLY.
+	 */
+	public boolean first() throws SQLException {
+		checkClosed();
+
+		boolean b = true;
+		
+		if (this.rowData.isEmpty()) {
+			b = false;
+		} else {
+	
+			if (this.onInsertRow) {
+				this.onInsertRow = false;
+			}
+	
+			if (this.doingUpdates) {
+				this.doingUpdates = false;
+			}
+	
+			this.rowData.beforeFirst();
+			this.thisRow = this.rowData.next();
+		}
+
+		setRowPositionValidity();
+		
+		return b;
+	}
+
+	/**
+	 * JDBC 2.0 Get an array column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing an SQL array
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.Array getArray(int i) throws SQLException {
+		checkColumnBounds(i);
+		
+		throw new NotImplemented();
+	}
+
+	/**
+	 * JDBC 2.0 Get an array column.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * 
+	 * @return an object representing an SQL array
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.Array getArray(String colName) throws SQLException {
+		return getArray(findColumn(colName));
+	}
+
+	/**
+	 * A column value can be retrieved as a stream of ASCII characters and then
+	 * read in chunks from the stream. This method is particulary suitable for
+	 * retrieving large LONGVARCHAR values. The JDBC driver will do any
+	 * necessary conversion from the database format into ASCII.
+	 * 
+	 * <p>
+	 * <B>Note:</B> All the data in the returned stream must be read prior to
+	 * getting the value of any other column. The next call to a get method
+	 * implicitly closes the stream. Also, a stream may return 0 for available()
+	 * whether there is data available or not.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return a Java InputStream that delivers the database column value as a
+	 *         stream of one byte ASCII characters. If the value is SQL NULL
+	 *         then the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see getBinaryStream
+	 */
+	public InputStream getAsciiStream(int columnIndex) throws SQLException {
+		checkRowPos();
+
+		if (!this.isBinaryEncoded) {
+			return getBinaryStream(columnIndex);
+		}
+
+		return getNativeBinaryStream(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public InputStream getAsciiStream(String columnName) throws SQLException {
+		return getAsciiStream(findColumn(columnName));
+	}
+
+	/**
+	 * JDBC 2.0 Get the value of a column in the current row as a
+	 * java.math.BigDecimal object.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return the column value (full precision); if the value is SQL NULL, the
+	 *         result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			String stringVal = getString(columnIndex);
+			BigDecimal val;
+
+			if (stringVal != null) {
+				if (stringVal.length() == 0) {
+					
+					val = new BigDecimal(
+							convertToZeroLiteralStringWithEmptyCheck());
+
+					return val;
+				}
+
+				try {
+					val = new BigDecimal(stringVal);
+
+					return val;
+				} catch (NumberFormatException ex) {
+					throw SQLError.createSQLException(Messages
+							.getString("ResultSet.Bad_format_for_BigDecimal",
+									new Object[] { stringVal,
+											Constants.integerValueOf(columnIndex) }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+			}
+
+			return null;
+		}
+
+		return getNativeBigDecimal(columnIndex);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.math.BigDecimal
+	 * object
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * @param scale
+	 *            the number of digits to the right of the decimal
+	 * 
+	 * @return the column value; if the value is SQL NULL, null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @deprecated
+	 */
+	public BigDecimal getBigDecimal(int columnIndex, int scale)
+			throws SQLException {
+		if (!this.isBinaryEncoded) {
+			String stringVal = getString(columnIndex);
+			BigDecimal val;
+
+			if (stringVal != null) {
+				if (stringVal.length() == 0) {
+					val = new BigDecimal(
+							convertToZeroLiteralStringWithEmptyCheck());
+
+					try {
+						return val.setScale(scale);
+					} catch (ArithmeticException ex) {
+						try {
+							return val
+									.setScale(scale, BigDecimal.ROUND_HALF_UP);
+						} catch (ArithmeticException arEx) {
+							throw SQLError.createSQLException(
+									Messages
+											.getString("ResultSet.Bad_format_for_BigDecimal____124") //$NON-NLS-1$
+											+ stringVal
+											+ Messages
+													.getString("ResultSet.___in_column__125")
+											+ columnIndex
+											+ "(" //$NON-NLS-1$
+											+ this.fields[columnIndex - 1]
+											+ ").",
+									SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+						}
+					}
+				}
+
+				try {
+					val = new BigDecimal(stringVal);
+				} catch (NumberFormatException ex) {
+					if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+						long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
+
+						val = new BigDecimal(valueAsLong);
+					} else {
+						throw SQLError.createSQLException(Messages
+							.getString("ResultSet.Bad_format_for_BigDecimal",
+									new Object[] { Constants.integerValueOf(columnIndex),
+											stringVal }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+						}
+				}
+
+				try {
+					return val.setScale(scale);
+				} catch (ArithmeticException ex) {
+					try {
+						return val.setScale(scale, BigDecimal.ROUND_HALF_UP);
+					} catch (ArithmeticException arithEx) {
+						throw SQLError.createSQLException(Messages.getString(
+								"ResultSet.Bad_format_for_BigDecimal",
+								new Object[] { Constants.integerValueOf(columnIndex),
+										stringVal }),
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+					}
+				}
+			}
+
+			return null;
+		}
+
+		return getNativeBigDecimal(columnIndex, scale);
+	}
+
+	/**
+	 * JDBC 2.0 Get the value of a column in the current row as a
+	 * java.math.BigDecimal object.
+	 * 
+	 * @param columnName
+	 *            the name of the column to retrieve the value from
+	 * 
+	 * @return the BigDecimal value in the column
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public BigDecimal getBigDecimal(String columnName) throws SQLException {
+		return getBigDecimal(findColumn(columnName));
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * @param scale
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * 
+	 * @deprecated
+	 */
+	public BigDecimal getBigDecimal(String columnName, int scale)
+			throws SQLException {
+		return getBigDecimal(findColumn(columnName), scale);
+	}
+
+	private final BigDecimal getBigDecimalFromString(String stringVal,
+			int columnIndex, int scale) throws SQLException {
+		BigDecimal bdVal;
+
+		if (stringVal != null) {
+			if (stringVal.length() == 0) {
+				bdVal = new BigDecimal(convertToZeroLiteralStringWithEmptyCheck());
+
+				try {
+					return bdVal.setScale(scale);
+				} catch (ArithmeticException ex) {
+					try {
+						return bdVal.setScale(scale, BigDecimal.ROUND_HALF_UP);
+					} catch (ArithmeticException arEx) {
+						throw new SQLException(Messages
+								.getString("ResultSet.Bad_format_for_BigDecimal",
+										new Object[] { stringVal,
+												Constants.integerValueOf(columnIndex) }),
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+					}
+				}
+			}
+
+			try {
+				try {
+					return new BigDecimal(stringVal).setScale(scale);
+				} catch (ArithmeticException ex) {
+					try {
+						return new BigDecimal(stringVal).setScale(scale,
+								BigDecimal.ROUND_HALF_UP);
+					} catch (ArithmeticException arEx) {
+						throw new SQLException(Messages
+								.getString("ResultSet.Bad_format_for_BigDecimal",
+										new Object[] { stringVal,
+												Constants.integerValueOf(columnIndex) }),
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+					}
+				}
+			} catch (NumberFormatException ex) {
+				if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+					long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
+
+					try {
+						return new BigDecimal(valueAsLong).setScale(scale);
+					} catch (ArithmeticException arEx1) {
+						try {
+							return new BigDecimal(valueAsLong).setScale(scale,
+									BigDecimal.ROUND_HALF_UP);
+						} catch (ArithmeticException arEx2) {
+							throw new SQLException(Messages
+									.getString("ResultSet.Bad_format_for_BigDecimal",
+											new Object[] { stringVal,
+													Constants.integerValueOf(columnIndex) }),
+									SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+						}
+					}
+				}
+				
+				if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TINY &&
+						this.connection.getTinyInt1isBit() && this.fields[columnIndex - 1].getLength() == 1) {
+					return new BigDecimal(stringVal.equalsIgnoreCase("true") ? 1 : 0).setScale(scale);
+				}
+				
+				throw new SQLException(Messages
+						.getString("ResultSet.Bad_format_for_BigDecimal",
+								new Object[] { stringVal,
+										Constants.integerValueOf(columnIndex) }),
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+		}
+		
+		return null;
+	}
+
+	/**
+	 * A column value can also be retrieved as a binary stream. This method is
+	 * suitable for retrieving LONGVARBINARY values.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return a Java InputStream that delivers the database column value as a
+	 *         stream of bytes. If the value is SQL NULL, then the result is
+	 *         null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see getAsciiStream
+	 * @see getUnicodeStream
+	 */
+	public InputStream getBinaryStream(int columnIndex) throws SQLException {
+		checkRowPos();
+
+		if (!this.isBinaryEncoded) {
+			checkColumnBounds(columnIndex);
+			
+			int columnIndexMinusOne = columnIndex - 1;
+			
+			if (this.thisRow.isNull(columnIndexMinusOne)) {
+				this.wasNullFlag = true;
+				
+				return null;
+			}
+			
+			this.wasNullFlag = false;
+			
+			return this.thisRow.getBinaryInputStream(columnIndexMinusOne);
+		}
+
+		return getNativeBinaryStream(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public InputStream getBinaryStream(String columnName) throws SQLException {
+		return getBinaryStream(findColumn(columnName));
+	}
+
+	/**
+	 * JDBC 2.0 Get a BLOB column.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a BLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	public java.sql.Blob getBlob(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkRowPos();
+
+			checkColumnBounds(columnIndex);
+
+			int columnIndexMinusOne = columnIndex - 1;
+			
+			if (this.thisRow.isNull(columnIndexMinusOne)) {
+				this.wasNullFlag = true;
+			} else {
+				this.wasNullFlag = false;
+			}
+			
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			if (!this.connection.getEmulateLocators()) {
+				return new Blob(this.thisRow.getColumnValue(columnIndexMinusOne));
+			}
+
+			return new BlobFromLocator(this, columnIndex);
+		}
+
+		return getNativeBlob(columnIndex);
+	}
+
+	/**
+	 * JDBC 2.0 Get a BLOB column.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * 
+	 * @return an object representing a BLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	public java.sql.Blob getBlob(String colName) throws SQLException {
+		return getBlob(findColumn(colName));
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java boolean
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value, false for SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean getBoolean(int columnIndex) throws SQLException {
+		
+		checkColumnBounds(columnIndex);
+
+		//
+		// MySQL 5.0 and newer have an actual BIT type,
+		// so we need to check for that here...
+		//
+
+		int columnIndexMinusOne = columnIndex - 1;
+
+		Field field = this.fields[columnIndexMinusOne];
+
+		if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+			return byteArrayToBoolean(columnIndexMinusOne);
+		}
+
+		this.wasNullFlag = false;
+		
+		int sqlType = field.getSQLType();
+		
+		switch (sqlType) {
+		case Types.BIT:
+		case Types.BOOLEAN:
+		case Types.TINYINT:
+		case Types.SMALLINT:
+		case Types.INTEGER:
+		case Types.BIGINT:
+		case Types.DECIMAL:
+		case Types.NUMERIC:
+		case Types.REAL:
+		case Types.FLOAT:
+		case Types.DOUBLE:
+			long boolVal = getLong(columnIndex, false);
+
+			return (boolVal == -1 || boolVal > 0);
+		default:
+			if (this.connection.getPedantic()) {
+				// Table B-6 from JDBC spec
+				switch (sqlType) {
+				case Types.BINARY:
+				case Types.VARBINARY:
+				case Types.LONGVARBINARY:
+				case Types.DATE:
+				case Types.TIME:
+				case Types.TIMESTAMP:
+				case Types.CLOB:
+				case Types.BLOB:
+				case Types.ARRAY:
+				case Types.REF:
+				case Types.DATALINK:
+				case Types.STRUCT:
+				case Types.JAVA_OBJECT:
+					throw SQLError.createSQLException("Required type conversion not allowed",
+							SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST);
+				}
+			}
+		
+			if (sqlType == Types.BINARY ||
+				sqlType == Types.VARBINARY ||
+				sqlType == Types.LONGVARBINARY ||
+				sqlType == Types.BLOB) {
+				return byteArrayToBoolean(columnIndexMinusOne);
+			}
+			
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getBoolean()", columnIndex,
+						this.thisRow.getColumnValue(columnIndexMinusOne), this.fields[columnIndex],
+						new int[] {
+								MysqlDefs.FIELD_TYPE_BIT,
+								MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+		
+			String stringVal = getString(columnIndex);
+
+			return getBooleanFromString(stringVal, columnIndex);
+		}
+	}
+
+	private boolean byteArrayToBoolean(int columnIndexMinusOne) throws SQLException {
+		Object value = this.thisRow.getColumnValue(columnIndexMinusOne);
+		
+		if (value == null) {
+			this.wasNullFlag = true;
+
+			return false;
+		}
+
+		this.wasNullFlag = false;
+
+		if (((byte[]) value).length == 0) {
+			return false;
+		}
+
+		byte boolVal = ((byte[]) value)[0];
+
+		if (boolVal == (byte)'1') {
+			return true;
+		} else if (boolVal == (byte)'0') {
+			return false;
+		}
+		
+		return (boolVal == -1 || boolVal > 0);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean getBoolean(String columnName) throws SQLException {
+		return getBoolean(findColumn(columnName));
+	}
+
+	private final boolean getBooleanFromString(String stringVal, int columnIndex)
+			throws SQLException {
+		if ((stringVal != null) && (stringVal.length() > 0)) {
+			int c = Character.toLowerCase(stringVal.charAt(0));
+
+			return ((c == 't') || (c == 'y') || (c == '1') || stringVal
+					.equals("-1"));
+		}
+
+		return false;
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java byte.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public byte getByte(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			String stringVal = getString(columnIndex);
+
+			if (this.wasNullFlag || (stringVal == null)) {
+				return 0;
+			}
+
+			return getByteFromString(stringVal, columnIndex);
+		}
+
+		return getNativeByte(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public byte getByte(String columnName) throws SQLException {
+		return getByte(findColumn(columnName));
+	}
+
+	private final byte getByteFromString(String stringVal, int columnIndex)
+			throws SQLException {
+
+		if (stringVal != null && stringVal.length() == 0) {
+			return (byte) convertToZeroWithEmptyCheck();
+		}
+
+		//
+		// JDK-6 doesn't like trailing whitespace
+		//
+		// Note this isn't a performance issue, other
+		// than the iteration over the string, as String.trim()
+		// will return a new string only if whitespace is present
+		//
+		
+		if (stringVal == null) {
+			return 0;
+		}
+		
+		stringVal = stringVal.trim(); 
+		
+		try {
+			int decimalIndex = stringVal.indexOf(".");
+
+			
+			if (decimalIndex != -1) {
+				double valueAsDouble = Double.parseDouble(stringVal);
+
+				if (this.jdbcCompliantTruncationForReads) {
+					if (valueAsDouble < Byte.MIN_VALUE
+							|| valueAsDouble > Byte.MAX_VALUE) {
+						throwRangeException(stringVal, columnIndex,
+								Types.TINYINT);
+					}
+				}
+
+				return (byte) valueAsDouble;
+			}
+
+			long valueAsLong = Long.parseLong(stringVal);
+
+			if (this.jdbcCompliantTruncationForReads) {
+				if (valueAsLong < Byte.MIN_VALUE
+						|| valueAsLong > Byte.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsLong),
+							columnIndex, Types.TINYINT);
+				}
+			}
+
+			return (byte) valueAsLong;
+		} catch (NumberFormatException NFE) {
+			throw SQLError.createSQLException(
+					Messages.getString("ResultSet.Value____173")
+							+ stringVal //$NON-NLS-1$
+							+ Messages
+									.getString("ResultSet.___is_out_of_range_[-127,127]_174"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java byte array.
+	 * 
+	 * <p>
+	 * <b>Be warned</b> If the blob is huge, then you may run out of memory.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public byte[] getBytes(int columnIndex) throws SQLException {
+		return getBytes(columnIndex, false);
+	}
+
+	protected byte[] getBytes(int columnIndex, boolean noConversion)
+			throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkRowPos();
+			
+			checkColumnBounds(columnIndex);
+			
+			int columnIndexMinusOne = columnIndex - 1;
+
+			if (this.thisRow.isNull(columnIndexMinusOne)) {
+				this.wasNullFlag = true;
+			} else {
+				this.wasNullFlag = false;
+			}
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			return this.thisRow.getColumnValue(columnIndexMinusOne);
+		}
+
+		return getNativeBytes(columnIndex, noConversion);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public byte[] getBytes(String columnName) throws SQLException {
+		return getBytes(findColumn(columnName));
+	}
+
+	private final byte[] getBytesFromString(String stringVal, int columnIndex)
+			throws SQLException {
+		if (stringVal != null) {
+			return StringUtils.getBytes(stringVal, this.connection
+					.getEncoding(), this.connection
+					.getServerCharacterEncoding(), this.connection
+					.parserKnowsUnicode(),
+					this.connection);
+		}
+
+		return null;
+	}
+
+	/**
+	 * Optimization to only use one calendar per-session, or calculate it for
+	 * each call, depending on user configuration
+	 */
+	protected Calendar getCalendarInstanceForSessionOrNew() {
+		if (this.connection != null) {
+			return this.connection.getCalendarInstanceForSessionOrNew();
+		} else {
+			// punt, no connection around
+			return new GregorianCalendar();
+		}
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the column to get the value from
+	 * 
+	 * @return the value in the column as a java.io.Reader.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.io.Reader getCharacterStream(int columnIndex)
+			throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkColumnBounds(columnIndex);
+			
+			int columnIndexMinusOne = columnIndex - 1;
+			
+			if (this.thisRow.isNull(columnIndexMinusOne)) {
+				this.wasNullFlag = true;
+				
+				return null;
+			}
+			
+			this.wasNullFlag = false;
+			
+			return this.thisRow.getReader(columnIndexMinusOne);
+		}
+
+		return getNativeCharacterStream(columnIndex);
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnName
+	 *            the column name to retrieve the value from
+	 * 
+	 * @return the value as a java.io.Reader
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.io.Reader getCharacterStream(String columnName)
+			throws SQLException {
+		return getCharacterStream(findColumn(columnName));
+	}
+
+	private final java.io.Reader getCharacterStreamFromString(String stringVal,
+			int columnIndex) throws SQLException {
+		if (stringVal != null) {
+			return new StringReader(stringVal);
+		}
+
+		return null;
+	}
+
+	/**
+	 * JDBC 2.0 Get a CLOB column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a CLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.sql.Clob getClob(int i) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			String asString = getStringForClob(i);
+			
+			if (asString == null) {
+				return null;
+			}
+
+			return new com.mysql.jdbc.Clob(asString);
+		}
+
+		return getNativeClob(i);
+	}
+
+	/**
+	 * JDBC 2.0 Get a CLOB column.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * 
+	 * @return an object representing a CLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.sql.Clob getClob(String colName) throws SQLException {
+		return getClob(findColumn(colName));
+	}
+
+	private final java.sql.Clob getClobFromString(String stringVal,
+			int columnIndex) throws SQLException {
+		return new com.mysql.jdbc.Clob(stringVal);
+	}
+
+	/**
+	 * JDBC 2.0 Return the concurrency of this result set. The concurrency used
+	 * is determined by the statement that created the result set.
+	 * 
+	 * @return the concurrency type, CONCUR_READ_ONLY, etc.
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs
+	 */
+	public int getConcurrency() throws SQLException {
+		return (CONCUR_READ_ONLY);
+	}
+
+	/**
+	 * Get the name of the SQL cursor used by this ResultSet
+	 * 
+	 * <p>
+	 * In SQL, a result table is retrieved though a cursor that is named. The
+	 * current row of a result can be updated or deleted using a positioned
+	 * update/delete statement that references the cursor name.
+	 * </p>
+	 * 
+	 * <p>
+	 * JDBC supports this SQL feature by providing the name of the SQL cursor
+	 * used by a ResultSet. The current row of a ResulSet is also the current
+	 * row of this SQL cursor.
+	 * </p>
+	 * 
+	 * <p>
+	 * <B>Note:</B> If positioned update is not supported, a SQLException is
+	 * thrown.
+	 * </p>
+	 * 
+	 * @return the ResultSet's SQL cursor name.
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getCursorName() throws SQLException {
+		throw SQLError.createSQLException(Messages
+				.getString("ResultSet.Positioned_Update_not_supported"),
+				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); //$NON-NLS-1$
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Date object
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value; null if SQL NULL
+	 * 
+	 * @exception java.sql.SQLException
+	 *                if a database access error occurs
+	 */
+	public java.sql.Date getDate(int columnIndex) throws java.sql.SQLException {
+		return getDate(columnIndex, null);
+	}
+
+	/**
+	 * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date
+	 * object. Use the calendar to construct an appropriate millisecond value
+	 * for the Date, if the underlying database doesn't store timezone
+	 * information.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param cal
+	 *            the calendar to use in constructing the date
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Date getDate(int columnIndex, Calendar cal)
+			throws SQLException {
+		if (this.isBinaryEncoded) {
+			return getNativeDate(columnIndex, (cal != null) ? cal.getTimeZone()
+					: this.getDefaultTimeZone());
+		}
+
+		if (!this.useFastDateParsing) {
+			String stringVal = getStringInternal(columnIndex, false);
+
+			if (stringVal == null) {
+				return null;
+			}
+			
+			return getDateFromString(stringVal, columnIndex);
+		}
+		
+		checkColumnBounds(columnIndex);
+		
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		if (this.thisRow.isNull(columnIndexMinusOne)) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+		
+		return this.thisRow.getDateFast(columnIndexMinusOne, this.connection, this);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws java.sql.SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.Date getDate(String columnName)
+			throws java.sql.SQLException {
+		return getDate(findColumn(columnName));
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Date object.
+	 * Use the calendar to construct an appropriate millisecond value for the
+	 * Date, if the underlying database doesn't store timezone information.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * @param cal
+	 *            the calendar to use in constructing the date
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Date getDate(String columnName, Calendar cal)
+			throws SQLException {
+		return getDate(findColumn(columnName), cal);
+	}
+
+	private final java.sql.Date getDateFromString(String stringVal,
+			int columnIndex) throws SQLException {
+		int year = 0;
+		int month = 0;
+		int day = 0;
+
+		try {
+			this.wasNullFlag = false;
+
+			if (stringVal == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+			
+			//
+			// JDK-6 doesn't like trailing whitespace
+			//
+			// Note this isn't a performance issue, other
+			// than the iteration over the string, as String.trim()
+			// will return a new string only if whitespace is present
+			//
+			
+			stringVal = stringVal.trim();
+
+			if (stringVal.equals("0") || stringVal.equals("0000-00-00")
+					|| stringVal.equals("0000-00-00 00:00:00")
+					|| stringVal.equals("00000000000000")
+					|| stringVal.equals("0")) {
+
+				if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					this.wasNullFlag = true;
+
+					return null;
+				} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					throw SQLError.createSQLException("Value '" + stringVal
+							+ "' can not be represented as java.sql.Date",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				// We're left with the case of 'round' to a date Java _can_
+				// represent, which is '0001-01-01'.
+				return fastDateCreate(null, 1, 1, 1);
+
+			} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
+				// Convert from TIMESTAMP
+				switch (stringVal.length()) {
+				case 21:
+				case 19: { // java.sql.Timestamp format
+					year = Integer.parseInt(stringVal.substring(0, 4));
+					month = Integer.parseInt(stringVal.substring(5, 7));
+					day = Integer.parseInt(stringVal.substring(8, 10));
+
+					return fastDateCreate(null, year, month, day);
+				}
+
+				case 14:
+				case 8: {
+					year = Integer.parseInt(stringVal.substring(0, 4));
+					month = Integer.parseInt(stringVal.substring(4, 6));
+					day = Integer.parseInt(stringVal.substring(6, 8));
+
+					return fastDateCreate(null, year, month, day);
+				}
+
+				case 12:
+				case 10:
+				case 6: {
+					year = Integer.parseInt(stringVal.substring(0, 2));
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					month = Integer.parseInt(stringVal.substring(2, 4));
+					day = Integer.parseInt(stringVal.substring(4, 6));
+
+					return fastDateCreate(null, year + 1900, month, day);
+				}
+
+				case 4: {
+					year = Integer.parseInt(stringVal.substring(0, 4));
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					month = Integer.parseInt(stringVal.substring(2, 4));
+
+					return fastDateCreate(null, year + 1900, month, 1);
+				}
+
+				case 2: {
+					year = Integer.parseInt(stringVal.substring(0, 2));
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					return fastDateCreate(null, year + 1900, 1, 1);
+				}
+
+				default:
+					throw SQLError.createSQLException(Messages.getString(
+							"ResultSet.Bad_format_for_Date", new Object[] {
+									stringVal, Constants.integerValueOf(columnIndex) }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				} /* endswitch */
+			} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+
+				if (stringVal.length() == 2 || stringVal.length() == 1) {
+					year = Integer.parseInt(stringVal);
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					year += 1900;
+				} else {
+					year = Integer.parseInt(stringVal.substring(0, 4));
+				}
+
+				return fastDateCreate(null, year, 1, 1);
+			} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) {
+				return fastDateCreate(null, 1970, 1, 1); // Return EPOCH
+			} else {
+				if (stringVal.length() < 10) {
+					if (stringVal.length() == 8) {
+						return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME
+					}
+					
+					throw SQLError.createSQLException(Messages.getString(
+							"ResultSet.Bad_format_for_Date", new Object[] {
+									stringVal, Constants.integerValueOf(columnIndex) }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+
+				if (stringVal.length() != 18) {
+					year = Integer.parseInt(stringVal.substring(0, 4));
+					month = Integer.parseInt(stringVal.substring(5, 7));
+					day = Integer.parseInt(stringVal.substring(8, 10));
+				} else {
+					// JDK-1.3 timestamp format, not real easy to parse positionally :p
+					StringTokenizer st = new StringTokenizer(stringVal, "- ");
+					
+					year = Integer.parseInt(st.nextToken());
+					month = Integer.parseInt(st.nextToken());
+					day = Integer.parseInt(st.nextToken());
+				}
+			}
+
+			return fastDateCreate(null, year, month, day);
+		} catch (SQLException sqlEx) {
+			throw sqlEx; // don't re-wrap
+		} catch (Exception e) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Bad_format_for_Date", new Object[] { stringVal,
+							Constants.integerValueOf(columnIndex) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+	
+	private TimeZone getDefaultTimeZone() {
+		return this.connection.getDefaultTimeZone();
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java double.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public double getDouble(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			return getDoubleInternal(columnIndex);
+		}
+
+		return getNativeDouble(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public double getDouble(String columnName) throws SQLException {
+		return getDouble(findColumn(columnName));
+	}
+
+	private final double getDoubleFromString(String stringVal, int columnIndex)
+			throws SQLException {
+		return getDoubleInternal(stringVal, columnIndex);
+	}
+
+	/**
+	 * Converts a string representation of a number to a double. Need a faster
+	 * way to do this.
+	 * 
+	 * @param colIndex
+	 *            the 1-based index of the column to retrieve a double from.
+	 * 
+	 * @return the double value represented by the string in buf
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected double getDoubleInternal(int colIndex) throws SQLException {
+		return getDoubleInternal(getString(colIndex), colIndex);
+	}
+
+	/**
+	 * Converts a string representation of a number to a double. Need a faster
+	 * way to do this.
+	 * 
+	 * @param stringVal
+	 *            the double as a String
+	 * @param colIndex
+	 *            the 1-based index of the column to retrieve a double from.
+	 * 
+	 * @return the double value represented by the string in buf
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected double getDoubleInternal(String stringVal, int colIndex)
+			throws SQLException {
+		try {
+			if ((stringVal == null)) {
+				return 0;
+			}
+
+			if (stringVal.length() == 0) {
+				return convertToZeroWithEmptyCheck();
+			}
+
+			double d = Double.parseDouble(stringVal);
+
+			if (this.useStrictFloatingPoint) {
+				// Fix endpoint rounding precision loss in MySQL server
+				if (d == 2.147483648E9) {
+					// Fix Odd end-point rounding on MySQL
+					d = 2.147483647E9;
+				} else if (d == 1.0000000036275E-15) {
+					// Fix odd end-point rounding on MySQL
+					d = 1.0E-15;
+				} else if (d == 9.999999869911E14) {
+					d = 9.99999999999999E14;
+				} else if (d == 1.4012984643248E-45) {
+					d = 1.4E-45;
+				} else if (d == 1.4013E-45) {
+					d = 1.4E-45;
+				} else if (d == 3.4028234663853E37) {
+					d = 3.4028235E37;
+				} else if (d == -2.14748E9) {
+					d = -2.147483648E9;
+				} else if (d == 3.40282E37) {
+					d = 3.4028235E37;
+				}
+			}
+
+			return d;
+		} catch (NumberFormatException e) {
+			if (this.fields[colIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+				long valueAsLong = getNumericRepresentationOfSQLBitType(colIndex);
+				
+				return valueAsLong;
+			}
+			
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Bad_format_for_number", new Object[] {
+							stringVal, Constants.integerValueOf(colIndex) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Returns the fetch direction for this result set.
+	 * 
+	 * @return the fetch direction for this result set.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public int getFetchDirection() throws SQLException {
+		return this.fetchDirection;
+	}
+
+	/**
+	 * JDBC 2.0 Return the fetch size for this result set.
+	 * 
+	 * @return the fetch size for this result set.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public int getFetchSize() throws SQLException {
+		return this.fetchSize;
+	}
+
+	/**
+	 * Returns the first character of the query that this result set was created
+	 * from.
+	 * 
+	 * @return the first character of the query...uppercased
+	 */
+	public char getFirstCharOfQuery() {
+		return this.firstCharOfQuery;
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java float.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public float getFloat(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			String val = null;
+
+			val = getString(columnIndex);
+
+			return getFloatFromString(val, columnIndex);
+		}
+
+		return getNativeFloat(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public float getFloat(String columnName) throws SQLException {
+		return getFloat(findColumn(columnName));
+	}
+
+	private final float getFloatFromString(String val, int columnIndex)
+			throws SQLException {
+		try {
+			if ((val != null)) {
+				if (val.length() == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				float f = Float.parseFloat(val);
+
+				if (this.jdbcCompliantTruncationForReads) {
+					if (f == Float.MIN_VALUE || f == Float.MAX_VALUE) {
+						double valAsDouble = Double.parseDouble(val);
+
+						// Straight comparison is not reliable when at
+						// absolute endpoints of Float.MIN_VALUE or 
+						// Float.MAX_VALUE, so use epsillons with DOUBLEs
+
+			            if ((valAsDouble < Float.MIN_VALUE - MIN_DIFF_PREC)
+			                || (valAsDouble > Float.MAX_VALUE - MAX_DIFF_PREC)) {
+			              throwRangeException(String.valueOf(valAsDouble), columnIndex,
+			                  Types.FLOAT);
+			            }
+					}
+				}
+
+				return f;
+			}
+
+			return 0; // for NULL
+		} catch (NumberFormatException nfe) {
+			try {
+				Double valueAsDouble = new Double(val);
+				float valueAsFloat = valueAsDouble.floatValue();
+				
+				if (this.jdbcCompliantTruncationForReads) {
+
+					if (this.jdbcCompliantTruncationForReads && 
+							valueAsFloat == Float.NEGATIVE_INFINITY ||
+							valueAsFloat == Float.POSITIVE_INFINITY) {
+						throwRangeException(valueAsDouble.toString(), 
+								columnIndex, Types.FLOAT);
+					}
+				}
+
+				return valueAsFloat;
+			} catch (NumberFormatException newNfe) {
+				; // ignore, it's not a number
+			}
+
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Invalid_value_for_getFloat()_-____200")
+							+ val //$NON-NLS-1$
+							+ Messages.getString("ResultSet.___in_column__201")
+							+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java int.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int getInt(int columnIndex) throws SQLException {
+		checkRowPos();
+
+		if (!this.isBinaryEncoded) {
+			int columnIndexMinusOne = columnIndex - 1;
+			if (this.useFastIntParsing) {
+				checkColumnBounds(columnIndex);
+
+				if (this.thisRow.isNull(columnIndexMinusOne)) {
+					this.wasNullFlag = true;
+				} else {
+					this.wasNullFlag = false;
+				}
+
+				if (this.wasNullFlag) {
+					return 0;
+				}
+
+				if (this.thisRow.length(columnIndexMinusOne) == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				boolean needsFullParse = this.thisRow
+						.isFloatingPointNumber(columnIndexMinusOne);
+
+				if (!needsFullParse) {
+					try {
+						return getIntWithOverflowCheck(columnIndexMinusOne);
+					} catch (NumberFormatException nfe) {
+						try {
+
+							return parseIntAsDouble(columnIndex, this.thisRow
+									.getString(columnIndexMinusOne,
+											this.fields[columnIndexMinusOne]
+													.getCharacterSet(),
+											this.connection));
+						} catch (NumberFormatException newNfe) {
+							// ignore, it's not a number
+						}
+
+						if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+							long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
+
+							if (this.connection
+									.getJdbcCompliantTruncationForReads()
+									&& (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE)) {
+								throwRangeException(
+										String.valueOf(valueAsLong),
+										columnIndex, Types.INTEGER);
+							}
+
+							return (int) valueAsLong;
+						}
+
+						throw SQLError
+								.createSQLException(
+										Messages
+												.getString("ResultSet.Invalid_value_for_getInt()_-____74")
+												+ this.thisRow
+														.getString(
+																columnIndexMinusOne,
+																this.fields[columnIndexMinusOne]
+																		.getCharacterSet(),
+																this.connection) //$NON-NLS-1$
+												+ "'",
+										SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+			}
+
+			String val = null;
+
+			try {
+				val = getString(columnIndex);
+
+				if ((val != null)) {
+					if (val.length() == 0) {
+						return convertToZeroWithEmptyCheck();
+					}
+
+					if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
+							&& (val.indexOf(".") == -1)) {
+						int intVal = Integer.parseInt(val);
+						
+						checkForIntegerTruncation(columnIndex, null, val, intVal);
+						
+						return intVal;
+					}
+
+					// Convert floating point
+					int intVal =  parseIntAsDouble(columnIndex, val);
+					
+					checkForIntegerTruncation(columnIndex, null, val, intVal);
+					
+					return intVal;
+				}
+
+				return 0;
+			} catch (NumberFormatException nfe) {
+				try {
+					return parseIntAsDouble(columnIndex, val);
+				} catch (NumberFormatException newNfe) {
+					; // ignore, it's not a number
+				}
+
+				if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+					long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
+
+					if (this.jdbcCompliantTruncationForReads
+							&& (valueAsLong < Integer.MIN_VALUE || valueAsLong > Integer.MAX_VALUE)) {
+						throwRangeException(String.valueOf(valueAsLong),
+								columnIndex, Types.INTEGER);
+					}
+
+					return (int) valueAsLong;
+				}
+
+				throw SQLError
+						.createSQLException(
+								Messages
+										.getString("ResultSet.Invalid_value_for_getInt()_-____74")
+										+ val //$NON-NLS-1$
+										+ "'",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		return getNativeInt(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getInt(String columnName) throws SQLException {
+		return getInt(findColumn(columnName));
+	}
+
+	private final int getIntFromString(String val, int columnIndex)
+			throws SQLException {
+		try {
+			if ((val != null)) {
+
+				if (val.length() == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
+						&& (val.indexOf(".") == -1)) {
+					//
+					// JDK-6 doesn't like trailing whitespace
+					//
+					// Note this isn't a performance issue, other
+					// than the iteration over the string, as String.trim()
+					// will return a new string only if whitespace is present
+					//
+					
+					val = val.trim(); 
+					
+					int valueAsInt = Integer.parseInt(val);
+
+					if (this.jdbcCompliantTruncationForReads) {
+						if (valueAsInt == Integer.MIN_VALUE
+								|| valueAsInt == Integer.MAX_VALUE) {
+							long valueAsLong = Long.parseLong(val);
+
+							if (valueAsLong < Integer.MIN_VALUE
+									|| valueAsLong > Integer.MAX_VALUE) {
+								throwRangeException(
+										String.valueOf(valueAsLong),
+										columnIndex, Types.INTEGER);
+							}
+						}
+					}
+
+					return valueAsInt;
+				}
+
+				// Convert floating point
+
+				double valueAsDouble = Double.parseDouble(val);
+
+				if (this.jdbcCompliantTruncationForReads) {
+					if (valueAsDouble < Integer.MIN_VALUE
+							|| valueAsDouble > Integer.MAX_VALUE) {
+						throwRangeException(String.valueOf(valueAsDouble),
+								columnIndex, Types.INTEGER);
+					}
+				}
+
+				return (int) valueAsDouble;
+			}
+
+			return 0; // for NULL
+		} catch (NumberFormatException nfe) {
+			try {
+				double valueAsDouble = Double.parseDouble(val);
+
+				if (this.jdbcCompliantTruncationForReads) {
+					if (valueAsDouble < Integer.MIN_VALUE
+							|| valueAsDouble > Integer.MAX_VALUE) {
+						throwRangeException(String.valueOf(valueAsDouble),
+								columnIndex, Types.INTEGER);
+					}
+				}
+
+				return (int) valueAsDouble;
+			} catch (NumberFormatException newNfe) {
+				; // ignore, it's not a number
+			}
+
+			throw SQLError.createSQLException(Messages
+					.getString("ResultSet.Invalid_value_for_getInt()_-____206")
+					+ val //$NON-NLS-1$
+					+ Messages.getString("ResultSet.___in_column__207")
+					+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java long.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public long getLong(int columnIndex) throws SQLException {
+		return getLong(columnIndex, true);
+	}
+	
+	private long getLong(int columnIndex, boolean overflowCheck) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkRowPos();
+			
+			int columnIndexMinusOne = columnIndex - 1;
+			
+			if (this.useFastIntParsing) {
+			
+				checkColumnBounds(columnIndex);
+
+				if (this.thisRow.isNull(columnIndexMinusOne)) {
+					this.wasNullFlag = true;
+				} else {
+					this.wasNullFlag = false;
+				}
+				
+				if (this.wasNullFlag) {
+					return 0;
+				}
+
+				if (this.thisRow.length(columnIndexMinusOne) == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				boolean needsFullParse = this.thisRow.isFloatingPointNumber(columnIndexMinusOne);
+
+				if (!needsFullParse) {
+					try {
+						return getLongWithOverflowCheck(columnIndexMinusOne, overflowCheck);
+					} catch (NumberFormatException nfe) {
+						try {
+							// To do: Warn of over/underflow???
+							return parseLongAsDouble(columnIndex, this.thisRow
+									.getString(columnIndexMinusOne,
+											this.fields[columnIndexMinusOne]
+													.getCharacterSet(),
+											this.connection));
+						} catch (NumberFormatException newNfe) {
+							// ; // ignore, it's not a number
+						}
+
+						if (this.fields[columnIndexMinusOne].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+							return getNumericRepresentationOfSQLBitType(columnIndex);
+						}
+						
+						throw SQLError.createSQLException(
+								Messages
+										.getString("ResultSet.Invalid_value_for_getLong()_-____79")
+										+ this.thisRow
+										.getString(columnIndexMinusOne,
+												this.fields[columnIndexMinusOne]
+														.getCharacterSet(),
+												this.connection) //$NON-NLS-1$
+										+ "'",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+			}
+
+			String val = null;
+
+			try {
+				val = getString(columnIndex);
+
+				if ((val != null)) {
+					if (val.length() == 0) {
+						return convertToZeroWithEmptyCheck();
+					}
+
+					if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) {
+						return parseLongWithOverflowCheck(columnIndex, null,
+								val, overflowCheck);
+					}
+
+					// Convert floating point
+					return parseLongAsDouble(columnIndex, val);
+				}
+
+				return 0; // for NULL
+			} catch (NumberFormatException nfe) {
+				try {
+					return parseLongAsDouble(columnIndex, val);
+				} catch (NumberFormatException newNfe) {
+					// ; // ignore, it's not a number
+				}
+
+				throw SQLError.createSQLException(
+						Messages
+								.getString("ResultSet.Invalid_value_for_getLong()_-____79")
+								+ val //$NON-NLS-1$
+								+ "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		return getNativeLong(columnIndex, overflowCheck, true);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public long getLong(String columnName) throws SQLException {
+		return getLong(findColumn(columnName));
+	}
+
+	private final long getLongFromString(String val, int columnIndex)
+			throws SQLException {
+		try {
+			if ((val != null)) {
+
+				if (val.length() == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)) {
+					return parseLongWithOverflowCheck(columnIndex, null, val, true);
+				}
+
+				// Convert floating point
+				return parseLongAsDouble(columnIndex, val);
+			}
+
+			return 0; // for NULL
+		} catch (NumberFormatException nfe) {
+			try {
+				// To do: Warn of over/underflow???
+				return parseLongAsDouble(columnIndex, val);
+			} catch (NumberFormatException newNfe) {
+				; // ignore, it's not a number
+			}
+
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Invalid_value_for_getLong()_-____211")
+							+ val //$NON-NLS-1$
+							+ Messages.getString("ResultSet.___in_column__212")
+							+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * The numbers, types and properties of a ResultSet's columns are provided
+	 * by the getMetaData method
+	 * 
+	 * @return a description of the ResultSet's columns
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public java.sql.ResultSetMetaData getMetaData() throws SQLException {
+		checkClosed();
+
+		return new com.mysql.jdbc.ResultSetMetaData(this.fields,
+				this.connection.getUseOldAliasMetadataBehavior());
+	}
+
+	/**
+	 * JDBC 2.0 Get an array column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing an SQL array
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	protected java.sql.Array getNativeArray(int i) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * A column value can be retrieved as a stream of ASCII characters and then
+	 * read in chunks from the stream. This method is particulary suitable for
+	 * retrieving large LONGVARCHAR values. The JDBC driver will do any
+	 * necessary conversion from the database format into ASCII.
+	 * 
+	 * <p>
+	 * <B>Note:</B> All the data in the returned stream must be read prior to
+	 * getting the value of any other column. The next call to a get method
+	 * implicitly closes the stream. Also, a stream may return 0 for available()
+	 * whether there is data available or not.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return a Java InputStream that delivers the database column value as a
+	 *         stream of one byte ASCII characters. If the value is SQL NULL
+	 *         then the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see getBinaryStream
+	 */
+	protected InputStream getNativeAsciiStream(int columnIndex)
+			throws SQLException {
+		checkRowPos();
+
+		return getNativeBinaryStream(columnIndex);
+	}
+
+	/**
+	 * JDBC 2.0 Get the value of a column in the current row as a
+	 * java.math.BigDecimal object.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return the column value (full precision); if the value is SQL NULL, the
+	 *         result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	protected BigDecimal getNativeBigDecimal(int columnIndex)
+			throws SQLException {
+
+		checkColumnBounds(columnIndex);
+		
+		int scale = this.fields[columnIndex - 1].getDecimals();
+		
+		return getNativeBigDecimal(columnIndex, scale);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.math.BigDecimal
+	 * object
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * @param scale
+	 *            the number of digits to the right of the decimal
+	 * 
+	 * @return the column value; if the value is SQL NULL, null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected BigDecimal getNativeBigDecimal(int columnIndex, int scale)
+			throws SQLException {
+		checkColumnBounds(columnIndex);
+		
+		String stringVal = null;
+		
+		Field f = this.fields[columnIndex - 1];
+		
+		Object value = this.thisRow.getColumnValue(columnIndex - 1);
+		
+		if (value == null) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+		
+		switch (f.getSQLType()) {
+			case Types.DECIMAL:
+			case Types.NUMERIC:
+				stringVal = StringUtils
+						.toAsciiString((byte[]) value);
+				break;
+			default:
+				stringVal = getNativeString(columnIndex);
+		}
+
+		return getBigDecimalFromString(stringVal, columnIndex, scale);
+	}
+
+	/**
+	 * A column value can also be retrieved as a binary stream. This method is
+	 * suitable for retrieving LONGVARBINARY values.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return a Java InputStream that delivers the database column value as a
+	 *         stream of bytes. If the value is SQL NULL, then the result is
+	 *         null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see getAsciiStream
+	 * @see getUnicodeStream
+	 */
+	protected InputStream getNativeBinaryStream(int columnIndex)
+			throws SQLException {
+		checkRowPos();
+
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		if (this.thisRow.isNull(columnIndexMinusOne)) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+		
+		switch (this.fields[columnIndexMinusOne].getSQLType()) {
+		case Types.BIT:
+		case Types.BINARY:
+		case Types.VARBINARY:
+		case Types.BLOB:
+		case Types.LONGVARBINARY:
+			return this.thisRow.getBinaryInputStream(columnIndexMinusOne);
+		}
+		
+		byte[] b = getNativeBytes(columnIndex, false);
+
+		if (b != null) {
+			return new ByteArrayInputStream(b);
+		}
+
+		return null;
+	}
+
+	/**
+	 * JDBC 2.0 Get a BLOB column.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a BLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected java.sql.Blob getNativeBlob(int columnIndex) throws SQLException {
+		checkRowPos();
+
+		checkColumnBounds(columnIndex);
+
+		Object value = this.thisRow.getColumnValue(columnIndex - 1);
+		
+		if (value == null) {
+			this.wasNullFlag = true;
+		} else {
+			this.wasNullFlag = false;
+		}
+
+		if (this.wasNullFlag) {
+			return null;
+		}
+
+		int mysqlType = this.fields[columnIndex - 1].getMysqlType();
+
+		byte[] dataAsBytes = null;
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+		case MysqlDefs.FIELD_TYPE_BLOB:
+			dataAsBytes = (byte[]) value;
+			break;
+
+		default:
+			dataAsBytes = getNativeBytes(columnIndex, false);
+		}
+
+		if (!this.connection.getEmulateLocators()) {
+			return new Blob(dataAsBytes);
+		}
+
+		return new BlobFromLocator(this, columnIndex);
+	}
+
+	public static boolean arraysEqual(byte[] left, byte[] right) {
+		if (left == null) {
+			return right == null;
+		}
+		if (right == null) {
+			return false;
+		}
+		if (left.length != right.length) {
+			return false;
+		}
+		for (int i = 0; i < left.length; i++) {
+			if (left[i] != right[i]) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java byte.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected byte getNativeByte(int columnIndex) throws SQLException {
+		return getNativeByte(columnIndex, true);
+	}
+	
+	protected byte getNativeByte(int columnIndex, boolean overflowCheck) throws SQLException {
+		checkRowPos();
+
+		checkColumnBounds(columnIndex);
+
+		Object value = this.thisRow.getColumnValue(columnIndex - 1);
+		
+		if (value == null) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		if (value == null) {
+			this.wasNullFlag = true;
+		} else {
+			this.wasNullFlag = false;
+		}
+			
+		if (this.wasNullFlag) {
+			return 0;
+		}
+
+		columnIndex--;
+
+		Field field = this.fields[columnIndex];
+
+		switch (field.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_BIT:
+			long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1);
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
+					(valueAsLong < Byte.MIN_VALUE
+							|| valueAsLong > Byte.MAX_VALUE)) {
+				throwRangeException(String.valueOf(valueAsLong), columnIndex + 1,
+						Types.TINYINT);
+			}
+			
+			return (byte)valueAsLong;
+		case MysqlDefs.FIELD_TYPE_TINY:
+			byte valueAsByte = ((byte[]) value)[0];
+			
+			if (!field.isUnsigned()) {
+				return valueAsByte;
+			}
+
+			short valueAsShort = (valueAsByte >= 0) ? 
+					valueAsByte : (short)(valueAsByte + (short)256);
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsShort > Byte.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsShort),
+							columnIndex + 1, Types.TINYINT);
+				}
+			}
+			
+			return (byte)valueAsShort;
+
+		case MysqlDefs.FIELD_TYPE_SHORT:
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			valueAsShort = getNativeShort(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsShort < Byte.MIN_VALUE
+						|| valueAsShort > Byte.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsShort),
+							columnIndex + 1, Types.TINYINT);
+				}
+			}
+
+			return (byte) valueAsShort;
+		case MysqlDefs.FIELD_TYPE_INT24:
+		case MysqlDefs.FIELD_TYPE_LONG:
+			int valueAsInt = getNativeInt(columnIndex + 1, false);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsInt < Byte.MIN_VALUE || valueAsInt > Byte.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsInt),
+							columnIndex + 1, Types.TINYINT);
+				}
+			}
+
+			return (byte) valueAsInt;
+
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			float valueAsFloat = getNativeFloat(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsFloat < Byte.MIN_VALUE
+						|| valueAsFloat > Byte.MAX_VALUE) {
+
+					throwRangeException(String.valueOf(valueAsFloat),
+							columnIndex + 1, Types.TINYINT);
+				}
+			}
+
+			return (byte) valueAsFloat;
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			double valueAsDouble = getNativeDouble(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsDouble < Byte.MIN_VALUE
+						|| valueAsDouble > Byte.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.TINYINT);
+				}
+			}
+
+			return (byte) valueAsDouble;
+
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			valueAsLong = getNativeLong(columnIndex + 1, false, true);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsLong < Byte.MIN_VALUE
+						|| valueAsLong > Byte.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsLong),
+							columnIndex + 1, Types.TINYINT);
+				}
+			}
+
+			return (byte) valueAsLong;
+
+		default:
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getByte()", columnIndex,
+						this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex],
+						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+
+			return getByteFromString(getNativeString(columnIndex + 1),
+					columnIndex + 1);
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java byte array.
+	 * 
+	 * <p>
+	 * <b>Be warned</b> If the blob is huge, then you may run out of memory.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected byte[] getNativeBytes(int columnIndex, boolean noConversion)
+			throws SQLException {
+		checkRowPos();
+
+		checkColumnBounds(columnIndex);
+
+		Object value = this.thisRow.getColumnValue(columnIndex - 1);
+		
+		if (value == null) {
+			this.wasNullFlag = true;
+		} else {
+			this.wasNullFlag = false;
+		}
+
+		if (this.wasNullFlag) {
+			return null;
+		}
+
+		Field field = this.fields[columnIndex - 1];
+		
+		int mysqlType = field.getMysqlType();
+
+		// Workaround for emulated locators in servers > 4.1,
+		// as server returns SUBSTRING(blob) as STRING type...
+		if (noConversion) {
+			mysqlType = MysqlDefs.FIELD_TYPE_BLOB;
+		}
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+		case MysqlDefs.FIELD_TYPE_BLOB:
+		case MysqlDefs.FIELD_TYPE_BIT:
+			return (byte[]) value;
+
+		default:
+			int sqlType = field.getSQLType();
+		
+			if (sqlType == Types.VARBINARY || sqlType == Types.BINARY) {
+				return (byte[]) value;
+			}
+		
+			return getBytesFromString(getNativeString(columnIndex), columnIndex);
+		}
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Get the value of a column in the current row as a java.io.Reader.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the column to get the value from
+	 * 
+	 * @return the value in the column as a java.io.Reader.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected java.io.Reader getNativeCharacterStream(int columnIndex)
+			throws SQLException {
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		switch (this.fields[columnIndexMinusOne].getSQLType()) {
+		case Types.CHAR:
+		case Types.VARCHAR:
+		case Types.LONGVARCHAR:
+		case Types.CLOB:		
+			if (this.thisRow.isNull(columnIndexMinusOne)) {
+				this.wasNullFlag = true;
+				
+				return null;
+			}
+			
+			this.wasNullFlag = false;
+			
+			return this.thisRow.getReader(columnIndexMinusOne);
+		}
+		
+		String asString = null;
+		
+		asString = getStringForClob(columnIndex);
+
+		if (asString == null) {
+			return null;
+		}
+		
+		return getCharacterStreamFromString(asString, columnIndex);
+	}
+
+	/**
+	 * JDBC 2.0 Get a CLOB column.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing a CLOB
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected java.sql.Clob getNativeClob(int columnIndex) throws SQLException {
+		String stringVal = getStringForClob(columnIndex);
+
+		if (stringVal == null) {
+			return null;
+		}
+
+		return getClobFromString(stringVal, columnIndex);
+	}
+
+	private String getNativeConvertToString(int columnIndex, 
+			Field field)
+			throws SQLException {
+
+		
+		int sqlType = field.getSQLType();
+		int mysqlType = field.getMysqlType();
+
+		switch (sqlType) {
+		case Types.BIT:
+			return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex));
+		case Types.BOOLEAN:
+			boolean booleanVal = getBoolean(columnIndex);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			return String.valueOf(booleanVal);
+
+		case Types.TINYINT:
+			byte tinyintVal = getNativeByte(columnIndex, false);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			if (!field.isUnsigned() || tinyintVal >= 0) {
+				return String.valueOf(tinyintVal);
+			}
+
+			short unsignedTinyVal = (short) (tinyintVal & 0xff);
+
+			return String.valueOf(unsignedTinyVal);
+
+		case Types.SMALLINT:
+
+			int intVal = getNativeInt(columnIndex, false);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			if (!field.isUnsigned() || intVal >= 0) {
+				return String.valueOf(intVal);
+			}
+
+			intVal = intVal & 0xffff;
+
+			return String.valueOf(intVal);
+
+		case Types.INTEGER:
+			intVal = getNativeInt(columnIndex, false);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			if (!field.isUnsigned() || intVal >= 0
+					|| field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
+
+				return String.valueOf(intVal);
+			}
+
+			long longVal = intVal & 0xffffffffL;
+
+			return String.valueOf(longVal);
+
+		case Types.BIGINT:
+
+			if (!field.isUnsigned()) {
+				longVal = getNativeLong(columnIndex, false, true);
+
+				if (this.wasNullFlag) {
+					return null;
+				}
+
+				return String.valueOf(longVal);
+			}
+
+			longVal = getNativeLong(columnIndex, false, false);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			return String.valueOf(convertLongToUlong(longVal));
+		case Types.REAL:
+			float floatVal = getNativeFloat(columnIndex);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			return String.valueOf(floatVal);
+
+		case Types.FLOAT:
+		case Types.DOUBLE:
+			double doubleVal = getNativeDouble(columnIndex);
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			return String.valueOf(doubleVal);
+
+		case Types.DECIMAL:
+		case Types.NUMERIC:
+			String stringVal = StringUtils
+					.toAsciiString((byte[]) this.thisRow.getColumnValue(columnIndex - 1));
+
+			BigDecimal val;
+
+			if (stringVal != null) {
+				this.wasNullFlag = false;
+				
+				if (stringVal.length() == 0) {
+					val = new BigDecimal(0);
+
+					return val.toString();
+				}
+
+				try {
+					val = new BigDecimal(stringVal);
+				} catch (NumberFormatException ex) {
+					throw SQLError.createSQLException(
+							Messages
+									.getString("ResultSet.Bad_format_for_BigDecimal", 
+											new Object[] {stringVal, Constants.integerValueOf(columnIndex)}),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				return val.toString();
+			}
+
+			this.wasNullFlag = true;
+			
+			return null;
+
+		case Types.CHAR:
+		case Types.VARCHAR:
+		case Types.LONGVARCHAR:
+
+			return extractStringFromNativeColumn(columnIndex, mysqlType);
+		case Types.BINARY:
+		case Types.VARBINARY:
+		case Types.LONGVARBINARY:
+
+			if (!field.isBlob()) {
+				return extractStringFromNativeColumn(columnIndex, mysqlType);
+			} else if (!field.isBinary()) {
+				return extractStringFromNativeColumn(columnIndex, mysqlType);
+			} else {
+				byte[] data = getBytes(columnIndex);
+				Object obj = data;
+
+				if ((data != null) && (data.length >= 2)) {
+					if ((data[0] == -84) && (data[1] == -19)) {
+						// Serialized object?
+						try {
+							ByteArrayInputStream bytesIn = new ByteArrayInputStream(
+									data);
+							ObjectInputStream objIn = new ObjectInputStream(
+									bytesIn);
+							obj = objIn.readObject();
+							objIn.close();
+							bytesIn.close();
+						} catch (ClassNotFoundException cnfe) {
+							throw SQLError.createSQLException(
+									Messages
+											.getString("ResultSet.Class_not_found___91") //$NON-NLS-1$
+											+ cnfe.toString()
+											+ Messages
+													.getString("ResultSet._while_reading_serialized_object_92")); //$NON-NLS-1$
+						} catch (IOException ex) {
+							obj = data; // not serialized?
+						}
+					}
+
+					return obj.toString();
+				}
+
+				return extractStringFromNativeColumn(columnIndex, mysqlType);
+			}
+
+		case Types.DATE:
+
+			// The YEAR datatype needs to be handled differently here.
+			if (mysqlType == MysqlDefs.FIELD_TYPE_YEAR) {
+				short shortVal = getNativeShort(columnIndex);
+
+				if (!this.connection.getYearIsDateType()) {
+
+					if (this.wasNullFlag) {
+						return null;
+					}
+
+					return String.valueOf(shortVal);
+				}
+
+				if (field.getLength() == 2) {
+
+					if (shortVal <= 69) {
+						shortVal = (short) (shortVal + 100);
+					}
+
+					shortVal += 1900;
+				}
+
+				return fastDateCreate(null, shortVal, 1, 1).toString();
+
+			}
+
+			Date dt = getNativeDate(columnIndex);
+
+			if (dt == null) {
+				return null;
+			}
+
+			return String.valueOf(dt);
+
+		case Types.TIME:
+			Time tm = getNativeTime(columnIndex, null, this.defaultTimeZone, false);
+
+			if (tm == null) {
+				return null;
+			}
+
+			return String.valueOf(tm);
+
+		case Types.TIMESTAMP:
+			Timestamp tstamp = getNativeTimestamp(columnIndex,
+					null, this.defaultTimeZone, false);
+
+			if (tstamp == null) {
+				return null;
+			}
+
+			String result = String.valueOf(tstamp);
+
+			if (!this.connection.getNoDatetimeStringSync()) {
+				return result;
+			}
+
+			if (result.endsWith(".0")) {
+				return result.substring(0, result.length() - 2);
+			}
+
+		default:
+			return extractStringFromNativeColumn(columnIndex, mysqlType);
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Date object
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value; null if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected java.sql.Date getNativeDate(int columnIndex) throws SQLException {
+		return getNativeDate(columnIndex, null);
+	}
+
+	/**
+	 * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date
+	 * object. Use the calendar to construct an appropriate millisecond value
+	 * for the Date, if the underlying database doesn't store timezone
+	 * information.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param tz
+	 *            the calendar to use in constructing the date
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	protected java.sql.Date getNativeDate(int columnIndex, TimeZone tz)
+			throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		int mysqlType = this.fields[columnIndexMinusOne].getMysqlType();
+		
+		java.sql.Date dateToReturn = null;
+		
+		if (mysqlType == MysqlDefs.FIELD_TYPE_DATE) {
+
+			dateToReturn = this.thisRow.getNativeDate(columnIndexMinusOne, 
+					this.connection, this);	
+		} else {
+
+			boolean rollForward = (tz != null && !tz.equals(this.getDefaultTimeZone()));
+			
+			dateToReturn = (Date) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne,
+					null, Types.DATE, mysqlType, tz, rollForward, this.connection,
+					this);
+		}
+		
+		//
+		// normally, we allow ResultSetImpl methods to check for null first,
+		// but with DATETIME values we have this wacky need to support
+		// 0000-00-00 00:00:00 -> NULL, so we have to defer
+		// to the RowHolder implementation, and check the return value.
+		//
+
+		if (dateToReturn == null) {
+
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		this.wasNullFlag = false;
+
+		return dateToReturn;
+	}
+
+	java.sql.Date getNativeDateViaParseConversion(int columnIndex) throws SQLException {
+		if (this.useUsageAdvisor) {
+			issueConversionViaParsingWarning("getDate()", columnIndex,
+					this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1],
+					new int[] { MysqlDefs.FIELD_TYPE_DATE });
+		}
+
+		String stringVal = getNativeString(columnIndex);
+
+		return getDateFromString(stringVal, columnIndex);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java double.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected double getNativeDouble(int columnIndex) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		columnIndex--; // / JDBC is 1-based
+
+		if (this.thisRow.isNull(columnIndex)) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		this.wasNullFlag = false;
+
+		Field f= this.fields[columnIndex];
+		
+		switch (f.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			return this.thisRow.getNativeDouble(columnIndex);
+		case MysqlDefs.FIELD_TYPE_TINY:
+			if (!f.isUnsigned()) {
+				return (double) getNativeByte(columnIndex + 1);
+			}
+			
+			return (double) getNativeShort(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_SHORT:
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			if (!f.isUnsigned()) {
+				return (double) getNativeShort(columnIndex + 1);
+			}
+			
+			return (double) getNativeInt(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_INT24:
+		case MysqlDefs.FIELD_TYPE_LONG:
+			if (!f.isUnsigned()) {
+				return (double) getNativeInt(columnIndex + 1);
+			}
+			
+			return (double) getNativeLong(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			long valueAsLong = getNativeLong(columnIndex + 1);
+			
+			if (!f.isUnsigned()) {
+				return (double) valueAsLong;
+			}
+			
+			BigInteger asBigInt = convertLongToUlong(valueAsLong);
+			
+			// TODO: Check for overflow
+			
+			return asBigInt.doubleValue();
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			return (double) getNativeFloat(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_BIT:
+			return getNumericRepresentationOfSQLBitType(columnIndex + 1);
+		default:
+			String stringVal = getNativeString(columnIndex + 1);
+
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getDouble()", columnIndex,
+						stringVal, this.fields[columnIndex],
+						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+
+			return getDoubleFromString(stringVal, columnIndex + 1);
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java float.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected float getNativeFloat(int columnIndex) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		columnIndex--; // / JDBC is 1-based
+
+		if (this.thisRow.isNull(columnIndex)) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		this.wasNullFlag = false;
+
+		Field f = this.fields[columnIndex];
+		
+		switch (f.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_BIT:
+			long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1);
+
+			return valueAsLong;
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			
+			// Only foolproof way to check for overflow
+			// Not efficient, but if you don't want to be inefficient, use the
+			// correct binding for the type!
+			
+			Double valueAsDouble = new Double(getNativeDouble(columnIndex + 1));
+			
+			float valueAsFloat = valueAsDouble.floatValue();
+			
+			if (this.jdbcCompliantTruncationForReads && 
+					valueAsFloat == Float.NEGATIVE_INFINITY ||
+					valueAsFloat == Float.POSITIVE_INFINITY) {
+				throwRangeException(valueAsDouble.toString(), 
+						columnIndex + 1, Types.FLOAT);
+			}
+
+			return (float) getNativeDouble(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_TINY:
+			if (!f.isUnsigned()) {
+				return (float) getNativeByte(columnIndex + 1);
+			}
+			
+			return (float) getNativeShort(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_SHORT:
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			if (!f.isUnsigned()) {
+				return (float) getNativeShort(columnIndex + 1);
+			}
+			
+			return (float) getNativeInt(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_INT24:
+		case MysqlDefs.FIELD_TYPE_LONG:
+			if (!f.isUnsigned()) {
+				return (float) getNativeInt(columnIndex + 1);
+			}
+			
+			return (float) getNativeLong(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			valueAsLong = getNativeLong(columnIndex + 1);
+			
+			if (!f.isUnsigned()) {
+				return (float) valueAsLong;
+			}
+			
+			BigInteger asBigInt = convertLongToUlong(valueAsLong);
+			
+			// TODO: Check for overflow
+			
+			return asBigInt.floatValue();
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			
+			return this.thisRow.getNativeFloat(columnIndex);
+
+		default:
+			String stringVal = getNativeString(columnIndex + 1);
+		
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getFloat()", columnIndex,
+						stringVal, this.fields[columnIndex],
+						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+
+			return getFloatFromString(stringVal, columnIndex + 1);
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java int.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected int getNativeInt(int columnIndex) throws SQLException {
+		return getNativeInt(columnIndex, true);
+	}
+	
+	protected int getNativeInt(int columnIndex, boolean overflowCheck) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		columnIndex--; // / JDBC is 1-based
+
+		if (this.thisRow.isNull(columnIndex)) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		this.wasNullFlag = false;
+
+		Field f = this.fields[columnIndex];
+
+		switch (f.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_BIT:
+			long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1);
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
+					(valueAsLong < Integer.MIN_VALUE
+							|| valueAsLong > Integer.MAX_VALUE)) {
+				throwRangeException(String.valueOf(valueAsLong), columnIndex + 1,
+						Types.INTEGER);
+			}
+			
+			return (short)valueAsLong;
+		case MysqlDefs.FIELD_TYPE_TINY:
+			byte tinyintVal = getNativeByte(columnIndex + 1, false);
+			
+			if (!f.isUnsigned() || tinyintVal >= 0) {
+				return tinyintVal;
+			}
+
+			return tinyintVal + 256;
+		case MysqlDefs.FIELD_TYPE_SHORT:
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			short asShort = getNativeShort(columnIndex + 1, false);
+			
+			if (!f.isUnsigned() || asShort >= 0) {
+				return asShort;
+			}
+
+			return asShort + 65536;
+		case MysqlDefs.FIELD_TYPE_INT24:
+		case MysqlDefs.FIELD_TYPE_LONG:
+			
+			int valueAsInt = this.thisRow.getNativeInt(columnIndex);
+
+			if (!f.isUnsigned()) {	
+				return valueAsInt;
+			}
+			
+			valueAsLong = (valueAsInt >= 0) ? 
+					valueAsInt : valueAsInt + 4294967296L; 
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
+					valueAsLong > Integer.MAX_VALUE) {
+				throwRangeException(String.valueOf(valueAsLong),
+						columnIndex + 1, Types.INTEGER);
+			}
+			
+			return (int)valueAsLong;
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			valueAsLong = getNativeLong(columnIndex + 1, false, true);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsLong < Integer.MIN_VALUE
+						|| valueAsLong > Integer.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsLong),
+							columnIndex + 1, Types.INTEGER);
+				}
+			}
+
+			return (int) valueAsLong;
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			double valueAsDouble = getNativeDouble(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsDouble < Integer.MIN_VALUE
+						|| valueAsDouble > Integer.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.INTEGER);
+				}
+			}
+
+			return (int) valueAsDouble;
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			valueAsDouble = getNativeFloat(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsDouble < Integer.MIN_VALUE
+						|| valueAsDouble > Integer.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.INTEGER);
+				}
+			}
+
+			return (int) valueAsDouble;
+
+		default:
+			String stringVal = getNativeString(columnIndex + 1);
+		
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getInt()", columnIndex,
+						stringVal, this.fields[columnIndex],
+						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+
+			return getIntFromString(stringVal, columnIndex + 1);
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java long.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected long getNativeLong(int columnIndex) throws SQLException {
+		return getNativeLong(columnIndex, true, true);
+	}
+	
+	protected long getNativeLong(int columnIndex, boolean overflowCheck, 
+			boolean expandUnsignedLong) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		columnIndex--; // / JDBC is 1-based
+
+		if (this.thisRow.isNull(columnIndex)) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		this.wasNullFlag = false;
+
+		Field f = this.fields[columnIndex];
+
+		switch (f.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_BIT:
+			return getNumericRepresentationOfSQLBitType(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_TINY:
+			if (!f.isUnsigned()) {
+				return getNativeByte(columnIndex + 1);
+			}
+
+			return getNativeInt(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_SHORT:
+			if (!f.isUnsigned()) {
+				return getNativeShort(columnIndex + 1);
+			}
+
+			return getNativeInt(columnIndex + 1, false);
+		case MysqlDefs.FIELD_TYPE_YEAR:
+
+			return getNativeShort(columnIndex + 1);
+		case MysqlDefs.FIELD_TYPE_INT24:
+		case MysqlDefs.FIELD_TYPE_LONG:
+			int asInt = getNativeInt(columnIndex + 1, false);
+			
+			if (!f.isUnsigned() || asInt >= 0) {
+				return asInt;
+			}
+
+			return asInt + 4294967296L;
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			long valueAsLong = this.thisRow.getNativeLong(columnIndex);
+
+			if (!f.isUnsigned() || !expandUnsignedLong) {
+				return valueAsLong;
+			}
+			
+			BigInteger asBigInt = convertLongToUlong(valueAsLong);
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads && 
+					((asBigInt.compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0 ) ||
+					 (asBigInt.compareTo(new BigInteger(String.valueOf(Long.MIN_VALUE))) < 0))) {
+				throwRangeException(asBigInt.toString(),
+						columnIndex + 1, Types.BIGINT);
+			}
+			
+			return getLongFromString(asBigInt.toString(), columnIndex + 1);
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			double valueAsDouble = getNativeDouble(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsDouble < Long.MIN_VALUE
+						|| valueAsDouble > Long.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.BIGINT);
+				}
+			}
+
+			return (long) valueAsDouble;
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			valueAsDouble = getNativeFloat(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsDouble < Long.MIN_VALUE
+						|| valueAsDouble > Long.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.BIGINT);
+				}
+			}
+
+			return (long) valueAsDouble;
+		default:
+			String stringVal = getNativeString(columnIndex + 1);
+		
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getLong()", columnIndex,
+						stringVal, this.fields[columnIndex],
+						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+
+			return getLongFromString(stringVal, columnIndex + 1);
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Get a REF(&lt;structured-type&gt;) column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing data of an SQL REF type
+	 * 
+	 * @throws SQLException
+	 *             as this is not implemented
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	protected java.sql.Ref getNativeRef(int i) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java short.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected short getNativeShort(int columnIndex) throws SQLException {
+		return getNativeShort(columnIndex, true);
+	}
+	
+	protected short getNativeShort(int columnIndex, boolean overflowCheck) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		columnIndex--; // / JDBC is 1-based
+
+		
+		if (this.thisRow.isNull(columnIndex)) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		this.wasNullFlag = false;
+
+		Field f = this.fields[columnIndex];
+
+		switch (f.getMysqlType()) {
+
+		case MysqlDefs.FIELD_TYPE_TINY:
+			byte tinyintVal = getNativeByte(columnIndex + 1, false);
+			
+			if (!f.isUnsigned() || tinyintVal >= 0) {
+             	return tinyintVal;
+			}
+			
+			return (short)(tinyintVal + (short)256);
+		case MysqlDefs.FIELD_TYPE_SHORT:
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			
+			short asShort = this.thisRow.getNativeShort(columnIndex);
+
+			if (!f.isUnsigned()) {
+				return asShort;
+			}
+			
+			int valueAsInt = asShort & 0xffff;
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
+					valueAsInt > Short.MAX_VALUE) {
+				throwRangeException(String.valueOf(valueAsInt),
+						columnIndex + 1, Types.SMALLINT);
+			}
+			
+			return (short)valueAsInt;
+		case MysqlDefs.FIELD_TYPE_INT24:
+		case MysqlDefs.FIELD_TYPE_LONG:
+			if (!f.isUnsigned()) {
+				valueAsInt = getNativeInt(columnIndex + 1, false);
+				
+				if (overflowCheck && this.jdbcCompliantTruncationForReads &&
+						valueAsInt > Short.MAX_VALUE ||
+						valueAsInt < Short.MIN_VALUE) {
+					throwRangeException(String.valueOf(valueAsInt),
+							columnIndex + 1, Types.SMALLINT);
+				}
+				
+				return (short)valueAsInt;
+			}
+			
+			long valueAsLong = getNativeLong(columnIndex + 1, false, true);
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads &&
+					valueAsLong > Short.MAX_VALUE) {
+				throwRangeException(String.valueOf(valueAsLong),
+						columnIndex + 1, Types.SMALLINT);
+			}
+			
+			return (short)valueAsLong;
+			
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			valueAsLong = getNativeLong(columnIndex + 1, false, false);
+			
+			if (!f.isUnsigned()) {
+				if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+					if (valueAsLong < Short.MIN_VALUE
+							|| valueAsLong > Short.MAX_VALUE) {
+						throwRangeException(String.valueOf(valueAsLong),
+								columnIndex + 1, Types.SMALLINT);
+					}
+				}
+	
+				return (short) valueAsLong;
+			}
+			
+			BigInteger asBigInt = convertLongToUlong(valueAsLong);
+			
+			if (overflowCheck && this.jdbcCompliantTruncationForReads && 
+					((asBigInt.compareTo(new BigInteger(String.valueOf(Short.MAX_VALUE))) > 0 ) ||
+					 (asBigInt.compareTo(new BigInteger(String.valueOf(Short.MIN_VALUE))) < 0))) {
+				throwRangeException(asBigInt.toString(),
+						columnIndex + 1, Types.SMALLINT);
+			}
+			
+			return (short)getIntFromString(asBigInt.toString(), columnIndex + 1);
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			double valueAsDouble = getNativeDouble(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsDouble < Short.MIN_VALUE
+						|| valueAsDouble > Short.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.SMALLINT);
+				}
+			}
+
+			return (short) valueAsDouble;
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			float valueAsFloat = getNativeFloat(columnIndex + 1);
+
+			if (overflowCheck && this.jdbcCompliantTruncationForReads) {
+				if (valueAsFloat < Short.MIN_VALUE
+						|| valueAsFloat > Short.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsFloat),
+							columnIndex + 1, Types.SMALLINT);
+				}
+			}
+
+			return (short) valueAsFloat;
+		default:
+			String stringVal = getNativeString(columnIndex + 1);
+		
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getShort()", columnIndex,
+						stringVal, this.fields[columnIndex],
+						new int[] { MysqlDefs.FIELD_TYPE_DOUBLE,
+								MysqlDefs.FIELD_TYPE_TINY,
+								MysqlDefs.FIELD_TYPE_SHORT,
+								MysqlDefs.FIELD_TYPE_LONG,
+								MysqlDefs.FIELD_TYPE_LONGLONG,
+								MysqlDefs.FIELD_TYPE_FLOAT });
+			}
+
+			return getShortFromString(stringVal, columnIndex + 1);
+		}
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java String
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value, null for SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	protected String getNativeString(int columnIndex) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		if (this.fields == null) {
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Query_generated_no_fields_for_ResultSet_133"), //$NON-NLS-1$
+					SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
+		}
+		
+		if (this.thisRow.isNull(columnIndex - 1)) {
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		this.wasNullFlag = false;
+
+		String stringVal = null;
+
+		Field field = this.fields[columnIndex - 1];
+
+		// TODO: Check Types Here.
+		stringVal = getNativeConvertToString(columnIndex, field);
+
+		if (field.isZeroFill() && (stringVal != null)) {
+			int origLength = stringVal.length();
+
+			StringBuffer zeroFillBuf = new StringBuffer(origLength);
+
+			long numZeros = field.getLength() - origLength;
+
+			for (long i = 0; i < numZeros; i++) {
+				zeroFillBuf.append('0');
+			}
+
+			zeroFillBuf.append(stringVal);
+
+			stringVal = zeroFillBuf.toString();
+		}
+
+		return stringVal;
+	}
+
+	private Time getNativeTime(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward)
+			throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		int mysqlType = this.fields[columnIndexMinusOne].getMysqlType();
+
+		Time timeVal = null;
+		
+		if (mysqlType == MysqlDefs.FIELD_TYPE_TIME) {
+			timeVal = this.thisRow.getNativeTime(columnIndexMinusOne, 
+					targetCalendar, tz, rollForward, this.connection, this);
+			
+		} else {
+			timeVal = (Time) this.thisRow.getNativeDateTimeValue(columnIndexMinusOne,
+					null, Types.TIME, mysqlType, tz, rollForward, this.connection,
+					this);
+		}
+		
+		//
+		// normally, we allow ResultSetImpl methods to check for null first,
+		// but with DATETIME values we have this wacky need to support
+		// 0000-00-00 00:00:00 -> NULL, so we have to defer
+		// to the RowHolder implementation, and check the return value.
+		//
+
+		if (timeVal == null) {
+
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		this.wasNullFlag = false;
+
+		return timeVal;
+	}
+	
+	Time getNativeTimeViaParseConversion(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward) throws SQLException {
+		if (this.useUsageAdvisor) {
+			issueConversionViaParsingWarning("getTime()", columnIndex,
+					this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1],
+					new int[] { MysqlDefs.FIELD_TYPE_TIME });
+		}
+	
+		String strTime = getNativeString(columnIndex);
+	
+		return getTimeFromString(strTime, targetCalendar, columnIndex, tz, rollForward);
+	}
+
+	private Timestamp getNativeTimestamp(int columnIndex, 
+			Calendar targetCalendar,
+			TimeZone tz,
+			boolean rollForward) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		Timestamp tsVal = null;
+
+		int mysqlType = this.fields[columnIndexMinusOne].getMysqlType();
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			tsVal = this.thisRow.getNativeTimestamp(columnIndexMinusOne,
+					targetCalendar, tz, rollForward, this.connection, this);
+			break;
+
+		default:
+			
+
+			tsVal = (Timestamp) this.thisRow.getNativeDateTimeValue(
+					columnIndexMinusOne, null, Types.TIMESTAMP, mysqlType, tz,
+					rollForward, this.connection, this);
+		}
+
+		//
+		// normally, we allow ResultSetImpl methods to check for null first,
+		// but with DATETIME values we have this wacky need to support
+		// 0000-00-00 00:00:00 -> NULL, so we have to defer
+		// to the RowHolder implementation, and check the return value.
+		//
+		
+		if (tsVal == null) {
+
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		this.wasNullFlag = false;
+
+		return tsVal;
+	}
+
+	Timestamp getNativeTimestampViaParseConversion(int columnIndex, Calendar targetCalendar, 
+			TimeZone tz, boolean rollForward) throws SQLException {
+		if (this.useUsageAdvisor) {
+			issueConversionViaParsingWarning("getTimestamp()", columnIndex,
+					this.thisRow.getColumnValue(columnIndex - 1), this.fields[columnIndex - 1],
+					new int[] { MysqlDefs.FIELD_TYPE_TIMESTAMP,
+							MysqlDefs.FIELD_TYPE_DATETIME });
+		}
+
+		String strTimestamp = getNativeString(columnIndex);
+
+		return getTimestampFromString(columnIndex, targetCalendar, strTimestamp, tz,
+				rollForward);
+	}
+
+	// ---------------------------------------------------------------------
+	// Updates
+	// ---------------------------------------------------------------------
+
+	/**
+	 * A column value can also be retrieved as a stream of Unicode characters.
+	 * We implement this as a binary stream.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return a Java InputStream that delivers the database column value as a
+	 *         stream of two byte Unicode characters. If the value is SQL NULL,
+	 *         then the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see getAsciiStream
+	 * @see getBinaryStream
+	 */
+	protected InputStream getNativeUnicodeStream(int columnIndex)
+			throws SQLException {
+		checkRowPos();
+
+		return getBinaryStream(columnIndex);
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#getURL(int)
+	 */
+	protected URL getNativeURL(int colIndex) throws SQLException {
+		String val = getString(colIndex);
+
+		if (val == null) {
+			return null;
+		}
+
+		try {
+			return new URL(val);
+		} catch (MalformedURLException mfe) {
+			throw SQLError.createSQLException(Messages
+					.getString("ResultSet.Malformed_URL____141")
+					+ val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the nextResultSet, if any, null if none exists.
+	 */
+	public ResultSetInternalMethods getNextResultSet() {
+		return this.nextResultSet;
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java object
+	 * 
+	 * <p>
+	 * This method will return the value of the given column as a Java object.
+	 * The type of the Java object will be the default Java Object type
+	 * corresponding to the column's SQL type, following the mapping specified
+	 * in the JDBC specification.
+	 * </p>
+	 * 
+	 * <p>
+	 * This method may also be used to read database specific abstract data
+	 * types.
+	 * </p>
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return a Object holding the column value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public Object getObject(int columnIndex) throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+		
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		if (this.thisRow.isNull(columnIndexMinusOne)) {
+			this.wasNullFlag = true;
+
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+
+		Field field;
+		field = this.fields[columnIndexMinusOne];
+
+		switch (field.getSQLType()) {
+		case Types.BIT:
+		case Types.BOOLEAN:
+			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT
+					&& !field.isSingleBit()) {
+				return getBytes(columnIndex);
+			}
+
+			// valueOf would be nicer here, but it isn't
+			// present in JDK-1.3.1, which is what the CTS
+			// uses.
+			return Boolean.valueOf(getBoolean(columnIndex));
+
+		case Types.TINYINT:
+			if (!field.isUnsigned()) {
+				return Constants.integerValueOf(getByte(columnIndex));
+			}
+
+			return Constants.integerValueOf(getInt(columnIndex));
+
+		case Types.SMALLINT:
+
+			return Constants.integerValueOf(getInt(columnIndex));
+
+		case Types.INTEGER:
+
+			if (!field.isUnsigned() || 
+					field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
+				return Constants.integerValueOf(getInt(columnIndex));
+			}
+
+			return Constants.longValueOf(getLong(columnIndex));
+			
+		case Types.BIGINT:
+
+			if (!field.isUnsigned()) {
+				return Constants.longValueOf(getLong(columnIndex));
+			}
+
+			String stringVal = getString(columnIndex);
+
+			if (stringVal == null) {
+				return null;
+			}
+
+			try {
+				return new BigInteger(stringVal);
+			} catch (NumberFormatException nfe) {
+				throw SQLError.createSQLException(Messages.getString(
+						"ResultSet.Bad_format_for_BigInteger", new Object[] {
+								Constants.integerValueOf(columnIndex), stringVal }),
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+
+		case Types.DECIMAL:
+		case Types.NUMERIC:
+			stringVal = getString(columnIndex);
+
+			BigDecimal val;
+
+			if (stringVal != null) {
+				if (stringVal.length() == 0) {
+					val = new BigDecimal(0);
+
+					return val;
+				}
+
+				try {
+					val = new BigDecimal(stringVal);
+				} catch (NumberFormatException ex) {
+					throw SQLError.createSQLException(
+							Messages
+									.getString("ResultSet.Bad_format_for_BigDecimal____86") //$NON-NLS-1$
+									+ stringVal
+									+ Messages
+											.getString("ResultSet.___in_column__87")
+									+ columnIndex + "(" //$NON-NLS-1$
+									+ this.fields[columnIndex - 1] + ").",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				return val;
+			}
+
+			return null;
+
+		case Types.REAL:
+			return new Float(getFloat(columnIndex));
+
+		case Types.FLOAT:
+		case Types.DOUBLE:
+			return new Double(getDouble(columnIndex));
+
+		case Types.CHAR:
+		case Types.VARCHAR:
+			if (!field.isOpaqueBinary()) {
+				return getString(columnIndex);
+			}
+
+			return getBytes(columnIndex);
+		case Types.LONGVARCHAR:
+			if (!field.isOpaqueBinary()) {
+				return getStringForClob(columnIndex);
+			}
+
+			return getBytes(columnIndex);
+
+		case Types.BINARY:
+		case Types.VARBINARY:
+		case Types.LONGVARBINARY:
+			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_GEOMETRY) {
+				return getBytes(columnIndex);
+			} else if (field.isBinary() || field.isBlob()) {
+				byte[] data = getBytes(columnIndex);
+
+				if (this.connection.getAutoDeserialize()) {
+					Object obj = data;
+
+					if ((data != null) && (data.length >= 2)) {
+						if ((data[0] == -84) && (data[1] == -19)) {
+							// Serialized object?
+							try {
+								ByteArrayInputStream bytesIn = new ByteArrayInputStream(
+										data);
+								ObjectInputStream objIn = new ObjectInputStream(
+										bytesIn);
+								obj = objIn.readObject();
+								objIn.close();
+								bytesIn.close();
+							} catch (ClassNotFoundException cnfe) {
+								throw SQLError.createSQLException(
+										Messages
+												.getString("ResultSet.Class_not_found___91") //$NON-NLS-1$
+												+ cnfe.toString()
+												+ Messages
+														.getString("ResultSet._while_reading_serialized_object_92")); //$NON-NLS-1$
+							} catch (IOException ex) {
+								obj = data; // not serialized?
+							}
+						} else {
+							return getString(columnIndex);
+						}
+					}
+
+					return obj;
+				}
+
+				return data;
+			}
+			
+			return getBytes(columnIndex); 
+
+		case Types.DATE:
+			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR
+					&& !this.connection.getYearIsDateType()) {
+				return Constants.shortValueOf(getShort(columnIndex));
+			}
+
+			return getDate(columnIndex);
+
+		case Types.TIME:
+			return getTime(columnIndex);
+
+		case Types.TIMESTAMP:
+			return getTimestamp(columnIndex);
+
+		default:
+			return getString(columnIndex);
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Returns the value of column i as a Java object. Use the map to
+	 * determine the class from which to construct data of SQL structured and
+	 * distinct types.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * @param map
+	 *            the mapping from SQL type names to Java classes
+	 * 
+	 * @return an object representing the SQL value
+	 * 
+	 * @throws SQLException
+	 *             because this is not implemented
+	 */
+	public Object getObject(int i, java.util.Map map) throws SQLException {
+		return getObject(i);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java object
+	 * 
+	 * <p>
+	 * This method will return the value of the given column as a Java object.
+	 * The type of the Java object will be the default Java Object type
+	 * corresponding to the column's SQL type, following the mapping specified
+	 * in the JDBC specification.
+	 * </p>
+	 * 
+	 * <p>
+	 * This method may also be used to read database specific abstract data
+	 * types.
+	 * </p>
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * 
+	 * @return a Object holding the column value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public Object getObject(String columnName) throws SQLException {
+		return getObject(findColumn(columnName));
+	}
+
+	/**
+	 * JDBC 2.0 Returns the value of column i as a Java object. Use the map to
+	 * determine the class from which to construct data of SQL structured and
+	 * distinct types.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * @param map
+	 *            the mapping from SQL type names to Java classes
+	 * 
+	 * @return an object representing the SQL value
+	 * 
+	 * @throws SQLException
+	 *             as this is not implemented
+	 */
+	public Object getObject(String colName, java.util.Map map)
+			throws SQLException {
+		return getObject(findColumn(colName), map);
+	}
+
+	public Object getObjectStoredProc(int columnIndex, int desiredSqlType)
+			throws SQLException {
+		checkRowPos();
+		checkColumnBounds(columnIndex);
+
+		Object value = this.thisRow.getColumnValue(columnIndex - 1);
+		
+		if (value == null) {
+			this.wasNullFlag = true;
+
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+
+		Field field;
+		field = this.fields[columnIndex - 1];
+
+		switch (desiredSqlType) {
+		case Types.BIT:
+		case Types.BOOLEAN:
+			// valueOf would be nicer here, but it isn't
+			// present in JDK-1.3.1, which is what the CTS
+			// uses.
+			return Boolean.valueOf(getBoolean(columnIndex));
+
+		case Types.TINYINT:
+			return Constants.integerValueOf(getInt(columnIndex));
+
+		case Types.SMALLINT:
+			return Constants.integerValueOf(getInt(columnIndex));
+
+		case Types.INTEGER:
+
+			if (!field.isUnsigned() || 
+					field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
+				return Constants.integerValueOf(getInt(columnIndex));
+			}
+
+			return Constants.longValueOf(getLong(columnIndex));
+
+		case Types.BIGINT:
+
+			if (field.isUnsigned()) {
+				return getBigDecimal(columnIndex);
+			}
+
+			return Constants.longValueOf(getLong(columnIndex));
+
+		case Types.DECIMAL:
+		case Types.NUMERIC:
+
+			String stringVal = getString(columnIndex);
+			BigDecimal val;
+
+			if (stringVal != null) {
+				if (stringVal.length() == 0) {
+					val = new BigDecimal(0);
+
+					return val;
+				}
+
+				try {
+					val = new BigDecimal(stringVal);
+				} catch (NumberFormatException ex) {
+					throw SQLError.createSQLException(
+							Messages
+									.getString("ResultSet.Bad_format_for_BigDecimal____86") //$NON-NLS-1$
+									+ stringVal
+									+ Messages
+											.getString("ResultSet.___in_column__87")
+									+ columnIndex + "(" //$NON-NLS-1$
+									+ this.fields[columnIndex - 1] + ").",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				return val;
+			}
+
+			return null;
+
+		case Types.REAL:
+			return new Float(getFloat(columnIndex));
+
+		case Types.FLOAT:
+
+			if (!this.connection.getRunningCTS13()) {
+				return new Double(getFloat(columnIndex));
+			} else {
+				return new Float(getFloat(columnIndex)); // NB - bug in JDBC
+															// compliance test,
+															// according
+				// to JDBC spec, FLOAT type should return DOUBLE
+				// but causes ClassCastException in CTS :(
+			}
+		case Types.DOUBLE:
+			return new Double(getDouble(columnIndex));
+
+		case Types.CHAR:
+		case Types.VARCHAR:
+			return getString(columnIndex);
+		case Types.LONGVARCHAR:
+			return getStringForClob(columnIndex);
+		case Types.BINARY:
+		case Types.VARBINARY:
+		case Types.LONGVARBINARY:
+			return getBytes(columnIndex);
+
+		case Types.DATE:
+			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR
+					&& !this.connection.getYearIsDateType()) {
+				return Constants.shortValueOf(getShort(columnIndex));
+			}
+
+			return getDate(columnIndex);
+
+		case Types.TIME:
+			return getTime(columnIndex);
+
+		case Types.TIMESTAMP:
+			return getTimestamp(columnIndex);
+
+		default:
+			return getString(columnIndex);
+		}
+	}
+
+	public Object getObjectStoredProc(int i, java.util.Map map,
+			int desiredSqlType) throws SQLException {
+		return getObjectStoredProc(i, desiredSqlType);
+	}
+
+	public Object getObjectStoredProc(String columnName, int desiredSqlType)
+			throws SQLException {
+		return getObjectStoredProc(findColumn(columnName), desiredSqlType);
+	}
+
+	public Object getObjectStoredProc(String colName, java.util.Map map,
+			int desiredSqlType) throws SQLException {
+		return getObjectStoredProc(findColumn(colName), map, desiredSqlType);
+	}
+
+	/**
+	 * JDBC 2.0 Get a REF(&lt;structured-type&gt;) column.
+	 * 
+	 * @param i
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @return an object representing data of an SQL REF type
+	 * 
+	 * @throws SQLException
+	 *             as this is not implemented
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.Ref getRef(int i) throws SQLException {
+		checkColumnBounds(i);
+		throw new NotImplemented();
+	}
+
+	/**
+	 * JDBC 2.0 Get a REF(&lt;structured-type&gt;) column.
+	 * 
+	 * @param colName
+	 *            the column name
+	 * 
+	 * @return an object representing data of an SQL REF type
+	 * 
+	 * @throws SQLException
+	 *             as this method is not implemented.
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.Ref getRef(String colName) throws SQLException {
+		return getRef(findColumn(colName));
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Determine the current row number. The first row is number 1, the second
+	 * number 2, etc.
+	 * </p>
+	 * 
+	 * @return the current row number, else return 0 if there is no current row
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public int getRow() throws SQLException {
+		checkClosed();
+
+		int currentRowNumber = this.rowData.getCurrentRowNumber();
+		int row = 0;
+
+		// Non-dynamic result sets can be interrogated
+		// for this information
+		if (!this.rowData.isDynamic()) {
+			if ((currentRowNumber < 0) || this.rowData.isAfterLast()
+					|| this.rowData.isEmpty()) {
+				row = 0;
+			} else {
+				row = currentRowNumber + 1;
+			}
+		} else {
+			// dynamic (streaming) can not
+			row = currentRowNumber + 1;
+		}
+
+		return row;
+	}
+
+	/**
+	 * Returns the server info (if any), or null if none.
+	 * 
+	 * @return server info created for this ResultSet
+	 */
+	public String getServerInfo() {
+		return this.serverInfo;
+	}
+
+	private long getNumericRepresentationOfSQLBitType(int columnIndex) throws SQLException {
+		
+		Object value = this.thisRow.getColumnValue(columnIndex - 1);
+		
+		if (this.fields[columnIndex - 1].isSingleBit() || 
+				((byte[])value).length == 1) {
+			return ((byte[])value)[0];
+		}
+		
+		
+		byte[] asBytes = (byte[])value;
+		
+		
+		int shift = 0;
+		
+		long[] steps = new long[asBytes.length];
+		
+		for (int i = asBytes.length - 1; i >= 0; i--) {
+			steps[i] = (long)(asBytes[i] & 0xff) << shift;
+			shift += 8;
+		}
+		
+		long valueAsLong = 0;
+		
+		for (int i = 0; i < asBytes.length; i++) {
+			valueAsLong |= steps[i];
+		}
+		
+		return valueAsLong;
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java short.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2,...
+	 * 
+	 * @return the column value; 0 if SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public short getShort(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkRowPos();
+			
+			if (this.useFastIntParsing) {
+				
+				checkColumnBounds(columnIndex);
+
+				Object value = this.thisRow.getColumnValue(columnIndex - 1);
+				
+				if (value == null) {
+					this.wasNullFlag = true;
+				} else {
+					this.wasNullFlag = false;
+				}
+				
+				if (this.wasNullFlag) {
+					return 0;
+				}
+
+				byte[] shortAsBytes = (byte[]) value;
+
+				if (shortAsBytes.length == 0) {
+					return (short) convertToZeroWithEmptyCheck();
+				}
+
+				boolean needsFullParse = false;
+
+				for (int i = 0; i < shortAsBytes.length; i++) {
+					if (((char) shortAsBytes[i] == 'e')
+							|| ((char) shortAsBytes[i] == 'E')) {
+						needsFullParse = true;
+
+						break;
+					}
+				}
+
+				if (!needsFullParse) {
+					try {
+						return parseShortWithOverflowCheck(columnIndex,
+								shortAsBytes, null);
+					} catch (NumberFormatException nfe) {
+						try {
+							// To do: Warn of over/underflow???
+							return parseShortAsDouble(columnIndex, new String(
+									shortAsBytes));
+						} catch (NumberFormatException newNfe) {
+							; // ignore, it's not a number
+						}
+
+						if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+							long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
+							
+							if (this.jdbcCompliantTruncationForReads &&
+									(valueAsLong < Short.MIN_VALUE
+											|| valueAsLong > Short.MAX_VALUE)) {
+								throwRangeException(String.valueOf(valueAsLong), columnIndex,
+										Types.SMALLINT);
+							}
+							
+							return (short)valueAsLong;
+						}
+						
+						throw SQLError.createSQLException(
+								Messages
+										.getString("ResultSet.Invalid_value_for_getShort()_-____96")
+										+ new String(shortAsBytes) //$NON-NLS-1$
+										+ "'",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+			}
+
+			String val = null;
+
+			try {
+				val = getString(columnIndex);
+
+				if ((val != null)) {
+
+					if (val.length() == 0) {
+						return (short) convertToZeroWithEmptyCheck();
+					}
+
+					if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
+							&& (val.indexOf(".") == -1)) {
+						return parseShortWithOverflowCheck(columnIndex, null,
+								val);
+					}
+
+					// Convert floating point
+					return parseShortAsDouble(columnIndex, val);
+				}
+
+				return 0; // for NULL
+			} catch (NumberFormatException nfe) {
+				try {
+					return parseShortAsDouble(columnIndex, val);
+				} catch (NumberFormatException newNfe) {
+					; // ignore, it's not a number
+				}
+
+				if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+					long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex);
+					
+					if (this.jdbcCompliantTruncationForReads &&
+							(valueAsLong < Short.MIN_VALUE
+									|| valueAsLong > Short.MAX_VALUE)) {
+						throwRangeException(String.valueOf(valueAsLong), columnIndex,
+								Types.SMALLINT);
+					}
+					
+					return (short)valueAsLong;
+				}
+				
+				throw SQLError.createSQLException(
+						Messages
+								.getString("ResultSet.Invalid_value_for_getShort()_-____96")
+								+ val //$NON-NLS-1$
+								+ "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		return getNativeShort(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public short getShort(String columnName) throws SQLException {
+		return getShort(findColumn(columnName));
+	}
+
+	private final short getShortFromString(String val, int columnIndex)
+			throws SQLException {
+		try {
+			if ((val != null)) {
+
+				if (val.length() == 0) {
+					return (short) convertToZeroWithEmptyCheck();
+				}
+
+				if ((val.indexOf("e") == -1) && (val.indexOf("E") == -1)
+						&& (val.indexOf(".") == -1)) {
+					return parseShortWithOverflowCheck(columnIndex, null, val);
+				}
+
+				// Convert floating point
+				return parseShortAsDouble(columnIndex, val);
+			}
+
+			return 0; // for NULL
+		} catch (NumberFormatException nfe) {
+			try {
+				return parseShortAsDouble(columnIndex, val);
+			} catch (NumberFormatException newNfe) {
+				; // ignore, it's not a number
+			}
+
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Invalid_value_for_getShort()_-____217")
+							+ val //$NON-NLS-1$
+							+ Messages.getString("ResultSet.___in_column__218")
+							+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Return the Statement that produced the ResultSet.
+	 * 
+	 * @return the Statment that produced the result set, or null if the result
+	 *         was produced some other way.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public java.sql.Statement getStatement() throws SQLException {
+		if (this.isClosed && !this.retainOwningStatement) {
+			throw SQLError.createSQLException(
+					"Operation not allowed on closed ResultSet. Statements "
+							+ "can be retained over result set closure by setting the connection property "
+							+ "\"retainStatementAfterResultSetClose\" to \"true\".",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+
+		}
+		
+		if (this.wrapperStatement != null) {
+			return this.wrapperStatement;
+		}
+
+		return this.owningStatement;
+	}
+
+	/**
+	 * Get the value of a column in the current row as a Java String
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value, null for SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getString(int columnIndex) throws SQLException {
+		String stringVal = getStringInternal(columnIndex, true);
+		
+		if (this.padCharsWithSpace) {
+			Field f = this.fields[columnIndex - 1];
+			
+			if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING ) {
+				int fieldLength = (int)f.getLength() /* safe, bytes in a CHAR <= 1024 */ / 
+					f.getMaxBytesPerCharacter(); /* safe, this will never be 0 */
+				
+				int currentLength = stringVal.length();
+				
+				if (currentLength < fieldLength) {
+					StringBuffer paddedBuf = new StringBuffer(fieldLength);
+					paddedBuf.append(stringVal);
+					
+					int difference = fieldLength - currentLength;
+					
+					paddedBuf.append(EMPTY_SPACE, 0, difference);
+					
+					stringVal = paddedBuf.toString();
+				}
+			}
+		}
+		
+		return stringVal;
+	}
+
+	/**
+	 * The following routines simply convert the columnName into a columnIndex
+	 * and then call the appropriate routine above.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * 
+	 * @return the column value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public String getString(String columnName) throws SQLException {
+		return getString(findColumn(columnName));
+	}
+
+	private String getStringForClob(int columnIndex) throws SQLException {
+		String asString = null;
+		
+		String forcedEncoding = 
+			this.connection.getClobCharacterEncoding();
+		
+		if (forcedEncoding == null) {
+			if (!this.isBinaryEncoded) {
+				asString = getString(columnIndex);
+			} else {
+				asString = getNativeString(columnIndex);
+			}
+		} else {
+			try {
+				byte[] asBytes = null;
+				
+				if (!this.isBinaryEncoded) {
+					asBytes = getBytes(columnIndex);
+				} else {
+					asBytes = getNativeBytes(columnIndex, true);
+				}
+				
+				if (asBytes != null) {
+					asString = new String(asBytes, forcedEncoding);
+				}
+			} catch (UnsupportedEncodingException uee) {
+				throw SQLError.createSQLException("Unsupported character encoding " + 
+						forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+		
+		return asString;
+	}
+
+	protected String getStringInternal(int columnIndex, boolean checkDateTypes)
+			throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkRowPos();
+			checkColumnBounds(columnIndex);
+
+			if (this.fields == null) {
+				throw SQLError.createSQLException(
+						Messages
+								.getString("ResultSet.Query_generated_no_fields_for_ResultSet_99"), //$NON-NLS-1$
+						SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
+			}
+
+			// JDBC is 1-based, Java is not !?
+			
+			int internalColumnIndex = columnIndex - 1;
+			
+			if (this.thisRow.isNull(internalColumnIndex)) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+
+			this.wasNullFlag = false;
+
+			
+			Field metadata = this.fields[internalColumnIndex];
+			
+			String stringVal = null;
+			
+			if (metadata.getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+				if (metadata.isSingleBit()) {
+					byte[] value = this.thisRow.getColumnValue(internalColumnIndex);
+					
+					if (value.length == 0) {
+						return String.valueOf(convertToZeroWithEmptyCheck());
+					}
+					
+					return String.valueOf(value[0]);
+				}
+				
+				return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex));
+			}
+			
+			String encoding = metadata.getCharacterSet();
+
+			stringVal = this.thisRow.getString(internalColumnIndex, encoding, this.connection);
+
+			//
+			// Special handling for YEAR type from mysql, some people
+			// want it as a DATE, others want to treat it as a SHORT
+			//
+
+			if (metadata.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+				if (!this.connection.getYearIsDateType()) {
+					return stringVal;
+				}
+
+				Date dt = getDateFromString(stringVal, columnIndex);
+
+				if (dt == null) {
+					this.wasNullFlag = true;
+
+					return null;
+				}
+
+				this.wasNullFlag = false;
+
+				return dt.toString();
+			}
+
+			// Handles timezone conversion and zero-date behavior
+
+			if (checkDateTypes && !this.connection.getNoDatetimeStringSync()) {
+				switch (metadata.getSQLType()) {
+				case Types.TIME:
+					Time tm = getTimeFromString(stringVal, null, columnIndex,
+							this.getDefaultTimeZone(), false);
+
+					if (tm == null) {
+						this.wasNullFlag = true;
+
+						return null;
+					}
+
+					this.wasNullFlag = false;
+
+					return tm.toString();
+				case Types.DATE:
+
+					Date dt = getDateFromString(stringVal, columnIndex);
+
+					if (dt == null) {
+						this.wasNullFlag = true;
+
+						return null;
+					}
+
+					this.wasNullFlag = false;
+
+					return dt.toString();
+				case Types.TIMESTAMP:
+					Timestamp ts = getTimestampFromString(columnIndex,
+							null, stringVal, this.getDefaultTimeZone(), false);
+
+					if (ts == null) {
+						this.wasNullFlag = true;
+
+						return null;
+					}
+
+					this.wasNullFlag = false;
+
+					return ts.toString();
+				default:
+					break;
+				}
+			}
+
+			return stringVal;
+		}
+
+		return getNativeString(columnIndex);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Time object
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value; null if SQL NULL
+	 * 
+	 * @throws java.sql.SQLException
+	 *             if a database access error occurs
+	 */
+	public Time getTime(int columnIndex) throws java.sql.SQLException {
+		return getTimeInternal(columnIndex, null, this.getDefaultTimeZone(), false);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Time object.
+	 * Use the calendar to construct an appropriate millisecond value for the
+	 * Time, if the underlying database doesn't store timezone information.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param cal
+	 *            the calendar to use in constructing the time
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Time getTime(int columnIndex, Calendar cal)
+			throws SQLException {
+		return getTimeInternal(columnIndex, cal, cal.getTimeZone(), true);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Time object.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @throws java.sql.SQLException
+	 *             if a database-access error occurs.
+	 */
+	public Time getTime(String columnName) throws java.sql.SQLException {
+		return getTime(findColumn(columnName));
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Time object.
+	 * Use the calendar to construct an appropriate millisecond value for the
+	 * Time, if the underlying database doesn't store timezone information.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * @param cal
+	 *            the calendar to use in constructing the time
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Time getTime(String columnName, Calendar cal)
+			throws SQLException {
+		return getTime(findColumn(columnName), cal);
+	}
+
+	private Time getTimeFromString(String timeAsString, Calendar targetCalendar,
+			int columnIndex,
+			TimeZone tz, 
+			boolean rollForward) throws SQLException {
+		int hr = 0;
+		int min = 0;
+		int sec = 0;
+
+		try {
+			
+			if (timeAsString == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			} 
+			
+			//
+			// JDK-6 doesn't like trailing whitespace
+			//
+			// Note this isn't a performance issue, other
+			// than the iteration over the string, as String.trim()
+			// will return a new string only if whitespace is present
+			//
+			
+			timeAsString = timeAsString.trim();
+			
+			if (timeAsString.equals("0")
+					|| timeAsString.equals("0000-00-00")
+					|| timeAsString.equals("0000-00-00 00:00:00")
+					|| timeAsString.equals("00000000000000")) {
+				if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					this.wasNullFlag = true;
+
+					return null;
+				} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					throw SQLError.createSQLException("Value '" + timeAsString
+							+ "' can not be represented as java.sql.Time",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				// We're left with the case of 'round' to a time Java _can_
+				// represent, which is '00:00:00'
+				return fastTimeCreate(null, 0, 0, 0);
+			}
+
+			this.wasNullFlag = false;
+
+			Field timeColField = this.fields[columnIndex - 1];
+
+			if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
+				// It's a timestamp
+				int length = timeAsString.length();
+
+				switch (length) {
+				case 19: { // YYYY-MM-DD hh:mm:ss
+				 
+						hr = Integer.parseInt(timeAsString.substring(length - 8,
+								length - 6));
+						min = Integer.parseInt(timeAsString.substring(length - 5,
+								length - 3));
+						sec = Integer.parseInt(timeAsString.substring(length - 2,
+								length));
+				}
+
+						break;
+				case 14:
+				case 12: {
+					hr = Integer.parseInt(timeAsString.substring(length - 6,
+							length - 4));
+					min = Integer.parseInt(timeAsString.substring(length - 4,
+							length - 2));
+					sec = Integer.parseInt(timeAsString.substring(length - 2,
+							length));
+				}
+
+					break;
+
+				case 10: {
+					hr = Integer.parseInt(timeAsString.substring(6, 8));
+					min = Integer.parseInt(timeAsString.substring(8, 10));
+					sec = 0;
+				}
+
+					break;
+
+				default:
+					throw SQLError.createSQLException(
+							Messages
+									.getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") //$NON-NLS-1$
+									+ columnIndex
+									+ "("
+									+ this.fields[columnIndex - 1] + ").",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				} /* endswitch */
+
+				SQLWarning precisionLost = new SQLWarning(
+						Messages
+								.getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") //$NON-NLS-1$
+								+ columnIndex
+								+ "("
+								+ this.fields[columnIndex - 1] + ").");
+
+				if (this.warningChain == null) {
+					this.warningChain = precisionLost;
+				} else {
+					this.warningChain.setNextWarning(precisionLost);
+				}
+			} else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) {
+				hr = Integer.parseInt(timeAsString.substring(11, 13));
+				min = Integer.parseInt(timeAsString.substring(14, 16));
+				sec = Integer.parseInt(timeAsString.substring(17, 19));
+
+				SQLWarning precisionLost = new SQLWarning(
+						Messages
+								.getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") //$NON-NLS-1$
+								+ columnIndex
+								+ "("
+								+ this.fields[columnIndex - 1] + ").");
+
+				if (this.warningChain == null) {
+					this.warningChain = precisionLost;
+				} else {
+					this.warningChain.setNextWarning(precisionLost);
+				}
+			} else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) {
+				return fastTimeCreate(null, 0, 0, 0); // midnight on the given
+														// date
+			} else {
+				// convert a String to a Time
+				if ((timeAsString.length() != 5)
+						&& (timeAsString.length() != 8)) {
+					throw SQLError.createSQLException(Messages
+							.getString("ResultSet.Bad_format_for_Time____267") //$NON-NLS-1$
+							+ timeAsString
+							+ Messages.getString("ResultSet.___in_column__268")
+							+ columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				hr = Integer.parseInt(timeAsString.substring(0, 2));
+				min = Integer.parseInt(timeAsString.substring(3, 5));
+				sec = (timeAsString.length() == 5) ? 0 : Integer
+						.parseInt(timeAsString.substring(6));
+			}
+
+			Calendar sessionCalendar = this.getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				return TimeUtil.changeTimezone(this.connection, 
+						sessionCalendar,
+						targetCalendar, 
+						fastTimeCreate(
+						sessionCalendar, hr, min, sec), 
+						this.connection.getServerTimezoneTZ(),
+						tz, rollForward);
+			}
+		} catch (Exception ex) {
+			throw SQLError.createSQLException(ex.toString(),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+	
+	/**
+	 * Get the value of a column in the current row as a java.sql.Time object in
+	 * the given timezone
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * @param tz
+	 *            the Timezone to use
+	 * 
+	 * @return the column value; null if SQL NULL
+	 * 
+	 * @exception java.sql.SQLException
+	 *                if a database access error occurs
+	 */
+	private Time getTimeInternal(int columnIndex, Calendar targetCalendar,
+			TimeZone tz,
+			boolean rollForward) throws java.sql.SQLException {
+		if (this.isBinaryEncoded) {
+			return getNativeTime(columnIndex, targetCalendar, tz, rollForward);
+		}
+
+		if (!this.useFastDateParsing) {
+			String timeAsString = getStringInternal(columnIndex, false);
+
+			return getTimeFromString(timeAsString, targetCalendar,
+				columnIndex, tz, rollForward);
+		}
+		
+		checkColumnBounds(columnIndex);
+		
+		int columnIndexMinusOne = columnIndex - 1;
+		
+		if (this.thisRow.isNull(columnIndexMinusOne)) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+		
+		return this.thisRow.getTimeFast(columnIndexMinusOne, 
+				targetCalendar, tz, rollForward, this.connection, this);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Timestamp
+	 * object
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the column value; null if SQL NULL
+	 * 
+	 * @exception java.sql.SQLException
+	 *                if a database access error occurs
+	 */
+	public Timestamp getTimestamp(int columnIndex) throws java.sql.SQLException {
+		checkColumnBounds(columnIndex);
+		
+		return getTimestampInternal(columnIndex, null, this.getDefaultTimeZone(),
+				false);
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Timestamp
+	 * object. Use the calendar to construct an appropriate millisecond value
+	 * for the Timestamp, if the underlying database doesn't store timezone
+	 * information.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param cal
+	 *            the calendar to use in constructing the timestamp
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal)
+			throws SQLException {
+		return getTimestampInternal(columnIndex, cal, cal.getTimeZone(), true);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws java.sql.SQLException
+	 *             DOCUMENT ME!
+	 */
+	public Timestamp getTimestamp(String columnName)
+			throws java.sql.SQLException {
+		return getTimestamp(findColumn(columnName));
+	}
+
+	/**
+	 * Get the value of a column in the current row as a java.sql.Timestamp
+	 * object. Use the calendar to construct an appropriate millisecond value
+	 * for the Timestamp, if the underlying database doesn't store timezone
+	 * information.
+	 * 
+	 * @param columnName
+	 *            is the SQL name of the column
+	 * @param cal
+	 *            the calendar to use in constructing the timestamp
+	 * 
+	 * @return the column value; if the value is SQL NULL, the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.Timestamp getTimestamp(String columnName, Calendar cal)
+			throws SQLException {
+		return getTimestamp(findColumn(columnName), cal);
+	}
+
+	private Timestamp getTimestampFromString(int columnIndex,
+			Calendar targetCalendar,
+			String timestampValue, TimeZone tz, boolean rollForward)
+	throws java.sql.SQLException {
+		try {
+			this.wasNullFlag = false;
+			
+			if (timestampValue == null) {
+				this.wasNullFlag = true;
+				
+				return null;
+			}
+			
+			//
+			// JDK-6 doesn't like trailing whitespace
+			//
+			// Note this isn't a performance issue, other
+			// than the iteration over the string, as String.trim()
+			// will return a new string only if whitespace is present
+			//
+			
+			timestampValue = timestampValue.trim();
+			
+			int length = timestampValue.length();
+			
+			Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
+					this.connection.getUtcCalendar() : 
+						getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				if ((length > 0)
+						&& (timestampValue.charAt(0) == '0')
+						&& (timestampValue.equals("0000-00-00")
+								|| timestampValue.equals("0000-00-00 00:00:00")
+								|| timestampValue.equals("00000000000000") || timestampValue
+								.equals("0"))) {
+					
+					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						this.wasNullFlag = true;
+						
+						return null;
+					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						throw SQLError.createSQLException("Value '" + timestampValue
+								+ "' can not be represented as java.sql.Timestamp",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+					
+					// We're left with the case of 'round' to a date Java _can_
+					// represent, which is '0001-01-01'.
+					return fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0);
+					
+				} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+					
+					return TimeUtil.changeTimezone(this.connection,
+							sessionCalendar, 
+							targetCalendar,
+							fastTimestampCreate(sessionCalendar, 
+									Integer
+									.parseInt(timestampValue.substring(0, 4)), 1,
+									1, 0, 0, 0, 0), this.connection
+									.getServerTimezoneTZ(), tz, rollForward);
+					
+				} else {
+					if (timestampValue.endsWith(".")) {
+						timestampValue = timestampValue.substring(0, timestampValue
+								.length() - 1);
+					}
+					
+					// Convert from TIMESTAMP or DATE
+					switch (length) {
+					case 26:
+					case 25:
+					case 24:
+					case 23:
+					case 22:
+					case 21:
+					case 20:
+					case 19: {
+						int year = Integer.parseInt(timestampValue.substring(0, 4));
+						int month = Integer
+						.parseInt(timestampValue.substring(5, 7));
+						int day = Integer.parseInt(timestampValue.substring(8, 10));
+						int hour = Integer.parseInt(timestampValue
+								.substring(11, 13));
+						int minutes = Integer.parseInt(timestampValue.substring(14,
+								16));
+						int seconds = Integer.parseInt(timestampValue.substring(17,
+								19));
+						
+						int nanos = 0;
+						
+						if (length > 19) {
+							int decimalIndex = timestampValue.lastIndexOf('.');
+							
+							if (decimalIndex != -1) {
+								if ((decimalIndex + 2) <= timestampValue.length()) {
+									nanos = Integer.parseInt(timestampValue
+											.substring(decimalIndex + 1));
+								} else {
+									throw new IllegalArgumentException(); // re-thrown
+									// further
+									// down
+									// with
+									// a
+									// much better error message
+								}
+							}
+						}
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year, month, day, hour,
+										minutes, seconds, nanos), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 14: {
+						int year = Integer.parseInt(timestampValue.substring(0, 4));
+						int month = Integer
+						.parseInt(timestampValue.substring(4, 6));
+						int day = Integer.parseInt(timestampValue.substring(6, 8));
+						int hour = Integer
+						.parseInt(timestampValue.substring(8, 10));
+						int minutes = Integer.parseInt(timestampValue.substring(10,
+								12));
+						int seconds = Integer.parseInt(timestampValue.substring(12,
+								14));
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar, 
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year, month, day, hour,
+										minutes, seconds, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 12: {
+						int year = Integer.parseInt(timestampValue.substring(0, 2));
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						int month = Integer
+						.parseInt(timestampValue.substring(2, 4));
+						int day = Integer.parseInt(timestampValue.substring(4, 6));
+						int hour = Integer.parseInt(timestampValue.substring(6, 8));
+						int minutes = Integer.parseInt(timestampValue.substring(8,
+								10));
+						int seconds = Integer.parseInt(timestampValue.substring(10,
+								12));
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year + 1900, month, day,
+										hour, minutes, seconds, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 10: {
+						int year;
+						int month;
+						int day;
+						int hour;
+						int minutes;
+						
+						if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE)
+								|| (timestampValue.indexOf("-") != -1)) {
+							year = Integer.parseInt(timestampValue.substring(0, 4));
+							month = Integer
+							.parseInt(timestampValue.substring(5, 7));
+							day = Integer.parseInt(timestampValue.substring(8, 10));
+							hour = 0;
+							minutes = 0;
+						} else {
+							year = Integer.parseInt(timestampValue.substring(0, 2));
+							
+							if (year <= 69) {
+								year = (year + 100);
+							}
+							
+							month = Integer
+							.parseInt(timestampValue.substring(2, 4));
+							day = Integer.parseInt(timestampValue.substring(4, 6));
+							hour = Integer.parseInt(timestampValue.substring(6, 8));
+							minutes = Integer.parseInt(timestampValue.substring(8,
+									10));
+							
+							year += 1900; // two-digit year
+						}
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year, month, day, hour,
+										minutes, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 8: {
+						if (timestampValue.indexOf(":") != -1) {
+							int hour = Integer.parseInt(timestampValue.substring(0,
+									2));
+							int minutes = Integer.parseInt(timestampValue
+									.substring(3, 5));
+							int seconds = Integer.parseInt(timestampValue
+									.substring(6, 8));
+							
+							return TimeUtil
+							.changeTimezone(this.connection,
+									sessionCalendar,
+									targetCalendar,
+									fastTimestampCreate(sessionCalendar, 1970, 1, 1,
+											hour, minutes, seconds, 0),
+											this.connection.getServerTimezoneTZ(),
+											tz, rollForward);
+							
+						}
+						
+						int year = Integer.parseInt(timestampValue.substring(0, 4));
+						int month = Integer
+						.parseInt(timestampValue.substring(4, 6));
+						int day = Integer.parseInt(timestampValue.substring(6, 8));
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year - 1900, month - 1,
+										day, 0, 0, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 6: {
+						int year = Integer.parseInt(timestampValue.substring(0, 2));
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						int month = Integer
+						.parseInt(timestampValue.substring(2, 4));
+						int day = Integer.parseInt(timestampValue.substring(4, 6));
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year + 1900, month, day,
+										0, 0, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 4: {
+						int year = Integer.parseInt(timestampValue.substring(0, 2));
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						int month = Integer
+						.parseInt(timestampValue.substring(2, 4));
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year + 1900, month, 1, 0,
+										0, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 2: {
+						int year = Integer.parseInt(timestampValue.substring(0, 2));
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(null, year + 1900, 1, 1, 0, 0,
+										0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					default:
+						throw new java.sql.SQLException(
+								"Bad format for Timestamp '" + timestampValue
+								+ "' in column " + columnIndex + ".",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+			}
+		} catch (Exception e) {
+			throw new java.sql.SQLException("Cannot convert value '"
+					+ timestampValue + "' from column " + columnIndex
+					+ " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+	}
+
+	private Timestamp getTimestampFromBytes(int columnIndex,
+			Calendar targetCalendar,
+			byte[] timestampAsBytes, TimeZone tz, boolean rollForward)
+	throws java.sql.SQLException {
+		checkColumnBounds(columnIndex);
+		
+		try {
+			this.wasNullFlag = false;
+			
+			if (timestampAsBytes == null) {
+				this.wasNullFlag = true;
+				
+				return null;
+			}
+
+			int length = timestampAsBytes.length;
+			
+			Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
+					this.connection.getUtcCalendar() : 
+						getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				boolean allZeroTimestamp = true;
+				
+				boolean onlyTimePresent = StringUtils.indexOf(timestampAsBytes, ':') != -1;
+				
+				for (int i = 0; i < length; i++) {
+					byte b = timestampAsBytes[i];
+
+					if (b == ' ' || b == '-' || b == '/') {
+						onlyTimePresent = false;
+					}
+
+					if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/'
+							&& b != '.') {
+						allZeroTimestamp = false;
+
+						break;
+					}
+				}
+
+				if (!onlyTimePresent && allZeroTimestamp) {
+					
+					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						this.wasNullFlag = true;
+						
+						return null;
+					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						throw SQLError.createSQLException("Value '" + timestampAsBytes
+								+ "' can not be represented as java.sql.Timestamp",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+					
+					// We're left with the case of 'round' to a date Java _can_
+					// represent, which is '0001-01-01'.
+					return fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0);
+					
+				} else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+					
+					return TimeUtil.changeTimezone(this.connection,
+							sessionCalendar, 
+							targetCalendar,
+							fastTimestampCreate(sessionCalendar, 
+									StringUtils.getInt(timestampAsBytes, 0, 4), 1,
+									1, 0, 0, 0, 0), this.connection
+									.getServerTimezoneTZ(), tz, rollForward);
+					
+				} else {
+					if (timestampAsBytes[length - 1] == '.') {
+						length--;
+					}
+					
+					// Convert from TIMESTAMP or DATE
+					switch (length) {
+					case 26:
+					case 25:
+					case 24:
+					case 23:
+					case 22:
+					case 21:
+					case 20:
+					case 19: {
+						int year = StringUtils.getInt(timestampAsBytes, 0, 4);
+						int month = StringUtils.getInt(timestampAsBytes, 5, 7);
+						int day = StringUtils.getInt(timestampAsBytes, 8, 10);
+						int hour = StringUtils.getInt(timestampAsBytes, 11, 13);
+						int minutes = StringUtils.getInt(timestampAsBytes, 14, 16);
+						int seconds = StringUtils.getInt(timestampAsBytes, 17, 19);
+						
+						int nanos = 0;
+						
+						if (length > 19) {
+							int decimalIndex = StringUtils.lastIndexOf(timestampAsBytes, '.');
+							
+							if (decimalIndex != -1) {
+								if ((decimalIndex + 2) <= length) {
+									nanos = StringUtils.getInt(timestampAsBytes, decimalIndex + 1, length);
+								} else {
+									throw new IllegalArgumentException(); // re-thrown
+									// further
+									// down
+									// with
+									// a
+									// much better error message
+								}
+							}
+						}
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year, month, day, hour,
+										minutes, seconds, nanos), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 14: {
+						int year = StringUtils.getInt(timestampAsBytes, 0, 4);
+						int month = StringUtils.getInt(timestampAsBytes, 4, 6);
+						int day = StringUtils.getInt(timestampAsBytes, 6, 8);
+						int hour = StringUtils.getInt(timestampAsBytes, 8, 10);
+						int minutes = StringUtils.getInt(timestampAsBytes, 10, 12);
+						int seconds = StringUtils.getInt(timestampAsBytes, 12, 14);
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar, 
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year, month, day, hour,
+										minutes, seconds, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 12: {
+						int year = StringUtils.getInt(timestampAsBytes, 0, 2);
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						int month = StringUtils.getInt(timestampAsBytes, 2, 4);
+						int day = StringUtils.getInt(timestampAsBytes, 4, 6);
+						int hour = StringUtils.getInt(timestampAsBytes, 6, 8);
+						int minutes = StringUtils.getInt(timestampAsBytes, 8,	10);
+						int seconds = StringUtils.getInt(timestampAsBytes, 10, 12);
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year + 1900, month, day,
+										hour, minutes, seconds, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 10: {
+						int year;
+						int month;
+						int day;
+						int hour;
+						int minutes;
+						
+						if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE)
+								|| (StringUtils.indexOf(timestampAsBytes, '-') != -1)) {
+							year = StringUtils.getInt(timestampAsBytes, 0, 4);
+							month = StringUtils.getInt(timestampAsBytes, 5, 7);
+							day = StringUtils.getInt(timestampAsBytes, 8, 10);
+							hour = 0;
+							minutes = 0;
+						} else {
+							year = StringUtils.getInt(timestampAsBytes, 0, 2);
+							
+							if (year <= 69) {
+								year = (year + 100);
+							}
+							
+							month = StringUtils.getInt(timestampAsBytes, 2, 4);
+							day = StringUtils.getInt(timestampAsBytes, 4, 6);
+							hour = StringUtils.getInt(timestampAsBytes, 6, 8);
+							minutes = StringUtils.getInt(timestampAsBytes, 8, 10);
+							
+							year += 1900; // two-digit year
+						}
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year, month, day, hour,
+										minutes, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 8: {
+						if (StringUtils.indexOf(timestampAsBytes, ':') != -1) {
+							int hour = StringUtils.getInt(timestampAsBytes, 0, 2);
+							int minutes = StringUtils.getInt(timestampAsBytes, 3, 5);
+							int seconds = StringUtils.getInt(timestampAsBytes, 6, 8);
+							
+							return TimeUtil
+							.changeTimezone(this.connection,
+									sessionCalendar,
+									targetCalendar,
+									fastTimestampCreate(sessionCalendar, 1970, 1, 1,
+											hour, minutes, seconds, 0),
+											this.connection.getServerTimezoneTZ(),
+											tz, rollForward);
+							
+						}
+						
+						int year = StringUtils.getInt(timestampAsBytes, 0, 4);
+						int month = StringUtils.getInt(timestampAsBytes, 4, 6);
+						int day = StringUtils.getInt(timestampAsBytes, 6, 8);
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year - 1900, month - 1,
+										day, 0, 0, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 6: {
+						int year = StringUtils.getInt(timestampAsBytes, 0, 2);
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						int month = StringUtils.getInt(timestampAsBytes, 2, 4);
+						int day = StringUtils.getInt(timestampAsBytes, 4, 6);
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year + 1900, month, day,
+										0, 0, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 4: {
+						int year = StringUtils.getInt(timestampAsBytes, 0, 2);
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						int month = StringUtils.getInt(timestampAsBytes, 2, 4);
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(sessionCalendar, year + 1900, month, 1, 0,
+										0, 0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					case 2: {
+						int year = StringUtils.getInt(timestampAsBytes, 0, 2);
+						
+						if (year <= 69) {
+							year = (year + 100);
+						}
+						
+						return TimeUtil.changeTimezone(this.connection,
+								sessionCalendar,
+								targetCalendar,
+								fastTimestampCreate(null, year + 1900, 1, 1, 0, 0,
+										0, 0), this.connection
+										.getServerTimezoneTZ(), tz, rollForward);
+					}
+					
+					default:
+						throw new java.sql.SQLException(
+								"Bad format for Timestamp '" + new String(timestampAsBytes)
+								+ "' in column " + columnIndex + ".",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+			}
+		} catch (Exception e) {
+			throw new java.sql.SQLException("Cannot convert value '"
+					+ new String(timestampAsBytes) + "' from column " + columnIndex
+					+ " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}	
+	}
+	
+	/**
+	 * Get the value of a column in the current row as a java.sql.Timestamp
+	 * object in the given timezone
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * @param tz
+	 *            the timezone to use
+	 * 
+	 * @return the column value; null if SQL NULL
+	 * 
+	 * @exception java.sql.SQLException
+	 *                if a database access error occurs
+	 */
+	private Timestamp getTimestampInternal(int columnIndex, Calendar targetCalendar,
+			TimeZone tz,
+			boolean rollForward) throws java.sql.SQLException {
+		if (this.isBinaryEncoded) {
+			return getNativeTimestamp(columnIndex, targetCalendar, tz, rollForward);
+		}
+
+		Timestamp tsVal = null;
+		
+		if (!this.useFastDateParsing) {
+			String timestampValue = getStringInternal(columnIndex, false);
+
+			tsVal = getTimestampFromString(columnIndex, targetCalendar, 
+					timestampValue, tz,
+					rollForward);
+		} else {
+			tsVal = this.thisRow.getTimestampFast(columnIndex - 1, 
+					targetCalendar, tz, rollForward, this.connection, this);
+		}
+		
+		if (tsVal == null) {
+			this.wasNullFlag = true;
+		} else {
+			this.wasNullFlag = false;
+		}
+		
+		return tsVal;
+	}
+
+	/**
+	 * JDBC 2.0 Return the type of this result set. The type is determined based
+	 * on the statement that created the result set.
+	 * 
+	 * @return TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE, or
+	 *         TYPE_SCROLL_SENSITIVE
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public int getType() throws SQLException {
+		return this.resultSetType;
+	}
+
+	/**
+	 * A column value can also be retrieved as a stream of Unicode characters.
+	 * We implement this as a binary stream.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return a Java InputStream that delivers the database column value as a
+	 *         stream of two byte Unicode characters. If the value is SQL NULL,
+	 *         then the result is null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see getAsciiStream
+	 * @see getBinaryStream
+	 * @deprecated
+	 */
+	public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+		if (!this.isBinaryEncoded) {
+			checkRowPos();
+
+			return getBinaryStream(columnIndex);
+		}
+
+		return getNativeBinaryStream(columnIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param columnName
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * 
+	 * @deprecated
+	 */
+	public InputStream getUnicodeStream(String columnName) throws SQLException {
+		return getUnicodeStream(findColumn(columnName));
+	}
+
+	public long getUpdateCount() {
+		return this.updateCount;
+	}
+
+	public long getUpdateID() {
+		return this.updateId;
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#getURL(int)
+	 */
+	public URL getURL(int colIndex) throws SQLException {
+		String val = getString(colIndex);
+
+		if (val == null) {
+			return null;
+		}
+
+		try {
+			return new URL(val);
+		} catch (MalformedURLException mfe) {
+			throw SQLError.createSQLException(Messages
+					.getString("ResultSet.Malformed_URL____104")
+					+ val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#getURL(String)
+	 */
+	public URL getURL(String colName) throws SQLException {
+		String val = getString(colName);
+
+		if (val == null) {
+			return null;
+		}
+
+		try {
+			return new URL(val);
+		} catch (MalformedURLException mfe) {
+			throw SQLError.createSQLException(Messages
+					.getString("ResultSet.Malformed_URL____107")
+					+ val + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * The first warning reported by calls on this ResultSet is returned.
+	 * Subsequent ResultSet warnings will be chained to this
+	 * java.sql.SQLWarning.
+	 * 
+	 * <p>
+	 * The warning chain is automatically cleared each time a new row is read.
+	 * </p>
+	 * 
+	 * <p>
+	 * <B>Note:</B> This warning chain only covers warnings caused by ResultSet
+	 * methods. Any warnings caused by statement methods (such as reading OUT
+	 * parameters) will be chained on the Statement object.
+	 * </p>
+	 * 
+	 * @return the first java.sql.SQLWarning or null;
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs.
+	 */
+	public java.sql.SQLWarning getWarnings() throws SQLException {
+		return this.warningChain;
+	}
+
+	/**
+	 * JDBC 2.0 Insert the contents of the insert row into the result set and
+	 * the database. Must be on the insert row when this method is called.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, if called when not on
+	 *                the insert row, or if all non-nullable columns in the
+	 *                insert row have not been given a value
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void insertRow() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Determine if the cursor is after the last row in the result set.
+	 * </p>
+	 * 
+	 * @return true if after the last row, false otherwise. Returns false when
+	 *         the result set contains no rows.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean isAfterLast() throws SQLException {
+		checkClosed();
+
+		boolean b = this.rowData.isAfterLast();
+
+		return b;
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Determine if the cursor is before the first row in the result set.
+	 * </p>
+	 * 
+	 * @return true if before the first row, false otherwise. Returns false when
+	 *         the result set contains no rows.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean isBeforeFirst() throws SQLException {
+		checkClosed();
+
+		return this.rowData.isBeforeFirst();
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Determine if the cursor is on the first row of the result set.
+	 * </p>
+	 * 
+	 * @return true if on the first row, false otherwise.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean isFirst() throws SQLException {
+		checkClosed();
+
+		return this.rowData.isFirst();
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Determine if the cursor is on the last row of the result set. Note:
+	 * Calling isLast() may be expensive since the JDBC driver might need to
+	 * fetch ahead one row in order to determine whether the current row is the
+	 * last row in the result set.
+	 * </p>
+	 * 
+	 * @return true if on the last row, false otherwise.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean isLast() throws SQLException {
+		checkClosed();
+
+		return this.rowData.isLast();
+	}
+
+	/**
+	 * @param string
+	 * @param mysqlType
+	 * @param s
+	 */
+	private void issueConversionViaParsingWarning(String methodName,
+			int columnIndex, Object value, Field fieldInfo,
+			int[] typesWithNoParseConversion) throws SQLException {
+		
+		StringBuffer originalQueryBuf = new StringBuffer();
+		
+		if (this.owningStatement != null
+				&& this.owningStatement instanceof com.mysql.jdbc.PreparedStatement) {
+			originalQueryBuf.append(Messages.getString("ResultSet.CostlyConversionCreatedFromQuery"));
+			originalQueryBuf
+					.append(((com.mysql.jdbc.PreparedStatement) this.owningStatement).originalSql);
+			originalQueryBuf.append("\n\n");
+		} else {
+			originalQueryBuf.append(".");
+		}
+		
+		StringBuffer convertibleTypesBuf = new StringBuffer();
+		
+		for (int i = 0; i < typesWithNoParseConversion.length; i++) {
+			convertibleTypesBuf.append(MysqlDefs.typeToName(typesWithNoParseConversion[i]));
+			convertibleTypesBuf.append("\n");
+		}
+		
+		String message = Messages.getString("ResultSet.CostlyConversion", new Object[] {
+				methodName,
+				new Integer(columnIndex + 1),
+				fieldInfo.getOriginalName(),
+				fieldInfo.getOriginalTableName(),
+				originalQueryBuf.toString(),
+				value != null ? value.getClass().getName() : ResultSetMetaData.getClassNameForJavaType(
+						fieldInfo.getSQLType(), 
+						fieldInfo.isUnsigned(), 
+						fieldInfo.getMysqlType(), 
+						fieldInfo.isBinary() || fieldInfo.isBlob(),
+						fieldInfo.isOpaqueBinary()),
+				MysqlDefs.typeToName(fieldInfo.getMysqlType()),
+				convertibleTypesBuf.toString()});
+				
+		this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN,
+				"", (this.owningStatement == null) ? "N/A"
+						: this.owningStatement.currentCatalog,
+				this.connectionId, (this.owningStatement == null) ? (-1)
+						: this.owningStatement.getId(), this.resultId, System
+						.currentTimeMillis(), 0, Constants.MILLIS_I18N, null,
+				this.pointOfOrigin, message));
+
+	}
+	
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Moves to the last row in the result set.
+	 * </p>
+	 * 
+	 * @return true if on a valid row, false if no rows in the result set.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or result set type is
+	 *                TYPE_FORWARD_ONLY.
+	 */
+	public boolean last() throws SQLException {
+		checkClosed();
+
+		boolean b = true;
+		
+		if (this.rowData.size() == 0) {
+			b = false;
+		} else {
+
+			if (this.onInsertRow) {
+				this.onInsertRow = false;
+			}
+	
+			if (this.doingUpdates) {
+				this.doingUpdates = false;
+			}
+	
+			if (this.thisRow != null) {
+				this.thisRow.closeOpenStreams();
+			}
+			
+			this.rowData.beforeLast();
+			this.thisRow = this.rowData.next();
+		}
+
+		setRowPositionValidity();
+		
+		return b;
+	}
+
+	// /////////////////////////////////////////
+	//
+	// These number conversion routines save
+	// a ton of "new()s", especially for the heavily
+	// used getInt() and getDouble() methods
+	//
+	// /////////////////////////////////////////
+
+	/**
+	 * JDBC 2.0 Move the cursor to the remembered cursor position, usually the
+	 * current row. Has no effect unless the cursor is on the insert row.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the result set is
+	 *                not updatable
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void moveToCurrentRow() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Move to the insert row. The current cursor position is
+	 * remembered while the cursor is positioned on the insert row. The insert
+	 * row is a special row associated with an updatable result set. It is
+	 * essentially a buffer where a new row may be constructed by calling the
+	 * updateXXX() methods prior to inserting the row into the result set. Only
+	 * the updateXXX(), getXXX(), and insertRow() methods may be called when the
+	 * cursor is on the insert row. All of the columns in a result set must be
+	 * given a value each time this method is called before calling insertRow().
+	 * UpdateXXX()must be called before getXXX() on a column.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the result set is
+	 *                not updatable
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void moveToInsertRow() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * A ResultSet is initially positioned before its first row, the first call
+	 * to next makes the first row the current row; the second call makes the
+	 * second row the current row, etc.
+	 * 
+	 * <p>
+	 * If an input stream from the previous row is open, it is implicitly
+	 * closed. The ResultSet's warning chain is cleared when a new row is read
+	 * </p>
+	 * 
+	 * @return true if the new current is valid; false if there are no more rows
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean next() throws SQLException {
+		checkClosed();
+
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+		}
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+		}
+
+		boolean b;
+
+		if (!reallyResult()) {
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.ResultSet_is_from_UPDATE._No_Data_115"),
+					SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+		}
+
+		if (this.thisRow != null) {
+			this.thisRow.closeOpenStreams();
+		}
+		
+		if (this.rowData.size() == 0) {
+			b = false;
+		} else {
+			this.thisRow = this.rowData.next();
+			
+			if (this.thisRow == null) {
+				b = false;
+			} else {
+				clearWarnings();
+				
+				b = true;
+				
+			}
+		}
+
+		setRowPositionValidity();
+		
+		return b;
+	}
+
+	private int parseIntAsDouble(int columnIndex, String val)
+			throws NumberFormatException, SQLException {
+		if (val == null) {
+			return 0;
+		}
+
+		double valueAsDouble = Double.parseDouble(val);
+
+		if (this.jdbcCompliantTruncationForReads) {
+			if (valueAsDouble < Integer.MIN_VALUE
+					|| valueAsDouble > Integer.MAX_VALUE) {
+				throwRangeException(String.valueOf(valueAsDouble), columnIndex,
+						Types.INTEGER);
+			}
+		}
+
+		return (int) valueAsDouble;
+	}
+
+	private int getIntWithOverflowCheck(int columnIndex) throws SQLException {
+		int intValue = this.thisRow.getInt(columnIndex);
+
+		checkForIntegerTruncation(columnIndex + 1 /* only reported in errors */, 
+				null, this.thisRow.getString(
+				columnIndex, this.fields[columnIndex].getCharacterSet(),
+				this.connection), intValue);
+
+		return intValue;
+	}
+	
+	private void checkForIntegerTruncation(int columnIndex,
+			byte[] valueAsBytes, String valueAsString, int intValue)
+			throws SQLException {
+		if (this.jdbcCompliantTruncationForReads) {
+			if (intValue == Integer.MIN_VALUE || intValue == Integer.MAX_VALUE) {
+				long valueAsLong = Long
+						.parseLong(valueAsString == null ? new String(
+								valueAsBytes) : valueAsString);
+
+				if (valueAsLong < Integer.MIN_VALUE
+						|| valueAsLong > Integer.MAX_VALUE) {
+					throwRangeException(valueAsString == null ? new String(
+							valueAsBytes) : valueAsString, columnIndex,
+							Types.INTEGER);
+				}
+			}
+		}
+	}
+
+	private long parseLongAsDouble(int columnIndex, String val)
+			throws NumberFormatException, SQLException {
+		if (val == null) {
+			return 0;
+		}
+
+		double valueAsDouble = Double.parseDouble(val);
+
+		if (this.jdbcCompliantTruncationForReads) {
+			if (valueAsDouble < Long.MIN_VALUE
+					|| valueAsDouble > Long.MAX_VALUE) {
+				throwRangeException(val, columnIndex, Types.BIGINT);
+			}
+		}
+
+		return (long) valueAsDouble;
+	}
+
+	private long getLongWithOverflowCheck(int columnIndex, boolean doOverflowCheck) throws SQLException {
+		long longValue = this.thisRow.getLong(columnIndex);
+
+		if (doOverflowCheck) {
+			checkForLongTruncation(columnIndex + 1 /* only reported in errors */, 
+					null, this.thisRow.getString(
+					columnIndex, this.fields[columnIndex].getCharacterSet(),
+					this.connection), longValue);
+		}
+
+		return longValue;
+	}
+	
+	private long parseLongWithOverflowCheck(int columnIndex,
+			byte[] valueAsBytes, String valueAsString, boolean doCheck)
+			throws NumberFormatException, SQLException {
+
+		long longValue = 0;
+
+		if (valueAsBytes == null && valueAsString == null) {
+			return 0;
+		}
+
+		if (valueAsBytes != null) {
+			longValue = StringUtils.getLong(valueAsBytes);
+		} else {
+			//
+			// JDK-6 doesn't like trailing whitespace
+			//
+			// Note this isn't a performance issue, other
+			// than the iteration over the string, as String.trim()
+			// will return a new string only if whitespace is present
+			//
+			
+			valueAsString = valueAsString.trim();
+			
+			longValue = Long.parseLong(valueAsString);
+		}
+
+		if (doCheck && this.jdbcCompliantTruncationForReads) {
+			checkForLongTruncation(columnIndex, valueAsBytes, valueAsString,
+					longValue);
+		}
+
+		return longValue;
+	}
+
+	private void checkForLongTruncation(int columnIndex, byte[] valueAsBytes,
+			String valueAsString, long longValue) throws SQLException {
+		if (longValue == Long.MIN_VALUE
+				|| longValue == Long.MAX_VALUE) {
+			double valueAsDouble = Double
+					.parseDouble(valueAsString == null ? new String(
+							valueAsBytes) : valueAsString);
+
+			if (valueAsDouble < Long.MIN_VALUE
+					|| valueAsDouble > Long.MAX_VALUE) {
+				throwRangeException(valueAsString == null ? new String(
+						valueAsBytes) : valueAsString, columnIndex,
+						Types.BIGINT);
+			}
+		}
+	}
+
+	private short parseShortAsDouble(int columnIndex, String val)
+			throws NumberFormatException, SQLException {
+		if (val == null) {
+			return 0;
+		}
+
+		double valueAsDouble = Double.parseDouble(val);
+
+		if (this.jdbcCompliantTruncationForReads) {
+			if (valueAsDouble < Short.MIN_VALUE
+					|| valueAsDouble > Short.MAX_VALUE) {
+				throwRangeException(String.valueOf(valueAsDouble), columnIndex,
+						Types.SMALLINT);
+			}
+		}
+
+		return (short) valueAsDouble;
+	}
+
+	private short parseShortWithOverflowCheck(int columnIndex,
+			byte[] valueAsBytes, String valueAsString)
+			throws NumberFormatException, SQLException {
+
+		short shortValue = 0;
+
+		if (valueAsBytes == null && valueAsString == null) {
+			return 0;
+		}
+
+		if (valueAsBytes != null) {
+			shortValue = StringUtils.getShort(valueAsBytes);
+		} else {
+			//
+			// JDK-6 doesn't like trailing whitespace
+			//
+			// Note this isn't a performance issue, other
+			// than the iteration over the string, as String.trim()
+			// will return a new string only if whitespace is present
+			//
+			
+			valueAsString = valueAsString.trim();
+		
+			shortValue = Short.parseShort(valueAsString);
+		}
+
+		if (this.jdbcCompliantTruncationForReads) {
+			if (shortValue == Short.MIN_VALUE || shortValue == Short.MAX_VALUE) {
+				long valueAsLong = Long
+						.parseLong(valueAsString == null ? new String(
+								valueAsBytes) : valueAsString);
+
+				if (valueAsLong < Short.MIN_VALUE
+						|| valueAsLong > Short.MAX_VALUE) {
+					throwRangeException(valueAsString == null ? new String(
+							valueAsBytes) : valueAsString, columnIndex,
+							Types.SMALLINT);
+				}
+			}
+		}
+
+		return shortValue;
+	}
+
+	// --------------------------JDBC 2.0-----------------------------------
+	// ---------------------------------------------------------------------
+	// Getter's and Setter's
+	// ---------------------------------------------------------------------
+
+	/**
+	 * The prev method is not part of JDBC, but because of the architecture of
+	 * this driver it is possible to move both forward and backward within the
+	 * result set.
+	 * 
+	 * <p>
+	 * If an input stream from the previous row is open, it is implicitly
+	 * closed. The ResultSet's warning chain is cleared when a new row is read
+	 * </p>
+	 * 
+	 * @return true if the new current is valid; false if there are no more rows
+	 * 
+	 * @exception java.sql.SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean prev() throws java.sql.SQLException {
+		checkClosed();
+
+		int rowIndex = this.rowData.getCurrentRowNumber();
+
+		if (this.thisRow != null) {
+			this.thisRow.closeOpenStreams();
+		}
+		
+		boolean b = true;
+		
+		if ((rowIndex - 1) >= 0) {
+			rowIndex--;
+			this.rowData.setCurrentRow(rowIndex);
+			this.thisRow = this.rowData.getAt(rowIndex);
+
+			b = true;
+		} else if ((rowIndex - 1) == -1) {
+			rowIndex--;
+			this.rowData.setCurrentRow(rowIndex);
+			this.thisRow = null;
+
+			b = false;
+		} else {
+			b = false;
+		}
+		
+		setRowPositionValidity();
+		
+		return b;
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Moves to the previous row in the result set.
+	 * </p>
+	 * 
+	 * <p>
+	 * Note: previous() is not the same as relative(-1) since it makes sense to
+	 * call previous() when there is no current row.
+	 * </p>
+	 * 
+	 * @return true if on a valid row, false if off the result set.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or result set type is
+	 *                TYPE_FORWAR_DONLY.
+	 */
+	public boolean previous() throws SQLException {
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+		}
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+		}
+
+		return prev();
+	}
+
+	/**
+	 * Closes this ResultSet and releases resources.
+	 * 
+	 * @param calledExplicitly
+	 *            was this called by close()?
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void realClose(boolean calledExplicitly) throws SQLException {
+		if (this.isClosed) {
+			return;
+		}
+
+		try {
+			if (this.useUsageAdvisor) {
+				
+				// Report on result set closed by driver instead of application
+				
+				if (!calledExplicitly) {		
+					this.eventSink
+							.consumeEvent(new ProfilerEvent(
+									ProfilerEvent.TYPE_WARN,
+									"",
+									(this.owningStatement == null) ? "N/A"
+											: this.owningStatement.currentCatalog,
+									this.connectionId,
+									(this.owningStatement == null) ? (-1)
+											: this.owningStatement.getId(),
+									this.resultId,
+									System.currentTimeMillis(),
+									0,
+									Constants.MILLIS_I18N,
+									null,
+									this.pointOfOrigin,
+									Messages
+											.getString("ResultSet.ResultSet_implicitly_closed_by_driver"))); //$NON-NLS-1$
+				}
+
+				if (this.rowData instanceof RowDataStatic) {
+					
+					// Report on possibly too-large result sets
+					
+					if (this.rowData.size() > this.connection
+							.getResultSetSizeThreshold()) {
+						this.eventSink
+								.consumeEvent(new ProfilerEvent(
+										ProfilerEvent.TYPE_WARN,
+										"",
+										(this.owningStatement == null) ? Messages
+												.getString("ResultSet.N/A_159")
+												: this.owningStatement.currentCatalog, //$NON-NLS-1$
+										this.connectionId,
+										(this.owningStatement == null) ? (-1)
+												: this.owningStatement.getId(),
+										this.resultId,
+										System.currentTimeMillis(),
+										0,
+										Constants.MILLIS_I18N,
+										null,
+										this.pointOfOrigin,
+										Messages
+												.getString(
+														"ResultSet.Too_Large_Result_Set",
+														new Object[] {
+																new Integer(
+																		this.rowData
+																				.size()),
+																new Integer(
+																		this.connection
+																				.getResultSetSizeThreshold()) })));
+					}
+					
+					if (!isLast() && !isAfterLast() && (this.rowData.size() != 0)) {
+
+						this.eventSink
+								.consumeEvent(new ProfilerEvent(
+										ProfilerEvent.TYPE_WARN,
+										"",
+										(this.owningStatement == null) ? Messages
+												.getString("ResultSet.N/A_159")
+												: this.owningStatement.currentCatalog, //$NON-NLS-1$
+										this.connectionId,
+										(this.owningStatement == null) ? (-1)
+												: this.owningStatement.getId(),
+										this.resultId,
+										System.currentTimeMillis(),
+										0,
+										Constants.MILLIS_I18N,
+										null,
+										this.pointOfOrigin,
+										Messages
+												.getString(
+														"ResultSet.Possible_incomplete_traversal_of_result_set", //$NON-NLS-1$
+														new Object[] {
+																new Integer(
+																		getRow()),
+																new Integer(
+																		this.rowData
+																				.size()) })));
+					}
+				}
+
+				//
+				// Report on any columns that were selected but
+				// not referenced
+				//
+				
+				if (this.columnUsed.length > 0 && !this.rowData.wasEmpty()) {
+					StringBuffer buf = new StringBuffer(
+							Messages
+									.getString("ResultSet.The_following_columns_were_never_referenced")); //$NON-NLS-1$
+
+					boolean issueWarn = false;
+
+					for (int i = 0; i < this.columnUsed.length; i++) {
+						if (!this.columnUsed[i]) {
+							if (!issueWarn) {
+								issueWarn = true;
+							} else {
+								buf.append(", ");
+							}
+
+							buf.append(this.fields[i].getFullName());
+						}
+					}
+
+					if (issueWarn) {
+						this.eventSink.consumeEvent(new ProfilerEvent(
+								ProfilerEvent.TYPE_WARN, "",
+								(this.owningStatement == null) ? "N/A"
+										: this.owningStatement.currentCatalog,
+								this.connectionId,
+								(this.owningStatement == null) ? (-1)
+										: this.owningStatement.getId(), 0,
+								System.currentTimeMillis(), 0,
+								Constants.MILLIS_I18N, null,
+								this.pointOfOrigin, buf.toString()));
+					}
+				}
+			}
+		} finally {
+			SQLException exceptionDuringClose = null;
+
+			if (this.rowData != null) {
+				try {
+					this.rowData.close();
+				} catch (SQLException sqlEx) {
+					exceptionDuringClose = sqlEx;
+				}
+			}
+
+			this.rowData = null;
+			this.defaultTimeZone = null;
+			this.fields = null;
+			this.columnNameToIndex = null;
+			this.fullColumnNameToIndex = null;
+			this.eventSink = null;
+			this.warningChain = null;
+			
+			if (!this.retainOwningStatement) {
+				this.owningStatement = null;
+			}
+			
+			this.catalog = null;
+			this.serverInfo = null;
+			this.thisRow = null;
+			this.fastDateCal = null;
+			this.connection = null;
+
+			this.isClosed = true;
+
+			if (exceptionDuringClose != null) {
+				throw exceptionDuringClose;
+			}
+		}
+	}
+
+	public boolean reallyResult() {
+		if (this.rowData != null) {
+			return true;
+		}
+
+		return this.reallyResult;
+	}
+
+	/**
+	 * JDBC 2.0 Refresh the value of the current row with its current value in
+	 * the database. Cannot be called when on the insert row. The refreshRow()
+	 * method provides a way for an application to explicitly tell the JDBC
+	 * driver to refetch a row(s) from the database. An application may want to
+	 * call refreshRow() when caching or prefetching is being done by the JDBC
+	 * driver to fetch the latest value of a row from the database. The JDBC
+	 * driver may actually refresh multiple rows at once if the fetch size is
+	 * greater than one. All values are refetched subject to the transaction
+	 * isolation level and cursor sensitivity. If refreshRow() is called after
+	 * calling updateXXX(), but before calling updateRow() then the updates made
+	 * to the row are lost. Calling refreshRow() frequently will likely slow
+	 * performance.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or if called when on
+	 *                the insert row.
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void refreshRow() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Moves a relative number of rows, either positive or negative. Attempting
+	 * to move beyond the first/last row in the result set positions the cursor
+	 * before/after the the first/last row. Calling relative(0) is valid, but
+	 * does not change the cursor position.
+	 * </p>
+	 * 
+	 * <p>
+	 * Note: Calling relative(1) is different than calling next() since is makes
+	 * sense to call next() when there is no current row, for example, when the
+	 * cursor is positioned before the first row or after the last row of the
+	 * result set.
+	 * </p>
+	 * 
+	 * @param rows
+	 *            the number of relative rows to move the cursor.
+	 * 
+	 * @return true if on a row, false otherwise.
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs, or there is no current
+	 *             row, or result set type is TYPE_FORWARD_ONLY.
+	 */
+	public boolean relative(int rows) throws SQLException {
+		checkClosed();
+
+		if (this.rowData.size() == 0) {
+			setRowPositionValidity();
+			
+			return false;
+		}
+
+		if (this.thisRow != null) {
+			this.thisRow.closeOpenStreams();
+		}
+		
+		this.rowData.moveRowRelative(rows);
+		this.thisRow = this.rowData.getAt(this.rowData.getCurrentRowNumber());
+
+		setRowPositionValidity();
+		
+		return (!this.rowData.isAfterLast() && !this.rowData.isBeforeFirst());
+	}
+
+	/**
+	 * JDBC 2.0 Determine if this row has been deleted. A deleted row may leave
+	 * a visible "hole" in a result set. This method can be used to detect holes
+	 * in a result set. The value returned depends on whether or not the result
+	 * set can detect deletions.
+	 * 
+	 * @return true if deleted and deletes are detected
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 * 
+	 * @see DatabaseMetaData#deletesAreDetected
+	 */
+	public boolean rowDeleted() throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * JDBC 2.0 Determine if the current row has been inserted. The value
+	 * returned depends on whether or not the result set can detect visible
+	 * inserts.
+	 * 
+	 * @return true if inserted and inserts are detected
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 * 
+	 * @see DatabaseMetaData#insertsAreDetected
+	 */
+	public boolean rowInserted() throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * JDBC 2.0 Determine if the current row has been updated. The value
+	 * returned depends on whether or not the result set can detect updates.
+	 * 
+	 * @return true if the row has been visibly updated by the owner or another,
+	 *         and updates are detected
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 * 
+	 * @see DatabaseMetaData#updatesAreDetected
+	 */
+	public boolean rowUpdated() throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * Flag that this result set is 'binary' encoded (from a PreparedStatement),
+	 * not stored as strings.
+	 */
+	protected void setBinaryEncoded() {
+		this.isBinaryEncoded = true;
+	}
+
+	private void setDefaultTimeZone(TimeZone defaultTimeZone) {
+		this.defaultTimeZone = defaultTimeZone;
+	}
+
+	/**
+	 * JDBC 2.0 Give a hint as to the direction in which the rows in this result
+	 * set will be processed. The initial value is determined by the statement
+	 * that produced the result set. The fetch direction may be changed at any
+	 * time.
+	 * 
+	 * @param direction
+	 *            the direction to fetch rows in.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the result set type
+	 *                is TYPE_FORWARD_ONLY and direction is not FETCH_FORWARD.
+	 *                MM.MySQL actually ignores this, because it has the whole
+	 *                result set anyway, so the direction is immaterial.
+	 */
+	public void setFetchDirection(int direction) throws SQLException {
+		if ((direction != FETCH_FORWARD) && (direction != FETCH_REVERSE)
+				&& (direction != FETCH_UNKNOWN)) {
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Illegal_value_for_fetch_direction_64"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		this.fetchDirection = direction;
+	}
+
+	/**
+	 * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should
+	 * be fetched from the database when more rows are needed for this result
+	 * set. If the fetch size specified is zero, then the JDBC driver ignores
+	 * the value, and is free to make its own best guess as to what the fetch
+	 * size should be. The default value is set by the statement that creates
+	 * the result set. The fetch size may be changed at any time.
+	 * 
+	 * @param rows
+	 *            the number of rows to fetch
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the condition 0 lteq
+	 *                rows lteq this.getMaxRows() is not satisfied. Currently
+	 *                ignored by this driver.
+	 */
+	public void setFetchSize(int rows) throws SQLException {
+		if (rows < 0) { /* || rows > getMaxRows() */
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Value_must_be_between_0_and_getMaxRows()_66"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		this.fetchSize = rows;
+	}
+
+	/**
+	 * Sets the first character of the query that this result set was created
+	 * from.
+	 * 
+	 * @param c
+	 *            the first character of the query...uppercased
+	 */
+	public void setFirstCharOfQuery(char c) {
+		this.firstCharOfQuery = c;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param nextResultSet
+	 *            Sets the next result set in the result set chain for multiple
+	 *            result sets.
+	 */
+	protected void setNextResultSet(ResultSetInternalMethods nextResultSet) {
+		this.nextResultSet = nextResultSet;
+	}
+
+	public void setOwningStatement(com.mysql.jdbc.StatementImpl owningStatement) {
+		this.owningStatement = owningStatement;
+	}
+
+	/**
+	 * Sets the concurrency (JDBC2)
+	 * 
+	 * @param concurrencyFlag
+	 *            CONCUR_UPDATABLE or CONCUR_READONLY
+	 */
+	protected void setResultSetConcurrency(int concurrencyFlag) {
+		this.resultSetConcurrency = concurrencyFlag;
+	}
+
+	/**
+	 * Sets the result set type for (JDBC2)
+	 * 
+	 * @param typeFlag
+	 *            SCROLL_SENSITIVE or SCROLL_INSENSITIVE (we only support
+	 *            SCROLL_INSENSITIVE)
+	 */
+	protected void setResultSetType(int typeFlag) {
+		this.resultSetType = typeFlag;
+	}
+
+	/**
+	 * Sets server info (if any)
+	 * 
+	 * @param info
+	 *            the server info message
+	 */
+	protected void setServerInfo(String info) {
+		this.serverInfo = info;
+	}
+
+	public void setStatementUsedForFetchingRows(PreparedStatement stmt) {
+		this.statementUsedForFetchingRows = stmt;
+	}
+
+	/**
+	 * @param wrapperStatement
+	 *            The wrapperStatement to set.
+	 */
+	public void setWrapperStatement(java.sql.Statement wrapperStatement) {
+		this.wrapperStatement = wrapperStatement;
+	}
+
+	private void throwRangeException(String valueAsString, int columnIndex,
+			int jdbcType) throws SQLException {
+		String datatype = null;
+
+		switch (jdbcType) {
+		case Types.TINYINT:
+			datatype = "TINYINT";
+			break;
+		case Types.SMALLINT:
+			datatype = "SMALLINT";
+			break;
+		case Types.INTEGER:
+			datatype = "INTEGER";
+			break;
+		case Types.BIGINT:
+			datatype = "BIGINT";
+			break;
+		case Types.REAL:
+			datatype = "REAL";
+			break;
+		case Types.FLOAT:
+			datatype = "FLOAT";
+			break;
+		case Types.DOUBLE:
+			datatype = "DOUBLE";
+			break;
+		case Types.DECIMAL:
+			datatype = "DECIMAL";
+			break;
+		default:
+			datatype = " (JDBC type '" + jdbcType + "')";
+		}
+
+		throw SQLError.createSQLException("'" + valueAsString + "' in column '"
+				+ columnIndex + "' is outside valid range for the datatype "
+				+ datatype + ".", SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String toString() {
+		if (this.reallyResult) {
+			return super.toString();
+		}
+
+		return "Result set representing update count of " + this.updateCount;
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateArray(int, Array)
+	 */
+	public void updateArray(int arg0, Array arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateArray(String, Array)
+	 */
+	public void updateArray(String arg0, Array arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an ascii stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            the length of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateAsciiStream(int columnIndex, java.io.InputStream x,
+			int length) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an ascii stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateAsciiStream(String columnName, java.io.InputStream x,
+			int length) throws SQLException {
+		updateAsciiStream(findColumn(columnName), x, length);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateBigDecimal(int columnIndex, BigDecimal x)
+			throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a BigDecimal value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateBigDecimal(String columnName, BigDecimal x)
+			throws SQLException {
+		updateBigDecimal(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a binary stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            the length of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateBinaryStream(int columnIndex, java.io.InputStream x,
+			int length) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a binary stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateBinaryStream(String columnName, java.io.InputStream x,
+			int length) throws SQLException {
+		updateBinaryStream(findColumn(columnName), x, length);
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateBlob(int, Blob)
+	 */
+	public void updateBlob(int arg0, java.sql.Blob arg1) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateBlob(String, Blob)
+	 */
+	public void updateBlob(String arg0, java.sql.Blob arg1) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateBoolean(int columnIndex, boolean x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a boolean value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateBoolean(String columnName, boolean x) throws SQLException {
+		updateBoolean(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateByte(int columnIndex, byte x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a byte value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateByte(String columnName, byte x) throws SQLException {
+		updateByte(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateBytes(int columnIndex, byte[] x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a byte array value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateBytes(String columnName, byte[] x) throws SQLException {
+		updateBytes(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a character stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * @param length
+	 *            the length of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateCharacterStream(int columnIndex, java.io.Reader x,
+			int length) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a character stream value. The updateXXX()
+	 * methods are used to update column values in the current row, or the
+	 * insert row. The updateXXX() methods do not update the underlying
+	 * database, instead the updateRow() or insertRow() methods are called to
+	 * update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param reader
+	 *            the stream to update the column with
+	 * @param length
+	 *            of the stream
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs
+	 */
+	public void updateCharacterStream(String columnName, java.io.Reader reader,
+			int length) throws SQLException {
+		updateCharacterStream(findColumn(columnName), reader, length);
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateClob(int, Clob)
+	 */
+	public void updateClob(int arg0, java.sql.Clob arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateClob(String, Clob)
+	 */
+	public void updateClob(String columnName, java.sql.Clob clob)
+			throws SQLException {
+		updateClob(findColumn(columnName), clob);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateDate(int columnIndex, java.sql.Date x)
+			throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a Date value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateDate(String columnName, java.sql.Date x)
+			throws SQLException {
+		updateDate(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a Double value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateDouble(int columnIndex, double x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a double value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateDouble(String columnName, double x) throws SQLException {
+		updateDouble(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a float value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateFloat(int columnIndex, float x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a float value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateFloat(String columnName, float x) throws SQLException {
+		updateFloat(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an integer value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateInt(int columnIndex, int x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an integer value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateInt(String columnName, int x) throws SQLException {
+		updateInt(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a long value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateLong(int columnIndex, long x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a long value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateLong(String columnName, long x) throws SQLException {
+		updateLong(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Give a nullable column a null value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateNull(int columnIndex) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a null value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateNull(String columnName) throws SQLException {
+		updateNull(findColumn(columnName));
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an Object value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateObject(int columnIndex, Object x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an Object value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * @param scale
+	 *            For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
+	 *            this is the number of digits after the decimal. For all other
+	 *            types this value will be ignored.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateObject(int columnIndex, Object x, int scale)
+			throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an Object value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateObject(String columnName, Object x) throws SQLException {
+		updateObject(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with an Object value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * @param scale
+	 *            For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
+	 *            this is the number of digits after the decimal. For all other
+	 *            types this value will be ignored.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateObject(String columnName, Object x, int scale)
+			throws SQLException {
+		updateObject(findColumn(columnName), x);
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateRef(int, Ref)
+	 */
+	public void updateRef(int arg0, Ref arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see ResultSetInternalMethods#updateRef(String, Ref)
+	 */
+	public void updateRef(String arg0, Ref arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * JDBC 2.0 Update the underlying database with the new contents of the
+	 * current row. Cannot be called when on the insert row.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs, or if called when on
+	 *                the insert row
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateRow() throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a short value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateShort(int columnIndex, short x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a short value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateShort(String columnName, short x) throws SQLException {
+		updateShort(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a String value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateString(int columnIndex, String x) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a String value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateString(String columnName, String x) throws SQLException {
+		updateString(findColumn(columnName), x);
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateTime(int columnIndex, java.sql.Time x)
+			throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * JDBC 2.0 Update a column with a Time value. The updateXXX() methods are
+	 * used to update column values in the current row, or the insert row. The
+	 * updateXXX() methods do not update the underlying database, instead the
+	 * updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateTime(String columnName, java.sql.Time x)
+			throws SQLException {
+		updateTime(findColumn(columnName), x);
+	}
+
+	
+	/**
+	 * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnIndex
+	 *            the first column is 1, the second is 2, ...
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	public void updateTimestamp(int columnIndex, java.sql.Timestamp x)
+			throws SQLException {
+		throw new NotUpdatable();
+	}
+	
+	/**
+	 * JDBC 2.0 Update a column with a Timestamp value. The updateXXX() methods
+	 * are used to update column values in the current row, or the insert row.
+	 * The updateXXX() methods do not update the underlying database, instead
+	 * the updateRow() or insertRow() methods are called to update the database.
+	 * 
+	 * @param columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public void updateTimestamp(String columnName, java.sql.Timestamp x)
+			throws SQLException {
+		updateTimestamp(findColumn(columnName), x);
+	}
+
+	/**
+	 * A column may have the value of SQL NULL; wasNull() reports whether the
+	 * last column read had this special value. Note that you must first call
+	 * getXXX on a column to try to read its value and then call wasNull() to
+	 * find if the value was SQL NULL
+	 * 
+	 * @return true if the last column read was SQL NULL
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurred
+	 */
+	public boolean wasNull() throws SQLException {
+		return this.wasNullFlag;
+	}
+
+	protected Calendar getGmtCalendar() {
+		
+		// Worst case we allocate this twice and the other gets GC'd,
+		// however prevents deadlock
+		if (this.gmtCalendar == null) {
+			this.gmtCalendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		}
+		
+		return this.gmtCalendar;
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetInternalMethods.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetInternalMethods.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetInternalMethods.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+/**
+ * This interface is intended to be used by implementors of statement
+ * interceptors so that implementors can create static or dynamic (via 
+ * java.lang.reflect.Proxy) proxy instances of ResultSets. It consists 
+ * of methods outside of java.sql.Result that are used internally by 
+ * other classes in the driver.
+ * 
+ * This interface, although public is <strong>not</strong> designed to 
+ * be consumed publicly other than for the statement interceptor use case.
+ * 
+ * @version $Id: $
+ */
+public interface ResultSetInternalMethods extends java.sql.ResultSet {
+
+	/**
+	 * Returns a new instance of this result set, that shares the
+	 * underlying row data.
+	 */
+	public abstract ResultSetInternalMethods copy() throws SQLException;
+
+	/**
+	 * Does the result set contain rows, or is it the result of a DDL or DML
+	 * statement?
+	 */
+	public abstract boolean reallyResult();
+
+	/**
+	 * Functions like ResultSet.getObject(), but using the given SQL type
+	 * (as registered during CallableStatement.registerOutParameter()).
+	 */
+	public abstract  Object getObjectStoredProc(int columnIndex, int desiredSqlType)
+			throws SQLException;
+
+	/**
+	 * Functions like ResultSet.getObject(), but using the given SQL type
+	 * (as registered during CallableStatement.registerOutParameter()).
+	 */
+	public abstract  Object getObjectStoredProc(int i, java.util.Map map,
+			int desiredSqlType) throws SQLException;
+
+	/**
+	 * Functions like ResultSet.getObject(), but using the given SQL type
+	 * (as registered during CallableStatement.registerOutParameter()).
+	 */
+	public abstract  Object getObjectStoredProc(String columnName, int desiredSqlType)
+			throws SQLException;
+
+	/**
+	 * Functions like ResultSet.getObject(), but using the given SQL type
+	 * (as registered during CallableStatement.registerOutParameter()).
+	 */
+	public abstract  Object getObjectStoredProc(String colName, java.util.Map map,
+			int desiredSqlType) throws SQLException;
+
+	/**
+	 * Returns the server informational message returned from a DDL or DML
+	 * statement (if any), or null if none.
+	 */
+	public String getServerInfo();
+
+	/**
+	 * Returns the update count for this result set (if one exists), otherwise
+	 * -1.
+	 * 
+	 * @ return the update count for this result set (if one exists), otherwise
+	 * -1.
+	 */
+	public long getUpdateCount();
+
+	/**
+	 * Returns the AUTO_INCREMENT value for the DDL/DML statement which created
+	 * this result set.
+	 * 
+	 * @return the AUTO_INCREMENT value for the DDL/DML statement which created
+	 * this result set.
+	 */
+	public long getUpdateID();
+
+	/**
+	 * Closes this ResultSet and releases resources.
+	 * 
+	 * @param calledExplicitly was realClose called by the standard
+	 * ResultSet.close() method, or was it closed internally by the driver?
+	 */
+	public void realClose(boolean calledExplicitly) throws SQLException;
+
+	/**
+	 * Sets the first character of the query that was issued to create
+	 * this result set. The character should be upper-cased.
+	 */
+	public void setFirstCharOfQuery(char firstCharUpperCase);
+
+	/**
+	 * Sets the statement that "owns" this result set (usually used when the
+	 * result set should internally "belong" to one statement, but is created
+	 * by another.
+	 */
+	public void setOwningStatement(com.mysql.jdbc.StatementImpl owningStatement);
+
+	/**
+	 * Returns the first character of the query that was issued to create this 
+	 * result set, upper-cased.
+	 */
+	public char getFirstCharOfQuery();
+
+	/**
+	 * Clears the reference to the next result set in a multi-result set
+	 * "chain".
+	 */
+	public void clearNextResult();
+
+	/**
+	 * Returns the next ResultSet in a multi-resultset "chain", if any, 
+	 * null if none exists.
+	 */
+	public ResultSetInternalMethods getNextResultSet();
+
+	public void setStatementUsedForFetchingRows(PreparedStatement stmt);
+
+	/**
+	 * @param wrapperStatement
+	 *            The wrapperStatement to set.
+	 */
+	public void setWrapperStatement(java.sql.Statement wrapperStatement);
+
+	/**
+	 * Builds a hash between column names and their indices for fast retrieval.
+	 * This is done lazily to support findColumn() and get*(String), as it
+	 * can be more expensive than just retrieving result set values by ordinal
+	 * index.
+	 */
+	public void buildIndexMapping() throws SQLException;
+
+	public void initializeWithMetadata() throws SQLException;
+
+	/**
+	 * Used by DatabaseMetadata implementations to coerce the metadata returned
+	 * by metadata queries into that required by the JDBC specification.
+	 * 
+	 * @param metadataFields the coerced metadata to be applied to result sets 
+	 * returned by "SHOW ..." or SELECTs on INFORMATION_SCHEMA performed on behalf
+	 * of methods in DatabaseMetadata.
+	 */
+	public void redefineFieldsForDBMD(Field[] metadataFields);
+
+	public void populateCachedMetaData(CachedResultSetMetaData cachedMetaData) throws SQLException;
+
+	public void initializeFromCachedMetaData(CachedResultSetMetaData cachedMetaData);
+
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetMetaData.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetMetaData.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetMetaData.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -19,17 +19,14 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-
-
  */
 package com.mysql.jdbc;
 
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
 import java.sql.SQLException;
 import java.sql.Types;
 
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+
 /**
  * A ResultSetMetaData object can be used to find out about the types and
  * properties of the columns in a ResultSet
@@ -79,7 +76,7 @@
 
 	Field[] fields;
 	boolean useOldAliasBehavior = false;
-	
+
 	/**
 	 * Initialise for a result with a tuple set and a field descriptor set
 	 * 
@@ -247,8 +244,8 @@
 	public String getColumnName(int column) throws SQLException {
 		if (this.useOldAliasBehavior) {
 			return getField(column).getName();
-		}
-		
+	}
+
 		String name = getField(column).getNameNoAliases();
 		
 		if (name != null && name.length() == 0) {
@@ -306,7 +303,7 @@
 			return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
 
 		case MysqlDefs.FIELD_TYPE_LONG:
-			return field.isUnsigned() ? "INTEGER UNSIGNED" : "INTEGER";
+			return field.isUnsigned() ? "INT UNSIGNED" : "INT";
 
 		case MysqlDefs.FIELD_TYPE_FLOAT:
 			return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT";
@@ -806,4 +803,50 @@
 			return "java.lang.Object"; //$NON-NLS-1$
 		}
 	}
+	
+	/**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class iface) throws SQLException {
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface 
+     * @since 1.6
+     */
+	public Object unwrap(Class iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+    		return Util.cast(iface, this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
 }

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetRow.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetRow.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ResultSetRow.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,1371 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Calendar;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+/**
+ * Classes that implement this interface represent one row of data from the
+ * MySQL server that might be stored in different ways depending on whether the
+ * result set was streaming (so they wrap a reusable packet), or whether the
+ * result set was cached or via a server-side cursor (so they represent a
+ * byte[][]).
+ * 
+ * Notice that <strong>no</strong> bounds checking is expected for implementors
+ * of this interface, it happens in ResultSetImpl.
+ * 
+ * @version $Id: $
+ */
+public abstract class ResultSetRow {
+	/**
+	 * The metadata of the fields of this result set.
+	 */
+	protected Field[] metadata;
+
+	/**
+	 * Called during navigation to next row to close all open
+	 * streams.
+	 */
+	public abstract void closeOpenStreams();
+
+	/**
+	 * Returns data at the given index as an InputStream with no
+	 * character conversion.
+	 * 
+	 * @param columnIndex
+	 *            of the column value (starting at 0) to return.
+	 * @return the value at the given index as an InputStream or null
+	 *         if null.
+	 *         
+	 * @throws SQLException if an error occurs while retrieving the value.
+	 */
+	public abstract InputStream getBinaryInputStream(int columnIndex)
+			throws SQLException;
+
+	/**
+	 * Returns the value at the given column (index starts at 0) "raw" (i.e.
+	 * as-returned by the server).
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) to return.
+	 * @return the value for the given column (including NULL if it is)
+	 * @throws SQLException
+	 *             if an error occurs while retrieving the value.
+	 */
+	public abstract byte[] getColumnValue(int index) throws SQLException;
+
+	protected final java.sql.Date getDateFast(int columnIndex,
+			byte[] dateAsBytes, int offset, int length, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+
+		int year = 0;
+		int month = 0;
+		int day = 0;
+
+		try {
+			if (dateAsBytes == null) {
+				return null;
+			}
+
+			boolean allZeroDate = true;
+
+			boolean onlyTimePresent = false;
+
+			for (int i = 0; i < length; i++) {
+				if (dateAsBytes[offset + i] == ':') {
+					onlyTimePresent = true;
+					break;
+				}
+			}
+
+			for (int i = 0; i < length; i++) {
+				byte b = dateAsBytes[offset + i];
+
+				if (b == ' ' || b == '-' || b == '/') {
+					onlyTimePresent = false;
+				}
+
+				if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/'
+						&& b != '.') {
+					allZeroDate = false;
+
+					break;
+				}
+			}
+
+			if (!onlyTimePresent && allZeroDate) {
+
+				if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(conn.getZeroDateTimeBehavior())) {
+
+					return null;
+				} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+						.equals(conn.getZeroDateTimeBehavior())) {
+					throw SQLError.createSQLException("Value '"
+							+ new String(dateAsBytes)
+							+ "' can not be represented as java.sql.Date",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				// We're left with the case of 'round' to a date Java _can_
+				// represent, which is '0001-01-01'.
+				return rs.fastDateCreate(null, 1, 1, 1);
+
+			} else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
+				// Convert from TIMESTAMP
+				switch (length) {
+				case 21:
+				case 19: { // java.sql.Timestamp format
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 4);
+					month = StringUtils.getInt(dateAsBytes, offset + 5,
+							offset + 7);
+					day = StringUtils.getInt(dateAsBytes, offset + 8,
+							offset + 10);
+
+					return rs.fastDateCreate(null, year, month, day);
+				}
+
+				case 14:
+				case 8: {
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 4);
+					month = StringUtils.getInt(dateAsBytes, offset + 4,
+							offset + 6);
+					day = StringUtils.getInt(dateAsBytes, offset + 6,
+							offset + 8);
+
+					return rs.fastDateCreate(null, year, month, day);
+				}
+
+				case 12:
+				case 10:
+				case 6: {
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 2);
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					month = StringUtils.getInt(dateAsBytes, offset + 2,
+							offset + 4);
+					day = StringUtils.getInt(dateAsBytes, offset + 4,
+							offset + 6);
+
+					return rs.fastDateCreate(null, year + 1900, month, day);
+				}
+
+				case 4: {
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 4);
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					month = StringUtils.getInt(dateAsBytes, offset + 2,
+							offset + 4);
+
+					return rs.fastDateCreate(null, year + 1900, month, 1);
+				}
+
+				case 2: {
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 2);
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					return rs.fastDateCreate(null, year + 1900, 1, 1);
+				}
+
+				default:
+					throw SQLError
+							.createSQLException(
+									Messages
+											.getString(
+													"ResultSet.Bad_format_for_Date",
+													new Object[] {
+															new String(
+																	dateAsBytes),
+															Constants
+																	.integerValueOf(columnIndex + 1) }),
+									SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				} /* endswitch */
+			} else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+
+				if (length == 2 || length == 1) {
+					year = StringUtils.getInt(dateAsBytes, offset, offset
+							+ length);
+
+					if (year <= 69) {
+						year = year + 100;
+					}
+
+					year += 1900;
+				} else {
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 4);
+				}
+
+				return rs.fastDateCreate(null, year, 1, 1);
+			} else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_TIME) {
+				return rs.fastDateCreate(null, 1970, 1, 1); // Return EPOCH
+			} else {
+				if (length < 10) {
+					if (length == 8) {
+						return rs.fastDateCreate(null, 1970, 1, 1); // Return
+						// EPOCH for
+						// TIME
+					}
+
+					throw SQLError
+							.createSQLException(
+									Messages
+											.getString(
+													"ResultSet.Bad_format_for_Date",
+													new Object[] {
+															new String(
+																	dateAsBytes),
+															Constants
+																	.integerValueOf(columnIndex + 1) }),
+									SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+
+				if (length != 18) {
+					year = StringUtils.getInt(dateAsBytes, offset + 0,
+							offset + 4);
+					month = StringUtils.getInt(dateAsBytes, offset + 5,
+							offset + 7);
+					day = StringUtils.getInt(dateAsBytes, offset + 8,
+							offset + 10);
+				} else {
+					// JDK-1.3 timestamp format, not real easy to parse
+					// positionally :p
+					StringTokenizer st = new StringTokenizer(new String(
+							dateAsBytes, offset, length, "ISO8859_1"), "- ");
+
+					year = Integer.parseInt(st.nextToken());
+					month = Integer.parseInt(st.nextToken());
+					day = Integer.parseInt(st.nextToken());
+				}
+			}
+
+			return rs.fastDateCreate(null, year, month, day);
+		} catch (SQLException sqlEx) {
+			throw sqlEx; // don't re-wrap
+		} catch (Exception e) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Bad_format_for_Date", new Object[] {
+							new String(dateAsBytes),
+							Constants.integerValueOf(columnIndex + 1) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	public abstract java.sql.Date getDateFast(int columnIndex,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException;
+
+	/**
+	 * Returns the value at the given column (index starts at 0) as an int. *
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) to return.
+	 * @return the value for the given column (returns 0 if NULL, use isNull()
+	 *         to determine if the value was actually NULL)
+	 * @throws SQLException
+	 *             if an error occurs while retrieving the value.
+	 */
+	public abstract int getInt(int columnIndex) throws SQLException;
+
+	/**
+	 * Returns the value at the given column (index starts at 0) as a long. *
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) to return.
+	 * @return the value for the given column (returns 0 if NULL, use isNull()
+	 *         to determine if the value was actually NULL)
+	 * @throws SQLException
+	 *             if an error occurs while retrieving the value.
+	 */
+	public abstract long getLong(int columnIndex) throws SQLException;
+
+	protected java.sql.Date getNativeDate(int columnIndex, byte[] bits,
+			int offset, int length, ConnectionImpl conn, ResultSetImpl rs)
+			throws SQLException {
+
+		int year = 0;
+		int month = 0;
+		int day = 0;
+
+		if (length != 0) {
+			year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8);
+
+			month = bits[offset + 2];
+			day = bits[offset + 3];
+		}
+
+		if ((year == 0) && (month == 0) && (day == 0)) {
+			if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+					.equals(conn.getZeroDateTimeBehavior())) {
+				return null;
+			} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+					.equals(conn.getZeroDateTimeBehavior())) {
+				throw SQLError
+						.createSQLException(
+								"Value '0000-00-00' can not be represented as java.sql.Date",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			year = 1;
+			month = 1;
+			day = 1;
+		}
+
+		return rs.fastDateCreate(rs.getCalendarInstanceForSessionOrNew(), year,
+				month, day);
+	}
+
+	public abstract Date getNativeDate(int columnIndex, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException;
+
+	protected Object getNativeDateTimeValue(int columnIndex, byte[] bits,
+			int offset, int length, Calendar targetCalendar, int jdbcType,
+			int mysqlType, TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException {
+
+		int year = 0;
+		int month = 0;
+		int day = 0;
+
+		int hour = 0;
+		int minute = 0;
+		int seconds = 0;
+
+		int nanos = 0;
+
+		if (bits == null) {
+
+			return null;
+		}
+
+		Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn
+				.getUtcCalendar()
+				: rs.getCalendarInstanceForSessionOrNew();
+
+		boolean populatedFromDateTimeValue = false;
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			populatedFromDateTimeValue = true;
+
+			if (length != 0) {
+				year = (bits[offset + 0] & 0xff)
+						| ((bits[offset + 1] & 0xff) << 8);
+				month = bits[offset + 2];
+				day = bits[offset + 3];
+
+				if (length > 4) {
+					hour = bits[offset + 4];
+					minute = bits[offset + 5];
+					seconds = bits[offset + 6];
+				}
+
+				if (length > 7) {
+					// MySQL uses microseconds
+					nanos = ((bits[offset + 7] & 0xff)
+							| ((bits[offset + 8] & 0xff) << 8)
+							| ((bits[offset + 9] & 0xff) << 16) | ((bits[offset + 10] & 0xff) << 24)) * 1000;
+				}
+			}
+
+			break;
+		case MysqlDefs.FIELD_TYPE_DATE:
+			populatedFromDateTimeValue = true;
+
+			if (bits.length != 0) {
+				year = (bits[offset + 0] & 0xff)
+						| ((bits[offset + 1] & 0xff) << 8);
+				month = bits[offset + 2];
+				day = bits[offset + 3];
+			}
+
+			break;
+		case MysqlDefs.FIELD_TYPE_TIME:
+			populatedFromDateTimeValue = true;
+
+			if (bits.length != 0) {
+				// bits[0] // skip tm->neg
+				// binaryData.readLong(); // skip daysPart
+				hour = bits[offset + 5];
+				minute = bits[offset + 6];
+				seconds = bits[offset + 7];
+			}
+
+			year = 1970;
+			month = 1;
+			day = 1;
+
+			break;
+		default:
+			populatedFromDateTimeValue = false;
+		}
+
+		switch (jdbcType) {
+		case Types.TIME:
+			if (populatedFromDateTimeValue) {
+				Time time = TimeUtil.fastTimeCreate(rs
+						.getCalendarInstanceForSessionOrNew(), hour, minute,
+						seconds);
+
+				Time adjustedTime = TimeUtil.changeTimezone(conn,
+						sessionCalendar, targetCalendar, time, conn
+								.getServerTimezoneTZ(), tz, rollForward);
+
+				return adjustedTime;
+			}
+
+			return rs.getNativeTimeViaParseConversion(columnIndex + 1,
+					targetCalendar, tz, rollForward);
+
+		case Types.DATE:
+			if (populatedFromDateTimeValue) {
+				if ((year == 0) && (month == 0) && (day == 0)) {
+					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(conn.getZeroDateTimeBehavior())) {
+
+						return null;
+					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(conn.getZeroDateTimeBehavior())) {
+						throw new SQLException(
+								"Value '0000-00-00' can not be represented as java.sql.Date",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+
+					year = 1;
+					month = 1;
+					day = 1;
+				}
+
+				return rs
+						.fastDateCreate(
+								rs.getCalendarInstanceForSessionOrNew(), year,
+								month, day);
+			}
+
+			return rs.getNativeDateViaParseConversion(columnIndex + 1);
+		case Types.TIMESTAMP:
+			if (populatedFromDateTimeValue) {
+				if ((year == 0) && (month == 0) && (day == 0)) {
+					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(conn.getZeroDateTimeBehavior())) {
+
+						return null;
+					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(conn.getZeroDateTimeBehavior())) {
+						throw new SQLException(
+								"Value '0000-00-00' can not be represented as java.sql.Timestamp",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+
+					year = 1;
+					month = 1;
+					day = 1;
+				}
+
+				Timestamp ts = rs.fastTimestampCreate(rs
+						.getCalendarInstanceForSessionOrNew(), year, month,
+						day, hour, minute, seconds, nanos);
+
+				Timestamp adjustedTs = TimeUtil.changeTimezone(conn,
+						sessionCalendar, targetCalendar, ts, conn
+								.getServerTimezoneTZ(), tz, rollForward);
+
+				return adjustedTs;
+			}
+
+			return rs.getNativeTimestampViaParseConversion(columnIndex + 1,
+					targetCalendar, tz, rollForward);
+
+		default:
+			throw new SQLException(
+					"Internal error - conversion method doesn't support this type",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	public abstract Object getNativeDateTimeValue(int columnIndex,
+			Calendar targetCalendar, int jdbcType, int mysqlType,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn, ResultSetImpl rs)
+			throws SQLException;
+
+	protected double getNativeDouble(byte[] bits, int offset) {
+		long valueAsLong = (bits[offset + 0] & 0xff)
+				| ((long) (bits[offset + 1] & 0xff) << 8)
+				| ((long) (bits[offset + 2] & 0xff) << 16)
+				| ((long) (bits[offset + 3] & 0xff) << 24)
+				| ((long) (bits[offset + 4] & 0xff) << 32)
+				| ((long) (bits[offset + 5] & 0xff) << 40)
+				| ((long) (bits[offset + 6] & 0xff) << 48)
+				| ((long) (bits[offset + 7] & 0xff) << 56);
+
+		return Double.longBitsToDouble(valueAsLong);
+	}
+
+	public abstract double getNativeDouble(int columnIndex) throws SQLException;
+
+	protected float getNativeFloat(byte[] bits, int offset) {
+		int asInt = (bits[offset + 0] & 0xff)
+				| ((bits[offset + 1] & 0xff) << 8)
+				| ((bits[offset + 2] & 0xff) << 16)
+				| ((bits[offset + 3] & 0xff) << 24);
+
+		return Float.intBitsToFloat(asInt);
+	}
+
+	public abstract float getNativeFloat(int columnIndex) throws SQLException;
+
+	protected int getNativeInt(byte[] bits, int offset) {
+
+		int valueAsInt = (bits[offset + 0] & 0xff)
+				| ((bits[offset + 1] & 0xff) << 8)
+				| ((bits[offset + 2] & 0xff) << 16)
+				| ((bits[offset + 3] & 0xff) << 24);
+
+		return valueAsInt;
+	}
+
+	public abstract int getNativeInt(int columnIndex) throws SQLException;
+
+	protected long getNativeLong(byte[] bits, int offset) {
+		long valueAsLong = (bits[offset + 0] & 0xff)
+				| ((long) (bits[offset + 1] & 0xff) << 8)
+				| ((long) (bits[offset + 2] & 0xff) << 16)
+				| ((long) (bits[offset + 3] & 0xff) << 24)
+				| ((long) (bits[offset + 4] & 0xff) << 32)
+				| ((long) (bits[offset + 5] & 0xff) << 40)
+				| ((long) (bits[offset + 6] & 0xff) << 48)
+				| ((long) (bits[offset + 7] & 0xff) << 56);
+
+		return valueAsLong;
+	}
+
+	public abstract long getNativeLong(int columnIndex) throws SQLException;
+
+	protected short getNativeShort(byte[] bits, int offset) {
+		short asShort = (short) ((bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8));
+
+		return asShort;
+	}
+
+	public abstract short getNativeShort(int columnIndex) throws SQLException;
+
+	protected Time getNativeTime(int columnIndex, byte[] bits, int offset,
+			int length, Calendar targetCalendar, TimeZone tz,
+			boolean rollForward, ConnectionImpl conn, ResultSetImpl rs)
+			throws SQLException {
+
+		int hour = 0;
+		int minute = 0;
+		int seconds = 0;
+
+		if (length != 0) {
+			// bits[0] // skip tm->neg
+			// binaryData.readLong(); // skip daysPart
+			hour = bits[offset + 5];
+			minute = bits[offset + 6];
+			seconds = bits[offset + 7];
+		}
+
+		Calendar sessionCalendar = rs.getCalendarInstanceForSessionOrNew();
+
+		synchronized (sessionCalendar) {
+			Time time = TimeUtil.fastTimeCreate(sessionCalendar, hour, minute,
+					seconds);
+
+			Time adjustedTime = TimeUtil.changeTimezone(conn, sessionCalendar,
+					targetCalendar, time, conn.getServerTimezoneTZ(), tz,
+					rollForward);
+
+			return adjustedTime;
+		}
+	}
+
+	public abstract Time getNativeTime(int columnIndex,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException;
+
+	protected Timestamp getNativeTimestamp(byte[] bits, int offset, int length,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException {
+		int year = 0;
+		int month = 0;
+		int day = 0;
+
+		int hour = 0;
+		int minute = 0;
+		int seconds = 0;
+
+		int nanos = 0;
+
+		if (length != 0) {
+			year = (bits[offset + 0] & 0xff) | ((bits[offset + 1] & 0xff) << 8);
+			month = bits[2];
+			day = bits[3];
+
+			if (length > 4) {
+				hour = bits[offset + 4];
+				minute = bits[offset + 5];
+				seconds = bits[offset + 6];
+			}
+
+			if (length > 7) {
+				// MySQL uses microseconds
+				nanos = ((bits[offset + 7] & 0xff)
+						| ((bits[offset + 8] & 0xff) << 8)
+						| ((bits[offset + 9] & 0xff) << 16) | ((bits[offset + 10] & 0xff) << 24)) * 1000;
+			}
+		}
+
+		if ((year == 0) && (month == 0) && (day == 0)) {
+			if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+					.equals(conn.getZeroDateTimeBehavior())) {
+
+				return null;
+			} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+					.equals(conn.getZeroDateTimeBehavior())) {
+				throw SQLError
+						.createSQLException(
+								"Value '0000-00-00' can not be represented as java.sql.Timestamp",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			year = 1;
+			month = 1;
+			day = 1;
+		}
+
+		Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn
+				.getUtcCalendar()
+				: rs.getCalendarInstanceForSessionOrNew();
+
+		synchronized (sessionCalendar) {
+			Timestamp ts = rs.fastTimestampCreate(sessionCalendar, year, month,
+					day, hour, minute, seconds, nanos);
+
+			Timestamp adjustedTs = TimeUtil.changeTimezone(conn,
+					sessionCalendar, targetCalendar, ts, conn
+							.getServerTimezoneTZ(), tz, rollForward);
+
+			return adjustedTs;
+		}
+	}
+
+	public abstract Timestamp getNativeTimestamp(int columnIndex,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException;
+
+	public abstract Reader getReader(int columnIndex) throws SQLException;
+
+	/**
+	 * Returns the value at the given column (index starts at 0) as a
+	 * java.lang.String with the requested encoding, using the given
+	 * ConnectionImpl to find character converters.
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) to return.
+	 * @param encoding
+	 *            the Java name for the character encoding
+	 * @param conn
+	 *            the connection that created this result set row
+	 * 
+	 * @return the value for the given column (including NULL if it is) as a
+	 *         String
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs while retrieving the value.
+	 */
+	public abstract String getString(int index, String encoding,
+			ConnectionImpl conn) throws SQLException;
+
+	/**
+	 * Convenience method for turning a byte[] into a string with the given
+	 * encoding.
+	 * 
+	 * @param encoding
+	 *            the Java encoding name for the byte[] -> char conversion
+	 * @param conn
+	 *            the ConnectionImpl that created the result set
+	 * @param value
+	 *            the String value as a series of bytes, encoded using
+	 *            "encoding"
+	 * @param offset
+	 *            where to start the decoding
+	 * @param length
+	 *            how many bytes to decode
+	 * 
+	 * @return the String as decoded from bytes with the given encoding
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected String getString(String encoding, ConnectionImpl conn,
+			byte[] value, int offset, int length) throws SQLException {
+		String stringVal = null;
+
+		if ((conn != null) && conn.getUseUnicode()) {
+			try {
+				if (encoding == null) {
+					stringVal = new String(value);
+				} else {
+					SingleByteCharsetConverter converter = conn
+							.getCharsetConverter(encoding);
+
+					if (converter != null) {
+						stringVal = converter.toString(value, offset, length);
+					} else {
+						stringVal = new String(value, offset, length, encoding);
+					}
+				}
+			} catch (java.io.UnsupportedEncodingException E) {
+				throw SQLError
+						.createSQLException(
+								Messages
+										.getString("ResultSet.Unsupported_character_encoding____101") //$NON-NLS-1$
+										+ encoding + "'.", "0S100");
+			}
+		} else {
+			stringVal = StringUtils.toAsciiString(value, offset, length);
+		}
+
+		return stringVal;
+	}
+
+	protected Time getTimeFast(int columnIndex, byte[] timeAsBytes, int offset,
+			int length, Calendar targetCalendar, TimeZone tz,
+			boolean rollForward, ConnectionImpl conn, ResultSetImpl rs)
+			throws SQLException {
+
+		int hr = 0;
+		int min = 0;
+		int sec = 0;
+
+		try {
+
+			if (timeAsBytes == null) {
+				return null;
+			}
+
+			boolean allZeroTime = true;
+			boolean onlyTimePresent = false;
+
+			for (int i = 0; i < length; i++) {
+				if (timeAsBytes[offset + i] == ':') {
+					onlyTimePresent = true;
+					break;
+				}
+			}
+
+			for (int i = 0; i < length; i++) {
+				byte b = timeAsBytes[offset + i];
+
+				if (b == ' ' || b == '-' || b == '/') {
+					onlyTimePresent = false;
+				}
+
+				if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/'
+						&& b != '.') {
+					allZeroTime = false;
+
+					break;
+				}
+			}
+
+			if (!onlyTimePresent && allZeroTime) {
+				if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(conn.getZeroDateTimeBehavior())) {
+					return null;
+				} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+						.equals(conn.getZeroDateTimeBehavior())) {
+					throw SQLError.createSQLException("Value '"
+							+ new String(timeAsBytes)
+							+ "' can not be represented as java.sql.Time",
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				// We're left with the case of 'round' to a time Java _can_
+				// represent, which is '00:00:00'
+				return rs.fastTimeCreate(null, 0, 0, 0);
+			}
+
+			Field timeColField = this.metadata[columnIndex];
+
+			if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) {
+
+				switch (length) {
+				case 19: { // YYYY-MM-DD hh:mm:ss
+
+					hr = StringUtils.getInt(timeAsBytes, offset + length - 8,
+							offset + length - 6);
+					min = StringUtils.getInt(timeAsBytes, offset + length - 5,
+							offset + length - 3);
+					sec = StringUtils.getInt(timeAsBytes, offset + length - 2,
+							offset + length);
+				}
+
+					break;
+				case 14:
+				case 12: {
+					hr = StringUtils.getInt(timeAsBytes, offset + length - 6,
+							offset + length - 4);
+					min = StringUtils.getInt(timeAsBytes, offset + length - 4,
+							offset + length - 2);
+					sec = StringUtils.getInt(timeAsBytes, offset + length - 2,
+							offset + length);
+				}
+
+					break;
+
+				case 10: {
+					hr = StringUtils
+							.getInt(timeAsBytes, offset + 6, offset + 8);
+					min = StringUtils.getInt(timeAsBytes, offset + 8,
+							offset + 10);
+					sec = 0;
+				}
+
+					break;
+
+				default:
+					throw SQLError
+							.createSQLException(
+									Messages
+											.getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") //$NON-NLS-1$
+											+ (columnIndex + 1)
+											+ "("
+											+ timeColField + ").",
+									SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				} /* endswitch */
+
+				SQLWarning precisionLost = new SQLWarning(
+						Messages
+								.getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") //$NON-NLS-1$
+								+ columnIndex + "(" + timeColField + ").");
+				/*
+				 * if (this.warningChain == null) { this.warningChain =
+				 * precisionLost; } else {
+				 * this.warningChain.setNextWarning(precisionLost); }
+				 */
+			} else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) {
+				hr = StringUtils.getInt(timeAsBytes, offset + 11, offset + 13);
+				min = StringUtils.getInt(timeAsBytes, offset + 14, offset + 16);
+				sec = StringUtils.getInt(timeAsBytes, offset + 17, offset + 19);
+
+				SQLWarning precisionLost = new SQLWarning(
+						Messages
+								.getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") //$NON-NLS-1$
+								+ (columnIndex + 1) + "(" + timeColField + ").");
+
+				/*
+				 * if (this.warningChain == null) { this.warningChain =
+				 * precisionLost; } else {
+				 * this.warningChain.setNextWarning(precisionLost); }
+				 */
+			} else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) {
+				return rs.fastTimeCreate(null, 0, 0, 0); // midnight on the
+				// given
+				// date
+			} else {
+				// convert a String to a Time
+				if ((length != 5) && (length != 8)) {
+					throw SQLError.createSQLException(Messages
+							.getString("ResultSet.Bad_format_for_Time____267") //$NON-NLS-1$
+							+ new String(timeAsBytes)
+							+ Messages.getString("ResultSet.___in_column__268")
+							+ (columnIndex + 1),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+				}
+
+				hr = StringUtils.getInt(timeAsBytes, offset + 0, offset + 2);
+				min = StringUtils.getInt(timeAsBytes, offset + 3, offset + 5);
+				sec = (length == 5) ? 0 : StringUtils.getInt(timeAsBytes,
+						offset + 6, offset + 8);
+			}
+
+			Calendar sessionCalendar = rs.getCalendarInstanceForSessionOrNew();
+
+			synchronized (sessionCalendar) {
+				return TimeUtil.changeTimezone(conn, sessionCalendar,
+						targetCalendar, rs.fastTimeCreate(sessionCalendar, hr,
+								min, sec), conn.getServerTimezoneTZ(), tz,
+						rollForward);
+			}
+		} catch (Exception ex) {
+			throw SQLError.createSQLException(ex.toString(),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	public abstract Time getTimeFast(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward, ConnectionImpl conn,
+			ResultSetImpl rs) throws SQLException;
+
+	protected Timestamp getTimestampFast(int columnIndex,
+			byte[] timestampAsBytes, int offset, int length,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException {
+
+		try {
+			Calendar sessionCalendar = conn.getUseJDBCCompliantTimezoneShift() ? conn
+					.getUtcCalendar()
+					: rs.getCalendarInstanceForSessionOrNew();
+
+			synchronized (sessionCalendar) {
+				boolean allZeroTimestamp = true;
+
+				boolean onlyTimePresent = false;
+
+				for (int i = 0; i < length; i++) {
+					if (timestampAsBytes[offset + i] == ':') {
+						onlyTimePresent = true;
+						break;
+					}
+				}
+
+				for (int i = 0; i < length; i++) {
+					byte b = timestampAsBytes[offset + i];
+
+					if (b == ' ' || b == '-' || b == '/') {
+						onlyTimePresent = false;
+					}
+
+					if (b != '0' && b != ' ' && b != ':' && b != '-'
+							&& b != '/' && b != '.') {
+						allZeroTimestamp = false;
+
+						break;
+					}
+				}
+
+				if (!onlyTimePresent && allZeroTimestamp) {
+
+					if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(conn.getZeroDateTimeBehavior())) {
+
+						return null;
+					} else if (ConnectionPropertiesImpl.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(conn.getZeroDateTimeBehavior())) {
+						throw SQLError
+								.createSQLException(
+										"Value '"
+												+ timestampAsBytes
+												+ "' can not be represented as java.sql.Timestamp",
+										SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+
+					// We're left with the case of 'round' to a date Java _can_
+					// represent, which is '0001-01-01'.
+					return rs.fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0);
+
+				} else if (this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+
+					return TimeUtil.changeTimezone(conn, sessionCalendar,
+							targetCalendar, rs.fastTimestampCreate(
+									sessionCalendar, StringUtils.getInt(
+											timestampAsBytes, offset, 4), 1, 1,
+									0, 0, 0, 0), conn.getServerTimezoneTZ(),
+							tz, rollForward);
+
+				} else {
+					if (timestampAsBytes[offset + length - 1] == '.') {
+						length--;
+					}
+
+					// Convert from TIMESTAMP or DATE
+					switch (length) {
+					case 26:
+					case 25:
+					case 24:
+					case 23:
+					case 22:
+					case 21:
+					case 20:
+					case 19: {
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 4);
+						int month = StringUtils.getInt(timestampAsBytes,
+								offset + 5, offset + 7);
+						int day = StringUtils.getInt(timestampAsBytes,
+								offset + 8, offset + 10);
+						int hour = StringUtils.getInt(timestampAsBytes,
+								offset + 11, offset + 13);
+						int minutes = StringUtils.getInt(timestampAsBytes,
+								offset + 14, offset + 16);
+						int seconds = StringUtils.getInt(timestampAsBytes,
+								offset + 17, offset + 19);
+
+						int nanos = 0;
+
+						if (length > 19) {
+							int decimalIndex = -1;
+
+							for (int i = 0; i < length; i++) {
+								if (timestampAsBytes[offset + i] == '.') {
+									decimalIndex = i;
+								}
+							}
+
+							if (decimalIndex != -1) {
+								if ((decimalIndex + 2) <= length) {
+									nanos = StringUtils.getInt(
+											timestampAsBytes, decimalIndex + 1,
+											offset + length);
+								} else {
+									throw new IllegalArgumentException(); // re-thrown
+									// further
+									// down
+									// with
+									// a
+									// much better error message
+								}
+							}
+						}
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar, rs.fastTimestampCreate(
+												sessionCalendar, year, month,
+												day, hour, minutes, seconds,
+												nanos), conn
+												.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					case 14: {
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 4);
+						int month = StringUtils.getInt(timestampAsBytes,
+								offset + 4, offset + 6);
+						int day = StringUtils.getInt(timestampAsBytes,
+								offset + 6, offset + 8);
+						int hour = StringUtils.getInt(timestampAsBytes,
+								offset + 8, offset + 10);
+						int minutes = StringUtils.getInt(timestampAsBytes,
+								offset + 10, offset + 12);
+						int seconds = StringUtils.getInt(timestampAsBytes,
+								offset + 12, offset + 14);
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar,
+										rs.fastTimestampCreate(sessionCalendar,
+												year, month, day, hour,
+												minutes, seconds, 0), conn
+												.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					case 12: {
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 2);
+
+						if (year <= 69) {
+							year = (year + 100);
+						}
+
+						int month = StringUtils.getInt(timestampAsBytes,
+								offset + 2, offset + 4);
+						int day = StringUtils.getInt(timestampAsBytes,
+								offset + 4, offset + 6);
+						int hour = StringUtils.getInt(timestampAsBytes,
+								offset + 6, offset + 8);
+						int minutes = StringUtils.getInt(timestampAsBytes,
+								offset + 8, offset + 10);
+						int seconds = StringUtils.getInt(timestampAsBytes,
+								offset + 10, offset + 12);
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar, rs.fastTimestampCreate(
+												sessionCalendar, year + 1900,
+												month, day, hour, minutes,
+												seconds, 0), conn
+												.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					case 10: {
+						int year;
+						int month;
+						int day;
+						int hour;
+						int minutes;
+
+						boolean hasDash = false;
+
+						for (int i = 0; i < length; i++) {
+							if (timestampAsBytes[offset + i] == '-') {
+								hasDash = true;
+								break;
+							}
+						}
+
+						if ((this.metadata[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE)
+								|| hasDash) {
+							year = StringUtils.getInt(timestampAsBytes,
+									offset + 0, offset + 4);
+							month = StringUtils.getInt(timestampAsBytes,
+									offset + 5, offset + 7);
+							day = StringUtils.getInt(timestampAsBytes,
+									offset + 8, offset + 10);
+							hour = 0;
+							minutes = 0;
+						} else {
+							year = StringUtils.getInt(timestampAsBytes,
+									offset + 0, offset + 2);
+
+							if (year <= 69) {
+								year = (year + 100);
+							}
+
+							month = StringUtils.getInt(timestampAsBytes,
+									offset + 2, offset + 4);
+							day = StringUtils.getInt(timestampAsBytes,
+									offset + 4, offset + 6);
+							hour = StringUtils.getInt(timestampAsBytes,
+									offset + 6, offset + 8);
+							minutes = StringUtils.getInt(timestampAsBytes,
+									offset + 8, offset + 10);
+
+							year += 1900; // two-digit year
+						}
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar, rs.fastTimestampCreate(
+												sessionCalendar, year, month,
+												day, hour, minutes, 0, 0), conn
+												.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					case 8: {
+						boolean hasColon = false;
+
+						for (int i = 0; i < length; i++) {
+							if (timestampAsBytes[offset + i] == ':') {
+								hasColon = true;
+								break;
+							}
+						}
+
+						if (hasColon) {
+							int hour = StringUtils.getInt(timestampAsBytes,
+									offset + 0, offset + 2);
+							int minutes = StringUtils.getInt(timestampAsBytes,
+									offset + 3, offset + 5);
+							int seconds = StringUtils.getInt(timestampAsBytes,
+									offset + 6, offset + 8);
+
+							return TimeUtil.changeTimezone(conn,
+									sessionCalendar, targetCalendar, rs
+											.fastTimestampCreate(
+													sessionCalendar, 1970, 1,
+													1, hour, minutes, seconds,
+													0), conn
+											.getServerTimezoneTZ(), tz,
+									rollForward);
+
+						}
+
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 4);
+						int month = StringUtils.getInt(timestampAsBytes,
+								offset + 4, offset + 6);
+						int day = StringUtils.getInt(timestampAsBytes,
+								offset + 6, offset + 8);
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar, rs.fastTimestampCreate(
+												sessionCalendar, year - 1900,
+												month - 1, day, 0, 0, 0, 0),
+										conn.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					case 6: {
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 2);
+
+						if (year <= 69) {
+							year = (year + 100);
+						}
+
+						int month = StringUtils.getInt(timestampAsBytes,
+								offset + 2, offset + 4);
+						int day = StringUtils.getInt(timestampAsBytes,
+								offset + 4, offset + 6);
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar, rs.fastTimestampCreate(
+												sessionCalendar, year + 1900,
+												month, day, 0, 0, 0, 0), conn
+												.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					case 4: {
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 2);
+
+						if (year <= 69) {
+							year = (year + 100);
+						}
+
+						int month = StringUtils.getInt(timestampAsBytes,
+								offset + 2, offset + 4);
+
+						return TimeUtil.changeTimezone(conn, sessionCalendar,
+								targetCalendar, rs.fastTimestampCreate(
+										sessionCalendar, year + 1900, month, 1,
+										0, 0, 0, 0),
+								conn.getServerTimezoneTZ(), tz, rollForward);
+					}
+
+					case 2: {
+						int year = StringUtils.getInt(timestampAsBytes,
+								offset + 0, offset + 2);
+
+						if (year <= 69) {
+							year = (year + 100);
+						}
+
+						return TimeUtil
+								.changeTimezone(conn, sessionCalendar,
+										targetCalendar, rs.fastTimestampCreate(
+												null, year + 1900, 1, 1, 0, 0,
+												0, 0), conn
+												.getServerTimezoneTZ(), tz,
+										rollForward);
+					}
+
+					default:
+						throw new java.sql.SQLException(
+								"Bad format for Timestamp '"
+										+ new String(timestampAsBytes)
+										+ "' in column " + (columnIndex + 1)
+										+ ".",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+			}
+		} catch (Exception e) {
+			throw new java.sql.SQLException("Cannot convert value '"
+					+ getString(columnIndex, "ISO8859_1", conn)
+					+ "' from column " + (columnIndex + 1) + " to TIMESTAMP.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	public abstract Timestamp getTimestampFast(int columnIndex,
+			Calendar targetCalendar, TimeZone tz, boolean rollForward,
+			ConnectionImpl conn, ResultSetImpl rs) throws SQLException;
+
+	/**
+	 * Could the column value at the given index (which starts at 0) be
+	 * interpreted as a floating-point number (has +/-/E/e in it)?
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) to check.
+	 * 
+	 * @return true if the column value at the given index looks like it might
+	 *         be a floating-point number, false if not.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public abstract boolean isFloatingPointNumber(int index)
+			throws SQLException;
+
+	/**
+	 * Is the column value at the given index (which starts at 0) NULL?
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) to check.
+	 * 
+	 * @return true if the column value is NULL, false if not.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public abstract boolean isNull(int index) throws SQLException;
+
+	/**
+	 * Returns the length of the column at the given index (which starts at 0).
+	 * 
+	 * @param index
+	 *            of the column value (starting at 0) for which to return the
+	 *            length.
+	 * @return the length of the requested column, 0 if null (clients of this
+	 *         interface should use isNull() beforehand to determine status of
+	 *         NULL values in the column).
+	 * 
+	 * @throws SQLException
+	 */
+	public abstract long length(int index) throws SQLException;
+
+	/**
+	 * Sets the given column value (only works currently with
+	 * ByteArrayRowHolder).
+	 * 
+	 * @param index
+	 *            index of the column value (starting at 0) to set.
+	 * @param value
+	 *            the (raw) value to set
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs, or the concrete RowHolder doesn't support
+	 *             this operation.
+	 */
+	public abstract void setColumnValue(int index, byte[] value)
+			throws SQLException;
+
+	public void setMetadata(Field[] f) throws SQLException {
+		this.metadata = f;
+	}
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/RowData.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/RowData.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/RowData.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -34,8 +34,6 @@
  * @author dgan
  */
 public interface RowData {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
 
 	/**
 	 * What's returned for the size of a result set when its size can not be
@@ -43,9 +41,6 @@
 	 */
 	public static final int RESULT_SET_SIZE_UNKNOWN = -1;
 
-	// ~ Methods
-	// ----------------------------------------------------------------
-
 	/**
 	 * Adds a row to this row data.
 	 * 
@@ -54,7 +49,7 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	void addRow(byte[][] row) throws SQLException;
+	void addRow(ResultSetRow row) throws SQLException;
 
 	/**
 	 * Moves to after last.
@@ -97,7 +92,7 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	Object[] getAt(int index) throws SQLException;
+	ResultSetRow getAt(int index) throws SQLException;
 
 	/**
 	 * Returns the current position in the result set as a row number.
@@ -111,7 +106,7 @@
 	/**
 	 * Returns the result set that 'owns' this RowData
 	 */
-	ResultSet getOwner();
+	ResultSetInternalMethods getOwner();
 
 	/**
 	 * Returns true if another row exsists.
@@ -196,7 +191,7 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	Object[] next() throws SQLException;
+	ResultSetRow next() throws SQLException;
 
 	/**
 	 * Removes the row at the given index.
@@ -224,7 +219,7 @@
 	 * @param rs
 	 *            the result set that 'owns' this RowData
 	 */
-	void setOwner(ResultSet rs);
+	void setOwner(ResultSetImpl rs);
 
 	/**
 	 * Only works on non dynamic result sets.
@@ -234,4 +229,18 @@
 	 *             if a database error occurs
 	 */
 	int size() throws SQLException;
+	
+	/**
+	 * Did this result set have no rows?
+	 */
+	boolean wasEmpty();
+	
+	/**
+	 * Sometimes the driver doesn't have metadata until after
+	 * the statement has the result set in-hand (because it's cached), 
+	 * so it can call this to set it after the fact.
+	 * 
+	 * @param metadata field-level metadata for the result set
+	 */
+	void setMetadata(Field[] metadata);
 }

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataCursor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataCursor.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataCursor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,481 @@
+/*
+ Copyright (C) 2002-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Model for result set data backed by a cursor. Only works for forward-only
+ * result sets (but still works with updatable concurrency).
+ * 
+ * @version $Id: CursorRowProvider.java,v 1.1.2.1 2005/05/19 18:31:49 mmatthews
+ *          Exp $
+ */
+public class RowDataCursor implements RowData {
+
+	private final static int BEFORE_START_OF_ROWS = -1;
+
+	/**
+	 * The cache of rows we have retrieved from the server.
+	 */
+	private List fetchedRows;
+
+	/**
+	 * Where we are positionaly in the entire result set, used mostly to
+	 * facilitate easy 'isBeforeFirst()' and 'isFirst()' methods.
+	 */
+	private int currentPositionInEntireResult = BEFORE_START_OF_ROWS;
+
+	/**
+	 * Position in cache of rows, used to determine if we need to fetch more
+	 * rows from the server to satisfy a request for the next row.
+	 */
+	private int currentPositionInFetchedRows = BEFORE_START_OF_ROWS;
+
+	/**
+	 * The result set that we 'belong' to.
+	 */
+	private ResultSetImpl owner;
+
+	/**
+	 * Have we been told from the server that we have seen the last row?
+	 */
+	private boolean lastRowFetched = false;
+
+	/**
+	 * Field-level metadata from the server. We need this, because it is not
+	 * sent for each batch of rows, but we need the metadata to unpack the
+	 * results for each field.
+	 */
+	private Field[] metadata;
+
+	/**
+	 * Communications channel to the server
+	 */
+	private MysqlIO mysql;
+
+	/**
+	 * Identifier for the statement that created this cursor.
+	 */
+	private long statementIdOnServer;
+
+	/**
+	 * The prepared statement that created this cursor.
+	 */
+	private ServerPreparedStatement prepStmt;
+
+	/**
+	 * The server status for 'last-row-sent'...This might belong in mysqldefs,
+	 * but it it only ever referenced from here.
+	 */
+	private static final int SERVER_STATUS_LAST_ROW_SENT = 128;
+
+	/**
+	 * Have we attempted to fetch any rows yet?
+	 */
+	private boolean firstFetchCompleted = false;
+
+	private boolean wasEmpty = false;
+
+	private boolean useBufferRowExplicit = false;
+	
+	/**
+	 * Creates a new cursor-backed row provider.
+	 * 
+	 * @param ioChannel
+	 *            connection to the server.
+	 * @param creatingStatement
+	 *            statement that opened the cursor.
+	 * @param metadata
+	 *            field-level metadata for the results that this cursor covers.
+	 */
+	public RowDataCursor(MysqlIO ioChannel,
+			ServerPreparedStatement creatingStatement, Field[] metadata) {
+		this.currentPositionInEntireResult = BEFORE_START_OF_ROWS;
+		this.metadata = metadata;
+		this.mysql = ioChannel;
+		this.statementIdOnServer = creatingStatement.getServerStatementId();
+		this.prepStmt = creatingStatement;
+		this.useBufferRowExplicit = MysqlIO.useBufferRowExplicit(this.metadata);
+		
+	}
+
+	/**
+	 * Returns true if we got the last element.
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isAfterLast() {
+		return lastRowFetched
+				&& this.currentPositionInFetchedRows > this.fetchedRows.size();
+	}
+
+	/**
+	 * Only works on non dynamic result sets.
+	 * 
+	 * @param index
+	 *            row number to get at
+	 * @return row data at index
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public ResultSetRow getAt(int ind) throws SQLException {
+		notSupported();
+
+		return null;
+	}
+
+	/**
+	 * Returns if iteration has not occured yet.
+	 * 
+	 * @return true if before first row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean isBeforeFirst() throws SQLException {
+		return this.currentPositionInEntireResult < 0;
+	}
+
+	/**
+	 * Moves the current position in the result set to the given row number.
+	 * 
+	 * @param rowNumber
+	 *            row to move to
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setCurrentRow(int rowNumber) throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * Returns the current position in the result set as a row number.
+	 * 
+	 * @return the current row number
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public int getCurrentRowNumber() throws SQLException {
+		return this.currentPositionInEntireResult + 1;
+	}
+
+	/**
+	 * Returns true if the result set is dynamic.
+	 * 
+	 * This means that move back and move forward won't work because we do not
+	 * hold on to the records.
+	 * 
+	 * @return true if this result set is streaming from the server
+	 */
+	public boolean isDynamic() {
+		return true;
+	}
+
+	/**
+	 * Has no records.
+	 * 
+	 * @return true if no records
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean isEmpty() throws SQLException {
+		return this.isBeforeFirst() && this.isAfterLast();
+	}
+
+	/**
+	 * Are we on the first row of the result set?
+	 * 
+	 * @return true if on first row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean isFirst() throws SQLException {
+		return this.currentPositionInEntireResult == 0;
+	}
+
+	/**
+	 * Are we on the last row of the result set?
+	 * 
+	 * @return true if on last row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean isLast() throws SQLException {
+		return this.lastRowFetched
+				&& this.currentPositionInFetchedRows == (this.fetchedRows
+						.size() - 1);
+	}
+
+	/**
+	 * Adds a row to this row data.
+	 * 
+	 * @param row
+	 *            the row to add
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void addRow(ResultSetRow row) throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * Moves to after last.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void afterLast() throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * Moves to before first.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void beforeFirst() throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * Moves to before last so next el is the last el.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void beforeLast() throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * We're done.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void close() throws SQLException {
+
+		this.metadata = null;
+		this.owner = null;
+	}
+
+	/**
+	 * Returns true if another row exists.
+	 * 
+	 * @return true if more rows
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean hasNext() throws SQLException {
+
+		if (this.fetchedRows != null && this.fetchedRows.size() == 0) {
+			return false;
+		}
+
+		if (this.owner != null && this.owner.owningStatement != null) {
+			int maxRows = this.owner.owningStatement.maxRows;
+			
+			if (maxRows != -1 && this.currentPositionInEntireResult + 1 > maxRows) {
+				return false;
+			}	
+		}
+		
+		if (this.currentPositionInEntireResult != BEFORE_START_OF_ROWS) {
+			// Case, we've fetched some rows, but are not at end of fetched
+			// block
+			if (this.currentPositionInFetchedRows < (this.fetchedRows.size() - 1)) {
+				return true;
+			} else if (this.currentPositionInFetchedRows == this.fetchedRows
+					.size()
+					&& this.lastRowFetched) {
+				return false;
+			} else {
+				// need to fetch to determine
+				fetchMoreRows();
+
+				return (this.fetchedRows.size() > 0);
+			}
+		}
+
+		// Okay, no rows _yet_, so fetch 'em
+
+		fetchMoreRows();
+
+		return this.fetchedRows.size() > 0;
+	}
+
+	/**
+	 * Moves the current position relative 'rows' from the current position.
+	 * 
+	 * @param rows
+	 *            the relative number of rows to move
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void moveRowRelative(int rows) throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * Returns the next row.
+	 * 
+	 * @return the next row value
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public ResultSetRow next() throws SQLException {
+		if (this.fetchedRows == null && this.currentPositionInEntireResult != BEFORE_START_OF_ROWS) {
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+		
+		if (!hasNext()) {
+			return null;
+		}
+		
+		this.currentPositionInEntireResult++;
+		this.currentPositionInFetchedRows++;
+
+		// Catch the forced scroll-passed-end
+		if (this.fetchedRows != null && this.fetchedRows.size() == 0) {
+			return null;
+		}
+
+		if (this.currentPositionInFetchedRows > (this.fetchedRows.size() - 1)) {
+			fetchMoreRows();
+			this.currentPositionInFetchedRows = 0;
+		}
+
+		ResultSetRow row = (ResultSetRow) this.fetchedRows
+				.get(this.currentPositionInFetchedRows);
+
+		row.setMetadata(this.metadata);
+		
+		return row;
+	}
+
+	/**
+	 * 
+	 */
+	private void fetchMoreRows() throws SQLException {
+		if (this.lastRowFetched) {
+			this.fetchedRows = new ArrayList(0);
+			return;
+		}
+
+		synchronized (this.owner.connection.getMutex()) {
+			boolean oldFirstFetchCompleted = this.firstFetchCompleted;
+			
+			if (!this.firstFetchCompleted) {
+				this.firstFetchCompleted = true;
+			}
+
+			int numRowsToFetch = this.owner.getFetchSize();
+
+			if (numRowsToFetch == 0) {
+				numRowsToFetch = this.prepStmt.getFetchSize();
+			}
+			
+			if (numRowsToFetch == Integer.MIN_VALUE) {
+				// Handle the case where the user used 'old'
+				// streaming result sets
+
+				numRowsToFetch = 1;
+			}
+
+			this.fetchedRows = this.mysql.fetchRowsViaCursor(this.fetchedRows,
+					this.statementIdOnServer, this.metadata, numRowsToFetch, 
+					this.useBufferRowExplicit);
+			this.currentPositionInFetchedRows = BEFORE_START_OF_ROWS;
+
+			if ((this.mysql.getServerStatus() & SERVER_STATUS_LAST_ROW_SENT) != 0) {
+				this.lastRowFetched = true;
+				
+				if (!oldFirstFetchCompleted && this.fetchedRows.size() == 0) {
+					this.wasEmpty  = true;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Removes the row at the given index.
+	 * 
+	 * @param index
+	 *            the row to move to
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void removeRow(int ind) throws SQLException {
+		notSupported();
+	}
+
+	/**
+	 * Only works on non dynamic result sets.
+	 * 
+	 * @return the size of this row data
+	 */
+	public int size() {
+		return RESULT_SET_SIZE_UNKNOWN;
+	}
+
+	private void nextRecord() throws SQLException {
+
+	}
+
+	private void notSupported() throws SQLException {
+		throw new OperationNotSupportedException();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.RowProvider#setOwner(com.mysql.jdbc.ResultSet)
+	 */
+	public void setOwner(ResultSetImpl rs) {
+		this.owner = rs;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.RowProvider#getOwner()
+	 */
+	public ResultSetInternalMethods getOwner() {
+		return this.owner;
+	}
+
+	public boolean wasEmpty() {
+		return this.wasEmpty;
+	}
+	
+	public void setMetadata(Field[] metadata) {
+		this.metadata = metadata;
+	}
+
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataDynamic.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataDynamic.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataDynamic.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -33,11 +33,9 @@
  * Allows streaming of MySQL data.
  * 
  * @author dgan
- * @version $Id: RowDataDynamic.java 4891 2006-02-03 19:10:02Z mmatthews $
+ * @version $Id: RowDataDynamic.java 6454 2007-06-21 17:03:53Z mmatthews $
  */
 public class RowDataDynamic implements RowData {
-	// ~ Instance fields
-	// --------------------------------------------------------
 
 	class OperationNotSupportedException extends SQLException {
 		OperationNotSupportedException() {
@@ -48,7 +46,7 @@
 
 	private int columnCount;
 
-	private Field[] fields;
+	private Field[] metadata;
 
 	private int index = -1;
 
@@ -60,22 +58,23 @@
 
 	private boolean isBinaryEncoded = false;
 
-	private Object[] nextRow;
+	private ResultSetRow nextRow;
 
-	private ResultSet owner;
+	private ResultSetImpl owner;
 
 	private boolean streamerClosed = false;
+	
+	private boolean wasEmpty = false; // we don't know until we attempt to traverse
 
-	// ~ Methods
-	// ----------------------------------------------------------------
+	private boolean useBufferRowExplicit;
 
 	/**
 	 * Creates a new RowDataDynamic object.
 	 * 
 	 * @param io
 	 *            the connection to MySQL that this data is coming from
-	 * @param fields
-	 *            the fields that describe this data
+	 * @param metadata
+	 *            the metadata that describe this data
 	 * @param isBinaryEncoded
 	 *            is this data in native format?
 	 * @param colCount
@@ -88,8 +87,9 @@
 		this.io = io;
 		this.columnCount = colCount;
 		this.isBinaryEncoded = isBinaryEncoded;
-		this.fields = fields;
-		nextRecord();
+		this.metadata = fields;
+		
+		this.useBufferRowExplicit = MysqlIO.useBufferRowExplicit(this.metadata);
 	}
 
 	/**
@@ -100,7 +100,7 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public void addRow(byte[][] row) throws SQLException {
+	public void addRow(ResultSetRow row) throws SQLException {
 		notSupported();
 	}
 
@@ -141,60 +141,99 @@
 	 *             if a database error occurs
 	 */
 	public void close() throws SQLException {
+		// Belt and suspenders here - if we don't
+		// have a reference to the connection
+		// it's more than likely dead/gone and we
+		// won't be able to consume rows anyway
 
+		Object mutex = this;
+
+		ConnectionImpl conn = null;
+
+		if (this.owner != null) {
+			conn = this.owner.connection;
+
+			if (conn != null) {
+				mutex = conn.getMutex();
+			}
+		}
+
 		boolean hadMore = false;
 		int howMuchMore = 0;
 
-		// drain the rest of the records.
-		while (this.hasNext()) {
-			this.next();
-			hadMore = true;
-			howMuchMore++;
+		synchronized (mutex) {
+			// drain the rest of the records.
+			while (next() != null) {
+				hadMore = true;
+				howMuchMore++;
 
-			if (howMuchMore % 100 == 0) {
-				Thread.yield();
+				if (howMuchMore % 100 == 0) {
+					Thread.yield();
+				}
 			}
-		}
 
-		if (this.owner != null) {
-			Connection conn = this.owner.connection;
+			if (conn != null) {
+				if (!conn.getClobberStreamingResults() && 
+						conn.getNetTimeoutForStreamingResults() > 0) {
+					String oldValue = conn
+					.getServerVariable("net_write_timeout");
 
-			if (conn != null && conn.getUseUsageAdvisor()) {
-				if (hadMore) {
+					if (oldValue == null || oldValue.length() == 0) {
+						oldValue = "60"; // the current default
+					}
 
-					ProfileEventSink eventSink = ProfileEventSink
-							.getInstance(conn);
+					this.io.clearInputStream();
+					
+					java.sql.Statement stmt = null;
+					
+					try {
+						stmt = conn.createStatement();
+						stmt.executeUpdate("SET net_write_timeout=" + oldValue);
+					} finally {
+						if (stmt != null) {
+							stmt.close();
+						}
+					}
+				}
+				
+				if (conn.getUseUsageAdvisor()) {
+					if (hadMore) {
 
-					eventSink
-							.consumeEvent(new ProfilerEvent(
-									ProfilerEvent.TYPE_WARN,
-									"", //$NON-NLS-1$
-									this.owner.owningStatement == null ? "N/A" : this.owner.owningStatement.currentCatalog, //$NON-NLS-1$
-									this.owner.connectionId,
-									this.owner.owningStatement == null ? -1
-											: this.owner.owningStatement
-													.getId(),
-									-1,
-									System.currentTimeMillis(),
-									0,
-									null,
-									null,
-									Messages.getString("RowDataDynamic.2") //$NON-NLS-1$
-											+ howMuchMore
-											+ Messages
-													.getString("RowDataDynamic.3") //$NON-NLS-1$
-											+ Messages
-													.getString("RowDataDynamic.4") //$NON-NLS-1$
-											+ Messages
-													.getString("RowDataDynamic.5") //$NON-NLS-1$
-											+ Messages
-													.getString("RowDataDynamic.6") //$NON-NLS-1$
-											+ this.owner.pointOfOrigin));
+						ProfileEventSink eventSink = ProfileEventSink
+						.getInstance(conn);
+
+						eventSink
+						.consumeEvent(new ProfilerEvent(
+								ProfilerEvent.TYPE_WARN,
+								"", //$NON-NLS-1$
+								this.owner.owningStatement == null ? "N/A" : this.owner.owningStatement.currentCatalog, //$NON-NLS-1$
+								this.owner.connectionId,
+								this.owner.owningStatement == null ? -1
+										: this.owner.owningStatement
+												.getId(),
+								-1,
+								System.currentTimeMillis(),
+								0,
+								Constants.MILLIS_I18N,
+								null,
+								null,
+								Messages.getString("RowDataDynamic.2") //$NON-NLS-1$
+										+ howMuchMore
+										+ Messages
+												.getString("RowDataDynamic.3") //$NON-NLS-1$
+										+ Messages
+												.getString("RowDataDynamic.4") //$NON-NLS-1$
+										+ Messages
+												.getString("RowDataDynamic.5") //$NON-NLS-1$
+										+ Messages
+												.getString("RowDataDynamic.6") //$NON-NLS-1$
+										+ this.owner.pointOfOrigin));
+					}
 				}
 			}
 		}
 
-		this.fields = null;
+		this.metadata = null;
 		this.owner = null;
 	}
 
@@ -207,7 +246,7 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public Object[] getAt(int ind) throws SQLException {
+	public ResultSetRow getAt(int ind) throws SQLException {
 		notSupported();
 
 		return null;
@@ -229,7 +268,7 @@
 	/**
 	 * @see com.mysql.jdbc.RowData#getOwner()
 	 */
-	public ResultSet getOwner() {
+	public ResultSetInternalMethods getOwner() {
 		return this.owner;
 	}
 
@@ -343,33 +382,50 @@
 	 * @throws SQLException
 	 *             if a database error occurs
 	 */
-	public Object[] next() throws SQLException {
-		if (this.index != Integer.MAX_VALUE) {
-			this.index++;
-		}
+	public ResultSetRow next() throws SQLException {
+		
 
-		Object[] ret = this.nextRow;
 		nextRecord();
 
-		return ret;
+		if (this.nextRow == null && !this.streamerClosed) {
+			this.io.closeStreamer(this);
+			this.streamerClosed = true;
+		}
+		
+		if (this.nextRow != null) {
+			if (this.index != Integer.MAX_VALUE) {
+				this.index++;
+			}
+		}
+		
+		return this.nextRow;
 	}
 
+
 	private void nextRecord() throws SQLException {
 
 		try {
-			if (!this.isAtEnd) {
-
-				this.nextRow = this.io.nextRow(this.fields, this.columnCount,
+			if (!this.isAtEnd) {				
+				this.nextRow = this.io.nextRow(this.metadata, this.columnCount,
 						this.isBinaryEncoded,
-						java.sql.ResultSet.CONCUR_READ_ONLY);
+						java.sql.ResultSet.CONCUR_READ_ONLY, true, 
+						this.useBufferRowExplicit, true, null);
 
 				if (this.nextRow == null) {
 					this.isAtEnd = true;
+					
+					if (this.index == -1) {
+						this.wasEmpty = true;
+					}
 				}
 			} else {
 				this.isAfterEnd = true;
 			}
 		} catch (SQLException sqlEx) {
+			if (sqlEx instanceof StreamingNotifiable) {
+				((StreamingNotifiable)sqlEx).setWasStreamingResults();
+			}
+			
 			// don't wrap SQLExceptions
 			throw sqlEx;
 		} catch (Exception ex) {
@@ -402,9 +458,6 @@
 		notSupported();
 	}
 
-	// ~ Inner Classes
-	// ----------------------------------------------------------
-
 	/**
 	 * Moves the current position in the result set to the given row number.
 	 * 
@@ -418,9 +471,9 @@
 	}
 
 	/**
-	 * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSet)
+	 * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSetInternalMethods)
 	 */
-	public void setOwner(ResultSet rs) {
+	public void setOwner(ResultSetImpl rs) {
 		this.owner = rs;
 	}
 
@@ -433,4 +486,11 @@
 		return RESULT_SET_SIZE_UNKNOWN;
 	}
 
+	public boolean wasEmpty() {
+		return this.wasEmpty;
+	}
+
+	public void setMetadata(Field[] metadata) {
+		this.metadata = metadata;
+	}
 }

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataKeyset.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataKeyset.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataKeyset.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,173 @@
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+class RowDataKeyset implements RowData {
+
+	private ResultSetInternalMethods keyset;
+	
+	private void buildKeysetColumnsClause(Field[] originalQueryMetadata) 
+		throws SQLException {
+		
+		StringBuffer buf = new StringBuffer();
+		
+		for (int i = 0; i < originalQueryMetadata.length; i++) {
+			if (originalQueryMetadata[i].isPrimaryKey()) {
+				if (buf.length() != 0) {
+					buf.append(", ");
+				}
+				
+				buf.append("`");
+				buf.append(originalQueryMetadata[i].getDatabaseName());
+				buf.append("`.`");
+				buf.append(originalQueryMetadata[i].getOriginalTableName());
+				buf.append("`.`");
+				buf.append(originalQueryMetadata[i].getOriginalName());
+				buf.append("`");
+			}
+		}
+	}
+	
+	private String extractWhereClause(String sql) {
+		String delims = "'`\"";
+
+		String canonicalSql = StringUtils.stripComments(sql, delims, delims,
+				true, false, true, true);
+
+		int whereClausePos = StringUtils.indexOfIgnoreCaseRespectMarker(0,
+				canonicalSql, " WHERE ", delims, delims, false /* fixme */);
+
+		if (whereClausePos == -1) {
+			return "";
+		}
+
+		return canonicalSql.substring(whereClausePos);
+	}
+
+	public void addRow(ResultSetRow row) throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void afterLast() throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void beforeFirst() throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void beforeLast() throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void close() throws SQLException {
+		SQLException caughtWhileClosing = null;
+		
+		if (this.keyset != null) {
+			try {
+				this.keyset.close();
+			} catch (SQLException sqlEx) {
+				caughtWhileClosing = sqlEx;
+			}
+			
+			this.keyset = null;
+		}
+		
+		if (caughtWhileClosing != null) {
+			throw caughtWhileClosing;
+		}
+	}
+
+	public ResultSetRow getAt(int index) throws SQLException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public int getCurrentRowNumber() throws SQLException {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public ResultSetInternalMethods getOwner() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public boolean hasNext() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isAfterLast() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isBeforeFirst() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isDynamic() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isEmpty() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isFirst() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isLast() throws SQLException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void moveRowRelative(int rows) throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public ResultSetRow next() throws SQLException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public void removeRow(int index) throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void setCurrentRow(int rowNumber) throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public void setOwner(ResultSetImpl rs) {
+		// TODO Auto-generated method stub
+
+	}
+
+	public int size() throws SQLException {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public boolean wasEmpty() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void setMetadata(Field[] metadata) {
+		// no-op
+	}
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataStatic.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataStatic.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/RowDataStatic.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,6 +24,7 @@
  */
 package com.mysql.jdbc;
 
+import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -31,12 +32,14 @@
  * Represents an in-memory result set
  * 
  * @author dgan
- * @version $Id: RowDataStatic.java 3726 2005-05-19 15:52:24Z mmatthews $
+ * @version $Id: RowDataStatic.java 6451 2007-06-15 01:24:27Z mmatthews $
  */
 public class RowDataStatic implements RowData {
+	private Field[] metadata;
+	
 	private int index;
 
-	ResultSet owner;
+	ResultSetImpl owner;
 
 	private List rows;
 
@@ -46,7 +49,7 @@
 	 * @param rows
 	 *            DOCUMENT ME!
 	 */
-	public RowDataStatic(ArrayList rows) {
+	public RowDataStatic(List rows) {
 		this.index = -1;
 		this.rows = rows;
 	}
@@ -57,7 +60,7 @@
 	 * @param row
 	 *            DOCUMENT ME!
 	 */
-	public void addRow(byte[][] row) {
+	public void addRow(ResultSetRow row) {
 		this.rows.add(row);
 	}
 
@@ -96,12 +99,12 @@
 	 * 
 	 * @return DOCUMENT ME!
 	 */
-	public Object[] getAt(int atIndex) {
+	public ResultSetRow getAt(int atIndex) {
 		if ((atIndex < 0) || (atIndex >= this.rows.size())) {
 			return null;
 		}
 
-		return (Object[]) this.rows.get(atIndex);
+		return (ResultSetRow) this.rows.get(atIndex);
 	}
 
 	/**
@@ -116,7 +119,7 @@
 	/**
 	 * @see com.mysql.jdbc.RowData#getOwner()
 	 */
-	public ResultSet getOwner() {
+	public ResultSetInternalMethods getOwner() {
 		return this.owner;
 	}
 
@@ -208,11 +211,14 @@
 	 * 
 	 * @return DOCUMENT ME!
 	 */
-	public Object[] next() {
+	public ResultSetRow next() throws SQLException {
 		this.index++;
 
 		if (this.index < this.rows.size()) {
-			return (Object[]) this.rows.get(this.index);
+			ResultSetRow row = (ResultSetRow) this.rows.get(this.index);
+			row.setMetadata(this.metadata);
+			
+			return row; 
 		}
 
 		return null;
@@ -239,9 +245,9 @@
 	}
 
 	/**
-	 * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSet)
+	 * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSetInternalMethods)
 	 */
-	public void setOwner(ResultSet rs) {
+	public void setOwner(ResultSetImpl rs) {
 		this.owner = rs;
 	}
 
@@ -253,4 +259,12 @@
 	public int size() {
 		return this.rows.size();
 	}
+
+	public boolean wasEmpty() {
+		return (this.rows != null && this.rows.size() == 0);
+	}
+
+	public void setMetadata(Field[] metadata) {
+		this.metadata = metadata;
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/SQLError.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/SQLError.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/SQLError.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,6 +24,9 @@
  */
 package com.mysql.jdbc;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.BindException;
 import java.sql.DataTruncation;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
@@ -39,13 +42,15 @@
 import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException;
 import com.mysql.jdbc.exceptions.MySQLSyntaxErrorException;
 import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException;
+import com.mysql.jdbc.exceptions.MySQLTransientConnectionException;
 
 /**
  * SQLError is a utility class that maps MySQL error codes to X/Open error codes
  * as is required by the JDBC spec.
  * 
  * @author Mark Matthews <mmatthew_at_worldserver.com>
- * @version $Id: SQLError.java 5122 2006-04-03 15:37:11Z mmatthews $
+ * @version $Id: SQLError.java 5122 2006-04-03 15:37:11 +0000 (Mon, 03 Apr 2006)
+ *          mmatthews $
  */
 public class SQLError {
 	static final int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196;
@@ -127,12 +132,48 @@
 	public static final String SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE = "08001"; //$NON-NLS-1$
 
 	public static final String SQL_STATE_WRONG_NO_OF_PARAMETERS = "07001"; //$NON-NLS-1$
-	
-	public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; //$NON_NLS-1$
 
+	public static final String SQL_STATE_INVALID_TRANSACTION_TERMINATION = "2D000"; // $NON_NLS-1$
+
 	private static Map sqlStateMessages;
 
+	private static final long DEFAULT_WAIT_TIMEOUT_SECONDS = 28800;
+
+	private static final int DUE_TO_TIMEOUT_FALSE = 0;
+
+	private static final int DUE_TO_TIMEOUT_MAYBE = 2;
+
+	private static final int DUE_TO_TIMEOUT_TRUE = 1;
+	
+	private static final Constructor JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR;
+	
+	private static Method THROWABLE_INIT_CAUSE_METHOD;
+	
 	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR = Class.forName(
+						"com.mysql.jdbc.exceptions.jdbc4.CommunicationsException")
+						.getConstructor(
+								new Class[] { ConnectionImpl.class, Long.TYPE, Exception.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR = null;
+		}
+		
+		try {
+			THROWABLE_INIT_CAUSE_METHOD = Throwable.class.getMethod("initCause", new Class[] {Throwable.class});
+		} catch (Throwable t) {
+			// we're not on a VM that has it
+			THROWABLE_INIT_CAUSE_METHOD = null;
+		}
+		
 		sqlStateMessages = new HashMap();
 		sqlStateMessages.put(SQL_STATE_DISCONNECT_ERROR, Messages
 				.getString("SQLError.35")); //$NON-NLS-1$
@@ -216,25 +257,25 @@
 		// ER_UNKNOWN_COM_ERROR 1047
 		// ER_IPSOCK_ERROR 1081
 		//
-		mysqlToSqlState.put(new Integer(1040), SQL_STATE_CONNECTION_REJECTED);
-		mysqlToSqlState.put(new Integer(1042), SQL_STATE_CONNECTION_REJECTED);
-		mysqlToSqlState.put(new Integer(1043), SQL_STATE_CONNECTION_REJECTED);
-		mysqlToSqlState.put(new Integer(1047),
+		mysqlToSqlState.put(Constants.integerValueOf(1040), SQL_STATE_CONNECTION_REJECTED);
+		mysqlToSqlState.put(Constants.integerValueOf(1042), SQL_STATE_CONNECTION_REJECTED);
+		mysqlToSqlState.put(Constants.integerValueOf(1043), SQL_STATE_CONNECTION_REJECTED);
+		mysqlToSqlState.put(Constants.integerValueOf(1047),
 				SQL_STATE_COMMUNICATION_LINK_FAILURE);
-		mysqlToSqlState.put(new Integer(1081),
+		mysqlToSqlState.put(Constants.integerValueOf(1081),
 				SQL_STATE_COMMUNICATION_LINK_FAILURE);
 
 		// ER_HOST_IS_BLOCKED 1129
 		// ER_HOST_NOT_PRIVILEGED 1130
-		mysqlToSqlState.put(new Integer(1129), SQL_STATE_CONNECTION_REJECTED);
-		mysqlToSqlState.put(new Integer(1130), SQL_STATE_CONNECTION_REJECTED);
+		mysqlToSqlState.put(Constants.integerValueOf(1129), SQL_STATE_CONNECTION_REJECTED);
+		mysqlToSqlState.put(Constants.integerValueOf(1130), SQL_STATE_CONNECTION_REJECTED);
 
 		//
 		// Authentication Errors
 		//
 		// ER_ACCESS_DENIED_ERROR 1045
 		//
-		mysqlToSqlState.put(new Integer(1045), SQL_STATE_INVALID_AUTH_SPEC);
+		mysqlToSqlState.put(Constants.integerValueOf(1045), SQL_STATE_INVALID_AUTH_SPEC);
 
 		//
 		// Resource errors
@@ -251,9 +292,9 @@
 		// ER_OUTOFMEMORY 1037
 		// ER_OUT_OF_SORTMEMORY 1038
 		//
-		mysqlToSqlState.put(new Integer(1037),
+		mysqlToSqlState.put(Constants.integerValueOf(1037),
 				SQL_STATE_MEMORY_ALLOCATION_FAILURE);
-		mysqlToSqlState.put(new Integer(1038),
+		mysqlToSqlState.put(Constants.integerValueOf(1038),
 				SQL_STATE_MEMORY_ALLOCATION_FAILURE);
 
 		//
@@ -262,8 +303,8 @@
 		// ER_PARSE_ERROR 1064
 		// ER_EMPTY_QUERY 1065
 		//
-		mysqlToSqlState.put(new Integer(1064), SQL_STATE_SYNTAX_ERROR);
-		mysqlToSqlState.put(new Integer(1065), SQL_STATE_SYNTAX_ERROR);
+		mysqlToSqlState.put(Constants.integerValueOf(1064), SQL_STATE_SYNTAX_ERROR);
+		mysqlToSqlState.put(Constants.integerValueOf(1065), SQL_STATE_SYNTAX_ERROR);
 
 		//
 		// Invalid argument errors
@@ -290,32 +331,32 @@
 		// ER_WRONG_FIELD_TERMINATORS 1083
 		// ER_BLOBS_AND_NO_TERMINATED 1084
 		//
-		mysqlToSqlState.put(new Integer(1055), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1056), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1057), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1059), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1060), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1061), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1062), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1063), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1066), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1067), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1068), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1069), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1070), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1071), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1072), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1073), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1074), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1075), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1082), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1083), SQL_STATE_ILLEGAL_ARGUMENT);
-		mysqlToSqlState.put(new Integer(1084), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1055), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1056), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1057), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1059), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1060), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1061), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1062), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1063), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1066), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1067), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1068), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1069), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1070), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1071), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1072), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1073), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1074), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1075), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1082), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1083), SQL_STATE_ILLEGAL_ARGUMENT);
+		mysqlToSqlState.put(Constants.integerValueOf(1084), SQL_STATE_ILLEGAL_ARGUMENT);
 
 		//
 		// ER_WRONG_VALUE_COUNT 1058
 		//
-		mysqlToSqlState.put(new Integer(1058),
+		mysqlToSqlState.put(Constants.integerValueOf(1058),
 				SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST);
 
 		// ER_CANT_CREATE_DB 1006
@@ -351,12 +392,12 @@
 		// ER_BAD_DB_ERROR 1049
 		// ER_TABLE_EXISTS_ERROR 1050
 		// ER_BAD_TABLE_ERROR 1051
-		mysqlToSqlState.put(new Integer(1051),
+		mysqlToSqlState.put(Constants.integerValueOf(1051),
 				SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND);
 
 		// ER_NON_UNIQ_ERROR 1052
 		// ER_BAD_FIELD_ERROR 1054
-		mysqlToSqlState.put(new Integer(1054), SQL_STATE_COLUMN_NOT_FOUND);
+		mysqlToSqlState.put(Constants.integerValueOf(1054), SQL_STATE_COLUMN_NOT_FOUND);
 
 		// ER_TEXTFILE_NOT_READABLE 1085
 		// ER_FILE_EXISTS_ERROR 1086
@@ -368,274 +409,274 @@
 		// ER_INSERT_INFO 1092
 		// ER_INSERT_TABLE_USED 1093
 		// ER_LOCK_DEADLOCK 1213
-		mysqlToSqlState.put(new Integer(1205), SQL_STATE_DEADLOCK);
-		mysqlToSqlState.put(new Integer(1213), SQL_STATE_DEADLOCK);
+		mysqlToSqlState.put(Constants.integerValueOf(1205), SQL_STATE_DEADLOCK);
+		mysqlToSqlState.put(Constants.integerValueOf(1213), SQL_STATE_DEADLOCK);
 
 		mysqlToSql99State = new HashMap();
 
-		mysqlToSql99State.put(new Integer(1205), SQL_STATE_DEADLOCK);
-		mysqlToSql99State.put(new Integer(1213), SQL_STATE_DEADLOCK);
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_KEY),
+		mysqlToSql99State.put(Constants.integerValueOf(1205), SQL_STATE_DEADLOCK);
+		mysqlToSql99State.put(Constants.integerValueOf(1213), SQL_STATE_DEADLOCK);
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_DUP_KEY),
 				"23000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_OUTOFMEMORY),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_OUTOFMEMORY),
 				"HY001");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_OUT_OF_SORTMEMORY), "HY001");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_CON_COUNT_ERROR), "08004");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_BAD_HOST_ERROR),
+				Constants.integerValueOf(MysqlErrorNumbers.ER_CON_COUNT_ERROR), "08004");
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_BAD_HOST_ERROR),
 				"08S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_HANDSHAKE_ERROR), "08S01");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_HANDSHAKE_ERROR), "08S01");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR), "28000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR), "42S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_BAD_TABLE_ERROR), "42S02");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NON_UNIQ_ERROR),
+				Constants.integerValueOf(MysqlErrorNumbers.ER_BAD_TABLE_ERROR), "42S02");
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_NON_UNIQ_ERROR),
 				"23000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_SERVER_SHUTDOWN), "08S01");
+				Constants.integerValueOf(MysqlErrorNumbers.ER_SERVER_SHUTDOWN), "08S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_BAD_FIELD_ERROR), "42S22");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_BAD_FIELD_ERROR), "42S22");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_FIELD_WITH_GROUP), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_GROUP_FIELD), "42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_WRONG_SUM_SELECT), "42000");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_SUM_SELECT), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_VALUE_COUNT), "21S01");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_LONG_IDENT),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_LONG_IDENT),
 				"42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_FIELDNAME),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_DUP_FIELDNAME),
 				"42S21");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_KEYNAME),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_DUP_KEYNAME),
 				"42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_ENTRY),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_DUP_ENTRY),
 				"23000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_PARSE_ERROR),
+				Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_PARSE_ERROR),
 				"42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_EMPTY_QUERY),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_EMPTY_QUERY),
 				"42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NONUNIQ_TABLE),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_NONUNIQ_TABLE),
 				"42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_INVALID_DEFAULT), "42000");
+				Constants.integerValueOf(MysqlErrorNumbers.ER_INVALID_DEFAULT), "42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_MANY_KEYS),
+				Constants.integerValueOf(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_MANY_KEYS),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TOO_MANY_KEY_PARTS), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_LONG_KEY),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_LONG_KEY),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_KEY_COLUMN_DOES_NOT_EXITS), "42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY), "42000");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TOO_BIG_FIELDLENGTH), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_WRONG_AUTO_KEY),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_AUTO_KEY),
 				"42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_FORCING_CLOSE),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_FORCING_CLOSE),
 				"08S01");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_IPSOCK_ERROR),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_IPSOCK_ERROR),
 				"08S01");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NO_SUCH_INDEX),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_NO_SUCH_INDEX),
 				"42S12");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_FIELD_TERMINATORS), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_BLOBS_AND_NO_TERMINATED), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CANT_REMOVE_ALL_FIELDS), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CANT_DROP_FIELD_OR_KEY), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_BLOB_CANT_HAVE_DEFAULT), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_WRONG_DB_NAME),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_DB_NAME),
 				"42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_WRONG_TABLE_NAME), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_BIG_SELECT),
+				Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_TABLE_NAME), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_BIG_SELECT),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_UNKNOWN_PROCEDURE), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_PROCEDURE), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_UNKNOWN_TABLE),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_UNKNOWN_TABLE),
 				"42S02");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_FIELD_SPECIFIED_TWICE), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_UNSUPPORTED_EXTENSION), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TABLE_MUST_HAVE_COLUMNS), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_UNKNOWN_CHARACTER_SET), "42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE), "42000");
+				Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE), "42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN), "42000");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NULL_COLUMN_IN_INDEX), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_PASSWORD_ANONYMOUS_USER), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_PASSWORD_NOT_ALLOWED), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_PASSWORD_NO_MATCH), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_VALUE_COUNT_ON_ROW), "21S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_INVALID_USE_OF_NULL), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_REGEXP_ERROR),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_REGEXP_ERROR),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_MIX_OF_GROUP_FUNC_AND_FIELDS), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NONEXISTING_GRANT), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TABLEACCESS_DENIED_ERROR), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_COLUMNACCESS_DENIED_ERROR), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_ILLEGAL_GRANT_FOR_TABLE), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_GRANT_WRONG_HOST_OR_USER), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NO_SUCH_TABLE),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_NO_SUCH_TABLE),
 				"42S02");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NONEXISTING_TABLE_GRANT), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NOT_ALLOWED_COMMAND), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_SYNTAX_ERROR),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_SYNTAX_ERROR),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_ABORTING_CONNECTION), "08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_PACKET_TOO_LARGE), "08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_READ_ERROR_FROM_PIPE), "08S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_NET_FCNTL_ERROR), "08S01");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_NET_FCNTL_ERROR), "08S01");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_PACKETS_OUT_OF_ORDER), "08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_UNCOMPRESS_ERROR), "08S01");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NET_READ_ERROR),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_NET_READ_ERROR),
 				"08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_READ_INTERRUPTED), "08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_ERROR_ON_WRITE), "08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NET_WRITE_INTERRUPTED), "08S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_TOO_LONG_STRING), "42000");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_LONG_STRING), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_BLOB), "42000");
 		mysqlToSql99State
-				.put(new Integer(
+				.put(Constants.integerValueOf(
 						MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_AUTO_INCREMENT),
 						"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_COLUMN_NAME), "42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_UNIQUE),
+				Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN), "42000");
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_DUP_UNIQUE),
 				"23000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_BLOB_KEY_WITHOUT_LENGTH), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_PRIMARY_CANT_HAVE_NULL), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_MANY_ROWS),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_TOO_MANY_ROWS),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_REQUIRES_PRIMARY_KEY), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CHECK_NO_SUCH_TABLE), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CHECK_NOT_IMPLEMENTED), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CANT_DO_THIS_DURING_AN_TRANSACTION),
 				"25000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NEW_ABORTING_CONNECTION), "08S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_MASTER_NET_READ), "08S01");
+				Constants.integerValueOf(MysqlErrorNumbers.ER_MASTER_NET_READ), "08S01");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_MASTER_NET_WRITE), "08S01");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_MASTER_NET_WRITE), "08S01");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TOO_MANY_USER_CONNECTIONS), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_READ_ONLY_TRANSACTION), "25000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NO_PERMISSION_TO_CREATE_USER), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_LOCK_DEADLOCK),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_LOCK_DEADLOCK),
 				"40001");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NO_REFERENCED_ROW), "23000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_ROW_IS_REFERENCED), "23000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CONNECT_TO_MASTER), "08S01");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
 				"21000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_USER_LIMIT_REACHED), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NO_DEFAULT),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_NO_DEFAULT),
 				"42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_VALUE_FOR_VAR), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_TYPE_FOR_VAR), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_CANT_USE_OPTION_HERE), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NOT_SUPPORTED_YET), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_WRONG_FK_DEF),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_WRONG_FK_DEF),
 				"42000");
 		mysqlToSql99State.put(
-				new Integer(MysqlErrorNumbers.ER_OPERAND_COLUMNS), "21000");
-		mysqlToSql99State.put(new Integer(
+				Constants.integerValueOf(MysqlErrorNumbers.ER_OPERAND_COLUMNS), "21000");
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_SUBQUERY_NO_1_ROW), "21000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_ILLEGAL_REFERENCE), "42S22");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_DERIVED_MUST_HAVE_ALIAS), "42000");
-		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_SELECT_REDUCED),
+		mysqlToSql99State.put(Constants.integerValueOf(MysqlErrorNumbers.ER_SELECT_REDUCED),
 				"01000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_TABLENAME_NOT_ALLOWED_HERE), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_NOT_SUPPORTED_AUTH_MODE), "08004");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_SPATIAL_CANT_HAVE_NULL), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_COLLATION_CHARSET_MISMATCH), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS), "01000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WARN_TOO_MANY_RECORDS), "01000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WARN_NULL_TO_NOTNULL), "01000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE), "01000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED), "01000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_NAME_FOR_INDEX), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_WRONG_NAME_FOR_CATALOG), "42000");
-		mysqlToSql99State.put(new Integer(
+		mysqlToSql99State.put(Constants.integerValueOf(
 				MysqlErrorNumbers.ER_UNKNOWN_STORAGE_ENGINE), "42000");
 	}
 
@@ -835,7 +876,7 @@
 	}
 
 	private static String mysqlToSql99(int errno) {
-		Integer err = new Integer(errno);
+		Integer err = Constants.integerValueOf(errno);
 
 		if (mysqlToSql99State.containsKey(err)) {
 			return (String) mysqlToSql99State.get(err);
@@ -861,7 +902,7 @@
 	}
 
 	private static String mysqlToXOpen(int errno) {
-		Integer err = new Integer(errno);
+		Integer err = Constants.integerValueOf(errno);
 
 		if (mysqlToSqlState.containsKey(err)) {
 			return (String) mysqlToSqlState.get(err);
@@ -883,66 +924,338 @@
 
 	public static SQLException createSQLException(String message,
 			String sqlState) {
-		if (sqlState != null) {
-			if (sqlState.startsWith("08")) {
-				return new MySQLNonTransientConnectionException(message,
-						sqlState);
+		return createSQLException(message, sqlState, 0);
+	}
+
+	public static SQLException createSQLException(String message) {
+		return new SQLException(message);
+	}
+
+	public static SQLException createSQLException(String message, String sqlState, Throwable cause) {
+		if (THROWABLE_INIT_CAUSE_METHOD == null) {
+			if (cause != null) {
+				message = message + " due to " + cause.toString();
 			}
+		}
+		
+		SQLException sqlEx = createSQLException(message, sqlState);
+		
+		if (cause != null && THROWABLE_INIT_CAUSE_METHOD != null) {
+			try {
+				THROWABLE_INIT_CAUSE_METHOD.invoke(sqlEx, new Object[] {cause});
+			} catch (Throwable t) {
+				// we're not going to muck with that here, since it's
+				// an error condition anyway!
+			}
+		}
+		
+		return sqlEx;
+	}
+	
+	public static SQLException createSQLException(String message,
+			String sqlState, int vendorErrorCode) {
+		return createSQLException(message, sqlState, vendorErrorCode, false);
+	}
+	
+	public static SQLException createSQLException(String message,
+			String sqlState, int vendorErrorCode, boolean isTransient) {
+		try {
+			if (sqlState != null) {
+				if (sqlState.startsWith("08")) {
+					if (isTransient) {
+						if (!Util.isJdbc4()) {
+							return new MySQLTransientConnectionException(
+									message, sqlState, vendorErrorCode);
+						}
 
-			if (sqlState.startsWith("22")) {
-				return new MySQLDataException(message, sqlState);
+						return (SQLException) Util
+								.getInstance(
+										"com.mysql.jdbc.exceptions.jdbc4.MySQLTransientConnectionException",
+										new Class[] { String.class,
+												String.class, Integer.TYPE },
+										new Object[] { message, sqlState,
+												Constants.integerValueOf(vendorErrorCode) });
+					}
+
+					if (!Util.isJdbc4()) {
+						return new MySQLNonTransientConnectionException(
+								message, sqlState, vendorErrorCode);
+					}
+
+					return (SQLException) Util
+							.getInstance(
+									"com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException",
+									new Class[] { String.class, String.class,
+											Integer.TYPE  }, new Object[] {
+											message, sqlState,
+											Constants.integerValueOf(vendorErrorCode) });
+				}
+
+				if (sqlState.startsWith("22")) {
+					if (!Util.isJdbc4()) {
+						return new MySQLDataException(message, sqlState,
+								vendorErrorCode);
+					}
+
+					return (SQLException) Util
+							.getInstance(
+									"com.mysql.jdbc.exceptions.jdbc4.MySQLDataException",
+									new Class[] { String.class, String.class,
+											Integer.TYPE  }, new Object[] {
+											message, sqlState,
+											Constants.integerValueOf(vendorErrorCode) });
+				}
+
+				if (sqlState.startsWith("23")) {
+
+					if (!Util.isJdbc4()) {
+						return new MySQLIntegrityConstraintViolationException(
+								message, sqlState, vendorErrorCode);
+					}
+
+					return (SQLException) Util
+							.getInstance(
+									"com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException",
+									new Class[] { String.class, String.class,
+											Integer.TYPE  }, new Object[] {
+											message, sqlState,
+											Constants.integerValueOf(vendorErrorCode) });
+				}
+
+				if (sqlState.startsWith("42")) {
+					if (!Util.isJdbc4()) {
+						return new MySQLSyntaxErrorException(message, sqlState,
+								vendorErrorCode);
+					}
+
+					return (SQLException) Util
+							.getInstance(
+									"com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException",
+									new Class[] { String.class, String.class,
+											Integer.TYPE  }, new Object[] {
+											message, sqlState,
+											Constants.integerValueOf(vendorErrorCode) });
+				}
+
+				if (sqlState.startsWith("40")) {
+					if (!Util.isJdbc4()) {
+						return new MySQLTransactionRollbackException(message,
+								sqlState, vendorErrorCode);
+					}
+
+					return (SQLException) Util
+							.getInstance(
+									"com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException",
+									new Class[] { String.class, String.class,
+											Integer.TYPE  }, new Object[] {
+											message, sqlState,
+											Constants.integerValueOf(vendorErrorCode) });
+				}
 			}
 
-			if (sqlState.startsWith("23")) {
-				return new MySQLIntegrityConstraintViolationException(message,
-						sqlState);
+			return new SQLException(message, sqlState, vendorErrorCode);
+		} catch (SQLException sqlEx) {
+			return new SQLException(
+					"Unable to create correct SQLException class instance, error class/codes may be incorrect. Reason: "
+							+ Util.stackTraceToString(sqlEx),
+					SQL_STATE_GENERAL_ERROR);
+		}
+	}
+	
+	public static SQLException createCommunicationsException(ConnectionImpl conn, long lastPacketSentTimeMs,
+			Exception underlyingException) {
+		SQLException exToReturn = null;
+		
+		if (!Util.isJdbc4()) {
+			exToReturn = new CommunicationsException(conn, lastPacketSentTimeMs, underlyingException);
+		} else {
+		
+			try {
+				exToReturn = (SQLException) Util.handleNewInstance(JDBC_4_COMMUNICATIONS_EXCEPTION_CTOR, new Object[] {
+					conn, Constants.longValueOf(lastPacketSentTimeMs), underlyingException});
+			} catch (SQLException sqlEx) {
+				// We should _never_ get this, but let's not swallow it either
+				
+				return sqlEx;
 			}
+		}
+		
+		if (THROWABLE_INIT_CAUSE_METHOD != null && underlyingException != null) {
+			try {
+				THROWABLE_INIT_CAUSE_METHOD.invoke(exToReturn, new Object[] {underlyingException});
+			} catch (Throwable t) {
+				// we're not going to muck with that here, since it's
+				// an error condition anyway!
+			}
+		}
+		
+		return exToReturn;
+	}
+	
+	/**
+	 * Creates a communications link failure message to be used
+	 * in CommunicationsException that (hopefully) has some better
+	 * information and suggestions based on heuristics.
+	 *  
+	 * @param conn
+	 * @param lastPacketSentTimeMs
+	 * @param underlyingException
+	 * @param streamingResultSetInPlay
+	 * @return
+	 */
+	public static String createLinkFailureMessageBasedOnHeuristics(
+			ConnectionImpl conn,
+			long lastPacketSentTimeMs, Exception underlyingException,
+			boolean streamingResultSetInPlay) {
+		long serverTimeoutSeconds = 0;
+		boolean isInteractiveClient = false;
 
-			if (sqlState.startsWith("42")) {
-				return new MySQLSyntaxErrorException(message, sqlState);
+		if (conn != null) {
+			isInteractiveClient = conn.getInteractiveClient();
+
+			String serverTimeoutSecondsStr = null;
+
+			if (isInteractiveClient) {
+				serverTimeoutSecondsStr = conn
+						.getServerVariable("interactive_timeout"); //$NON-NLS-1$
+			} else {
+				serverTimeoutSecondsStr = conn
+						.getServerVariable("wait_timeout"); //$NON-NLS-1$
 			}
 
-			if (sqlState.startsWith("40")) {
-				return new MySQLTransactionRollbackException(message, sqlState);
+			if (serverTimeoutSecondsStr != null) {
+				try {
+					serverTimeoutSeconds = Long
+							.parseLong(serverTimeoutSecondsStr);
+				} catch (NumberFormatException nfe) {
+					serverTimeoutSeconds = 0;
+				}
 			}
 		}
 
-		return new SQLException(message, sqlState);
-	}
+		StringBuffer exceptionMessageBuf = new StringBuffer();
 
-	public static SQLException createSQLException(String message) {
-		return new SQLException(message);
-	}
+		if (lastPacketSentTimeMs == 0) {
+			lastPacketSentTimeMs = System.currentTimeMillis();
+		}
 
-	public static SQLException createSQLException(String message,
-			String sqlState, int vendorErrorCode) {
-		if (sqlState != null) {
-			if (sqlState.startsWith("08")) {
-				return new MySQLNonTransientConnectionException(message,
-						sqlState, vendorErrorCode);
-			}
+		long timeSinceLastPacket = (System.currentTimeMillis() - lastPacketSentTimeMs) / 1000;
 
-			if (sqlState.startsWith("22")) {
-				return new MySQLDataException(message, sqlState,
-						vendorErrorCode);
+		int dueToTimeout = DUE_TO_TIMEOUT_FALSE;
+
+		StringBuffer timeoutMessageBuf = null;
+
+		if (streamingResultSetInPlay) {
+			exceptionMessageBuf.append(Messages
+					.getString("CommunicationsException.ClientWasStreaming")); //$NON-NLS-1$
+		} else {
+			if (serverTimeoutSeconds != 0) {
+				if (timeSinceLastPacket > serverTimeoutSeconds) {
+					dueToTimeout = DUE_TO_TIMEOUT_TRUE;
+
+					timeoutMessageBuf = new StringBuffer();
+
+					timeoutMessageBuf.append(Messages
+							.getString("CommunicationsException.2")); //$NON-NLS-1$
+
+					if (!isInteractiveClient) {
+						timeoutMessageBuf.append(Messages
+								.getString("CommunicationsException.3")); //$NON-NLS-1$
+					} else {
+						timeoutMessageBuf.append(Messages
+								.getString("CommunicationsException.4")); //$NON-NLS-1$
+					}
+
+				}
+			} else if (timeSinceLastPacket > DEFAULT_WAIT_TIMEOUT_SECONDS) {
+				dueToTimeout = DUE_TO_TIMEOUT_MAYBE;
+
+				timeoutMessageBuf = new StringBuffer();
+
+				timeoutMessageBuf.append(Messages
+						.getString("CommunicationsException.5")); //$NON-NLS-1$
+				timeoutMessageBuf.append(Messages
+						.getString("CommunicationsException.6")); //$NON-NLS-1$
+				timeoutMessageBuf.append(Messages
+						.getString("CommunicationsException.7")); //$NON-NLS-1$
+				timeoutMessageBuf.append(Messages
+						.getString("CommunicationsException.8")); //$NON-NLS-1$
 			}
 
-			if (sqlState.startsWith("23")) {
-				return new MySQLIntegrityConstraintViolationException(message,
-						sqlState, vendorErrorCode);
+			if (dueToTimeout == DUE_TO_TIMEOUT_TRUE
+					|| dueToTimeout == DUE_TO_TIMEOUT_MAYBE) {
+
+				exceptionMessageBuf.append(Messages
+						.getString("CommunicationsException.9")); //$NON-NLS-1$
+				exceptionMessageBuf.append(timeSinceLastPacket);
+				exceptionMessageBuf.append(Messages
+						.getString("CommunicationsException.10")); //$NON-NLS-1$
+
+				if (timeoutMessageBuf != null) {
+					exceptionMessageBuf.append(timeoutMessageBuf);
+				}
+
+				exceptionMessageBuf.append(Messages
+						.getString("CommunicationsException.11")); //$NON-NLS-1$
+				exceptionMessageBuf.append(Messages
+						.getString("CommunicationsException.12")); //$NON-NLS-1$
+				exceptionMessageBuf.append(Messages
+						.getString("CommunicationsException.13")); //$NON-NLS-1$
+
+			} else {
+				//
+				// Attempt to determine the reason for the underlying exception
+				// (we can only make a best-guess here)
+				//
+
+				if (underlyingException instanceof BindException) {
+					if (conn.getLocalSocketAddress() != null
+							&& !Util.interfaceExists(conn
+									.getLocalSocketAddress())) {
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.19a")); //$NON-NLS-1$
+					} else {
+						// too many client connections???
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.14")); //$NON-NLS-1$
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.15")); //$NON-NLS-1$
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.16")); //$NON-NLS-1$
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.17")); //$NON-NLS-1$
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.18")); //$NON-NLS-1$
+						exceptionMessageBuf.append(Messages
+								.getString("CommunicationsException.19")); //$NON-NLS-1$
+					}
+				}
 			}
+		}
 
-			if (sqlState.startsWith("42")) {
-				return new MySQLSyntaxErrorException(message, sqlState,
-						vendorErrorCode);
+		if (exceptionMessageBuf.length() == 0) {
+			// We haven't figured out a good reason, so copy it.
+			exceptionMessageBuf.append(Messages
+					.getString("CommunicationsException.20")); //$NON-NLS-1$
+
+			if (THROWABLE_INIT_CAUSE_METHOD == null && 
+					underlyingException != null) {
+				exceptionMessageBuf.append(Messages
+						.getString("CommunicationsException.21")); //$NON-NLS-1$
+				exceptionMessageBuf.append(Util
+						.stackTraceToString(underlyingException));
 			}
 
-			if (sqlState.startsWith("40")) {
-				return new MySQLTransactionRollbackException(message, sqlState,
-						vendorErrorCode);
+			if (conn != null && conn.getMaintainTimeStats()
+					&& !conn.getParanoid()) {
+				exceptionMessageBuf
+						.append("\n\nLast packet sent to the server was ");
+				exceptionMessageBuf.append(System.currentTimeMillis()
+						- lastPacketSentTimeMs);
+				exceptionMessageBuf.append(" ms ago.");
 			}
 		}
-
-		return new SQLException(message, sqlState, vendorErrorCode);
+		
+		return exceptionMessageBuf.toString();
 	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/ServerPreparedStatement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/ServerPreparedStatement.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/ServerPreparedStatement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,17 +24,18 @@
  */
 package com.mysql.jdbc;
 
-import com.mysql.jdbc.Statement.CancelTask;
+import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
 import com.mysql.jdbc.exceptions.MySQLTimeoutException;
 import com.mysql.jdbc.profiler.ProfileEventSink;
 import com.mysql.jdbc.profiler.ProfilerEvent;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
 import java.io.UnsupportedEncodingException;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 
 import java.net.URL;
@@ -45,15 +46,15 @@
 import java.sql.Date;
 import java.sql.ParameterMetaData;
 import java.sql.Ref;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
 
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.Calendar;
-import java.util.Locale;
+import java.util.Properties;
 import java.util.TimeZone;
 
 /**
@@ -64,6 +65,27 @@
  *          mmatthews Exp $
  */
 public class ServerPreparedStatement extends PreparedStatement {
+	private static final Constructor JDBC_4_SPS_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_SPS_CTOR = Class.forName("com.mysql.jdbc.JDBC4ServerPreparedStatement")
+				.getConstructor(
+				new Class[] { ConnectionImpl.class, String.class, String.class,
+						Integer.TYPE, Integer.TYPE});
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			} 
+		} else {
+			JDBC_4_SPS_CTOR = null;
+		}
+	}
+	
 	protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192;
 
 	static class BatchedBindValues {
@@ -80,11 +102,11 @@
 		}
 	}
 
-	static class BindValue {
+	public static class BindValue {
 
 		long boundBeforeExecutionNum = 0;
 		
-		long bindLength; /* Default length of data */
+		public long bindLength; /* Default length of data */
 
 		int bufferType; /* buffer type */
 
@@ -96,9 +118,9 @@
 
 		int intBinding;
 
-		boolean isLongData; /* long data indicator */
+		public boolean isLongData; /* long data indicator */
 
-		boolean isNull; /* NULL indicator */
+		public boolean isNull; /* NULL indicator */
 
 		boolean isSet = false; /* has this parameter been set? */
 
@@ -106,7 +128,7 @@
 
 		short shortBinding;
 
-		Object value; /* The value to store */
+		public Object value; /* The value to store */
 
 		BindValue() {
 		}
@@ -272,6 +294,42 @@
 	private boolean serverNeedsResetBeforeEachExecution;
 
 	/**
+	 * Creates a prepared statement instance -- We need to provide factory-style
+	 * methods so we can support both JDBC3 (and older) and JDBC4 runtimes,
+	 * otherwise the class verifier complains when it tries to load JDBC4-only
+	 * interface classes that are present in JDBC4 method signatures.
+	 */
+
+	protected static ServerPreparedStatement getInstance(ConnectionImpl conn,
+			String sql, String catalog, int resultSetType,
+			int resultSetConcurrency) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new ServerPreparedStatement(conn, sql, catalog,
+					resultSetType, resultSetConcurrency);
+		}
+
+		try {
+			return (ServerPreparedStatement) JDBC_4_SPS_CTOR.newInstance(new Object[] { conn,
+					sql, catalog, Constants.integerValueOf(resultSetType),
+					Constants.integerValueOf(resultSetConcurrency) });
+		} catch (IllegalArgumentException e) {
+			throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (InstantiationException e) {
+			throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (IllegalAccessException e) {
+			throw new SQLException(e.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (InvocationTargetException e) {
+			Throwable target = e.getTargetException(); 
+			
+			if (target instanceof SQLException) {
+				throw (SQLException)target;
+			}
+			
+			throw new SQLException(target.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	/**
 	 * Creates a new ServerPreparedStatement object.
 	 * 
 	 * @param conn
@@ -284,16 +342,19 @@
 	 * @throws SQLException
 	 *             If an error occurs
 	 */
-	public ServerPreparedStatement(Connection conn, String sql, String catalog,
+	protected ServerPreparedStatement(ConnectionImpl conn, String sql, String catalog,
 			int resultSetType, int resultSetConcurrency)
 			throws SQLException {
 		super(conn, catalog);
 
 		checkNullOrEmptyQuery(sql);
 
-		this.isSelectQuery = StringUtils.startsWithIgnoreCaseAndWs(sql,
-				"SELECT"); //$NON-NLS-1$
+		int startOfStatement = findStartOfStatement(sql);
 		
+		this.firstCharOfStmt = StringUtils.firstAlphaCharUc(sql, startOfStatement);
+		
+		this.isSelectQuery = 'S' == this.firstCharOfStmt;
+		
 		if (this.connection.versionMeetsMinimum(5, 0, 0)) {
 			this.serverNeedsResetBeforeEachExecution = 
 				!this.connection.versionMeetsMinimum(5, 0, 3);
@@ -302,11 +363,15 @@
 				!this.connection.versionMeetsMinimum(4, 1, 10);
 		}
 		
+		this.useAutoSlowLog = this.connection.getAutoSlowLog();
 		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
 		this.hasLimitClause = (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1); //$NON-NLS-1$
-		this.firstCharOfStmt = StringUtils.firstNonWsCharUc(sql);
-		this.originalSql = sql;
+		
+		String statementComment = this.connection.getStatementComment();
 
+		this.originalSql = (statementComment == null) ? sql : "/* "
+				+ statementComment + " */ " + sql;
+
 		if (this.connection.versionMeetsMinimum(4, 1, 2)) {
 			this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING;
 		} else {
@@ -328,6 +393,8 @@
 		
 		setResultSetType(resultSetType);
 		setResultSetConcurrency(resultSetConcurrency);
+		
+		this.parameterTypes = new int[this.parameterCount];
 	}
 
 	/**
@@ -336,7 +403,7 @@
 	 * @exception SQLException
 	 *                if a database-access error occurs.
 	 * 
-	 * @see Statement#addBatch
+	 * @see StatementImpl#addBatch
 	 */
 	public synchronized void addBatch() throws SQLException {
 		checkClosed();
@@ -357,7 +424,7 @@
 		PreparedStatement pStmtForSub = null;
 
 		try {
-			pStmtForSub = new PreparedStatement(this.connection,
+			pStmtForSub = PreparedStatement.getInstance(this.connection,
 					this.originalSql, this.currentCatalog);
 
 			int numParameters = pStmtForSub.parameterCount;
@@ -460,6 +527,8 @@
 
 	protected boolean isCached = false;
 
+	private boolean useAutoSlowLog;
+
 	protected void setClosed(boolean flag) {
 		this.isClosed = flag;
 	}
@@ -634,8 +703,8 @@
 
 										while (rs.next()) {
 											this.batchedGeneratedKeys
-													.add(new byte[][] { rs
-															.getBytes(1) });
+													.add(new ByteArrayRow(new byte[][] { rs
+															.getBytes(1) }));
 										}
 									} finally {
 										if (rs != null) {
@@ -682,15 +751,17 @@
 	 * @see com.mysql.jdbc.PreparedStatement#executeInternal(int,
 	 *      com.mysql.jdbc.Buffer, boolean, boolean)
 	 */
-	protected com.mysql.jdbc.ResultSet executeInternal(int maxRowsToRetrieve,
+	protected com.mysql.jdbc.ResultSetInternalMethods executeInternal(int maxRowsToRetrieve,
 			Buffer sendPacket, boolean createStreamingResultSet,
-			boolean queryIsSelectOnly, boolean unpackFields, boolean isBatch)
+			boolean queryIsSelectOnly, Field[] metadataFromCache,
+			boolean isBatch)
 			throws SQLException {
 		this.numberOfExecutions++;
 
 		// We defer to server-side execution
 		try {
-			return serverExecute(maxRowsToRetrieve, createStreamingResultSet);
+			return serverExecute(maxRowsToRetrieve, createStreamingResultSet, 
+					metadataFromCache);
 		} catch (SQLException sqlEx) {
 			// don't wrap SQLExceptions
 			if (this.connection.getEnablePacketDebug()) {
@@ -705,7 +776,7 @@
 						.append("\n\nQuery being executed when exception was thrown:\n\n");
 				messageBuf.append(extractedSql);
 
-				sqlEx = Connection.appendMessageToException(sqlEx, messageBuf
+				sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf
 						.toString());
 			}
 
@@ -726,7 +797,7 @@
 						.append("\n\nQuery being executed when exception was thrown:\n\n");
 				messageBuf.append(extractedSql);
 
-				sqlEx = Connection.appendMessageToException(sqlEx, messageBuf
+				sqlEx = ConnectionImpl.appendMessageToException(sqlEx, messageBuf
 						.toString());
 			}
 
@@ -751,7 +822,16 @@
 		return null; // we don't use this type of packet
 	}
 
-	private BindValue getBinding(int parameterIndex, boolean forLongData)
+	/**
+	 * Returns the structure representing the value that (can be)/(is)
+	 * bound at the given parameter index.
+	 * 
+	 * @param parameterIndex 1-based
+	 * @param forLongData is this for a stream?
+	 * @return
+	 * @throws SQLException
+	 */
+	protected BindValue getBinding(int parameterIndex, boolean forLongData)
 			throws SQLException {
 		checkClosed();
 		
@@ -878,52 +958,49 @@
 			if (this.connection.getAutoGenerateTestcaseScript()) {
 				dumpCloseForTestcase();
 			}
-			
-			synchronized (this.connection.getMutex()) {
-	
-				//
-				// Don't communicate with the server if we're being
-				// called from the finalizer...
-				// 
-				// This will leak server resources, but if we don't do this,
-				// we'll deadlock (potentially, because there's no guarantee
-				// when, what order, and what concurrency finalizers will be
-				// called with). Well-behaved programs won't rely on finalizers
-				// to clean up their statements.
-				//
-				
-				SQLException exceptionDuringClose = null;
-				
 
-				if (calledExplicitly) {
+			//
+			// Don't communicate with the server if we're being
+			// called from the finalizer...
+			// 
+			// This will leak server resources, but if we don't do this,
+			// we'll deadlock (potentially, because there's no guarantee
+			// when, what order, and what concurrency finalizers will be
+			// called with). Well-behaved programs won't rely on finalizers
+			// to clean up their statements.
+			//
+
+			SQLException exceptionDuringClose = null;
+
+			if (calledExplicitly && !this.connection.isClosed()) {
+				synchronized (this.connection.getMutex()) {
 					try {
-						
+
 						MysqlIO mysql = this.connection.getIO();
-						
+
 						Buffer packet = mysql.getSharedSendPacket();
-						
+
 						packet.writeByte((byte) MysqlDefs.COM_CLOSE_STATEMENT);
 						packet.writeLong(this.serverStatementId);
-						
+
 						mysql.sendCommand(MysqlDefs.COM_CLOSE_STATEMENT, null,
 								packet, true, null);
 					} catch (SQLException sqlEx) {
 						exceptionDuringClose = sqlEx;
 					}
-					
 				}
+			}
 
-				super.realClose(calledExplicitly, closeOpenResults);
+			super.realClose(calledExplicitly, closeOpenResults);
 
-				clearParametersInternal(false);
-				this.parameterBindings = null;
-				
-				this.parameterFields = null;
-				this.resultFields = null;
-				
-				if (exceptionDuringClose != null) {
-					throw exceptionDuringClose;
-				}
+			clearParametersInternal(false);
+			this.parameterBindings = null;
+
+			this.parameterFields = null;
+			this.resultFields = null;
+
+			if (exceptionDuringClose != null) {
+				throw exceptionDuringClose;
 			}
 		}
 	}
@@ -1009,8 +1086,9 @@
 	 * 
 	 * @throws SQLException
 	 */
-	private com.mysql.jdbc.ResultSet serverExecute(int maxRowsToRetrieve,
-			boolean createStreamingResultSet) throws SQLException {
+	private com.mysql.jdbc.ResultSetInternalMethods serverExecute(int maxRowsToRetrieve,
+			boolean createStreamingResultSet, 
+			Field[] metadataFromCache) throws SQLException {
 		synchronized (this.connection.getMutex()) {
 			if (this.detectedLongParameterSwitch) {
 				// Check when values were bound
@@ -1148,20 +1226,23 @@
 
 			long begin = 0;
 
-			if (this.connection.getProfileSql()
-					|| this.connection.getLogSlowQueries()
-					|| this.connection.getGatherPerformanceMetrics()) {
-				begin = System.currentTimeMillis();
+			boolean logSlowQueries = this.connection.getLogSlowQueries();
+			boolean gatherPerformanceMetrics = this.connection
+					.getGatherPerformanceMetrics();
+
+			if (this.profileSQL || logSlowQueries || gatherPerformanceMetrics) {
+				begin = mysql.getCurrentTimeNanosOrMillis();
 			}
 
-			this.wasCancelled = false;
+			resetCancelledState();
 			
 			CancelTask timeoutTask = null;
 
 			try {
-				if (this.timeoutInMillis != 0
+				if (this.connection.getEnableQueryTimeouts() &&
+						this.timeoutInMillis != 0
 						&& this.connection.versionMeetsMinimum(5, 0, 0)) {
-					timeoutTask = new CancelTask();
+					timeoutTask = new CancelTask(this);
 					this.connection.getCancelTimer().schedule(timeoutTask, 
 							this.timeoutInMillis);
 				}
@@ -1169,83 +1250,136 @@
 				Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE,
 					null, packet, false, null);
 				
+				long queryEndTime = 0L;
+				
+				if (logSlowQueries || gatherPerformanceMetrics || this.profileSQL) {
+					queryEndTime = mysql.getCurrentTimeNanosOrMillis();
+				}
+				
 				if (timeoutTask != null) {
 					timeoutTask.cancel();
+					
+					if (timeoutTask.caughtWhileCancelling != null) {
+						throw timeoutTask.caughtWhileCancelling;
+					}
+					
 					timeoutTask = null;
 				}
 				
-				if (this.wasCancelled) {
-					this.wasCancelled = false;
-					throw new MySQLTimeoutException();
+				synchronized (this.cancelTimeoutMutex) {
+					if (this.wasCancelled) {
+						SQLException cause = null;
+						
+						if (this.wasCancelledByTimeout) {
+							cause = new MySQLTimeoutException();
+						} else {
+							cause = new MySQLStatementCancelledException();
+						}
+						
+						resetCancelledState();
+						
+						throw cause;
+					}
 				}
-			
-				this.connection.incrementNumberOfPreparedExecutes();
-	
-				if (this.connection.getProfileSql()) {
-					this.eventSink = ProfileEventSink.getInstance(this.connection);
-	
-					this.eventSink.consumeEvent(new ProfilerEvent(
-							ProfilerEvent.TYPE_EXECUTE, "", this.currentCatalog, //$NON-NLS-1$
-							this.connectionId, this.statementId, -1, System
-									.currentTimeMillis(), (int) (System
-									.currentTimeMillis() - begin), null,
-							new Throwable(), truncateQueryToLog(asSql(true))));
-				}
-	
-				com.mysql.jdbc.ResultSet rs = mysql.readAllResults(this,
-						maxRowsToRetrieve, this.resultSetType,
-						this.resultSetConcurrency, createStreamingResultSet,
-						this.currentCatalog, resultPacket, true, this.fieldCount,
-						true);
-	
+
+				boolean queryWasSlow = false;
 				
-				if (!createStreamingResultSet && 
-						this.serverNeedsResetBeforeEachExecution) {
-					serverResetStatement(); // clear any long data...
-				}
-	
-				this.sendTypesToServer = false;
-				this.results = rs;
-	
-				if (this.connection.getLogSlowQueries()
-						|| this.connection.getGatherPerformanceMetrics()) {
-					long elapsedTime = System.currentTimeMillis() - begin;
-	
-					if (this.connection.getLogSlowQueries()
-							&& (elapsedTime >= this.connection
-									.getSlowQueryThresholdMillis())) {
+				if (logSlowQueries || gatherPerformanceMetrics) {
+					long elapsedTime = queryEndTime - begin;
+
+					if (logSlowQueries) {
+		    			if (this.useAutoSlowLog) {
+		    				queryWasSlow = elapsedTime > this.connection.getSlowQueryThresholdMillis();
+		    			} else {
+		    				queryWasSlow = this.connection.isAbonormallyLongQuery(elapsedTime);
+		    				
+		    				this.connection.reportQueryTime(elapsedTime);
+		    			}
+					}
+
+					if (queryWasSlow) {
+						
 						StringBuffer mesgBuf = new StringBuffer(
 								48 + this.originalSql.length());
 						mesgBuf.append(Messages
 								.getString("ServerPreparedStatement.15")); //$NON-NLS-1$
-						mesgBuf.append(this.connection
-								.getSlowQueryThresholdMillis());
+						mesgBuf.append(mysql.getSlowQueryThreshold());
 						mesgBuf.append(Messages
 								.getString("ServerPreparedStatement.15a")); //$NON-NLS-1$
 						mesgBuf.append(elapsedTime);
 						mesgBuf.append(Messages
 								.getString("ServerPreparedStatement.16")); //$NON-NLS-1$
-						
+
 						mesgBuf.append("as prepared: ");
 						mesgBuf.append(this.originalSql);
 						mesgBuf.append("\n\n with parameters bound:\n\n");
 						mesgBuf.append(asSql(true));
-	
-						this.connection.getLog().logWarn(mesgBuf.toString());
-	
-						if (this.connection.getExplainSlowQueries()) {
-							String queryAsString = asSql(true);
-	
-							mysql.explainSlowQuery(queryAsString.getBytes(),
-									queryAsString);
-						}
+
+						this.eventSink
+								.consumeEvent(new ProfilerEvent(
+										ProfilerEvent.TYPE_SLOW_QUERY,
+										"", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$
+										getId(), 0, System.currentTimeMillis(),
+										elapsedTime, mysql
+												.getQueryTimingUnits(), null,
+										new Throwable(), mesgBuf.toString()));
 					}
-	
-					if (this.connection.getGatherPerformanceMetrics()) {
+
+					if (gatherPerformanceMetrics) {
 						this.connection.registerQueryExecutionTime(elapsedTime);
 					}
 				}
+
+				this.connection.incrementNumberOfPreparedExecutes();
+
+				if (this.profileSQL) {
+					this.eventSink = ProfileEventSink
+							.getInstance(this.connection);
+
+					this.eventSink.consumeEvent(new ProfilerEvent(
+							ProfilerEvent.TYPE_EXECUTE,
+							"", this.currentCatalog, //$NON-NLS-1$
+							this.connectionId, this.statementId, -1, System
+									.currentTimeMillis(), (int) (mysql
+									.getCurrentTimeNanosOrMillis() - begin),
+							mysql.getQueryTimingUnits(), null, new Throwable(),
+							truncateQueryToLog(asSql(true))));
+				}
+	
+				com.mysql.jdbc.ResultSetInternalMethods rs = mysql.readAllResults(this,
+						maxRowsToRetrieve, this.resultSetType,
+						this.resultSetConcurrency, createStreamingResultSet,
+						this.currentCatalog, resultPacket, true, this.fieldCount,
+						metadataFromCache);
 				
+				if (this.profileSQL) {
+					long fetchEndTime = mysql.getCurrentTimeNanosOrMillis();
+
+					this.eventSink.consumeEvent(new ProfilerEvent(
+							ProfilerEvent.TYPE_FETCH,
+							"", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$
+							getId(), 0 /* FIXME rs.resultId */, System.currentTimeMillis(),
+							(fetchEndTime - queryEndTime), mysql
+									.getQueryTimingUnits(), null,
+							new Throwable(), null));
+				}
+	
+				if (queryWasSlow && this.connection.getExplainSlowQueries()) {
+					String queryAsString = asSql(true);
+
+					mysql.explainSlowQuery(queryAsString.getBytes(),
+							queryAsString);
+				}
+				
+				if (!createStreamingResultSet && 
+						this.serverNeedsResetBeforeEachExecution) {
+					serverResetStatement(); // clear any long data...
+				}
+	
+				
+				this.sendTypesToServer = false;
+				this.results = rs;
+	
 				if (mysql.hadWarnings()) {
 		            mysql.scanForAndThrowDataTruncation();
 		        }
@@ -1259,7 +1393,6 @@
 		}
 	}
 
-
 	/**
 	 * Sends stream-type data parameters to the server.
 	 * 
@@ -1378,16 +1511,14 @@
 
 				this.connection.incrementNumberOfPrepares();
 
-				if (this.connection.getProfileSql()) {
-					this.eventSink = ProfileEventSink
-							.getInstance(this.connection);
-
+				if (this.profileSQL) {
 					this.eventSink.consumeEvent(new ProfilerEvent(
 							ProfilerEvent.TYPE_PREPARE,
 							"", this.currentCatalog, //$NON-NLS-1$
 							this.connectionId, this.statementId, -1,
-							System.currentTimeMillis(), (int) (System
-									.currentTimeMillis() - begin), null,
+							System.currentTimeMillis(), 
+							mysql.getCurrentTimeNanosOrMillis() - begin, 
+							mysql.getQueryTimingUnits(), null,
 							new Throwable(), truncateQueryToLog(sql)));
 				}
 
@@ -1432,7 +1563,7 @@
 							.append("\n\nQuery being prepared when exception was thrown:\n\n");
 					messageBuf.append(this.originalSql);
 
-					sqlEx = Connection.appendMessageToException(sqlEx,
+					sqlEx = ConnectionImpl.appendMessageToException(sqlEx,
 							messageBuf.toString());
 				}
 
@@ -1540,7 +1671,8 @@
 				setType(binding, this.stringTypeCode);
 			}
 
-			binding.value = StringUtils.fixDecimalExponent(x.toString());
+			binding.value = StringUtils
+				.fixDecimalExponent(StringUtils.consistentToString(x));
 			binding.isNull = false;
 			binding.isLongData = false;
 		}
@@ -1896,7 +2028,7 @@
 	 */
 	public void setTime(int parameterIndex, java.sql.Time x)
 			throws SQLException {
-		setTimeInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+		setTimeInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false);
 	}
 
 	/**
@@ -1973,7 +2105,7 @@
 	 */
 	public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
 			throws SQLException {
-		setTimestampInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+		setTimestampInternal(parameterIndex, x, null, this.connection.getDefaultTimeZone(), false);
 	}
 
 	/**
@@ -2023,7 +2155,7 @@
 		}
 	}
 
-	private void setType(BindValue oldValue, int bufferType) {
+	protected void setType(BindValue oldValue, int bufferType) {
 		if (oldValue.bufferType != bufferType) {
 			this.sendTypesToServer = true;
 		}
@@ -2217,12 +2349,12 @@
 
 				byte length = (byte) 7;
 
-				intoBuf.ensureCapacity(length);
-
 				if (dt instanceof java.sql.Timestamp) {
 					length = (byte) 11;
 				}
 
+				intoBuf.ensureCapacity(length);
+				
 				intoBuf.writeByte(length); // length
 
 				int year = sessionCalendar.get(Calendar.YEAR);
@@ -2247,7 +2379,8 @@
 				}
 
 				if (length == 11) {
-					intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos());
+					//	MySQL expects microseconds, not nanos
+					intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos() / 1000);
 				}
 			
 			} finally {
@@ -2442,4 +2575,6 @@
 	protected long getServerStatementId() {
 		return serverStatementId;
 	}
+
+
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/SingleByteCharsetConverter.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/SingleByteCharsetConverter.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/SingleByteCharsetConverter.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -178,7 +178,27 @@
 
 		return bytes;
 	}
+	
+	public final byte[] toBytesWrapped(char[] c, char beginWrap, char endWrap) {
+		if (c == null) {
+			return null;
+		}
 
+		int length = c.length + 2;
+		int charLength = c.length;
+		
+		byte[] bytes = new byte[length];
+		bytes[0] = this.charToByteMap[beginWrap];
+		
+		for (int i = 0; i < charLength; i++) {
+			bytes[i + 1] = this.charToByteMap[c[i]];
+		}
+		
+		bytes[length - 1] = this.charToByteMap[endWrap];
+
+		return bytes;
+	}
+
 	public final byte[] toBytes(char[] chars, int offset, int length) {
 		if (chars == null) {
 			return null;
@@ -218,7 +238,29 @@
 
 		return bytes;
 	}
+	
+	public final byte[] toBytesWrapped(String s, char beginWrap, char endWrap) {
+		if (s == null) {
+			return null;
+		}
 
+		int stringLength = s.length();
+		
+		int length = stringLength + 2;
+		
+		byte[] bytes = new byte[length];
+		
+		bytes[0] = this.charToByteMap[beginWrap];
+		
+		for (int i = 0; i < stringLength; i++) {
+			bytes[i + 1] = this.charToByteMap[s.charAt(i)];
+		}
+
+		bytes[length - 1] = this.charToByteMap[endWrap];
+		
+		return bytes;
+	}
+
 	/**
 	 * Convert the given string to an array of bytes.
 	 * 

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/StandardSocketFactory.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/StandardSocketFactory.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/StandardSocketFactory.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -42,9 +42,42 @@
  * @author Mark Matthews
  */
 public class StandardSocketFactory implements SocketFactory {
-	// ~ Instance fields
-	// --------------------------------------------------------
 
+	public static final String TCP_NO_DELAY_PROPERTY_NAME = "tcpNoDelay";
+
+	public static final String TCP_KEEP_ALIVE_DEFAULT_VALUE = "true";
+
+	public static final String TCP_KEEP_ALIVE_PROPERTY_NAME = "tcpKeepAlive";
+
+	public static final String TCP_RCV_BUF_PROPERTY_NAME = "tcpRcvBuf";
+
+	public static final String TCP_SND_BUF_PROPERTY_NAME = "tcpSndBuf";
+
+	public static final String TCP_TRAFFIC_CLASS_PROPERTY_NAME = "tcpTrafficClass";
+
+	public static final String TCP_RCV_BUF_DEFAULT_VALUE = "0";
+
+	public static final String TCP_SND_BUF_DEFAULT_VALUE = "0";
+
+	public static final String TCP_TRAFFIC_CLASS_DEFAULT_VALUE = "0";
+
+	public static final String TCP_NO_DELAY_DEFAULT_VALUE = "true";
+
+	/** Use reflection for pre-1.4 VMs */
+
+	private static Method setTraficClassMethod;
+
+	static {
+		try {
+			setTraficClassMethod = Socket.class.getMethod("setTrafficClass",
+					new Class[] { Integer.TYPE });
+		} catch (SecurityException e) {
+			setTraficClassMethod = null;
+		} catch (NoSuchMethodException e) {
+			setTraficClassMethod = null;
+		}
+	}
+
 	/** The hostname to connect to */
 	protected String host = null;
 
@@ -54,9 +87,6 @@
 	/** The underlying TCP/IP socket to use */
 	protected Socket rawSocket = null;
 
-	// ~ Methods
-	// ----------------------------------------------------------------
-
 	/**
 	 * Called by the driver after issuing the MySQL protocol handshake and
 	 * reading the results of the handshake.
@@ -88,6 +118,56 @@
 	}
 
 	/**
+	 * Configures socket properties based on properties from the connection
+	 * (tcpNoDelay, snd/rcv buf, traffic class, etc).
+	 * 
+	 * @param props
+	 * @throws SocketException
+	 * @throws IOException
+	 */
+	private void configureSocket(Socket sock, Properties props) throws SocketException,
+			IOException {
+		try {
+			sock.setTcpNoDelay(Boolean.valueOf(
+					props.getProperty(TCP_NO_DELAY_PROPERTY_NAME,
+							TCP_NO_DELAY_DEFAULT_VALUE)).booleanValue());
+
+			String keepAlive = props.getProperty(TCP_KEEP_ALIVE_PROPERTY_NAME,
+					TCP_KEEP_ALIVE_DEFAULT_VALUE);
+
+			if (keepAlive != null && keepAlive.length() > 0) {
+				sock.setKeepAlive(Boolean.valueOf(keepAlive)
+						.booleanValue());
+			}
+
+			int receiveBufferSize = Integer.parseInt(props.getProperty(
+					TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));
+
+			if (receiveBufferSize > 0) {
+				sock.setReceiveBufferSize(receiveBufferSize);
+			}
+
+			int sendBufferSize = Integer.parseInt(props.getProperty(
+					TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));
+
+			if (sendBufferSize > 0) {
+				sock.setSendBufferSize(sendBufferSize);
+			}
+
+			int trafficClass = Integer.parseInt(props.getProperty(
+					TCP_TRAFFIC_CLASS_PROPERTY_NAME,
+					TCP_TRAFFIC_CLASS_DEFAULT_VALUE));
+
+			if (trafficClass > 0 && setTraficClassMethod != null) {
+				setTraficClassMethod.invoke(sock,
+						new Object[] { new Integer(trafficClass) });
+			}
+		} catch (Throwable t) {
+			unwrapExceptionToProperClassAndThrowIt(t);
+		}
+	}
+
+	/**
 	 * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
 	 */
 	public Socket connect(String hostname, int portNumber, Properties props)
@@ -98,54 +178,86 @@
 
 			this.port = portNumber;
 
-			boolean hasConnectTimeoutMethod = false;
-
 			Method connectWithTimeoutMethod = null;
+			Method socketBindMethod = null;
+			Class socketAddressClass = null;
 
-			try {
-				// Have to do this with reflection, otherwise older JVMs croak
-				Class socketAddressClass = Class
-						.forName("java.net.SocketAddress");
+			String localSocketHostname = props
+					.getProperty("localSocketAddress");
 
-				connectWithTimeoutMethod = Socket.class.getMethod("connect",
-						new Class[] { socketAddressClass, Integer.TYPE });
+			String connectTimeoutStr = props.getProperty("connectTimeout");
 
-				hasConnectTimeoutMethod = true;
-			} catch (NoClassDefFoundError noClassDefFound) {
-				hasConnectTimeoutMethod = false;
-			} catch (NoSuchMethodException noSuchMethodEx) {
-				hasConnectTimeoutMethod = false;
-			} catch (Throwable catchAll) {
-				hasConnectTimeoutMethod = false;
-			}
-
 			int connectTimeout = 0;
 
-			String connectTimeoutStr = props.getProperty("connectTimeout");
+			boolean wantsTimeout = (connectTimeoutStr != null
+					&& connectTimeoutStr.length() > 0 && !connectTimeoutStr
+					.equals("0"));
 
-			if (connectTimeoutStr != null) {
+			boolean wantsLocalBind = (localSocketHostname != null && localSocketHostname
+					.length() > 0);
+
+			boolean needsConfigurationBeforeConnect = socketNeedsConfigurationBeforeConnect(props);
+			
+			if (wantsTimeout || wantsLocalBind || needsConfigurationBeforeConnect) {
+
+				if (connectTimeoutStr != null) {
+					try {
+						connectTimeout = Integer.parseInt(connectTimeoutStr);
+					} catch (NumberFormatException nfe) {
+						throw new SocketException("Illegal value '"
+								+ connectTimeoutStr + "' for connectTimeout");
+					}
+				}
+
 				try {
-					connectTimeout = Integer.parseInt(connectTimeoutStr);
-				} catch (NumberFormatException nfe) {
-					throw new SocketException("Illegal value '"
-							+ connectTimeoutStr + "' for connectTimeout");
+					// Have to do this with reflection, otherwise older JVMs
+					// croak
+					socketAddressClass = Class
+							.forName("java.net.SocketAddress");
+
+					connectWithTimeoutMethod = Socket.class.getMethod(
+							"connect", new Class[] { socketAddressClass,
+									Integer.TYPE });
+
+					socketBindMethod = Socket.class.getMethod("bind",
+							new Class[] { socketAddressClass });
+
+				} catch (NoClassDefFoundError noClassDefFound) {
+					// ignore, we give a better error below if needed
+				} catch (NoSuchMethodException noSuchMethodEx) {
+					// ignore, we give a better error below if needed
+				} catch (Throwable catchAll) {
+					// ignore, we give a better error below if needed
 				}
+
+				if (wantsLocalBind && socketBindMethod == null) {
+					throw new SocketException(
+							"Can't specify \"localSocketAddress\" on JVMs older than 1.4");
+				}
+
+				if (wantsTimeout && connectWithTimeoutMethod == null) {
+					throw new SocketException(
+							"Can't specify \"connectTimeout\" on JVMs older than 1.4");
+				}
 			}
 
 			if (this.host != null) {
-				if (!hasConnectTimeoutMethod || (connectTimeout == 0)) {
+				if (!(wantsLocalBind || wantsTimeout || needsConfigurationBeforeConnect)) {
 					InetAddress[] possibleAddresses = InetAddress
 							.getAllByName(this.host);
 
-					Exception caughtWhileConnecting = null;
+					Throwable caughtWhileConnecting = null;
 
 					// Need to loop through all possible addresses, in case
 					// someone has IPV6 configured (SuSE, for example...)
 
 					for (int i = 0; i < possibleAddresses.length; i++) {
 						try {
-							rawSocket = new Socket(possibleAddresses[i], port);
+							this.rawSocket = new Socket(possibleAddresses[i],
+									port);
 
+							configureSocket(this.rawSocket, props);
+
 							break;
 						} catch (Exception ex) {
 							caughtWhileConnecting = ex;
@@ -153,72 +265,145 @@
 					}
 
 					if (rawSocket == null) {
-						throw new SocketException(caughtWhileConnecting
-								.toString());
+						unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
 					}
 				} else {
 					// must explicitly state this due to classloader issues
 					// when running on older JVMs :(
 					try {
-						Class inetSocketAddressClass = Class
-								.forName("java.net.InetSocketAddress");
-						Constructor addrConstructor = inetSocketAddressClass
-						.getConstructor(new Class[] { InetAddress.class,
-								Integer.TYPE });
 
-				InetAddress[] possibleAddresses = InetAddress
-						.getAllByName(this.host);
+						InetAddress[] possibleAddresses = InetAddress
+								.getAllByName(this.host);
 
-				Exception caughtWhileConnecting = null;
+						Throwable caughtWhileConnecting = null;
 
-				// Need to loop through all possible addresses, in case
-				// someone has IPV6 configured (SuSE, for example...)
+						Object localSockAddr = null;
 
-				for (int i = 0; i < possibleAddresses.length; i++) {
-					
-					try {
-						Object sockAddr = addrConstructor
-								.newInstance(new Object[] { possibleAddresses[i],
-										new Integer(port) });
-						
-								rawSocket = new Socket();
+						Class inetSocketAddressClass = null;
+
+						Constructor addrConstructor = null;
+
+						try {
+							inetSocketAddressClass = Class
+									.forName("java.net.InetSocketAddress");
+
+							addrConstructor = inetSocketAddressClass
+									.getConstructor(new Class[] {
+											InetAddress.class, Integer.TYPE });
+
+							if (wantsLocalBind) {
+								localSockAddr = addrConstructor
+										.newInstance(new Object[] {
+												InetAddress
+														.getByName(localSocketHostname),
+												new Integer(0 /*
+																 * use ephemeral
+																 * port
+																 */) });
+
+							}
+						} catch (Throwable ex) {
+							unwrapExceptionToProperClassAndThrowIt(ex);
+						}
+
+						// Need to loop through all possible addresses, in case
+						// someone has IPV6 configured (SuSE, for example...)
+
+						for (int i = 0; i < possibleAddresses.length; i++) {
+
+							try {
+								this.rawSocket = new Socket();
+
+								configureSocket(this.rawSocket, props);
+
+								Object sockAddr = addrConstructor
+										.newInstance(new Object[] {
+												possibleAddresses[i],
+												new Integer(port) });
+								// bind to the local port, null is 'ok', it
+								// means
+								// use the ephemeral port
+								socketBindMethod.invoke(rawSocket,
+										new Object[] { localSockAddr });
+
 								connectWithTimeoutMethod.invoke(rawSocket,
 										new Object[] { sockAddr,
 												new Integer(connectTimeout) });
 
 								break;
-							} catch (Exception ex) {
-								rawSocket = null;
+							} catch (Exception ex) {	
+								this.rawSocket = null;
 
 								caughtWhileConnecting = ex;
 							}
 						}
 
-						if (rawSocket == null) {
-							throw new SocketException(caughtWhileConnecting
-									.toString());
+						if (this.rawSocket == null) {
+							unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
 						}
 
 					} catch (Throwable t) {
-						if (!(t instanceof SocketException)) {
-							throw new SocketException(t.toString());
-						}
-
-						throw (SocketException) t;
+						unwrapExceptionToProperClassAndThrowIt(t);
 					}
 				}
 
-				try {
-					this.rawSocket.setTcpNoDelay(true);
-				} catch (Exception ex) {
-					/* Ignore */
-					;
-				}
-
 				return this.rawSocket;
 			}
 		}
 
 		throw new SocketException("Unable to create socket");
 	}
-}
+
+	/**
+	 * Does the configureSocket() need to be called before the socket is
+	 * connect()d based on the properties supplied?
+	 * 
+	 */
+	private boolean socketNeedsConfigurationBeforeConnect(Properties props) {
+		int receiveBufferSize = Integer.parseInt(props.getProperty(
+				TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));
+
+		if (receiveBufferSize > 0) {
+			return true;
+		}
+
+		int sendBufferSize = Integer.parseInt(props.getProperty(
+				TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));
+
+		if (sendBufferSize > 0) {
+			return true;
+		}
+
+		int trafficClass = Integer.parseInt(props.getProperty(
+				TCP_TRAFFIC_CLASS_PROPERTY_NAME,
+				TCP_TRAFFIC_CLASS_DEFAULT_VALUE));
+
+		if (trafficClass > 0 && setTraficClassMethod != null) {
+			return true;
+		}
+
+		return false;
+	}
+
+	private void unwrapExceptionToProperClassAndThrowIt(
+			Throwable caughtWhileConnecting) throws SocketException,
+			IOException {
+		if (caughtWhileConnecting instanceof InvocationTargetException) {
+
+			// Replace it with the target, don't use 1.4 chaining as this still
+			// needs to run on older VMs
+			caughtWhileConnecting = ((InvocationTargetException) caughtWhileConnecting)
+					.getTargetException();
+		}
+
+		if (caughtWhileConnecting instanceof SocketException) {
+			throw (SocketException) caughtWhileConnecting;
+		}
+
+		if (caughtWhileConnecting instanceof IOException) {
+			throw (IOException) caughtWhileConnecting;
+		}
+
+		throw new SocketException(caughtWhileConnecting.toString());
+	}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Statement.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Statement.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Statement.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,13 +1,13 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as 
+ it under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.
 
- There are special exceptions to the terms and conditions of the GPL 
- as it is applied to this software. View the full text of the 
- exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
  software distribution.
 
  This program is distributed in the hope that it will be useful,
@@ -19,2289 +19,70 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+*/
 
-
- */
 package com.mysql.jdbc;
 
-import com.mysql.jdbc.exceptions.MySQLTimeoutException;
-import com.mysql.jdbc.profiler.ProfileEventSink;
-import com.mysql.jdbc.profiler.ProfilerEvent;
-import com.mysql.jdbc.util.LRUCache;
-
-import java.sql.DataTruncation;
+import java.io.InputStream;
 import java.sql.SQLException;
-import java.sql.SQLWarning;
-import java.sql.Types;
 
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimerTask;
-
 /**
- * A Statement object is used for executing a static SQL statement and obtaining
- * the results produced by it.
- * 
- * <p>
- * Only one ResultSet per Statement can be open at any point in time. Therefore,
- * if the reading of one ResultSet is interleaved with the reading of another,
- * each must have been generated by different Statements. All statement execute
- * methods implicitly close a statement's current ResultSet if an open one
- * exists.
- * </p>
- * 
- * @author Mark Matthews
- * @version $Id: Statement.java 4624 2005-11-28 14:24:29 -0600 (Mon, 28 Nov
- *          2005) mmatthews $
- * 
- * @see java.sql.Statement
- * @see ResultSet
+ * This interface contains methods that are considered the "vendor extension"
+ * to the JDBC API for MySQL's implementation of java.sql.Statement.
+ *
+ * For those looking further into the driver implementation, it is not
+ * an API that is used for plugability of implementations inside our driver
+ * (which is why there are still references to StatementImpl throughout the
+ * code).
+ *
+ * @version $Id: $
+ *
  */
-public class Statement implements java.sql.Statement {
-	class CachedResultSetMetaData {
-		/** Map column names (and all of their permutations) to column indices */
-		Map columnNameToIndex = null;
+public interface Statement extends java.sql.Statement {
 
-		/** Cached Field info */
-		Field[] fields;
-
-		/** Map of fully-specified column names to column indices */
-		Map fullColumnNameToIndex = null;
-
-		/** Cached ResultSetMetaData */
-		java.sql.ResultSetMetaData metadata;
-	}
-
 	/**
-	 * Thread used to implement query timeouts...Eventually we could be more
-	 * efficient and have one thread with timers, but this is a straightforward
-	 * and simple way to implement a feature that isn't used all that often.
-	 */
-	class CancelTask extends TimerTask {
-
-		long connectionId = 0;
-
-		CancelTask() throws SQLException {
-			connectionId = connection.getIO().getThreadId();
-		}
-
-		public void run() {
-
-			Thread cancelThread = new Thread() {
-
-				public void run() {
-					Connection cancelConn = null;
-					java.sql.Statement cancelStmt = null;
-
-					try {
-						cancelConn = connection.duplicate();
-						cancelStmt = cancelConn.createStatement();
-						cancelStmt.execute("KILL QUERY " + connectionId);
-						wasCancelled = true;
-					} catch (SQLException sqlEx) {
-						throw new RuntimeException(sqlEx.toString());
-					} finally {
-						if (cancelStmt != null) {
-							try {
-								cancelStmt.close();
-							} catch (SQLException sqlEx) {
-								throw new RuntimeException(sqlEx.toString());
-							}
-						}
-
-						if (cancelConn != null) {
-							try {
-								cancelConn.close();
-							} catch (SQLException sqlEx) {
-								throw new RuntimeException(sqlEx.toString());
-							}
-						}
-					}
-				}
-			};
-
-			cancelThread.start();
-		}
-	}
-
-	/** Used to generate IDs when profiling. */
-	protected static int statementCounter = 1;
-
-	public final static byte USES_VARIABLES_FALSE = 0;
-
-	public final static byte USES_VARIABLES_TRUE = 1;
-
-	public final static byte USES_VARIABLES_UNKNOWN = -1;
-
-	protected boolean wasCancelled = false;
-
-	/** Holds batched commands */
-	protected List batchedArgs;
-
-	/** The character converter to use (if available) */
-	protected SingleByteCharsetConverter charConverter = null;
-
-	/** The character encoding to use (if available) */
-	protected String charEncoding = null;
-
-	/** The connection that created us */
-	protected Connection connection = null;
-	
-	protected long connectionId = 0;
-
-	/** The catalog in use */
-	protected String currentCatalog = null;
-
-	/** Should we process escape codes? */
-	protected boolean doEscapeProcessing = true;
-
-	/** If we're profiling, where should events go to? */
-	protected ProfileEventSink eventSink = null;
-
-	/** The number of rows to fetch at a time (currently ignored) */
-	private int fetchSize = 0;
-
-	/** Has this statement been closed? */
-	protected boolean isClosed = false;
-
-	/** The auto_increment value for the last insert */
-	protected long lastInsertId = -1;
-
-	/** The max field size for this statement */
-	protected int maxFieldSize = MysqlIO.getMaxBuf();
-
-	/**
-	 * The maximum number of rows to return for this statement (-1 means _all_
-	 * rows)
-	 */
-	protected int maxRows = -1;
-
-	/** Has someone changed this for this statement? */
-	protected boolean maxRowsChanged = false;
-
-	/** List of currently-open ResultSets */
-	protected List openResults = new ArrayList();
-
-	/** Are we in pedantic mode? */
-	protected boolean pedantic = false;
-
-	/**
-	 * Where this statement was created, only used if profileSql or
-	 * useUsageAdvisor set to true.
-	 */
-	protected Throwable pointOfOrigin;
-
-	/** Should we profile? */
-	protected boolean profileSQL = false;
-
-	/** The current results */
-	protected ResultSet results = null;
-
-	/** The concurrency for this result set (updatable or not) */
-	protected int resultSetConcurrency = 0;
-
-	/** Cache of ResultSet metadata */
-	protected LRUCache resultSetMetadataCache;
-
-	/** The type of this result set (scroll sensitive or in-sensitive) */
-	protected int resultSetType = 0;
-
-	/** Used to identify this statement when profiling. */
-	protected int statementId;
-
-	/** The timeout for a query */
-	protected int timeoutInMillis = 0;
-
-	/** The update count for this statement */
-	protected long updateCount = -1;
-
-	/** Should we use the usage advisor? */
-	protected boolean useUsageAdvisor = false;
-
-	/** The warnings chain. */
-	protected SQLWarning warningChain = null;
-
-	/**
-	 * Should this statement hold results open over .close() irregardless of
-	 * connection's setting?
-	 */
-	protected boolean holdResultsOpenOverClose = false;
-
-	protected ArrayList batchedGeneratedKeys = null;
-
-	protected boolean retrieveGeneratedKeys = false;
-
-	protected boolean continueBatchOnError = false;
-	
-	/**
-	 * Constructor for a Statement.
-	 * 
-	 * @param c
-	 *            the Connection instantation that creates us
-	 * @param catalog
-	 *            the database name in use when we were created
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs.
-	 */
-	public Statement(Connection c, String catalog) throws SQLException {
-		if ((c == null) || c.isClosed()) {
-			throw SQLError.createSQLException(
-					Messages.getString("Statement.0"), //$NON-NLS-1$
-					SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-
-		this.connection = c;
-		this.connectionId = this.connection.getId();
-		
-		this.currentCatalog = catalog;
-		this.pedantic = this.connection.getPedantic();
-		this.continueBatchOnError = this.connection.getContinueBatchOnError();
-		
-		if (!this.connection.getDontTrackOpenResources()) {
-			this.connection.registerStatement(this);
-		}
-
-		//
-		// Adjust, if we know it
-		//
-
-		if (this.connection != null) {
-			this.maxFieldSize = this.connection.getMaxAllowedPacket();
-
-			int defaultFetchSize = this.connection.getDefaultFetchSize();
-
-			if (defaultFetchSize != 0) {
-				setFetchSize(defaultFetchSize);
-			}
-		}
-
-		if (this.connection.getUseUnicode()) {
-			this.charEncoding = this.connection.getEncoding();
-
-			this.charConverter = this.connection
-					.getCharsetConverter(this.charEncoding);
-		}
-
-		boolean profiling = this.connection.getProfileSql()
-				|| this.connection.getUseUsageAdvisor();
-
-		if (this.connection.getAutoGenerateTestcaseScript() || profiling) {
-			this.statementId = statementCounter++;
-		}
-
-		if (profiling) {
-			this.pointOfOrigin = new Throwable();
-			this.profileSQL = this.connection.getProfileSql();
-			this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
-			this.eventSink = ProfileEventSink.getInstance(this.connection);
-		}
-
-		int maxRowsConn = this.connection.getMaxRows();
-
-		if (maxRowsConn != -1) {
-			setMaxRows(maxRowsConn);
-		}
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @param sql
-	 *            DOCUMENT ME!
-	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	public synchronized void addBatch(String sql) throws SQLException {
-		if (this.batchedArgs == null) {
-			this.batchedArgs = new ArrayList();
-		}
-
-		if (sql != null) {
-			this.batchedArgs.add(sql);
-		}
-	}
-
-	/**
-	 * Cancels this Statement object if both the DBMS and driver support
-	 * aborting an SQL statement. This method can be used by one thread to
-	 * cancel a statement that is being executed by another thread.
-	 */
-	public void cancel() throws SQLException {
-		if (!this.isClosed &&
-				this.connection != null && 
-				this.connection.versionMeetsMinimum(5, 0, 0)) {
-			Connection cancelConn = null;
-			java.sql.Statement cancelStmt = null;
-
-			try {
-				cancelConn = this.connection.duplicate();
-				cancelStmt = cancelConn.createStatement();
-				cancelStmt.execute("KILL QUERY "
-						+ this.connection.getIO().getThreadId());
-				this.wasCancelled = true;
-			} finally {
-				if (cancelStmt != null) {
-					cancelStmt.close();
-				}
-
-				if (cancelConn != null) {
-					cancelConn.close();
-				}
-			}
-
-		}
-	}
-
-	// --------------------------JDBC 2.0-----------------------------
-
-	/**
-	 * Checks if closed() has been called, and throws an exception if so
-	 * 
-	 * @throws SQLException
-	 *             if this statement has been closed
-	 */
-	protected void checkClosed() throws SQLException {
-		if (this.isClosed) {
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.49"), //$NON-NLS-1$
-					SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$
-		}
-	}
-
-	/**
-	 * Checks if the given SQL query with the given first non-ws char is a DML
-	 * statement. Throws an exception if it is.
-	 * 
-	 * @param sql
-	 *            the SQL to check
-	 * @param firstStatementChar
-	 *            the UC first non-ws char of the statement
-	 * 
-	 * @throws SQLException
-	 *             if the statement contains DML
-	 */
-	protected void checkForDml(String sql, char firstStatementChar)
-			throws SQLException {
-		if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
-				|| (firstStatementChar == 'D') || (firstStatementChar == 'A')
-				|| (firstStatementChar == 'C')) {
-			if (StringUtils.startsWithIgnoreCaseAndWs(sql, "INSERT") //$NON-NLS-1$
-					|| StringUtils.startsWithIgnoreCaseAndWs(sql, "UPDATE") //$NON-NLS-1$
-					|| StringUtils.startsWithIgnoreCaseAndWs(sql, "DELETE") //$NON-NLS-1$
-					|| StringUtils.startsWithIgnoreCaseAndWs(sql, "DROP") //$NON-NLS-1$
-					|| StringUtils.startsWithIgnoreCaseAndWs(sql, "CREATE") //$NON-NLS-1$
-					|| StringUtils.startsWithIgnoreCaseAndWs(sql, "ALTER")) { //$NON-NLS-1$
-				throw SQLError.createSQLException(Messages
-						.getString("Statement.57"), //$NON-NLS-1$
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-			}
-		}
-	}
-
-	/**
-	 * Method checkNullOrEmptyQuery.
-	 * 
-	 * @param sql
-	 *            the SQL to check
-	 * 
-	 * @throws SQLException
-	 *             if query is null or empty.
-	 */
-	protected void checkNullOrEmptyQuery(String sql) throws SQLException {
-		if (sql == null) {
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.59"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-
-		if (sql.length() == 0) {
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.61"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-	}
-
-	/**
-	 * JDBC 2.0 Make the set of commands in the current batch empty. This method
-	 * is optional.
-	 * 
-	 * @exception SQLException
-	 *                if a database-access error occurs, or the driver does not
-	 *                support batch statements
-	 */
-	public synchronized void clearBatch() throws SQLException {
-		if (this.batchedArgs != null) {
-			this.batchedArgs.clear();
-		}
-	}
-
-	/**
-	 * After this call, getWarnings returns null until a new warning is reported
-	 * for this Statement.
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs (why?)
-	 */
-	public void clearWarnings() throws SQLException {
-		this.warningChain = null;
-	}
-
-	/**
-	 * In many cases, it is desirable to immediately release a Statement's
-	 * database and JDBC resources instead of waiting for this to happen when it
-	 * is automatically closed. The close method provides this immediate
-	 * release.
-	 * 
-	 * <p>
-	 * <B>Note:</B> A Statement is automatically closed when it is garbage
-	 * collected. When a Statement is closed, its current ResultSet, if one
-	 * exists, is also closed.
-	 * </p>
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public void close() throws SQLException {
-		realClose(true, true);
-	}
-
-	/**
-	 * Close any open result sets that have been 'held open'
-	 */
-	protected void closeAllOpenResults() {
-		if (this.openResults != null) {
-			for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
-				ResultSet element = (ResultSet) iter.next();
-
-				try {
-					element.realClose(false);
-				} catch (SQLException sqlEx) {
-					AssertionFailedException.shouldNotHappen(sqlEx);
-				}
-			}
-
-			this.openResults.clear();
-		}
-	}
-
-	/**
-	 * @param sql
-	 * @return
-	 */
-	private ResultSet createResultSetUsingServerFetch(String sql)
-			throws SQLException {
-		java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
-				sql, this.resultSetType, this.resultSetConcurrency);
-
-		pStmt.setFetchSize(this.fetchSize);
-
-		pStmt.execute();
-
-		//
-		// Need to be able to get resultset irrespective if we issued DML or
-		// not to make this work.
-		//
-		ResultSet rs = ((com.mysql.jdbc.Statement) pStmt)
-				.getResultSetInternal();
-
-		rs
-				.setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt);
-
-		this.results = rs;
-
-		return rs;
-	}
-
-	/**
-	 * We only stream result sets when they are forward-only, read-only, and the
-	 * fetch size has been set to Integer.MIN_VALUE
-	 * 
-	 * @return true if this result set should be streamed row at-a-time, rather
-	 *         than read all at once.
-	 */
-	protected boolean createStreamingResultSet() {
-		return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
-				&& (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
-	}
-
-	/**
 	 * Workaround for containers that 'check' for sane values of
-	 * Statement.setFetchSize().
-	 * 
+	 * Statement.setFetchSize() so that applications can use
+	 * the Java variant of libmysql's mysql_use_result() behavior.
+	 *
 	 * @throws SQLException
 	 */
-	public void enableStreamingResults() throws SQLException {
-		setFetchSize(Integer.MIN_VALUE);
-		setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
-	}
+	public abstract void enableStreamingResults() throws SQLException;
 
 	/**
-	 * Execute a SQL statement that may return multiple results. We don't have
-	 * to worry about this since we do not support multiple ResultSets. You can
-	 * use getResultSet or getUpdateCount to retrieve the result.
-	 * 
-	 * @param sql
-	 *            any SQL statement
-	 * 
-	 * @return true if the next result is a ResulSet, false if it is an update
-	 *         count or there are no more results
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public boolean execute(String sql) throws SQLException {
-		checkClosed();
-		
-
-		Connection locallyScopedConn = this.connection;
-		
-		synchronized (locallyScopedConn.getMutex()) {
-			this.wasCancelled = false;
-	
-			checkNullOrEmptyQuery(sql);
-	
-			checkClosed();
-	
-			char firstNonWsChar = StringUtils.firstNonWsCharUc(sql);
-	
-			boolean isSelect = true;
-	
-			if (firstNonWsChar != 'S') {
-				isSelect = false;
-	
-				if (locallyScopedConn.isReadOnly()) {
-					throw SQLError.createSQLException(Messages
-							.getString("Statement.27") //$NON-NLS-1$
-							+ Messages.getString("Statement.28"), //$NON-NLS-1$
-							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-				}
-			}
-	
-			if (this.doEscapeProcessing) {
-				Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
-						locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn);
-	
-				if (escapedSqlResult instanceof String) {
-					sql = (String) escapedSqlResult;
-				} else {
-					sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
-				}
-			}
-	
-			if (this.results != null) {
-				if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
-					this.results.realClose(false);
-				}
-			}
-	
-			CachedResultSetMetaData cachedMetaData = null;
-	
-			ResultSet rs = null;
-	
-			// If there isn't a limit clause in the SQL
-			// then limit the number of rows to return in
-			// an efficient manner. Only do this if
-			// setMaxRows() hasn't been used on any Statements
-			// generated from the current Connection (saves
-			// a query, and network traffic).
-			
-			this.batchedGeneratedKeys = null;
-			
-			if (useServerFetch()) {
-				rs = createResultSetUsingServerFetch(sql);
-			} else {
-				CancelTask timeoutTask = null;
-
-				try {
-					if (this.timeoutInMillis != 0
-							&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
-						timeoutTask = new CancelTask();
-						Connection.getCancelTimer().schedule(timeoutTask, 
-								this.timeoutInMillis);
-					}
-
-					String oldCatalog = null;
-
-					if (!locallyScopedConn.getCatalog().equals(
-							this.currentCatalog)) {
-						oldCatalog = locallyScopedConn.getCatalog();
-						locallyScopedConn.setCatalog(this.currentCatalog);
-					}
-
-					//
-					// Check if we have cached metadata for this query...
-					//
-					if (locallyScopedConn.getCacheResultSetMetadata()) {
-						cachedMetaData = getCachedMetaData(sql);
-					}
-
-					//
-					// Only apply max_rows to selects
-					//
-					if (locallyScopedConn.useMaxRows()) {
-						int rowLimit = -1;
-
-						if (isSelect) {
-							if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
-								rowLimit = this.maxRows;
-							} else {
-								if (this.maxRows <= 0) {
-									locallyScopedConn
-											.execSQL(
-													this,
-													"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, //$NON-NLS-1$
-													null,
-													java.sql.ResultSet.TYPE_FORWARD_ONLY,
-													java.sql.ResultSet.CONCUR_READ_ONLY,
-													false, 
-													this.currentCatalog, true); //$NON-NLS-1$
-								} else {
-									locallyScopedConn
-											.execSQL(
-													this,
-													"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, //$NON-NLS-1$
-													-1,
-													null,
-													java.sql.ResultSet.TYPE_FORWARD_ONLY,
-													java.sql.ResultSet.CONCUR_READ_ONLY,
-													false, 
-													this.currentCatalog, true); //$NON-NLS-1$
-								}
-							}
-						} else {
-							locallyScopedConn
-									.execSQL(
-											this,
-											"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
-											java.sql.ResultSet.TYPE_FORWARD_ONLY,
-											java.sql.ResultSet.CONCUR_READ_ONLY,
-											false, this.currentCatalog,
-											true); //$NON-NLS-1$
-						}
-
-						// Finally, execute the query
-						rs = locallyScopedConn.execSQL(this, sql, rowLimit, null,
-								this.resultSetType, this.resultSetConcurrency,
-								createStreamingResultSet(), 
-								this.currentCatalog, (cachedMetaData == null));
-					} else {
-						rs = locallyScopedConn.execSQL(this, sql, -1, null,
-								this.resultSetType, this.resultSetConcurrency,
-								createStreamingResultSet(), 
-								this.currentCatalog, (cachedMetaData == null));
-					}
-
-					if (timeoutTask != null) {
-						timeoutTask.cancel();
-						timeoutTask = null;
-					}
-					
-					if (oldCatalog != null) {
-						locallyScopedConn.setCatalog(oldCatalog);
-					}
-
-					if (this.wasCancelled) {
-						this.wasCancelled = false;
-						throw new MySQLTimeoutException();
-					}
-				} finally {
-					if (timeoutTask != null) {
-						timeoutTask.cancel();
-					}
-				}
-			}
-
-			this.lastInsertId = rs.getUpdateID();
-
-			if (rs != null) {
-				this.results = rs;
-
-				rs.setFirstCharOfQuery(firstNonWsChar);
-
-				if (rs.reallyResult()) {
-					if (cachedMetaData != null) {
-						initializeResultsMetadataFromCache(sql, cachedMetaData,
-								this.results);
-					} else {
-						if (this.connection.getCacheResultSetMetadata()) {
-							initializeResultsMetadataFromCache(sql,
-									null /* will be created */, this.results);
-						}
-					}
-				}
-			}
-
-			return ((rs != null) && rs.reallyResult());
-		}
-	}
-
-	/**
-	 * @see Statement#execute(String, int)
-	 */
-	public boolean execute(String sql, int returnGeneratedKeys)
-			throws SQLException {
-		
-		
-		if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
-			checkClosed();
-			
-			Connection locallyScopedConn = this.connection;
-			
-			synchronized (locallyScopedConn.getMutex()) {
-				// If this is a 'REPLACE' query, we need to be able to parse
-				// the 'info' message returned from the server to determine
-				// the actual number of keys generated.
-				boolean readInfoMsgState = this.connection
-						.isReadInfoMsgEnabled();
-				locallyScopedConn.setReadInfoMsgEnabled(true);
-
-				try {
-					return execute(sql);
-				} finally {
-					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
-				}
-			}
-		}
-
-		return execute(sql);
-	}
-
-	/**
-	 * @see Statement#execute(String, int[])
-	 */
-	public boolean execute(String sql, int[] generatedKeyIndices)
-			throws SQLException {
-		if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
-			checkClosed();
-			
-			Connection locallyScopedConn = this.connection;
-			
-			synchronized (locallyScopedConn.getMutex()) {
-				// If this is a 'REPLACE' query, we need to be able to parse
-				// the 'info' message returned from the server to determine
-				// the actual number of keys generated.
-				boolean readInfoMsgState = locallyScopedConn
-						.isReadInfoMsgEnabled();
-				locallyScopedConn.setReadInfoMsgEnabled(true);
-
-				try {
-					return execute(sql);
-				} finally {
-					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
-				}
-			}
-		}
-
-		return execute(sql);
-	}
-
-	/**
-	 * @see Statement#execute(String, String[])
-	 */
-	public boolean execute(String sql, String[] generatedKeyNames)
-			throws SQLException {
-		if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
-			checkClosed();
-
-			Connection locallyScopedConn = this.connection;
-			
-			synchronized (locallyScopedConn.getMutex()) {
-				// If this is a 'REPLACE' query, we need to be able to parse
-				// the 'info' message returned from the server to determine
-				// the actual number of keys generated.
-				boolean readInfoMsgState = this.connection
-						.isReadInfoMsgEnabled();
-				locallyScopedConn.setReadInfoMsgEnabled(true);
-
-				try {
-					return execute(sql);
-				} finally {
-					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
-				}
-			}
-		}
-
-		return execute(sql);
-	}
-
-	/**
-	 * JDBC 2.0 Submit a batch of commands to the database for execution. This
-	 * method is optional.
-	 * 
-	 * @return an array of update counts containing one element for each command
-	 *         in the batch. The array is ordered according to the order in
-	 *         which commands were inserted into the batch
-	 * 
-	 * @exception SQLException
-	 *                if a database-access error occurs, or the driver does not
-	 *                support batch statements
-	 * @throws java.sql.BatchUpdateException
-	 *             DOCUMENT ME!
-	 */
-	public synchronized int[] executeBatch() throws SQLException {
-		checkClosed();
-		
-		Connection locallyScopedConn = this.connection;
-		
-		if (locallyScopedConn.isReadOnly()) {
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.34") //$NON-NLS-1$
-					+ Messages.getString("Statement.35"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-		}
-
-		if (this.results != null) {
-			if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
-				this.results.realClose(false);
-			}
-		}
-
-		synchronized (locallyScopedConn.getMutex()) {
-			try {
-				this.retrieveGeneratedKeys = true;
-				
-				int[] updateCounts = null;
-
-				if (this.batchedArgs != null) {
-					int nbrCommands = this.batchedArgs.size();
-
-					this.batchedGeneratedKeys = new ArrayList(this.batchedArgs.size());
-					
-					boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
-					
-					if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) && 
-							(multiQueriesEnabled || 
-							(locallyScopedConn.getRewriteBatchedStatements() && 
-									nbrCommands > 4))) {
-						return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands);
-					}
-					
-					updateCounts = new int[nbrCommands];
-
-					for (int i = 0; i < nbrCommands; i++) {
-						updateCounts[i] = -3;
-					}
-
-					SQLException sqlEx = null;
-
-					int commandIndex = 0;
-
-					for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
-						try {
-							updateCounts[commandIndex] = executeUpdate((String) this.batchedArgs
-									.get(commandIndex), true);
-							getBatchedGeneratedKeys();
-						} catch (SQLException ex) {
-							updateCounts[commandIndex] = EXECUTE_FAILED;
-
-							if (this.continueBatchOnError) {
-								sqlEx = ex;
-							} else {
-								int[] newUpdateCounts = new int[commandIndex];
-								System.arraycopy(updateCounts, 0,
-										newUpdateCounts, 0, commandIndex);
-
-								throw new java.sql.BatchUpdateException(ex
-										.getMessage(), ex.getSQLState(), ex
-										.getErrorCode(), newUpdateCounts);
-							}
-						}
-					}
-
-					if (sqlEx != null) {
-						throw new java.sql.BatchUpdateException(sqlEx
-								.getMessage(), sqlEx.getSQLState(), sqlEx
-								.getErrorCode(), updateCounts);
-					}
-				}
-
-				return (updateCounts != null) ? updateCounts : new int[0];
-			} finally {
-				this.retrieveGeneratedKeys = false;
-				
-				clearBatch();
-			}
-		}
-	}
-
-	/**
-	 * Rewrites batch into a single query to send to the server. This method
-	 * will constrain each batch to be shorter than max_allowed_packet on the
-	 * server.
-	 * 
-	 * @return update counts in the same manner as executeBatch()
+	 * Resets this statements fetch size and result set type to the values
+	 * they had before enableStreamingResults() was called.
+	 *
 	 * @throws SQLException
 	 */
-	private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
-			int nbrCommands) throws SQLException {
+	public abstract void disableStreamingResults() throws SQLException;
 
-		Connection locallyScopedConn = this.connection;
-		
-		if (!multiQueriesEnabled) {
-			locallyScopedConn.getIO().enableMultiQueries();
-		}
-
-		try {
-			int[] updateCounts = new int[nbrCommands];
-
-			for (int i = 0; i < nbrCommands; i++) {
-				updateCounts[i] = -3;
-			}
-
-			int commandIndex = 0;
-
-			StringBuffer queryBuf = new StringBuffer();
-
-			java.sql.Statement batchStmt = locallyScopedConn.createStatement();
-
-			int counter = 0;
-
-			int numberOfBytesPerChar = 1;
-
-			String connectionEncoding = locallyScopedConn.getEncoding();
-
-			if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) {
-				numberOfBytesPerChar = 3;
-			} else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) {
-				numberOfBytesPerChar = 2;
-			}
-
-			int escapeAdjust = 1;
-			
-			if (this.doEscapeProcessing) {
-				escapeAdjust = 2; /* We assume packet _could_ grow by this amount, as we're not
-				                     sure how big statement will end up after
-				                     escape processing */
-			}
-			
-			for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
-				String nextQuery = (String) this.batchedArgs.get(commandIndex);
-
-				if (((((queryBuf.length() + nextQuery.length())
-						* numberOfBytesPerChar) + 1 /* for semicolon */ 
-						+ MysqlIO.HEADER_LENGTH) * escapeAdjust)  + 32 > this.connection
-						.getMaxAllowedPacket()) {
-					batchStmt.execute(queryBuf.toString());
-
-					updateCounts[counter++] = batchStmt.getUpdateCount();
-					long generatedKeyStart = ((com.mysql.jdbc.Statement)batchStmt).getLastInsertID();
-					byte[][] row = new byte[1][];
-					row[0] = Long.toString(generatedKeyStart++).getBytes();
-					this.batchedGeneratedKeys.add(row);
-
-					while (batchStmt.getMoreResults()
-							|| batchStmt.getUpdateCount() != -1) {
-						updateCounts[counter++] = batchStmt.getUpdateCount();
-						row = new byte[1][];
-						row[0] = Long.toString(generatedKeyStart++).getBytes();
-						this.batchedGeneratedKeys.add(row);
-					}
-
-					queryBuf = new StringBuffer();
-				}
-
-				queryBuf.append(nextQuery);
-				queryBuf.append(";");
-			}
-
-			if (queryBuf.length() > 0) {
-				batchStmt.execute(queryBuf.toString());
-
-				long generatedKeyStart = ((com.mysql.jdbc.Statement)batchStmt).getLastInsertID();
-				byte[][] row = new byte[1][];
-				row[0] = Long.toString(generatedKeyStart++).getBytes();
-				this.batchedGeneratedKeys.add(row);
-				
-				updateCounts[counter++] = batchStmt.getUpdateCount();
-
-				while (batchStmt.getMoreResults()
-						|| batchStmt.getUpdateCount() != -1) {
-					updateCounts[counter++] = batchStmt.getUpdateCount();
-					row = new byte[1][];
-					row[0] = Long.toString(generatedKeyStart++).getBytes();
-					this.batchedGeneratedKeys.add(row);
-				}
-			}
-
-			return (updateCounts != null) ? updateCounts : new int[0];
-		} finally {
-			if (!multiQueriesEnabled) {
-				locallyScopedConn.getIO().disableMultiQueries();
-			}
-		}
-	}
-	
 	/**
-	 * Execute a SQL statement that retruns a single ResultSet
-	 * 
-	 * @param sql
-	 *            typically a static SQL SELECT statement
-	 * 
-	 * @return a ResulSet that contains the data produced by the query
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * Sets an InputStream instance that will be used to send data
+	 * to the MySQL server for a "LOAD DATA LOCAL INFILE" statement
+	 * rather than a FileInputStream or URLInputStream that represents
+	 * the path given as an argument to the statement.
+	 *
+	 * This stream will be read to completion upon execution of a
+	 * "LOAD DATA LOCAL INFILE" statement, and will automatically
+	 * be closed by the driver, so it needs to be reset
+	 * before each call to execute*() that would cause the MySQL
+	 * server to request data to fulfill the request for
+	 * "LOAD DATA LOCAL INFILE".
+	 *
+	 * If this value is set to NULL, the driver will revert to using
+	 * a FileInputStream or URLInputStream as required.
 	 */
-	public java.sql.ResultSet executeQuery(String sql)
-			throws SQLException {
-		checkClosed();
-		
-		Connection locallyScopedConn = this.connection;
-		
-		synchronized (locallyScopedConn.getMutex()) {
-			this.wasCancelled = false;
-	
-			checkNullOrEmptyQuery(sql);
-	
-			
-	
-			if (this.doEscapeProcessing) {
-				Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
-						locallyScopedConn.serverSupportsConvertFn(), this.connection);
-	
-				if (escapedSqlResult instanceof String) {
-					sql = (String) escapedSqlResult;
-				} else {
-					sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
-				}
-			}
-	
-			char firstStatementChar = StringUtils.firstNonWsCharUc(sql);
-	
-			checkForDml(sql, firstStatementChar);
-	
-			if (this.results != null) {
-				if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
-					this.results.realClose(false);
-				}
-			}
-	
-			CachedResultSetMetaData cachedMetaData = null;
-	
-			// If there isn't a limit clause in the SQL
-			// then limit the number of rows to return in
-			// an efficient manner. Only do this if
-			// setMaxRows() hasn't been used on any Statements
-			// generated from the current Connection (saves
-			// a query, and network traffic).
-			
-			if (useServerFetch()) {
-				this.results = createResultSetUsingServerFetch(sql);
+	public abstract void setLocalInfileInputStream(InputStream stream);
 
-				return this.results;
-			}
-
-			CancelTask timeoutTask = null;
-
-			try {
-				if (this.timeoutInMillis != 0
-						&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
-					timeoutTask = new CancelTask();
-					Connection.getCancelTimer().schedule(timeoutTask, 
-							this.timeoutInMillis);
-				}
-
-				String oldCatalog = null;
-
-				if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
-					oldCatalog = locallyScopedConn.getCatalog();
-					locallyScopedConn.setCatalog(this.currentCatalog);
-				}
-
-				//
-				// Check if we have cached metadata for this query...
-				//
-				if (locallyScopedConn.getCacheResultSetMetadata()) {
-					cachedMetaData = getCachedMetaData(sql);
-				}
-
-				if (locallyScopedConn.useMaxRows()) {
-					// We need to execute this all together
-					// So synchronize on the Connection's mutex (because
-					// even queries going through there synchronize
-					// on the connection
-					if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
-						this.results = locallyScopedConn.execSQL(this, sql,
-								this.maxRows, null, this.resultSetType,
-								this.resultSetConcurrency,
-								createStreamingResultSet(),
-								this.currentCatalog, (cachedMetaData == null));
-					} else {
-						if (this.maxRows <= 0) {
-							locallyScopedConn
-									.execSQL(
-											this,
-											"SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$
-											java.sql.ResultSet.TYPE_FORWARD_ONLY,
-											java.sql.ResultSet.CONCUR_READ_ONLY,
-											false, this.currentCatalog,
-											true); //$NON-NLS-1$
-						} else {
-							locallyScopedConn
-									.execSQL(
-											this,
-											"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows, -1, //$NON-NLS-1$
-											null,
-											java.sql.ResultSet.TYPE_FORWARD_ONLY,
-											java.sql.ResultSet.CONCUR_READ_ONLY,
-											false, this.currentCatalog,
-											true); //$NON-NLS-1$
-						}
-
-						this.results = locallyScopedConn.execSQL(this, sql, -1,
-								null, this.resultSetType,
-								this.resultSetConcurrency,
-								createStreamingResultSet(),
-								this.currentCatalog, (cachedMetaData == null));
-
-						if (oldCatalog != null) {
-							locallyScopedConn.setCatalog(oldCatalog);
-						}
-					}
-				} else {
-					this.results = locallyScopedConn.execSQL(this, sql, -1, null,
-							this.resultSetType, this.resultSetConcurrency,
-							createStreamingResultSet(),
-							this.currentCatalog, (cachedMetaData == null));
-				}
-
-				if (timeoutTask != null) {
-					timeoutTask.cancel();
-					timeoutTask = null;
-				}
-				
-				if (oldCatalog != null) {
-					locallyScopedConn.setCatalog(oldCatalog);
-				}
-
-				if (this.wasCancelled) {
-					this.wasCancelled = false;
-
-					throw new MySQLTimeoutException();
-				}
-			} finally {
-				if (timeoutTask != null) {
-					timeoutTask.cancel();
-				}
-			}
-
-			this.lastInsertId = this.results.getUpdateID();
-
-			/*
-			 * if (!this.results.reallyResult()) { if
-			 * (!this.connection.getAutoCommit()) { this.connection.rollback(); }
-			 * 
-			 * throw
-			 * SQLError.createSQLException(Messages.getString("Statement.40"),
-			 * //$NON-NLS-1$ SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ }
-			 */
-			if (cachedMetaData != null) {
-				initializeResultsMetadataFromCache(sql, cachedMetaData,
-						this.results);
-			} else {
-				if (this.connection.getCacheResultSetMetadata()) {
-					initializeResultsMetadataFromCache(sql,
-							null /* will be created */, this.results);
-				}
-			}
-
-			return this.results;
-		}
-	}
-
 	/**
-	 * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
-	 * statements that return nothing such as SQL DDL statements can be executed
-	 * Any IDs generated for AUTO_INCREMENT fields can be retrieved by casting
-	 * this Statement to org.gjt.mm.mysql.Statement and calling the
-	 * getLastInsertID() method.
-	 * 
-	 * @param sql
-	 *            a SQL statement
-	 * 
-	 * @return either a row count, or 0 for SQL commands
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
+	 * Returns the InputStream instance that will be used to send
+	 * data in response to a "LOAD DATA LOCAL INFILE" statement.
+	 *
+	 * This method returns NULL if no such stream has been set
+	 * via setLocalInfileInputStream().
 	 */
-	public int executeUpdate(String sql) throws SQLException {
-		return executeUpdate(sql, false);
-	}
+	public abstract InputStream getLocalInfileInputStream();
 
-	protected int executeUpdate(String sql, boolean isBatch)
-			throws SQLException {
-		checkClosed();
-		
-		Connection locallyScopedConn = this.connection;
-		
-		char firstStatementChar = StringUtils.firstNonWsCharUc(sql);
-
-		ResultSet rs = null;
-
-		synchronized (locallyScopedConn.getMutex()) {
-			this.wasCancelled = false;
-	
-			checkNullOrEmptyQuery(sql);
-
-			if (this.doEscapeProcessing) {
-				Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
-						this.connection.serverSupportsConvertFn(), this.connection);
-
-				if (escapedSqlResult instanceof String) {
-					sql = (String) escapedSqlResult;
-				} else {
-					sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
-				}
-			}
-			
-			if (locallyScopedConn.isReadOnly()) {
-				throw SQLError.createSQLException(Messages
-						.getString("Statement.42") //$NON-NLS-1$
-						+ Messages.getString("Statement.43"), //$NON-NLS-1$
-						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-			}
-	
-			if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
-				throw SQLError.createSQLException(Messages
-						.getString("Statement.46"), //$NON-NLS-1$
-						"01S03"); //$NON-NLS-1$
-			}
-	
-			if (this.results != null) {
-				if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
-					this.results.realClose(false);
-				}
-			}
-	
-			// The checking and changing of catalogs
-			// must happen in sequence, so synchronize
-			// on the same mutex that _conn is using
-		
-			CancelTask timeoutTask = null;
-
-			try {
-				if (this.timeoutInMillis != 0
-						&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
-					timeoutTask = new CancelTask();
-					Connection.getCancelTimer().schedule(timeoutTask, 
-							this.timeoutInMillis);
-				}
-
-				String oldCatalog = null;
-
-				if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
-					oldCatalog = locallyScopedConn.getCatalog();
-					locallyScopedConn.setCatalog(this.currentCatalog);
-				}
-
-				//
-				// Only apply max_rows to selects
-				//
-				if (locallyScopedConn.useMaxRows()) {
-					locallyScopedConn.execSQL(
-							this,
-							"SET OPTION SQL_SELECT_LIMIT=DEFAULT", //$NON-NLS-1$
-							-1, null, java.sql.ResultSet.TYPE_FORWARD_ONLY,
-							java.sql.ResultSet.CONCUR_READ_ONLY, false,
-							this.currentCatalog, true);
-				}
-
-				rs = locallyScopedConn.execSQL(this, sql, -1, null,
-						java.sql.ResultSet.TYPE_FORWARD_ONLY,
-						java.sql.ResultSet.CONCUR_READ_ONLY, false,
-						this.currentCatalog,
-						true /* force read of field info on DML */,
-						isBatch);
-				
-				if (timeoutTask != null) {
-					timeoutTask.cancel();
-					timeoutTask = null;
-				}
-
-				if (oldCatalog != null) {
-					locallyScopedConn.setCatalog(oldCatalog);
-				}
-
-				if (this.wasCancelled) {
-					this.wasCancelled = false;
-					throw new MySQLTimeoutException();
-				}
-			} finally {
-				if (timeoutTask != null) {
-					timeoutTask.cancel();
-				}
-			}
-		}
-
-		this.results = rs;
-
-		rs.setFirstCharOfQuery(firstStatementChar);
-
-		this.updateCount = rs.getUpdateCount();
-
-		int truncatedUpdateCount = 0;
-
-		if (this.updateCount > Integer.MAX_VALUE) {
-			truncatedUpdateCount = Integer.MAX_VALUE;
-		} else {
-			truncatedUpdateCount = (int) this.updateCount;
-		}
-
-		this.lastInsertId = rs.getUpdateID();
-
-		return truncatedUpdateCount;
-	}
-
-	/**
-	 * @see Statement#executeUpdate(String, int)
-	 */
-	public int executeUpdate(String sql, int returnGeneratedKeys)
-			throws SQLException {
-		if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
-			checkClosed();
-
-			Connection locallyScopedConn = this.connection;
-			
-			synchronized (locallyScopedConn.getMutex()) {
-				// If this is a 'REPLACE' query, we need to be able to parse
-				// the 'info' message returned from the server to determine
-				// the actual number of keys generated.
-				boolean readInfoMsgState = locallyScopedConn
-						.isReadInfoMsgEnabled();
-				locallyScopedConn.setReadInfoMsgEnabled(true);
-
-				try {
-					return executeUpdate(sql);
-				} finally {
-					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
-				}
-			}
-		}
-
-		return executeUpdate(sql);
-	}
-
-	/**
-	 * @see Statement#executeUpdate(String, int[])
-	 */
-	public int executeUpdate(String sql, int[] generatedKeyIndices)
-			throws SQLException {
-		if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
-			checkClosed();
-			
-			Connection locallyScopedConn = this.connection;
-			
-			synchronized (locallyScopedConn.getMutex()) {
-				// If this is a 'REPLACE' query, we need to be able to parse
-				// the 'info' message returned from the server to determine
-				// the actual number of keys generated.
-				boolean readInfoMsgState = locallyScopedConn
-						.isReadInfoMsgEnabled();
-				locallyScopedConn.setReadInfoMsgEnabled(true);
-
-				try {
-					return executeUpdate(sql);
-				} finally {
-					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
-				}
-			}
-		}
-
-		return executeUpdate(sql);
-	}
-
-	/**
-	 * @see Statement#executeUpdate(String, String[])
-	 */
-	public int executeUpdate(String sql, String[] generatedKeyNames)
-			throws SQLException {
-		if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
-			checkClosed();
-
-			Connection locallyScopedConn = this.connection;
-			
-			synchronized (locallyScopedConn.getMutex()) {
-				// If this is a 'REPLACE' query, we need to be able to parse
-				// the 'info' message returned from the server to determine
-				// the actual number of keys generated.
-				boolean readInfoMsgState = this.connection
-						.isReadInfoMsgEnabled();
-				locallyScopedConn.setReadInfoMsgEnabled(true);
-
-				try {
-					return executeUpdate(sql);
-				} finally {
-					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
-				}
-			}
-		}
-
-		return executeUpdate(sql);
-	}
-
-	/**
-	 * Returns cached metadata (or null if not cached) for the given query,
-	 * which must match _exactly_. Note this method is guarded against
-	 * concurrent access via the synchronized{} block in execute() and
-	 * executeQuery().
-	 * 
-	 * @param sql
-	 *            the query that is the key to the cache
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	protected CachedResultSetMetaData getCachedMetaData(String sql) {
-		if (this.resultSetMetadataCache != null) {
-			return (CachedResultSetMetaData) this.resultSetMetadataCache
-					.get(sql);
-		}
-
-		return null; // no cache exists (yet)
-	}
-
-	/**
-	 * Optimization to only use one calendar per-session, or calculate it for
-	 * each call, depending on user configuration
-	 */
-	protected Calendar getCalendarInstanceForSessionOrNew() {
-		if (this.connection != null) {
-			return this.connection.getCalendarInstanceForSessionOrNew();
-		} else {
-			// punt, no connection around
-			return new GregorianCalendar();
-		}
-	}
-
-	/**
-	 * JDBC 2.0 Return the Connection that produced the Statement.
-	 * 
-	 * @return the Connection that produced the Statement
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs
-	 */
-	public java.sql.Connection getConnection() throws SQLException {
-		return this.connection;
-	}
-
-	/**
-	 * JDBC 2.0 Determine the fetch direction.
-	 * 
-	 * @return the default fetch direction
-	 * 
-	 * @exception SQLException
-	 *                if a database-access error occurs
-	 */
-	public int getFetchDirection() throws SQLException {
-		return java.sql.ResultSet.FETCH_FORWARD;
-	}
-
-	/**
-	 * JDBC 2.0 Determine the default fetch size.
-	 * 
-	 * @return the number of rows to fetch at a time
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs
-	 */
-	public int getFetchSize() throws SQLException {
-		return this.fetchSize;
-	}
-
-	/**
-	 * DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
-	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	public java.sql.ResultSet getGeneratedKeys()
-			throws SQLException {
-		if (this.batchedGeneratedKeys == null) {
-			return getGeneratedKeysInternal();
-		}
-
-		Field[] fields = new Field[1];
-		fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
-		fields[0].setConnection(this.connection);
-
-		return new com.mysql.jdbc.ResultSet(this.currentCatalog, fields,
-				new RowDataStatic(this.batchedGeneratedKeys), this.connection,
-				this);
-	}
-	
-	/*
-	 * Needed because there's no concept of super.super to get to this
-	 * implementation from ServerPreparedStatement when dealing with batched
-	 * updates.
-	 */
-	protected java.sql.ResultSet getGeneratedKeysInternal()
-			throws SQLException {
-		Field[] fields = new Field[1];
-		fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
-		fields[0].setConnection(this.connection);
-
-		ArrayList rowSet = new ArrayList();
-
-		long beginAt = getLastInsertID();
-		int numKeys = getUpdateCount();
-
-		if (this.results != null) {
-			String serverInfo = this.results.getServerInfo();
-	
-			// 
-			// Only parse server info messages for 'REPLACE'
-			// queries
-			//
-			if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R')
-					&& (serverInfo != null) && (serverInfo.length() > 0)) {
-				numKeys = getRecordCountFromInfo(serverInfo);
-			}
-	
-			if ((beginAt > 0) && (numKeys > 0)) {
-				for (int i = 0; i < numKeys; i++) {
-					byte[][] row = new byte[1][];
-					row[0] = Long.toString(beginAt++).getBytes();
-					rowSet.add(row);
-				}
-			}
-		}
-
-		return new com.mysql.jdbc.ResultSet(this.currentCatalog, fields,
-				new RowDataStatic(rowSet), this.connection, this);
-	}
-
-	/**
-	 * Returns the id used when profiling
-	 * 
-	 * @return the id used when profiling.
-	 */
-	protected int getId() {
-		return this.statementId;
-	}
-
-	/**
-	 * getLastInsertID returns the value of the auto_incremented key after an
-	 * executeQuery() or excute() call.
-	 * 
-	 * <p>
-	 * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
-	 * which is tied to the Connection that created this Statement, and
-	 * therefore could have had many INSERTS performed before one gets a chance
-	 * to call "select LAST_INSERT_ID()".
-	 * </p>
-	 * 
-	 * @return the last update ID.
-	 */
-	public long getLastInsertID() {
-		return this.lastInsertId;
-	}
-
-	/**
-	 * getLongUpdateCount returns the current result as an update count, if the
-	 * result is a ResultSet or there are no more results, -1 is returned. It
-	 * should only be called once per result.
-	 * 
-	 * <p>
-	 * This method returns longs as MySQL server versions newer than 3.22.4
-	 * return 64-bit values for update counts
-	 * </p>
-	 * 
-	 * @return the current update count.
-	 */
-	public long getLongUpdateCount() {
-		if (this.results == null) {
-			return -1;
-		}
-
-		if (this.results.reallyResult()) {
-			return -1;
-		}
-
-		return this.updateCount;
-	}
-
-	/**
-	 * The maxFieldSize limit (in bytes) is the maximum amount of data returned
-	 * for any column value; it only applies to BINARY, VARBINARY,
-	 * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
-	 * exceeded, the excess data is silently discarded.
-	 * 
-	 * @return the current max column size limit; zero means unlimited
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public int getMaxFieldSize() throws SQLException {
-		return this.maxFieldSize;
-	}
-
-	/**
-	 * The maxRows limit is set to limit the number of rows that any ResultSet
-	 * can contain. If the limit is exceeded, the excess rows are silently
-	 * dropped.
-	 * 
-	 * @return the current maximum row limit; zero means unlimited
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public int getMaxRows() throws SQLException {
-		if (this.maxRows <= 0) {
-			return 0;
-		}
-
-		return this.maxRows;
-	}
-
-	/**
-	 * getMoreResults moves to a Statement's next result. If it returns true,
-	 * this result is a ResulSet.
-	 * 
-	 * @return true if the next ResultSet is valid
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public boolean getMoreResults() throws SQLException {
-		return getMoreResults(CLOSE_CURRENT_RESULT);
-	}
-
-	/**
-	 * @see Statement#getMoreResults(int)
-	 */
-	public boolean getMoreResults(int current) throws SQLException {
-
-		if (this.results == null) {
-			return false;
-		}
-
-		ResultSet nextResultSet = this.results.getNextResultSet();
-
-		switch (current) {
-		case java.sql.Statement.CLOSE_CURRENT_RESULT:
-
-			if (this.results != null) {
-				this.results.close();
-				this.results.clearNextResult();
-			}
-
-			break;
-
-		case java.sql.Statement.CLOSE_ALL_RESULTS:
-
-			if (this.results != null) {
-				this.results.close();
-				this.results.clearNextResult();
-			}
-
-			closeAllOpenResults();
-
-			break;
-
-		case java.sql.Statement.KEEP_CURRENT_RESULT:
-			if (!this.connection.getDontTrackOpenResources()) {
-				this.openResults.add(this.results);
-			}
-
-			this.results.clearNextResult(); // nobody besides us should
-			// ever need this value...
-			break;
-
-		default:
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.19"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-		}
-
-		this.results = nextResultSet;
-
-		if (this.results == null) {
-			this.updateCount = -1;
-			this.lastInsertId = -1;
-		} else if (this.results.reallyResult()) {
-			this.updateCount = -1;
-			this.lastInsertId = -1;
-		} else {
-			this.updateCount = this.results.getUpdateCount();
-			this.lastInsertId = this.results.getUpdateID();
-		}
-
-		return ((this.results != null) && this.results.reallyResult()) ? true
-				: false;
-	}
-
-	/**
-	 * The queryTimeout limit is the number of seconds the driver will wait for
-	 * a Statement to execute. If the limit is exceeded, a SQLException is
-	 * thrown.
-	 * 
-	 * @return the current query timeout limit in seconds; 0 = unlimited
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public int getQueryTimeout() throws SQLException {
-		return this.timeoutInMillis / 1000;
-	}
-
-	/**
-	 * Parses actual record count from 'info' message
-	 * 
-	 * @param serverInfo
-	 *            DOCUMENT ME!
-	 * 
-	 * @return DOCUMENT ME!
-	 */
-	private int getRecordCountFromInfo(String serverInfo) {
-		StringBuffer recordsBuf = new StringBuffer();
-		int recordsCount = 0;
-		int duplicatesCount = 0;
-
-		char c = (char) 0;
-
-		int length = serverInfo.length();
-		int i = 0;
-
-		for (; i < length; i++) {
-			c = serverInfo.charAt(i);
-
-			if (Character.isDigit(c)) {
-				break;
-			}
-		}
-
-		recordsBuf.append(c);
-		i++;
-
-		for (; i < length; i++) {
-			c = serverInfo.charAt(i);
-
-			if (!Character.isDigit(c)) {
-				break;
-			}
-
-			recordsBuf.append(c);
-		}
-
-		recordsCount = Integer.parseInt(recordsBuf.toString());
-
-		StringBuffer duplicatesBuf = new StringBuffer();
-
-		for (; i < length; i++) {
-			c = serverInfo.charAt(i);
-
-			if (Character.isDigit(c)) {
-				break;
-			}
-		}
-
-		duplicatesBuf.append(c);
-		i++;
-
-		for (; i < length; i++) {
-			c = serverInfo.charAt(i);
-
-			if (!Character.isDigit(c)) {
-				break;
-			}
-
-			duplicatesBuf.append(c);
-		}
-
-		duplicatesCount = Integer.parseInt(duplicatesBuf.toString());
-
-		return recordsCount - duplicatesCount;
-	}
-
-	/**
-	 * getResultSet returns the current result as a ResultSet. It should only be
-	 * called once per result.
-	 * 
-	 * @return the current result set; null if there are no more
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs (why?)
-	 */
-	public java.sql.ResultSet getResultSet() throws SQLException {
-		return ((this.results != null) && this.results.reallyResult()) ? (java.sql.ResultSet) this.results
-				: null;
-	}
-
-	/**
-	 * JDBC 2.0 Determine the result set concurrency.
-	 * 
-	 * @return CONCUR_UPDATABLE or CONCUR_READONLY
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs
-	 */
-	public int getResultSetConcurrency() throws SQLException {
-		return this.resultSetConcurrency;
-	}
-
-	/**
-	 * @see Statement#getResultSetHoldability()
-	 */
-	public int getResultSetHoldability() throws SQLException {
-		return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
-	}
-
-	protected ResultSet getResultSetInternal() {
-		return this.results;
-	}
-
-	/**
-	 * JDBC 2.0 Determine the result set type.
-	 * 
-	 * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs.
-	 */
-	public int getResultSetType() throws SQLException {
-		return this.resultSetType;
-	}
-
-	/**
-	 * getUpdateCount returns the current result as an update count, if the
-	 * result is a ResultSet or there are no more results, -1 is returned. It
-	 * should only be called once per result.
-	 * 
-	 * @return the current result as an update count.
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public int getUpdateCount() throws SQLException {
-		if (this.results == null) {
-			return -1;
-		}
-
-		if (this.results.reallyResult()) {
-			return -1;
-		}
-
-		int truncatedUpdateCount = 0;
-
-		if (this.results.getUpdateCount() > Integer.MAX_VALUE) {
-			truncatedUpdateCount = Integer.MAX_VALUE;
-		} else {
-			truncatedUpdateCount = (int) this.results.getUpdateCount();
-		}
-
-		return truncatedUpdateCount;
-	}
-
-	/**
-	 * The first warning reported by calls on this Statement is returned. A
-	 * Statement's execute methods clear its java.sql.SQLWarning chain.
-	 * Subsequent Statement warnings will be chained to this
-	 * java.sql.SQLWarning.
-	 * 
-	 * <p>
-	 * The Warning chain is automatically cleared each time a statement is
-	 * (re)executed.
-	 * </p>
-	 * 
-	 * <p>
-	 * <B>Note:</B> If you are processing a ResultSet then any warnings
-	 * associated with ResultSet reads will be chained on the ResultSet object.
-	 * </p>
-	 * 
-	 * @return the first java.sql.SQLWarning or null
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public java.sql.SQLWarning getWarnings() throws SQLException {
-		checkClosed();
-
-		if (this.connection != null && !this.connection.isClosed()
-				&& this.connection.versionMeetsMinimum(4, 1, 0)) {
-			SQLWarning pendingWarningsFromServer = SQLError
-					.convertShowWarningsToSQLWarnings(this.connection);
-
-			if (this.warningChain != null) {
-				this.warningChain.setNextWarning(pendingWarningsFromServer);
-			} else {
-				this.warningChain = pendingWarningsFromServer;
-			}
-
-			return this.warningChain;
-		}
-
-		return this.warningChain;
-	}
-
-	/**
-	 * Caches CachedResultSetMetaData that has been placed in the cache using
-	 * the given SQL as a key.
-	 * 
-	 * @param sql
-	 *            DOCUMENT ME!
-	 * @param cachedMetaData
-	 *            DOCUMENT ME!
-	 * @param resultSet
-	 *            DOCUMENT ME!
-	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	protected void initializeResultsMetadataFromCache(String sql,
-			CachedResultSetMetaData cachedMetaData, ResultSet resultSet)
-			throws SQLException {
-		synchronized (resultSet) {
-			if (cachedMetaData == null) {
-				// read from results
-				cachedMetaData = new CachedResultSetMetaData();
-				cachedMetaData.fields = this.results.fields;
-
-				// assume that users will use named-based
-				// lookups
-				resultSet.buildIndexMapping();
-
-				cachedMetaData.columnNameToIndex = resultSet.columnNameToIndex;
-				cachedMetaData.fullColumnNameToIndex = resultSet.fullColumnNameToIndex;
-
-				cachedMetaData.metadata = resultSet.getMetaData();
-
-				if (this.resultSetMetadataCache == null) {
-					this.resultSetMetadataCache = new LRUCache(this.connection
-							.getMetadataCacheSize());
-				}
-
-				this.resultSetMetadataCache.put(sql, cachedMetaData);
-			} else {
-				// initialize results from cached data
-				resultSet.fields = cachedMetaData.fields;
-				resultSet.columnNameToIndex = cachedMetaData.columnNameToIndex;
-				resultSet.fullColumnNameToIndex = cachedMetaData.fullColumnNameToIndex;
-				resultSet.hasBuiltIndexMapping = true;
-
-				// results.resultSetMetaData = cachedMetaData.metadata;
-			}
-		}
-	}
-
-	/**
-	 * Closes this statement, and frees resources.
-	 * 
-	 * @param calledExplicitly
-	 *            was this called from close()?
-	 * 
-	 * @throws SQLException
-	 *             if an error occurs
-	 */
-	protected void realClose(boolean calledExplicitly, boolean closeOpenResults)
-			throws SQLException {
-		if (this.isClosed) {
-			return;
-		}
-
-		if (this.useUsageAdvisor) {
-			if (!calledExplicitly) {
-				String message = Messages.getString("Statement.63") //$NON-NLS-1$
-						+ Messages.getString("Statement.64"); //$NON-NLS-1$
-
-				this.eventSink.consumeEvent(new ProfilerEvent(
-						ProfilerEvent.TYPE_WARN,
-						"", //$NON-NLS-1$
-						this.currentCatalog, this.connectionId, this
-								.getId(), -1, System.currentTimeMillis(), 0,
-						null, this.pointOfOrigin, message));
-			}
-		}
-
-		if (this.results != null) {
-			if (closeOpenResults) {
-				closeOpenResults = !this.holdResultsOpenOverClose;
-			}
-
-			if (closeOpenResults && this.connection != null
-					&& !this.connection.getHoldResultsOpenOverStatementClose()) {
-				try {
-					this.results.close();
-				} catch (Exception ex) {
-					;
-				}
-
-				this.closeAllOpenResults();
-			}
-		}
-
-		if (this.connection != null) {
-			if (this.maxRowsChanged) {
-				this.connection.unsetMaxRows(this);
-			}
-
-			if (!this.connection.getDontTrackOpenResources()) {
-				this.connection.unregisterStatement(this);
-			}
-		}
-
-		this.results = null;
-		this.connection = null;
-		this.warningChain = null;
-		this.openResults = null;
-		this.batchedGeneratedKeys = null;
-		this.isClosed = true;
-	}
-
-	/**
-	 * setCursorName defines the SQL cursor name that will be used by subsequent
-	 * execute methods. This name can then be used in SQL positioned
-	 * update/delete statements to identify the current row in the ResultSet
-	 * generated by this statement. If a database doesn't support positioned
-	 * update/delete, this method is a no-op.
-	 * 
-	 * <p>
-	 * <b>Note:</b> This MySQL driver does not support cursors.
-	 * </p>
-	 * 
-	 * @param name
-	 *            the new cursor name
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public void setCursorName(String name) throws SQLException {
-		// No-op
-	}
-
-	/**
-	 * If escape scanning is on (the default), the driver will do escape
-	 * substitution before sending the SQL to the database.
-	 * 
-	 * @param enable
-	 *            true to enable; false to disable
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public void setEscapeProcessing(boolean enable)
-			throws SQLException {
-		this.doEscapeProcessing = enable;
-	}
-
-	/**
-	 * JDBC 2.0 Give a hint as to the direction in which the rows in a result
-	 * set will be processed. The hint applies only to result sets created using
-	 * this Statement object. The default value is ResultSet.FETCH_FORWARD.
-	 * 
-	 * @param direction
-	 *            the initial direction for processing rows
-	 * 
-	 * @exception SQLException
-	 *                if a database-access error occurs or direction is not one
-	 *                of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or
-	 *                ResultSet.FETCH_UNKNOWN
-	 */
-	public void setFetchDirection(int direction) throws SQLException {
-		switch (direction) {
-		case java.sql.ResultSet.FETCH_FORWARD:
-		case java.sql.ResultSet.FETCH_REVERSE:
-		case java.sql.ResultSet.FETCH_UNKNOWN:
-			break;
-
-		default:
-			throw SQLError.createSQLException(
-					Messages.getString("Statement.5"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-		}
-	}
-
-	/**
-	 * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should
-	 * be fetched from the database when more rows are needed. The number of
-	 * rows specified only affects result sets created using this statement. If
-	 * the value specified is zero, then the hint is ignored. The default value
-	 * is zero.
-	 * 
-	 * @param rows
-	 *            the number of rows to fetch
-	 * 
-	 * @exception SQLException
-	 *                if a database-access error occurs, or the condition 0
-	 *                &lt;= rows &lt;= this.getMaxRows() is not satisfied.
-	 */
-	public void setFetchSize(int rows) throws SQLException {
-		if (((rows < 0) && (rows != Integer.MIN_VALUE))
-				|| ((this.maxRows != 0) && (this.maxRows != -1) && (rows > this
-						.getMaxRows()))) {
-			throw SQLError.createSQLException(
-					Messages.getString("Statement.7"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-
-		this.fetchSize = rows;
-	}
-
-	protected void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) {
-		this.holdResultsOpenOverClose = holdResultsOpenOverClose;
-	}
-
-	/**
-	 * Sets the maxFieldSize
-	 * 
-	 * @param max
-	 *            the new max column size limit; zero means unlimited
-	 * 
-	 * @exception SQLException
-	 *                if size exceeds buffer size
-	 */
-	public void setMaxFieldSize(int max) throws SQLException {
-		if (max < 0) {
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.11"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-		}
-
-		int maxBuf = (this.connection != null) ? this.connection
-				.getMaxAllowedPacket() : MysqlIO.getMaxBuf();
-
-		if (max > maxBuf) {
-			throw SQLError.createSQLException(Messages.getString(
-					"Statement.13", //$NON-NLS-1$
-					new Object[] { new Long(maxBuf) }), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-		}
-
-		this.maxFieldSize = max;
-	}
-
-	/**
-	 * Set the maximum number of rows
-	 * 
-	 * @param max
-	 *            the new max rows limit; zero means unlimited
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 * 
-	 * @see getMaxRows
-	 */
-	public void setMaxRows(int max) throws SQLException {
-		if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
-			throw SQLError
-					.createSQLException(
-							Messages.getString("Statement.15") + max //$NON-NLS-1$
-									+ " > " //$NON-NLS-1$ //$NON-NLS-2$
-									+ MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-
-		if (max == 0) {
-			max = -1;
-		}
-
-		this.maxRows = max;
-		this.maxRowsChanged = true;
-
-		if (this.maxRows == -1) {
-			this.connection.unsetMaxRows(this);
-			this.maxRowsChanged = false;
-		} else {
-			// Most people don't use setMaxRows()
-			// so don't penalize them
-			// with the extra query it takes
-			// to do it efficiently unless we need
-			// to.
-			this.connection.maxRowsChanged(this);
-		}
-	}
-
-	/**
-	 * Sets the queryTimeout limit
-	 * 
-	 * @param seconds -
-	 *            the new query timeout limit in seconds
-	 * 
-	 * @exception SQLException
-	 *                if a database access error occurs
-	 */
-	public void setQueryTimeout(int seconds) throws SQLException {
-		if (seconds < 0) {
-			throw SQLError.createSQLException(Messages
-					.getString("Statement.21"), //$NON-NLS-1$
-					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
-		}
-
-		this.timeoutInMillis = seconds * 1000;
-	}
-
-	/**
-	 * Sets the concurrency for result sets generated by this statement
-	 * 
-	 * @param concurrencyFlag
-	 *            DOCUMENT ME!
-	 */
-	void setResultSetConcurrency(int concurrencyFlag) {
-		this.resultSetConcurrency = concurrencyFlag;
-	}
-
-	/**
-	 * Sets the result set type for result sets generated by this statement
-	 * 
-	 * @param typeFlag
-	 *            DOCUMENT ME!
-	 */
-	void setResultSetType(int typeFlag) {
-		this.resultSetType = typeFlag;
-	}
-
-	protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException {
-		if (this.retrieveGeneratedKeys) {
-			java.sql.ResultSet rs = null;
-	
-			try {
-				rs = batchedStatement.getGeneratedKeys();
-	
-				while (rs.next()) {
-					this.batchedGeneratedKeys
-							.add(new byte[][] { rs.getBytes(1) });
-				}
-			} finally {
-				if (rs != null) {
-					rs.close();
-				}
-			}
-		}
-	}
-	
-	protected void getBatchedGeneratedKeys() throws SQLException {
-		if (this.retrieveGeneratedKeys) {
-			java.sql.ResultSet rs = null;
-	
-			try {
-				rs = getGeneratedKeysInternal();
-	
-				while (rs.next()) {
-					this.batchedGeneratedKeys
-							.add(new byte[][] { rs.getBytes(1) });
-				}
-			} finally {
-				if (rs != null) {
-					rs.close();
-				}
-			}
-		}
-	}
-	
-	/**
-	 * @return
-	 */
-	private boolean useServerFetch() throws SQLException {
-
-		return this.connection.isCursorFetchEnabled() && this.fetchSize > 0
-				&& this.resultSetConcurrency == ResultSet.CONCUR_READ_ONLY
-				&& this.resultSetType == ResultSet.TYPE_FORWARD_ONLY;
-	}
-}
+	public void setPingTarget(PingTarget pingTarget);
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/StatementImpl.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/StatementImpl.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/StatementImpl.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,2609 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.exceptions.MySQLStatementCancelledException;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+import com.mysql.jdbc.exceptions.MySQLTimeoutException;
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+import com.mysql.jdbc.util.LRUCache;
+
+import java.io.InputStream;
+import java.sql.BatchUpdateException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Types;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TimerTask;
+
+/**
+ * A Statement object is used for executing a static SQL statement and obtaining
+ * the results produced by it.
+ *
+ * <p>
+ * Only one ResultSet per Statement can be open at any point in time. Therefore,
+ * if the reading of one ResultSet is interleaved with the reading of another,
+ * each must have been generated by different Statements. All statement execute
+ * methods implicitly close a statement's current ResultSet if an open one
+ * exists.
+ * </p>
+ *
+ * @author Mark Matthews
+ * @version $Id: Statement.java 4624 2005-11-28 14:24:29 -0600 (Mon, 28 Nov
+ *          2005) mmatthews $
+ *
+ * @see java.sql.Statement
+ * @see ResultSetInternalMethods
+ */
+public class StatementImpl implements Statement {
+        protected static final String PING_MARKER = "/* ping */";
+	/**
+	 * Thread used to implement query timeouts...Eventually we could be more
+	 * efficient and have one thread with timers, but this is a straightforward
+	 * and simple way to implement a feature that isn't used all that often.
+	 */
+	class CancelTask extends TimerTask {
+
+		long connectionId = 0;
+		SQLException caughtWhileCancelling = null;
+		StatementImpl toCancel;
+		
+		CancelTask(StatementImpl cancellee) throws SQLException {
+			connectionId = connection.getIO().getThreadId();
+			toCancel = cancellee;
+		}
+
+		public void run() {
+
+			Thread cancelThread = new Thread() {
+
+				public void run() {
+					Connection cancelConn = null;
+					java.sql.Statement cancelStmt = null;
+
+					try {
+						synchronized (cancelTimeoutMutex) {
+							cancelConn = connection.duplicate();
+							cancelStmt = cancelConn.createStatement();
+							cancelStmt.execute("KILL QUERY " + connectionId);
+							toCancel.wasCancelled = true;
+							toCancel.wasCancelledByTimeout = true;
+						}
+					} catch (SQLException sqlEx) {
+						caughtWhileCancelling = sqlEx;
+					} catch (NullPointerException npe) {
+						// Case when connection closed while starting to cancel
+						// We can't easily synchronize this, because then one thread
+						// can't cancel() a running query
+
+						// ignore, we shouldn't re-throw this, because the connection's
+						// already closed, so the statement has been timed out.
+					} finally {
+						if (cancelStmt != null) {
+							try {
+								cancelStmt.close();
+							} catch (SQLException sqlEx) {
+								throw new RuntimeException(sqlEx.toString());
+							}
+						}
+
+						if (cancelConn != null) {
+							try {
+								cancelConn.close();
+							} catch (SQLException sqlEx) {
+								throw new RuntimeException(sqlEx.toString());
+							}
+						}
+					}
+				}
+			};
+
+			cancelThread.start();
+		}
+	}
+
+	/** Mutex to prevent race between returning query results and noticing
+    that we're timed-out or cancelled. */
+
+	protected Object cancelTimeoutMutex = new Object();
+
+	/** Used to generate IDs when profiling. */
+	protected static int statementCounter = 1;
+
+	public final static byte USES_VARIABLES_FALSE = 0;
+
+	public final static byte USES_VARIABLES_TRUE = 1;
+
+	public final static byte USES_VARIABLES_UNKNOWN = -1;
+
+	protected boolean wasCancelled = false;
+	protected boolean wasCancelledByTimeout = false;
+
+	/** Holds batched commands */
+	protected List batchedArgs;
+
+	/** The character converter to use (if available) */
+	protected SingleByteCharsetConverter charConverter = null;
+
+	/** The character encoding to use (if available) */
+	protected String charEncoding = null;
+
+	/** The connection that created us */
+	protected ConnectionImpl connection = null;
+
+	protected long connectionId = 0;
+
+	/** The catalog in use */
+	protected String currentCatalog = null;
+
+	/** Should we process escape codes? */
+	protected boolean doEscapeProcessing = true;
+
+	/** If we're profiling, where should events go to? */
+	protected ProfileEventSink eventSink = null;
+
+	/** The number of rows to fetch at a time (currently ignored) */
+	private int fetchSize = 0;
+
+	/** Has this statement been closed? */
+	protected boolean isClosed = false;
+
+	/** The auto_increment value for the last insert */
+	protected long lastInsertId = -1;
+
+	/** The max field size for this statement */
+	protected int maxFieldSize = MysqlIO.getMaxBuf();
+
+	/**
+	 * The maximum number of rows to return for this statement (-1 means _all_
+	 * rows)
+	 */
+	protected int maxRows = -1;
+
+	/** Has someone changed this for this statement? */
+	protected boolean maxRowsChanged = false;
+
+	/** List of currently-open ResultSets */
+	protected List openResults = new ArrayList();
+
+	/** Are we in pedantic mode? */
+	protected boolean pedantic = false;
+
+	/**
+	 * Where this statement was created, only used if profileSql or
+	 * useUsageAdvisor set to true.
+	 */
+	protected Throwable pointOfOrigin;
+
+	/** Should we profile? */
+	protected boolean profileSQL = false;
+
+	/** The current results */
+	protected ResultSetInternalMethods results = null;
+
+	/** The concurrency for this result set (updatable or not) */
+	protected int resultSetConcurrency = 0;
+
+	/** The type of this result set (scroll sensitive or in-sensitive) */
+	protected int resultSetType = 0;
+
+	/** Used to identify this statement when profiling. */
+	protected int statementId;
+
+	/** The timeout for a query */
+	protected int timeoutInMillis = 0;
+
+	/** The update count for this statement */
+	protected long updateCount = -1;
+
+	/** Should we use the usage advisor? */
+	protected boolean useUsageAdvisor = false;
+
+	/** The warnings chain. */
+	protected SQLWarning warningChain = null;
+
+	/**
+	 * Should this statement hold results open over .close() irregardless of
+	 * connection's setting?
+	 */
+	protected boolean holdResultsOpenOverClose = false;
+
+	protected ArrayList batchedGeneratedKeys = null;
+
+	protected boolean retrieveGeneratedKeys = false;
+
+	protected boolean continueBatchOnError = false;
+
+	protected PingTarget pingTarget = null;
+	
+	
+	/**
+	 * Constructor for a Statement.
+	 *
+	 * @param c
+	 *            the Connection instantation that creates us
+	 * @param catalog
+	 *            the database name in use when we were created
+	 *
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	public StatementImpl(ConnectionImpl c, String catalog) throws SQLException {
+		if ((c == null) || c.isClosed()) {
+			throw SQLError.createSQLException(
+					Messages.getString("Statement.0"), //$NON-NLS-1$
+					SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		this.connection = c;
+		this.connectionId = this.connection.getId();
+
+		this.currentCatalog = catalog;
+		this.pedantic = this.connection.getPedantic();
+		this.continueBatchOnError = this.connection.getContinueBatchOnError();
+
+		if (!this.connection.getDontTrackOpenResources()) {
+			this.connection.registerStatement(this);
+		}
+
+		//
+		// Adjust, if we know it
+		//
+
+		if (this.connection != null) {
+			this.maxFieldSize = this.connection.getMaxAllowedPacket();
+
+			int defaultFetchSize = this.connection.getDefaultFetchSize();
+
+			if (defaultFetchSize != 0) {
+				setFetchSize(defaultFetchSize);
+			}
+		}
+
+		if (this.connection.getUseUnicode()) {
+			this.charEncoding = this.connection.getEncoding();
+
+			this.charConverter = this.connection
+					.getCharsetConverter(this.charEncoding);
+		}
+
+		boolean profiling = this.connection.getProfileSql()
+				|| this.connection.getUseUsageAdvisor();
+
+		if (this.connection.getAutoGenerateTestcaseScript() || profiling) {
+			this.statementId = statementCounter++;
+		}
+
+		if (profiling) {
+			this.pointOfOrigin = new Throwable();
+			this.profileSQL = this.connection.getProfileSql();
+			this.useUsageAdvisor = this.connection.getUseUsageAdvisor();
+			this.eventSink = ProfileEventSink.getInstance(this.connection);
+		}
+
+		int maxRowsConn = this.connection.getMaxRows();
+
+		if (maxRowsConn != -1) {
+			setMaxRows(maxRowsConn);
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 *
+	 * @param sql
+	 *            DOCUMENT ME!
+	 *
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public synchronized void addBatch(String sql) throws SQLException {
+		if (this.batchedArgs == null) {
+			this.batchedArgs = new ArrayList();
+		}
+
+		if (sql != null) {
+			this.batchedArgs.add(sql);
+		}
+	}
+
+	/**
+	 * Cancels this Statement object if both the DBMS and driver support
+	 * aborting an SQL statement. This method can be used by one thread to
+	 * cancel a statement that is being executed by another thread.
+	 */
+	public void cancel() throws SQLException {
+		if (!this.isClosed &&
+				this.connection != null &&
+				this.connection.versionMeetsMinimum(5, 0, 0)) {
+			Connection cancelConn = null;
+			java.sql.Statement cancelStmt = null;
+
+			try {
+				cancelConn = this.connection.duplicate();
+				cancelStmt = cancelConn.createStatement();
+				cancelStmt.execute("KILL QUERY "
+						+ this.connection.getIO().getThreadId());
+				this.wasCancelled = true;
+			} finally {
+				if (cancelStmt != null) {
+					cancelStmt.close();
+				}
+
+				if (cancelConn != null) {
+					cancelConn.close();
+				}
+			}
+
+		}
+	}
+
+	// --------------------------JDBC 2.0-----------------------------
+
+	/**
+	 * Checks if closed() has been called, and throws an exception if so
+	 *
+	 * @throws SQLException
+	 *             if this statement has been closed
+	 */
+	protected void checkClosed() throws SQLException {
+		if (this.isClosed) {
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.49"), //$NON-NLS-1$
+					SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Checks if the given SQL query with the given first non-ws char is a DML
+	 * statement. Throws an exception if it is.
+	 *
+	 * @param sql
+	 *            the SQL to check
+	 * @param firstStatementChar
+	 *            the UC first non-ws char of the statement
+	 *
+	 * @throws SQLException
+	 *             if the statement contains DML
+	 */
+	protected void checkForDml(String sql, char firstStatementChar)
+			throws SQLException {
+		if ((firstStatementChar == 'I') || (firstStatementChar == 'U')
+				|| (firstStatementChar == 'D') || (firstStatementChar == 'A')
+				|| (firstStatementChar == 'C')) {
+			String noCommentSql = StringUtils.stripComments(sql,
+					"'\"", "'\"", true, false, true, true);
+
+			if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") //$NON-NLS-1$
+					|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") //$NON-NLS-1$
+					|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") //$NON-NLS-1$
+					|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") //$NON-NLS-1$
+					|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") //$NON-NLS-1$
+					|| StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")) { //$NON-NLS-1$
+				throw SQLError.createSQLException(Messages
+						.getString("Statement.57"), //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+		}
+	}
+
+	/**
+	 * Method checkNullOrEmptyQuery.
+	 *
+	 * @param sql
+	 *            the SQL to check
+	 *
+	 * @throws SQLException
+	 *             if query is null or empty.
+	 */
+	protected void checkNullOrEmptyQuery(String sql) throws SQLException {
+		if (sql == null) {
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.59"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		if (sql.length() == 0) {
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.61"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Make the set of commands in the current batch empty. This method
+	 * is optional.
+	 *
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the driver does not
+	 *                support batch statements
+	 */
+	public synchronized void clearBatch() throws SQLException {
+		if (this.batchedArgs != null) {
+			this.batchedArgs.clear();
+		}
+	}
+
+	/**
+	 * After this call, getWarnings returns null until a new warning is reported
+	 * for this Statement.
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs (why?)
+	 */
+	public void clearWarnings() throws SQLException {
+		this.warningChain = null;
+	}
+
+	/**
+	 * In many cases, it is desirable to immediately release a Statement's
+	 * database and JDBC resources instead of waiting for this to happen when it
+	 * is automatically closed. The close method provides this immediate
+	 * release.
+	 *
+	 * <p>
+	 * <B>Note:</B> A Statement is automatically closed when it is garbage
+	 * collected. When a Statement is closed, its current ResultSet, if one
+	 * exists, is also closed.
+	 * </p>
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void close() throws SQLException {
+		realClose(true, true);
+	}
+
+	/**
+	 * Close any open result sets that have been 'held open'
+	 */
+	protected void closeAllOpenResults() {
+		if (this.openResults != null) {
+			for (Iterator iter = this.openResults.iterator(); iter.hasNext();) {
+				ResultSetInternalMethods element = (ResultSetInternalMethods) iter.next();
+
+				try {
+					element.realClose(false);
+				} catch (SQLException sqlEx) {
+					AssertionFailedException.shouldNotHappen(sqlEx);
+				}
+			}
+
+			this.openResults.clear();
+		}
+	}
+
+	/**
+	 * @param sql
+	 * @return
+	 */
+	private ResultSetInternalMethods createResultSetUsingServerFetch(String sql)
+			throws SQLException {
+		java.sql.PreparedStatement pStmt = this.connection.prepareStatement(
+				sql, this.resultSetType, this.resultSetConcurrency);
+
+		pStmt.setFetchSize(this.fetchSize);
+
+		if (this.maxRows > -1) {
+			pStmt.setMaxRows(this.maxRows);
+		}
+
+		pStmt.execute();
+
+		//
+		// Need to be able to get resultset irrespective if we issued DML or
+		// not to make this work.
+		//
+		ResultSetInternalMethods rs = ((com.mysql.jdbc.StatementImpl) pStmt)
+				.getResultSetInternal();
+
+		rs
+				.setStatementUsedForFetchingRows((com.mysql.jdbc.PreparedStatement) pStmt);
+
+		this.results = rs;
+
+		return rs;
+	}
+
+	/**
+	 * We only stream result sets when they are forward-only, read-only, and the
+	 * fetch size has been set to Integer.MIN_VALUE
+	 *
+	 * @return true if this result set should be streamed row at-a-time, rather
+	 *         than read all at once.
+	 */
+	protected boolean createStreamingResultSet() {
+		return ((this.resultSetType == java.sql.ResultSet.TYPE_FORWARD_ONLY)
+				&& (this.resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY) && (this.fetchSize == Integer.MIN_VALUE));
+	}
+
+	private int originalResultSetType = 0;
+	private int originalFetchSize = 0;
+
+	/* (non-Javadoc)
+	 * @see com.mysql.jdbc.IStatement#enableStreamingResults()
+	 */
+	public void enableStreamingResults() throws SQLException {
+		this.originalResultSetType = this.resultSetType;
+		this.originalFetchSize = this.fetchSize;
+
+		setFetchSize(Integer.MIN_VALUE);
+		setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
+	}
+
+	public void disableStreamingResults() throws SQLException {
+		if (this.fetchSize == Integer.MIN_VALUE &&
+				this.resultSetType == ResultSet.TYPE_FORWARD_ONLY) {
+			setFetchSize(this.originalFetchSize);
+			setResultSetType(this.originalResultSetType);
+		}
+	}
+
+	/**
+	 * Execute a SQL statement that may return multiple results. We don't have
+	 * to worry about this since we do not support multiple ResultSets. You can
+	 * use getResultSet or getUpdateCount to retrieve the result.
+	 *
+	 * @param sql
+	 *            any SQL statement
+	 *
+	 * @return true if the next result is a ResulSet, false if it is an update
+	 *         count or there are no more results
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean execute(String sql) throws SQLException {
+		checkClosed();
+
+		ConnectionImpl locallyScopedConn = this.connection;
+
+		synchronized (locallyScopedConn.getMutex()) {
+			resetCancelledState();
+
+			checkNullOrEmptyQuery(sql);
+
+			checkClosed();
+
+			char firstNonWsChar = StringUtils.firstAlphaCharUc(sql, findStartOfStatement(sql));
+
+			boolean isSelect = true;
+
+			if (firstNonWsChar != 'S') {
+				isSelect = false;
+
+				if (locallyScopedConn.isReadOnly()) {
+					throw SQLError.createSQLException(Messages
+							.getString("Statement.27") //$NON-NLS-1$
+							+ Messages.getString("Statement.28"), //$NON-NLS-1$
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+			}
+
+			boolean doStreaming = createStreamingResultSet();
+
+			// Adjust net_write_timeout to a higher value if we're
+			// streaming result sets. More often than not, someone runs into
+			// an issue where they blow net_write_timeout when using this
+			// feature, and if they're willing to hold a result set open
+			// for 30 seconds or more, one more round-trip isn't going to hurt
+			//
+			// This is reset by RowDataDynamic.close().
+
+			if (doStreaming
+					&& this.connection.getNetTimeoutForStreamingResults() > 0) {
+				executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
+						+ this.connection.getNetTimeoutForStreamingResults());
+			}
+
+			if (this.doEscapeProcessing) {
+				Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
+						locallyScopedConn.serverSupportsConvertFn(), locallyScopedConn);
+
+				if (escapedSqlResult instanceof String) {
+					sql = (String) escapedSqlResult;
+				} else {
+					sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
+				}
+			}
+
+			if (this.results != null) {
+				if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
+					this.results.realClose(false);
+				}
+			}
+
+			if (sql.charAt(0) == '/') {
+				if (sql.startsWith(PING_MARKER)) {
+					doPingInstead();
+				
+					return true;
+				}
+			}
+
+			CachedResultSetMetaData cachedMetaData = null;
+
+			ResultSetInternalMethods rs = null;
+
+			// If there isn't a limit clause in the SQL
+			// then limit the number of rows to return in
+			// an efficient manner. Only do this if
+			// setMaxRows() hasn't been used on any Statements
+			// generated from the current Connection (saves
+			// a query, and network traffic).
+
+			this.batchedGeneratedKeys = null;
+
+			if (useServerFetch()) {
+				rs = createResultSetUsingServerFetch(sql);
+			} else {
+				CancelTask timeoutTask = null;
+
+				String oldCatalog = null;
+
+				try {
+					if (locallyScopedConn.getEnableQueryTimeouts() &&
+							this.timeoutInMillis != 0
+							&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
+						timeoutTask = new CancelTask(this);
+						ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+								this.timeoutInMillis);
+					}
+
+
+
+					if (!locallyScopedConn.getCatalog().equals(
+							this.currentCatalog)) {
+						oldCatalog = locallyScopedConn.getCatalog();
+						locallyScopedConn.setCatalog(this.currentCatalog);
+					}
+
+					//
+					// Check if we have cached metadata for this query...
+					//
+
+					Field[] cachedFields = null;
+
+					if (locallyScopedConn.getCacheResultSetMetadata()) {
+						cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
+
+						if (cachedMetaData != null) {
+							cachedFields = cachedMetaData.fields;
+						}
+					}
+
+					//
+					// Only apply max_rows to selects
+					//
+					if (locallyScopedConn.useMaxRows()) {
+						int rowLimit = -1;
+
+						if (isSelect) {
+							if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
+								rowLimit = this.maxRows;
+							} else {
+								if (this.maxRows <= 0) {
+									executeSimpleNonQuery(locallyScopedConn,
+											"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
+								} else {
+									executeSimpleNonQuery(locallyScopedConn,
+											"SET OPTION SQL_SELECT_LIMIT="
+													+ this.maxRows);
+								}
+							}
+						} else {
+							executeSimpleNonQuery(locallyScopedConn,
+									"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
+						}
+
+						// Finally, execute the query
+						rs = locallyScopedConn.execSQL(this, sql, rowLimit, null,
+								this.resultSetType, this.resultSetConcurrency,
+								doStreaming,
+								this.currentCatalog, cachedFields);
+					} else {
+						rs = locallyScopedConn.execSQL(this, sql, -1, null,
+								this.resultSetType, this.resultSetConcurrency,
+								doStreaming,
+								this.currentCatalog, cachedFields);
+					}
+
+					if (timeoutTask != null) {
+						if (timeoutTask.caughtWhileCancelling != null) {
+							throw timeoutTask.caughtWhileCancelling;
+						}
+
+						timeoutTask.cancel();
+						timeoutTask = null;
+					}
+
+					synchronized (this.cancelTimeoutMutex) {
+						if (this.wasCancelled) {
+							SQLException cause = null;
+							
+							if (this.wasCancelledByTimeout) {
+								cause = new MySQLTimeoutException();
+							} else {
+								cause = new MySQLStatementCancelledException();
+							}
+							
+							resetCancelledState();
+							
+							throw cause;
+						}
+					}
+				} finally {
+					if (timeoutTask != null) {
+						timeoutTask.cancel();
+					}
+
+					if (oldCatalog != null) {
+						locallyScopedConn.setCatalog(oldCatalog);
+					}
+				}
+			}
+
+			if (rs != null) {
+				this.lastInsertId = rs.getUpdateID();
+
+				this.results = rs;
+
+				rs.setFirstCharOfQuery(firstNonWsChar);
+
+				if (rs.reallyResult()) {
+					if (cachedMetaData != null) {
+						locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
+								this.results);
+					} else {
+						if (this.connection.getCacheResultSetMetadata()) {
+							locallyScopedConn.initializeResultsMetadataFromCache(sql,
+									null /* will be created */, this.results);
+						}
+					}
+				}
+			}
+
+			return ((rs != null) && rs.reallyResult());
+		}
+	}
+
+	protected synchronized void resetCancelledState() {
+		if (this.cancelTimeoutMutex == null) {
+			return;
+		}
+		
+		synchronized (this.cancelTimeoutMutex) {
+			this.wasCancelled = false;
+			this.wasCancelledByTimeout = false;
+		}
+	}
+
+	/**
+	 * @see StatementImpl#execute(String, int)
+	 */
+	public boolean execute(String sql, int returnGeneratedKeys)
+			throws SQLException {
+
+
+		if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
+			checkClosed();
+
+			ConnectionImpl locallyScopedConn = this.connection;
+
+			synchronized (locallyScopedConn.getMutex()) {
+				// If this is a 'REPLACE' query, we need to be able to parse
+				// the 'info' message returned from the server to determine
+				// the actual number of keys generated.
+				boolean readInfoMsgState = this.connection
+						.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+
+				try {
+					return execute(sql);
+				} finally {
+					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
+				}
+			}
+		}
+
+		return execute(sql);
+	}
+
+	/**
+	 * @see StatementImpl#execute(String, int[])
+	 */
+	public boolean execute(String sql, int[] generatedKeyIndices)
+			throws SQLException {
+		if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
+			checkClosed();
+
+			ConnectionImpl locallyScopedConn = this.connection;
+
+			synchronized (locallyScopedConn.getMutex()) {
+				// If this is a 'REPLACE' query, we need to be able to parse
+				// the 'info' message returned from the server to determine
+				// the actual number of keys generated.
+				boolean readInfoMsgState = locallyScopedConn
+						.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+
+				try {
+					return execute(sql);
+				} finally {
+					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
+				}
+			}
+		}
+
+		return execute(sql);
+	}
+
+	/**
+	 * @see StatementImpl#execute(String, String[])
+	 */
+	public boolean execute(String sql, String[] generatedKeyNames)
+			throws SQLException {
+		if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
+			checkClosed();
+
+			ConnectionImpl locallyScopedConn = this.connection;
+
+			synchronized (locallyScopedConn.getMutex()) {
+				// If this is a 'REPLACE' query, we need to be able to parse
+				// the 'info' message returned from the server to determine
+				// the actual number of keys generated.
+				boolean readInfoMsgState = this.connection
+						.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+
+				try {
+					return execute(sql);
+				} finally {
+					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
+				}
+			}
+		}
+
+		return execute(sql);
+	}
+
+	/**
+	 * JDBC 2.0 Submit a batch of commands to the database for execution. This
+	 * method is optional.
+	 *
+	 * @return an array of update counts containing one element for each command
+	 *         in the batch. The array is ordered according to the order in
+	 *         which commands were inserted into the batch
+	 *
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the driver does not
+	 *                support batch statements
+	 * @throws java.sql.BatchUpdateException
+	 *             DOCUMENT ME!
+	 */
+	public synchronized int[] executeBatch() throws SQLException {
+		checkClosed();
+
+		ConnectionImpl locallyScopedConn = this.connection;
+
+		if (locallyScopedConn.isReadOnly()) {
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.34") //$NON-NLS-1$
+					+ Messages.getString("Statement.35"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		if (this.results != null) {
+			if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
+				this.results.realClose(false);
+			}
+		}
+
+		synchronized (locallyScopedConn.getMutex()) {
+			if (this.batchedArgs == null || this.batchedArgs.size() == 0) {
+                return new int[0];
+            }
+			
+			// we timeout the entire batch, not individual statements
+			int individualStatementTimeout = this.timeoutInMillis;
+			this.timeoutInMillis = 0;
+			
+			CancelTask timeoutTask = null;
+			
+			try {
+				resetCancelledState();
+
+				this.retrieveGeneratedKeys = true;
+
+				int[] updateCounts = null;
+
+				
+				if (this.batchedArgs != null) {
+					int nbrCommands = this.batchedArgs.size();
+
+					this.batchedGeneratedKeys = new ArrayList(this.batchedArgs.size());
+
+					boolean multiQueriesEnabled = locallyScopedConn.getAllowMultiQueries();
+
+					if (locallyScopedConn.versionMeetsMinimum(4, 1, 1) &&
+							(multiQueriesEnabled ||
+							(locallyScopedConn.getRewriteBatchedStatements() &&
+									nbrCommands > 4))) {
+						return executeBatchUsingMultiQueries(multiQueriesEnabled, nbrCommands, individualStatementTimeout);
+					}
+
+					if (locallyScopedConn.getEnableQueryTimeouts() &&
+							individualStatementTimeout != 0
+							&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
+						timeoutTask = new CancelTask(this);
+						ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+								individualStatementTimeout);
+					}
+					
+					updateCounts = new int[nbrCommands];
+
+					for (int i = 0; i < nbrCommands; i++) {
+						updateCounts[i] = -3;
+					}
+
+					SQLException sqlEx = null;
+
+					int commandIndex = 0;
+
+					for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
+						try {
+							updateCounts[commandIndex] = executeUpdate((String) this.batchedArgs
+									.get(commandIndex), true);
+							getBatchedGeneratedKeys();
+						} catch (SQLException ex) {
+							updateCounts[commandIndex] = EXECUTE_FAILED;
+
+							if (this.continueBatchOnError && 
+									!(ex instanceof MySQLTimeoutException) && 
+									!(ex instanceof MySQLStatementCancelledException)) {
+								sqlEx = ex;
+							} else {
+								int[] newUpdateCounts = new int[commandIndex];
+								System.arraycopy(updateCounts, 0,
+										newUpdateCounts, 0, commandIndex);
+
+								throw new java.sql.BatchUpdateException(ex
+										.getMessage(), ex.getSQLState(), ex
+										.getErrorCode(), newUpdateCounts);
+							}
+						}
+					}
+
+					if (sqlEx != null) {
+						throw new java.sql.BatchUpdateException(sqlEx
+								.getMessage(), sqlEx.getSQLState(), sqlEx
+								.getErrorCode(), updateCounts);
+					}
+				}
+
+				if (timeoutTask != null) {
+					if (timeoutTask.caughtWhileCancelling != null) {
+						throw timeoutTask.caughtWhileCancelling;
+					}
+
+					timeoutTask.cancel();
+					timeoutTask = null;
+				}
+				
+				return (updateCounts != null) ? updateCounts : new int[0];
+			} finally {
+				
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
+				
+				resetCancelledState();
+				
+				this.timeoutInMillis = individualStatementTimeout;
+				this.retrieveGeneratedKeys = false;
+
+				clearBatch();
+			}
+		}
+	}
+
+	/**
+	 * Rewrites batch into a single query to send to the server. This method
+	 * will constrain each batch to be shorter than max_allowed_packet on the
+	 * server.
+	 *
+	 * @return update counts in the same manner as executeBatch()
+	 * @throws SQLException
+	 */
+	private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
+			int nbrCommands, int individualStatementTimeout) throws SQLException {
+
+		ConnectionImpl locallyScopedConn = this.connection;
+
+		if (!multiQueriesEnabled) {
+			locallyScopedConn.getIO().enableMultiQueries();
+		}
+
+		java.sql.Statement batchStmt = null;
+
+		CancelTask timeoutTask = null;
+		
+		try {
+			int[] updateCounts = new int[nbrCommands];
+
+			for (int i = 0; i < nbrCommands; i++) {
+				updateCounts[i] = -3;
+			}
+
+			int commandIndex = 0;
+
+			StringBuffer queryBuf = new StringBuffer();
+
+			batchStmt = locallyScopedConn.createStatement();
+
+			if (locallyScopedConn.getEnableQueryTimeouts() &&
+					individualStatementTimeout != 0
+					&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
+				timeoutTask = new CancelTask((StatementImpl)batchStmt);
+				ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+						individualStatementTimeout);
+			}
+			
+			int counter = 0;
+
+			int numberOfBytesPerChar = 1;
+
+			String connectionEncoding = locallyScopedConn.getEncoding();
+
+			if (StringUtils.startsWithIgnoreCase(connectionEncoding, "utf")) {
+				numberOfBytesPerChar = 3;
+			} else if (CharsetMapping.isMultibyteCharset(connectionEncoding)) {
+				numberOfBytesPerChar = 2;
+			}
+
+			int escapeAdjust = 1;
+
+			if (this.doEscapeProcessing) {
+				escapeAdjust = 2; /* We assume packet _could_ grow by this amount, as we're not
+				                     sure how big statement will end up after
+				                     escape processing */
+			}
+
+			SQLException sqlEx = null;
+			
+			int argumentSetsInBatchSoFar = 0;
+			
+			for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
+				String nextQuery = (String) this.batchedArgs.get(commandIndex);
+
+				if (((((queryBuf.length() + nextQuery.length())
+						* numberOfBytesPerChar) + 1 /* for semicolon */
+						+ MysqlIO.HEADER_LENGTH) * escapeAdjust)  + 32 > this.connection
+						.getMaxAllowedPacket()) {
+					try {
+						batchStmt.execute(queryBuf.toString());
+					} catch (SQLException ex) {
+						sqlEx = handleExceptionForBatch(commandIndex,
+								argumentSetsInBatchSoFar, updateCounts, ex);
+					}
+
+					counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter, 
+							updateCounts);
+
+					queryBuf = new StringBuffer();
+					argumentSetsInBatchSoFar = 0;
+				}
+
+				queryBuf.append(nextQuery);
+				queryBuf.append(";");
+				argumentSetsInBatchSoFar++;
+			}
+
+			if (queryBuf.length() > 0) {
+				try {
+					batchStmt.execute(queryBuf.toString());
+				} catch (SQLException ex) {
+					sqlEx = handleExceptionForBatch(commandIndex - 1,
+							argumentSetsInBatchSoFar, updateCounts, ex);
+				}
+
+				counter = processMultiCountsAndKeys((StatementImpl)batchStmt, counter, 
+						updateCounts);
+			}
+
+			if (timeoutTask != null) {
+				if (timeoutTask.caughtWhileCancelling != null) {
+					throw timeoutTask.caughtWhileCancelling;
+				}
+
+				timeoutTask.cancel();
+				timeoutTask = null;
+			}
+			
+			if (sqlEx != null) {
+				throw new java.sql.BatchUpdateException(sqlEx
+						.getMessage(), sqlEx.getSQLState(), sqlEx
+						.getErrorCode(), updateCounts);
+			}
+			
+			return (updateCounts != null) ? updateCounts : new int[0];
+		} finally {
+			if (timeoutTask != null) {
+				timeoutTask.cancel();
+			}
+			
+			resetCancelledState();
+			
+			try {
+				if (batchStmt != null) {
+					batchStmt.close();
+				}
+			} finally {
+				if (!multiQueriesEnabled) {
+					locallyScopedConn.getIO().disableMultiQueries();
+				}
+			}
+		}
+	}
+	
+	protected int processMultiCountsAndKeys(
+			StatementImpl batchedStatement,
+			int updateCountCounter, int[] updateCounts) throws SQLException {
+		updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
+		
+		boolean doGenKeys = this.batchedGeneratedKeys != null;
+		
+		long generatedKeyStart = 0;
+		byte[][] row = null;
+		
+		if (doGenKeys) {
+			generatedKeyStart = batchedStatement.getLastInsertID();
+		
+			row = new byte[1][];
+			row[0] = Long.toString(generatedKeyStart++).getBytes();
+			this.batchedGeneratedKeys.add(new ByteArrayRow(row));
+		}
+
+		while (batchedStatement.getMoreResults()
+				|| batchedStatement.getUpdateCount() != -1) {
+			updateCounts[updateCountCounter++] = batchedStatement.getUpdateCount();
+			
+			if (doGenKeys) {
+				row = new byte[1][];
+				row[0] = Long.toString(generatedKeyStart++).getBytes();
+				this.batchedGeneratedKeys.add(new ByteArrayRow(row));
+			}
+		}
+		
+		return updateCountCounter;
+	}
+	
+	protected SQLException handleExceptionForBatch(int endOfBatchIndex,
+			int numValuesPerBatch, int[] updateCounts, SQLException ex)
+			throws BatchUpdateException {
+		SQLException sqlEx;
+	
+		for (int j = endOfBatchIndex; j > endOfBatchIndex - numValuesPerBatch; j--) {
+			updateCounts[j] = EXECUTE_FAILED;
+		}
+
+		if (this.continueBatchOnError && 
+				!(ex instanceof MySQLTimeoutException) && 
+				!(ex instanceof MySQLStatementCancelledException)) {
+			sqlEx = ex;
+		} else {
+			int[] newUpdateCounts = new int[endOfBatchIndex];
+			System.arraycopy(updateCounts, 0,
+					newUpdateCounts, 0, endOfBatchIndex);
+
+			throw new java.sql.BatchUpdateException(ex
+					.getMessage(), ex.getSQLState(), ex
+					.getErrorCode(), newUpdateCounts);
+		}
+		
+		return sqlEx;
+	}
+	
+	/**
+	 * Execute a SQL statement that retruns a single ResultSet
+	 *
+	 * @param sql
+	 *            typically a static SQL SELECT statement
+	 *
+	 * @return a ResulSet that contains the data produced by the query
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public java.sql.ResultSet executeQuery(String sql)
+			throws SQLException {
+		checkClosed();
+
+		ConnectionImpl locallyScopedConn = this.connection;
+
+		synchronized (locallyScopedConn.getMutex()) {
+			resetCancelledState();
+
+			checkNullOrEmptyQuery(sql);
+
+			boolean doStreaming = createStreamingResultSet();
+
+			// Adjust net_write_timeout to a higher value if we're
+			// streaming result sets. More often than not, someone runs into
+			// an issue where they blow net_write_timeout when using this
+			// feature, and if they're willing to hold a result set open
+			// for 30 seconds or more, one more round-trip isn't going to hurt
+			//
+			// This is reset by RowDataDynamic.close().
+
+			if (doStreaming
+					&& this.connection.getNetTimeoutForStreamingResults() > 0) {
+				executeSimpleNonQuery(locallyScopedConn, "SET net_write_timeout="
+						+ this.connection.getNetTimeoutForStreamingResults());
+			}
+
+			if (this.doEscapeProcessing) {
+				Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
+						locallyScopedConn.serverSupportsConvertFn(), this.connection);
+
+				if (escapedSqlResult instanceof String) {
+					sql = (String) escapedSqlResult;
+				} else {
+					sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
+				}
+			}
+
+			char firstStatementChar = StringUtils.firstNonWsCharUc(sql,
+					findStartOfStatement(sql));
+
+			if (sql.charAt(0) == '/') {
+				if (sql.startsWith(PING_MARKER)) {
+					doPingInstead();
+				
+					return this.results;
+				}
+			}
+			
+			checkForDml(sql, firstStatementChar);
+
+			if (this.results != null) {
+				if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
+					this.results.realClose(false);
+				}
+			}
+
+			CachedResultSetMetaData cachedMetaData = null;
+
+			// If there isn't a limit clause in the SQL
+			// then limit the number of rows to return in
+			// an efficient manner. Only do this if
+			// setMaxRows() hasn't been used on any Statements
+			// generated from the current Connection (saves
+			// a query, and network traffic).
+
+			if (useServerFetch()) {
+				this.results = createResultSetUsingServerFetch(sql);
+
+				return this.results;
+			}
+
+			CancelTask timeoutTask = null;
+
+			String oldCatalog = null;
+
+			try {
+				if (locallyScopedConn.getEnableQueryTimeouts() &&
+						this.timeoutInMillis != 0
+						&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
+					timeoutTask = new CancelTask(this);
+					ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+							this.timeoutInMillis);
+				}
+
+				if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
+					oldCatalog = locallyScopedConn.getCatalog();
+					locallyScopedConn.setCatalog(this.currentCatalog);
+				}
+
+				//
+				// Check if we have cached metadata for this query...
+				//
+
+				Field[] cachedFields = null;
+
+				if (locallyScopedConn.getCacheResultSetMetadata()) {
+					cachedMetaData = locallyScopedConn.getCachedMetaData(sql);
+
+					if (cachedMetaData != null) {
+						cachedFields = cachedMetaData.fields;
+					}
+				}
+
+				if (locallyScopedConn.useMaxRows()) {
+					// We need to execute this all together
+					// So synchronize on the Connection's mutex (because
+					// even queries going through there synchronize
+					// on the connection
+					if (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1) { //$NON-NLS-1$
+						this.results = locallyScopedConn.execSQL(this, sql,
+								this.maxRows, null, this.resultSetType,
+								this.resultSetConcurrency,
+								doStreaming,
+								this.currentCatalog, cachedFields);
+					} else {
+						if (this.maxRows <= 0) {
+							executeSimpleNonQuery(locallyScopedConn,
+									"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
+						} else {
+							executeSimpleNonQuery(locallyScopedConn,
+											"SET OPTION SQL_SELECT_LIMIT=" + this.maxRows);
+						}
+
+						this.results = locallyScopedConn.execSQL(this, sql, -1,
+								null, this.resultSetType,
+								this.resultSetConcurrency,
+								doStreaming,
+								this.currentCatalog, cachedFields);
+
+						if (oldCatalog != null) {
+							locallyScopedConn.setCatalog(oldCatalog);
+						}
+					}
+				} else {
+					this.results = locallyScopedConn.execSQL(this, sql, -1, null,
+							this.resultSetType, this.resultSetConcurrency,
+							doStreaming,
+							this.currentCatalog, cachedFields);
+				}
+
+				if (timeoutTask != null) {
+					if (timeoutTask.caughtWhileCancelling != null) {
+						throw timeoutTask.caughtWhileCancelling;
+					}
+
+					timeoutTask.cancel();
+					timeoutTask = null;
+				}
+
+				synchronized (this.cancelTimeoutMutex) {
+					if (this.wasCancelled) {
+						SQLException cause = null;
+						
+						if (this.wasCancelledByTimeout) {
+							cause = new MySQLTimeoutException();
+						} else {
+							cause = new MySQLStatementCancelledException();
+						}
+						
+						resetCancelledState();
+						
+						throw cause;
+					}
+				}
+			} finally {
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
+
+				if (oldCatalog != null) {
+					locallyScopedConn.setCatalog(oldCatalog);
+				}
+			}
+
+			this.lastInsertId = this.results.getUpdateID();
+
+			if (cachedMetaData != null) {
+				locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData,
+						this.results);
+			} else {
+				if (this.connection.getCacheResultSetMetadata()) {
+					locallyScopedConn.initializeResultsMetadataFromCache(sql,
+							null /* will be created */, this.results);
+				}
+			}
+
+			return this.results;
+		}
+	}
+
+	protected void doPingInstead() throws SQLException {
+		if (this.pingTarget != null) {
+			this.pingTarget.doPing();
+		} else {
+			this.connection.ping();
+		}
+
+		ResultSetInternalMethods fakeSelectOneResultSet = generatePingResultSet();
+		this.results = fakeSelectOneResultSet;
+	}
+
+	protected ResultSetInternalMethods generatePingResultSet() throws SQLException {
+		Field[] fields = { new Field(null, "1", Types.BIGINT, 1) };
+		ArrayList rows = new ArrayList();
+		byte[] colVal = new byte[] { (byte) '1' };
+
+		rows.add(new ByteArrayRow(new byte[][] { colVal }));
+
+		return (ResultSetInternalMethods) DatabaseMetaData.buildResultSet(fields, rows,
+				this.connection);
+	}
+	
+	protected void executeSimpleNonQuery(ConnectionImpl c, String nonQuery)
+			throws SQLException {
+		c.execSQL(this, nonQuery,
+				-1, null, ResultSet.TYPE_FORWARD_ONLY,
+				ResultSet.CONCUR_READ_ONLY, false, this.currentCatalog,
+				null, false).close();
+	}
+
+	/**
+	 * Execute a SQL INSERT, UPDATE or DELETE statement. In addition SQL
+	 * statements that return nothing such as SQL DDL statements can be executed
+	 * Any IDs generated for AUTO_INCREMENT fields can be retrieved by casting
+	 * this Statement to org.gjt.mm.mysql.Statement and calling the
+	 * getLastInsertID() method.
+	 *
+	 * @param sql
+	 *            a SQL statement
+	 *
+	 * @return either a row count, or 0 for SQL commands
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int executeUpdate(String sql) throws SQLException {
+		return executeUpdate(sql, false);
+	}
+
+	protected int executeUpdate(String sql, boolean isBatch)
+		throws SQLException {
+		checkClosed();
+
+		ConnectionImpl locallyScopedConn = this.connection;
+
+		char firstStatementChar = StringUtils.firstAlphaCharUc(sql,
+				findStartOfStatement(sql));
+
+		ResultSetInternalMethods rs = null;
+
+		synchronized (locallyScopedConn.getMutex()) {
+			resetCancelledState();
+
+			checkNullOrEmptyQuery(sql);
+
+			if (this.doEscapeProcessing) {
+				Object escapedSqlResult = EscapeProcessor.escapeSQL(sql,
+						this.connection.serverSupportsConvertFn(), this.connection);
+
+				if (escapedSqlResult instanceof String) {
+					sql = (String) escapedSqlResult;
+				} else {
+					sql = ((EscapeProcessorResult) escapedSqlResult).escapedSql;
+				}
+			}
+
+			if (locallyScopedConn.isReadOnly()) {
+				throw SQLError.createSQLException(Messages
+						.getString("Statement.42") //$NON-NLS-1$
+						+ Messages.getString("Statement.43"), //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+
+			if (StringUtils.startsWithIgnoreCaseAndWs(sql, "select")) { //$NON-NLS-1$
+				throw SQLError.createSQLException(Messages
+						.getString("Statement.46"), //$NON-NLS-1$
+				"01S03"); //$NON-NLS-1$
+			}
+
+			if (this.results != null) {
+				if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
+					this.results.realClose(false);
+				}
+			}
+
+			// The checking and changing of catalogs
+			// must happen in sequence, so synchronize
+			// on the same mutex that _conn is using
+
+			CancelTask timeoutTask = null;
+
+			String oldCatalog = null;
+
+			try {
+				if (locallyScopedConn.getEnableQueryTimeouts() &&
+						this.timeoutInMillis != 0
+						&& locallyScopedConn.versionMeetsMinimum(5, 0, 0)) {
+					timeoutTask = new CancelTask(this);
+					ConnectionImpl.getCancelTimer().schedule(timeoutTask,
+							this.timeoutInMillis);
+				}
+
+				if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) {
+					oldCatalog = locallyScopedConn.getCatalog();
+					locallyScopedConn.setCatalog(this.currentCatalog);
+				}
+
+				//
+				// Only apply max_rows to selects
+				//
+				if (locallyScopedConn.useMaxRows()) {
+					executeSimpleNonQuery(locallyScopedConn,
+							"SET OPTION SQL_SELECT_LIMIT=DEFAULT");
+				}
+
+				rs = locallyScopedConn.execSQL(this, sql, -1, null,
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY, false,
+						this.currentCatalog,
+						null /* force read of field info on DML */,
+						isBatch);
+
+				if (timeoutTask != null) {
+					if (timeoutTask.caughtWhileCancelling != null) {
+						throw timeoutTask.caughtWhileCancelling;
+					}
+
+					timeoutTask.cancel();
+					timeoutTask = null;
+				}
+
+				synchronized (this.cancelTimeoutMutex) {
+					if (this.wasCancelled) {
+						SQLException cause = null;
+						
+						if (this.wasCancelledByTimeout) {
+							cause = new MySQLTimeoutException();
+						} else {
+							cause = new MySQLStatementCancelledException();
+						}
+						
+						resetCancelledState();
+						
+						throw cause;
+					}
+				}
+			} finally {
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
+
+				if (oldCatalog != null) {
+					locallyScopedConn.setCatalog(oldCatalog);
+				}
+			}
+		}
+
+		this.results = rs;
+
+		rs.setFirstCharOfQuery(firstStatementChar);
+
+		this.updateCount = rs.getUpdateCount();
+
+		int truncatedUpdateCount = 0;
+
+		if (this.updateCount > Integer.MAX_VALUE) {
+			truncatedUpdateCount = Integer.MAX_VALUE;
+		} else {
+			truncatedUpdateCount = (int) this.updateCount;
+		}
+
+		this.lastInsertId = rs.getUpdateID();
+
+		return truncatedUpdateCount;
+	}
+
+
+	/**
+	 * @see StatementImpl#executeUpdate(String, int)
+	 */
+	public int executeUpdate(String sql, int returnGeneratedKeys)
+			throws SQLException {
+		if (returnGeneratedKeys == java.sql.Statement.RETURN_GENERATED_KEYS) {
+			checkClosed();
+
+			ConnectionImpl locallyScopedConn = this.connection;
+
+			synchronized (locallyScopedConn.getMutex()) {
+				// If this is a 'REPLACE' query, we need to be able to parse
+				// the 'info' message returned from the server to determine
+				// the actual number of keys generated.
+				boolean readInfoMsgState = locallyScopedConn
+						.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+
+				try {
+					return executeUpdate(sql);
+				} finally {
+					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
+				}
+			}
+		}
+
+		return executeUpdate(sql);
+	}
+
+	/**
+	 * @see StatementImpl#executeUpdate(String, int[])
+	 */
+	public int executeUpdate(String sql, int[] generatedKeyIndices)
+			throws SQLException {
+		if ((generatedKeyIndices != null) && (generatedKeyIndices.length > 0)) {
+			checkClosed();
+
+			ConnectionImpl locallyScopedConn = this.connection;
+
+			synchronized (locallyScopedConn.getMutex()) {
+				// If this is a 'REPLACE' query, we need to be able to parse
+				// the 'info' message returned from the server to determine
+				// the actual number of keys generated.
+				boolean readInfoMsgState = locallyScopedConn
+						.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+
+				try {
+					return executeUpdate(sql);
+				} finally {
+					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
+				}
+			}
+		}
+
+		return executeUpdate(sql);
+	}
+
+	/**
+	 * @see StatementImpl#executeUpdate(String, String[])
+	 */
+	public int executeUpdate(String sql, String[] generatedKeyNames)
+			throws SQLException {
+		if ((generatedKeyNames != null) && (generatedKeyNames.length > 0)) {
+			checkClosed();
+
+			ConnectionImpl locallyScopedConn = this.connection;
+
+			synchronized (locallyScopedConn.getMutex()) {
+				// If this is a 'REPLACE' query, we need to be able to parse
+				// the 'info' message returned from the server to determine
+				// the actual number of keys generated.
+				boolean readInfoMsgState = this.connection
+						.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+
+				try {
+					return executeUpdate(sql);
+				} finally {
+					locallyScopedConn.setReadInfoMsgEnabled(readInfoMsgState);
+				}
+			}
+		}
+
+		return executeUpdate(sql);
+	}
+
+	/**
+	 * Optimization to only use one calendar per-session, or calculate it for
+	 * each call, depending on user configuration
+	 */
+	protected Calendar getCalendarInstanceForSessionOrNew() {
+		if (this.connection != null) {
+			return this.connection.getCalendarInstanceForSessionOrNew();
+		} else {
+			// punt, no connection around
+			return new GregorianCalendar();
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Return the Connection that produced the Statement.
+	 *
+	 * @return the Connection that produced the Statement
+	 *
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.sql.Connection getConnection() throws SQLException {
+		return this.connection;
+	}
+
+	/**
+	 * JDBC 2.0 Determine the fetch direction.
+	 *
+	 * @return the default fetch direction
+	 *
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public int getFetchDirection() throws SQLException {
+		return java.sql.ResultSet.FETCH_FORWARD;
+	}
+
+	/**
+	 * JDBC 2.0 Determine the default fetch size.
+	 *
+	 * @return the number of rows to fetch at a time
+	 *
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public int getFetchSize() throws SQLException {
+		return this.fetchSize;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 *
+	 * @return DOCUMENT ME!
+	 *
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public synchronized java.sql.ResultSet getGeneratedKeys()
+			throws SQLException {
+		if (this.batchedGeneratedKeys == null) {
+			return getGeneratedKeysInternal();
+		}
+
+		Field[] fields = new Field[1];
+		fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
+		fields[0].setConnection(this.connection);
+
+		return com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
+				new RowDataStatic(this.batchedGeneratedKeys), this.connection,
+				this, false);
+	}
+
+	/*
+	 * Needed because there's no concept of super.super to get to this
+	 * implementation from ServerPreparedStatement when dealing with batched
+	 * updates.
+	 */
+	protected java.sql.ResultSet getGeneratedKeysInternal()
+			throws SQLException {
+		Field[] fields = new Field[1];
+		fields[0] = new Field("", "GENERATED_KEY", Types.BIGINT, 17); //$NON-NLS-1$ //$NON-NLS-2$
+		fields[0].setConnection(this.connection);
+
+		ArrayList rowSet = new ArrayList();
+
+		long beginAt = getLastInsertID();
+		int numKeys = getUpdateCount();
+
+		if (this.results != null) {
+			String serverInfo = this.results.getServerInfo();
+
+			//
+			// Only parse server info messages for 'REPLACE'
+			// queries
+			//
+			if ((numKeys > 0) && (this.results.getFirstCharOfQuery() == 'R')
+					&& (serverInfo != null) && (serverInfo.length() > 0)) {
+				numKeys = getRecordCountFromInfo(serverInfo);
+			}
+
+			if ((beginAt > 0) && (numKeys > 0)) {
+				for (int i = 0; i < numKeys; i++) {
+					byte[][] row = new byte[1][];
+					row[0] = Long.toString(beginAt++).getBytes();
+					rowSet.add(new ByteArrayRow(row));
+				}
+			}
+		}
+
+		return com.mysql.jdbc.ResultSetImpl.getInstance(this.currentCatalog, fields,
+				new RowDataStatic(rowSet), this.connection, this, false);
+	}
+
+	/**
+	 * Returns the id used when profiling
+	 *
+	 * @return the id used when profiling.
+	 */
+	protected int getId() {
+		return this.statementId;
+	}
+
+	/**
+	 * getLastInsertID returns the value of the auto_incremented key after an
+	 * executeQuery() or excute() call.
+	 *
+	 * <p>
+	 * This gets around the un-threadsafe behavior of "select LAST_INSERT_ID()"
+	 * which is tied to the Connection that created this Statement, and
+	 * therefore could have had many INSERTS performed before one gets a chance
+	 * to call "select LAST_INSERT_ID()".
+	 * </p>
+	 *
+	 * @return the last update ID.
+	 */
+	public long getLastInsertID() {
+		return this.lastInsertId;
+	}
+
+	/**
+	 * getLongUpdateCount returns the current result as an update count, if the
+	 * result is a ResultSet or there are no more results, -1 is returned. It
+	 * should only be called once per result.
+	 *
+	 * <p>
+	 * This method returns longs as MySQL server versions newer than 3.22.4
+	 * return 64-bit values for update counts
+	 * </p>
+	 *
+	 * @return the current update count.
+	 */
+	public long getLongUpdateCount() {
+		if (this.results == null) {
+			return -1;
+		}
+
+		if (this.results.reallyResult()) {
+			return -1;
+		}
+
+		return this.updateCount;
+	}
+
+	/**
+	 * The maxFieldSize limit (in bytes) is the maximum amount of data returned
+	 * for any column value; it only applies to BINARY, VARBINARY,
+	 * LONGVARBINARY, CHAR, VARCHAR and LONGVARCHAR columns. If the limit is
+	 * exceeded, the excess data is silently discarded.
+	 *
+	 * @return the current max column size limit; zero means unlimited
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int getMaxFieldSize() throws SQLException {
+		return this.maxFieldSize;
+	}
+
+	/**
+	 * The maxRows limit is set to limit the number of rows that any ResultSet
+	 * can contain. If the limit is exceeded, the excess rows are silently
+	 * dropped.
+	 *
+	 * @return the current maximum row limit; zero means unlimited
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int getMaxRows() throws SQLException {
+		if (this.maxRows <= 0) {
+			return 0;
+		}
+
+		return this.maxRows;
+	}
+
+	/**
+	 * getMoreResults moves to a Statement's next result. If it returns true,
+	 * this result is a ResulSet.
+	 *
+	 * @return true if the next ResultSet is valid
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean getMoreResults() throws SQLException {
+		return getMoreResults(CLOSE_CURRENT_RESULT);
+	}
+
+	/**
+	 * @see StatementImpl#getMoreResults(int)
+	 */
+	public boolean getMoreResults(int current) throws SQLException {
+
+		if (this.results == null) {
+			return false;
+		}
+
+		ResultSetInternalMethods nextResultSet = this.results.getNextResultSet();
+
+		switch (current) {
+		case java.sql.Statement.CLOSE_CURRENT_RESULT:
+
+			if (this.results != null) {
+				this.results.close();
+				this.results.clearNextResult();
+			}
+
+			break;
+
+		case java.sql.Statement.CLOSE_ALL_RESULTS:
+
+			if (this.results != null) {
+				this.results.close();
+				this.results.clearNextResult();
+			}
+
+			closeAllOpenResults();
+
+			break;
+
+		case java.sql.Statement.KEEP_CURRENT_RESULT:
+			if (!this.connection.getDontTrackOpenResources()) {
+				this.openResults.add(this.results);
+			}
+
+			this.results.clearNextResult(); // nobody besides us should
+			// ever need this value...
+			break;
+
+		default:
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.19"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		this.results = nextResultSet;
+
+		if (this.results == null) {
+			this.updateCount = -1;
+			this.lastInsertId = -1;
+		} else if (this.results.reallyResult()) {
+			this.updateCount = -1;
+			this.lastInsertId = -1;
+		} else {
+			this.updateCount = this.results.getUpdateCount();
+			this.lastInsertId = this.results.getUpdateID();
+		}
+
+		return ((this.results != null) && this.results.reallyResult()) ? true
+				: false;
+	}
+
+	/**
+	 * The queryTimeout limit is the number of seconds the driver will wait for
+	 * a Statement to execute. If the limit is exceeded, a SQLException is
+	 * thrown.
+	 *
+	 * @return the current query timeout limit in seconds; 0 = unlimited
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int getQueryTimeout() throws SQLException {
+		return this.timeoutInMillis / 1000;
+	}
+
+	/**
+	 * Parses actual record count from 'info' message
+	 *
+	 * @param serverInfo
+	 *            DOCUMENT ME!
+	 *
+	 * @return DOCUMENT ME!
+	 */
+	private int getRecordCountFromInfo(String serverInfo) {
+		StringBuffer recordsBuf = new StringBuffer();
+		int recordsCount = 0;
+		int duplicatesCount = 0;
+
+		char c = (char) 0;
+
+		int length = serverInfo.length();
+		int i = 0;
+
+		for (; i < length; i++) {
+			c = serverInfo.charAt(i);
+
+			if (Character.isDigit(c)) {
+				break;
+			}
+		}
+
+		recordsBuf.append(c);
+		i++;
+
+		for (; i < length; i++) {
+			c = serverInfo.charAt(i);
+
+			if (!Character.isDigit(c)) {
+				break;
+			}
+
+			recordsBuf.append(c);
+		}
+
+		recordsCount = Integer.parseInt(recordsBuf.toString());
+
+		StringBuffer duplicatesBuf = new StringBuffer();
+
+		for (; i < length; i++) {
+			c = serverInfo.charAt(i);
+
+			if (Character.isDigit(c)) {
+				break;
+			}
+		}
+
+		duplicatesBuf.append(c);
+		i++;
+
+		for (; i < length; i++) {
+			c = serverInfo.charAt(i);
+
+			if (!Character.isDigit(c)) {
+				break;
+			}
+
+			duplicatesBuf.append(c);
+		}
+
+		duplicatesCount = Integer.parseInt(duplicatesBuf.toString());
+
+		return recordsCount - duplicatesCount;
+	}
+
+	/**
+	 * getResultSet returns the current result as a ResultSet. It should only be
+	 * called once per result.
+	 *
+	 * @return the current result set; null if there are no more
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs (why?)
+	 */
+	public java.sql.ResultSet getResultSet() throws SQLException {
+		return ((this.results != null) && this.results.reallyResult()) ? (java.sql.ResultSet) this.results
+				: null;
+	}
+
+	/**
+	 * JDBC 2.0 Determine the result set concurrency.
+	 *
+	 * @return CONCUR_UPDATABLE or CONCUR_READONLY
+	 *
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public int getResultSetConcurrency() throws SQLException {
+		return this.resultSetConcurrency;
+	}
+
+	/**
+	 * @see StatementImpl#getResultSetHoldability()
+	 */
+	public int getResultSetHoldability() throws SQLException {
+		return java.sql.ResultSet.HOLD_CURSORS_OVER_COMMIT;
+	}
+
+	protected ResultSetInternalMethods getResultSetInternal() {
+		return this.results;
+	}
+
+	/**
+	 * JDBC 2.0 Determine the result set type.
+	 *
+	 * @return the ResultSet type (SCROLL_SENSITIVE or SCROLL_INSENSITIVE)
+	 *
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	public int getResultSetType() throws SQLException {
+		return this.resultSetType;
+	}
+
+	/**
+	 * getUpdateCount returns the current result as an update count, if the
+	 * result is a ResultSet or there are no more results, -1 is returned. It
+	 * should only be called once per result.
+	 *
+	 * @return the current result as an update count.
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int getUpdateCount() throws SQLException {
+		if (this.results == null) {
+			return -1;
+		}
+
+		if (this.results.reallyResult()) {
+			return -1;
+		}
+
+		int truncatedUpdateCount = 0;
+
+		if (this.results.getUpdateCount() > Integer.MAX_VALUE) {
+			truncatedUpdateCount = Integer.MAX_VALUE;
+		} else {
+			truncatedUpdateCount = (int) this.results.getUpdateCount();
+		}
+
+		return truncatedUpdateCount;
+	}
+
+	/**
+	 * The first warning reported by calls on this Statement is returned. A
+	 * Statement's execute methods clear its java.sql.SQLWarning chain.
+	 * Subsequent Statement warnings will be chained to this
+	 * java.sql.SQLWarning.
+	 *
+	 * <p>
+	 * The Warning chain is automatically cleared each time a statement is
+	 * (re)executed.
+	 * </p>
+	 *
+	 * <p>
+	 * <B>Note:</B> If you are processing a ResultSet then any warnings
+	 * associated with ResultSet reads will be chained on the ResultSet object.
+	 * </p>
+	 *
+	 * @return the first java.sql.SQLWarning or null
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public java.sql.SQLWarning getWarnings() throws SQLException {
+		checkClosed();
+
+		if (this.connection != null && !this.connection.isClosed()
+				&& this.connection.versionMeetsMinimum(4, 1, 0)) {
+			SQLWarning pendingWarningsFromServer = SQLError
+					.convertShowWarningsToSQLWarnings(this.connection);
+
+			if (this.warningChain != null) {
+				this.warningChain.setNextWarning(pendingWarningsFromServer);
+			} else {
+				this.warningChain = pendingWarningsFromServer;
+			}
+
+			return this.warningChain;
+		}
+
+		return this.warningChain;
+	}
+
+	/**
+	 * Closes this statement, and frees resources.
+	 *
+	 * @param calledExplicitly
+	 *            was this called from close()?
+	 *
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected void realClose(boolean calledExplicitly, boolean closeOpenResults)
+			throws SQLException {
+		if (this.isClosed) {
+			return;
+		}
+
+		if (this.useUsageAdvisor) {
+			if (!calledExplicitly) {
+				String message = Messages.getString("Statement.63") //$NON-NLS-1$
+						+ Messages.getString("Statement.64"); //$NON-NLS-1$
+
+				this.eventSink.consumeEvent(new ProfilerEvent(
+						ProfilerEvent.TYPE_WARN,
+						"", //$NON-NLS-1$
+						this.currentCatalog, this.connectionId, this.getId(),
+						-1, System.currentTimeMillis(), 0,
+						Constants.MILLIS_I18N, null, this.pointOfOrigin,
+						message));
+			}
+		}
+
+		if (this.results != null) {
+			if (closeOpenResults) {
+				closeOpenResults = !this.holdResultsOpenOverClose;
+			}
+
+			if (closeOpenResults && this.connection != null
+					&& !this.connection.getHoldResultsOpenOverStatementClose()) {
+				try {
+					this.results.close();
+				} catch (Exception ex) {
+					;
+				}
+
+				this.closeAllOpenResults();
+			}
+		}
+
+		if (this.connection != null) {
+			if (this.maxRowsChanged) {
+				this.connection.unsetMaxRows(this);
+			}
+
+			if (!this.connection.getDontTrackOpenResources()) {
+				this.connection.unregisterStatement(this);
+			}
+		}
+
+		this.isClosed = true;
+
+		this.results = null;
+		this.connection = null;
+		this.warningChain = null;
+		this.openResults = null;
+		this.batchedGeneratedKeys = null;
+		this.cancelTimeoutMutex = null;
+		this.localInfileInputStream = null;
+		this.pingTarget = null;
+	}
+
+	/**
+	 * setCursorName defines the SQL cursor name that will be used by subsequent
+	 * execute methods. This name can then be used in SQL positioned
+	 * update/delete statements to identify the current row in the ResultSet
+	 * generated by this statement. If a database doesn't support positioned
+	 * update/delete, this method is a no-op.
+	 *
+	 * <p>
+	 * <b>Note:</b> This MySQL driver does not support cursors.
+	 * </p>
+	 *
+	 * @param name
+	 *            the new cursor name
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setCursorName(String name) throws SQLException {
+		// No-op
+	}
+
+	/**
+	 * If escape scanning is on (the default), the driver will do escape
+	 * substitution before sending the SQL to the database.
+	 *
+	 * @param enable
+	 *            true to enable; false to disable
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setEscapeProcessing(boolean enable)
+			throws SQLException {
+		this.doEscapeProcessing = enable;
+	}
+
+	/**
+	 * JDBC 2.0 Give a hint as to the direction in which the rows in a result
+	 * set will be processed. The hint applies only to result sets created using
+	 * this Statement object. The default value is ResultSet.FETCH_FORWARD.
+	 *
+	 * @param direction
+	 *            the initial direction for processing rows
+	 *
+	 * @exception SQLException
+	 *                if a database-access error occurs or direction is not one
+	 *                of ResultSet.FETCH_FORWARD, ResultSet.FETCH_REVERSE, or
+	 *                ResultSet.FETCH_UNKNOWN
+	 */
+	public void setFetchDirection(int direction) throws SQLException {
+		switch (direction) {
+		case java.sql.ResultSet.FETCH_FORWARD:
+		case java.sql.ResultSet.FETCH_REVERSE:
+		case java.sql.ResultSet.FETCH_UNKNOWN:
+			break;
+
+		default:
+			throw SQLError.createSQLException(
+					Messages.getString("Statement.5"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Give the JDBC driver a hint as to the number of rows that should
+	 * be fetched from the database when more rows are needed. The number of
+	 * rows specified only affects result sets created using this statement. If
+	 * the value specified is zero, then the hint is ignored. The default value
+	 * is zero.
+	 *
+	 * @param rows
+	 *            the number of rows to fetch
+	 *
+	 * @exception SQLException
+	 *                if a database-access error occurs, or the condition 0
+	 *                &lt;= rows &lt;= this.getMaxRows() is not satisfied.
+	 */
+	public void setFetchSize(int rows) throws SQLException {
+		if (((rows < 0) && (rows != Integer.MIN_VALUE))
+				|| ((this.maxRows != 0) && (this.maxRows != -1) && (rows > this
+						.getMaxRows()))) {
+			throw SQLError.createSQLException(
+					Messages.getString("Statement.7"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		this.fetchSize = rows;
+	}
+
+	protected void setHoldResultsOpenOverClose(boolean holdResultsOpenOverClose) {
+		this.holdResultsOpenOverClose = holdResultsOpenOverClose;
+	}
+
+	/**
+	 * Sets the maxFieldSize
+	 *
+	 * @param max
+	 *            the new max column size limit; zero means unlimited
+	 *
+	 * @exception SQLException
+	 *                if size exceeds buffer size
+	 */
+	public void setMaxFieldSize(int max) throws SQLException {
+		if (max < 0) {
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.11"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		int maxBuf = (this.connection != null) ? this.connection
+				.getMaxAllowedPacket() : MysqlIO.getMaxBuf();
+
+		if (max > maxBuf) {
+			throw SQLError.createSQLException(Messages.getString(
+					"Statement.13", //$NON-NLS-1$
+					new Object[] { Constants.longValueOf(maxBuf) }), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		this.maxFieldSize = max;
+	}
+
+	/**
+	 * Set the maximum number of rows
+	 *
+	 * @param max
+	 *            the new max rows limit; zero means unlimited
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 *
+	 * @see getMaxRows
+	 */
+	public void setMaxRows(int max) throws SQLException {
+		if ((max > MysqlDefs.MAX_ROWS) || (max < 0)) {
+			throw SQLError
+					.createSQLException(
+							Messages.getString("Statement.15") + max //$NON-NLS-1$
+									+ " > " //$NON-NLS-1$ //$NON-NLS-2$
+									+ MysqlDefs.MAX_ROWS + ".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		if (max == 0) {
+			max = -1;
+		}
+
+		this.maxRows = max;
+		this.maxRowsChanged = true;
+
+		if (this.maxRows == -1) {
+			this.connection.unsetMaxRows(this);
+			this.maxRowsChanged = false;
+		} else {
+			// Most people don't use setMaxRows()
+			// so don't penalize them
+			// with the extra query it takes
+			// to do it efficiently unless we need
+			// to.
+			this.connection.maxRowsChanged(this);
+		}
+	}
+
+	/**
+	 * Sets the queryTimeout limit
+	 *
+	 * @param seconds -
+	 *            the new query timeout limit in seconds
+	 *
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setQueryTimeout(int seconds) throws SQLException {
+		if (seconds < 0) {
+			throw SQLError.createSQLException(Messages
+					.getString("Statement.21"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		this.timeoutInMillis = seconds * 1000;
+	}
+
+	/**
+	 * Sets the concurrency for result sets generated by this statement
+	 *
+	 * @param concurrencyFlag
+	 *            DOCUMENT ME!
+	 */
+	void setResultSetConcurrency(int concurrencyFlag) {
+		this.resultSetConcurrency = concurrencyFlag;
+	}
+
+	/**
+	 * Sets the result set type for result sets generated by this statement
+	 *
+	 * @param typeFlag
+	 *            DOCUMENT ME!
+	 */
+	void setResultSetType(int typeFlag) {
+		this.resultSetType = typeFlag;
+	}
+
+	protected void getBatchedGeneratedKeys(java.sql.Statement batchedStatement) throws SQLException {
+		if (this.retrieveGeneratedKeys) {
+			java.sql.ResultSet rs = null;
+
+			try {
+				rs = batchedStatement.getGeneratedKeys();
+
+				while (rs.next()) {
+					this.batchedGeneratedKeys
+							.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }));
+				}
+			} finally {
+				if (rs != null) {
+					rs.close();
+				}
+			}
+		}
+	}
+
+	protected void getBatchedGeneratedKeys() throws SQLException {
+		if (this.retrieveGeneratedKeys) {
+			java.sql.ResultSet rs = null;
+
+			try {
+				rs = getGeneratedKeysInternal();
+
+				while (rs.next()) {
+					this.batchedGeneratedKeys
+							.add(new ByteArrayRow(new byte[][] { rs.getBytes(1) }));
+				}
+			} finally {
+				if (rs != null) {
+					rs.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * @return
+	 */
+	private boolean useServerFetch() throws SQLException {
+
+		return this.connection.isCursorFetchEnabled() && this.fetchSize > 0
+				&& this.resultSetConcurrency == ResultSet.CONCUR_READ_ONLY
+				&& this.resultSetType == ResultSet.TYPE_FORWARD_ONLY;
+	}
+
+	public synchronized boolean isClosed() throws SQLException {
+		return this.isClosed;
+	}
+
+	private boolean isPoolable = true;
+
+	public boolean isPoolable() throws SQLException {
+		return this.isPoolable;
+	}
+
+	public void setPoolable(boolean poolable) throws SQLException {
+		this.isPoolable = poolable;
+	}
+
+	/**
+     * Returns true if this either implements the interface argument or is directly or indirectly a wrapper
+     * for an object that does. Returns false otherwise. If this implements the interface then return true,
+     * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
+     * object. If this does not implement the interface and is not a wrapper, return false.
+     * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
+     * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
+     * returns true then calling <code>unwrap</code> with the same argument should succeed.
+     *
+     * @param interfaces a Class defining an interface.
+     * @return true if this implements the interface or directly or indirectly wraps an object that does.
+     * @throws java.sql.SQLException  if an error occurs while determining whether this is a wrapper
+     * for an object with the given interface.
+     * @since 1.6
+     */
+	public boolean isWrapperFor(Class iface) throws SQLException {
+		checkClosed();
+
+		// This works for classes that aren't actually wrapping
+		// anything
+		return iface.isInstance(this);
+	}
+
+    /**
+     * Returns an object that implements the given interface to allow access to non-standard methods,
+     * or standard methods not exposed by the proxy.
+     * The result may be either the object found to implement the interface or a proxy for that object.
+     * If the receiver implements the interface then that is the object. If the receiver is a wrapper
+     * and the wrapped object implements the interface then that is the object. Otherwise the object is
+     *  the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
+     * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
+     *
+     * @param iface A Class defining an interface that the result must implement.
+     * @return an object that implements the interface. May be a proxy for the actual implementing object.
+     * @throws java.sql.SQLException If no object found that implements the interface
+     * @since 1.6
+     */
+	public Object unwrap(Class iface) throws java.sql.SQLException {
+    	try {
+    		// This works for classes that aren't actually wrapping
+    		// anything
+            return Util.cast(iface, this);
+        } catch (ClassCastException cce) {
+            throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(),
+            		SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        }
+    }
+
+	protected int findStartOfStatement(String sql) {
+		int statementStartPos = 0;
+
+		if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) {
+			statementStartPos = sql.indexOf("*/");
+
+			if (statementStartPos == -1) {
+				statementStartPos = 0;
+			} else {
+				statementStartPos += 2;
+			}
+		} else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--")
+			|| StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) {
+			statementStartPos = sql.indexOf('\n');
+
+			if (statementStartPos == -1) {
+				statementStartPos = sql.indexOf('\r');
+
+				if (statementStartPos == -1) {
+					statementStartPos = 0;
+				}
+			}
+		}
+
+		return statementStartPos;
+	}
+
+	private InputStream localInfileInputStream;
+
+    public synchronized InputStream getLocalInfileInputStream() {
+        return this.localInfileInputStream;
+    }
+
+    public synchronized void setLocalInfileInputStream(InputStream stream) {
+        this.localInfileInputStream = stream;
+    }
+    
+    public synchronized void setPingTarget(PingTarget pingTarget) {
+		this.pingTarget = pingTarget;
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/StatementInterceptor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/StatementInterceptor.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/StatementInterceptor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,143 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * Implement this interface to be placed "in between" query execution, so that
+ * you can influence it. (currently experimental).
+ * 
+ * StatementInterceptors are "chainable" when configured by the user, the
+ * results returned by the "current" interceptor will be passed on to the next
+ * on in the chain, from left-to-right order, as specified by the user in the 
+ * JDBC configuration property "statementInterceptors".
+ * 
+ * @version $Id: $
+ */
+
+public interface StatementInterceptor extends Extension {
+
+	/**
+	 * Called once per connection that wants to use the interceptor
+	 * 
+	 * The properties are the same ones passed in in the URL or arguments to
+	 * Driver.connect() or DriverManager.getConnection().
+	 * 
+	 * @param conn the connection for which this interceptor is being created
+	 * @param props configuration values as passed to the connection. Note that
+	 * in order to support javax.sql.DataSources, configuration properties specific
+	 * to an interceptor <strong>must</strong> be passed via setURL() on the
+	 * DataSource. StatementInterceptor properties are not exposed via 
+	 * accessor/mutator methods on DataSources.
+	 * 
+	 * @throws SQLException should be thrown if the the StatementInterceptor
+	 * can not initialize itself.
+	 */
+	
+	public abstract void init(Connection conn, Properties props) throws SQLException;
+
+	/**
+	 * Called before the given statement is going to be sent to the
+	 * server for processing.
+	 * 
+	 * Interceptors are free to return a result set (which must implement the
+	 * interface com.mysql.jdbc.ResultSetInternalMethods), and if so,
+	 * the server will not execute the query, and the given result set will be
+	 * returned to the application instead.
+	 * 
+	 * This method will be called while the connection-level mutex is held, so
+	 * it will only be called from one thread at a time.
+	 * 
+	 * @param sql the SQL representation of the statement
+	 * @param interceptedStatement the actual statement instance being intercepted
+	 * @param connection the connection the statement is using (passed in to make
+	 * thread-safe implementations straightforward)
+	 * 
+	 * @return a result set that should be returned to the application instead
+	 * of results that are created from actual execution of the intercepted 
+	 * statement.
+	 * 
+	 * @throws SQLException if an error occurs during execution
+	 * 
+	 * @see com.mysql.jdbc.ResultSetInternalMethods
+	 */
+
+	public abstract ResultSetInternalMethods preProcess(String sql,
+			Statement interceptedStatement, Connection connection)
+			throws SQLException;
+
+	/**
+	 * Called after the given statement has been sent to the server
+	 * for processing.
+	 * 
+	 * Interceptors are free to inspect the "original" result set, and if a
+	 * different result set is returned by the interceptor, it is used in place
+	 * of the "original" result set. (the result set returned by the interceptor
+	 * must implement the interface
+	 * com.mysql.jdbc.ResultSetInternalMethods).
+	 * 
+	 * This method will be called while the connection-level mutex is held, so
+	 * it will only be called from one thread at a time.
+	 * 
+	 * @param sql the SQL representation of the statement
+	 * @param interceptedStatement the actual statement instance being intercepted
+	 * @param connection the connection the statement is using (passed in to make
+	 * thread-safe implementations straightforward)
+	 * 
+	 * @return a result set that should be returned to the application instead
+	 * of results that are created from actual execution of the intercepted 
+	 * statement.
+	 * 
+	 * @throws SQLException if an error occurs during execution
+	 * 
+	 * @see com.mysql.jdbc.ResultSetInternalMethods
+	 */
+	public abstract ResultSetInternalMethods postProcess(String sql,
+			Statement interceptedStatement,
+			ResultSetInternalMethods originalResultSet,
+			Connection connection) throws SQLException;
+	
+	/**
+	 * Should the driver execute this interceptor only for the
+	 * "original" top-level query, and not put it in the execution
+	 * path for queries that may be executed from other interceptors?
+	 * 
+	 * If an interceptor issues queries using the connection it was created for,
+	 * and does not return <code>true</code> for this method, it must ensure 
+	 * that it does not cause infinite recursion.
+	 * 
+	 * @return true if the driver should ensure that this interceptor is only
+	 * executed for the top-level "original" query.
+	 */
+	public abstract boolean executeTopLevelOnly();
+
+	/**
+	 * Called by the driver when this extension should release any resources
+	 * it is holding and cleanup internally before the connection is
+	 * closed.
+	 */
+	public abstract void destroy();
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/StreamingNotifiable.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/StreamingNotifiable.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/StreamingNotifiable.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+package com.mysql.jdbc;
+
+public interface StreamingNotifiable {
+
+	public abstract void setWasStreamingResults();
+
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/StringUtils.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/StringUtils.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/StringUtils.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -25,6 +25,8 @@
 package com.mysql.jdbc;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -141,8 +143,10 @@
 			outputBuf.append("    "); //$NON-NLS-1$
 
 			for (int j = 0; j < 8; j++) {
-				if ((byteBuffer[p] > 32) && (byteBuffer[p] < 127)) {
-					outputBuf.append((char) byteBuffer[p] + " "); //$NON-NLS-1$
+				int b = 0xff & byteBuffer[p];
+				
+				if (b > 32 && b < 127) {
+					outputBuf.append((char) b + " "); //$NON-NLS-1$
 				} else {
 					outputBuf.append(". "); //$NON-NLS-1$
 				}
@@ -173,8 +177,10 @@
 		outputBuf.append("    "); //$NON-NLS-1$
 
 		for (int i = p; i < length; i++) {
-			if ((byteBuffer[i] > 32) && (byteBuffer[i] < 127)) {
-				outputBuf.append((char) byteBuffer[i] + " "); //$NON-NLS-1$
+			int b = 0xff & byteBuffer[i];
+			
+			if (b > 32 && b < 127) {
+				outputBuf.append((char) b + " "); //$NON-NLS-1$
 			} else {
 				outputBuf.append(". "); //$NON-NLS-1$
 			}
@@ -314,13 +320,17 @@
 	 * @return the first non-whitespace character, upper cased.
 	 */
 	public static char firstNonWsCharUc(String searchIn) {
+		return firstNonWsCharUc(searchIn, 0);
+	}
+	
+	public static char firstNonWsCharUc(String searchIn, int startAt) {
 		if (searchIn == null) {
 			return 0;
 		}
 
 		int length = searchIn.length();
 
-		for (int i = 0; i < length; i++) {
+		for (int i = startAt; i < length; i++) {
 			char c = searchIn.charAt(i);
 
 			if (!Character.isWhitespace(c)) {
@@ -331,6 +341,24 @@
 		return 0;
 	}
 
+	public static char firstAlphaCharUc(String searchIn, int startAt) {
+		if (searchIn == null) {
+			return 0;
+		}
+
+		int length = searchIn.length();
+
+		for (int i = startAt; i < length; i++) {
+			char c = searchIn.charAt(i);
+
+			if (Character.isLetter(c)) {
+				return Character.toUpperCase(c);
+			}
+		}
+
+		return 0;
+	}
+	
 	/**
 	 * Adds '+' to decimal numbers that are positive (MySQL doesn't understand
 	 * them otherwise
@@ -443,7 +471,8 @@
 	}
 
 	public static final byte[] getBytes(char[] c, String encoding,
-			String serverEncoding, boolean parserKnowsUnicode, Connection conn)
+			String serverEncoding, boolean parserKnowsUnicode, 
+			ConnectionImpl conn)
 			throws SQLException {
 		try {
 			
@@ -516,6 +545,48 @@
 		}
 	}
 
+	public static final byte[] getBytesWrapped(String s, char beginWrap, char endWrap,
+			SingleByteCharsetConverter converter, String encoding,
+			String serverEncoding, boolean parserKnowsUnicode)
+			throws SQLException {
+		try {
+			byte[] b = null;
+
+			if (converter != null) {
+				b = converter.toBytesWrapped(s, beginWrap, endWrap);
+			} else if (encoding == null) {
+				StringBuffer buf = new StringBuffer(s.length() + 2);
+				buf.append(beginWrap);
+				buf.append(s);
+				buf.append(endWrap);
+				
+				b = buf.toString().getBytes();
+			} else {
+				StringBuffer buf = new StringBuffer(s.length() + 2);
+				buf.append(beginWrap);
+				buf.append(s);
+				buf.append(endWrap);
+				
+				b = buf.toString().getBytes(encoding);
+
+				if (!parserKnowsUnicode && (encoding.equalsIgnoreCase("SJIS") //$NON-NLS-1$
+						|| encoding.equalsIgnoreCase("BIG5") //$NON-NLS-1$
+				|| encoding.equalsIgnoreCase("GBK"))) { //$NON-NLS-1$
+
+					if (!encoding.equalsIgnoreCase(serverEncoding)) {
+						b = escapeEasternUnicodeByteStream(b, s, 0, s.length());
+					}
+				}
+			}
+
+			return b;
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException(Messages.getString("StringUtils.5") //$NON-NLS-1$
+					+ encoding + Messages.getString("StringUtils.6"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+	
 	/**
 	 * DOCUMENT ME!
 	 * 
@@ -600,7 +671,8 @@
 	 *             if an encoding unsupported by the JVM is supplied.
 	 */
 	public static final byte[] getBytes(String s, String encoding,
-			String serverEncoding, boolean parserKnowsUnicode, Connection conn)
+			String serverEncoding, boolean parserKnowsUnicode, 
+			ConnectionImpl conn)
 			throws SQLException {
 		try {
 			SingleByteCharsetConverter converter = null;
@@ -620,17 +692,17 @@
 		}
 	}
 
-	public static int getInt(byte[] buf) throws NumberFormatException {
+	public static int getInt(byte[] buf, int offset, int endPos) throws NumberFormatException {
 		int base = 10;
 
-		int s = 0;
+		int s = offset;
 
 		/* Skip white space. */
-		while (Character.isWhitespace((char) buf[s]) && (s < buf.length)) {
+		while (Character.isWhitespace((char) buf[s]) && (s < endPos)) {
 			++s;
 		}
 
-		if (s == buf.length) {
+		if (s == endPos) {
 			throw new NumberFormatException(new String(buf));
 		}
 
@@ -658,7 +730,7 @@
 
 		int i = 0;
 
-		for (; s < buf.length; s++) {
+		for (; s < endPos; s++) {
 			char c = (char) buf[s];
 
 			if (Character.isDigit(c)) {
@@ -693,18 +765,26 @@
 		/* Return the result of the appropriate sign. */
 		return (negative ? (-i) : i);
 	}
+	
+	public static int getInt(byte[] buf) throws NumberFormatException {
+		return getInt(buf, 0, buf.length);
+	}
 
 	public static long getLong(byte[] buf) throws NumberFormatException {
+		return getLong(buf, 0, buf.length);
+	}
+	
+	public static long getLong(byte[] buf, int offset, int endpos) throws NumberFormatException {
 		int base = 10;
 
-		int s = 0;
+		int s = offset;
 
 		/* Skip white space. */
-		while (Character.isWhitespace((char) buf[s]) && (s < buf.length)) {
+		while (Character.isWhitespace((char) buf[s]) && (s < endpos)) {
 			++s;
 		}
 
-		if (s == buf.length) {
+		if (s == endpos) {
 			throw new NumberFormatException(new String(buf));
 		}
 
@@ -731,7 +811,7 @@
 		boolean overflow = false;
 		long i = 0;
 
-		for (; s < buf.length; s++) {
+		for (; s < endpos; s++) {
 			char c = (char) buf[s];
 
 			if (Character.isDigit(c)) {
@@ -968,7 +1048,10 @@
 			} else if (c == quoteChar && !escaped
 					&& contextMarker == Character.MIN_VALUE) {
 				contextMarker = c;
-			} else if (c == target.charAt(0) && !escaped
+			// This test looks complex, but remember that in certain locales, upper case
+			// of two different codepoints coverts to same codepoint, and vice-versa.
+			} else if ((Character.toUpperCase(c) == Character.toUpperCase(target.charAt(0)) ||
+					Character.toLowerCase(c) == Character.toLowerCase(target.charAt(0))) && !escaped
 					&& contextMarker == Character.MIN_VALUE) {
 				if (startsWithIgnoreCase(src, i, target))
 					return i;
@@ -1417,7 +1500,7 @@
 				: WILD_COMPARE_MATCH_NO_WILD);
 	}
 	
-	static byte[] s2b(String s, Connection conn) throws SQLException {
+	static byte[] s2b(String s, ConnectionImpl conn) throws SQLException {
 		if (s == null) {
 			return null;
 		}
@@ -1445,4 +1528,183 @@
 
 		return s.getBytes();
 	}
+	
+	public static int lastIndexOf(byte[] s, char c) {
+		if (s == null) {
+			return -1;
+		}
+		
+		for (int i = s.length - 1; i >= 0; i--) {
+			if (s[i] == c) {
+				return i;
+			}
+		}
+		
+		return -1;
+	}
+
+	public static int indexOf(byte[] s, char c) {
+		if (s == null) {
+			return -1;
+		}
+		
+		int length = s.length;
+		
+		for (int i = 0; i < length; i++) {
+			if (s[i] == c) {
+				return i;
+			}
+		}
+		
+		return -1;
+	}
+	
+	public static boolean isNullOrEmpty(String toTest) {
+		return (toTest == null || toTest.length() == 0);
+	}
+	
+	/**
+	 * Returns the given string, with comments removed
+	 * 
+	 * @param src
+	 *            the source string
+	 * @param stringOpens
+	 *            characters which delimit the "open" of a string
+	 * @param stringCloses
+	 *            characters which delimit the "close" of a string, in
+	 *            counterpart order to <code>stringOpens</code>
+	 * @param slashStarComments
+	 *            strip slash-star type "C" style comments
+	 * @param slashSlashComments
+	 *            strip slash-slash C++ style comments to end-of-line
+	 * @param hashComments
+	 *            strip #-style comments to end-of-line
+	 * @param dashDashComments
+	 *            strip "--" style comments to end-of-line
+	 * @return the input string with all comment-delimited data removed
+	 */
+	public static String stripComments(String src, String stringOpens,
+			String stringCloses, boolean slashStarComments,
+			boolean slashSlashComments, boolean hashComments,
+			boolean dashDashComments) {
+		if (src == null) {
+			return null;
+		}
+
+		StringBuffer buf = new StringBuffer(src.length());
+
+		// It's just more natural to deal with this as a stream
+		// when parsing..This code is currently only called when
+		// parsing the kind of metadata that developers are strongly
+		// recommended to cache anyways, so we're not worried
+		// about the _1_ extra object allocation if it cleans
+		// up the code
+
+		StringReader sourceReader = new StringReader(src);
+
+		int contextMarker = Character.MIN_VALUE;
+		boolean escaped = false;
+		int markerTypeFound = -1;
+
+		int ind = 0;
+
+		int currentChar = 0;
+
+		try {
+			while ((currentChar = sourceReader.read()) != -1) {
+
+				if (false && currentChar == '\\') {
+					escaped = !escaped;
+				} else if (markerTypeFound != -1 && currentChar == stringCloses.charAt(markerTypeFound)
+						&& !escaped) {
+					contextMarker = Character.MIN_VALUE;
+					markerTypeFound = -1;
+				} else if ((ind = stringOpens.indexOf(currentChar)) != -1
+						&& !escaped && contextMarker == Character.MIN_VALUE) {
+					markerTypeFound = ind;
+					contextMarker = currentChar;
+				}
+
+				if (contextMarker == Character.MIN_VALUE && currentChar == '/'
+						&& (slashSlashComments || slashStarComments)) {
+					currentChar = sourceReader.read();
+					if (currentChar == '*' && slashStarComments) {
+						int prevChar = 0;
+						while ((currentChar = sourceReader.read()) != '/'
+								|| prevChar != '*') {
+							if (currentChar == '\r') {
+
+								currentChar = sourceReader.read();
+								if (currentChar == '\n') {
+									currentChar = sourceReader.read();
+								}
+							} else {
+								if (currentChar == '\n') {
+
+									currentChar = sourceReader.read();
+								}
+							}
+							if (currentChar < 0)
+								break;
+							prevChar = currentChar;
+						}
+						continue;
+					} else if (currentChar == '/' && slashSlashComments) {
+						while ((currentChar = sourceReader.read()) != '\n'
+								&& currentChar != '\r' && currentChar >= 0)
+							;
+					}
+				} else if (contextMarker == Character.MIN_VALUE
+						&& currentChar == '#' && hashComments) {
+					// Slurp up everything until the newline
+					while ((currentChar = sourceReader.read()) != '\n'
+							&& currentChar != '\r' && currentChar >= 0)
+						;
+				} else if (contextMarker == Character.MIN_VALUE
+						&& currentChar == '-' && dashDashComments) {
+					currentChar = sourceReader.read();
+
+					if (currentChar == -1 || currentChar != '-') {
+						buf.append('-');
+
+						if (currentChar != -1) {
+							buf.append(currentChar);
+						}
+
+						continue;
+					}
+
+					// Slurp up everything until the newline
+
+					while ((currentChar = sourceReader.read()) != '\n'
+							&& currentChar != '\r' && currentChar >= 0)
+						;
+				}
+
+				if (currentChar != -1) {
+					buf.append((char) currentChar);
+				}
+			}
+		} catch (IOException ioEx) {
+			// we'll never see this from a StringReader
+		}
+
+		return buf.toString();
+	}
+	
+	public static final boolean isEmptyOrWhitespaceOnly(String str) {
+		if (str == null || str.length() == 0) {
+			return true;
+		}
+		
+		int length = str.length();
+		
+		for (int i = 0; i < length; i++) {
+			if (!Character.isWhitespace(str.charAt(i))) {
+				return false;
+			}
+		}
+		
+		return true;
+	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/TimeUtil.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/TimeUtil.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/TimeUtil.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -33,7 +33,6 @@
 import java.util.Collections;
 import java.util.GregorianCalendar;
 import java.util.HashMap;
-import java.util.Locale;
 import java.util.Map;
 import java.util.TimeZone;
 
@@ -809,7 +808,7 @@
 	 * 
 	 * @return the times changed to the timezone 'toTz'
 	 */
-	public static Time changeTimezone(Connection conn,
+	public static Time changeTimezone(ConnectionImpl conn,
 			Calendar sessionCalendar, 
 			Calendar targetCalendar, 
 			Time t, 
@@ -871,7 +870,7 @@
 	 * 
 	 * @return the timestamp changed to the timezone 'toTz'
 	 */
-	public static Timestamp changeTimezone(Connection conn, 
+	public static Timestamp changeTimezone(ConnectionImpl conn, 
 			Calendar sessionCalendar, 
 			Calendar targetCalendar, 
 			Timestamp tstamp,
@@ -1153,7 +1152,6 @@
 	
 	private static String timeFormattedString(int hours, int minutes, int seconds) {
 		StringBuffer buf = new StringBuffer(8);
-		
 		if (hours < 10) {
 			buf.append("0");
 		}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/UpdatableResultSet.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/UpdatableResultSet.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/UpdatableResultSet.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,13 +1,13 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as 
+ it under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.
 
- There are special exceptions to the terms and conditions of the GPL 
- as it is applied to this software. View the full text of the 
- exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
  software distribution.
 
  This program is distributed in the hope that it will be useful,
@@ -27,25 +27,30 @@
 import com.mysql.jdbc.profiler.ProfileEventSink;
 import com.mysql.jdbc.profiler.ProfilerEvent;
 
+import java.io.InputStream;
+import java.io.Reader;
 import java.math.BigDecimal;
 
 import java.sql.SQLException;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
 
 /**
  * A result set that is updatable.
- * 
+ *
  * @author Mark Matthews
  */
-public class UpdatableResultSet extends ResultSet {
+public class UpdatableResultSet extends ResultSetImpl {
 	/** Marker for 'stream' data when doing INSERT rows */
-	private final static byte[] STREAM_DATA_MARKER = "** STREAM DATA **" //$NON-NLS-1$
+	protected final static byte[] STREAM_DATA_MARKER = "** STREAM DATA **" //$NON-NLS-1$
 	.getBytes();
 
-	private SingleByteCharsetConverter charConverter;
+	protected SingleByteCharsetConverter charConverter;
 
 	private String charEncoding;
 
@@ -60,13 +65,16 @@
 	private boolean initializedCharConverter = false;
 
 	/** PreparedStatement used to insert data */
-	private com.mysql.jdbc.PreparedStatement inserter = null;
+	protected com.mysql.jdbc.PreparedStatement inserter = null;
 
 	private String insertSQL = null;
 
 	/** Is this result set updateable? */
 	private boolean isUpdatable = false;
 
+	/** Reason the result set is not updatable */
+	private String notUpdatableReason = null;
+
 	/** List of primary keys */
 	private List primaryKeyIndicies = null;
 
@@ -80,40 +88,21 @@
 	private String refreshSQL = null;
 
 	/** The binary data for the 'current' row */
-	private Object[] savedCurrentRow;
+	private ResultSetRow savedCurrentRow;
 
-	private String tableOnlyName;
-
 	/** PreparedStatement used to delete data */
-	private com.mysql.jdbc.PreparedStatement updater = null;
+	protected com.mysql.jdbc.PreparedStatement updater = null;
 
 	/** SQL for in-place modifcation */
 	private String updateSQL = null;
 
-	/**
-	 * Create a result set for an executeUpdate statement.
-	 * 
-	 * @param updateCount
-	 *            the number of rows affected by the update
-	 * @param updateID
-	 *            the autoincrement value (if any)
-	 * @param conn
-	 *            DOCUMENT ME!
-	 * @param creatorStmt
-	 *            DOCUMENT ME!
-	 * 
-	 * @throws SQLException
-	 *             DOCUMENT ME!
-	 */
-	public UpdatableResultSet(long updateCount, long updateID, Connection conn,
-			Statement creatorStmt) throws SQLException {
-		super(updateCount, updateID, conn, creatorStmt);
-		checkUpdatability();
-	}
+	private boolean populateInserterWithDefaultValues = false;
 
+	private Map databasesUsedToTablesUsed = null;
+
 	/**
 	 * Creates a new ResultSet object.
-	 * 
+	 *
 	 * @param catalog
 	 *            the database in use when we were created
 	 * @param fields
@@ -124,50 +113,52 @@
 	 *            the Connection that created us.
 	 * @param creatorStmt
 	 *            DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
-	public UpdatableResultSet(String catalog, Field[] fields, RowData tuples,
-			Connection conn, Statement creatorStmt) throws SQLException {
+	protected UpdatableResultSet(String catalog, Field[] fields, RowData tuples,
+			ConnectionImpl conn, StatementImpl creatorStmt) throws SQLException {
 		super(catalog, fields, tuples, conn, creatorStmt);
 		checkUpdatability();
+		this.populateInserterWithDefaultValues =
+			this.connection.getPopulateInsertRowWithDefaultValues();
 	}
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Move to an absolute row number in the result set.
 	 * </p>
-	 * 
+	 *
 	 * <p>
 	 * If row is positive, moves to an absolute row with respect to the
 	 * beginning of the result set. The first row is row 1, the second is row 2,
 	 * etc.
 	 * </p>
-	 * 
+	 *
 	 * <p>
 	 * If row is negative, moves to an absolute row position with respect to the
 	 * end of result set. For example, calling absolute(-1) positions the cursor
 	 * on the last row, absolute(-2) indicates the next-to-last row, etc.
 	 * </p>
-	 * 
+	 *
 	 * <p>
 	 * An attempt to position the cursor beyond the first/last row in the result
 	 * set, leaves the cursor before/after the first/last row, respectively.
 	 * </p>
-	 * 
+	 *
 	 * <p>
 	 * Note: Calling absolute(1) is the same as calling first(). Calling
 	 * absolute(-1) is the same as calling last().
 	 * </p>
-	 * 
+	 *
 	 * @param row
 	 *            DOCUMENT ME!
-	 * 
+	 *
 	 * @return true if on the result set, false if off.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or row is 0, or result
 	 *                set type is TYPE_FORWARD_ONLY.
@@ -178,12 +169,12 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Moves to the end of the result set, just after the last row. Has no
 	 * effect if the result set contains no rows.
 	 * </p>
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or result set type is
 	 *                TYPE_FORWARD_ONLY.
@@ -194,12 +185,12 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Moves to the front of the result set, just before the first row. Has no
 	 * effect if the result set contains no rows.
 	 * </p>
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or result set type is
 	 *                TYPE_FORWARD_ONLY
@@ -213,7 +204,7 @@
 	 * updateXXX() method(s) and before calling updateRow() to rollback the
 	 * updates made to a row. If no updates have been made or updateRow() has
 	 * already been called, then this method has no effect.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or if called when on
 	 *                the insert row.
@@ -229,7 +220,7 @@
 
 	/*
 	 * (non-Javadoc)
-	 * 
+	 *
 	 * @see com.mysql.jdbc.ResultSet#checkRowPos()
 	 */
 	protected void checkRowPos() throws SQLException {
@@ -242,16 +233,41 @@
 
 	/**
 	 * Is this ResultSet updateable?
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
-	private void checkUpdatability() throws SQLException {
+	protected void checkUpdatability() throws SQLException {
+		if (this.fields == null) {
+			// we've been created to be populated with cached
+			// metadata, and we don't have the metadata yet,
+			// we'll be called again by
+			// Connection.initializeResultsMetadataFromCache()
+			// when the metadata has been made available
+
+			return;
+		}
+
 		String singleTableName = null;
 		String catalogName = null;
 
 		int primaryKeyCount = 0;
 
+		// We can only do this if we know that there is a currently
+		// selected database, or if we're talking to a > 4.1 version
+		// of MySQL server (as it returns database names in field
+		// info)
+		//
+		if ((this.catalog == null) || (this.catalog.length() == 0)) {
+			this.catalog = this.fields[0].getDatabaseName();
+
+			if ((this.catalog == null) || (this.catalog.length() == 0)) {
+				throw SQLError.createSQLException(Messages
+						.getString("UpdatableResultSet.43") //$NON-NLS-1$
+						, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
 		if (this.fields.length > 0) {
 			singleTableName = this.fields[0].getOriginalTableName();
 			catalogName = this.fields[0].getDatabaseName();
@@ -261,6 +277,13 @@
 				catalogName = this.catalog;
 			}
 
+			if (singleTableName != null && singleTableName.length() == 0) {
+				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
+
+				return;
+			}
+
 			if (this.fields[0].isPrimaryKey()) {
 				primaryKeyCount++;
 			}
@@ -277,9 +300,17 @@
 					otherCatalogName = this.catalog;
 				}
 
+				if (otherTableName != null && otherTableName.length() == 0) {
+					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
+
+					return;
+				}
+
 				if ((singleTableName == null)
 						|| !otherTableName.equals(singleTableName)) {
 					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.0");
 
 					return;
 				}
@@ -288,6 +319,7 @@
 				if ((catalogName == null)
 						|| !otherCatalogName.equals(catalogName)) {
 					this.isUpdatable = false;
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.1");
 
 					return;
 				}
@@ -299,39 +331,17 @@
 
 			if ((singleTableName == null) || (singleTableName.length() == 0)) {
 				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.2");
 
 				return;
 			}
 		} else {
 			this.isUpdatable = false;
+			this.notUpdatableReason = Messages.getString("NotUpdatableReason.3");
 
 			return;
 		}
 
-		// 
-		// Must have at least one primary key
-		//
-		if (primaryKeyCount == 0) {
-			this.isUpdatable = false;
-
-			return;
-		}
-
-		// We can only do this if we know that there is a currently
-		// selected database, or if we're talking to a > 4.1 version
-		// of MySQL server (as it returns database names in field
-		// info)
-		//
-		if ((this.catalog == null) || (this.catalog.length() == 0)) {
-			this.catalog = this.fields[0].getDatabaseName();
-
-			if ((this.catalog == null) || (this.catalog.length() == 0)) {
-				throw SQLError.createSQLException(Messages
-						.getString("UpdatableResultSet.43") //$NON-NLS-1$
-						, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
-			}
-		}
-
 		if (this.connection.getStrictUpdates()) {
 			java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
 
@@ -358,8 +368,11 @@
 				}
 			}
 
-			if (primaryKeyNames.size() == 0) {
+			int existingPrimaryKeysCount = primaryKeyNames.size();
+
+			if (existingPrimaryKeysCount == 0) {
 				this.isUpdatable = false;
+				this.notUpdatableReason = Messages.getString("NotUpdatableReason.5");
 
 				return; // we can't update tables w/o keys
 			}
@@ -381,6 +394,8 @@
 									.toUpperCase()) == null) {
 								// we don't know about this key, so give up :(
 								this.isUpdatable = false;
+								this.notUpdatableReason = Messages.getString("NotUpdatableReason.6",
+										new Object[] {originalName});
 
 								return;
 							}
@@ -391,10 +406,29 @@
 
 			this.isUpdatable = primaryKeyNames.isEmpty();
 
+			if (!this.isUpdatable) {
+				if (existingPrimaryKeysCount > 1) {
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.7");
+				} else {
+					this.notUpdatableReason = Messages.getString("NotUpdatableReason.4");
+				}
+
+				return;
+			}
+		}
+
+		//
+		// Must have at least one primary key
+		//
+		if (primaryKeyCount == 0) {
+			this.isUpdatable = false;
+			this.notUpdatableReason = Messages.getString("NotUpdatableReason.4");
+
 			return;
 		}
 
 		this.isUpdatable = true;
+		this.notUpdatableReason = null;
 
 		return;
 	}
@@ -402,7 +436,7 @@
 	/**
 	 * JDBC 2.0 Delete the current row from the result set and the underlying
 	 * database. Cannot be called when on the insert row.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or if called when on
 	 *                the insert row.
@@ -413,7 +447,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.onInsertRow) {
@@ -431,7 +465,7 @@
 				generateStatements();
 			}
 
-			this.deleter = this.connection
+			this.deleter = (PreparedStatement) this.connection
 					.clientPrepareStatement(this.deleteSQL);
 		}
 
@@ -453,16 +487,16 @@
 				int index = ((Integer) this.primaryKeyIndicies.get(0))
 						.intValue();
 				String currentVal = ((characterEncoding == null) ? new String(
-						(byte[]) this.thisRow[index]) : new String(
-						(byte[]) this.thisRow[index], characterEncoding));
+						(byte[]) this.thisRow.getColumnValue(index)) : new String(
+						(byte[]) this.thisRow.getColumnValue(index), characterEncoding));
 				this.deleter.setString(1, currentVal);
 			} else {
 				for (int i = 0; i < numKeys; i++) {
 					int index = ((Integer) this.primaryKeyIndicies.get(i))
 							.intValue();
 					String currentVal = ((characterEncoding == null) ? new String(
-							(byte[]) this.thisRow[index])
-							: new String((byte[]) this.thisRow[index],
+							(byte[]) this.thisRow.getColumnValue(index))
+							: new String((byte[]) this.thisRow.getColumnValue(index),
 									characterEncoding));
 					this.deleter.setString(i + 1, currentVal);
 				}
@@ -479,60 +513,56 @@
 
 	private synchronized void extractDefaultValues() throws SQLException {
 		java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
-
+		this.defaultColumnValue = new byte[this.fields.length][];
+		
 		java.sql.ResultSet columnsResultSet = null;
+		Iterator referencedDbs = this.databasesUsedToTablesUsed.entrySet().iterator();
 
-		try {
-			columnsResultSet = dbmd.getColumns(this.catalog, null,
-					this.tableOnlyName, "%"); //$NON-NLS-1$
+		while (referencedDbs.hasNext()) {
+		    Map.Entry dbEntry = (Map.Entry)referencedDbs.next();
+		    String databaseName = dbEntry.getKey().toString();
 
-			HashMap columnNameToDefaultValueMap = new HashMap(
-					this.fields.length /* at least this big... */);
+		    Iterator referencedTables = ((Map)dbEntry.getValue()).entrySet().iterator();
 
-			while (columnsResultSet.next()) {
-				String columnName = columnsResultSet.getString("COLUMN_NAME"); //$NON-NLS-1$
-				byte[] defaultValue = columnsResultSet.getBytes("COLUMN_DEF"); //$NON-NLS-1$
+		    while (referencedTables.hasNext()) {
+		        Map.Entry tableEntry = (Map.Entry)referencedTables.next();
+		        String tableName = tableEntry.getKey().toString();
+		        Map columnNamesToIndices = (Map)tableEntry.getValue();
 
-				columnNameToDefaultValueMap.put(columnName, defaultValue);
-			}
+        		try {
+        			columnsResultSet = dbmd.getColumns(this.catalog, null,
+        			        tableName, "%"); //$NON-NLS-1$
 
-			int numFields = this.fields.length;
+        			while (columnsResultSet.next()) {
+        				String columnName = columnsResultSet.getString("COLUMN_NAME"); //$NON-NLS-1$
+        				byte[] defaultValue = columnsResultSet.getBytes("COLUMN_DEF"); //$NON-NLS-1$
 
-			this.defaultColumnValue = new byte[numFields][];
+        				if (columnNamesToIndices.containsKey(columnName)) {
+        				    int localColumnIndex = ((Integer)columnNamesToIndices.get(columnName)).intValue();
 
-			for (int i = 0; i < numFields; i++) {
-				String defValTableName = this.fields[i].getOriginalName();
+        				    this.defaultColumnValue[localColumnIndex] = defaultValue;
+        				} // else assert?
+        			}
+        		} finally {
+        			if (columnsResultSet != null) {
+        				columnsResultSet.close();
 
-				if ((defValTableName == null)
-						|| (defValTableName.length() == 0)) {
-					defValTableName = this.fields[i].getName();
-				}
-
-				if (defValTableName != null) {
-					byte[] defaultVal = (byte[]) columnNameToDefaultValueMap
-							.get(defValTableName);
-
-					this.defaultColumnValue[i] = defaultVal;
-				}
-			}
-		} finally {
-			if (columnsResultSet != null) {
-				columnsResultSet.close();
-
-				columnsResultSet = null;
-			}
+        				columnsResultSet = null;
+        			}
+        		}
+		    }
 		}
 	}
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Moves to the first row in the result set.
 	 * </p>
-	 * 
+	 *
 	 * @return true if on a valid row, false if no rows in the result set.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or result set type is
 	 *                TYPE_FORWARD_ONLY.
@@ -544,7 +574,7 @@
 	/**
 	 * Figure out whether or not this ResultSet is updateable, and if so,
 	 * generate the PreparedStatements to support updates.
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 * @throws NotUpdatable
@@ -555,40 +585,19 @@
 			this.doingUpdates = false;
 			this.onInsertRow = false;
 
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		String quotedId = getQuotedIdChar();
 
-		if (this.fields[0].getOriginalTableName() != null) {
-			StringBuffer tableNameBuffer = new StringBuffer();
+		Map tableNamesSoFar = null;
 
-			String databaseName = this.fields[0].getDatabaseName();
-
-			if ((databaseName != null) && (databaseName.length() > 0)) {
-				tableNameBuffer.append(quotedId);
-				tableNameBuffer.append(databaseName);
-				tableNameBuffer.append(quotedId);
-				tableNameBuffer.append('.');
-			}
-
-			this.tableOnlyName = this.fields[0].getOriginalTableName();
-
-			tableNameBuffer.append(quotedId);
-			tableNameBuffer.append(this.tableOnlyName);
-			tableNameBuffer.append(quotedId);
-
-			this.qualifiedAndQuotedTableName = tableNameBuffer.toString();
+		if (this.connection.lowerCaseTableNames()) {
+		    tableNamesSoFar = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+		    this.databasesUsedToTablesUsed = new TreeMap(String.CASE_INSENSITIVE_ORDER);
 		} else {
-			StringBuffer tableNameBuffer = new StringBuffer();
-
-			this.tableOnlyName = this.fields[0].getTableName();
-
-			tableNameBuffer.append(quotedId);
-			tableNameBuffer.append(this.tableOnlyName);
-			tableNameBuffer.append(quotedId);
-
-			this.qualifiedAndQuotedTableName = tableNameBuffer.toString();
+		    tableNamesSoFar = new TreeMap();
+		    this.databasesUsedToTablesUsed = new TreeMap();
 		}
 
 		this.primaryKeyIndicies = new ArrayList();
@@ -597,6 +606,9 @@
 		StringBuffer keyValues = new StringBuffer();
 		StringBuffer columnNames = new StringBuffer();
 		StringBuffer insertPlaceHolders = new StringBuffer();
+		StringBuffer allTablesBuf = new StringBuffer();
+		Map columnIndicesToTable = new HashMap();
+
 		boolean firstTime = true;
 		boolean keysFirstTime = true;
 
@@ -604,6 +616,66 @@
 				: "=";
 
 		for (int i = 0; i < this.fields.length; i++) {
+		    StringBuffer tableNameBuffer = new StringBuffer();
+		    Map columnNameToIndex = null;
+
+		    // FIXME: What about no table?
+		    if (this.fields[i].getOriginalTableName() != null) {
+
+	            String databaseName = this.fields[i].getDatabaseName();
+
+	            if ((databaseName != null) && (databaseName.length() > 0)) {
+	                tableNameBuffer.append(quotedId);
+	                tableNameBuffer.append(databaseName);
+	                tableNameBuffer.append(quotedId);
+	                tableNameBuffer.append('.');
+	            }
+
+	            String tableOnlyName = this.fields[i].getOriginalTableName();
+
+	            tableNameBuffer.append(quotedId);
+	            tableNameBuffer.append(tableOnlyName);
+	            tableNameBuffer.append(quotedId);
+
+	            String fqTableName = tableNameBuffer.toString();
+
+	            if (!tableNamesSoFar.containsKey(fqTableName)) {
+	                if (!tableNamesSoFar.isEmpty()) {
+	                    allTablesBuf.append(',');
+	                }
+
+	                allTablesBuf.append(fqTableName);
+	                tableNamesSoFar.put(fqTableName, fqTableName);
+	            }
+
+	            columnIndicesToTable.put(new Integer(i), fqTableName);
+
+	            columnNameToIndex = getColumnsToIndexMapForTableAndDB(databaseName, tableOnlyName);
+	        } else {
+	            String tableOnlyName = this.fields[i].getTableName();
+
+	            if (tableOnlyName != null) {
+    	            tableNameBuffer.append(quotedId);
+    	            tableNameBuffer.append(tableOnlyName);
+    	            tableNameBuffer.append(quotedId);
+
+    	            String fqTableName = tableNameBuffer.toString();
+
+                    if (!tableNamesSoFar.containsKey(fqTableName)) {
+                        if (!tableNamesSoFar.isEmpty()) {
+                            allTablesBuf.append(',');
+                        }
+
+                        allTablesBuf.append(fqTableName);
+                        tableNamesSoFar.put(fqTableName, fqTableName);
+                    }
+
+                    columnIndicesToTable.put(new Integer(i), fqTableName);
+
+                    columnNameToIndex = getColumnsToIndexMapForTableAndDB(this.catalog, tableOnlyName);
+	            }
+	        }
+
 			String originalColumnName = this.fields[i].getOriginalName();
 			String columnName = null;
 
@@ -615,8 +687,43 @@
 				columnName = this.fields[i].getName();
 			}
 
+			if (columnNameToIndex != null && columnName != null) {
+			    columnNameToIndex.put(columnName, new Integer(i));
+			}
+
+			String originalTableName = this.fields[i].getOriginalTableName();
+            String tableName = null;
+
+            if (this.connection.getIO().hasLongColumnInfo()
+                    && (originalTableName != null)
+                    && (originalTableName.length() > 0)) {
+                tableName = originalTableName;
+            } else {
+                tableName = this.fields[i].getTableName();
+            }
+
+            StringBuffer fqcnBuf = new StringBuffer();
+            String databaseName = this.fields[i].getDatabaseName();
+
+            if (databaseName != null && databaseName.length() > 0) {
+                fqcnBuf.append(quotedId);
+                fqcnBuf.append(databaseName);
+                fqcnBuf.append(quotedId);
+                fqcnBuf.append('.');
+            }
+
+            fqcnBuf.append(quotedId);
+            fqcnBuf.append(tableName);
+            fqcnBuf.append(quotedId);
+            fqcnBuf.append('.');
+            fqcnBuf.append(quotedId);
+            fqcnBuf.append(columnName);
+            fqcnBuf.append(quotedId);
+
+            String qualifiedColumnName = fqcnBuf.toString();
+
 			if (this.fields[i].isPrimaryKey()) {
-				this.primaryKeyIndicies.add(new Integer(i));
+				this.primaryKeyIndicies.add(Constants.integerValueOf(i));
 
 				if (!keysFirstTime) {
 					keyValues.append(" AND "); //$NON-NLS-1$
@@ -624,9 +731,7 @@
 					keysFirstTime = false;
 				}
 
-				keyValues.append(quotedId);
-				keyValues.append(columnName);
-				keyValues.append(quotedId);
+				keyValues.append(qualifiedColumnName);
 				keyValues.append(equalsStr);
 				keyValues.append("?"); //$NON-NLS-1$
 			}
@@ -642,16 +747,14 @@
 
 			insertPlaceHolders.append("?"); //$NON-NLS-1$
 
-			columnNames.append(quotedId);
-			columnNames.append(columnName);
-			columnNames.append(quotedId);
+			columnNames.append(qualifiedColumnName);
 
-			fieldValues.append(quotedId);
-			fieldValues.append(columnName);
-			fieldValues.append(quotedId);
+			fieldValues.append(qualifiedColumnName);
 			fieldValues.append("=?"); //$NON-NLS-1$
 		}
 
+		this.qualifiedAndQuotedTableName = allTablesBuf.toString();
+
 		this.updateSQL = "UPDATE " + this.qualifiedAndQuotedTableName + " " //$NON-NLS-1$ //$NON-NLS-2$
 				+ fieldValues.toString() //$NON-NLS-1$ //$NON-NLS-2$
 				+ " WHERE " + keyValues.toString(); //$NON-NLS-1$
@@ -666,6 +769,30 @@
 				+ keyValues.toString();
 	}
 
+    private Map getColumnsToIndexMapForTableAndDB(String databaseName, String tableName) {
+        Map nameToIndex;
+        Map tablesUsedToColumnsMap = (Map)this.databasesUsedToTablesUsed.get(databaseName);
+
+        if (tablesUsedToColumnsMap == null) {
+            if (this.connection.lowerCaseTableNames()) {
+                tablesUsedToColumnsMap = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+            } else {
+                tablesUsedToColumnsMap = new TreeMap();
+            }
+
+            this.databasesUsedToTablesUsed.put(databaseName, tablesUsedToColumnsMap);
+        }
+
+        nameToIndex = (Map)tablesUsedToColumnsMap.get(tableName);
+
+        if (nameToIndex == null) {
+            nameToIndex = new HashMap();
+            tablesUsedToColumnsMap.put(tableName, nameToIndex);
+        }
+
+        return nameToIndex;
+    }
+
 	private synchronized SingleByteCharsetConverter getCharConverter()
 			throws SQLException {
 		if (!this.initializedCharConverter) {
@@ -684,9 +811,9 @@
 	/**
 	 * JDBC 2.0 Return the concurrency of this result set. The concurrency used
 	 * is determined by the statement that created the result set.
-	 * 
+	 *
 	 * @return the concurrency type, CONCUR_READ_ONLY, etc.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -713,7 +840,7 @@
 	/**
 	 * JDBC 2.0 Insert the contents of the insert row into the result set and
 	 * the database. Must be on the insert row when this method is called.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, if called when not on
 	 *                the insert row, or if all non-nullable columns in the
@@ -745,23 +872,28 @@
 			//
 			if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) {
 				newRow[i] = String.valueOf(autoIncrementId).getBytes();
+				this.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]);
 			}
 		}
 
-		this.rowData.addRow(newRow);
+		ResultSetRow resultSetRow = new ByteArrayRow(newRow);
+
+		refreshRow(this.inserter, resultSetRow);
+
+		this.rowData.addRow(resultSetRow);
 		resetInserter();
 	}
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Determine if the cursor is after the last row in the result set.
 	 * </p>
-	 * 
+	 *
 	 * @return true if after the last row, false otherwise. Returns false when
 	 *         the result set contains no rows.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs.
 	 */
@@ -771,14 +903,14 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Determine if the cursor is before the first row in the result set.
 	 * </p>
-	 * 
+	 *
 	 * @return true if before the first row, false otherwise. Returns false when
 	 *         the result set contains no rows.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs.
 	 */
@@ -788,13 +920,13 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Determine if the cursor is on the first row of the result set.
 	 * </p>
-	 * 
+	 *
 	 * @return true if on the first row, false otherwise.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs.
 	 */
@@ -804,16 +936,16 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Determine if the cursor is on the last row of the result set. Note:
 	 * Calling isLast() may be expensive since the JDBC driver might need to
 	 * fetch ahead one row in order to determine whether the current row is the
 	 * last row in the result set.
 	 * </p>
-	 * 
+	 *
 	 * @return true if on the last row, false otherwise.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs.
 	 */
@@ -827,13 +959,13 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Moves to the last row in the result set.
 	 * </p>
-	 * 
+	 *
 	 * @return true if on a valid row, false if no rows in the result set.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or result set type is
 	 *                TYPE_FORWARD_ONLY.
@@ -845,7 +977,7 @@
 	/**
 	 * JDBC 2.0 Move the cursor to the remembered cursor position, usually the
 	 * current row. Has no effect unless the cursor is on the insert row.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or the result set is
 	 *                not updatable
@@ -856,7 +988,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.onInsertRow) {
@@ -875,7 +1007,7 @@
 	 * cursor is on the insert row. All of the columns in a result set must be
 	 * given a value each time this method is called before calling insertRow().
 	 * UpdateXXX()must be called before getXXX() on a column.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or the result set is
 	 *                not updatable
@@ -886,7 +1018,7 @@
 		checkClosed();
 
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.inserter == null) {
@@ -894,9 +1026,12 @@
 				generateStatements();
 			}
 
-			this.inserter = this.connection
+			this.inserter = (PreparedStatement) this.connection
 					.clientPrepareStatement(this.insertSQL);
-			extractDefaultValues();
+			if (this.populateInserterWithDefaultValues) {
+				extractDefaultValues();
+			}
+
 			resetInserter();
 		} else {
 			resetInserter();
@@ -907,47 +1042,54 @@
 		this.onInsertRow = true;
 		this.doingUpdates = false;
 		this.savedCurrentRow = this.thisRow;
-		this.thisRow = new byte[numFields][];
+		byte[][] newRowData = new byte[numFields][];
+		this.thisRow = new ByteArrayRow(newRowData);
 
 		for (int i = 0; i < numFields; i++) {
-			if (this.defaultColumnValue[i] != null) {
-				Field f = this.fields[i];
+			if (!this.populateInserterWithDefaultValues) {
+				this.inserter.setBytesNoEscapeNoQuotes(i + 1,
+						"DEFAULT".getBytes());
+				newRowData = null;
+			} else {
+				if (this.defaultColumnValue[i] != null) {
+					Field f = this.fields[i];
 
-				switch (f.getMysqlType()) {
-				case MysqlDefs.FIELD_TYPE_DATE:
-				case MysqlDefs.FIELD_TYPE_DATETIME:
-				case MysqlDefs.FIELD_TYPE_NEWDATE:
-				case MysqlDefs.FIELD_TYPE_TIME:
-				case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+					switch (f.getMysqlType()) {
+					case MysqlDefs.FIELD_TYPE_DATE:
+					case MysqlDefs.FIELD_TYPE_DATETIME:
+					case MysqlDefs.FIELD_TYPE_NEWDATE:
+					case MysqlDefs.FIELD_TYPE_TIME:
+					case MysqlDefs.FIELD_TYPE_TIMESTAMP:
 
-					if (this.defaultColumnValue[i].length > 7
-							&& this.defaultColumnValue[i][0] == (byte) 'C'
-							&& this.defaultColumnValue[i][1] == (byte) 'U'
-							&& this.defaultColumnValue[i][2] == (byte) 'R'
-							&& this.defaultColumnValue[i][3] == (byte) 'R'
-							&& this.defaultColumnValue[i][4] == (byte) 'E'
-							&& this.defaultColumnValue[i][5] == (byte) 'N'
-							&& this.defaultColumnValue[i][6] == (byte) 'T'
-							&& this.defaultColumnValue[i][7] == (byte) '_') {
-						this.inserter.setBytesNoEscapeNoQuotes(i + 1,
-								this.defaultColumnValue[i]);
+						if (this.defaultColumnValue[i].length > 7
+								&& this.defaultColumnValue[i][0] == (byte) 'C'
+								&& this.defaultColumnValue[i][1] == (byte) 'U'
+								&& this.defaultColumnValue[i][2] == (byte) 'R'
+								&& this.defaultColumnValue[i][3] == (byte) 'R'
+								&& this.defaultColumnValue[i][4] == (byte) 'E'
+								&& this.defaultColumnValue[i][5] == (byte) 'N'
+								&& this.defaultColumnValue[i][6] == (byte) 'T'
+								&& this.defaultColumnValue[i][7] == (byte) '_') {
+							this.inserter.setBytesNoEscapeNoQuotes(i + 1,
+									this.defaultColumnValue[i]);
 
-						break;
+							break;
+						}
+					default:
+						this.inserter.setBytes(i + 1, this.defaultColumnValue[i],
+								false, false);
 					}
-				default:
-					this.inserter.setBytes(i + 1, this.defaultColumnValue[i],
-							false, false);
+
+					// This value _could_ be changed from a getBytes(), so we
+					// need a copy....
+					byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
+					System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0,
+							defaultValueCopy.length);
+					newRowData[i] = defaultValueCopy;
+				} else {
+					this.inserter.setNull(i + 1, java.sql.Types.NULL);
+					newRowData[i] = null;
 				}
-
-				// This value _could_ be changed from a getBytes(), so we
-				// need a copy....
-				byte[] defaultValueCopy = new byte[this.defaultColumnValue[i].length];
-				System.arraycopy(defaultColumnValue[i], 0, defaultValueCopy, 0,
-						defaultValueCopy.length);
-				this.thisRow[i] = defaultValueCopy;
-			} else {
-				this.inserter.setNull(i + 1, java.sql.Types.NULL);
-				this.thisRow[i] = null;
 			}
 		}
 	}
@@ -960,14 +1102,14 @@
 	 * A ResultSet is initially positioned before its first row, the first call
 	 * to next makes the first row the current row; the second call makes the
 	 * second row the current row, etc.
-	 * 
+	 *
 	 * <p>
 	 * If an input stream from the previous row is open, it is implicitly
 	 * closed. The ResultSet's warning chain is cleared when a new row is read
 	 * </p>
-	 * 
+	 *
 	 * @return true if the new current is valid; false if there are no more rows
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database access error occurs
 	 */
@@ -979,14 +1121,14 @@
 	 * The prev method is not part of JDBC, but because of the architecture of
 	 * this driver it is possible to move both forward and backward within the
 	 * result set.
-	 * 
+	 *
 	 * <p>
 	 * If an input stream from the previous row is open, it is implicitly
 	 * closed. The ResultSet's warning chain is cleared when a new row is read
 	 * </p>
-	 * 
+	 *
 	 * @return true if the new current is valid; false if there are no more rows
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database access error occurs
 	 */
@@ -996,18 +1138,18 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Moves to the previous row in the result set.
 	 * </p>
-	 * 
+	 *
 	 * <p>
 	 * Note: previous() is not the same as relative(-1) since it makes sense to
 	 * call previous() when there is no current row.
 	 * </p>
-	 * 
+	 *
 	 * @return true if on a valid row, false if off the result set.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or result set type is
 	 *                TYPE_FORWAR_DONLY.
@@ -1018,18 +1160,18 @@
 
 	/**
 	 * Closes this ResultSet, releasing all resources.
-	 * 
+	 *
 	 * @param calledExplicitly
 	 *            was this called from close()?
-	 * 
+	 *
 	 * @throws SQLException
 	 *             if an error occurs.
 	 */
-	protected void realClose(boolean calledExplicitly) throws SQLException {
+	public void realClose(boolean calledExplicitly) throws SQLException {
 		if (this.isClosed) {
 			return;
 		}
-		
+
 		SQLException sqlEx = null;
 
 		if (this.useUsageAdvisor) {
@@ -1040,14 +1182,15 @@
 				String message = Messages.getString("UpdatableResultSet.34"); //$NON-NLS-1$
 
 				this.eventSink.consumeEvent(new ProfilerEvent(
-						ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$
+						ProfilerEvent.TYPE_WARN,
+						"", //$NON-NLS-1$
 						(this.owningStatement == null) ? "N/A" //$NON-NLS-1$
 								: this.owningStatement.currentCatalog, //$NON-NLS-1$
 						this.connectionId,
 						(this.owningStatement == null) ? (-1)
 								: this.owningStatement.getId(), this.resultId,
-						System.currentTimeMillis(), 0, null,
-						this.pointOfOrigin, message));
+						System.currentTimeMillis(), 0, Constants.MILLIS_I18N,
+						null, this.pointOfOrigin, message));
 			}
 		}
 
@@ -1103,7 +1246,7 @@
 	 * calling updateXXX(), but before calling updateRow() then the updates made
 	 * to the row are lost. Calling refreshRow() frequently will likely slow
 	 * performance.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or if called when on
 	 *                the insert row.
@@ -1127,12 +1270,17 @@
 			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11")); //$NON-NLS-1$
 		}
 
+		refreshRow(this.updater, this.thisRow);
+	}
+
+	private synchronized void refreshRow(PreparedStatement updateInsertStmt,
+			ResultSetRow rowToRefresh) throws SQLException {
 		if (this.refresher == null) {
 			if (this.refreshSQL == null) {
 				generateStatements();
 			}
 
-			this.refresher = this.connection
+			this.refresher = (PreparedStatement) this.connection
 					.clientPrepareStatement(this.refreshSQL);
 		}
 
@@ -1144,14 +1292,14 @@
 			byte[] dataFrom = null;
 			int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue();
 
-			if (!this.doingUpdates) {
-				dataFrom = (byte[]) this.thisRow[index];
+			if (!this.doingUpdates && !this.onInsertRow) {
+				dataFrom = (byte[]) rowToRefresh.getColumnValue(index);
 			} else {
-				dataFrom = this.updater.getBytesRepresentation(index);
+				dataFrom = updateInsertStmt.getBytesRepresentation(index);
 
 				// Primary keys not set?
-				if (this.updater.isNull(index) || (dataFrom.length == 0)) {
-					dataFrom = (byte[]) this.thisRow[index];
+				if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) {
+					dataFrom = (byte[]) rowToRefresh.getColumnValue(index);
 				} else {
 					dataFrom = stripBinaryPrefix(dataFrom);
 				}
@@ -1164,14 +1312,14 @@
 				int index = ((Integer) this.primaryKeyIndicies.get(i))
 						.intValue();
 
-				if (!this.doingUpdates) {
-					dataFrom = (byte[]) this.thisRow[index];
+				if (!this.doingUpdates && !this.onInsertRow) {
+					dataFrom = (byte[]) rowToRefresh.getColumnValue(index);
 				} else {
-					dataFrom = this.updater.getBytesRepresentation(index);
+					dataFrom = updateInsertStmt.getBytesRepresentation(index);
 
 					// Primary keys not set?
-					if (this.updater.isNull(index) || (dataFrom.length == 0)) {
-						dataFrom = (byte[]) this.thisRow[index];
+					if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) {
+						dataFrom = (byte[]) rowToRefresh.getColumnValue(index);
 					} else {
 						dataFrom = stripBinaryPrefix(dataFrom);
 					}
@@ -1193,9 +1341,9 @@
 					byte[] val = rs.getBytes(i + 1);
 
 					if ((val == null) || rs.wasNull()) {
-						this.thisRow[i] = null;
+						rowToRefresh.setColumnValue(i, null);
 					} else {
-						this.thisRow[i] = rs.getBytes(i + 1);
+						rowToRefresh.setColumnValue(i, rs.getBytes(i + 1));
 					}
 				}
 			} else {
@@ -1216,26 +1364,26 @@
 
 	/**
 	 * JDBC 2.0
-	 * 
+	 *
 	 * <p>
 	 * Moves a relative number of rows, either positive or negative. Attempting
 	 * to move beyond the first/last row in the result set positions the cursor
 	 * before/after the the first/last row. Calling relative(0) is valid, but
 	 * does not change the cursor position.
 	 * </p>
-	 * 
+	 *
 	 * <p>
 	 * Note: Calling relative(1) is different than calling next() since is makes
 	 * sense to call next() when there is no current row, for example, when the
 	 * cursor is positioned before the first row or after the last row of the
 	 * result set.
 	 * </p>
-	 * 
+	 *
 	 * @param rows
 	 *            DOCUMENT ME!
-	 * 
+	 *
 	 * @return true if on a row, false otherwise.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or there is no current
 	 *                row, or result set type is TYPE_FORWARD_ONLY.
@@ -1257,14 +1405,14 @@
 	 * a visible "hole" in a result set. This method can be used to detect holes
 	 * in a result set. The value returned depends on whether or not the result
 	 * set can detect deletions.
-	 * 
+	 *
 	 * @return true if deleted and deletes are detected
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 * @throws NotImplemented
 	 *             DOCUMENT ME!
-	 * 
+	 *
 	 * @see DatabaseMetaData#deletesAreDetected
 	 */
 	public synchronized boolean rowDeleted() throws SQLException {
@@ -1275,14 +1423,14 @@
 	 * JDBC 2.0 Determine if the current row has been inserted. The value
 	 * returned depends on whether or not the result set can detect visible
 	 * inserts.
-	 * 
+	 *
 	 * @return true if inserted and inserts are detected
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 * @throws NotImplemented
 	 *             DOCUMENT ME!
-	 * 
+	 *
 	 * @see DatabaseMetaData#insertsAreDetected
 	 */
 	public synchronized boolean rowInserted() throws SQLException {
@@ -1292,15 +1440,15 @@
 	/**
 	 * JDBC 2.0 Determine if the current row has been updated. The value
 	 * returned depends on whether or not the result set can detect updates.
-	 * 
+	 *
 	 * @return true if the row has been visibly updated by the owner or another,
 	 *         and updates are detected
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 * @throws NotImplemented
 	 *             DOCUMENT ME!
-	 * 
+	 *
 	 * @see DatabaseMetaData#updatesAreDetected
 	 */
 	public synchronized boolean rowUpdated() throws SQLException {
@@ -1309,7 +1457,7 @@
 
 	/**
 	 * Sets the concurrency type of this result set
-	 * 
+	 *
 	 * @param concurrencyFlag
 	 *            the type of concurrency that this ResultSet should support.
 	 */
@@ -1334,17 +1482,17 @@
 	/**
 	 * Reset UPDATE prepared statement to value in current row. This_Row MUST
 	 * point to current, valid row.
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
-	synchronized void syncUpdate() throws SQLException {
+	protected synchronized void syncUpdate() throws SQLException {
 		if (this.updater == null) {
 			if (this.updateSQL == null) {
 				generateStatements();
 			}
 
-			this.updater = this.connection
+			this.updater = (PreparedStatement) this.connection
 					.clientPrepareStatement(this.updateSQL);
 		}
 
@@ -1352,8 +1500,8 @@
 		this.updater.clearParameters();
 
 		for (int i = 0; i < numFields; i++) {
-			if (this.thisRow[i] != null) {
-				this.updater.setBytes(i + 1, (byte[]) this.thisRow[i],
+			if (this.thisRow.getColumnValue(i) != null) {
+				this.updater.setBytes(i + 1, (byte[]) this.thisRow.getColumnValue(i),
 						this.fields[i].isBinary(), false);
 			} else {
 				this.updater.setNull(i + 1, 0);
@@ -1364,12 +1512,12 @@
 
 		if (numKeys == 1) {
 			int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue();
-			byte[] keyData = (byte[]) this.thisRow[index];
+			byte[] keyData = (byte[]) this.thisRow.getColumnValue(index);
 			this.updater.setBytes(numFields + 1, keyData, false, false);
 		} else {
 			for (int i = 0; i < numKeys; i++) {
-				byte[] currentVal = (byte[]) this.thisRow[((Integer) this.primaryKeyIndicies
-						.get(i)).intValue()];
+				byte[] currentVal = (byte[]) this.thisRow.getColumnValue(((Integer) this.primaryKeyIndicies
+						.get(i)).intValue());
 
 				if (currentVal != null) {
 					this.updater.setBytes(numFields + i + 1, currentVal, false,
@@ -1387,14 +1535,14 @@
 	 * insert row. The updateXXX() methods do not update the underlying
 	 * database, instead the updateRow() or insertRow() methods are called to
 	 * update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
 	 * @param length
 	 *            the length of the stream
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1409,7 +1557,7 @@
 			this.updater.setAsciiStream(columnIndex, x, length);
 		} else {
 			this.inserter.setAsciiStream(columnIndex, x, length);
-			this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+			this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER);
 		}
 	}
 
@@ -1419,14 +1567,14 @@
 	 * insert row. The updateXXX() methods do not update the underlying
 	 * database, instead the updateRow() or insertRow() methods are called to
 	 * update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
 	 * @param length
 	 *            of the stream
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1440,12 +1588,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1462,9 +1610,9 @@
 			this.inserter.setBigDecimal(columnIndex, x);
 
 			if (x == null) {
-				this.thisRow[columnIndex - 1] = null;
+				this.thisRow.setColumnValue(columnIndex - 1, null);
 			} else {
-				this.thisRow[columnIndex - 1] = x.toString().getBytes();
+				this.thisRow.setColumnValue(columnIndex - 1, x.toString().getBytes());
 			}
 		}
 	}
@@ -1474,12 +1622,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1494,14 +1642,14 @@
 	 * insert row. The updateXXX() methods do not update the underlying
 	 * database, instead the updateRow() or insertRow() methods are called to
 	 * update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
 	 * @param length
 	 *            the length of the stream
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1518,9 +1666,9 @@
 			this.inserter.setBinaryStream(columnIndex, x, length);
 
 			if (x == null) {
-				this.thisRow[columnIndex - 1] = null;
+				this.thisRow.setColumnValue(columnIndex - 1, null);
 			} else {
-				this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+				this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER);
 			}
 		}
 	}
@@ -1531,14 +1679,14 @@
 	 * insert row. The updateXXX() methods do not update the underlying
 	 * database, instead the updateRow() or insertRow() methods are called to
 	 * update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
 	 * @param length
 	 *            of the stream
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1548,7 +1696,7 @@
 	}
 
 	/**
-	 * @see ResultSet#updateBlob(int, Blob)
+	 * @see ResultSetInternalMethods#updateBlob(int, Blob)
 	 */
 	public synchronized void updateBlob(int columnIndex, java.sql.Blob blob)
 			throws SQLException {
@@ -1563,15 +1711,15 @@
 			this.inserter.setBlob(columnIndex, blob);
 
 			if (blob == null) {
-				this.thisRow[columnIndex - 1] = null;
+				this.thisRow.setColumnValue(columnIndex - 1, null);
 			} else {
-				this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+				this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER);
 			}
 		}
 	}
 
 	/**
-	 * @see ResultSet#updateBlob(String, Blob)
+	 * @see ResultSetInternalMethods#updateBlob(String, Blob)
 	 */
 	public synchronized void updateBlob(String columnName, java.sql.Blob blob)
 			throws SQLException {
@@ -1583,12 +1731,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1604,8 +1752,8 @@
 		} else {
 			this.inserter.setBoolean(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -1614,12 +1762,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1633,12 +1781,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1654,8 +1802,8 @@
 		} else {
 			this.inserter.setByte(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -1664,12 +1812,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1683,12 +1831,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1704,7 +1852,7 @@
 		} else {
 			this.inserter.setBytes(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = x;
+			this.thisRow.setColumnValue(columnIndex - 1, x);
 		}
 	}
 
@@ -1713,12 +1861,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1733,14 +1881,14 @@
 	 * insert row. The updateXXX() methods do not update the underlying
 	 * database, instead the updateRow() or insertRow() methods are called to
 	 * update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
 	 * @param length
 	 *            the length of the stream
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1757,9 +1905,9 @@
 			this.inserter.setCharacterStream(columnIndex, x, length);
 
 			if (x == null) {
-				this.thisRow[columnIndex - 1] = null;
+				this.thisRow.setColumnValue(columnIndex - 1, null);
 			} else {
-				this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+				this.thisRow.setColumnValue(columnIndex - 1, STREAM_DATA_MARKER);
 			}
 		}
 	}
@@ -1770,14 +1918,14 @@
 	 * insert row. The updateXXX() methods do not update the underlying
 	 * database, instead the updateRow() or insertRow() methods are called to
 	 * update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param reader
 	 *            the new column value
 	 * @param length
 	 *            of the stream
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1787,7 +1935,7 @@
 	}
 
 	/**
-	 * @see ResultSet#updateClob(int, Clob)
+	 * @see ResultSetInternalMethods#updateClob(int, Clob)
 	 */
 	public void updateClob(int columnIndex, java.sql.Clob clob)
 			throws SQLException {
@@ -1804,12 +1952,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1825,8 +1973,8 @@
 		} else {
 			this.inserter.setDate(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -1835,12 +1983,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1854,12 +2002,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1875,8 +2023,8 @@
 		} else {
 			this.inserter.setDouble(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -1885,12 +2033,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1904,12 +2052,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1925,8 +2073,8 @@
 		} else {
 			this.inserter.setFloat(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -1935,12 +2083,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1954,12 +2102,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -1975,8 +2123,8 @@
 		} else {
 			this.inserter.setInt(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -1985,12 +2133,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2004,12 +2152,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2025,8 +2173,8 @@
 		} else {
 			this.inserter.setLong(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -2035,12 +2183,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2054,10 +2202,10 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2072,7 +2220,7 @@
 		} else {
 			this.inserter.setNull(columnIndex, 0);
 
-			this.thisRow[columnIndex - 1] = null;
+			this.thisRow.setColumnValue(columnIndex - 1, null);
 		}
 	}
 
@@ -2081,10 +2229,10 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2097,12 +2245,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2118,8 +2266,8 @@
 		} else {
 			this.inserter.setObject(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -2128,7 +2276,7 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
@@ -2137,7 +2285,7 @@
 	 *            For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
 	 *            this is the number of digits after the decimal. For all other
 	 *            types this value will be ignored.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2153,8 +2301,8 @@
 		} else {
 			this.inserter.setObject(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -2163,12 +2311,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2182,7 +2330,7 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
@@ -2191,7 +2339,7 @@
 	 *            For java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types
 	 *            this is the number of digits after the decimal. For all other
 	 *            types this value will be ignored.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2203,7 +2351,7 @@
 	/**
 	 * JDBC 2.0 Update the underlying database with the new contents of the
 	 * current row. Cannot be called when on the insert row.
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs, or if called when on
 	 *                the insert row
@@ -2212,7 +2360,7 @@
 	 */
 	public synchronized void updateRow() throws SQLException {
 		if (!this.isUpdatable) {
-			throw new NotUpdatable();
+			throw new NotUpdatable(this.notUpdatableReason);
 		}
 
 		if (this.doingUpdates) {
@@ -2232,12 +2380,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2253,8 +2401,8 @@
 		} else {
 			this.inserter.setShort(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -2263,12 +2411,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2282,19 +2430,19 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
 	public synchronized void updateString(int columnIndex, String x)
 			throws SQLException {
 		checkClosed();
-		
+
 		if (!this.onInsertRow) {
 			if (!this.doingUpdates) {
 				this.doingUpdates = true;
@@ -2306,15 +2454,15 @@
 			this.inserter.setString(columnIndex, x);
 
 			if (x == null) {
-				this.thisRow[columnIndex - 1] = null;
+				this.thisRow.setColumnValue(columnIndex - 1, null);
 			} else {
 				if (getCharConverter() != null) {
-					this.thisRow[columnIndex - 1] = StringUtils.getBytes(x,
+					this.thisRow.setColumnValue(columnIndex - 1, StringUtils.getBytes(x,
 							this.charConverter, this.charEncoding,
 							this.connection.getServerCharacterEncoding(),
-							this.connection.parserKnowsUnicode());
+							this.connection.parserKnowsUnicode()));
 				} else {
-					this.thisRow[columnIndex - 1] = x.getBytes();
+					this.thisRow.setColumnValue(columnIndex - 1, x.getBytes());
 				}
 			}
 		}
@@ -2325,12 +2473,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2344,12 +2492,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2365,8 +2513,8 @@
 		} else {
 			this.inserter.setTime(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -2375,12 +2523,12 @@
 	 * used to update column values in the current row, or the insert row. The
 	 * updateXXX() methods do not update the underlying database, instead the
 	 * updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2394,12 +2542,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnIndex
 	 *            the first column is 1, the second is 2, ...
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */
@@ -2415,8 +2563,8 @@
 		} else {
 			this.inserter.setTimestamp(columnIndex, x);
 
-			this.thisRow[columnIndex - 1] = this.inserter
-					.getBytesRepresentation(columnIndex - 1);
+			this.thisRow.setColumnValue(columnIndex - 1, this.inserter
+					.getBytesRepresentation(columnIndex - 1));
 		}
 	}
 
@@ -2425,12 +2573,12 @@
 	 * are used to update column values in the current row, or the insert row.
 	 * The updateXXX() methods do not update the underlying database, instead
 	 * the updateRow() or insertRow() methods are called to update the database.
-	 * 
+	 *
 	 * @param columnName
 	 *            the name of the column
 	 * @param x
 	 *            the new column value
-	 * 
+	 *
 	 * @exception SQLException
 	 *                if a database-access error occurs
 	 */

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/Util.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/Util.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/Util.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -27,6 +27,19 @@
 import java.io.ObjectInputStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TimeZone;
 
 /**
  * Various utility methods for the driver.
@@ -34,9 +47,33 @@
  * @author Mark Matthews
  */
 public class Util {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
+	protected static Method systemNanoTimeMethod;
 
+	static {
+		try {
+			systemNanoTimeMethod = System.class.getMethod("nanoTime", null);
+		} catch (SecurityException e) {
+			systemNanoTimeMethod = null;
+		} catch (NoSuchMethodException e) {
+			systemNanoTimeMethod = null;
+		}
+	}
+
+	protected static boolean nanoTimeAvailable() {
+		return systemNanoTimeMethod != null;
+	}
+
+	private static Method CAST_METHOD;
+
+	// cache this ourselves, as the method call is statically-synchronized in
+	// all but JDK6!
+
+	private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault();
+
+	static final TimeZone getDefaultTimeZone() {
+		return (TimeZone) DEFAULT_TIMEZONE.clone();
+	}
+
 	class RandStructcture {
 		long maxValue;
 
@@ -47,10 +84,53 @@
 		long seed2;
 	}
 
+	private static Util enclosingInstance = new Util();
+
+	private static boolean isJdbc4 = false;
+	
+	private static boolean isColdFusion = false;
+
+	static {
+		try {
+			CAST_METHOD = Class.class.getMethod("cast",
+					new Class[] { Object.class });
+		} catch (Throwable t) {
+			// ignore - not available in this VM
+		}
+
+		try {
+			Class.forName("java.sql.NClob");
+			isJdbc4 = true;
+		} catch (Throwable t) {
+			isJdbc4 = false;
+		}
+		
+		//
+		// Detect the ColdFusion MX environment
+		// 
+		// Unfortunately, no easy-to-discern classes are available
+		// to our classloader to check...
+		//
+		
+		String loadedFrom = stackTraceToString(new Throwable());
+		
+		if (loadedFrom != null) {
+			isColdFusion = loadedFrom.indexOf("coldfusion") != -1;
+		} else {
+			isColdFusion = false;
+		}
+	}
+
 	// ~ Methods
 	// ----------------------------------------------------------------
 
-	private static Util enclosingInstance = new Util();
+	public static boolean isJdbc4() {
+		return isJdbc4;
+	}
+	
+	public static boolean isColdFusion() {
+		return isColdFusion;
+	}
 
 	// Right from Monty's code
 	static String newCrypt(String password, String seed) {
@@ -293,4 +373,221 @@
 
 		return traceBuf.toString();
 	}
-}
+
+	public static Object getInstance(String className, Class[] argTypes,
+			Object[] args) throws SQLException {
+
+		try {
+			return handleNewInstance(Class.forName(className).getConstructor(
+					argTypes), args);
+		} catch (SecurityException e) {
+			throw SQLError.createSQLException(
+					"Can't instantiate required class",
+					SQLError.SQL_STATE_GENERAL_ERROR, e);
+		} catch (NoSuchMethodException e) {
+			throw SQLError.createSQLException(
+					"Can't instantiate required class",
+					SQLError.SQL_STATE_GENERAL_ERROR, e);
+		} catch (ClassNotFoundException e) {
+			throw SQLError.createSQLException(
+					"Can't instantiate required class",
+					SQLError.SQL_STATE_GENERAL_ERROR, e);
+		}
+	}
+
+	/**
+	 * Handles constructing new instance with the given constructor and wrapping
+	 * (or not, as required) the exceptions that could possibly be generated
+	 */
+	public static final Object handleNewInstance(Constructor ctor, Object[] args)
+			throws SQLException {
+		try {
+
+			return ctor.newInstance(args);
+		} catch (IllegalArgumentException e) {
+			throw SQLError.createSQLException(
+					"Can't instantiate required class",
+					SQLError.SQL_STATE_GENERAL_ERROR, e);
+		} catch (InstantiationException e) {
+			throw SQLError.createSQLException(
+					"Can't instantiate required class",
+					SQLError.SQL_STATE_GENERAL_ERROR, e);
+		} catch (IllegalAccessException e) {
+			throw SQLError.createSQLException(
+					"Can't instantiate required class",
+					SQLError.SQL_STATE_GENERAL_ERROR, e);
+		} catch (InvocationTargetException e) {
+			Throwable target = e.getTargetException();
+
+			if (target instanceof SQLException) {
+				throw (SQLException) target;
+			}
+
+			if (target instanceof ExceptionInInitializerError) {
+				target = ((ExceptionInInitializerError) target).getException();
+			}
+
+			throw SQLError.createSQLException(target.toString(),
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	/**
+	 * Does a network interface exist locally with the given hostname?
+	 * 
+	 * @param hostname
+	 *            the hostname (or IP address in string form) to check
+	 * @return true if it exists, false if no, or unable to determine due to VM
+	 *         version support of java.net.NetworkInterface
+	 */
+	public static boolean interfaceExists(String hostname) {
+		try {
+			Class networkInterfaceClass = Class
+					.forName("java.net.NetworkInterface");
+			return networkInterfaceClass.getMethod("getByName", null).invoke(
+					networkInterfaceClass, new Object[] { hostname }) != null;
+		} catch (Throwable t) {
+			return false;
+		}
+	}
+
+	/**
+	 * Reflexive access on JDK-1.5's Class.cast() method so we don't have to
+	 * move that out into separate classes built for JDBC-4.0.
+	 * 
+	 * @param invokeOn
+	 * @param toCast
+	 * @return
+	 */
+	public static Object cast(Object invokeOn, Object toCast) {
+		if (CAST_METHOD != null) {
+			try {
+				return CAST_METHOD.invoke(invokeOn, new Object[] { toCast });
+			} catch (Throwable t) {
+				return null;
+			}
+		}
+
+		return null;
+	}
+
+	public static long getCurrentTimeNanosOrMillis() {
+		if (systemNanoTimeMethod != null) {
+			try {
+				return ((Long) systemNanoTimeMethod.invoke(null, null))
+						.longValue();
+			} catch (IllegalArgumentException e) {
+				// ignore - fall through to currentTimeMillis()
+			} catch (IllegalAccessException e) {
+				// ignore - fall through to currentTimeMillis()
+			} catch (InvocationTargetException e) {
+				// ignore - fall through to currentTimeMillis()
+			}
+		}
+
+		return System.currentTimeMillis();
+	}
+
+	public static void resultSetToMap(Map mappedValues, java.sql.ResultSet rs)
+			throws SQLException {
+		while (rs.next()) {
+			mappedValues.put(rs.getObject(1), rs.getObject(2));
+		}
+	}
+
+	public static Map calculateDifferences(Map map1, Map map2) {
+		Map diffMap = new HashMap();
+
+		Iterator map1Entries = map1.entrySet().iterator();
+
+		while (map1Entries.hasNext()) {
+			Map.Entry entry = (Map.Entry) map1Entries.next();
+			Object key = entry.getKey();
+
+			Number value1 = null;
+			Number value2 = null;
+
+			if (entry.getValue() instanceof Number) {
+
+				value1 = (Number) entry.getValue();
+				value2 = (Number) map2.get(key);
+			} else {
+				try {
+					value1 = new Double(entry.getValue().toString());
+					value2 = new Double(map2.get(key).toString());
+				} catch (NumberFormatException nfe) {
+					continue;
+				}
+			}
+
+			if (value1.equals(value2)) {
+				continue;
+			}
+			
+			if (value1 instanceof Byte) {
+				diffMap.put(key, new Byte(
+						(byte) (((Byte) value2).byteValue() - ((Byte) value1)
+								.byteValue())));
+			} else if (value1 instanceof Short) {
+				diffMap.put(key, new Short((short) (((Short) value2)
+						.shortValue() - ((Short) value1).shortValue())));
+			} else if (value1 instanceof Integer) {
+				diffMap.put(key, new Integer(
+						(((Integer) value2).intValue() - ((Integer) value1)
+								.intValue())));
+			} else if (value1 instanceof Long) {
+				diffMap.put(key, new Long(
+						(((Long) value2).longValue() - ((Long) value1)
+								.longValue())));
+			} else if (value1 instanceof Float) {
+				diffMap.put(key, new Float(((Float) value2).floatValue()
+						- ((Float) value1).floatValue()));
+			} else if (value1 instanceof Double) {
+				diffMap.put(key, new Double(
+						(((Double) value2).shortValue() - ((Double) value1)
+								.shortValue())));
+			} else if (value1 instanceof BigDecimal) {
+				diffMap.put(key, ((BigDecimal) value2)
+						.subtract((BigDecimal) value1));
+			} else if (value1 instanceof BigInteger) {
+				diffMap.put(key, ((BigInteger) value2)
+						.subtract((BigInteger) value1));
+			}
+		}
+
+		return diffMap;
+	}
+	
+	public static List loadExtensions(Connection conn,
+			Properties props, String extensionClassNames,
+			String errorMessageKey) throws SQLException {
+		List extensionList = new LinkedList();
+
+		List interceptorsToCreate = StringUtils.split(extensionClassNames, ",",
+				true);
+
+		Iterator iter = interceptorsToCreate.iterator();
+
+		String className = null;
+
+		try {
+			while (iter.hasNext()) {
+				className = iter.next().toString();
+				Extension extensionInstance = (Extension) Class.forName(
+						className).newInstance();
+				extensionInstance.init(conn, props);
+
+				extensionList.add(extensionInstance);
+			}
+		} catch (Throwable t) {
+			SQLException sqlEx = SQLError.createSQLException(Messages
+					.getString(errorMessageKey, new Object[] { className }));
+			sqlEx.initCause(t);
+
+			throw sqlEx;
+		}
+
+		return extensionList;
+	}
+
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/configs/3-0-Compat.properties
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/configs/3-0-Compat.properties	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/configs/3-0-Compat.properties	2007-11-30 10:46:51 UTC (rev 4921)
@@ -13,4 +13,7 @@
 zeroDateTimeBehavior=convertToNull
 useServerPrepStmts=false
 autoClosePStmtStreams=true
-processEscapeCodesForPrepStmts=false
\ No newline at end of file
+processEscapeCodesForPrepStmts=false
+useFastDateParsing=false
+populateInsertRowWithDefaultValues=false
+useDirectRowUnpack=false
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/configs/5-0-Compat.properties
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/configs/5-0-Compat.properties	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/configs/5-0-Compat.properties	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,6 @@
+#
+# Settings to maintain Connector/J 5.0.x compatibility
+# (as much as it can be)
+#
+
+useDirectRowUnpack=false
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/configs/coldFusion.properties
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/configs/coldFusion.properties	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/configs/coldFusion.properties	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,25 @@
+#
+# Properties for optimal usage in ColdFusion
+#
+# Automagically pulled in if "autoConfigureForColdFusion" is "true"
+# which is the default configuration of the driver
+#
+
+#
+# CF uses a _lot_ of RSMD.isCaseSensitive() - this optimizes it
+#
+
+useDynamicCharsetInfo=false
+
+#
+# CF's pool tends to be "chatty" like DBCP
+#
+
+alwaysSendSetIsolation=false
+useLocalSessionState=true
+
+#
+# CF's pool seems to loose connectivity on page restart
+#
+
+autoReconnect=true

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/configs/maxPerformance.properties
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/configs/maxPerformance.properties	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/configs/maxPerformance.properties	2007-11-30 10:46:51 UTC (rev 4921)
@@ -22,4 +22,8 @@
 
 useLocalSessionState=true
 elideSetAutoCommits=true
-alwaysSendSetIsolation=false
\ No newline at end of file
+alwaysSendSetIsolation=false
+
+# Can cause high-GC pressure if timeouts are used on every
+# query
+enableQueryTimeouts=false
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/MySQLStatementCancelledException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions;
+
+public class MySQLStatementCancelledException extends MySQLNonTransientException {
+	public MySQLStatementCancelledException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLStatementCancelledException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLStatementCancelledException(String reason) {
+		super(reason);
+	}
+
+	public MySQLStatementCancelledException() {
+		super("Statement cancelled due to client request");
+	}
+
+	public int getErrorCode() {
+		// TODO Auto-generated method stub
+		return super.getErrorCode();
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/NotYetImplementedException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/NotYetImplementedException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/NotYetImplementedException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,18 @@
+package com.mysql.jdbc.exceptions;
+
+public class NotYetImplementedException extends RuntimeException {
+
+	public NotYetImplementedException() {
+		// TODO Auto-generated constructor stub
+	}
+
+	public NotYetImplementedException(String reason) {
+		super(reason);
+		// TODO Auto-generated constructor stub
+	}
+
+	public NotYetImplementedException(Throwable cause) {
+		super(cause);
+		// TODO Auto-generated constructor stub
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/CommunicationsException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.net.BindException;
+
+import java.sql.SQLRecoverableException;
+
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.StreamingNotifiable;
+
+/**
+ * An exception to represent communications errors with the database.
+ * 
+ * Attempts to provide 'friendler' error messages to end-users, including last
+ * time a packet was sent to the database, what the client-timeout is set to,
+ * and whether the idle time has been exceeded.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: CommunicationsException.java,v 1.1.2.1 2005/05/13 18:58:37
+ *          mmatthews Exp $
+ */
+public class CommunicationsException extends SQLRecoverableException implements StreamingNotifiable {
+
+	private String exceptionMessage;
+
+	private boolean streamingResultSetInPlay = false;
+
+	public CommunicationsException(ConnectionImpl conn, long lastPacketSentTimeMs,
+			Exception underlyingException) {
+
+		this.exceptionMessage = SQLError.createLinkFailureMessageBasedOnHeuristics(conn,
+				lastPacketSentTimeMs, underlyingException, this.streamingResultSetInPlay);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Throwable#getMessage()
+	 */
+	public String getMessage() {
+		return this.exceptionMessage;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.SQLException#getSQLState()
+	 */
+	public String getSQLState() {
+		return SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE;
+	}
+
+	public void setWasStreamingResults() {
+		this.streamingResultSetInPlay = true;
+	}
+
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLDataException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLDataException;
+
+public class MySQLDataException extends SQLDataException {
+
+	public MySQLDataException() {
+		super();
+	}
+
+	public MySQLDataException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLDataException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLDataException(String reason) {
+		super(reason);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLIntegrityConstraintViolationException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLIntegrityConstraintViolationException;
+
+public class MySQLIntegrityConstraintViolationException extends
+		SQLIntegrityConstraintViolationException {
+
+	public MySQLIntegrityConstraintViolationException() {
+		super();
+	}
+
+	public MySQLIntegrityConstraintViolationException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLIntegrityConstraintViolationException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLIntegrityConstraintViolationException(String reason) {
+		super(reason);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLInvalidAuthorizationSpecException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLInvalidAuthorizationSpecException;
+
+public class MySQLInvalidAuthorizationSpecException extends
+		SQLInvalidAuthorizationSpecException {
+
+	public MySQLInvalidAuthorizationSpecException() {
+		super();
+	}
+
+	public MySQLInvalidAuthorizationSpecException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLInvalidAuthorizationSpecException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLInvalidAuthorizationSpecException(String reason) {
+		super(reason);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientConnectionException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLNonTransientConnectionException;
+
+public class MySQLNonTransientConnectionException extends
+		SQLNonTransientConnectionException {
+
+	public MySQLNonTransientConnectionException() {
+		super();
+	}
+
+	public MySQLNonTransientConnectionException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLNonTransientConnectionException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLNonTransientConnectionException(String reason) {
+		super(reason);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLNonTransientException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLException;
+import java.sql.SQLNonTransientException;
+
+public class MySQLNonTransientException extends SQLNonTransientException {
+
+	public MySQLNonTransientException() {
+		super();
+	}
+
+	public MySQLNonTransientException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLNonTransientException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLNonTransientException(String reason) {
+		super(reason);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLSyntaxErrorException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLSyntaxErrorException;
+
+public class MySQLSyntaxErrorException extends SQLSyntaxErrorException {
+
+	public MySQLSyntaxErrorException() {
+		super();
+	}
+
+	public MySQLSyntaxErrorException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLSyntaxErrorException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLSyntaxErrorException(String reason) {
+		super(reason);
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTimeoutException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLTimeoutException;
+
+public class MySQLTimeoutException extends SQLTimeoutException {
+
+	public MySQLTimeoutException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLTimeoutException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLTimeoutException(String reason) {
+		super(reason);
+	}
+
+	public MySQLTimeoutException() {
+		super("Statement cancelled due to timeout or client request");
+	}
+
+	public int getErrorCode() {
+		// TODO Auto-generated method stub
+		return super.getErrorCode();
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransactionRollbackException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLTransactionRollbackException;
+
+public class MySQLTransactionRollbackException extends SQLTransactionRollbackException {
+
+	public MySQLTransactionRollbackException(String reason, String SQLState,
+			int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLTransactionRollbackException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLTransactionRollbackException(String reason) {
+		super(reason);
+	}
+
+	public MySQLTransactionRollbackException() {
+		super();
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientConnectionException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLTransientConnectionException;
+
+public class MySQLTransientConnectionException extends SQLTransientConnectionException {
+
+	public MySQLTransientConnectionException(String reason, String SQLState,
+			int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLTransientConnectionException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLTransientConnectionException(String reason) {
+		super(reason);
+	}
+
+	public MySQLTransientConnectionException() {
+		super();
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/exceptions/jdbc4/MySQLTransientException.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2005 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+*/
+
+package com.mysql.jdbc.exceptions.jdbc4;
+
+import java.sql.SQLException;
+import java.sql.SQLTransientException;
+
+public class MySQLTransientException extends SQLTransientException {
+
+	public MySQLTransientException(String reason, String SQLState, int vendorCode) {
+		super(reason, SQLState, vendorCode);
+	}
+
+	public MySQLTransientException(String reason, String SQLState) {
+		super(reason, SQLState);
+	}
+
+	public MySQLTransientException(String reason) {
+		super(reason);
+	}
+
+	public MySQLTransientException() {
+		super();
+	}
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -65,12 +65,18 @@
 	 * @see com.mchange.v2.c3p0.ConnectionTester#activeCheckConnection(java.sql.Connection)
 	 */
 	public int activeCheckConnection(Connection con) {
-		C3P0ProxyConnection castCon = (C3P0ProxyConnection) con;
-
 		try {
 			if (pingMethod != null) {
-				castCon.rawConnectionOperation(pingMethod,
-						C3P0ProxyConnection.RAW_CONNECTION, NO_ARGS_ARRAY);
+				if (con instanceof com.mysql.jdbc.Connection) {
+					// We've been passed an instance of a MySQL connection --
+					// no need for reflection
+					((com.mysql.jdbc.Connection) con).ping();
+				} else {
+					// Assume the connection is a C3P0 proxy
+					C3P0ProxyConnection castCon = (C3P0ProxyConnection) con;
+					castCon.rawConnectionOperation(pingMethod,
+							C3P0ProxyConnection.RAW_CONNECTION, NO_ARGS_ARRAY);
+				}
 			} else {
 				Statement pingStatement = null;
 
@@ -97,7 +103,9 @@
 	 *      java.lang.Throwable)
 	 */
 	public int statusOnException(Connection arg0, Throwable throwable) {
-		if (throwable instanceof CommunicationsException) {
+		if (throwable instanceof CommunicationsException
+				|| "com.mysql.jdbc.exceptions.jdbc4.CommunicationsException"
+						.equals(throwable.getClass().getName())) {
 			return CONNECTION_IS_INVALID;
 		}
 

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -77,7 +77,7 @@
 		if (conn instanceof com.mysql.jdbc.Connection) {
 			if (pingMethod != null) {
 				try {
-					this.pingMethod.invoke(conn, NO_ARGS_OBJECT_ARRAY);
+					this.pingMethod.invoke(conn, null);
 	
 					return null;
 				} catch (Exception ex) {
@@ -91,7 +91,7 @@
 		} else if (conn instanceof com.mysql.jdbc.jdbc2.optional.ConnectionWrapper) {
 			if (pingMethodWrapped != null) {
 				try {
-					this.pingMethodWrapped.invoke(conn, NO_ARGS_OBJECT_ARRAY);
+					this.pingMethodWrapped.invoke(conn, null);
 	
 					return null;
 				} catch (Exception ex) {
@@ -109,6 +109,8 @@
 		Statement pingStatement = null;
 
 		try {
+			pingStatement = conn.createStatement();
+			
 			pingStatement.executeQuery("SELECT 1").close();
 
 			return null;

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ResultSetScannerInterceptor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+
+package com.mysql.jdbc.interceptors;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.ResultSetInternalMethods;
+import com.mysql.jdbc.Statement;
+import com.mysql.jdbc.StatementInterceptor;
+
+public class ResultSetScannerInterceptor implements StatementInterceptor {
+	
+	private Pattern regexP;
+	
+	public void init(Connection conn, Properties props) throws SQLException {
+		String regexFromUser = props.getProperty("resultSetScannerRegex");
+		
+		if (regexFromUser == null || regexFromUser.length() == 0) {
+			throw new SQLException("resultSetScannerRegex must be configured, and must be > 0 characters");
+		}
+		
+		try {
+			this.regexP = Pattern.compile(regexFromUser);
+		} catch (Throwable t) {
+			SQLException sqlEx = new SQLException("Can't use configured regex due to underlying exception.");
+			sqlEx.initCause(t);
+			
+			throw sqlEx;
+		}
+		
+	}
+	
+	public ResultSetInternalMethods postProcess(String sql, Statement interceptedStatement,
+			ResultSetInternalMethods originalResultSet, Connection connection)
+			throws SQLException {
+		
+		// requirement of anonymous class
+		final ResultSetInternalMethods finalResultSet = originalResultSet;
+		
+		return (ResultSetInternalMethods)Proxy.newProxyInstance(originalResultSet.getClass().getClassLoader(),
+				new Class[] {ResultSetInternalMethods.class},
+				new InvocationHandler() {
+
+					public Object invoke(Object proxy, Method method,
+							Object[] args) throws Throwable {
+						
+						Object invocationResult = method.invoke(finalResultSet, args);
+						
+						String methodName = method.getName();
+						
+						if (invocationResult != null && invocationResult instanceof String 
+								|| "getString".equals(methodName) 
+								|| "getObject".equals(methodName)
+								|| "getObjectStoredProc".equals(methodName)) {
+							Matcher matcher = regexP.matcher(invocationResult.toString());
+							
+							if (matcher.matches()) {
+								throw new SQLException("value disallowed by filter");
+							}
+						}
+						
+						return invocationResult;
+					}});
+	
+	}
+
+	public ResultSetInternalMethods preProcess(String sql, Statement interceptedStatement,
+			Connection connection) throws SQLException {
+		// we don't care about this event
+		
+		return null;
+	}
+
+	// we don't issue queries, so it should be safe to intercept
+	// at any point
+	public boolean executeTopLevelOnly() {
+		return false;
+	}
+
+	public void destroy() {
+		// TODO Auto-generated method stub
+		
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/ServerStatusDiffInterceptor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+ */
+
+package com.mysql.jdbc.interceptors;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.ResultSetInternalMethods;
+import com.mysql.jdbc.Statement;
+import com.mysql.jdbc.StatementInterceptor;
+import com.mysql.jdbc.Util;
+
+public class ServerStatusDiffInterceptor implements StatementInterceptor {
+
+	private Map preExecuteValues = new HashMap();
+
+	private Map postExecuteValues = new HashMap();
+
+	public void init(Connection conn, Properties props) throws SQLException {
+
+	}
+
+	public ResultSetInternalMethods postProcess(String sql,
+			Statement interceptedStatement,
+			ResultSetInternalMethods originalResultSet, Connection connection)
+			throws SQLException {
+
+		if (connection.versionMeetsMinimum(5, 0, 2)) {
+			populateMapWithSessionStatusValues(connection, this.postExecuteValues);
+	
+			connection.getLog().logInfo(
+					"Server status change for statement:\n"
+							+ Util.calculateDifferences(this.preExecuteValues,
+									this.postExecuteValues));
+		}
+
+		return null; // we don't actually modify a result set
+
+	}
+
+	private void populateMapWithSessionStatusValues(Connection connection,
+			Map toPopulate) throws SQLException {
+		java.sql.Statement stmt = null;
+		java.sql.ResultSet rs = null;
+
+		try {
+			toPopulate.clear();
+
+			stmt = connection.createStatement();
+			rs = stmt.executeQuery("SHOW SESSION STATUS");
+			Util.resultSetToMap(toPopulate, rs);
+		} finally {
+			if (rs != null) {
+				rs.close();
+			}
+
+			if (stmt != null) {
+				stmt.close();
+			}
+		}
+	}
+
+	public ResultSetInternalMethods preProcess(String sql,
+			Statement interceptedStatement, Connection connection)
+			throws SQLException {
+
+		if (connection.versionMeetsMinimum(5, 0, 2)) {
+			populateMapWithSessionStatusValues(connection,
+					this.preExecuteValues);
+		}
+
+		return null; // we don't actually modify a result set
+	}
+
+	public boolean executeTopLevelOnly() {
+		return true;
+	}
+
+	public void destroy() {
+		// TODO Auto-generated method stub
+		
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/interceptors/SessionAssociationInterceptor.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,70 @@
+package com.mysql.jdbc.interceptors;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.ResultSetInternalMethods;
+import com.mysql.jdbc.Statement;
+import com.mysql.jdbc.StatementInterceptor;
+
+public class SessionAssociationInterceptor implements StatementInterceptor {
+
+	protected String currentSessionKey;
+	protected static ThreadLocal sessionLocal = new ThreadLocal();
+	
+	public static final void setSessionKey(String key) {
+		sessionLocal.set(key);
+	}
+	
+	public static final void resetSessionKey() {
+		sessionLocal.set(null);
+	}
+	
+	public static final String getSessionKey() {
+		return (String)sessionLocal.get();
+	}
+	
+	public boolean executeTopLevelOnly() {
+		return true;
+	}
+
+	public void init(Connection conn, Properties props) throws SQLException {
+		// TODO Auto-generated method stub
+
+	}
+
+	public ResultSetInternalMethods postProcess(String sql,
+			Statement interceptedStatement,
+			ResultSetInternalMethods originalResultSet, Connection connection)
+			throws SQLException {
+		return null;
+	}
+
+	public ResultSetInternalMethods preProcess(String sql,
+			Statement interceptedStatement, Connection connection)
+			throws SQLException {
+		String key = getSessionKey();
+		
+		if (key != null && !key.equals(this.currentSessionKey)) {
+			PreparedStatement pstmt = connection.clientPrepareStatement("SET @mysql_proxy_session=?");
+			
+			try {
+				pstmt.setString(1, key);
+				pstmt.execute();
+			} finally {
+				pstmt.close();
+			}
+			
+			this.currentSessionKey = key;
+		}
+		
+		return null;
+	}
+
+	public void destroy() {
+		// TODO Auto-generated method stub
+		
+	}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -26,6 +26,7 @@
 
 import java.io.InputStream;
 import java.io.Reader;
+import java.lang.reflect.Constructor;
 import java.math.BigDecimal;
 import java.net.URL;
 import java.sql.Array;
@@ -34,14 +35,19 @@
 import java.sql.Clob;
 import java.sql.Date;
 import java.sql.PreparedStatement;
+//import java.sql.NClob;
 import java.sql.Ref;
+//import java.sql.RowId;
 import java.sql.SQLException;
+//import java.sql.SQLXML;
 import java.sql.Time;
 import java.sql.Timestamp;
 import java.util.Calendar;
 import java.util.Map;
 
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.Util;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
 
 /**
  * Wraps callable statements created by pooled connections.
@@ -52,6 +58,42 @@
 public class CallableStatementWrapper extends PreparedStatementWrapper
 		implements CallableStatement {
 
+private static final Constructor JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR = Class.forName(
+						"com.mysql.jdbc.jdbc2.optional.JDBC4CallableStatementWrapper").getConstructor(
+						new Class[] { ConnectionWrapper.class, 
+								MysqlPooledConnection.class, 
+								CallableStatement.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR = null;
+		}
+	}
+	
+	protected static CallableStatementWrapper getInstance(ConnectionWrapper c, 
+			MysqlPooledConnection conn,
+			CallableStatement toWrap) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new CallableStatementWrapper(c, 
+					conn, toWrap);
+		}
+
+		return (CallableStatementWrapper) Util.handleNewInstance(
+				JDBC_4_CALLABLE_STATEMENT_WRAPPER_CTOR,
+				new Object[] {c, 
+						conn, toWrap });
+	}
+	
 	/**
 	 * @param c
 	 * @param conn
@@ -1761,5 +1803,839 @@
 
 		return null;
 	}
+//
+//	public Reader getCharacterStream(int parameterIndex) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getCharacterStream(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public Reader getCharacterStream(String parameterName) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getCharacterStream(parameterName);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public Reader getNCharacterStream(int parameterIndex) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getCharacterStream(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public Reader getNCharacterStream(String parameterName) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getNCharacterStream(parameterName);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public NClob getNClob(int parameterIndex) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getNClob(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public NClob getNClob(String parameterName) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getNClob(parameterName);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public String getNString(int parameterIndex) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getNString(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public String getNString(String parameterName) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getNString(parameterName);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public RowId getRowId(int parameterIndex) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getRowId(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public RowId getRowId(String parameterName) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getRowId(parameterName);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public SQLXML getSQLXML(int parameterIndex) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getSQLXML(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public SQLXML getSQLXML(String parameterName) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.getSQLXML(parameterName);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return null;
+//	}
+//
+//	public void setAsciiStream(String parameterName, InputStream x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setAsciiStream(parameterName, x) ;
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setAsciiStream(parameterName, x, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBinaryStream(String parameterName, InputStream x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBinaryStream(parameterName, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBinaryStream(parameterName, x, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(String parameterName, Blob x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBlob(parameterName, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(String parameterName, InputStream inputStream) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBlob(parameterName, inputStream);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBlob(parameterName, inputStream, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setCharacterStream(String parameterName, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setCharacterStream(parameterName, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setCharacterStream(parameterName, reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(String parameterName, Clob x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setClob(parameterName, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(String parameterName, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setClob(parameterName, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(String parameterName, Reader reader, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setClob(parameterName, reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNCharacterStream(String parameterName, Reader value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNCharacterStream(parameterName, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNCharacterStream(parameterName, value, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(String parameterName, NClob value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNClob(parameterName, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(String parameterName, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNClob(parameterName, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(String parameterName, Reader reader, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNClob(parameterName, reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNString(String parameterName, String value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNString(parameterName, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setRowId(String parameterName, RowId x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setRowId(parameterName, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setSQLXML(parameterName, xmlObject);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setAsciiStream(parameterIndex, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setAsciiStream(parameterIndex, x, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBinaryStream(parameterIndex, x) ;
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBinaryStream(parameterIndex, x, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBlob(parameterIndex, inputStream);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setBlob(parameterIndex, inputStream, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setCharacterStream(parameterIndex, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.getCharacterStream(parameterIndex);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(int parameterIndex, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setClob(parameterIndex, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setClob(parameterIndex, reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNCharacterStream(parameterIndex, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	
+//	}
+//
+//	public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNCharacterStream(parameterIndex, value, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(int parameterIndex, NClob value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNClob(parameterIndex, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(int parameterIndex, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNClob(parameterIndex, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNClob(parameterIndex, reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNString(int parameterIndex, String value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setNString(parameterIndex, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setRowId(parameterIndex, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setSQLXML(parameterIndex, xmlObject);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//	}
+//
+//	public boolean isClosed() throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						.isClosed();
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return true;
+//	}
+//
+//	public boolean isPoolable() throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((CallableStatement) this.wrappedStmt)
+//						. isPoolable();
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//		return false;
+//	}
+//
+//	public void setPoolable(boolean poolable) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((CallableStatement) this.wrappedStmt)
+//						.setPoolable(poolable);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//		
+//	}
+//
+//	public boolean isWrapperFor(Class arg0) throws SQLException {
+//		throw new NotYetImplementedException();
+//	}
+//
+//	public Object unwrap(Class arg0) throws SQLException {
+//		throw new NotYetImplementedException();
+//	}
 
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2006 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,13 +24,20 @@
  */
 package com.mysql.jdbc.jdbc2.optional;
 
+import java.lang.reflect.Constructor;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Savepoint;
 import java.sql.Statement;
+import java.util.TimeZone;
 
+import com.mysql.jdbc.ConnectionImpl;
 import com.mysql.jdbc.MysqlErrorNumbers;
+import com.mysql.jdbc.PreparedStatement;
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.ServerPreparedStatement;
+import com.mysql.jdbc.Util;
+import com.mysql.jdbc.log.Log;
 
 /**
  * This class serves as a wrapper for the org.gjt.mm.mysql.jdbc2.Connection
@@ -54,16 +61,54 @@
  * @see org.gjt.mm.mysql.jdbc2.Connection
  * @see org.gjt.mm.mysql.jdbc2.optional.MysqlPooledConnection
  */
-public class ConnectionWrapper extends WrapperBase implements Connection {
-	private com.mysql.jdbc.Connection mc = null;
+public class ConnectionWrapper extends WrapperBase implements Connection,
+		com.mysql.jdbc.Connection {
+	protected com.mysql.jdbc.ConnectionImpl mc = null;
 
 	private MysqlPooledConnection mpc = null;
 
 	private String invalidHandleStr = "Logical handle no longer valid";
 
 	private boolean closed;
+
 	private boolean isForXa;
-	
+
+	private static final Constructor JDBC_4_CONNECTION_WRAPPER_CTOR;
+
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_CONNECTION_WRAPPER_CTOR = Class.forName(
+						"com.mysql.jdbc.jdbc2.optional.JDBC4ConnectionWrapper")
+						.getConstructor(
+								new Class[] { MysqlPooledConnection.class,
+										ConnectionImpl.class, Boolean.TYPE });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_CONNECTION_WRAPPER_CTOR = null;
+		}
+	}
+
+	protected static ConnectionWrapper getInstance(
+			MysqlPooledConnection mysqlPooledConnection,
+			ConnectionImpl mysqlConnection, boolean forXa) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new ConnectionWrapper(mysqlPooledConnection,
+					mysqlConnection, forXa);
+		}
+
+		return (ConnectionWrapper) Util.handleNewInstance(
+				JDBC_4_CONNECTION_WRAPPER_CTOR, new Object[] {
+						mysqlPooledConnection, mysqlConnection,
+						Boolean.valueOf(forXa) });
+	}
+
 	/**
 	 * Construct a new LogicalHandle and set instance variables
 	 * 
@@ -76,17 +121,15 @@
 	 *             if an error occurs.
 	 */
 	public ConnectionWrapper(MysqlPooledConnection mysqlPooledConnection,
-			com.mysql.jdbc.Connection mysqlConnection,
-			boolean forXa) throws SQLException {
+			ConnectionImpl mysqlConnection, boolean forXa) throws SQLException {
 		this.mpc = mysqlPooledConnection;
 		this.mc = mysqlConnection;
 		this.closed = false;
 		this.pooledConnection = this.mpc;
 		this.isForXa = forXa;
-		
+
 		if (this.isForXa) {
 			setInGlobalTx(false);
-			setAutoCommit(false);
 		}
 	}
 
@@ -100,11 +143,12 @@
 		checkClosed();
 
 		if (autoCommit && isInGlobalTx()) {
-			throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection", 
-					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, 
+			throw SQLError.createSQLException(
+					"Can't set autocommit to 'true' on an XAConnection",
+					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
 					MysqlErrorNumbers.ER_XA_RMERR);
 		}
-		
+
 		try {
 			this.mc.setAutoCommit(autoCommit);
 		} catch (SQLException sqlException) {
@@ -177,7 +221,7 @@
 		return (this.closed || this.mc.isClosed());
 	}
 
-	public boolean isMasterConnection() throws SQLException {
+	public boolean isMasterConnection() {
 		return this.mc.isMasterConnection();
 	}
 
@@ -207,7 +251,7 @@
 		}
 
 		return Statement.CLOSE_CURRENT_RESULT; // we don't reach this code,
-												// compiler can't tell
+		// compiler can't tell
 	}
 
 	/**
@@ -281,11 +325,12 @@
 		checkClosed();
 
 		if (isInGlobalTx()) {
-			throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection", 
-					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, 
+			throw SQLError.createSQLException(
+					"Can't set autocommit to 'true' on an XAConnection",
+					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
 					MysqlErrorNumbers.ER_XA_RMERR);
 		}
-		
+
 		try {
 			return this.mc.setSavepoint();
 		} catch (SQLException sqlException) {
@@ -302,11 +347,12 @@
 		checkClosed();
 
 		if (isInGlobalTx()) {
-			throw SQLError.createSQLException("Can't set autocommit to 'true' on an XAConnection", 
-					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, 
+			throw SQLError.createSQLException(
+					"Can't set autocommit to 'true' on an XAConnection",
+					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
 					MysqlErrorNumbers.ER_XA_RMERR);
 		}
-		
+
 		try {
 			return this.mc.setSavepoint(arg0);
 		} catch (SQLException sqlException) {
@@ -348,7 +394,7 @@
 		}
 
 		return TRANSACTION_REPEATABLE_READ; // we don't reach this code,
-											// compiler can't tell
+		// compiler can't tell
 	}
 
 	/**
@@ -443,12 +489,13 @@
 	 */
 	public void commit() throws SQLException {
 		checkClosed();
-		
+
 		if (isInGlobalTx()) {
-			throw SQLError.createSQLException(
-					"Can't call commit() on an XAConnection associated with a global transaction",
-					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, 
-					MysqlErrorNumbers.ER_XA_RMERR);
+			throw SQLError
+					.createSQLException(
+							"Can't call commit() on an XAConnection associated with a global transaction",
+							SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+							MysqlErrorNumbers.ER_XA_RMERR);
 		}
 
 		try {
@@ -468,7 +515,7 @@
 		checkClosed();
 
 		try {
-			return new StatementWrapper(this, this.mpc, this.mc
+			return StatementWrapper.getInstance(this, this.mpc, this.mc
 					.createStatement());
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -488,7 +535,7 @@
 		checkClosed();
 
 		try {
-			return new StatementWrapper(this, this.mpc, this.mc
+			return StatementWrapper.getInstance(this, this.mpc, this.mc
 					.createStatement(resultSetType, resultSetConcurrency));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -505,7 +552,7 @@
 		checkClosed();
 
 		try {
-			return new StatementWrapper(this, this.mpc, this.mc
+			return StatementWrapper.getInstance(this, this.mpc, this.mc
 					.createStatement(arg0, arg1, arg2));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -543,7 +590,7 @@
 		checkClosed();
 
 		try {
-			return new CallableStatementWrapper(this, this.mpc, this.mc
+			return CallableStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareCall(sql));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -563,7 +610,7 @@
 		checkClosed();
 
 		try {
-			return new CallableStatementWrapper(this, this.mpc, this.mc
+			return CallableStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareCall(sql, resultSetType, resultSetConcurrency));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -580,7 +627,7 @@
 		checkClosed();
 
 		try {
-			return new CallableStatementWrapper(this, this.mpc, this.mc
+			return CallableStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareCall(arg0, arg1, arg2, arg3));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -589,36 +636,35 @@
 		return null; // we don't reach this code, compiler can't tell
 	}
 
-	public java.sql.PreparedStatement clientPrepare(String sql) throws SQLException
-	{
+	public java.sql.PreparedStatement clientPrepare(String sql)
+			throws SQLException {
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, 
-					this.mc.clientPrepareStatement(sql));
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.clientPrepareStatement(sql));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
 		}
-		
+
 		return null;
 	}
-	
+
 	public java.sql.PreparedStatement clientPrepare(String sql,
-			int resultSetType, int resultSetConcurrency) throws SQLException
-	{
+			int resultSetType, int resultSetConcurrency) throws SQLException {
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, 
-					this.mc.clientPrepareStatement(sql,
-							resultSetType, resultSetConcurrency));
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.clientPrepareStatement(sql, resultSetType,
+							resultSetConcurrency));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
 		}
-		
+
 		return null;
 	}
-	
+
 	/**
 	 * Passes call to method on physical connection instance. Notifies listeners
 	 * of any caught exceptions before re-throwing to client.
@@ -630,7 +676,7 @@
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, this.mc
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareStatement(sql));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -650,8 +696,9 @@
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, this.mc
-					.prepareStatement(sql, resultSetType, resultSetConcurrency));
+			return PreparedStatementWrapper
+					.getInstance(this, this.mpc, this.mc.prepareStatement(sql,
+							resultSetType, resultSetConcurrency));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
 		}
@@ -667,7 +714,7 @@
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, this.mc
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareStatement(arg0, arg1, arg2, arg3));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -684,7 +731,7 @@
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, this.mc
+			PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareStatement(arg0, arg1));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -701,7 +748,7 @@
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, this.mc
+			PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareStatement(arg0, arg1));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -718,7 +765,7 @@
 		checkClosed();
 
 		try {
-			return new PreparedStatementWrapper(this, this.mpc, this.mc
+			PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
 					.prepareStatement(arg0, arg1));
 		} catch (SQLException sqlException) {
 			checkAndFireConnectionError(sqlException);
@@ -749,13 +796,14 @@
 	public void rollback() throws SQLException {
 		checkClosed();
 
-
 		if (isInGlobalTx()) {
-			throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction",
-					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, 
-					MysqlErrorNumbers.ER_XA_RMERR);
+			throw SQLError
+					.createSQLException(
+							"Can't call rollback() on an XAConnection associated with a global transaction",
+							SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+							MysqlErrorNumbers.ER_XA_RMERR);
 		}
-		
+
 		try {
 			this.mc.rollback();
 		} catch (SQLException sqlException) {
@@ -770,11 +818,13 @@
 		checkClosed();
 
 		if (isInGlobalTx()) {
-			throw SQLError.createSQLException("Can't call rollback() on an XAConnection associated with a global transaction",
-					SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION, 
-					MysqlErrorNumbers.ER_XA_RMERR);
+			throw SQLError
+					.createSQLException(
+							"Can't call rollback() on an XAConnection associated with a global transaction",
+							SQLError.SQL_STATE_INVALID_TRANSACTION_TERMINATION,
+							MysqlErrorNumbers.ER_XA_RMERR);
 		}
-		
+
 		try {
 			this.mc.rollback(arg0);
 		} catch (SQLException sqlException) {
@@ -784,22 +834,21 @@
 
 	public boolean isSameResource(Connection c) {
 		if (c instanceof ConnectionWrapper) {
-			return this.mc.isSameResource(((ConnectionWrapper)c).mc);
+			return this.mc.isSameResource(((ConnectionWrapper) c).mc);
 		} else if (c instanceof com.mysql.jdbc.Connection) {
-			return this.mc.isSameResource((com.mysql.jdbc.Connection)c);
+			return this.mc.isSameResource((com.mysql.jdbc.Connection) c);
 		}
-		
+
 		return false;
 	}
-	
+
 	protected void close(boolean fireClosedEvent) throws SQLException {
 		synchronized (this.mpc) {
 			if (this.closed) {
 				return;
 			}
 
-			if (!isInGlobalTx() 
-					&& this.mc.getRollbackOnPooledClose()
+			if (!isInGlobalTx() && this.mc.getRollbackOnPooledClose()
 					&& !this.getAutoCommit()) {
 				rollback();
 			}
@@ -818,23 +867,1634 @@
 		}
 	}
 
-	private void checkClosed() throws SQLException {
+	protected void checkClosed() throws SQLException {
 		if (this.closed) {
 			throw SQLError.createSQLException(this.invalidHandleStr);
 		}
 	}
 
-	protected boolean isInGlobalTx() {
+	public boolean isInGlobalTx() {
 		return this.mc.isInGlobalTx();
 	}
 
 	protected void setInGlobalTx(boolean flag) {
 		this.mc.setInGlobalTx(flag);
 	}
-	
+
 	public void ping() throws SQLException {
 		if (this.mc != null) {
 			this.mc.ping();
 		}
 	}
+
+	public void changeUser(String userName, String newPassword)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.changeUser(userName, newPassword);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	public void clearHasTriedMaster() {
+		this.mc.clearHasTriedMaster();
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.clientPrepareStatement(sql));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.clientPrepareStatement(sql, autoGenKeyIndex));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.clientPrepareStatement(sql, resultSetType,
+							resultSetConcurrency));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.clientPrepareStatement(sql, resultSetType,
+							resultSetConcurrency, resultSetHoldability));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.clientPrepareStatement(sql, autoGenKeyIndexes));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement clientPrepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.clientPrepareStatement(sql, autoGenKeyColNames));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public int getActiveStatementCount() {
+		return this.mc.getActiveStatementCount();
+	}
+
+	public Log getLog() throws SQLException {
+		return this.mc.getLog();
+	}
+
+	public String getServerCharacterEncoding() {
+		return this.mc.getServerCharacterEncoding();
+	}
+
+	public TimeZone getServerTimezoneTZ() {
+		return this.mc.getServerTimezoneTZ();
+	}
+
+	public String getStatementComment() {
+		return this.mc.getStatementComment();
+	}
+
+	public boolean hasTriedMaster() {
+		return this.mc.hasTriedMaster();
+	}
+
+	public boolean isAbonormallyLongQuery(long millisOrNanos) {
+		return this.mc.isAbonormallyLongQuery(millisOrNanos);
+	}
+
+	public boolean isNoBackslashEscapesSet() {
+		return this.mc.isNoBackslashEscapesSet();
+	}
+
+	public boolean lowerCaseTableNames() {
+		return this.mc.lowerCaseTableNames();
+	}
+
+	public boolean parserKnowsUnicode() {
+		return this.mc.parserKnowsUnicode();
+	}
+
+	public void reportQueryTime(long millisOrNanos) {
+		this.mc.reportQueryTime(millisOrNanos);
+	}
+
+	public void resetServerState() throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.resetServerState();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	public java.sql.PreparedStatement serverPrepareStatement(String sql)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.serverPrepareStatement(sql));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int autoGenKeyIndex) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.serverPrepareStatement(sql, autoGenKeyIndex));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.serverPrepareStatement(sql, resultSetType,
+							resultSetConcurrency));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.serverPrepareStatement(sql, resultSetType,
+							resultSetConcurrency, resultSetHoldability));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			int[] autoGenKeyIndexes) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.serverPrepareStatement(sql, autoGenKeyIndexes));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public java.sql.PreparedStatement serverPrepareStatement(String sql,
+			String[] autoGenKeyColNames) throws SQLException {
+		try {
+			return PreparedStatementWrapper.getInstance(this, this.mpc, this.mc
+					.serverPrepareStatement(sql, autoGenKeyColNames));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public void setFailedOver(boolean flag) {
+		this.mc.setFailedOver(flag);
+
+	}
+
+	public void setPreferSlaveDuringFailover(boolean flag) {
+		this.mc.setPreferSlaveDuringFailover(flag);
+	}
+
+	public void setStatementComment(String comment) {
+		this.mc.setStatementComment(comment);
+
+	}
+
+	public void shutdownServer() throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.shutdownServer();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+	}
+
+	public boolean supportsIsolationLevel() {
+		return this.mc.supportsIsolationLevel();
+	}
+
+	public boolean supportsQuotedIdentifiers() {
+		return this.mc.supportsQuotedIdentifiers();
+	}
+
+	public boolean supportsTransactions() {
+		return this.mc.supportsTransactions();
+	}
+
+	public boolean versionMeetsMinimum(int major, int minor, int subminor)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.versionMeetsMinimum(major, minor, subminor);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return false;
+	}
+
+	public String exposeAsXml() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.exposeAsXml();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null;
+	}
+
+	public boolean getAllowLoadLocalInfile() {
+		return this.mc.getAllowLoadLocalInfile();
+	}
+
+	public boolean getAllowMultiQueries() {
+		return this.mc.getAllowMultiQueries();
+	}
+
+	public boolean getAllowNanAndInf() {
+		return this.mc.getAllowNanAndInf();
+	}
+
+	public boolean getAllowUrlInLocalInfile() {
+		return this.mc.getAllowUrlInLocalInfile();
+	}
+
+	public boolean getAlwaysSendSetIsolation() {
+		return this.mc.getAlwaysSendSetIsolation();
+	}
+
+	public boolean getAutoClosePStmtStreams() {
+		return this.mc.getAutoClosePStmtStreams();
+	}
+
+	public boolean getAutoDeserialize() {
+		return this.mc.getAutoDeserialize();
+	}
+
+	public boolean getAutoGenerateTestcaseScript() {
+		return this.mc.getAutoGenerateTestcaseScript();
+	}
+
+	public boolean getAutoReconnectForPools() {
+		return this.mc.getAutoReconnectForPools();
+	}
+
+	public boolean getAutoSlowLog() {
+		return this.mc.getAutoSlowLog();
+	}
+
+	public int getBlobSendChunkSize() {
+		return this.mc.getBlobSendChunkSize();
+	}
+
+	public boolean getBlobsAreStrings() {
+		return this.mc.getBlobsAreStrings();
+	}
+
+	public boolean getCacheCallableStatements() {
+		return this.mc.getCacheCallableStatements();
+	}
+
+	public boolean getCacheCallableStmts() {
+		return this.mc.getCacheCallableStmts();
+	}
+
+	public boolean getCachePrepStmts() {
+		return this.mc.getCachePrepStmts();
+	}
+
+	public boolean getCachePreparedStatements() {
+		return this.mc.getCachePreparedStatements();
+	}
+
+	public boolean getCacheResultSetMetadata() {
+		return this.mc.getCacheResultSetMetadata();
+	}
+
+	public boolean getCacheServerConfiguration() {
+		return this.mc.getCacheServerConfiguration();
+	}
+
+	public int getCallableStatementCacheSize() {
+		return this.mc.getCallableStatementCacheSize();
+	}
+
+	public int getCallableStmtCacheSize() {
+		return this.mc.getCallableStmtCacheSize();
+	}
+
+	public boolean getCapitalizeTypeNames() {
+		return this.mc.getCapitalizeTypeNames();
+	}
+
+	public String getCharacterSetResults() {
+		return this.mc.getCharacterSetResults();
+	}
+
+	public String getClientCertificateKeyStorePassword() {
+		return this.mc.getClientCertificateKeyStorePassword();
+	}
+
+	public String getClientCertificateKeyStoreType() {
+		return this.mc.getClientCertificateKeyStoreType();
+	}
+
+	public String getClientCertificateKeyStoreUrl() {
+		return this.mc.getClientCertificateKeyStoreUrl();
+	}
+
+	public String getClientInfoProvider() {
+		return this.mc.getClientInfoProvider();
+	}
+
+	public String getClobCharacterEncoding() {
+		return this.mc.getClobCharacterEncoding();
+	}
+
+	public boolean getClobberStreamingResults() {
+		return this.mc.getClobberStreamingResults();
+	}
+
+	public int getConnectTimeout() {
+		return this.mc.getConnectTimeout();
+	}
+
+	public String getConnectionCollation() {
+		return this.mc.getConnectionCollation();
+	}
+
+	public String getConnectionLifecycleInterceptors() {
+		return this.mc.getConnectionLifecycleInterceptors();
+	}
+
+	public boolean getContinueBatchOnError() {
+		return this.mc.getContinueBatchOnError();
+	}
+
+	public boolean getCreateDatabaseIfNotExist() {
+		return this.mc.getCreateDatabaseIfNotExist();
+	}
+
+	public int getDefaultFetchSize() {
+		return this.mc.getDefaultFetchSize();
+	}
+
+	public boolean getDontTrackOpenResources() {
+		return this.mc.getDontTrackOpenResources();
+	}
+
+	public boolean getDumpMetadataOnColumnNotFound() {
+		return this.mc.getDumpMetadataOnColumnNotFound();
+	}
+
+	public boolean getDumpQueriesOnException() {
+		return this.mc.getDumpQueriesOnException();
+	}
+
+	public boolean getDynamicCalendars() {
+		return this.mc.getDynamicCalendars();
+	}
+
+	public boolean getElideSetAutoCommits() {
+		return this.mc.getElideSetAutoCommits();
+	}
+
+	public boolean getEmptyStringsConvertToZero() {
+		return this.mc.getEmptyStringsConvertToZero();
+	}
+
+	public boolean getEmulateLocators() {
+		return this.mc.getEmulateLocators();
+	}
+
+	public boolean getEmulateUnsupportedPstmts() {
+		return this.mc.getEmulateUnsupportedPstmts();
+	}
+
+	public boolean getEnablePacketDebug() {
+		return this.mc.getEnablePacketDebug();
+	}
+
+	public boolean getEnableQueryTimeouts() {
+		return this.mc.getEnableQueryTimeouts();
+	}
+
+	public String getEncoding() {
+		return this.mc.getEncoding();
+	}
+
+	public boolean getExplainSlowQueries() {
+		return this.mc.getExplainSlowQueries();
+	}
+
+	public boolean getFailOverReadOnly() {
+		return this.mc.getFailOverReadOnly();
+	}
+
+	public boolean getFunctionsNeverReturnBlobs() {
+		return this.mc.getFunctionsNeverReturnBlobs();
+	}
+
+	public boolean getGatherPerfMetrics() {
+		return this.mc.getGatherPerfMetrics();
+	}
+
+	public boolean getGatherPerformanceMetrics() {
+		return this.mc.getGatherPerformanceMetrics();
+	}
+
+	public boolean getGenerateSimpleParameterMetadata() {
+		return this.mc.getGenerateSimpleParameterMetadata();
+	}
+
+	public boolean getHoldResultsOpenOverStatementClose() {
+		return this.mc.getHoldResultsOpenOverStatementClose();
+	}
+
+	public boolean getIgnoreNonTxTables() {
+		return this.mc.getIgnoreNonTxTables();
+	}
+
+	public boolean getIncludeInnodbStatusInDeadlockExceptions() {
+		return this.mc.getIncludeInnodbStatusInDeadlockExceptions();
+	}
+
+	public int getInitialTimeout() {
+		return this.mc.getInitialTimeout();
+	}
+
+	public boolean getInteractiveClient() {
+		return this.mc.getInteractiveClient();
+	}
+
+	public boolean getIsInteractiveClient() {
+		return this.mc.getIsInteractiveClient();
+	}
+
+	public boolean getJdbcCompliantTruncation() {
+		return this.mc.getJdbcCompliantTruncation();
+	}
+
+	public boolean getJdbcCompliantTruncationForReads() {
+		return this.mc.getJdbcCompliantTruncationForReads();
+	}
+
+	public String getLargeRowSizeThreshold() {
+		return this.mc.getLargeRowSizeThreshold();
+	}
+
+	public String getLoadBalanceStrategy() {
+		return this.mc.getLoadBalanceStrategy();
+	}
+
+	public String getLocalSocketAddress() {
+		return this.mc.getLocalSocketAddress();
+	}
+
+	public int getLocatorFetchBufferSize() {
+		return this.mc.getLocatorFetchBufferSize();
+	}
+
+	public boolean getLogSlowQueries() {
+		return this.mc.getLogSlowQueries();
+	}
+
+	public boolean getLogXaCommands() {
+		return this.mc.getLogXaCommands();
+	}
+
+	public String getLogger() {
+		return this.mc.getLogger();
+	}
+
+	public String getLoggerClassName() {
+		return this.mc.getLoggerClassName();
+	}
+
+	public boolean getMaintainTimeStats() {
+		return this.mc.getMaintainTimeStats();
+	}
+
+	public int getMaxQuerySizeToLog() {
+		return this.mc.getMaxQuerySizeToLog();
+	}
+
+	public int getMaxReconnects() {
+		return this.mc.getMaxReconnects();
+	}
+
+	public int getMaxRows() {
+		return this.mc.getMaxRows();
+	}
+
+	public int getMetadataCacheSize() {
+		return this.mc.getMetadataCacheSize();
+	}
+
+	public int getNetTimeoutForStreamingResults() {
+		return this.mc.getNetTimeoutForStreamingResults();
+	}
+
+	public boolean getNoAccessToProcedureBodies() {
+		return this.mc.getNoAccessToProcedureBodies();
+	}
+
+	public boolean getNoDatetimeStringSync() {
+		return this.mc.getNoDatetimeStringSync();
+	}
+
+	public boolean getNoTimezoneConversionForTimeType() {
+		return this.mc.getNoTimezoneConversionForTimeType();
+	}
+
+	public boolean getNullCatalogMeansCurrent() {
+		return this.mc.getNullCatalogMeansCurrent();
+	}
+
+	public boolean getNullNamePatternMatchesAll() {
+		return this.mc.getNullNamePatternMatchesAll();
+	}
+
+	public boolean getOverrideSupportsIntegrityEnhancementFacility() {
+		return this.mc.getOverrideSupportsIntegrityEnhancementFacility();
+	}
+
+	public int getPacketDebugBufferSize() {
+		return this.mc.getPacketDebugBufferSize();
+	}
+
+	public boolean getPadCharsWithSpace() {
+		return this.mc.getPadCharsWithSpace();
+	}
+
+	public boolean getParanoid() {
+		return this.mc.getParanoid();
+	}
+
+	public boolean getPedantic() {
+		return this.mc.getPedantic();
+	}
+
+	public boolean getPinGlobalTxToPhysicalConnection() {
+		return this.mc.getPinGlobalTxToPhysicalConnection();
+	}
+
+	public boolean getPopulateInsertRowWithDefaultValues() {
+		return this.mc.getPopulateInsertRowWithDefaultValues();
+	}
+
+	public int getPrepStmtCacheSize() {
+		return this.mc.getPrepStmtCacheSize();
+	}
+
+	public int getPrepStmtCacheSqlLimit() {
+		return this.mc.getPrepStmtCacheSqlLimit();
+	}
+
+	public int getPreparedStatementCacheSize() {
+		return this.mc.getPreparedStatementCacheSize();
+	}
+
+	public int getPreparedStatementCacheSqlLimit() {
+		return this.mc.getPreparedStatementCacheSqlLimit();
+	}
+
+	public boolean getProcessEscapeCodesForPrepStmts() {
+		return this.mc.getProcessEscapeCodesForPrepStmts();
+	}
+
+	public boolean getProfileSQL() {
+		return this.mc.getProfileSQL();
+	}
+
+	public boolean getProfileSql() {
+		return this.mc.getProfileSql();
+	}
+
+	public String getPropertiesTransform() {
+		return this.mc.getPropertiesTransform();
+	}
+
+	public int getQueriesBeforeRetryMaster() {
+		return this.mc.getQueriesBeforeRetryMaster();
+	}
+
+	public boolean getReconnectAtTxEnd() {
+		return this.mc.getReconnectAtTxEnd();
+	}
+
+	public boolean getRelaxAutoCommit() {
+		return this.mc.getRelaxAutoCommit();
+	}
+
+	public int getReportMetricsIntervalMillis() {
+		return this.mc.getReportMetricsIntervalMillis();
+	}
+
+	public boolean getRequireSSL() {
+		return this.mc.getRequireSSL();
+	}
+
+	public String getResourceId() {
+		return this.mc.getResourceId();
+	}
+
+	public int getResultSetSizeThreshold() {
+		return this.mc.getResultSetSizeThreshold();
+	}
+
+	public boolean getRewriteBatchedStatements() {
+		return this.mc.getRewriteBatchedStatements();
+	}
+
+	public boolean getRollbackOnPooledClose() {
+		return this.mc.getRollbackOnPooledClose();
+	}
+
+	public boolean getRoundRobinLoadBalance() {
+		return this.mc.getRoundRobinLoadBalance();
+	}
+
+	public boolean getRunningCTS13() {
+		return this.mc.getRunningCTS13();
+	}
+
+	public int getSecondsBeforeRetryMaster() {
+		return this.mc.getSecondsBeforeRetryMaster();
+	}
+
+	public String getServerTimezone() {
+		return this.mc.getServerTimezone();
+	}
+
+	public String getSessionVariables() {
+		return this.mc.getSessionVariables();
+	}
+
+	public int getSlowQueryThresholdMillis() {
+		return this.mc.getSlowQueryThresholdMillis();
+	}
+
+	public long getSlowQueryThresholdNanos() {
+		return this.mc.getSlowQueryThresholdNanos();
+	}
+
+	public String getSocketFactory() {
+		return this.mc.getSocketFactory();
+	}
+
+	public String getSocketFactoryClassName() {
+		return this.mc.getSocketFactoryClassName();
+	}
+
+	public int getSocketTimeout() {
+		return this.mc.getSocketTimeout();
+	}
+
+	public String getStatementInterceptors() {
+		return this.mc.getStatementInterceptors();
+	}
+
+	public boolean getStrictFloatingPoint() {
+		return this.mc.getStrictFloatingPoint();
+	}
+
+	public boolean getStrictUpdates() {
+		return this.mc.getStrictUpdates();
+	}
+
+	public boolean getTcpKeepAlive() {
+		return this.mc.getTcpKeepAlive();
+	}
+
+	public boolean getTcpNoDelay() {
+		return this.mc.getTcpNoDelay();
+	}
+
+	public int getTcpRcvBuf() {
+		return this.mc.getTcpRcvBuf();
+	}
+
+	public int getTcpSndBuf() {
+		return this.mc.getTcpSndBuf();
+	}
+
+	public int getTcpTrafficClass() {
+		return this.mc.getTcpTrafficClass();
+	}
+
+	public boolean getTinyInt1isBit() {
+		return this.mc.getTinyInt1isBit();
+	}
+
+	public boolean getTraceProtocol() {
+		return this.mc.getTraceProtocol();
+	}
+
+	public boolean getTransformedBitIsBoolean() {
+		return this.mc.getTransformedBitIsBoolean();
+	}
+
+	public boolean getTreatUtilDateAsTimestamp() {
+		return this.mc.getTreatUtilDateAsTimestamp();
+	}
+
+	public String getTrustCertificateKeyStorePassword() {
+		return this.mc.getTrustCertificateKeyStorePassword();
+	}
+
+	public String getTrustCertificateKeyStoreType() {
+		return this.mc.getTrustCertificateKeyStoreType();
+	}
+
+	public String getTrustCertificateKeyStoreUrl() {
+		return this.mc.getTrustCertificateKeyStoreUrl();
+	}
+
+	public boolean getUltraDevHack() {
+		return this.mc.getUltraDevHack();
+	}
+
+	public boolean getUseBlobToStoreUTF8OutsideBMP() {
+		return this.mc.getUseBlobToStoreUTF8OutsideBMP();
+	}
+
+	public boolean getUseCompression() {
+		return this.mc.getUseCompression();
+	}
+
+	public String getUseConfigs() {
+		return this.mc.getUseConfigs();
+	}
+
+	public boolean getUseCursorFetch() {
+		return this.mc.getUseCursorFetch();
+	}
+
+	public boolean getUseDirectRowUnpack() {
+		return this.mc.getUseDirectRowUnpack();
+	}
+
+	public boolean getUseDynamicCharsetInfo() {
+		return this.mc.getUseDynamicCharsetInfo();
+	}
+
+	public boolean getUseFastDateParsing() {
+		return this.mc.getUseFastDateParsing();
+	}
+
+	public boolean getUseFastIntParsing() {
+		return this.mc.getUseFastIntParsing();
+	}
+
+	public boolean getUseGmtMillisForDatetimes() {
+		return this.mc.getUseGmtMillisForDatetimes();
+	}
+
+	public boolean getUseHostsInPrivileges() {
+		return this.mc.getUseHostsInPrivileges();
+	}
+
+	public boolean getUseInformationSchema() {
+		return this.mc.getUseInformationSchema();
+	}
+
+	public boolean getUseJDBCCompliantTimezoneShift() {
+		return this.mc.getUseJDBCCompliantTimezoneShift();
+	}
+
+	public boolean getUseJvmCharsetConverters() {
+		return this.mc.getUseJvmCharsetConverters();
+	}
+
+	public boolean getUseLocalSessionState() {
+		return this.mc.getUseLocalSessionState();
+	}
+
+	public boolean getUseNanosForElapsedTime() {
+		return this.mc.getUseNanosForElapsedTime();
+	}
+
+	public boolean getUseOldAliasMetadataBehavior() {
+		return this.mc.getUseOldAliasMetadataBehavior();
+	}
+
+	public boolean getUseOldUTF8Behavior() {
+		return this.mc.getUseOldUTF8Behavior();
+	}
+
+	public boolean getUseOnlyServerErrorMessages() {
+		return this.mc.getUseOnlyServerErrorMessages();
+	}
+
+	public boolean getUseReadAheadInput() {
+		return this.mc.getUseReadAheadInput();
+	}
+
+	public boolean getUseSSL() {
+		return this.mc.getUseSSL();
+	}
+
+	public boolean getUseSSPSCompatibleTimezoneShift() {
+		return this.mc.getUseSSPSCompatibleTimezoneShift();
+	}
+
+	public boolean getUseServerPrepStmts() {
+		return this.mc.getUseServerPrepStmts();
+	}
+
+	public boolean getUseServerPreparedStmts() {
+		return this.mc.getUseServerPreparedStmts();
+	}
+
+	public boolean getUseSqlStateCodes() {
+		return this.mc.getUseSqlStateCodes();
+	}
+
+	public boolean getUseStreamLengthsInPrepStmts() {
+		return this.mc.getUseStreamLengthsInPrepStmts();
+	}
+
+	public boolean getUseTimezone() {
+		return this.mc.getUseTimezone();
+	}
+
+	public boolean getUseUltraDevWorkAround() {
+		return this.mc.getUseUltraDevWorkAround();
+	}
+
+	public boolean getUseUnbufferedInput() {
+		return this.mc.getUseUnbufferedInput();
+	}
+
+	public boolean getUseUnicode() {
+		return this.mc.getUseUnicode();
+	}
+
+	public boolean getUseUsageAdvisor() {
+		return this.mc.getUseUsageAdvisor();
+	}
+
+	public String getUtf8OutsideBmpExcludedColumnNamePattern() {
+		return this.mc.getUtf8OutsideBmpExcludedColumnNamePattern();
+	}
+
+	public String getUtf8OutsideBmpIncludedColumnNamePattern() {
+		return this.mc.getUtf8OutsideBmpIncludedColumnNamePattern();
+	}
+
+	public boolean getYearIsDateType() {
+		return this.mc.getYearIsDateType();
+	}
+
+	public String getZeroDateTimeBehavior() {
+		return this.mc.getZeroDateTimeBehavior();
+	}
+
+	public void setAllowLoadLocalInfile(boolean property) {
+		this.mc.setAllowLoadLocalInfile(property);
+	}
+
+	public void setAllowMultiQueries(boolean property) {
+		this.mc.setAllowMultiQueries(property);
+	}
+
+	public void setAllowNanAndInf(boolean flag) {
+		this.mc.setAllowNanAndInf(flag);
+	}
+
+	public void setAllowUrlInLocalInfile(boolean flag) {
+		this.mc.setAllowUrlInLocalInfile(flag);
+	}
+
+	public void setAlwaysSendSetIsolation(boolean flag) {
+		this.mc.setAlwaysSendSetIsolation(flag);
+	}
+
+	public void setAutoClosePStmtStreams(boolean flag) {
+		this.mc.setAutoClosePStmtStreams(flag);
+	}
+
+	public void setAutoDeserialize(boolean flag) {
+		this.mc.setAutoDeserialize(flag);
+	}
+
+	public void setAutoGenerateTestcaseScript(boolean flag) {
+		this.mc.setAutoGenerateTestcaseScript(flag);
+	}
+
+	public void setAutoReconnect(boolean flag) {
+		this.mc.setAutoReconnect(flag);
+	}
+
+	public void setAutoReconnectForConnectionPools(boolean property) {
+		this.mc.setAutoReconnectForConnectionPools(property);
+	}
+
+	public void setAutoReconnectForPools(boolean flag) {
+		this.mc.setAutoReconnectForPools(flag);
+	}
+
+	public void setAutoSlowLog(boolean flag) {
+		this.mc.setAutoSlowLog(flag);
+	}
+
+	public void setBlobSendChunkSize(String value) throws SQLException {
+		this.mc.setBlobSendChunkSize(value);
+	}
+
+	public void setBlobsAreStrings(boolean flag) {
+		this.mc.setBlobsAreStrings(flag);
+	}
+
+	public void setCacheCallableStatements(boolean flag) {
+		this.mc.setCacheCallableStatements(flag);
+	}
+
+	public void setCacheCallableStmts(boolean flag) {
+		this.mc.setCacheCallableStmts(flag);
+	}
+
+	public void setCachePrepStmts(boolean flag) {
+		this.mc.setCachePrepStmts(flag);
+	}
+
+	public void setCachePreparedStatements(boolean flag) {
+		this.mc.setCachePreparedStatements(flag);
+	}
+
+	public void setCacheResultSetMetadata(boolean property) {
+		this.mc.setCacheResultSetMetadata(property);
+	}
+
+	public void setCacheServerConfiguration(boolean flag) {
+		this.mc.setCacheServerConfiguration(flag);
+	}
+
+	public void setCallableStatementCacheSize(int size) {
+		this.mc.setCallableStatementCacheSize(size);
+	}
+
+	public void setCallableStmtCacheSize(int cacheSize) {
+		this.mc.setCallableStmtCacheSize(cacheSize);
+	}
+
+	public void setCapitalizeDBMDTypes(boolean property) {
+		this.mc.setCapitalizeDBMDTypes(property);
+	}
+
+	public void setCapitalizeTypeNames(boolean flag) {
+		this.mc.setCapitalizeTypeNames(flag);
+	}
+
+	public void setCharacterEncoding(String encoding) {
+		this.mc.setCharacterEncoding(encoding);
+	}
+
+	public void setCharacterSetResults(String characterSet) {
+		this.mc.setCharacterSetResults(characterSet);
+	}
+
+	public void setClientCertificateKeyStorePassword(String value) {
+		this.mc.setClientCertificateKeyStorePassword(value);
+	}
+
+	public void setClientCertificateKeyStoreType(String value) {
+		this.mc.setClientCertificateKeyStoreType(value);
+	}
+
+	public void setClientCertificateKeyStoreUrl(String value) {
+		this.mc.setClientCertificateKeyStoreUrl(value);
+	}
+
+	public void setClientInfoProvider(String classname) {
+		this.mc.setClientInfoProvider(classname);
+	}
+
+	public void setClobCharacterEncoding(String encoding) {
+		this.mc.setClobCharacterEncoding(encoding);
+	}
+
+	public void setClobberStreamingResults(boolean flag) {
+		this.mc.setClobberStreamingResults(flag);
+	}
+
+	public void setConnectTimeout(int timeoutMs) {
+		this.mc.setConnectTimeout(timeoutMs);
+	}
+
+	public void setConnectionCollation(String collation) {
+		this.mc.setConnectionCollation(collation);
+	}
+
+	public void setConnectionLifecycleInterceptors(String interceptors) {
+		this.mc.setConnectionLifecycleInterceptors(interceptors);
+	}
+
+	public void setContinueBatchOnError(boolean property) {
+		this.mc.setContinueBatchOnError(property);
+	}
+
+	public void setCreateDatabaseIfNotExist(boolean flag) {
+		this.mc.setCreateDatabaseIfNotExist(flag);
+	}
+
+	public void setDefaultFetchSize(int n) {
+		this.mc.setDefaultFetchSize(n);
+	}
+
+	public void setDetectServerPreparedStmts(boolean property) {
+		this.mc.setDetectServerPreparedStmts(property);
+	}
+
+	public void setDontTrackOpenResources(boolean flag) {
+		this.mc.setDontTrackOpenResources(flag);
+	}
+
+	public void setDumpMetadataOnColumnNotFound(boolean flag) {
+		this.mc.setDumpMetadataOnColumnNotFound(flag);
+	}
+
+	public void setDumpQueriesOnException(boolean flag) {
+		this.mc.setDumpQueriesOnException(flag);
+	}
+
+	public void setDynamicCalendars(boolean flag) {
+		this.mc.setDynamicCalendars(flag);
+	}
+
+	public void setElideSetAutoCommits(boolean flag) {
+		this.mc.setElideSetAutoCommits(flag);
+	}
+
+	public void setEmptyStringsConvertToZero(boolean flag) {
+		this.mc.setEmptyStringsConvertToZero(flag);
+	}
+
+	public void setEmulateLocators(boolean property) {
+		this.mc.setEmulateLocators(property);
+	}
+
+	public void setEmulateUnsupportedPstmts(boolean flag) {
+		this.mc.setEmulateUnsupportedPstmts(flag);
+	}
+
+	public void setEnablePacketDebug(boolean flag) {
+		this.mc.setEnablePacketDebug(flag);
+	}
+
+	public void setEnableQueryTimeouts(boolean flag) {
+		this.mc.setEnableQueryTimeouts(flag);
+	}
+
+	public void setEncoding(String property) {
+		this.mc.setEncoding(property);
+	}
+
+	public void setExplainSlowQueries(boolean flag) {
+		this.mc.setExplainSlowQueries(flag);
+	}
+
+	public void setFailOverReadOnly(boolean flag) {
+		this.mc.setFailOverReadOnly(flag);
+	}
+
+	public void setFunctionsNeverReturnBlobs(boolean flag) {
+		this.mc.setFunctionsNeverReturnBlobs(flag);
+	}
+
+	public void setGatherPerfMetrics(boolean flag) {
+		this.mc.setGatherPerfMetrics(flag);
+	}
+
+	public void setGatherPerformanceMetrics(boolean flag) {
+		this.mc.setGatherPerformanceMetrics(flag);
+	}
+
+	public void setGenerateSimpleParameterMetadata(boolean flag) {
+		this.mc.setGenerateSimpleParameterMetadata(flag);
+	}
+
+	public void setHoldResultsOpenOverStatementClose(boolean flag) {
+		this.mc.setHoldResultsOpenOverStatementClose(flag);
+	}
+
+	public void setIgnoreNonTxTables(boolean property) {
+		this.mc.setIgnoreNonTxTables(property);
+	}
+
+	public void setIncludeInnodbStatusInDeadlockExceptions(boolean flag) {
+		this.mc.setIncludeInnodbStatusInDeadlockExceptions(flag);
+	}
+
+	public void setInitialTimeout(int property) {
+		this.mc.setInitialTimeout(property);
+	}
+
+	public void setInteractiveClient(boolean property) {
+		this.mc.setInteractiveClient(property);
+	}
+
+	public void setIsInteractiveClient(boolean property) {
+		this.mc.setIsInteractiveClient(property);
+	}
+
+	public void setJdbcCompliantTruncation(boolean flag) {
+		this.mc.setJdbcCompliantTruncation(flag);
+	}
+
+	public void setJdbcCompliantTruncationForReads(
+			boolean jdbcCompliantTruncationForReads) {
+		this.mc
+				.setJdbcCompliantTruncationForReads(jdbcCompliantTruncationForReads);
+	}
+
+	public void setLargeRowSizeThreshold(String value) {
+		this.mc.setLargeRowSizeThreshold(value);
+	}
+
+	public void setLoadBalanceStrategy(String strategy) {
+		this.mc.setLoadBalanceStrategy(strategy);
+	}
+
+	public void setLocalSocketAddress(String address) {
+		this.mc.setLocalSocketAddress(address);
+	}
+
+	public void setLocatorFetchBufferSize(String value) throws SQLException {
+		this.mc.setLocatorFetchBufferSize(value);
+	}
+
+	public void setLogSlowQueries(boolean flag) {
+		this.mc.setLogSlowQueries(flag);
+	}
+
+	public void setLogXaCommands(boolean flag) {
+		this.mc.setLogXaCommands(flag);
+	}
+
+	public void setLogger(String property) {
+		this.mc.setLogger(property);
+	}
+
+	public void setLoggerClassName(String className) {
+		this.mc.setLoggerClassName(className);
+	}
+
+	public void setMaintainTimeStats(boolean flag) {
+		this.mc.setMaintainTimeStats(flag);
+	}
+
+	public void setMaxQuerySizeToLog(int sizeInBytes) {
+		this.mc.setMaxQuerySizeToLog(sizeInBytes);
+	}
+
+	public void setMaxReconnects(int property) {
+		this.mc.setMaxReconnects(property);
+	}
+
+	public void setMaxRows(int property) {
+		this.mc.setMaxRows(property);
+	}
+
+	public void setMetadataCacheSize(int value) {
+		this.mc.setMetadataCacheSize(value);
+	}
+
+	public void setNetTimeoutForStreamingResults(int value) {
+		this.mc.setNetTimeoutForStreamingResults(value);
+	}
+
+	public void setNoAccessToProcedureBodies(boolean flag) {
+		this.mc.setNoAccessToProcedureBodies(flag);
+	}
+
+	public void setNoDatetimeStringSync(boolean flag) {
+		this.mc.setNoDatetimeStringSync(flag);
+	}
+
+	public void setNoTimezoneConversionForTimeType(boolean flag) {
+		this.mc.setNoTimezoneConversionForTimeType(flag);
+	}
+
+	public void setNullCatalogMeansCurrent(boolean value) {
+		this.mc.setNullCatalogMeansCurrent(value);
+	}
+
+	public void setNullNamePatternMatchesAll(boolean value) {
+		this.mc.setNullNamePatternMatchesAll(value);
+	}
+
+	public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
+		this.mc.setOverrideSupportsIntegrityEnhancementFacility(flag);
+	}
+
+	public void setPacketDebugBufferSize(int size) {
+		this.mc.setPacketDebugBufferSize(size);
+	}
+
+	public void setPadCharsWithSpace(boolean flag) {
+		this.mc.setPadCharsWithSpace(flag);
+	}
+
+	public void setParanoid(boolean property) {
+		this.mc.setParanoid(property);
+	}
+
+	public void setPedantic(boolean property) {
+		this.mc.setPedantic(property);
+	}
+
+	public void setPinGlobalTxToPhysicalConnection(boolean flag) {
+		this.mc.setPinGlobalTxToPhysicalConnection(flag);
+	}
+
+	public void setPopulateInsertRowWithDefaultValues(boolean flag) {
+		this.mc.setPopulateInsertRowWithDefaultValues(flag);
+	}
+
+	public void setPrepStmtCacheSize(int cacheSize) {
+		this.mc.setPrepStmtCacheSize(cacheSize);
+	}
+
+	public void setPrepStmtCacheSqlLimit(int sqlLimit) {
+		this.mc.setPrepStmtCacheSqlLimit(sqlLimit);
+	}
+
+	public void setPreparedStatementCacheSize(int cacheSize) {
+		this.mc.setPreparedStatementCacheSize(cacheSize);
+	}
+
+	public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) {
+		this.mc.setPreparedStatementCacheSqlLimit(cacheSqlLimit);
+	}
+
+	public void setProcessEscapeCodesForPrepStmts(boolean flag) {
+		this.mc.setProcessEscapeCodesForPrepStmts(flag);
+	}
+
+	public void setProfileSQL(boolean flag) {
+		this.mc.setProfileSQL(flag);
+	}
+
+	public void setProfileSql(boolean property) {
+		this.mc.setProfileSql(property);
+	}
+
+	public void setPropertiesTransform(String value) {
+		this.mc.setPropertiesTransform(value);
+	}
+
+	public void setQueriesBeforeRetryMaster(int property) {
+		this.mc.setQueriesBeforeRetryMaster(property);
+	}
+
+	public void setReconnectAtTxEnd(boolean property) {
+		this.mc.setReconnectAtTxEnd(property);
+	}
+
+	public void setRelaxAutoCommit(boolean property) {
+		this.mc.setRelaxAutoCommit(property);
+	}
+
+	public void setReportMetricsIntervalMillis(int millis) {
+		this.mc.setReportMetricsIntervalMillis(millis);
+	}
+
+	public void setRequireSSL(boolean property) {
+		this.mc.setRequireSSL(property);
+	}
+
+	public void setResourceId(String resourceId) {
+		this.mc.setResourceId(resourceId);
+	}
+
+	public void setResultSetSizeThreshold(int threshold) {
+		this.mc.setResultSetSizeThreshold(threshold);
+	}
+
+	public void setRetainStatementAfterResultSetClose(boolean flag) {
+		this.mc.setRetainStatementAfterResultSetClose(flag);
+	}
+
+	public void setRewriteBatchedStatements(boolean flag) {
+		this.mc.setRewriteBatchedStatements(flag);
+	}
+
+	public void setRollbackOnPooledClose(boolean flag) {
+		this.mc.setRollbackOnPooledClose(flag);
+	}
+
+	public void setRoundRobinLoadBalance(boolean flag) {
+		this.mc.setRoundRobinLoadBalance(flag);
+	}
+
+	public void setRunningCTS13(boolean flag) {
+		this.mc.setRunningCTS13(flag);
+	}
+
+	public void setSecondsBeforeRetryMaster(int property) {
+		this.mc.setSecondsBeforeRetryMaster(property);
+	}
+
+	public void setServerTimezone(String property) {
+		this.mc.setServerTimezone(property);
+	}
+
+	public void setSessionVariables(String variables) {
+		this.mc.setSessionVariables(variables);
+	}
+
+	public void setSlowQueryThresholdMillis(int millis) {
+		this.mc.setSlowQueryThresholdMillis(millis);
+	}
+
+	public void setSlowQueryThresholdNanos(long nanos) {
+		this.mc.setSlowQueryThresholdNanos(nanos);
+	}
+
+	public void setSocketFactory(String name) {
+		this.mc.setSocketFactory(name);
+	}
+
+	public void setSocketFactoryClassName(String property) {
+		this.mc.setSocketFactoryClassName(property);
+	}
+
+	public void setSocketTimeout(int property) {
+		this.mc.setSocketTimeout(property);
+	}
+
+	public void setStatementInterceptors(String value) {
+		this.mc.setStatementInterceptors(value);
+	}
+
+	public void setStrictFloatingPoint(boolean property) {
+		this.mc.setStrictFloatingPoint(property);
+	}
+
+	public void setStrictUpdates(boolean property) {
+		this.mc.setStrictUpdates(property);
+	}
+
+	public void setTcpKeepAlive(boolean flag) {
+		this.mc.setTcpKeepAlive(flag);
+	}
+
+	public void setTcpNoDelay(boolean flag) {
+		this.mc.setTcpNoDelay(flag);
+	}
+
+	public void setTcpRcvBuf(int bufSize) {
+		this.mc.setTcpRcvBuf(bufSize);
+	}
+
+	public void setTcpSndBuf(int bufSize) {
+		this.mc.setTcpSndBuf(bufSize);
+	}
+
+	public void setTcpTrafficClass(int classFlags) {
+		this.mc.setTcpTrafficClass(classFlags);
+	}
+
+	public void setTinyInt1isBit(boolean flag) {
+		this.mc.setTinyInt1isBit(flag);
+	}
+
+	public void setTraceProtocol(boolean flag) {
+		this.mc.setTraceProtocol(flag);
+	}
+
+	public void setTransformedBitIsBoolean(boolean flag) {
+		this.mc.setTransformedBitIsBoolean(flag);
+	}
+
+	public void setTreatUtilDateAsTimestamp(boolean flag) {
+		this.mc.setTreatUtilDateAsTimestamp(flag);
+	}
+
+	public void setTrustCertificateKeyStorePassword(String value) {
+		this.mc.setTrustCertificateKeyStorePassword(value);
+	}
+
+	public void setTrustCertificateKeyStoreType(String value) {
+		this.mc.setTrustCertificateKeyStoreType(value);
+	}
+
+	public void setTrustCertificateKeyStoreUrl(String value) {
+		this.mc.setTrustCertificateKeyStoreUrl(value);
+	}
+
+	public void setUltraDevHack(boolean flag) {
+		this.mc.setUltraDevHack(flag);
+	}
+
+	public void setUseBlobToStoreUTF8OutsideBMP(boolean flag) {
+		this.mc.setUseBlobToStoreUTF8OutsideBMP(flag);
+	}
+
+	public void setUseCompression(boolean property) {
+		this.mc.setUseCompression(property);
+	}
+
+	public void setUseConfigs(String configs) {
+		this.mc.setUseConfigs(configs);
+	}
+
+	public void setUseCursorFetch(boolean flag) {
+		this.mc.setUseCursorFetch(flag);
+	}
+
+	public void setUseDirectRowUnpack(boolean flag) {
+		this.mc.setUseDirectRowUnpack(flag);
+	}
+
+	public void setUseDynamicCharsetInfo(boolean flag) {
+		this.mc.setUseDynamicCharsetInfo(flag);
+	}
+
+	public void setUseFastDateParsing(boolean flag) {
+		this.mc.setUseFastDateParsing(flag);
+	}
+
+	public void setUseFastIntParsing(boolean flag) {
+		this.mc.setUseFastIntParsing(flag);
+	}
+
+	public void setUseGmtMillisForDatetimes(boolean flag) {
+		this.mc.setUseGmtMillisForDatetimes(flag);
+	}
+
+	public void setUseHostsInPrivileges(boolean property) {
+		this.mc.setUseHostsInPrivileges(property);
+	}
+
+	public void setUseInformationSchema(boolean flag) {
+		this.mc.setUseInformationSchema(flag);
+	}
+
+	public void setUseJDBCCompliantTimezoneShift(boolean flag) {
+		this.mc.setUseJDBCCompliantTimezoneShift(flag);
+	}
+
+	public void setUseJvmCharsetConverters(boolean flag) {
+		this.mc.setUseJvmCharsetConverters(flag);
+	}
+
+	public void setUseLocalSessionState(boolean flag) {
+		this.mc.setUseLocalSessionState(flag);
+	}
+
+	public void setUseNanosForElapsedTime(boolean flag) {
+		this.mc.setUseNanosForElapsedTime(flag);
+	}
+
+	public void setUseOldAliasMetadataBehavior(boolean flag) {
+		this.mc.setUseOldAliasMetadataBehavior(flag);
+	}
+
+	public void setUseOldUTF8Behavior(boolean flag) {
+		this.mc.setUseOldUTF8Behavior(flag);
+	}
+
+	public void setUseOnlyServerErrorMessages(boolean flag) {
+		this.mc.setUseOnlyServerErrorMessages(flag);
+	}
+
+	public void setUseReadAheadInput(boolean flag) {
+		this.mc.setUseReadAheadInput(flag);
+	}
+
+	public void setUseSSL(boolean property) {
+		this.mc.setUseSSL(property);
+	}
+
+	public void setUseSSPSCompatibleTimezoneShift(boolean flag) {
+		this.mc.setUseSSPSCompatibleTimezoneShift(flag);
+	}
+
+	public void setUseServerPrepStmts(boolean flag) {
+		this.mc.setUseServerPrepStmts(flag);
+	}
+
+	public void setUseServerPreparedStmts(boolean flag) {
+		this.mc.setUseServerPreparedStmts(flag);
+	}
+
+	public void setUseSqlStateCodes(boolean flag) {
+		this.mc.setUseSqlStateCodes(flag);
+	}
+
+	public void setUseStreamLengthsInPrepStmts(boolean property) {
+		this.mc.setUseStreamLengthsInPrepStmts(property);
+	}
+
+	public void setUseTimezone(boolean property) {
+		this.mc.setUseTimezone(property);
+	}
+
+	public void setUseUltraDevWorkAround(boolean property) {
+		this.mc.setUseUltraDevWorkAround(property);
+	}
+
+	public void setUseUnbufferedInput(boolean flag) {
+		this.mc.setUseUnbufferedInput(flag);
+	}
+
+	public void setUseUnicode(boolean flag) {
+		this.mc.setUseUnicode(flag);
+	}
+
+	public void setUseUsageAdvisor(boolean useUsageAdvisorFlag) {
+		this.mc.setUseUsageAdvisor(useUsageAdvisorFlag);
+	}
+
+	public void setUtf8OutsideBmpExcludedColumnNamePattern(String regexPattern) {
+		this.mc.setUtf8OutsideBmpExcludedColumnNamePattern(regexPattern);
+	}
+
+	public void setUtf8OutsideBmpIncludedColumnNamePattern(String regexPattern) {
+		this.mc.setUtf8OutsideBmpIncludedColumnNamePattern(regexPattern);
+	}
+
+	public void setYearIsDateType(boolean flag) {
+		this.mc.setYearIsDateType(flag);
+	}
+
+	public void setZeroDateTimeBehavior(String behavior) {
+		this.mc.setZeroDateTimeBehavior(behavior);
+	}
+
+	public boolean useUnbufferedInput() {
+		return this.mc.useUnbufferedInput();
+	}
 }

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4CallableStatementWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,1008 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.RowId;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
+import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper;
+import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection;
+
+/**
+ */
+public class JDBC4CallableStatementWrapper extends CallableStatementWrapper {
+
+	public JDBC4CallableStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
+			CallableStatement toWrap) {
+		super(c, conn, toWrap);
+	}
+	
+	public void close() throws SQLException {
+		try {
+			super.close();
+		} finally {
+			this.unwrappedInterfaces = null;
+		}
+	}
+	
+	public boolean isClosed() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.isClosed();
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return false; // never get here - compiler can't tell
+	}
+	
+	public void setPoolable(boolean poolable) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setPoolable(poolable);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public boolean isPoolable() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.isPoolable();
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return false; // never get here - compiler can't tell
+	}
+    
+	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setNClob(int parameterIndex, NClob value) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+						value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setSQLXML(int parameterIndex, SQLXML xmlObject)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setSQLXML(parameterIndex,
+						xmlObject);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	
+	public void setNString(int parameterIndex,
+            String value)
+            throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNString(parameterIndex,
+						value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+            
+    public void setNCharacterStream(int parameterIndex,
+                    Reader value,
+                    long length)
+                    throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex,
+						value, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+
+    public void setClob(int parameterIndex,
+            Reader reader,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+						reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}	
+    }
+    
+    public void setBlob(int parameterIndex,
+            InputStream inputStream,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+						inputStream, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setNClob(int parameterIndex,
+            Reader reader,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+						reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setAsciiStream(int parameterIndex,
+            InputStream x,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex,
+						x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setBinaryStream(int parameterIndex,
+            InputStream x,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex,
+						x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setCharacterStream(int parameterIndex,
+            Reader reader,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex,
+						reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setAsciiStream(int parameterIndex,
+            InputStream x)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setBinaryStream(int parameterIndex,
+            InputStream x)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setCharacterStream(int parameterIndex,
+            Reader reader)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex,
+						reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    	
+    }
+    
+    public void setNCharacterStream(int parameterIndex,
+            Reader value)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex,
+						value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    	
+    }
+    
+    public void setClob(int parameterIndex,
+            Reader reader)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+						reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    	
+    }
+    
+    public void setBlob(int parameterIndex,
+            InputStream inputStream)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+						inputStream);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setNClob(int parameterIndex,
+            Reader reader)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+						reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+	
+	/**
+	 * Returns true if this either implements the interface argument or is
+	 * directly or indirectly a wrapper for an object that does. Returns false
+	 * otherwise. If this implements the interface then return true, else if
+	 * this is a wrapper then return the result of recursively calling
+	 * <code>isWrapperFor</code> on the wrapped object. If this does not
+	 * implement the interface and is not a wrapper, return false. This method
+	 * should be implemented as a low-cost operation compared to
+	 * <code>unwrap</code> so that callers can use this method to avoid
+	 * expensive <code>unwrap</code> calls that may fail. If this method
+	 * returns true then calling <code>unwrap</code> with the same argument
+	 * should succeed.
+	 * 
+	 * @param interfaces
+	 *            a Class defining an interface.
+	 * @return true if this implements the interface or directly or indirectly
+	 *         wraps an object that does.
+	 * @throws java.sql.SQLException
+	 *             if an error occurs while determining whether this is a
+	 *             wrapper for an object with the given interface.
+	 * @since 1.6
+	 */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+
+		boolean isInstance = iface.isInstance(this);
+
+		if (isInstance) {
+			return true;
+		}
+
+		String interfaceClassName = iface.getName();
+		
+		return (interfaceClassName.equals("com.mysql.jdbc.Statement")
+				|| interfaceClassName.equals("java.sql.Statement")
+				|| interfaceClassName.equals("java.sql.PreparedStatement")
+				|| interfaceClassName.equals("java.sql.Wrapper"));
+	}
+
+	/**
+	 * Returns an object that implements the given interface to allow access to
+	 * non-standard methods, or standard methods not exposed by the proxy. The
+	 * result may be either the object found to implement the interface or a
+	 * proxy for that object. If the receiver implements the interface then that
+	 * is the object. If the receiver is a wrapper and the wrapped object
+	 * implements the interface then that is the object. Otherwise the object is
+	 * the result of calling <code>unwrap</code> recursively on the wrapped
+	 * object. If the receiver is not a wrapper and does not implement the
+	 * interface, then an <code>SQLException</code> is thrown.
+	 * 
+	 * @param iface
+	 *            A Class defining an interface that the result must implement.
+	 * @return an object that implements the interface. May be a proxy for the
+	 *         actual implementing object.
+	 * @throws java.sql.SQLException
+	 *             If no object found that implements the interface
+	 * @since 1.6
+	 */
+	public synchronized <T> T unwrap(java.lang.Class<T> iface)
+			throws java.sql.SQLException {
+		try {
+			if ("java.sql.Statement".equals(iface.getName()) 
+					|| "java.sql.PreparedStatement".equals(iface.getName()) 
+					|| "java.sql.Wrapper.class".equals(iface.getName())) {
+				return iface.cast(this);
+			}
+			
+			if (unwrappedInterfaces == null) {
+				unwrappedInterfaces = new HashMap();
+			}
+			
+			Object cachedUnwrapped = unwrappedInterfaces.get(iface);
+			
+			if (cachedUnwrapped == null) {
+				if (cachedUnwrapped == null) {
+					cachedUnwrapped = Proxy.newProxyInstance(
+							this.wrappedStmt.getClass().getClassLoader(), 
+							new Class[] { iface },
+							new ConnectionErrorFiringInvocationHandler(this.wrappedStmt));
+					unwrappedInterfaces.put(iface, cachedUnwrapped);
+				}
+				unwrappedInterfaces.put(iface, cachedUnwrapped);
+			}
+			
+			return iface.cast(cachedUnwrapped);
+		} catch (ClassCastException cce) {
+			throw SQLError.createSQLException("Unable to unwrap to "
+					+ iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+	
+	public void setRowId(String parameterName, RowId x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setRowId(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setSQLXML(parameterName, xmlObject);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public SQLXML getSQLXML(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getSQLXML(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+
+	}
+
+	public SQLXML getSQLXML(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getSQLXML(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+
+	public RowId getRowId(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getRowId(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+	
+	public void setNClob(String parameterName, NClob value) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNClob(parameterName, value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setNClob(String parameterName, Reader reader) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNClob(parameterName, reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setNClob(String parameterName, Reader reader, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNClob(parameterName, reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setNString(String parameterName, String value) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNString(parameterName, value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getCharacterStream(int)
+	 */
+	public Reader getCharacterStream(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getCharacterStream(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getCharacterStream(java.lang.String)
+	 */
+	public Reader getCharacterStream(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getCharacterStream(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNCharacterStream(int)
+	 */
+	public Reader getNCharacterStream(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getNCharacterStream(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNCharacterStream(java.lang.String)
+	 */
+	public Reader getNCharacterStream(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getNCharacterStream(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNClob(java.lang.String)
+	 */
+	public NClob getNClob(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getNClob(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getNString(java.lang.String)
+	 */
+	public String getNString(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getNString(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+	
+	public void setAsciiStream(String parameterName, InputStream x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setAsciiStream(String parameterName, InputStream x, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setAsciiStream(parameterName, x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setBinaryStream(String parameterName, InputStream x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setBinaryStream(String parameterName, InputStream x, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBinaryStream(parameterName, x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setBlob(String parameterName, InputStream x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBlob(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setBlob(String parameterName, InputStream x, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBlob(parameterName, x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setBlob(String parameterName, Blob x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBlob(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setCharacterStream(String parameterName, Reader reader) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setCharacterStream(parameterName, reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setClob(String parameterName, Clob x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setClob(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setClob(String parameterName, Reader reader) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setClob(parameterName, reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setClob(String parameterName, Reader reader, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setClob(parameterName, reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setNCharacterStream(String parameterName, Reader reader) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNCharacterStream(parameterName, reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setNCharacterStream(String parameterName, Reader reader, long length) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNCharacterStream(parameterName, reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public NClob getNClob(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getNClob(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+	
+	public String getNString(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getNString(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+	
+	public RowId getRowId(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getRowId(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return null;
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4ConnectionWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,338 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.NClob;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.SQLError;
+
+/**
+ */
+public class JDBC4ConnectionWrapper extends ConnectionWrapper {
+
+	/**
+	 * Construct a new LogicalHandle and set instance variables
+	 * 
+	 * @param mysqlPooledConnection
+	 *            reference to object that instantiated this object
+	 * @param mysqlConnection
+	 *            physical connection to db
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	public JDBC4ConnectionWrapper(MysqlPooledConnection mysqlPooledConnection,
+			ConnectionImpl mysqlConnection, boolean forXa)
+			throws SQLException {
+		super(mysqlPooledConnection, mysqlConnection, forXa);
+	}
+
+	public void close() throws SQLException {
+		try {
+			super.close();
+		} finally {
+			this.unwrappedInterfaces = null;
+		}
+	}
+	
+	public SQLXML createSQLXML() throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).createSQLXML();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	public java.sql.Array createArrayOf(String typeName, Object[] elements)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).createArrayOf(typeName,
+					elements);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	public Struct createStruct(String typeName, Object[] attributes)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).createStruct(typeName,
+					attributes);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	public Properties getClientInfo() throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).getClientInfo();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	public String getClientInfo(String name) throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).getClientInfo(name);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	/**
+	 * Returns true if the connection has not been closed and is still valid.
+	 * The driver shall submit a query on the connection or use some other
+	 * mechanism that positively verifies the connection is still valid when
+	 * this method is called.
+	 * <p>
+	 * The query submitted by the driver to validate the connection shall be
+	 * executed in the context of the current transaction.
+	 * 
+	 * @param timeout -
+	 *            The time in seconds to wait for the database operation used to
+	 *            validate the connection to complete. If the timeout period
+	 *            expires before the operation completes, this method returns
+	 *            false. A value of 0 indicates a timeout is not applied to the
+	 *            database operation.
+	 *            <p>
+	 * @return true if the connection is valid, false otherwise
+	 * @exception SQLException
+	 *                if the value supplied for <code>timeout</code> is less
+	 *                then 0
+	 * @since 1.6
+	 */
+	public synchronized boolean isValid(int timeout) throws SQLException {
+		try {
+			return ((java.sql.Connection) this.mc).isValid(timeout);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return false; // never reached, but compiler can't tell
+	}
+
+	public void setClientInfo(Properties properties)
+			throws SQLClientInfoException {
+		try {
+			checkClosed();
+
+			((java.sql.Connection) this.mc).setClientInfo(properties);
+		} catch (SQLException sqlException) {
+			try {
+				checkAndFireConnectionError(sqlException);
+			} catch (SQLException sqlEx2) {
+				SQLClientInfoException clientEx = new SQLClientInfoException();
+				clientEx.initCause(sqlEx2);
+
+				throw clientEx;
+			}
+		}
+	}
+
+	public void setClientInfo(String name, String value)
+			throws SQLClientInfoException {
+		try {
+			checkClosed();
+
+			((java.sql.Connection) this.mc).setClientInfo(name, value);
+		} catch (SQLException sqlException) {
+			try {
+				checkAndFireConnectionError(sqlException);
+			} catch (SQLException sqlEx2) {
+				SQLClientInfoException clientEx = new SQLClientInfoException();
+				clientEx.initCause(sqlEx2);
+
+				throw clientEx;
+			}
+		}
+	}
+
+	/**
+	 * Returns true if this either implements the interface argument or is
+	 * directly or indirectly a wrapper for an object that does. Returns false
+	 * otherwise. If this implements the interface then return true, else if
+	 * this is a wrapper then return the result of recursively calling
+	 * <code>isWrapperFor</code> on the wrapped object. If this does not
+	 * implement the interface and is not a wrapper, return false. This method
+	 * should be implemented as a low-cost operation compared to
+	 * <code>unwrap</code> so that callers can use this method to avoid
+	 * expensive <code>unwrap</code> calls that may fail. If this method
+	 * returns true then calling <code>unwrap</code> with the same argument
+	 * should succeed.
+	 * 
+	 * @param interfaces
+	 *            a Class defining an interface.
+	 * @return true if this implements the interface or directly or indirectly
+	 *         wraps an object that does.
+	 * @throws java.sql.SQLException
+	 *             if an error occurs while determining whether this is a
+	 *             wrapper for an object with the given interface.
+	 * @since 1.6
+	 */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+		checkClosed();
+
+		boolean isInstance = iface.isInstance(this);
+
+		if (isInstance) {
+			return true;
+		}
+
+		return (iface.getName().equals("com.mysql.jdbc.Connection") || 
+				iface.getName().equals("com.mysql.jdbc.ConnectionProperties"));
+	}
+
+	/**
+	 * Returns an object that implements the given interface to allow access to
+	 * non-standard methods, or standard methods not exposed by the proxy. The
+	 * result may be either the object found to implement the interface or a
+	 * proxy for that object. If the receiver implements the interface then that
+	 * is the object. If the receiver is a wrapper and the wrapped object
+	 * implements the interface then that is the object. Otherwise the object is
+	 * the result of calling <code>unwrap</code> recursively on the wrapped
+	 * object. If the receiver is not a wrapper and does not implement the
+	 * interface, then an <code>SQLException</code> is thrown.
+	 * 
+	 * @param iface
+	 *            A Class defining an interface that the result must implement.
+	 * @return an object that implements the interface. May be a proxy for the
+	 *         actual implementing object.
+	 * @throws java.sql.SQLException
+	 *             If no object found that implements the interface
+	 * @since 1.6
+	 */
+	public synchronized <T> T unwrap(java.lang.Class<T> iface)
+			throws java.sql.SQLException {
+		try {
+			if ("java.sql.Connection".equals(iface.getName())
+					|| "java.sql.Wrapper.class".equals(iface.getName())) {
+				return iface.cast(this);
+			}
+			
+			if (unwrappedInterfaces == null) {
+				unwrappedInterfaces = new HashMap();
+			}
+			
+			Object cachedUnwrapped = unwrappedInterfaces.get(iface);
+			
+			if (cachedUnwrapped == null) {
+				cachedUnwrapped = Proxy.newProxyInstance(this.mc.getClass()
+						.getClassLoader(), new Class[] { iface },
+						new ConnectionErrorFiringInvocationHandler(this.mc));
+				unwrappedInterfaces.put(iface, cachedUnwrapped);
+			}
+			
+			return iface.cast(cachedUnwrapped);
+		} catch (ClassCastException cce) {
+			throw SQLError.createSQLException("Unable to unwrap to "
+					+ iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+	
+	/**
+	 * @see java.sql.Connection#createBlob()
+	 */
+	public Blob createBlob() throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).createBlob();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	/**
+	 * @see java.sql.Connection#createClob()
+	 */
+	public Clob createClob() throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).createClob();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+
+	/**
+	 * @see java.sql.Connection#createNClob()
+	 */
+	public NClob createNClob() throws SQLException {
+		checkClosed();
+
+		try {
+			return ((java.sql.Connection) this.mc).createNClob();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // never reached, but compiler can't tell
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4PreparedStatementWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,515 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.RowId;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper;
+import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection;
+
+/**
+ */
+public class JDBC4PreparedStatementWrapper extends PreparedStatementWrapper {
+
+	public JDBC4PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
+			PreparedStatement toWrap) {
+		super(c, conn, toWrap);
+	}
+	
+	public void close() throws SQLException {
+		try {
+			super.close();
+		} finally {
+			this.unwrappedInterfaces = null;
+		}
+	}
+	
+	public boolean isClosed() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.isClosed();
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return false; // never get here - compiler can't tell
+	}
+	
+	public void setPoolable(boolean poolable) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setPoolable(poolable);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public boolean isPoolable() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.isPoolable();
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return false; // never get here - compiler can't tell
+	}
+    
+	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public void setNClob(int parameterIndex, NClob value) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+						value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	public void setSQLXML(int parameterIndex, SQLXML xmlObject)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setSQLXML(parameterIndex,
+						xmlObject);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	
+	public void setNString(int parameterIndex,
+            String value)
+            throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNString(parameterIndex,
+						value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+            
+    public void setNCharacterStream(int parameterIndex,
+                    Reader value,
+                    long length)
+                    throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex,
+						value, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+
+    public void setClob(int parameterIndex,
+            Reader reader,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+						reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}	
+    }
+    
+    public void setBlob(int parameterIndex,
+            InputStream inputStream,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+						inputStream, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setNClob(int parameterIndex,
+            Reader reader,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+						reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setAsciiStream(int parameterIndex,
+            InputStream x,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex,
+						x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setBinaryStream(int parameterIndex,
+            InputStream x,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex,
+						x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setCharacterStream(int parameterIndex,
+            Reader reader,
+            long length)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex,
+						reader, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setAsciiStream(int parameterIndex,
+            InputStream x)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setAsciiStream(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setBinaryStream(int parameterIndex,
+            InputStream x)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBinaryStream(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setCharacterStream(int parameterIndex,
+            Reader reader)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setCharacterStream(parameterIndex,
+						reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    	
+    }
+    
+    public void setNCharacterStream(int parameterIndex,
+            Reader value)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNCharacterStream(parameterIndex,
+						value);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    	
+    }
+    
+    public void setClob(int parameterIndex,
+            Reader reader)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+						reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    	
+    }
+    
+    public void setBlob(int parameterIndex,
+            InputStream inputStream)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+						inputStream);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+    
+    public void setNClob(int parameterIndex,
+            Reader reader)
+            throws SQLException {
+    	try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+						reader);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+    }
+	
+	/**
+	 * Returns true if this either implements the interface argument or is
+	 * directly or indirectly a wrapper for an object that does. Returns false
+	 * otherwise. If this implements the interface then return true, else if
+	 * this is a wrapper then return the result of recursively calling
+	 * <code>isWrapperFor</code> on the wrapped object. If this does not
+	 * implement the interface and is not a wrapper, return false. This method
+	 * should be implemented as a low-cost operation compared to
+	 * <code>unwrap</code> so that callers can use this method to avoid
+	 * expensive <code>unwrap</code> calls that may fail. If this method
+	 * returns true then calling <code>unwrap</code> with the same argument
+	 * should succeed.
+	 * 
+	 * @param interfaces
+	 *            a Class defining an interface.
+	 * @return true if this implements the interface or directly or indirectly
+	 *         wraps an object that does.
+	 * @throws java.sql.SQLException
+	 *             if an error occurs while determining whether this is a
+	 *             wrapper for an object with the given interface.
+	 * @since 1.6
+	 */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+
+		boolean isInstance = iface.isInstance(this);
+
+		if (isInstance) {
+			return true;
+		}
+
+		String interfaceClassName = iface.getName();
+		
+		return (interfaceClassName.equals("com.mysql.jdbc.Statement")
+				|| interfaceClassName.equals("java.sql.Statement")
+				|| interfaceClassName.equals("java.sql.PreparedStatement")
+				|| interfaceClassName.equals("java.sql.Wrapper"));
+	}
+
+	/**
+	 * Returns an object that implements the given interface to allow access to
+	 * non-standard methods, or standard methods not exposed by the proxy. The
+	 * result may be either the object found to implement the interface or a
+	 * proxy for that object. If the receiver implements the interface then that
+	 * is the object. If the receiver is a wrapper and the wrapped object
+	 * implements the interface then that is the object. Otherwise the object is
+	 * the result of calling <code>unwrap</code> recursively on the wrapped
+	 * object. If the receiver is not a wrapper and does not implement the
+	 * interface, then an <code>SQLException</code> is thrown.
+	 * 
+	 * @param iface
+	 *            A Class defining an interface that the result must implement.
+	 * @return an object that implements the interface. May be a proxy for the
+	 *         actual implementing object.
+	 * @throws java.sql.SQLException
+	 *             If no object found that implements the interface
+	 * @since 1.6
+	 */
+	public synchronized <T> T unwrap(java.lang.Class<T> iface)
+			throws java.sql.SQLException {
+		try {
+			if ("java.sql.Statement".equals(iface.getName()) 
+					|| "java.sql.PreparedStatement".equals(iface.getName()) 
+					|| "java.sql.Wrapper.class".equals(iface.getName())) {
+				return iface.cast(this);
+			}
+			
+			if (unwrappedInterfaces == null) {
+				unwrappedInterfaces = new HashMap();
+			}
+			
+			Object cachedUnwrapped = unwrappedInterfaces.get(iface);
+			
+			if (cachedUnwrapped == null) {
+				if (cachedUnwrapped == null) {
+					cachedUnwrapped = Proxy.newProxyInstance(
+							this.wrappedStmt.getClass().getClassLoader(), 
+							new Class[] { iface },
+							new ConnectionErrorFiringInvocationHandler(this.wrappedStmt));
+					unwrappedInterfaces.put(iface, cachedUnwrapped);
+				}
+				unwrappedInterfaces.put(iface, cachedUnwrapped);
+			}
+			
+			return iface.cast(cachedUnwrapped);
+		} catch (ClassCastException cce) {
+			throw SQLError.createSQLException("Unable to unwrap to "
+					+ iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+}

Added: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/JDBC4StatementWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,198 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.NClob;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper;
+import com.mysql.jdbc.jdbc2.optional.MysqlPooledConnection;
+
+/**
+ */
+public class JDBC4StatementWrapper extends StatementWrapper {
+
+	public JDBC4StatementWrapper(ConnectionWrapper c, 
+			MysqlPooledConnection conn,
+			Statement toWrap) {
+		super(c, conn, toWrap);
+	}
+	
+	public void close() throws SQLException {
+		try {
+			super.close();
+		} finally {
+			this.unwrappedInterfaces = null;
+		}
+	}
+	
+	public boolean isClosed() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.isClosed();
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return false; // We never get here, compiler can't tell
+	}
+	
+	public void setPoolable(boolean poolable) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setPoolable(poolable);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+	
+	public boolean isPoolable() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.isPoolable();
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		
+		return false; // We never get here, compiler can't tell
+	}
+    
+	/**
+	 * Returns true if this either implements the interface argument or is
+	 * directly or indirectly a wrapper for an object that does. Returns false
+	 * otherwise. If this implements the interface then return true, else if
+	 * this is a wrapper then return the result of recursively calling
+	 * <code>isWrapperFor</code> on the wrapped object. If this does not
+	 * implement the interface and is not a wrapper, return false. This method
+	 * should be implemented as a low-cost operation compared to
+	 * <code>unwrap</code> so that callers can use this method to avoid
+	 * expensive <code>unwrap</code> calls that may fail. If this method
+	 * returns true then calling <code>unwrap</code> with the same argument
+	 * should succeed.
+	 * 
+	 * @param interfaces
+	 *            a Class defining an interface.
+	 * @return true if this implements the interface or directly or indirectly
+	 *         wraps an object that does.
+	 * @throws java.sql.SQLException
+	 *             if an error occurs while determining whether this is a
+	 *             wrapper for an object with the given interface.
+	 * @since 1.6
+	 */
+	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+
+		boolean isInstance = iface.isInstance(this);
+
+		if (isInstance) {
+			return true;
+		}
+
+		String interfaceClassName = iface.getName();
+		
+		return (interfaceClassName.equals("com.mysql.jdbc.Statement")
+				|| interfaceClassName.equals("java.sql.Statement")
+				|| interfaceClassName.equals("java.sql.Wrapper"));
+	}
+
+	/**
+	 * Returns an object that implements the given interface to allow access to
+	 * non-standard methods, or standard methods not exposed by the proxy. The
+	 * result may be either the object found to implement the interface or a
+	 * proxy for that object. If the receiver implements the interface then that
+	 * is the object. If the receiver is a wrapper and the wrapped object
+	 * implements the interface then that is the object. Otherwise the object is
+	 * the result of calling <code>unwrap</code> recursively on the wrapped
+	 * object. If the receiver is not a wrapper and does not implement the
+	 * interface, then an <code>SQLException</code> is thrown.
+	 * 
+	 * @param iface
+	 *            A Class defining an interface that the result must implement.
+	 * @return an object that implements the interface. May be a proxy for the
+	 *         actual implementing object.
+	 * @throws java.sql.SQLException
+	 *             If no object found that implements the interface
+	 * @since 1.6
+	 */
+	public synchronized <T> T unwrap(java.lang.Class<T> iface)
+			throws java.sql.SQLException {
+		try {
+			if ("java.sql.Statement".equals(iface.getName())
+					|| "java.sql.Wrapper.class".equals(iface.getName())) {
+				return iface.cast(this);
+			}
+			
+			if (unwrappedInterfaces == null) {
+				unwrappedInterfaces = new HashMap();
+			}
+			
+			Object cachedUnwrapped = unwrappedInterfaces.get(iface);
+			
+			if (cachedUnwrapped == null) {
+				cachedUnwrapped = Proxy.newProxyInstance(
+						this.wrappedStmt.getClass().getClassLoader(), 
+						new Class[] { iface },
+						new ConnectionErrorFiringInvocationHandler(this.wrappedStmt));
+				unwrappedInterfaces.put(iface, cachedUnwrapped);
+			}
+			
+			return iface.cast(cachedUnwrapped);
+		} catch (ClassCastException cce) {
+			throw SQLError.createSQLException("Unable to unwrap to "
+					+ iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,8 +24,9 @@
  */
 package com.mysql.jdbc.jdbc2.optional;
 
-import com.mysql.jdbc.ConnectionProperties;
+import com.mysql.jdbc.ConnectionPropertiesImpl;
 import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
 
 import java.io.PrintWriter;
 import java.io.Serializable;
@@ -46,8 +47,8 @@
  * 
  * @author Mark Matthews
  */
-public class MysqlDataSource extends ConnectionProperties implements
-		DataSource, Referenceable, Serializable {
+public class MysqlDataSource extends ConnectionPropertiesImpl implements
+		DataSource, Referenceable, Serializable  {
 	/** The driver to create connections with */
 	protected static com.mysql.jdbc.Driver mysqlDriver = null;
 
@@ -424,4 +425,12 @@
 
 		return mysqlDriver.connect(jdbcUrlToUse, props);
 	}
+//
+//	public boolean isWrapperFor(Class<?> iface) throws SQLException {
+//		throw new NotYetImplementedException();
+//	}
+//
+//	public <T> T unwrap(Class<T> iface) throws SQLException {
+//		throw new NotYetImplementedException();
+//	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2006 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -51,6 +51,12 @@
 	protected final static String POOL_DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";
 
 	/**
+	 * The class name for a MysqlXADataSource
+	 */
+	 
+	protected final static String XA_DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource";
+	
+	/**
 	 * DOCUMENT ME!
 	 * 
 	 * @param refObj
@@ -72,7 +78,8 @@
 
 		if ((className != null)
 				&& (className.equals(DATA_SOURCE_CLASS_NAME) || className
-						.equals(POOL_DATA_SOURCE_CLASS_NAME))) {
+						.equals(POOL_DATA_SOURCE_CLASS_NAME) ||
+						className.equals(XA_DATA_SOURCE_CLASS_NAME))) {
 			MysqlDataSource dataSource = null;
 
 			try {
@@ -141,4 +148,4 @@
 		
 		return asString;
 	}
-}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -19,9 +19,8 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+ */
 
- 
- */
 package com.mysql.jdbc.jdbc2.optional;
 
 import java.sql.Connection;
@@ -34,7 +33,10 @@
 import javax.sql.ConnectionEventListener;
 import javax.sql.PooledConnection;
 
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.Constants;
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
 
 /**
  * This class is used to wrap and return a physical connection within a logical
@@ -142,10 +144,12 @@
 			}
 
 			if (resetServerState) {
-				((com.mysql.jdbc.Connection) this.physicalConn).resetServerState();
+				this.physicalConn.resetServerState();
 			}
 
-			this.logicalHandle = new ConnectionWrapper(this, this.physicalConn, forXa);
+			this.logicalHandle = ConnectionWrapper.getInstance(this, 
+					(ConnectionImpl)this.physicalConn, 
+					forXa);
 		} catch (SQLException sqlException) {
 			callListener(CONNECTION_ERROR_EVENT, sqlException);
 
@@ -209,4 +213,36 @@
 			}
 		}
 	}
+	
+//	/**
+//	 * Registers a <code>StatementEventListener</code> with this <code>PooledConnection</code> object.  Components that 
+//	 * wish to be notified when  <code>PreparedStatement</code>s created by the
+//         * connection are closed or are detected to be invalid may use this method 
+//         * to register a <code>StatementEventListener</code> with this <code>PooledConnection</code> object.
+//	 * <p>
+//	 * @param listener	an component which implements the <code>StatementEventListener</code> 
+//	 * 					interface that is to be registered with this <code>PooledConnection</code> object
+//	 * <p>
+//	 * @since 1.6
+//	 */
+//	public void addStatementEventListener(StatementEventListener listener) {
+//		throw new NotYetImplementedException();
+//	}
+//	
+//	/**
+//	 * Removes the specified <code>StatementEventListener</code> from the list of 
+//	 * components that will be notified when the driver detects that a 
+//	 * <code>PreparedStatement</code> has been closed or is invalid.
+//	 * <p> 
+//	 * @param listener	the component which implements the
+//	 * 					<code>StatementEventListener</code> interface that was previously 
+//	 * 					registered with this <code>PooledConnection</code> object
+//	 * <p>
+//	 * @since 1.6
+//	 */
+//	public void removeStatementEventListener(StatementEventListener listener) {
+//		throw new NotYetImplementedException();
+//	}
+
+
 }
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -37,6 +37,8 @@
 import javax.transaction.xa.XAResource;
 import javax.transaction.xa.Xid;
 
+import com.mysql.jdbc.ConnectionImpl;
+import com.mysql.jdbc.Constants;
 import com.mysql.jdbc.log.Log;
 
 /*
@@ -61,21 +63,23 @@
 public class MysqlXAConnection extends MysqlPooledConnection implements
 		XAConnection, XAResource {
 
-	private com.mysql.jdbc.Connection underlyingConnection;
+	private com.mysql.jdbc.ConnectionImpl underlyingConnection;
 
 	private final static Map MYSQL_ERROR_CODES_TO_XA_ERROR_CODES;
 
 	private Log log;
 
+	protected boolean logXaCommands;
+	
 	static {
 		HashMap temp = new HashMap();
 
-		temp.put(new Integer(1397), new Integer(XAException.XAER_NOTA));
-		temp.put(new Integer(1398), new Integer(XAException.XAER_INVAL));
-		temp.put(new Integer(1399), new Integer(XAException.XAER_RMFAIL));
-		temp.put(new Integer(1400), new Integer(XAException.XAER_OUTSIDE));
-		temp.put(new Integer(1401), new Integer(XAException.XAER_RMERR));
-		temp.put(new Integer(1402), new Integer(XAException.XA_RBROLLBACK));
+		temp.put(Constants.integerValueOf(1397), Constants.integerValueOf(XAException.XAER_NOTA));
+		temp.put(Constants.integerValueOf(1398), Constants.integerValueOf(XAException.XAER_INVAL));
+		temp.put(Constants.integerValueOf(1399), Constants.integerValueOf(XAException.XAER_RMFAIL));
+		temp.put(Constants.integerValueOf(1400), Constants.integerValueOf(XAException.XAER_OUTSIDE));
+		temp.put(Constants.integerValueOf(1401), Constants.integerValueOf(XAException.XAER_RMERR));
+		temp.put(Constants.integerValueOf(1402), Constants.integerValueOf(XAException.XA_RBROLLBACK));
 
 		MYSQL_ERROR_CODES_TO_XA_ERROR_CODES = Collections.unmodifiableMap(temp);
 	}
@@ -83,11 +87,12 @@
 	/**
 	 * @param connection
 	 */
-	public MysqlXAConnection(com.mysql.jdbc.Connection connection)
+	public MysqlXAConnection(ConnectionImpl connection, boolean logXaCommands)
 			throws SQLException {
 		super(connection);
 		this.underlyingConnection = connection;
 		this.log = connection.getLog();
+		this.logXaCommands = logXaCommands;
 	}
 
 	/**
@@ -528,7 +533,7 @@
 		Statement stmt = null;
 
 		try {
-			if (this.log.isDebugEnabled()) {
+			if (this.logXaCommands) {
 				this.log.logDebug("Executing XA statement: " + command);
 			}
 
@@ -556,7 +561,7 @@
 	protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) {
 
 		Integer xaCode = (Integer) MYSQL_ERROR_CODES_TO_XA_ERROR_CODES
-				.get(new Integer(sqlEx.getErrorCode()));
+				.get(Constants.integerValueOf(sqlEx.getErrorCode()));
 
 		if (xaCode != null) {
 			return new MysqlXAException(xaCode.intValue(), sqlEx.getMessage(), null);

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -67,9 +67,9 @@
 	private XAConnection wrapConnection(Connection conn) throws SQLException {
 		if (getPinGlobalTxToPhysicalConnection() || 
 				((com.mysql.jdbc.Connection)conn).getPinGlobalTxToPhysicalConnection()) {
-			return new SuspendableXAConnection((com.mysql.jdbc.Connection) conn);
+			return new SuspendableXAConnection((com.mysql.jdbc.ConnectionImpl) conn);
 		}
 		
-		return new MysqlXAConnection((com.mysql.jdbc.Connection) conn);
+		return new MysqlXAConnection((com.mysql.jdbc.ConnectionImpl) conn, getLogXaCommands());
 	}
 }
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -25,10 +25,13 @@
 package com.mysql.jdbc.jdbc2.optional;
 
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.Util;
+import com.mysql.jdbc.exceptions.NotYetImplementedException;
 
 import java.io.InputStream;
 import java.io.Reader;
 
+import java.lang.reflect.Constructor;
 import java.math.BigDecimal;
 
 import java.net.URL;
@@ -37,12 +40,16 @@
 import java.sql.Blob;
 import java.sql.Clob;
 import java.sql.Date;
+//import java.sql.NClob;
 import java.sql.ParameterMetaData;
 import java.sql.PreparedStatement;
 import java.sql.Ref;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
+import java.sql.Statement;
+//import java.sql.RowId;
 import java.sql.SQLException;
+//import java.sql.SQLXML;
 import java.sql.Time;
 import java.sql.Timestamp;
 
@@ -59,6 +66,42 @@
  */
 public class PreparedStatementWrapper extends StatementWrapper implements
 		PreparedStatement {
+	private static final Constructor JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR = Class.forName(
+						"com.mysql.jdbc.jdbc2.optional.JDBC4PreparedStatementWrapper").getConstructor(
+						new Class[] { ConnectionWrapper.class, 
+								MysqlPooledConnection.class, 
+								PreparedStatement.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR = null;
+		}
+	}
+	
+	protected static PreparedStatementWrapper getInstance(ConnectionWrapper c, 
+			MysqlPooledConnection conn,
+			PreparedStatement toWrap) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new PreparedStatementWrapper(c, 
+					conn, toWrap);
+		}
+
+		return (PreparedStatementWrapper) Util.handleNewInstance(
+				JDBC_4_PREPARED_STATEMENT_WRAPPER_CTOR,
+				new Object[] {c, 
+						conn, toWrap });
+	}
+	
 	PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
 			PreparedStatement toWrap) {
 		super(c, conn, toWrap);
@@ -507,7 +550,7 @@
 		try {
 			if (this.wrappedStmt != null) {
 				((PreparedStatement) this.wrappedStmt).setObject(
-						parameterIndex, x, scale);
+						parameterIndex, x, targetSqlType, scale);
 			} else {
 				throw SQLError.createSQLException(
 						"No operations allowed after statement closed",
@@ -795,7 +838,7 @@
 		}
 
 		return false; // we actually never get here, but the compiler can't
-						// figure
+		// figure
 
 		// that out
 	}
@@ -811,7 +854,7 @@
 				ResultSet rs = ((PreparedStatement) this.wrappedStmt)
 						.executeQuery();
 
-				((com.mysql.jdbc.ResultSet) rs).setWrapperStatement(this);
+				((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this);
 
 				return rs;
 			}
@@ -824,7 +867,7 @@
 		}
 
 		return null; // we actually never get here, but the compiler can't
-						// figure
+		// figure
 
 		// that out
 	}
@@ -851,4 +894,342 @@
 
 		// that out
 	}
+//
+//	public void setAsciiStream(int parameterIndex, InputStream x)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setAsciiStream(
+//						parameterIndex, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setAsciiStream(int parameterIndex, InputStream x, long length)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setAsciiStream(
+//						parameterIndex, x, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBinaryStream(int parameterIndex, InputStream x)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setBinaryStream(
+//						parameterIndex, x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBinaryStream(int parameterIndex, InputStream x, long length)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setBinaryStream(
+//						parameterIndex, x, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(int parameterIndex, InputStream inputStream)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+//						inputStream);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setBlob(int parameterIndex, InputStream inputStream, long length)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+//						inputStream, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setCharacterStream(int parameterIndex, Reader reader)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setCharacterStream(
+//						parameterIndex, reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setCharacterStream(int parameterIndex, Reader reader,
+//			long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setCharacterStream(
+//						parameterIndex, reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(int parameterIndex, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+//						reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setClob(int parameterIndex, Reader reader, long length)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+//						reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNCharacterStream(int parameterIndex, Reader value)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setNCharacterStream(
+//						parameterIndex, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNCharacterStream(int parameterIndex, Reader value,
+//			long length) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setNCharacterStream(
+//						parameterIndex, value, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(int parameterIndex, NClob value) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+//						value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(int parameterIndex, Reader reader) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+//						reader);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNClob(int parameterIndex, Reader reader, long length)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setNClob(parameterIndex,
+//						reader, length);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setNString(int parameterIndex, String value)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setNString(
+//						parameterIndex, value);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setRowId(int parameterIndex, RowId x) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setRowId(parameterIndex,
+//						x);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public void setSQLXML(int parameterIndex, SQLXML xmlObject)
+//			throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setSQLXML(
+//						parameterIndex, xmlObject);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public boolean isClosed() throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((PreparedStatement) this.wrappedStmt).isClosed();
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//
+//		return true;
+//	}
+//
+//	public boolean isPoolable() throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				return ((PreparedStatement) this.wrappedStmt).isPoolable();
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//
+//		return false;
+//	}
+//
+//	public void setPoolable(boolean poolable) throws SQLException {
+//		try {
+//			if (this.wrappedStmt != null) {
+//				((PreparedStatement) this.wrappedStmt).setPoolable(poolable);
+//			} else {
+//				throw SQLError.createSQLException(
+//						"No operations allowed after statement closed",
+//						SQLError.SQL_STATE_GENERAL_ERROR);
+//			}
+//		} catch (SQLException sqlEx) {
+//			checkAndFireConnectionError(sqlEx);
+//		}
+//	}
+//
+//	public boolean isWrapperFor(Class arg0) throws SQLException {
+//		throw new NotYetImplementedException();
+//	}
+//
+//	public Object unwrap(Class arg0) throws SQLException {
+//		throw new NotYetImplementedException();
+//	}
 }

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,8 +24,11 @@
  */
 package com.mysql.jdbc.jdbc2.optional;
 
+import com.mysql.jdbc.ConnectionImpl;
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.Util;
 
+import java.lang.reflect.Constructor;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -42,11 +45,47 @@
  *          Exp $
  */
 public class StatementWrapper extends WrapperBase implements Statement {
+	private static final Constructor JDBC_4_STATEMENT_WRAPPER_CTOR;
+	
+	static {
+		if (Util.isJdbc4()) {
+			try {
+				JDBC_4_STATEMENT_WRAPPER_CTOR = Class.forName(
+						"com.mysql.jdbc.jdbc2.optional.JDBC4StatementWrapper").getConstructor(
+						new Class[] { ConnectionWrapper.class, 
+								MysqlPooledConnection.class, 
+								Statement.class });
+			} catch (SecurityException e) {
+				throw new RuntimeException(e);
+			} catch (NoSuchMethodException e) {
+				throw new RuntimeException(e);
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		} else {
+			JDBC_4_STATEMENT_WRAPPER_CTOR = null;
+		}
+	}
+	
+	protected static StatementWrapper getInstance(ConnectionWrapper c, 
+			MysqlPooledConnection conn,
+			Statement toWrap) throws SQLException {
+		if (!Util.isJdbc4()) {
+			return new StatementWrapper(c, 
+					conn, toWrap);
+		}
+
+		return (StatementWrapper) Util.handleNewInstance(
+				JDBC_4_STATEMENT_WRAPPER_CTOR,
+				new Object[] {c, 
+						conn, toWrap });
+	}
+	
 	protected Statement wrappedStmt;
 
 	protected ConnectionWrapper wrappedConn;
 
-	protected StatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
+	public StatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
 			Statement toWrap) {
 		this.pooledConnection = conn;
 		this.wrappedStmt = toWrap;
@@ -384,7 +423,7 @@
 			if (this.wrappedStmt != null) {
 				ResultSet rs = this.wrappedStmt.getResultSet();
 
-				((com.mysql.jdbc.ResultSet) rs).setWrapperStatement(this);
+				((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this);
 
 				return rs;
 			}
@@ -703,7 +742,7 @@
 			if (this.wrappedStmt != null) {
 
 				ResultSet rs = this.wrappedStmt.executeQuery(sql);
-				((com.mysql.jdbc.ResultSet) rs).setWrapperStatement(this);
+				((com.mysql.jdbc.ResultSetInternalMethods) rs).setWrapperStatement(this);
 
 				return rs;
 			}

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -11,11 +11,12 @@
 import javax.transaction.xa.Xid;
 
 import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.ConnectionImpl;
 
 public class SuspendableXAConnection extends MysqlPooledConnection implements
 XAConnection, XAResource {
 
-	public SuspendableXAConnection(Connection connection) {
+	public SuspendableXAConnection(ConnectionImpl connection) {
 		super(connection);
 		this.underlyingConnection = connection;
 	}
@@ -28,9 +29,9 @@
 	private XAConnection currentXAConnection;
 	private XAResource currentXAResource;
 	
-	private Connection underlyingConnection;
+	private ConnectionImpl underlyingConnection;
 	
-	private static synchronized XAConnection findConnectionForXid(Connection connectionToWrap, Xid xid) 
+	private static synchronized XAConnection findConnectionForXid(ConnectionImpl connectionToWrap, Xid xid) 
 		throws SQLException {
 		// TODO: check for same GTRID, but different BQUALs...MySQL doesn't allow this yet
 		
@@ -40,7 +41,8 @@
 		XAConnection conn = (XAConnection)XIDS_TO_PHYSICAL_CONNECTIONS.get(xid);
 
 		if (conn == null) {
-			conn = new MysqlXAConnection(connectionToWrap);
+			conn = new MysqlXAConnection(connectionToWrap,
+					connectionToWrap.getLogXaCommands());
 		}
 		
 		return conn;

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -24,7 +24,12 @@
  */
 package com.mysql.jdbc.jdbc2.optional;
 
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 import java.sql.SQLException;
+import java.util.Map;
 
 import com.mysql.jdbc.SQLError;
 
@@ -33,7 +38,7 @@
  * 
  * @author Mark matthews
  * 
- * @version $Id: WrapperBase.java 3726 2005-05-19 15:52:24Z mmatthews $
+ * @version $Id: WrapperBase.java 6407 2007-04-23 21:40:24Z mmatthews $
  */
 abstract class WrapperBase {
 	protected MysqlPooledConnection pooledConnection;
@@ -58,4 +63,64 @@
 
 		throw sqlEx;
 	}
+	
+	protected Map unwrappedInterfaces = null;
+
+	protected class ConnectionErrorFiringInvocationHandler implements InvocationHandler {
+		Object invokeOn = null;
+		
+		public ConnectionErrorFiringInvocationHandler(Object toInvokeOn) {
+			invokeOn = toInvokeOn;
+		}
+		
+		public Object invoke(Object proxy, Method method,
+				Object[] args) throws Throwable {
+			Object result = null;
+
+			try {
+				result = method.invoke(invokeOn, args);
+				
+				if (result != null) {
+					result = proxyIfInterfaceIsJdbc(result, 
+							result.getClass());
+				}
+			} catch (InvocationTargetException e) {
+				if (e.getTargetException() instanceof SQLException) {
+					checkAndFireConnectionError((SQLException) e
+							.getTargetException());
+				} else {
+					throw e;
+				}
+			}
+
+			return result;
+		}
+		
+		/**
+		 * Recursively checks for interfaces on the given object to determine
+		 * if it implements a java.sql interface, and if so, proxies the 
+		 * instance so that we can catch and fire SQL errors.
+		 * @param toProxy
+		 * @param clazz
+		 * @return
+		 */
+		private Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) {
+			Class[] interfaces = clazz.getInterfaces();
+			
+			for (int i = 0; i < interfaces.length; i++) {
+				String packageName = interfaces[i].getPackage().getName();
+				
+				if ("java.sql".equals(packageName) || 
+						"javax.sql".equals(packageName)) {
+					return Proxy.newProxyInstance(toProxy.getClass()
+							.getClassLoader(), interfaces,
+							new ConnectionErrorFiringInvocationHandler(toProxy));
+				}
+				
+				return proxyIfInterfaceIsJdbc(toProxy, interfaces[i]);
+			}
+			
+			return toProxy;
+		}
+	}
 }
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/log/LogUtils.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/log/LogUtils.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/log/LogUtils.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2005 MySQL AB
+ Copyright (C) 2005-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -83,7 +83,13 @@
 				appendLocationInfo = true;
 				
 				break;
-
+				
+			case ProfilerEvent.TYPE_SLOW_QUERY:
+				msgBuf.append("SLOW QUERY");
+				appendLocationInfo = false;
+				
+				break;
+				
 			default:
 				msgBuf.append("UNKNOWN");
 			}
@@ -91,8 +97,10 @@
 			msgBuf.append("] ");
 			msgBuf.append(findCallingClassAndMethod(locationException));
 			msgBuf.append(" duration: ");
-			msgBuf.append(evt.getEventDurationMillis());
-			msgBuf.append(" ms, connection-id: ");
+			msgBuf.append(evt.getEventDuration());
+			msgBuf.append(" ");
+			msgBuf.append(evt.getDurationUnits());
+			msgBuf.append(", connection-id: ");
 			msgBuf.append(evt.getConnectionId());
 			msgBuf.append(", statement-id: ");
 			msgBuf.append(evt.getStatementId());
@@ -117,9 +125,8 @@
 		}
 		
 		return possibleProfilerEvent;
-
 	}
-
+	
 	public static String findCallingClassAndMethod(Throwable t) {
 		String stackTraceAsString = Util.stackTraceToString(t);
 

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfileEventSink.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfileEventSink.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfileEventSink.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -19,9 +19,8 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
-
-
  */
+
 package com.mysql.jdbc.profiler;
 
 import com.mysql.jdbc.Connection;
@@ -89,5 +88,4 @@
 			throw new RuntimeException("Unable to get logger from connection");
 		}
 	}
-
-}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfilerEvent.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfilerEvent.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/profiler/ProfilerEvent.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -19,9 +19,8 @@
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+ */
 
-
- */
 package com.mysql.jdbc.profiler;
 
 import java.util.Date;
@@ -64,6 +63,11 @@
 	public static final byte TYPE_FETCH = 5;
 
 	/**
+	 * Profiler event for slow query
+	 */
+	public static final byte TYPE_SLOW_QUERY = 6;
+	
+	/**
 	 * Type of event
 	 */
 	protected byte eventType;
@@ -91,9 +95,14 @@
 	/**
 	 * How long did the event last?
 	 */
-	protected int eventDurationMillis;
+	protected long eventDuration;
 
 	/**
+	 * What units was the duration measured in?
+	 */
+	protected String durationUnits;
+	
+	/**
 	 * The hostname the event occurred on (as an index into a dictionary, used
 	 * by 'remote' profilers for efficiency)?
 	 */
@@ -165,7 +174,7 @@
 	 */
 	public ProfilerEvent(byte eventType, String hostName, String catalog,
 			long connectionId, int statementId, int resultSetId,
-			long eventCreationTime, int eventDurationMillis,
+			long eventCreationTime, long eventDuration, String durationUnits,
 			String eventCreationPointDesc, Throwable eventCreationPoint,
 			String message) {
 		this.eventType = eventType;
@@ -173,7 +182,8 @@
 		this.statementId = statementId;
 		this.resultSetId = resultSetId;
 		this.eventCreationTime = eventCreationTime;
-		this.eventDurationMillis = eventDurationMillis;
+		this.eventDuration = eventDuration;
+		this.durationUnits = durationUnits;
 		this.eventCreationPoint = eventCreationPoint;
 		this.eventCreationPointDesc = eventCreationPointDesc;
 		this.message = message;
@@ -225,6 +235,9 @@
 		case TYPE_WARN:
 			buf.append("WARN");
 			break;
+		case TYPE_SLOW_QUERY:
+			buf.append("SLOW QUERY");
+			break;
 		default:
 			buf.append("UNKNOWN");
 		}
@@ -232,7 +245,7 @@
 		buf.append(" created: ");
 		buf.append(new Date(this.eventCreationTime));
 		buf.append(" duration: ");
-		buf.append(this.eventDurationMillis);
+		buf.append(this.eventDuration);
 		buf.append(" connection: ");
 		buf.append(this.connectionId);
 		buf.append(" statement: ");
@@ -275,8 +288,16 @@
 		pos += 4;
 		long eventCreationTime = readLong(buf, pos);
 		pos += 8;
-		int eventDurationMillis = readInt(buf, pos);
+		long eventDuration = readLong(buf, pos);
 		pos += 4;
+		
+		byte[] eventDurationUnits = readBytes(buf, pos);
+		pos += 4;
+		
+		if (eventDurationUnits != null) {
+			pos += eventDurationUnits.length;
+		}
+		
 		int eventCreationPointIndex = readInt(buf, pos);
 		pos += 4;
 		byte[] eventCreationAsBytes = readBytes(buf, pos);
@@ -294,7 +315,8 @@
 		}
 
 		return new ProfilerEvent(eventType, "", "", connectionId, statementId,
-				resultSetId, eventCreationTime, eventDurationMillis,
+				resultSetId, eventCreationTime, eventDuration,
+				new String(eventDurationUnits, "ISO8859_1"),
 				new String(eventCreationAsBytes, "ISO8859_1"), null,
 				new String(message, "ISO8859_1"));
 	}
@@ -330,6 +352,15 @@
 		} else {
 			len += 4;
 		}
+		
+		byte[] durationUnitsAsBytes = null;
+		
+		if (durationUnits != null) {
+			durationUnitsAsBytes = this.durationUnits.getBytes("ISO8859_1");
+			len += (4 + durationUnitsAsBytes.length);
+		} else {
+			len += 4;
+		}
 
 		byte[] buf = new byte[len];
 
@@ -340,7 +371,8 @@
 		pos = writeInt(this.statementId, buf, pos);
 		pos = writeInt(this.resultSetId, buf, pos);
 		pos = writeLong(this.eventCreationTime, buf, pos);
-		pos = writeInt(this.eventDurationMillis, buf, pos);
+		pos = writeLong(this.eventDuration, buf, pos);
+		pos = writeBytes(durationUnitsAsBytes, buf, pos);
 		pos = writeInt(this.eventCreationPointIndex, buf, pos);
 
 		if (eventCreationAsBytes != null) {
@@ -459,11 +491,18 @@
 	 * 
 	 * @return the duration of the event in milliseconds
 	 */
-	public int getEventDurationMillis() {
-		return this.eventDurationMillis;
+	public long getEventDuration() {
+		return this.eventDuration;
 	}
 
 	/**
+	 * Returns the units for getEventDuration()
+	 */
+	public String getDurationUnits() {
+		return this.durationUnits;
+	}
+	
+	/**
 	 * Returns the event type flag
 	 * 
 	 * @return the event type flag
@@ -498,4 +537,4 @@
 	public String getMessage() {
 		return this.message;
 	}
-}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/util/PropertiesDocGenerator.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/util/PropertiesDocGenerator.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/util/PropertiesDocGenerator.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -26,13 +26,13 @@
 
 import java.sql.SQLException;
 
-import com.mysql.jdbc.ConnectionProperties;
+import com.mysql.jdbc.ConnectionPropertiesImpl;
 
 /**
  * Creates docbook table of connection properties from ConnectionProperties
  * class.
  */
-public class PropertiesDocGenerator extends ConnectionProperties {
+public class PropertiesDocGenerator extends ConnectionPropertiesImpl {
 
 	public static void main(String[] args) throws SQLException {
 		System.out.println(new PropertiesDocGenerator().exposeAsXml());

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/util/ServerController.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/util/ServerController.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/util/ServerController.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -28,7 +28,6 @@
 import java.io.IOException;
 
 import java.util.Iterator;
-import java.util.Locale;
 import java.util.Properties;
 
 import com.mysql.jdbc.StringUtils;

Modified: trunk/mysql-connector-java/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java
===================================================================
--- trunk/mysql-connector-java/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2005 MySQL AB
+ Copyright (C) 2005-2006 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -47,22 +47,26 @@
 		
 		String jdbcUrl = null;
 
-		
-		jdbcUrl = System.getProperty("com.mysql.jdbc.testsuite.url");
-		
-		
-		Connection conn = new NonRegisteringDriver().connect(jdbcUrl, null);
-
-		ResultSet rs = conn.createStatement().executeQuery("SELECT VERSION()");
-		rs.next();
-		String mysqlVersion = removeWhitespaceChars(rs.getString(1));
-
 		String jvmVersion = removeWhitespaceChars(System.getProperty("java.version"));
 		String jvmVendor = removeWhitespaceChars(System.getProperty("java.vendor"));
 		String osName = removeWhitespaceChars(System.getProperty("os.name"));
 		String osArch = removeWhitespaceChars(System.getProperty("os.arch"));
 		String osVersion = removeWhitespaceChars(System.getProperty("os.version"));
 		
+		jdbcUrl = System.getProperty("com.mysql.jdbc.testsuite.url");
+		
+		String mysqlVersion = "not-available";
+		
+		try {
+			Connection conn = new NonRegisteringDriver().connect(jdbcUrl, null);
+	
+			ResultSet rs = conn.createStatement().executeQuery("SELECT VERSION()");
+			rs.next();
+			mysqlVersion = removeWhitespaceChars(rs.getString(1));
+		} catch (Throwable t) {
+			mysqlVersion = "no-server-running-on-" + removeWhitespaceChars(jdbcUrl);
+		}
+
 		String jvmSubdirName = jvmVendor + "-" + jvmVersion;
 		String osSubdirName = osName + "-" + osArch + "-" + osVersion;
 		

Modified: trunk/mysql-connector-java/src/doc/sources/errorMapToDocbook.xsl
===================================================================
--- trunk/mysql-connector-java/src/doc/sources/errorMapToDocbook.xsl	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/doc/sources/errorMapToDocbook.xsl	2007-11-30 10:46:51 UTC (rev 4921)
@@ -12,6 +12,10 @@
 	<xsl:element name="table">
 		<xsl:element name="title">Mapping of MySQL Error Numbers to SQLStates</xsl:element>
     	<xsl:element name="tgroup"><xsl:attribute name="cols">4</xsl:attribute>
+                <xsl:element name="colspec"><xsl:attribute name="colwidth">20</xsl:attribute></xsl:element>
+                <xsl:element name="colspec"><xsl:attribute name="colwidth">40</xsl:attribute></xsl:element>
+                <xsl:element name="colspec"><xsl:attribute name="colwidth">20</xsl:attribute></xsl:element>
+                <xsl:element name="colspec"><xsl:attribute name="colwidth">20</xsl:attribute></xsl:element>
     		<xsl:element name="thead">
     			<xsl:element name="row">
     				<xsl:element name="entry">MySQL Error Number</xsl:element>

Modified: trunk/mysql-connector-java/src/testsuite/BaseTestCase.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/BaseTestCase.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/BaseTestCase.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -27,6 +27,7 @@
 import java.io.BufferedOutputStream;
 import java.io.FileOutputStream;
 import java.io.File;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.sql.Blob;
 import java.sql.Connection;
@@ -36,11 +37,13 @@
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Properties;
 
 import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.StringUtils;
 
 import junit.framework.TestCase;
 
@@ -49,13 +52,14 @@
  * closes them.
  * 
  * @author Mark Matthews
- * @version $Id: BaseTestCase.java 5440 2006-06-27 17:00:53Z mmatthews $
+ * @version $Id: BaseTestCase.java 5440 2006-06-27 17:00:53 +0000 (Tue, 27 Jun
+ *          2006) mmatthews $
  */
 public abstract class BaseTestCase extends TestCase {
 	private final static String ADMIN_CONNECTION_PROPERTY_NAME = "com.mysql.jdbc.testsuite.admin-url";
 
 	private final static String NO_MULTI_HOST_PROPERTY_NAME = "com.mysql.jdbc.testsuite.no-multi-hosts-tests";
-	
+
 	/**
 	 * JDBC URL, initialized from com.mysql.jdbc.testsuite.url system property,
 	 * or defaults to jdbc:mysql:///test
@@ -68,8 +72,8 @@
 	/** Connection to server, initialized in setUp() Cleaned up in tearDown(). */
 	protected Connection conn = null;
 
-	/** list of Tables to be dropped in tearDown */
-	private List createdTables;
+	/** list of schema objects to be dropped in tearDown */
+	private List createdObjects;
 
 	/** The driver to use */
 	protected String dbClass = "com.mysql.jdbc.Driver";
@@ -95,7 +99,7 @@
 	protected Statement stmt = null;
 
 	private boolean runningOnJdk131 = false;
-	
+
 	/**
 	 * Creates a new BaseTestCase object.
 	 * 
@@ -111,8 +115,9 @@
 		if ((newDbUrl != null) && (newDbUrl.trim().length() != 0)) {
 			dbUrl = newDbUrl;
 		} else {
-			String defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.default");
-			
+			String defaultDbUrl = System
+					.getProperty("com.mysql.jdbc.testsuite.url.default");
+
 			if ((defaultDbUrl != null) && (defaultDbUrl.trim().length() != 0)) {
 				dbUrl = defaultDbUrl;
 			}
@@ -124,33 +129,64 @@
 		if ((newDriver != null) && (newDriver.trim().length() != 0)) {
 			this.dbClass = newDriver;
 		}
-		
+
 		try {
-			Blob.class.getMethod("truncate", new Class[] {Long.TYPE});
+			Blob.class.getMethod("truncate", new Class[] { Long.TYPE });
 			this.runningOnJdk131 = false;
 		} catch (NoSuchMethodException nsme) {
 			this.runningOnJdk131 = true;
 		}
 	}
 
-	protected void createTable(String tableName, String columnsAndOtherStuff)
-			throws SQLException {
-		createdTables.add(tableName);
-		dropTable(tableName);
+	protected void createSchemaObject(String objectType, String objectName,
+			String columnsAndOtherStuff) throws SQLException {
+		this.createdObjects.add(new String[] {objectType, objectName});
+		dropSchemaObject(objectType, objectName);
 
-		StringBuffer createSql = new StringBuffer(tableName.length()
-				+ columnsAndOtherStuff.length() + 10);
-		createSql.append("CREATE TABLE ");
-		createSql.append(tableName);
+		StringBuffer createSql = new StringBuffer(objectName.length()
+				+ objectType.length() + columnsAndOtherStuff.length() + 10);
+		createSql.append("CREATE  ");
+		createSql.append(objectType);
 		createSql.append(" ");
+		createSql.append(objectName);
+		createSql.append(" ");
 		createSql.append(columnsAndOtherStuff);
 		this.stmt.executeUpdate(createSql.toString());
 	}
 
+	protected void createFunction(String functionName, String functionDefn)
+			throws SQLException {
+		createSchemaObject("FUNCTION", functionName, functionDefn);
+	}
+	
+	protected void dropFunction(String functionName) throws SQLException {
+		dropSchemaObject("FUNCTION", functionName);
+	}
+	
+	protected void createProcedure(String procedureName, String procedureDefn)
+			throws SQLException {
+		createSchemaObject("PROCEDURE", procedureName, procedureDefn);
+	}
+
+	protected void dropProcedure(String procedureName) throws SQLException {
+		dropSchemaObject("PROCEDURE", procedureName);
+	}
+
+	protected void createTable(String tableName, String columnsAndOtherStuff)
+			throws SQLException {
+		createSchemaObject("TABLE", tableName, columnsAndOtherStuff);
+	}
+
 	protected void dropTable(String tableName) throws SQLException {
-		this.stmt.executeUpdate("DROP TABLE IF EXISTS " + tableName);
+		dropSchemaObject("TABLE", tableName);
 	}
 
+	protected void dropSchemaObject(String objectType, String objectName)
+			throws SQLException {
+		this.stmt.executeUpdate("DROP " + objectType + " IF EXISTS "
+				+ objectName);
+	}
+
 	protected Connection getAdminConnection() throws SQLException {
 		return getAdminConnectionWithProps(new Properties());
 	}
@@ -166,6 +202,28 @@
 		}
 	}
 
+	protected Connection getConnectionWithProps(String propsList) throws SQLException {
+		return getConnectionWithProps(dbUrl, propsList);
+	}
+
+	protected Connection getConnectionWithProps(String url, String propsList) throws SQLException {
+		Properties props = new Properties();
+		
+		if (propsList != null) {
+			List keyValuePairs = StringUtils.split(propsList, ",", false);
+			
+			Iterator iter = keyValuePairs.iterator();
+			
+			while (iter.hasNext()) {
+				String kvp = (String)iter.next();
+				List splitUp = StringUtils.split(kvp, "=", false);
+				props.setProperty(splitUp.get(0).toString().trim(), splitUp.get(1).toString());
+			}
+		}
+		
+		return getConnectionWithProps(url, props);
+	}
+	
 	/**
 	 * Returns a new connection with the given properties
 	 * 
@@ -203,13 +261,12 @@
 		Object value = getSingleIndexedValueWithQuery(c, 2,
 				"SHOW VARIABLES LIKE '" + variableName + "'");
 
-		
 		if (value != null) {
 			if (value instanceof byte[]) {
 				// workaround for bad 4.1.x bugfix
-				return new String((byte[])value);
+				return new String((byte[]) value);
 			}
-			
+
 			return value.toString();
 		}
 
@@ -319,7 +376,7 @@
 	protected Object getSingleValue(String tableName, String columnName,
 			String whereClause) throws SQLException {
 		return getSingleValueWithQuery("SELECT " + columnName + " FROM "
-				+ tableName + ((whereClause == null) ? "" : whereClause));
+				+ tableName + ((whereClause == null) ? "" : " " + whereClause));
 	}
 
 	protected Object getSingleValueWithQuery(String query) throws SQLException {
@@ -343,6 +400,9 @@
 	protected File newTempBinaryFile(String name, long size) throws IOException {
 		File tempFile = File.createTempFile(name, "tmp");
 		tempFile.deleteOnExit();
+		
+		cleanupTempFiles(tempFile, name);
+		
 		FileOutputStream fos = new FileOutputStream(tempFile);
 		BufferedOutputStream bos = new BufferedOutputStream(fos);
 		for (long i = 0; i < size; i++) {
@@ -387,10 +447,8 @@
 		System.out.println("Loading JDBC driver '" + this.dbClass + "'");
 		Class.forName(this.dbClass).newInstance();
 		System.out.println("Done.\n");
-		createdTables = new ArrayList();
+		this.createdObjects = new ArrayList();
 
-		// System.out.println("Establishing connection to database '" + dbUrl
-		// + "'");
 
 		if (this.dbClass.equals("gwe.sql.gweMysqlDriver")) {
 			try {
@@ -412,11 +470,18 @@
 		this.stmt = this.conn.createStatement();
 
 		try {
-			this.rs = this.stmt.executeQuery("SELECT VERSION()");
-			this.rs.next();
-			logDebug("Connected to " + this.rs.getString(1));
-			this.rs.close();
-			this.rs = null;
+			if (dbUrl.indexOf("mysql") != -1) {
+				this.rs = this.stmt.executeQuery("SELECT VERSION()");
+				this.rs.next();
+				logDebug("Connected to " + this.rs.getString(1));
+				this.rs.close();
+				this.rs = null;
+			} else {
+				logDebug("Connected to "
+						+ this.conn.getMetaData().getDatabaseProductName()
+						+ " / "
+						+ this.conn.getMetaData().getDatabaseProductVersion());
+			}
 		} finally {
 			if (this.rs != null) {
 				this.rs.close();
@@ -439,9 +504,11 @@
 			}
 		}
 
-		for (int i = 0; i < createdTables.size(); i++) {
+		for (int i = 0; i < this.createdObjects.size(); i++) {
 			try {
-				dropTable((String) createdTables.get(i));
+				String[] objectInfo = (String[])this.createdObjects.get(i);
+				
+				dropSchemaObject(objectInfo[0], objectInfo[1]);
 			} catch (SQLException SQLE) {
 				;
 			}
@@ -510,36 +577,36 @@
 		return (((com.mysql.jdbc.Connection) this.conn).versionMeetsMinimum(
 				major, minor, subminor));
 	}
-	
+
 	protected boolean isRunningOnJdk131() {
 		return this.runningOnJdk131;
 	}
-	
+
 	protected boolean isClassAvailable(String classname) {
 		try {
 			Class.forName(classname);
 			return true;
 		} catch (ClassNotFoundException e) {
 			return false;
-		}	
+		}
 	}
 
 	protected void closeMemberJDBCResources() {
 		if (this.rs != null) {
 			ResultSet toClose = this.rs;
 			this.rs = null;
-			
+
 			try {
 				toClose.close();
 			} catch (SQLException sqlEx) {
 				// ignore
 			}
 		}
-		
+
 		if (this.pstmt != null) {
 			PreparedStatement toClose = this.pstmt;
 			this.pstmt = null;
-			
+
 			try {
 				toClose.close();
 			} catch (SQLException sqlEx) {
@@ -547,10 +614,43 @@
 			}
 		}
 	}
-	
+
 	protected boolean isRunningOnJRockit() {
 		String vmVendor = System.getProperty("java.vm.vendor");
+
+		return (vmVendor != null && vmVendor.toUpperCase(Locale.US).startsWith(
+				"BEA"));
+	}
+
+	protected String randomString() {
+		int length = (int)(Math.random() * 32);
 		
-		return (vmVendor != null && vmVendor.toUpperCase(Locale.US).startsWith("BEA"));
+		StringBuffer buf = new StringBuffer(length);
+		
+		for (int i = 0; i < length; i++) {
+			buf.append((char)((Math.random() * 26) + 'a'));
+		}
+		
+		return buf.toString();
 	}
-}
+
+	protected void cleanupTempFiles(final File exampleTempFile, final String tempfilePrefix) {
+	
+		File tempfilePath = exampleTempFile.getParentFile();
+		
+		File[] possibleFiles = tempfilePath.listFiles(new FilenameFilter() {
+	
+			public boolean accept(File dir, String name) {
+				return (name.indexOf(tempfilePrefix) != -1 
+						&& !exampleTempFile.getName().equals(name));
+			}});
+		
+		for (int i = 0; i < possibleFiles.length; i++) {
+			try {
+				possibleFiles[i].delete();
+			} catch (Throwable t) {
+				// ignore, we're only making a best effort cleanup attempt here
+			}
+		}
+	}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/testsuite/regression/CallableStatementRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/CallableStatementRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/CallableStatementRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -24,17 +24,22 @@
  */
 package testsuite.regression;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
 import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Types;
+import java.util.List;
 import java.util.Properties;
 
 import com.mysql.jdbc.DatabaseMetaData;
 import com.mysql.jdbc.NonRegisteringDriver;
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.StringUtils;
 
 import testsuite.BaseTestCase;
 
@@ -74,20 +79,22 @@
 	 *             if an error occurs.
 	 */
 	public void testBug3539() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			try {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3539");
-				this.stmt.executeUpdate("CREATE PROCEDURE testBug3539()\n"
-						+ "BEGIN\n" + "SELECT 1;" + "end\n");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-				this.rs = this.conn.getMetaData().getProcedures(null, null,
-						"testBug3539");
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3539");
+			this.stmt.executeUpdate("CREATE PROCEDURE testBug3539()\n"
+					+ "BEGIN\n" + "SELECT 1;" + "end\n");
 
-				assertTrue(this.rs.next());
-				assertTrue("testBug3539".equals(this.rs.getString(3)));
-			} finally {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3539");
-			}
+			this.rs = this.conn.getMetaData().getProcedures(null, null,
+			"testBug3539");
+
+			assertTrue(this.rs.next());
+			assertTrue("testBug3539".equals(this.rs.getString(3)));
+		} finally {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3539");
 		}
 	}
 
@@ -99,28 +106,29 @@
 	 *             if an error occurs.
 	 */
 	public void testBug3540() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			try {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3540");
-				this.stmt
-						.executeUpdate("CREATE PROCEDURE testBug3540(x int, out y int)\n"
-								+ "BEGIN\n" + "SELECT 1;" + "end\n");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3540");
+			this.stmt
+			.executeUpdate("CREATE PROCEDURE testBug3540(x int, out y int)\n"
+					+ "BEGIN\n" + "SELECT 1;" + "end\n");
 
-				this.rs = this.conn.getMetaData().getProcedureColumns(null,
-						null, "testBug3540%", "%");
+			this.rs = this.conn.getMetaData().getProcedureColumns(null,
+					null, "testBug3540%", "%");
 
-				assertTrue(this.rs.next());
-				assertTrue("testBug3540".equals(this.rs.getString(3)));
-				assertTrue("x".equals(this.rs.getString(4)));
+			assertTrue(this.rs.next());
+			assertTrue("testBug3540".equals(this.rs.getString(3)));
+			assertTrue("x".equals(this.rs.getString(4)));
 
-				assertTrue(this.rs.next());
-				assertTrue("testBug3540".equals(this.rs.getString(3)));
-				assertTrue("y".equals(this.rs.getString(4)));
+			assertTrue(this.rs.next());
+			assertTrue("testBug3540".equals(this.rs.getString(3)));
+			assertTrue("y".equals(this.rs.getString(4)));
 
-				assertTrue(!this.rs.next());
-			} finally {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3540");
-			}
+			assertTrue(!this.rs.next());
+		} finally {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3540");
 		}
 	}
 
@@ -132,46 +140,48 @@
 	 *             if the test fails.
 	 */
 	public void testBug7026() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			try {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug7026");
-				this.stmt
-						.executeUpdate("CREATE PROCEDURE testBug7026(x int, out y int)\n"
-								+ "BEGIN\n" + "SELECT 1;" + "end\n");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-				//
-				// Should be found this time.
-				//
-				this.rs = this.conn.getMetaData().getProcedures(
-						this.conn.getCatalog(), null, "testBug7026");
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug7026");
+			this.stmt
+			.executeUpdate("CREATE PROCEDURE testBug7026(x int, out y int)\n"
+					+ "BEGIN\n" + "SELECT 1;" + "end\n");
 
-				assertTrue(this.rs.next());
-				assertTrue("testBug7026".equals(this.rs.getString(3)));
+			//
+			// Should be found this time.
+			//
+			this.rs = this.conn.getMetaData().getProcedures(
+					this.conn.getCatalog(), null, "testBug7026");
 
-				assertTrue(!this.rs.next());
+			assertTrue(this.rs.next());
+			assertTrue("testBug7026".equals(this.rs.getString(3)));
 
-				//
-				// This time, shouldn't be found, because not associated with
-				// this (bogus) catalog
-				//
-				this.rs = this.conn.getMetaData().getProcedures("abfgerfg",
-						null, "testBug7026");
-				assertTrue(!this.rs.next());
+			assertTrue(!this.rs.next());
 
-				//
-				// Should be found this time as well, as we haven't
-				// specified a catalog.
-				//
-				this.rs = this.conn.getMetaData().getProcedures(null, null,
-						"testBug7026");
+			//
+			// This time, shouldn't be found, because not associated with
+			// this (bogus) catalog
+			//
+			this.rs = this.conn.getMetaData().getProcedures("abfgerfg",
+					null, "testBug7026");
+			assertTrue(!this.rs.next());
 
-				assertTrue(this.rs.next());
-				assertTrue("testBug7026".equals(this.rs.getString(3)));
+			//
+			// Should be found this time as well, as we haven't
+			// specified a catalog.
+			//
+			this.rs = this.conn.getMetaData().getProcedures(null, null,
+			"testBug7026");
 
-				assertTrue(!this.rs.next());
-			} finally {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug7026");
-			}
+			assertTrue(this.rs.next());
+			assertTrue("testBug7026".equals(this.rs.getString(3)));
+
+			assertTrue(!this.rs.next());
+		} finally {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug7026");
 		}
 	}
 
@@ -184,151 +194,154 @@
 	 *             if the test fails
 	 */
 	public void testBug9319() throws Exception {
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+
 		boolean doASelect = true; // SELECT currently causes the server to
 		// hang on the
 		// last execution of this testcase, filed as BUG#9405
 
-		if (versionMeetsMinimum(5, 0, 2)) {
-			if (isAdminConnectionConfigured()) {
-				Connection db2Connection = null;
-				Connection db1Connection = null;
 
-				try {
-					db2Connection = getAdminConnection();
-					db1Connection = getAdminConnection();
+		if (isAdminConnectionConfigured()) {
+			Connection db2Connection = null;
+			Connection db1Connection = null;
 
-					db2Connection.createStatement().executeUpdate(
-							"CREATE DATABASE IF NOT EXISTS db_9319_2");
-					db2Connection.setCatalog("db_9319_2");
+			try {
+				db2Connection = getAdminConnection();
+				db1Connection = getAdminConnection();
 
-					db2Connection.createStatement().executeUpdate(
-							"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
+				db2Connection.createStatement().executeUpdate(
+						"CREATE DATABASE IF NOT EXISTS db_9319_2");
+				db2Connection.setCatalog("db_9319_2");
 
-					db2Connection
-							.createStatement()
-							.executeUpdate(
-									"CREATE PROCEDURE COMPROVAR_USUARI(IN p_CodiUsuari VARCHAR(10),"
-											+ "\nIN p_contrasenya VARCHAR(10),"
-											+ "\nOUT p_userId INTEGER,"
-											+ "\nOUT p_userName VARCHAR(30),"
-											+ "\nOUT p_administrador VARCHAR(1),"
-											+ "\nOUT p_idioma VARCHAR(2))"
-											+ "\nBEGIN"
+				db2Connection.createStatement().executeUpdate(
+				"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
 
-											+ (doASelect ? "\nselect 2;"
-													: "\nSELECT 2 INTO p_administrador;")
-											+ "\nEND");
+				db2Connection
+				.createStatement()
+				.executeUpdate(
+						"CREATE PROCEDURE COMPROVAR_USUARI(IN p_CodiUsuari VARCHAR(10),"
+						+ "\nIN p_contrasenya VARCHAR(10),"
+						+ "\nOUT p_userId INTEGER,"
+						+ "\nOUT p_userName VARCHAR(30),"
+						+ "\nOUT p_administrador VARCHAR(1),"
+						+ "\nOUT p_idioma VARCHAR(2))"
+						+ "\nBEGIN"
 
-					db1Connection.createStatement().executeUpdate(
-							"CREATE DATABASE IF NOT EXISTS db_9319_1");
-					db1Connection.setCatalog("db_9319_1");
+						+ (doASelect ? "\nselect 2;"
+								: "\nSELECT 2 INTO p_administrador;")
+								+ "\nEND");
 
-					db1Connection.createStatement().executeUpdate(
-							"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
-					db1Connection
-							.createStatement()
-							.executeUpdate(
-									"CREATE PROCEDURE COMPROVAR_USUARI(IN p_CodiUsuari VARCHAR(10),"
-											+ "\nIN p_contrasenya VARCHAR(10),"
-											+ "\nOUT p_userId INTEGER,"
-											+ "\nOUT p_userName VARCHAR(30),"
-											+ "\nOUT p_administrador VARCHAR(1))"
-											+ "\nBEGIN"
-											+ (doASelect ? "\nselect 1;"
-													: "\nSELECT 1 INTO p_administrador;")
-											+ "\nEND");
+				db1Connection.createStatement().executeUpdate(
+				"CREATE DATABASE IF NOT EXISTS db_9319_1");
+				db1Connection.setCatalog("db_9319_1");
 
-					CallableStatement cstmt = db2Connection
-							.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }");
-					cstmt.setString(1, "abc");
-					cstmt.setString(2, "def");
-					cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
-					cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
-					cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
+				db1Connection.createStatement().executeUpdate(
+				"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
+				db1Connection
+				.createStatement()
+				.executeUpdate(
+						"CREATE PROCEDURE COMPROVAR_USUARI(IN p_CodiUsuari VARCHAR(10),"
+						+ "\nIN p_contrasenya VARCHAR(10),"
+						+ "\nOUT p_userId INTEGER,"
+						+ "\nOUT p_userName VARCHAR(30),"
+						+ "\nOUT p_administrador VARCHAR(1))"
+						+ "\nBEGIN"
+						+ (doASelect ? "\nselect 1;"
+								: "\nSELECT 1 INTO p_administrador;")
+								+ "\nEND");
 
-					cstmt.registerOutParameter(6, java.sql.Types.VARCHAR);
+				CallableStatement cstmt = db2Connection
+				.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }");
+				cstmt.setString(1, "abc");
+				cstmt.setString(2, "def");
+				cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
+				cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
+				cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
 
-					cstmt.execute();
+				cstmt.registerOutParameter(6, java.sql.Types.VARCHAR);
 
-					if (doASelect) {
-						this.rs = cstmt.getResultSet();
-						assertTrue(this.rs.next());
-						assertEquals(2, this.rs.getInt(1));
-					} else {
-						assertEquals(2, cstmt.getInt(5));
-					}
+				cstmt.execute();
 
-					cstmt = db1Connection
-							.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }");
-					cstmt.setString(1, "abc");
-					cstmt.setString(2, "def");
-					cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
-					cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
-					cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
+				if (doASelect) {
+					this.rs = cstmt.getResultSet();
+					assertTrue(this.rs.next());
+					assertEquals(2, this.rs.getInt(1));
+				} else {
+					assertEquals(2, cstmt.getInt(5));
+				}
 
-					try {
-						cstmt.registerOutParameter(6, java.sql.Types.VARCHAR);
-						fail("Should've thrown an exception");
-					} catch (SQLException sqlEx) {
-						assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
-								.getSQLState());
-					}
+				cstmt = db1Connection
+				.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?, ?) }");
+				cstmt.setString(1, "abc");
+				cstmt.setString(2, "def");
+				cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
+				cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
+				cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
 
-					cstmt = db1Connection
-							.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?) }");
-					cstmt.setString(1, "abc");
-					cstmt.setString(2, "def");
-					cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
-					cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
-					cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
+				try {
+					cstmt.registerOutParameter(6, java.sql.Types.VARCHAR);
+					fail("Should've thrown an exception");
+				} catch (SQLException sqlEx) {
+					assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
+							.getSQLState());
+				}
 
-					cstmt.execute();
+				cstmt = db1Connection
+				.prepareCall("{ call COMPROVAR_USUARI(?, ?, ?, ?, ?) }");
+				cstmt.setString(1, "abc");
+				cstmt.setString(2, "def");
+				cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
+				cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
+				cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
 
-					if (doASelect) {
-						this.rs = cstmt.getResultSet();
-						assertTrue(this.rs.next());
-						assertEquals(1, this.rs.getInt(1));
-					} else {
-						assertEquals(1, cstmt.getInt(5));
-					}
+				cstmt.execute();
 
-					String quoteChar = db2Connection.getMetaData()
-							.getIdentifierQuoteString();
+				if (doASelect) {
+					this.rs = cstmt.getResultSet();
+					assertTrue(this.rs.next());
+					assertEquals(1, this.rs.getInt(1));
+				} else {
+					assertEquals(1, cstmt.getInt(5));
+				}
 
-					cstmt = db2Connection.prepareCall("{ call " + quoteChar
-							+ db1Connection.getCatalog() + quoteChar + "."
-							+ quoteChar + "COMPROVAR_USUARI" + quoteChar
-							+ "(?, ?, ?, ?, ?) }");
-					cstmt.setString(1, "abc");
-					cstmt.setString(2, "def");
-					cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
-					cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
-					cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
+				String quoteChar = db2Connection.getMetaData()
+				.getIdentifierQuoteString();
 
-					cstmt.execute();
+				cstmt = db2Connection.prepareCall("{ call " + quoteChar
+						+ db1Connection.getCatalog() + quoteChar + "."
+						+ quoteChar + "COMPROVAR_USUARI" + quoteChar
+						+ "(?, ?, ?, ?, ?) }");
+				cstmt.setString(1, "abc");
+				cstmt.setString(2, "def");
+				cstmt.registerOutParameter(3, java.sql.Types.INTEGER);
+				cstmt.registerOutParameter(4, java.sql.Types.VARCHAR);
+				cstmt.registerOutParameter(5, java.sql.Types.VARCHAR);
 
-					if (doASelect) {
-						this.rs = cstmt.getResultSet();
-						assertTrue(this.rs.next());
-						assertEquals(1, this.rs.getInt(1));
-					} else {
-						assertEquals(1, cstmt.getInt(5));
-					}
-				} finally {
-					if (db2Connection != null) {
-						db2Connection.createStatement().executeUpdate(
-								"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
-						db2Connection.createStatement().executeUpdate(
-								"DROP DATABASE IF EXISTS db_9319_2");
-					}
+				cstmt.execute();
 
-					if (db1Connection != null) {
-						db1Connection.createStatement().executeUpdate(
-								"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
-						db1Connection.createStatement().executeUpdate(
-								"DROP DATABASE IF EXISTS db_9319_1");
-					}
+				if (doASelect) {
+					this.rs = cstmt.getResultSet();
+					assertTrue(this.rs.next());
+					assertEquals(1, this.rs.getInt(1));
+				} else {
+					assertEquals(1, cstmt.getInt(5));
 				}
+			} finally {
+				if (db2Connection != null) {
+					db2Connection.createStatement().executeUpdate(
+							"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
+					db2Connection.createStatement().executeUpdate(
+					"DROP DATABASE IF EXISTS db_9319_2");
+				}
+
+				if (db1Connection != null) {
+					db1Connection.createStatement().executeUpdate(
+							"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
+					db1Connection.createStatement().executeUpdate(
+					"DROP DATABASE IF EXISTS db_9319_1");
+				}
 			}
 		}
 	}
@@ -432,24 +445,26 @@
 	 *             if the test fails.
 	 */
 	public void testBug9682() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			CallableStatement cStmt = null;
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-			try {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug9682");
-				this.stmt
-						.executeUpdate("CREATE PROCEDURE testBug9682(decimalParam DECIMAL(18,0))"
-								+ "\nBEGIN" + "\n   SELECT 1;" + "\nEND");
-				cStmt = this.conn.prepareCall("Call testBug9682(?)");
-				cStmt.setDouble(1, 18.0);
-				cStmt.execute();
-			} finally {
-				if (cStmt != null) {
-					cStmt.close();
-				}
+		CallableStatement cStmt = null;
 
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug9682");
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug9682");
+			this.stmt
+			.executeUpdate("CREATE PROCEDURE testBug9682(decimalParam DECIMAL(18,0))"
+					+ "\nBEGIN" + "\n   SELECT 1;" + "\nEND");
+			cStmt = this.conn.prepareCall("Call testBug9682(?)");
+			cStmt.setDouble(1, 18.0);
+			cStmt.execute();
+		} finally {
+			if (cStmt != null) {
+				cStmt.close();
 			}
+
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug9682");
 		}
 	}
 
@@ -462,114 +477,116 @@
 	 *             if the test fails.
 	 */
 	public void testBug10310() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			CallableStatement cStmt = null;
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-			try {
-				this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310");
-				this.stmt
-						.executeUpdate("CREATE FUNCTION testBug10310(a float, b bigint, c int) RETURNS INT"
-								+ "\nBEGIN" + "\nRETURN a;" + "\nEND");
-				cStmt = this.conn.prepareCall("{? = CALL testBug10310(?,?,?)}");
-				cStmt.registerOutParameter(1, Types.INTEGER);
-				cStmt.setFloat(2, 2);
-				cStmt.setInt(3, 1);
-				cStmt.setInt(4, 1);
-				
-				if (!isRunningOnJdk131()) {
-					assertEquals(4, cStmt.getParameterMetaData().getParameterCount());
-					assertEquals(Types.OTHER, cStmt.getParameterMetaData().getParameterType(1));
-				}
-				
-				assertFalse(cStmt.execute());
-				assertEquals(2f, cStmt.getInt(1), .001);
-				assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
-						.getName());
+		CallableStatement cStmt = null;
 
-				assertEquals(-1, cStmt.executeUpdate());
-				assertEquals(2f, cStmt.getInt(1), .001);
-				assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
-						.getName());
+		try {
+			this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310");
+			this.stmt
+			.executeUpdate("CREATE FUNCTION testBug10310(a float, b bigint, c int) RETURNS INT"
+					+ "\nBEGIN" + "\nRETURN a;" + "\nEND");
+			cStmt = this.conn.prepareCall("{? = CALL testBug10310(?,?,?)}");
+			cStmt.registerOutParameter(1, Types.INTEGER);
+			cStmt.setFloat(2, 2);
+			cStmt.setInt(3, 1);
+			cStmt.setInt(4, 1);
 
-				if (!isRunningOnJdk131()) {
-					cStmt.setFloat("a", 4);
-					cStmt.setInt("b", 1);
-					cStmt.setInt("c", 1);
-					
-					assertFalse(cStmt.execute());
-					assertEquals(4f, cStmt.getInt(1), .001);
-					assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
-							.getName());
-					
-					assertEquals(-1, cStmt.executeUpdate());
-					assertEquals(4f, cStmt.getInt(1), .001);
-					assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
-							.getName());
-				}
-				
-				// Check metadata while we're at it
+			if (!isRunningOnJdk131()) {
+				assertEquals(4, cStmt.getParameterMetaData().getParameterCount());
+				assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(1));
+			}
 
-				java.sql.DatabaseMetaData dbmd = this.conn.getMetaData();
+			assertFalse(cStmt.execute());
+			assertEquals(2f, cStmt.getInt(1), .001);
+			assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
+					.getName());
 
-				this.rs = dbmd.getProcedures(this.conn.getCatalog(), null,
-						"testBug10310");
-				this.rs.next();
-				assertEquals("testBug10310", this.rs
-						.getString("PROCEDURE_NAME"));
-				assertEquals(DatabaseMetaData.procedureReturnsResult, this.rs
-						.getShort("PROCEDURE_TYPE"));
-				cStmt.setNull(2, Types.FLOAT);
-				cStmt.setInt(3, 1);
-				cStmt.setInt(4, 1);
-				
-				assertFalse(cStmt.execute());
-				assertEquals(0f, cStmt.getInt(1), .001);
-				assertEquals(true, cStmt.wasNull());
-				assertEquals(null, cStmt.getObject(1));
-				assertEquals(true, cStmt.wasNull());
+			assertEquals(-1, cStmt.executeUpdate());
+			assertEquals(2f, cStmt.getInt(1), .001);
+			assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
+					.getName());
 
-				assertEquals(-1, cStmt.executeUpdate());
-				assertEquals(0f, cStmt.getInt(1), .001);
-				assertEquals(true, cStmt.wasNull());
-				assertEquals(null, cStmt.getObject(1));
-				assertEquals(true, cStmt.wasNull());
+			if (!isRunningOnJdk131()) {
+				cStmt.setFloat("a", 4);
+				cStmt.setInt("b", 1);
+				cStmt.setInt("c", 1);
 
-
-				// Check with literals, not all parameters filled!
-				cStmt = this.conn.prepareCall("{? = CALL testBug10310(4,5,?)}");
-				cStmt.registerOutParameter(1, Types.INTEGER);
-				cStmt.setInt(2, 1);
-				
 				assertFalse(cStmt.execute());
 				assertEquals(4f, cStmt.getInt(1), .001);
 				assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
 						.getName());
-				
+
 				assertEquals(-1, cStmt.executeUpdate());
 				assertEquals(4f, cStmt.getInt(1), .001);
 				assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
 						.getName());
-				
-				if (!isRunningOnJdk131()) {
-					assertEquals(2, cStmt.getParameterMetaData().getParameterCount());
-					assertEquals(Types.OTHER, cStmt.getParameterMetaData().getParameterType(1));
-					assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(2));
-				}
-			} finally {
-				if (this.rs != null) {
-					this.rs.close();
-					this.rs = null;
-				}
+			}
 
-				if (cStmt != null) {
-					cStmt.close();
-				}
+			// Check metadata while we're at it
 
-				this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310");
+			java.sql.DatabaseMetaData dbmd = this.conn.getMetaData();
+
+			this.rs = dbmd.getProcedures(this.conn.getCatalog(), null,
+			"testBug10310");
+			this.rs.next();
+			assertEquals("testBug10310", this.rs
+					.getString("PROCEDURE_NAME"));
+			assertEquals(DatabaseMetaData.procedureReturnsResult, this.rs
+					.getShort("PROCEDURE_TYPE"));
+			cStmt.setNull(2, Types.FLOAT);
+			cStmt.setInt(3, 1);
+			cStmt.setInt(4, 1);
+
+			assertFalse(cStmt.execute());
+			assertEquals(0f, cStmt.getInt(1), .001);
+			assertEquals(true, cStmt.wasNull());
+			assertEquals(null, cStmt.getObject(1));
+			assertEquals(true, cStmt.wasNull());
+
+			assertEquals(-1, cStmt.executeUpdate());
+			assertEquals(0f, cStmt.getInt(1), .001);
+			assertEquals(true, cStmt.wasNull());
+			assertEquals(null, cStmt.getObject(1));
+			assertEquals(true, cStmt.wasNull());
+
+
+			// Check with literals, not all parameters filled!
+			cStmt = this.conn.prepareCall("{? = CALL testBug10310(4,5,?)}");
+			cStmt.registerOutParameter(1, Types.INTEGER);
+			cStmt.setInt(2, 1);
+
+			assertFalse(cStmt.execute());
+			assertEquals(4f, cStmt.getInt(1), .001);
+			assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
+					.getName());
+
+			assertEquals(-1, cStmt.executeUpdate());
+			assertEquals(4f, cStmt.getInt(1), .001);
+			assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
+					.getName());
+
+			if (!isRunningOnJdk131()) {
+				assertEquals(2, cStmt.getParameterMetaData().getParameterCount());
+				assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(1));
+				assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(2));
 			}
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+
+			if (cStmt != null) {
+				cStmt.close();
+			}
+
+			this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310");
 		}
 	}
-	
+
 	/**
 	 * Tests fix for Bug#12417 - stored procedure catalog name is case-sensitive
 	 * on Windows (this is actually a server bug, but we have a workaround in
@@ -579,15 +596,15 @@
 	 *             if the test fails.
 	 */
 	public void testBug12417() throws Exception {
-		if (versionMeetsMinimum(5, 0) && isServerRunningOnWindows()) {
+		if (serverSupportsStoredProcedures() && isServerRunningOnWindows()) {
 			Connection ucCatalogConn = null;
 
 			try {
 				this.stmt
-						.executeUpdate("DROP PROCEDURE IF EXISTS testBug12417");
+				.executeUpdate("DROP PROCEDURE IF EXISTS testBug12417");
 				this.stmt.executeUpdate("CREATE PROCEDURE testBug12417()\n"
 						+ "BEGIN\n" + "SELECT 1;" + "end\n");
-				ucCatalogConn = getConnectionWithProps(null);
+				ucCatalogConn = getConnectionWithProps((Properties)null);
 				ucCatalogConn.setCatalog(this.conn.getCatalog().toUpperCase());
 				ucCatalogConn.prepareCall("{call testBug12417()}");
 			} finally {
@@ -604,7 +621,7 @@
 		if (false /* needs to be fixed on server */) {
 			if (versionMeetsMinimum(5, 0)) {
 				this.stmt
-						.executeUpdate("DROP PROCEDURE IF EXISTS p_testBug15121");
+				.executeUpdate("DROP PROCEDURE IF EXISTS p_testBug15121");
 
 				this.stmt.executeUpdate("CREATE PROCEDURE p_testBug15121()\n"
 						+ "BEGIN\n" + "SELECT * from idonotexist;\n" + "END");
@@ -619,7 +636,7 @@
 
 					StringBuffer queryBuf = new StringBuffer("{call ");
 					String quotedId = this.conn.getMetaData()
-							.getIdentifierQuoteString();
+					.getIdentifierQuoteString();
 					queryBuf.append(quotedId);
 					queryBuf.append(this.conn.getCatalog());
 					queryBuf.append(quotedId);
@@ -643,35 +660,36 @@
 	 */
 
 	public void testBug15464() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			CallableStatement storedProc = null;
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+		CallableStatement storedProc = null;
 
-			try {
-				this.stmt
-						.executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
-				this.stmt
-						.executeUpdate("create procedure testInOutParam(IN p1 VARCHAR(255), INOUT p2 INT)\n"
-								+ "begin\n"
-								+ " DECLARE z INT;\n"
-								+ "SET z = p2 + 1;\n"
-								+ "SET p2 = z;\n"
-								+ "SELECT p1;\n"
-								+ "SELECT CONCAT('zyxw', p1);\n" + "end\n");
+		try {
+			this.stmt
+			.executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
+			this.stmt
+			.executeUpdate("create procedure testInOutParam(IN p1 VARCHAR(255), INOUT p2 INT)\n"
+					+ "begin\n"
+					+ " DECLARE z INT;\n"
+					+ "SET z = p2 + 1;\n"
+					+ "SET p2 = z;\n"
+					+ "SELECT p1;\n"
+					+ "SELECT CONCAT('zyxw', p1);\n" + "end\n");
 
-				storedProc = this.conn
-						.prepareCall("{call testInOutParam(?, ?)}");
+			storedProc = this.conn
+			.prepareCall("{call testInOutParam(?, ?)}");
 
-				storedProc.setString(1, "abcd");
-				storedProc.setInt(2, 4);
-				storedProc.registerOutParameter(2, Types.INTEGER);
+			storedProc.setString(1, "abcd");
+			storedProc.setInt(2, 4);
+			storedProc.registerOutParameter(2, Types.INTEGER);
 
-				storedProc.execute();
+			storedProc.execute();
 
-				assertEquals(5, storedProc.getInt(2));
-			} finally {
-				this.stmt
-						.executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
-			}
+			assertEquals(5, storedProc.getInt(2));
+		} finally {
+			this.stmt
+			.executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
 		}
 	}
 
@@ -685,24 +703,27 @@
 	 *             if the test fails
 	 */
 	public void testBug17898() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug17898");
-			this.stmt
-					.executeUpdate("CREATE PROCEDURE testBug17898(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\nSELECT 1 INTO rtn;\nSET param2=rtn;\nEND");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-			CallableStatement cstmt = this.conn
-					.prepareCall("{CALL testBug17898('foo', ?)}");
-			cstmt.registerOutParameter(1, Types.INTEGER);
+		this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug17898");
+		this.stmt
+		.executeUpdate("CREATE PROCEDURE testBug17898(param1 VARCHAR(50), OUT param2 INT)\nBEGIN\nDECLARE rtn INT;\nSELECT 1 INTO rtn;\nSET param2=rtn;\nEND");
+
+		CallableStatement cstmt = this.conn
+		.prepareCall("{CALL testBug17898('foo', ?)}");
+		cstmt.registerOutParameter(1, Types.INTEGER);
+		cstmt.execute();
+		assertEquals(1, cstmt.getInt(1));
+
+		if (!isRunningOnJdk131()) {
+			cstmt.clearParameters();
+			cstmt.registerOutParameter("param2", Types.INTEGER);
 			cstmt.execute();
 			assertEquals(1, cstmt.getInt(1));
+		}
 
-			if (!isRunningOnJdk131()) {
-				cstmt.clearParameters();
-				cstmt.registerOutParameter("param2", Types.INTEGER);
-				cstmt.execute();
-				assertEquals(1, cstmt.getInt(1));
-			}
-		}
 	}
 
 	/**
@@ -712,24 +733,27 @@
 	 * @throws Exception if the test fails.
 	 */
 	public void testBug21462() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			CallableStatement cstmt = null;
-			
-			try {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug21462");
-				this.stmt.executeUpdate("CREATE PROCEDURE testBug21462() BEGIN SELECT 1; END");
-				cstmt = this.conn.prepareCall("{CALL testBug21462}");
-				cstmt.execute();
-			} finally {
-				if (cstmt != null) {
-					cstmt.close();
-				}
-				
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug21462");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+
+		CallableStatement cstmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug21462");
+			this.stmt.executeUpdate("CREATE PROCEDURE testBug21462() BEGIN SELECT 1; END");
+			cstmt = this.conn.prepareCall("{CALL testBug21462}");
+			cstmt.execute();
+		} finally {
+			if (cstmt != null) {
+				cstmt.close();
 			}
+
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug21462");
 		}
+
 	}
-	
+
 	/** 
 	 * Tests fix for BUG#22024 - Newlines causing whitespace to span confuse
 	 * procedure parser when getting parameter metadata for stored procedures.
@@ -737,30 +761,33 @@
 	 * @throws Exception if the test fails
 	 */
 	public void testBug22024() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			CallableStatement cstmt = null;
-			
-			try {
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
-				this.stmt.executeUpdate("CREATE PROCEDURE testBug22024(\r\n)\r\n BEGIN SELECT 1; END");
-				cstmt = this.conn.prepareCall("{CALL testBug22024()}");
-				cstmt.execute();
-				
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
-				this.stmt.executeUpdate("CREATE PROCEDURE testBug22024(\r\na INT)\r\n BEGIN SELECT 1; END");
-				cstmt = this.conn.prepareCall("{CALL testBug22024(?)}");
-				cstmt.setInt(1, 1);
-				cstmt.execute();
-			} finally {
-				if (cstmt != null) {
-					cstmt.close();
-				}
-				
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+
+		CallableStatement cstmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
+			this.stmt.executeUpdate("CREATE PROCEDURE testBug22024(\r\n)\r\n BEGIN SELECT 1; END");
+			cstmt = this.conn.prepareCall("{CALL testBug22024()}");
+			cstmt.execute();
+
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
+			this.stmt.executeUpdate("CREATE PROCEDURE testBug22024(\r\na INT)\r\n BEGIN SELECT 1; END");
+			cstmt = this.conn.prepareCall("{CALL testBug22024(?)}");
+			cstmt.setInt(1, 1);
+			cstmt.execute();
+		} finally {
+			if (cstmt != null) {
+				cstmt.close();
 			}
+
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
 		}
+
 	}
-	
+
 	/**
 	 * Tests workaround for server crash when calling stored procedures
 	 * via a server-side prepared statement (driver now detects 
@@ -769,140 +796,451 @@
 	 * @throws Exception if the test fails
 	 */
 	public void testBug22297() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22297");
-			
-			createTable("tblTestBug2297_1", "("
-					+ "id varchar(20) NOT NULL default '',"
-					+ "Income double(19,2) default NULL)");	
-			
-			createTable("tblTestBug2297_2", "("
-					+ "id varchar(20) NOT NULL default ''," 
-					+ "CreatedOn datetime default NULL)");
-			
-			this.stmt.executeUpdate("CREATE PROCEDURE testBug22297(pcaseid INT)"
-					+ "BEGIN"
-					+ "\nSET @sql = \"DROP TEMPORARY TABLE IF EXISTS tmpOrders\";"
-					+ " PREPARE stmt FROM @sql;"
-					+ " EXECUTE stmt;"
-					+ " DEALLOCATE PREPARE stmt;"
-					+ "\nSET @sql = \"CREATE TEMPORARY TABLE tmpOrders SELECT id, 100 AS Income FROM tblTestBug2297_1 GROUP BY id\";"
-					+ " PREPARE stmt FROM @sql;"
-					+ " EXECUTE stmt;"
-					+ " DEALLOCATE PREPARE stmt;"
-					+ "\n SELECT id, Income FROM (SELECT e.id AS id ,COALESCE(prof.Income,0) AS Income"
-					+ "\n FROM tblTestBug2297_2 e LEFT JOIN tmpOrders prof ON e.id = prof.id"
-					+ "\n WHERE e.CreatedOn > '2006-08-01') AS Final ORDER BY id;" 
-					+ "\nEND");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-			this.stmt.executeUpdate("INSERT INTO tblTestBug2297_1 (`id`,`Income`) VALUES "
-					+ "('a',4094.00),"
-					+ "('b',500.00),"
-					+ "('c',3462.17),"
-					+ " ('d',500.00),"
-					+ " ('e',600.00)");
-			
-			this.stmt.executeUpdate("INSERT INTO tblTestBug2297_2 (`id`,`CreatedOn`) VALUES "
-					+ "('d','2006-08-31 00:00:00'),"
-					+ "('e','2006-08-31 00:00:00'),"
+		this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22297");
+
+		createTable("tblTestBug2297_1", "("
+				+ "id varchar(20) NOT NULL default '',"
+				+ "Income double(19,2) default NULL)");	
+
+		createTable("tblTestBug2297_2", "("
+				+ "id varchar(20) NOT NULL default ''," 
+				+ "CreatedOn datetime default NULL)");
+
+		this.stmt.executeUpdate("CREATE PROCEDURE testBug22297(pcaseid INT)"
+				+ "BEGIN"
+				+ "\nSET @sql = \"DROP TEMPORARY TABLE IF EXISTS tmpOrders\";"
+				+ " PREPARE stmt FROM @sql;"
+				+ " EXECUTE stmt;"
+				+ " DEALLOCATE PREPARE stmt;"
+				+ "\nSET @sql = \"CREATE TEMPORARY TABLE tmpOrders SELECT id, 100 AS Income FROM tblTestBug2297_1 GROUP BY id\";"
+				+ " PREPARE stmt FROM @sql;"
+				+ " EXECUTE stmt;"
+				+ " DEALLOCATE PREPARE stmt;"
+				+ "\n SELECT id, Income FROM (SELECT e.id AS id ,COALESCE(prof.Income,0) AS Income"
+				+ "\n FROM tblTestBug2297_2 e LEFT JOIN tmpOrders prof ON e.id = prof.id"
+				+ "\n WHERE e.CreatedOn > '2006-08-01') AS Final ORDER BY id;" 
+				+ "\nEND");
+
+		this.stmt.executeUpdate("INSERT INTO tblTestBug2297_1 (`id`,`Income`) VALUES "
+				+ "('a',4094.00),"
+				+ "('b',500.00),"
+				+ "('c',3462.17),"
+				+ " ('d',500.00),"
+				+ " ('e',600.00)");
+
+		this.stmt.executeUpdate("INSERT INTO tblTestBug2297_2 (`id`,`CreatedOn`) VALUES "
+				+ "('d','2006-08-31 00:00:00'),"
+				+ "('e','2006-08-31 00:00:00'),"
 				+ "('b','2006-08-31 00:00:00'),"
 				+ "('c','2006-08-31 00:00:00'),"
 				+ "('a','2006-08-31 00:00:00')");
-			
-			try {
-				this.pstmt = this.conn.prepareStatement("{CALL testBug22297(?)}");
-				this.pstmt.setInt(1, 1);
-				this.rs =this.pstmt.executeQuery();
-                
-				String[] ids = new String[] { "a", "b", "c", "d", "e"};
-                int pos = 0;
-                
-                while (this.rs.next()) {
-                	assertEquals(ids[pos++], rs.getString(1));
-                	assertEquals(100, rs.getInt(2));
-                }
-                
-                assertEquals(this.pstmt.getClass().getName(),
-                		com.mysql.jdbc.PreparedStatement.class.getName());
 
-			} finally {
-				closeMemberJDBCResources();
+		try {
+			this.pstmt = this.conn.prepareStatement("{CALL testBug22297(?)}");
+			this.pstmt.setInt(1, 1);
+			this.rs =this.pstmt.executeQuery();
+
+			String[] ids = new String[] { "a", "b", "c", "d", "e"};
+			int pos = 0;
+
+			while (this.rs.next()) {
+				assertEquals(ids[pos++], rs.getString(1));
+				assertEquals(100, rs.getInt(2));
 			}
+
+			assertTrue(this.pstmt.getClass().getName().indexOf("Server") == -1);
+		} finally {
+			closeMemberJDBCResources();
 		}
+
 	}
-	
+
 	public void testHugeNumberOfParameters() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			this.stmt
-					.executeUpdate("DROP PROCEDURE IF EXISTS testHugeNumberOfParameters");
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-			StringBuffer procDef = new StringBuffer(
-					"CREATE PROCEDURE testHugeNumberOfParameters(");
+		this.stmt
+		.executeUpdate("DROP PROCEDURE IF EXISTS testHugeNumberOfParameters");
 
-			for (int i = 0; i < 274; i++) {
-				if (i != 0) {
-					procDef.append(",");
-				}
+		StringBuffer procDef = new StringBuffer(
+		"CREATE PROCEDURE testHugeNumberOfParameters(");
 
-				procDef.append(" OUT param_" + i + " VARCHAR(32)");
+		for (int i = 0; i < 274; i++) {
+			if (i != 0) {
+				procDef.append(",");
 			}
 
-			procDef.append(")\nBEGIN\nSELECT 1;\nEND");
-			this.stmt.executeUpdate(procDef.toString());
+			procDef.append(" OUT param_" + i + " VARCHAR(32)");
+		}
 
-			CallableStatement cStmt = null;
+		procDef.append(")\nBEGIN\nSELECT 1;\nEND");
+		this.stmt.executeUpdate(procDef.toString());
 
-			try {
-				cStmt = this.conn
-						.prepareCall("{call testHugeNumberOfParameters(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
-								+
+		CallableStatement cStmt = null;
 
-								"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
-								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
-								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
-								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
-								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
-								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
-				cStmt.registerOutParameter(274, Types.VARCHAR);
+		try {
+			cStmt = this.conn
+			.prepareCall("{call testHugeNumberOfParameters(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+					+
 
-				cStmt.execute();
-			} finally {
-				if (cStmt != null) {
-					cStmt.close();
-				}
+					"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+					+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+					+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+					+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+					+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+					+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
+			cStmt.registerOutParameter(274, Types.VARCHAR);
+
+			cStmt.execute();
+		} finally {
+			if (cStmt != null) {
+				cStmt.close();
 			}
 		}
 	}
 
 	public void testPrepareOfMultiRs() throws Exception {
-		if (versionMeetsMinimum(5, 0)) {
-			this.stmt.executeUpdate("Drop procedure if exists p");
-			this.stmt
-					.executeUpdate("create procedure p () begin select 1; select 2; end;");
-			PreparedStatement ps = null;
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
 
-			try {
-				ps = this.conn.prepareStatement("call p()");
 
-				ps.execute();
-				this.rs = ps.getResultSet();
-				assertTrue(this.rs.next());
-				assertEquals(1, this.rs.getInt(1));
-				assertTrue(ps.getMoreResults());
-				this.rs = ps.getResultSet();
-				assertTrue(this.rs.next());
-				assertEquals(2, this.rs.getInt(1));
-				assertTrue(!ps.getMoreResults());
-			} finally {
-				if (this.rs != null) {
-					this.rs.close();
-					this.rs = null;
+		this.stmt.executeUpdate("Drop procedure if exists p");
+		this.stmt
+		.executeUpdate("create procedure p () begin select 1; select 2; end;");
+		PreparedStatement ps = null;
+
+		try {
+			ps = this.conn.prepareStatement("call p()");
+
+			ps.execute();
+			this.rs = ps.getResultSet();
+			assertTrue(this.rs.next());
+			assertEquals(1, this.rs.getInt(1));
+			assertTrue(ps.getMoreResults());
+			this.rs = ps.getResultSet();
+			assertTrue(this.rs.next());
+			assertEquals(2, this.rs.getInt(1));
+			assertTrue(!ps.getMoreResults());
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+
+			if (ps != null) {
+				ps.close();
+			}
+		}
+
+	}
+
+	/**
+	 * Tests fix for BUG#25379 - INOUT parameters in CallableStatements get doubly-escaped.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25379() throws Exception {
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+
+		createTable("testBug25379", "(col char(40))");
+
+		try {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug25379");
+			this.stmt.executeUpdate("CREATE PROCEDURE sp_testBug25379 (INOUT invalue char(255))"
+					+ "\nBEGIN"
+					+ "\ninsert into testBug25379(col) values(invalue);"
+					+ "\nEND");
+
+
+			CallableStatement cstmt = this.conn.prepareCall("{call sp_testBug25379(?)}");
+			cstmt.setString(1,"'john'");
+			cstmt.executeUpdate();
+			assertEquals("'john'", cstmt.getString(1));
+			assertEquals("'john'", getSingleValue("testBug25379", "col", "").toString());
+		} finally {
+			this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug25379");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#25715 - CallableStatements with OUT/INOUT parameters that
+	 * are "binary" have extra 7 bytes (which happens to be the _binary introducer!)
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25715() throws Exception {
+		if (!serverSupportsStoredProcedures()) {
+			return; // no stored procs
+		}
+		
+		if (isRunningOnJdk131()) {
+			return; // no such method to test
+		}
+
+		createProcedure("spbug25715", "(INOUT mblob MEDIUMBLOB)" + "BEGIN"
+				+ " SELECT 1 FROM DUAL WHERE 1=0;" + "\nEND");
+		CallableStatement cstmt = null;
+
+		try {
+			cstmt = this.conn.prepareCall("{call spbug25715(?)}");
+
+			byte[] buf = new byte[65];
+			for (int i = 0; i < 65; i++)
+				buf[i] = 1;
+			int il = buf.length;
+
+			int[] typesToTest = new int[] { Types.BIT, Types.BINARY, Types.BLOB, Types.JAVA_OBJECT,
+					Types.LONGVARBINARY, Types.VARBINARY };
+
+			for (int i = 0; i < typesToTest.length; i++) {
+
+				cstmt.setBinaryStream("mblob", new ByteArrayInputStream(buf),
+						buf.length);
+				cstmt.registerOutParameter("mblob", typesToTest[i]);
+
+				cstmt.executeUpdate();
+
+				InputStream is = cstmt.getBlob("mblob").getBinaryStream();
+				ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+				int bytesRead = 0;
+				byte[] readBuf = new byte[256];
+
+				while ((bytesRead = is.read(readBuf)) != -1) {
+					bOut.write(readBuf, 0, bytesRead);
 				}
 
-				if (ps != null) {
-					ps.close();
-				}
+				byte[] fromSelectBuf = bOut.toByteArray();
+
+				int ol = fromSelectBuf.length;
+
+				assertEquals(il, ol);
 			}
+
+			cstmt.close();
+		} finally {
+			closeMemberJDBCResources();
+
+			if (cstmt != null) {
+				cstmt.close();
+			}
 		}
+
 	}
-}
+
+	protected boolean serverSupportsStoredProcedures() throws SQLException {
+		return versionMeetsMinimum(5, 0);
+	}
+
+	public void testBug26143() throws Exception {
+		if (!serverSupportsStoredProcedures()) {
+			return; // no stored procedure support
+		}
+
+		this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug26143");
+
+		this.stmt.executeUpdate("CREATE DEFINER=CURRENT_USER PROCEDURE testBug26143(I INT) COMMENT 'abcdefg'"
+				+ "\nBEGIN\n"
+				+ "SELECT I * 10;"
+				+ "\nEND");
+
+		this.conn.prepareCall("{call testBug26143(?)").close();
+	}
+
+	/**
+	 * Tests fix for BUG#26959 - comments confuse procedure parser.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug26959() throws Exception {
+		if (!serverSupportsStoredProcedures()) {
+			return;
+		}
+
+		createProcedure(
+				"testBug26959",
+				"(_ACTION varchar(20),"
+				+ "\n`/*dumb-identifier-1*/` int,"
+				+ "\n`#dumb-identifier-2` int,"
+				+ "\n`--dumb-identifier-3` int,"
+				+ "\n_CLIENT_ID int, -- ABC"
+				+ "\n_LOGIN_ID  int, # DEF"
+				+ "\n_WHERE varchar(2000),"
+				+ "\n_SORT varchar(2000),"
+				+ "\n out _SQL varchar(/* inline right here - oh my gosh! */ 8000),"
+				+ "\n _SONG_ID int,"
+				+ "\n  _NOTES varchar(2000),"
+				+ "\n out _RESULT varchar(10)"
+				+ "\n /*"
+				+ "\n ,    -- Generic result parameter"
+				+ "\n out _PERIOD_ID int,         -- Returns the period_id. Useful when using @PREDEFLINK to return which is the last period"
+				+ "\n   _SONGS_LIST varchar(8000),"
+				+ "\n  _COMPOSERID int,"
+				+ "\n  _PUBLISHERID int,"
+				+ "\n   _PREDEFLINK int        -- If the user is accessing through a predefined link: 0=none  1=last period"
+				+ "\n */) BEGIN SELECT 1; END");
+
+		createProcedure(
+				"testBug26959_1",
+				"(`/*id*/` /* before type 1 */ varchar(20),"
+				+ "/* after type 1 */ OUT result2 DECIMAL(/*size1*/10,/*size2*/2) /* p2 */)"
+				+ "BEGIN SELECT action, result; END");
+
+		try {
+			this.conn.prepareCall(
+			"{call testBug26959(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}")
+			.close();
+			this.rs = this.conn.getMetaData().getProcedureColumns(
+					this.conn.getCatalog(), null, "testBug26959", "%");
+
+			String[] parameterNames = new String[] { "_ACTION",
+					"/*dumb-identifier-1*/", "#dumb-identifier-2",
+					"--dumb-identifier-3", "_CLIENT_ID", "_LOGIN_ID", "_WHERE",
+					"_SORT", "_SQL", "_SONG_ID", "_NOTES", "_RESULT" };
+
+			int[] parameterTypes = new int[] { Types.VARCHAR, Types.INTEGER,
+					Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER,
+					Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER,
+					Types.VARCHAR, Types.VARCHAR };
+
+			int[] direction = new int[] { DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnOut,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnOut };
+
+			int[] precision = new int[] { 20, 10, 10, 10, 10, 10, 2000, 2000,
+					8000, 10, 2000, 10 };
+
+			int index = 0;
+
+			while (this.rs.next()) {
+				assertEquals(parameterNames[index], this.rs
+						.getString("COLUMN_NAME"));
+				assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE"));
+				assertEquals(precision[index], this.rs.getInt("PRECISION"));
+				assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE"));
+				index++;
+			}
+
+			this.rs.close();
+
+			index = 0;
+			parameterNames = new String[] { "/*id*/", "result2" };
+			parameterTypes = new int[] { Types.VARCHAR, Types.DECIMAL };
+			precision = new int[] { 20, 10 };
+			direction = new int[] { DatabaseMetaData.procedureColumnIn,
+					DatabaseMetaData.procedureColumnOut };
+			int[] scale = new int[] { 0, 2 };
+
+			this.conn.prepareCall("{call testBug26959_1(?, ?)}").close();
+
+			this.rs = this.conn.getMetaData().getProcedureColumns(
+					this.conn.getCatalog(), null, "testBug26959_1", "%");
+
+			while (this.rs.next()) {
+				assertEquals(parameterNames[index], this.rs
+						.getString("COLUMN_NAME"));
+				assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE"));
+				assertEquals(precision[index], this.rs.getInt("PRECISION"));
+				assertEquals(scale[index], this.rs.getInt("SCALE"));
+				assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE"));
+
+				index++;
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#27400 - CALL [comment] some_proc() doesn't work
+	 */
+	public void testBug27400() throws Exception {
+		if (!serverSupportsStoredProcedures()) {
+			return; // SPs not supported
+		}
+
+		createProcedure("testBug27400", "(a INT, b VARCHAR(32)) BEGIN SELECT 1; END");
+
+		CallableStatement cStmt = null;
+
+		try {
+			cStmt = this.conn.prepareCall("{CALL /* SOME COMMENT */ testBug27400( /* does this work too? */ ?, ?)} # and a commented ? here too");
+			assertTrue(cStmt.toString().indexOf("/*") != -1); // we don't want to strip the comments
+			cStmt.setInt(1, 1);
+			cStmt.setString(2, "bleh");
+			cStmt.execute();
+		} finally {
+			if (cStmt != null) {
+				cStmt.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#28689 - CallableStatement.executeBatch()
+	 * doesn't work when connection property "noAccessToProcedureBodies"
+	 * has been set to "true".
+	 * 
+	 * The fix involves changing the behavior of "noAccessToProcedureBodies",
+	 * in that the driver will now report all paramters as "IN" paramters
+	 * but allow callers to call registerOutParameter() on them.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug28689() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return; // no stored procedures
+		}
+		
+		createTable("testBug28689", "(" +
+				
+				  "`id` int(11) NOT NULL auto_increment,"
+				  + "`usuario` varchar(255) default NULL,"
+				  + "PRIMARY KEY  (`id`)"
+				+ ")"); 
+
+		this.stmt.executeUpdate("INSERT INTO testBug28689 (usuario) VALUES ('AAAAAA')");
+
+		createProcedure("sp_testBug28689", "(tid INT)"
+				+ "\nBEGIN"
+				+ "\nUPDATE testBug28689 SET usuario = 'BBBBBB' WHERE id = tid;"
+				+ "\nEND");
+
+		Connection noProcedureBodiesConn = getConnectionWithProps("noAccessToProcedureBodies=true");
+		CallableStatement cStmt = null;
+		
+		try {
+			cStmt = noProcedureBodiesConn.prepareCall("{CALL sp_testBug28689(?)}");
+			cStmt.setInt(1, 1);
+			cStmt.addBatch();
+			cStmt.executeBatch();
+			
+			assertEquals("BBBBBB", getSingleIndexedValueWithQuery(noProcedureBodiesConn, 1, "SELECT `usuario` FROM testBug28689 WHERE id=1"));
+		} finally {
+			if (cStmt != null) {
+				cStmt.close();
+			}
+			
+			if (noProcedureBodiesConn != null) {
+				noProcedureBodiesConn.close();
+			}
+		}
+	}
+}
\ No newline at end of file

Modified: trunk/mysql-connector-java/src/testsuite/regression/ConnectionRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/ConnectionRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/ConnectionRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -26,10 +26,13 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.Connection;
 import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -42,10 +45,12 @@
 
 import testsuite.BaseTestCase;
 
+import com.mysql.jdbc.ConnectionImpl;
 import com.mysql.jdbc.Driver;
 import com.mysql.jdbc.NonRegisteringDriver;
 import com.mysql.jdbc.ReplicationConnection;
 import com.mysql.jdbc.ReplicationDriver;
+import com.mysql.jdbc.log.StandardLogger;
 
 /**
  * Regression tests for Connections
@@ -308,7 +313,7 @@
 
 		boolean isReadOnly = reconnectableConn.isReadOnly();
 
-		Connection killConn = getConnectionWithProps(null);
+		Connection killConn = getConnectionWithProps((Properties)null);
 
 		killConn.createStatement().executeUpdate("KILL " + connectionId);
 		Thread.sleep(2000);
@@ -707,7 +712,7 @@
 		props.setProperty("maxReconnects", "1");
 
 		Connection failoverConnection = null;
-		Connection killerConnection = getConnectionWithProps(null);
+		Connection killerConnection = getConnectionWithProps((String)null);
 
 		try {
 			failoverConnection = getConnectionWithProps("jdbc:mysql://"
@@ -880,7 +885,7 @@
 				if (isRunningOnJdk131()) {
 					assertEquals("WINDOWS-31J", charSetUC);
 				} else {
-					assertEquals("SHIFT_JIS", charSetUC);
+//					assertEquals("SHIFT_JIS", charSetUC);
 				}
 
 				props = new Properties();
@@ -1234,8 +1239,8 @@
 
 			try {
 				replConn = getMasterSlaveReplicationConnection();
-				assertTrue(!((ReplicationConnection) replConn)
-						.getMasterConnection().hasSameProperties(
+				assertTrue(!((ConnectionImpl) ((ReplicationConnection) replConn)
+						.getMasterConnection()).hasSameProperties(
 								((ReplicationConnection) replConn)
 										.getSlavesConnection()));
 			} finally {
@@ -1454,10 +1459,25 @@
 			try {
 				ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 				System.setErr(new PrintStream(bOut));
+				
+				HashMap methodsToSkipMap = new HashMap();
+				
+				// Needs an actual URL
+				methodsToSkipMap.put("getURL", null);
+				
+				// Java6 JDBC4.0 methods we don't implement
+				methodsToSkipMap.put("getNCharacterStream", null);
+				methodsToSkipMap.put("getNClob", null);
+				methodsToSkipMap.put("getNString", null);
+				methodsToSkipMap.put("getRowId", null);
+				methodsToSkipMap.put("getSQLXML", null);
+				
 				for (int j = 0; j < 2; j++) {
 					for (int i = 0; i < getMethods.length; i++) {
-						if (getMethods[i].getName().startsWith("get")
-								&& !getMethods[i].getName().equals("getURL")) {
+						String methodName = getMethods[i].getName();
+
+						if (methodName.startsWith("get")
+								&& !methodsToSkipMap.containsKey(methodName)) {
 							Class[] parameterTypes = getMethods[i]
 									.getParameterTypes();
 
@@ -1522,6 +1542,7 @@
 			}
 		}
 	}
+	
 
 	/**
 	 * Tests fix for BUG#15544, no "dos" character set in MySQL > 4.1.0
@@ -1711,8 +1732,11 @@
 		props.setProperty("autoReconnect", "false");
 		props.setProperty("roundRobinLoadBalance", "true");
 		props.setProperty("failoverReadOnly", "false");
-		props.setProperty("connectTimeout", "5000");
 		
+		if (!isRunningOnJdk131()) {
+			props.setProperty("connectTimeout", "5000");
+		}
+		
 		// Re-build the connection information
 		int firstIndexOfHost = BaseTestCase.dbUrl.indexOf("//") + 2;
 		int lastIndexOfHost = BaseTestCase.dbUrl.indexOf("/", firstIndexOfHost);
@@ -1791,4 +1815,438 @@
 			}
 		}
 	}
+	
+	/**
+	 * Tests to insure proper behavior for BUG#24706.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug24706() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return; // server status isn't there to support this feature
+		}
+		
+		Properties props = new Properties();
+		props.setProperty("elideSetAutoCommits", "true");
+		props.setProperty("logger", "StandardLogger");
+		props.setProperty("profileSQL", "true");
+		Connection c = null;
+		
+		StringBuffer logBuf = new StringBuffer();
+		
+		StandardLogger.bufferedLog = logBuf;
+		
+		try {
+			c = getConnectionWithProps(props);
+			c.setAutoCommit(true);
+			c.createStatement().execute("SELECT 1");
+			c.setAutoCommit(true);
+			c.setAutoCommit(false);
+			c.createStatement().execute("SELECT 1");
+			c.setAutoCommit(false);
+			
+			// We should only see _one_ "set autocommit=" sent to the server
+			
+			String log = logBuf.toString();
+			int searchFrom = 0;
+			int count = 0;
+			int found = 0;
+			
+			while ((found = log.indexOf("SET autocommit=", searchFrom)) != -1) {
+				searchFrom =  found + 1;
+				count++;
+			}
+			
+			// The SELECT doesn't actually start a transaction, so being pedantic the
+			// driver issues SET autocommit=0 again in this case.
+			assertEquals(2, count);
+		} finally {
+			StandardLogger.bufferedLog = null;
+			
+			if (c != null) {
+				c.close();
+			}
+			
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#25514 - Timer instance used for Statement.setQueryTimeout()
+	 * created per-connection, rather than per-VM, causing memory leak.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25514() throws Exception {
+
+		for (int i = 0; i < 10; i++) {
+			getConnectionWithProps((Properties)null).close();
+		}
+		
+		ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
+
+		while (root.getParent() != null) {
+	        root = root.getParent();
+	    }
+
+		int numThreadsNamedTimer = findNamedThreadCount(root, "Timer");
+
+		if (numThreadsNamedTimer == 0) {
+			numThreadsNamedTimer = findNamedThreadCount(root, "MySQL Statement Cancellation Timer");
+		}
+		
+		// Notice that this seems impossible to test on JDKs prior to 1.5, as there is no
+		// reliable way to find the TimerThread, so we have to rely on new JDKs for this 
+		// test.
+		assertTrue("More than one timer for cancel was created", numThreadsNamedTimer <= 1);
+	}
+	
+	private int findNamedThreadCount(ThreadGroup group, String nameStart) {
+		
+		int count = 0;
+		
+        int numThreads = group.activeCount();
+        Thread[] threads = new Thread[numThreads*2];
+        numThreads = group.enumerate(threads, false);
+    
+        for (int i=0; i<numThreads; i++) {
+            if (threads[i].getName().startsWith(nameStart)) {
+            	count++;
+            }
+        }
+
+        int numGroups = group.activeGroupCount();
+        ThreadGroup[] groups = new ThreadGroup[numGroups*2];
+        numGroups = group.enumerate(groups, false);
+    
+        for (int i=0; i<numGroups; i++) {
+        	count += findNamedThreadCount(groups[i], nameStart);
+        }
+
+        return count;
+	}
+	
+	/**
+	 * Ensures that we don't miss getters/setters for driver properties in
+	 * ConnectionProperties so that names given in documentation work with 
+	 * DataSources which will use JavaBean-style names and reflection to 
+	 * set the values (and often fail silently! when the method isn't available).
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug23626() throws Exception {
+		Class clazz = this.conn.getClass();
+		
+		DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo(dbUrl, null);
+		StringBuffer missingSettersBuf = new StringBuffer();
+		StringBuffer missingGettersBuf = new StringBuffer();
+		
+		Class[][] argTypes = {new Class[] { String.class }, new Class[] {Integer.TYPE}, new Class[] {Long.TYPE}, new Class[] {Boolean.TYPE}};
+		
+		for (int i = 0; i < dpi.length; i++) {
+			
+			String propertyName = dpi[i].name;
+		
+			if (propertyName.equals("HOST") || propertyName.equals("PORT") 
+					|| propertyName.equals("DBNAME") || propertyName.equals("user") ||
+					propertyName.equals("password")) {
+				continue;
+			}
+					
+			StringBuffer mutatorName = new StringBuffer("set");
+			mutatorName.append(Character.toUpperCase(propertyName.charAt(0)));
+			mutatorName.append(propertyName.substring(1));
+				
+			StringBuffer accessorName = new StringBuffer("get");
+			accessorName.append(Character.toUpperCase(propertyName.charAt(0)));
+			accessorName.append(propertyName.substring(1));
+			
+			try {
+				clazz.getMethod(accessorName.toString(), null);
+			} catch (NoSuchMethodException nsme) {
+				missingGettersBuf.append(accessorName.toString());
+				missingGettersBuf.append("\n");
+			}
+			
+			boolean foundMethod = false;
+			
+			for (int j = 0; j < argTypes.length; j++) {
+				try {
+					clazz.getMethod(mutatorName.toString(), argTypes[j]);
+					foundMethod = true;
+					break;
+				} catch (NoSuchMethodException nsme) {
+					
+				}
+			}
+			
+			if (!foundMethod) {
+				missingSettersBuf.append(mutatorName);
+				missingSettersBuf.append("\n");
+			}
+		}
+		
+		assertEquals("Missing setters for listed configuration properties.", "", missingSettersBuf.toString());
+		assertEquals("Missing getters for listed configuration properties.", "", missingSettersBuf.toString());
+	}
+	
+	/**
+	 * Tests fix for BUG#25545 - Client flags not sent correctly during handshake
+	 * when using SSL.
+	 * 
+	 * Requires test certificates from testsuite/ssl-test-certs to be installed
+	 * on the server being tested.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25545() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		if (isRunningOnJdk131()) {
+			return;
+		}
+	
+		createProcedure("testBug25545", "() BEGIN SELECT 1; END");
+		
+		String trustStorePath = "src/testsuite/ssl-test-certs/test-cert-store";
+		
+		System.setProperty("javax.net.ssl.keyStore", trustStorePath);
+		System.setProperty("javax.net.ssl.keyStorePassword","password");
+		System.setProperty("javax.net.ssl.trustStore", trustStorePath);
+		System.setProperty("javax.net.ssl.trustStorePassword","password");
+		
+		
+		Connection sslConn = null;
+		
+		try {
+			Properties props = new Properties();
+			props.setProperty("useSSL", "true");
+			props.setProperty("requireSSL", "true");
+			
+			sslConn = getConnectionWithProps(props);
+			sslConn.prepareCall("{ call testBug25545()}").execute();
+		} finally {
+			if (sslConn != null) {
+				sslConn.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#27655 - getTransactionIsolation() uses
+	 * "SHOW VARIABLES LIKE" which is very inefficient on MySQL-5.0+
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug27655() throws Exception {
+		StringBuffer logBuf = new StringBuffer();
+		Properties props = new Properties();
+		props.setProperty("profileSQL", "true");
+		props.setProperty("logger", "StandardLogger");
+		StandardLogger.bufferedLog = logBuf;
+		
+		Connection loggedConn = null;
+		
+		try {
+			loggedConn = getConnectionWithProps(props);
+			loggedConn.getTransactionIsolation();
+			
+			if (versionMeetsMinimum(4, 0, 3)) {
+				assertEquals(-1, logBuf.toString().indexOf("SHOW VARIABLES LIKE 'tx_isolation'"));
+			}
+		} finally {
+			if (loggedConn != null) {
+				loggedConn.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for issue where a failed-over connection would let
+	 * an application call setReadOnly(false), when that call 
+	 * should be ignored until the connection is reconnected to a 
+	 * writable master.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testFailoverReadOnly() throws Exception {
+		Properties props = getMasterSlaveProps();
+		props.setProperty("autoReconnect", "true");
+	
+		Connection failoverConn = null;
+
+		Statement failoverStmt = 
+			null;
+		
+		try {
+			failoverConn = getConnectionWithProps(getMasterSlaveUrl(), props);
+			
+			((com.mysql.jdbc.Connection)failoverConn).setPreferSlaveDuringFailover(true);
+			
+			failoverStmt = failoverConn.createStatement();
+			
+			String masterConnectionId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString();
+			
+			this.stmt.execute("KILL " + masterConnectionId);
+			
+			// die trying, so we get the next host
+			for (int i = 0; i < 100; i++) {
+				try {
+					failoverStmt.executeQuery("SELECT 1");
+				} catch (SQLException sqlEx) {
+					break;
+				}
+			}
+			
+			String slaveConnectionId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString();
+			
+			assertTrue("Didn't get a new physical connection",
+					!masterConnectionId.equals(slaveConnectionId));
+			
+			failoverConn.setReadOnly(false); // this should be ignored
+			
+			assertTrue(failoverConn.isReadOnly());
+			
+			((com.mysql.jdbc.Connection)failoverConn).setPreferSlaveDuringFailover(false);
+			
+			this.stmt.execute("KILL " + slaveConnectionId); // we can't issue this on our own connection :p
+			
+			// die trying, so we get the next host
+			for (int i = 0; i < 100; i++) {
+				try {
+					failoverStmt.executeQuery("SELECT 1");
+				} catch (SQLException sqlEx) {
+					break;
+				}
+			}
+			
+			String newMasterId = getSingleIndexedValueWithQuery(failoverConn, 1, "SELECT connection_id()").toString();
+			
+			assertTrue("Didn't get a new physical connection",
+					!slaveConnectionId.equals(newMasterId));
+			
+			failoverConn.setReadOnly(false);
+			
+			assertTrue(!failoverConn.isReadOnly());
+		} finally {
+			if (failoverStmt != null) {
+				failoverStmt.close();
+			}
+			
+			if (failoverConn != null) {
+				failoverConn.close();
+			}
+		}
+	}
+	
+	public void testPropertiesDescriptionsKeys() throws Exception {
+		DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo(
+				dbUrl, null);
+
+		for (int i = 0; i < dpi.length; i++) {
+			String description = dpi[i].description;
+			String propertyName = dpi[i].name;
+
+			if (description.indexOf("Missing error message for key '") != -1
+					|| description.startsWith("!")) {
+				fail("Missing message for configuration property "
+						+ propertyName);
+			}
+
+			if (description.length() < 10) {
+				fail("Suspiciously short description for configuration property "
+						+ propertyName);
+			}
+		}
+	}
+	
+	public void testBug29852() throws Exception {
+    	Connection lbConn = getLoadBalancedConnection();
+    	assertTrue(!lbConn.getClass().getName().startsWith("com.mysql.jdbc"));
+    	lbConn.close();
+    }
+
+	private Connection getLoadBalancedConnection() throws SQLException {
+		int indexOfHostStart = dbUrl.indexOf("://") + 3;
+    	int indexOfHostEnd = dbUrl.indexOf("/", indexOfHostStart);
+    	
+    	String backHalf = dbUrl.substring(indexOfHostStart, indexOfHostEnd);
+    	
+    	if (backHalf.length() == 0) {
+    		backHalf = "localhost:3306";
+    	}
+    	
+    	String dbAndConfigs = dbUrl.substring(indexOfHostEnd);
+    	
+    	Connection lbConn = DriverManager.getConnection("jdbc:mysql:loadbalance://" + backHalf + "," + backHalf + dbAndConfigs);
+		return lbConn;
+	}
+	
+	/**
+	 * Test of a new feature to fix BUG 22643, specifying a
+	 * "validation query" in your connection pool that starts
+	 * with "slash-star ping slash-star" _exactly_ will cause the driver to " +
+	 * instead send a ping to the server (much lighter weight), and when using
+	 * a ReplicationConnection or a LoadBalancedConnection, will send
+	 * the ping across all active connections.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug22643() throws Exception {
+		checkPingQuery(this.conn);
+		
+		Connection replConnection = getMasterSlaveReplicationConnection();
+		
+		try {
+			checkPingQuery(replConnection);
+		} finally {
+			if (replConnection != null) {
+				replConnection.close();
+			}
+		}
+		
+		Connection lbConn = getLoadBalancedConnection();
+		
+		try {
+			checkPingQuery(lbConn);
+		} finally {
+			if (lbConn != null) {
+				lbConn.close();
+			}
+		}
+	}
+
+	private void checkPingQuery(Connection c) throws SQLException {
+		// Yes, I know we're sending 2, and looking for 1
+		// that's part of the test, since we don't _really_
+		// send the query to the server!
+		String aPingQuery = "/* ping */ SELECT 2";
+		Statement pingStmt = c.createStatement();
+		PreparedStatement pingPStmt = null;
+		
+		try {
+			this.rs = pingStmt.executeQuery(aPingQuery);
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 1);
+			
+			assertTrue(pingStmt.execute(aPingQuery));
+			this.rs = pingStmt.getResultSet();
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 1);
+			
+			pingPStmt = c.prepareStatement(aPingQuery);
+			
+			assertTrue(pingPStmt.execute());
+			this.rs = pingPStmt.getResultSet();
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 1);
+			
+			this.rs = pingPStmt.executeQuery();
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 1);
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/regression/DataSourceRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/DataSourceRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/DataSourceRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -35,7 +35,6 @@
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.Hashtable;
-import java.util.Properties;
 
 import javax.naming.Context;
 import javax.naming.InitialContext;

Modified: trunk/mysql-connector-java/src/testsuite/regression/EscapeProcessorRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/EscapeProcessorRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/EscapeProcessorRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2005 MySQL AB
+ Copyright (C) 2005-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -84,4 +84,14 @@
 		assertEquals("select '{\"','}'", this.conn
 				.nativeSQL("select '{\"','}'"));
 	}
+	
+	/**
+	 * Tests fix for BUG#25399 - EscapeProcessor gets confused by multiple backslashes
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25399() throws Exception {
+		assertEquals("\\' {d}",
+				getSingleValueWithQuery("SELECT '\\\\\\' {d}'"));
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/regression/MetaDataRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/MetaDataRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/MetaDataRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -26,6 +26,7 @@
 
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
+import java.sql.DriverPropertyInfo;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
@@ -38,6 +39,8 @@
 import testsuite.BaseTestCase;
 
 import com.mysql.jdbc.Driver;
+import com.mysql.jdbc.Field;
+import com.mysql.jdbc.NonRegisteringDriver;
 import com.mysql.jdbc.SQLError;
 
 /**
@@ -590,7 +593,7 @@
 	public void testBug4138() throws Exception {
 		try {
 			String[] typesToTest = new String[] { "TINYINT", "SMALLINT",
-					"MEDIUMINT", "INTEGER", "BIGINT", "FLOAT", "DOUBLE",
+					"MEDIUMINT", "INT", "BIGINT", "FLOAT", "DOUBLE",
 					"DECIMAL" };
 
 			short[] jdbcMapping = new short[] { Types.TINYINT, Types.SMALLINT,
@@ -1189,7 +1192,7 @@
 	public void testBug11781() throws Exception {
 
 		if (versionMeetsMinimum(5, 1)) {
-			if (!versionMeetsMinimum(5, 1, 12)) {
+			if (!versionMeetsMinimum(5, 2)) {
 				// server bug prevents this test from functioning
 				
 				return;
@@ -1539,8 +1542,52 @@
 
 		assertEquals(true, dbmd.supportsGroupByUnrelated());
 	}
-	
+
 	/**
+	 * Tests fix for BUG#21267, ParameterMetaData throws NullPointerException
+	 * when prepared SQL actually has a syntax error
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug21267() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // no parameter metadata on JDK-1.3.1
+		}
+		
+		createTable(
+				"bug21267",
+				"(`Col1` int(11) NOT NULL,`Col2` varchar(45) default NULL,`Col3` varchar(45) default NULL,PRIMARY KEY  (`Col1`))");
+
+		try {
+			this.pstmt = this.conn
+					.prepareStatement("SELECT Col1, Col2,Col4 FROM bug21267 WHERE Col1=?");
+			this.pstmt.setInt(1, 1);
+
+			java.sql.ParameterMetaData psMeta = this.pstmt
+					.getParameterMetaData();
+
+			try {
+				assertEquals(0, psMeta.getParameterType(1));
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, sqlEx.getSQLState());
+			}
+			
+			this.pstmt.close();
+			
+			Properties props = new Properties();
+			props.setProperty("generateSimpleParameterMetadata", "true");
+			
+			this.pstmt = getConnectionWithProps(props).prepareStatement("SELECT Col1, Col2,Col4 FROM bug21267 WHERE Col1=?");
+			
+			psMeta = this.pstmt.getParameterMetaData();
+			
+			assertEquals(Types.VARCHAR, psMeta.getParameterType(1));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
 	 * Tests fix for BUG#21544 - When using information_schema for metadata, 
 	 * COLUMN_SIZE for getColumns() is not clamped to range of 
 	 * java.lang.Integer as is the case when not using 
@@ -1555,8 +1602,8 @@
 		}
 		
 		createTable("testBug21544",
-                "(foo_id INT NOT NULL, stuff LONGTEXT"
-                + ", PRIMARY KEY (foo_id)) TYPE=INNODB");
+	            "(foo_id INT NOT NULL, stuff LONGTEXT"
+	            + ", PRIMARY KEY (foo_id)) TYPE=INNODB");
 		
 		Connection infoSchemConn = null;
 		
@@ -1575,12 +1622,12 @@
 	        	rs.getInt("COLUMN_SIZE");   
 	        }
 	    } finally {
-            if (infoSchemConn != null) {
-            	infoSchemConn.close();
-            }
-            
-            closeMemberJDBCResources();
-        }
+	        if (infoSchemConn != null) {
+	        	infoSchemConn.close();
+	        }
+	        
+	        closeMemberJDBCResources();
+	    }
 	}
 
 	/** 
@@ -1636,7 +1683,28 @@
 		}
 	}
 
-
+	/**
+	 * Fix for BUG#22628 - Driver.getPropertyInfo() throws NullPointerException for URL that only specifies
+	 * host and/or port.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug22628() throws Exception {
+		DriverPropertyInfo[] dpi = new NonRegisteringDriver().getPropertyInfo("jdbc:mysql://bogus:9999", 
+				new Properties());
+		
+		boolean foundHost = false;
+		
+		for (int i = 0; i < dpi.length; i++) {
+			if ("bogus:9999".equals(dpi[i].value)) {
+				foundHost = true;
+				break;
+			}
+		}
+		
+		assertTrue(foundHost);
+	}
+	
 	private void testAbsenceOfMetadataForQuery(String query) throws Exception {
 		try {
 			this.pstmt = this.conn.prepareStatement(query);
@@ -1667,7 +1735,17 @@
 	}
 	
 	public void testCharacterSetForDBMD() throws Exception {
-		String tableName = "\u00e9\u0074\u00e9";
+		if (versionMeetsMinimum(4, 0)) {
+			// server is broken, fixed in 5.2/6.0?
+			
+			if (!versionMeetsMinimum(5, 2)) {
+				return;
+			}
+		}
+		
+		String quoteChar = this.conn.getMetaData().getIdentifierQuoteString();
+		
+		String tableName = quoteChar + "\u00e9\u0074\u00e9" + quoteChar;
 		createTable(tableName, "(field1 int)");
 		this.rs = this.conn.getMetaData().getTables(this.conn.getCatalog(), 
 				null, tableName, new String[] {"TABLE"});
@@ -1689,4 +1767,304 @@
 		this.conn.getMetaData().getProcedures(bogusDatabaseName, "%", "%");
 	}
 
+
+	/**
+	 * Tests fix for BUG#23303 - DBMD.getSchemas() doesn't return a TABLE_CATALOG column.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug23303() throws Exception {
+		try {
+			this.rs = this.conn.getMetaData().getSchemas();
+			this.rs.findColumn("TABLE_CATALOG");
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#23304 - DBMD using "show" and DBMD using 
+	 * information_schema do not return results consistent with eachother.
+	 * 
+	 * (note this fix only addresses the inconsistencies, not the issue that
+	 * the driver is treating schemas differently than some users expect.
+	 * 
+	 * We will revisit this behavior when there is full support for schemas
+	 * in MySQL).
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug23304() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		Connection connShow = null;
+		Connection connInfoSchema = null;
+		
+		ResultSet rsShow = null;
+		ResultSet rsInfoSchema = null;
+		
+		try {
+			Properties noInfoSchemaProps = new Properties();
+			noInfoSchemaProps.setProperty("useInformationSchema", "false");
+			
+			Properties infoSchemaProps = new Properties();
+			infoSchemaProps.setProperty("useInformationSchema", "true");
+			infoSchemaProps.setProperty("dumpQueriesOnException", "true");
+			
+			connShow = getConnectionWithProps(noInfoSchemaProps);
+			connInfoSchema = getConnectionWithProps(infoSchemaProps);
+			
+			DatabaseMetaData dbmdUsingShow = connShow.getMetaData();
+			DatabaseMetaData dbmdUsingInfoSchema = connInfoSchema.getMetaData();
+			
+			assertNotSame(dbmdUsingShow.getClass(), dbmdUsingInfoSchema.getClass());
+			
+			if (!isRunningOnJdk131()) {
+				rsShow = dbmdUsingShow.getSchemas();
+				rsInfoSchema = dbmdUsingInfoSchema.getSchemas();
+			
+				compareResultSets(rsShow, rsInfoSchema);	
+			}
+			
+			/*
+			rsShow = dbmdUsingShow.getTables(connShow.getCatalog(), null, "%", new String[] {"TABLE", "VIEW"});
+			rsInfoSchema = dbmdUsingInfoSchema.getTables(connInfoSchema.getCatalog(), null, "%", new String[] {"TABLE", "VIEW"});
+			
+			compareResultSets(rsShow, rsInfoSchema);
+			
+			rsShow = dbmdUsingShow.getTables(null, null, "%", new String[] {"TABLE", "VIEW"});
+			rsInfoSchema = dbmdUsingInfoSchema.getTables(null, null, "%", new String[] {"TABLE", "VIEW"});
+		
+			compareResultSets(rsShow, rsInfoSchema);
+			*/
+			
+			createTable("t_testBug23304", "(field1 int primary key not null, field2 tinyint, field3 mediumint, field4 mediumint, field5 bigint, field6 float, field7 double, field8 decimal, field9 char(32), field10 varchar(32), field11 blob, field12 mediumblob, field13 longblob, field14 text, field15 mediumtext, field16 longtext, field17 date, field18 time, field19 datetime, field20 timestamp)");
+			
+			rsShow = dbmdUsingShow.getColumns(connShow.getCatalog(), null, "t_testBug23304", "%");
+			rsInfoSchema = dbmdUsingInfoSchema.getColumns(connInfoSchema.getCatalog(), null, "t_testBug23304", "%");
+			
+			compareResultSets(rsShow, rsInfoSchema);
+		} finally {
+			if (rsShow != null) {
+				rsShow.close();
+			}
+			
+			if (rsInfoSchema != null) {
+				rsInfoSchema.close();
+			}
+		}
+	}
+	
+	private void compareResultSets(ResultSet expected, ResultSet actual) throws Exception {
+		if (expected == null && actual != null) {
+			fail("Expected null result set, actual was not null.");
+		} else if (expected != null && actual == null) {
+			fail("Expected non-null actual result set.");
+		} else if (expected == null && actual == null) {
+			return;
+		}
+		
+		expected.last();
+		
+		int expectedRows = expected.getRow();
+		
+		actual.last();
+		
+		int actualRows = actual.getRow();
+		
+		assertEquals(expectedRows, actualRows);
+		
+		ResultSetMetaData metadataExpected = expected.getMetaData();
+		ResultSetMetaData metadataActual = actual.getMetaData();
+		
+		assertEquals(metadataExpected.getColumnCount(), metadataActual.getColumnCount());
+		
+		for (int i = 0; i < metadataExpected.getColumnCount(); i++) {
+			assertEquals(metadataExpected.getColumnName(i + 1), metadataActual.getColumnName(i + 1));
+			assertEquals(metadataExpected.getColumnType(i + 1), metadataActual.getColumnType(i + 1));
+			assertEquals(metadataExpected.getColumnClassName(i + 1), metadataActual.getColumnClassName(i + 1));
+		}
+		
+		expected.beforeFirst();
+		actual.beforeFirst();
+		
+		StringBuffer messageBuf = null;
+		
+		while (expected.next() && actual.next()) {
+			
+			if (messageBuf != null) {
+				messageBuf.append("\n");
+			}
+			
+			for (int i = 0; i < metadataExpected.getColumnCount(); i++) {
+				if (expected.getObject(i + 1) == null && actual.getObject(i + 1) == null) {
+					continue;
+				}
+				
+				if ((expected.getObject(i + 1) == null && actual.getObject(i + 1) != null) ||
+						(expected.getObject(i + 1) != null && actual.getObject(i + 1) == null) ||
+						(!expected.getObject(i + 1).equals(actual.getObject(i + 1)))) {
+					if ("COLUMN_DEF".equals(metadataExpected.getColumnName(i + 1)) && 
+							(expected.getObject(i + 1) == null && actual.getString(i + 1).length() == 0) ||
+							(expected.getString(i + 1).length() == 0 && actual.getObject(i + 1) == null)) {
+						continue; // known bug with SHOW FULL COLUMNS, and we can't distinguish between null and ''
+						          // for a default
+					}
+					
+					if (messageBuf == null) {
+						messageBuf = new StringBuffer();
+					} else {
+						messageBuf.append("\n");
+					}
+					
+					messageBuf.append("On row " + expected.getRow() + " ,for column named " + metadataExpected.getColumnName(i + 1) + ", expected '" + expected.getObject(i + 1) + "', found '" + actual.getObject(i + 1) + "'");
+					
+				}
+			}
+		}
+		
+		if (messageBuf != null) {
+			fail(messageBuf.toString());
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#25624 - Whitespace surrounding storage/size specifiers in stored procedure
+	 * declaration causes NumberFormatException to be thrown when calling stored procedure.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug25624() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+
+		//
+		// we changed up the parameters to get coverage of the fixes,
+		// also note that whitespace _is_ significant in the DDL...
+		//
+		
+		createProcedure(
+				"testBug25624",
+				"(in _par1 decimal( 10 , 2 ) , in _par2 varchar( 4 )) BEGIN select 1; END");
+
+		this.conn.prepareCall("{call testBug25624(?,?)}").close();
+	}
+
+	
+	/**
+	 * Tests fix for BUG#27867 - Schema objects with identifiers other than
+	 * the connection character aren't retrieved correctly in ResultSetMetadata.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug27867() throws Exception {
+		try {
+			String gbkColumnName = "\u00e4\u00b8\u00ad\u00e6\u2013\u2021\u00e6\u00b5\u2039\u00e8\u00af\u2022";
+			createTable("ColumnNameEncoding", "(" + "`" + gbkColumnName
+					+ "` varchar(1) default NULL,"
+					+ "`ASCIIColumn` varchar(1) default NULL"
+					+ ")ENGINE=MyISAM DEFAULT CHARSET=utf8");
+			
+			this.rs = this.stmt
+					.executeQuery("SELECT * FROM ColumnNameEncoding");
+			java.sql.ResultSetMetaData tblMD = this.rs.getMetaData();
+
+			assertEquals(gbkColumnName, tblMD.getColumnName(1));
+			assertEquals("ASCIIColumn", tblMD.getColumnName(2));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Fixed BUG#27915 - DatabaseMetaData.getColumns() doesn't
+	 * contain SCOPE_* or IS_AUTOINCREMENT columns.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug27915() throws Exception {
+		createTable("testBug27915",
+				"(field1 int not null primary key auto_increment, field2 int)");
+		DatabaseMetaData dbmd = this.conn.getMetaData();
+
+		try {
+			this.rs = dbmd.getColumns(this.conn.getCatalog(), null,
+					"testBug27915", "%");
+			this.rs.next();
+
+			checkBug27915();
+
+			if (versionMeetsMinimum(5, 0)) {
+				this.rs = getConnectionWithProps("useInformationSchema=true")
+						.getMetaData().getColumns(this.conn.getCatalog(), null,
+								"testBug27915", "%");
+				this.rs.next();
+
+				checkBug27915();
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	private void checkBug27915() throws SQLException {
+		assertNull(this.rs.getString("SCOPE_CATALOG"));
+		assertNull(this.rs.getString("SCOPE_SCHEMA"));
+		assertNull(this.rs.getString("SCOPE_TABLE"));
+		assertNull(this.rs.getString("SOURCE_DATA_TYPE"));
+		assertEquals("YES", this.rs.getString("IS_AUTOINCREMENT"));
+
+		this.rs.next();
+		
+		assertNull(this.rs.getString("SCOPE_CATALOG"));
+		assertNull(this.rs.getString("SCOPE_SCHEMA"));
+		assertNull(this.rs.getString("SCOPE_TABLE"));
+		assertNull(this.rs.getString("SOURCE_DATA_TYPE"));
+		assertEquals("NO", this.rs.getString("IS_AUTOINCREMENT"));
+	}
+	
+	/**
+	 * Tests fix for BUG#27916 - UNSIGNED types not reported
+	 * via DBMD.getTypeInfo(), and capitalization of types is
+	 * not consistent between DBMD.getColumns(), RSMD.getColumnTypeName()
+	 * and DBMD.getTypeInfo().
+	 * 
+	 * This fix also ensures that the precision of UNSIGNED MEDIUMINT
+	 * and UNSIGNED BIGINT is reported correctly via DBMD.getColumns().
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug27916() throws Exception {
+		createTable(
+				"testBug27916",
+				"(field1 TINYINT UNSIGNED, field2 SMALLINT UNSIGNED, field3 INT UNSIGNED, field4 INTEGER UNSIGNED, field5 MEDIUMINT UNSIGNED, field6 BIGINT UNSIGNED)");
+
+		ResultSetMetaData rsmd = this.stmt.executeQuery(
+				"SELECT * FROM testBug27916").getMetaData();
+
+		HashMap typeNameToPrecision = new HashMap();
+		this.rs = this.conn.getMetaData().getTypeInfo();
+
+		while (this.rs.next()) {
+			typeNameToPrecision.put(this.rs.getString("TYPE_NAME"), this.rs
+					.getObject("PRECISION"));
+		}
+
+		this.rs = this.conn.getMetaData().getColumns(this.conn.getCatalog(),
+				null, "testBug27916", "%");
+
+		for (int i = 0; i < rsmd.getColumnCount(); i++) {
+			this.rs.next();
+			String typeName = this.rs.getString("TYPE_NAME");
+
+			assertEquals(typeName, rsmd.getColumnTypeName(i + 1));
+			assertEquals(typeName, this.rs.getInt("COLUMN_SIZE"), rsmd
+					.getPrecision(i + 1));
+			assertEquals(typeName, new Integer(rsmd.getPrecision(i + 1)),
+					typeNameToPrecision.get(typeName));
+		}
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/regression/ResultSetRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/ResultSetRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/ResultSetRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as
@@ -25,6 +25,9 @@
 package testsuite.regression;
 
 import java.io.Reader;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.sql.CallableStatement;
 import java.sql.Clob;
@@ -48,9 +51,13 @@
 
 import testsuite.BaseTestCase;
 
+import com.mysql.jdbc.Messages;
 import com.mysql.jdbc.MysqlDataTruncation;
 import com.mysql.jdbc.NotUpdatable;
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.StringUtils;
+import com.mysql.jdbc.Util;
+import com.mysql.jdbc.log.StandardLogger;
 
 /**
  * Regression test cases for the ResultSet class.
@@ -299,7 +306,7 @@
 		try {
 			Properties props = new Properties();
 			props.setProperty("clobberStreamingResults", "true");
-
+			
 			Connection clobberConn = getConnectionWithProps(props);
 
 			Statement clobberStmt = clobberConn.createStatement();
@@ -1503,7 +1510,7 @@
 			assertTrue(0 == this.rs.getInt("field3"));
 
 			assertTrue(this.rs.next());
-			assertTrue(this.rs.getString("field1").equals("2004-11-20"));
+			assertEquals("2004-11-20", this.rs.getString("field1"));
 			assertTrue(null == this.rs.getObject("field2"));
 			assertTrue(0 == this.rs.getInt("field3"));
 
@@ -2147,8 +2154,14 @@
 
 					assertEquals("java.lang.Integer", storedProc.getObject(1)
 							.getClass().getName());
-					assertEquals("java.lang.Integer", storedProc.getObject(2)
+					
+					if (versionMeetsMinimum(5, 1)) {
+						assertEquals("java.lang.Long", storedProc.getObject(2)
+								.getClass().getName());
+					} else {
+						assertEquals("java.lang.Integer", storedProc.getObject(2)
 							.getClass().getName());
+					}
 
 				} finally {
 					if (storedProc != null) {
@@ -3745,6 +3758,228 @@
 		}
 	}
 	
+	/**
+	 * Tests for a server bug - needs to be revisited when the server is fixed.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug24710() throws Exception {
+		if (!versionMeetsMinimum(6, 0)) {
+			return;
+		}
+
+		createTable("testBug24710", "(x varbinary(256))");
+
+		try {
+			this.stmt
+					.executeUpdate("insert into testBug24710(x) values(0x0000000000),"
+							+ "(0x1111111111),"
+							+ "(0x2222222222),"
+							+ "(0x3333333333),"
+							+ "(0x4444444444),"
+							+ "(0x5555555555),"
+							+ "(0x6666666666),"
+							+ "(0x7777777777),"
+							+ "(0x8888888888),"
+							+ "(0x9999999999),"
+							+ "(0xaaaaaaaaaa),"
+							+ "(0xbbbbbbbbbb),"
+							+ "(0xcccccccccc),"
+							+ "(0xdddddddddd),"
+							+ "(0xeeeeeeeeee),"
+							+ "(0xffffffffff)");
+
+			this.rs = this.stmt
+					.executeQuery("select t1.x t1x,(select x from testBug24710 t2 where t2.x=t1.x) t2x from testBug24710 t1");
+
+			assertEquals(Types.VARBINARY, this.rs.getMetaData()
+					.getColumnType(1));
+			assertEquals(Types.VARBINARY, this.rs.getMetaData()
+					.getColumnType(2));
+
+			this.rs = ((com.mysql.jdbc.Connection) this.conn)
+					.serverPrepareStatement(
+							"select t1.x t1x,(select x from testBug24710 t2 where t2.x=t1.x) t2x from testBug24710 t1")
+					.executeQuery();
+
+			assertEquals(Types.VARBINARY, this.rs.getMetaData()
+					.getColumnType(1));
+			assertEquals(Types.VARBINARY, this.rs.getMetaData()
+					.getColumnType(2));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#25328 - BIT(> 1) is returned as java.lang.String
+	 * from ResultSet.getObject() rather than byte[].
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testbug25328() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		createTable("testBug25382", "(BINARY_VAL BIT(64) NULL)");
+
+		byte[] bytearr = new byte[8];
+
+		this.pstmt = this.conn
+				.prepareStatement("INSERT INTO testBug25382 VALUES(?)");
+		try {
+
+			this.pstmt.setObject(1, bytearr, java.sql.Types.BINARY);
+			assertEquals(1, this.pstmt.executeUpdate());
+			this.pstmt.clearParameters();
+
+			this.rs = this.stmt.executeQuery("Select BINARY_VAL from testBug25382");
+			this.rs.next();
+			assertEquals(this.rs.getObject(1).getClass(), bytearr.getClass());
+		} finally {
+			closeMemberJDBCResources();
+		}        
+	}
+	
+	/**
+	 * Tests fix for BUG#25517 - Statement.setMaxRows() is not effective
+	 * on result sets materialized from cursors.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug25517() throws Exception {
+		Connection fetchConn = null;
+		Statement fetchStmt = null;
+		
+		createTable("testBug25517", "(field1 int)");
+		
+		StringBuffer insertBuf = new StringBuffer("INSERT INTO testBug25517 VALUES (1)");
+		
+		for (int i = 0; i < 100; i++) {
+			insertBuf.append(",(" + i + ")");
+		}
+		
+		this.stmt.executeUpdate(insertBuf.toString());
+		
+		try {
+			Properties props = new Properties();
+			props.setProperty("useServerPrepStmts", "true");
+			props.setProperty("useCursorFetch", "true");
+		
+			fetchConn = getConnectionWithProps(props);
+			fetchStmt = fetchConn.createStatement();
+			
+			//int[] maxRows = new int[] {1, 4, 5, 11, 12, 13, 16, 50, 51, 52, 100};
+			int[] fetchSizes = new int[] {1, 4, 10, 25, 100};
+			List maxRows = new ArrayList();
+			maxRows.add(new Integer(1));
+			
+			for (int i = 0; i < fetchSizes.length; i++) {
+				if (fetchSizes[i] != 1) {
+					maxRows.add(new Integer(fetchSizes[i] - 1));
+				}
+				
+				maxRows.add(new Integer(fetchSizes[i]));
+				
+				if (i != fetchSizes.length - 1) {
+					maxRows.add(new Integer(fetchSizes[i] + 1));
+				}
+			}
+			
+			for (int fetchIndex = 0; fetchIndex < fetchSizes.length; fetchIndex++) {
+				fetchStmt.setFetchSize(fetchSizes[fetchIndex]);
+				
+				for (int maxRowIndex = 0; maxRowIndex < maxRows.size(); maxRowIndex++) {
+					
+					int maxRowsToExpect = ((Integer)maxRows.get(maxRowIndex)).intValue();
+					fetchStmt.setMaxRows(maxRowsToExpect);
+					
+					int rowCount = 0;
+					
+					this.rs = fetchStmt.executeQuery("SELECT * FROM testBug25517");
+					
+					while (this.rs.next()) {
+						rowCount++;
+					}
+					
+					assertEquals(maxRowsToExpect, rowCount);
+				}
+			}
+			
+			this.pstmt = fetchConn.prepareStatement("SELECT * FROM testBug25517");
+			
+			for (int fetchIndex = 0; fetchIndex < fetchSizes.length; fetchIndex++) {
+				this.pstmt.setFetchSize(fetchSizes[fetchIndex]);
+				
+				for (int maxRowIndex = 0; maxRowIndex < maxRows.size(); maxRowIndex++) {
+					
+					int maxRowsToExpect = ((Integer)maxRows.get(maxRowIndex)).intValue();
+					this.pstmt.setMaxRows(maxRowsToExpect);
+					
+					int rowCount = 0;
+					
+					this.rs = this.pstmt.executeQuery();
+					
+					while (this.rs.next()) {
+						rowCount++;
+					}
+					
+					assertEquals(maxRowsToExpect, rowCount);
+				}
+			}
+			
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (fetchStmt != null) {
+				fetchStmt.close();
+			}
+			
+			if (fetchConn != null) {
+				fetchConn.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#25787 - java.util.Date should be serialized for PreparedStatement.setObject().
+	 * 
+	 * We add a new configuration option "treatUtilDateAsTimestamp", which is false by default,
+	 * as (1) We already had specific behavior to treat java.util.Date as a java.sql.Timestamp because
+	 * it's useful to many folks, and (2) that behavior will very likely be in JDBC-post-4.0 as a 
+	 * requirement.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25787() throws Exception {
+		createTable("testBug25787", "(MY_OBJECT_FIELD BLOB)");
+		
+		Connection deserializeConn = null;
+		
+		Properties props = new Properties();
+		props.setProperty("autoDeserialize", "true");
+		props.setProperty("treatUtilDateAsTimestamp", "false");
+		
+		try {
+			deserializeConn = getConnectionWithProps(props);
+			
+			this.pstmt = deserializeConn.prepareStatement("INSERT INTO testBug25787 (MY_OBJECT_FIELD) VALUES (?)");
+			java.util.Date dt = new java.util.Date();
+			
+			this.pstmt.setObject(1, dt);
+			this.pstmt.execute();
+			
+			this.rs = deserializeConn.createStatement().executeQuery("SELECT MY_OBJECT_FIELD FROM testBug25787");
+			this.rs.next();
+			assertEquals("java.util.Date", this.rs.getObject(1).getClass().getName());
+			assertEquals(dt, this.rs.getObject(1));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
 	public void testTruncationDisable() throws Exception {
 		Properties props = new Properties();
 		props.setProperty("jdbcCompliantTruncation", "false");
@@ -3760,4 +3995,482 @@
 		}
 		
 	}
+	
+	public void testUsageAdvisorOnZeroRowResultSet() throws Exception {
+		Connection advisorConn = null;
+		Statement advisorStmt = null;
+		
+		try {
+			Properties props = new Properties();
+			props.setProperty("useUsageAdvisor", "true");
+			
+			advisorConn = getConnectionWithProps(props);
+			
+			advisorStmt = advisorConn.createStatement();
+			
+			StringBuffer advisorBuf = new StringBuffer();
+			StandardLogger.bufferedLog = advisorBuf;
+			
+			this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0");
+			this.rs.next();
+			this.rs.close();
+			
+			advisorStmt.close();
+			
+			advisorStmt = advisorConn.createStatement(ResultSet.TYPE_FORWARD_ONLY, 
+					ResultSet.CONCUR_READ_ONLY);
+			
+			advisorStmt.setFetchSize(Integer.MIN_VALUE);
+			
+			this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0");
+			this.rs.next();
+			this.rs.close();
+			
+			StandardLogger.bufferedLog = null;
+			
+			if (versionMeetsMinimum(5, 0, 2)) {
+				advisorConn.close();
+				
+				props.setProperty("useCursorFetch", "true");
+				props.setProperty("useServerPrepStmts", "true");
+				
+				advisorConn = getConnectionWithProps(props);
+				
+				advisorStmt = advisorConn.createStatement();
+				advisorStmt.setFetchSize(1);
+				
+				StandardLogger.bufferedLog = advisorBuf;
+				
+				this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0");
+				this.rs.next();
+				this.rs.close();
+			}
+			
+			assertEquals("", advisorBuf.toString());
+		} finally {
+			StandardLogger.bufferedLog = null;
+			
+			closeMemberJDBCResources();
+			
+			if (advisorStmt != null) {
+				advisorStmt.close();
+			}
+			
+			if (advisorConn != null) {
+				advisorConn.close();
+			}
+		}
+	}
+	
+	public void testBug25894() throws Exception {
+    	createTable("bug25894", "("+
+    		    "tinyInt_type TINYINT DEFAULT 1,"+
+    		    "tinyIntU_type TINYINT UNSIGNED DEFAULT 1,"+
+    		    "smallInt_type SMALLINT DEFAULT 1,"+
+    		    "smallIntU_type SMALLINT UNSIGNED DEFAULT 1,"+
+    		    "mediumInt_type MEDIUMINT DEFAULT 1,"+
+    		    "mediumIntU_type MEDIUMINT UNSIGNED DEFAULT 1,"+
+    		    "int_type INT DEFAULT 1,"+
+    		    "intU_type INT UNSIGNED DEFAULT 1,"+
+    		    "bigInt_type BIGINT DEFAULT 1,"+
+    		    "bigIntU_type BIGINT UNSIGNED DEFAULT 1"+
+   			");");    
+	    	try {
+	    		this.stmt.executeUpdate("INSERT INTO bug25894 VALUES (-1,1,-1,1,-1,1,-1,1,-1,1)"); 
+	    		this.rs = this.stmt.executeQuery("SELECT * FROM bug25894");
+	    		java.sql.ResultSetMetaData tblMD = this.rs.getMetaData();
+	    		this.rs.first();
+	    		for (int i=1; i<tblMD.getColumnCount()+1; i++)
+	    		{	
+	    			String typesName = "";
+	    			switch (tblMD.getColumnType(i)) {
+	    			case Types.INTEGER:
+	    				typesName = "Types.INTEGER";
+	    				break;
+	    			case Types.TINYINT:
+	    				typesName = "Types.TINYINT";
+	    				break;
+	    			case Types.BIGINT:
+	    				typesName = "Types.BIGINT";
+	    				break;
+	    			case Types.SMALLINT:
+	    				typesName = "Types.SMALLINT";
+	    				break;
+	    			}
+	    			
+	    			System.out.println(i + " .fld: " + tblMD.getColumnName(i) + "T: " + typesName + ", MDC: " +
+	    					tblMD.getColumnClassName(i) + " " + tblMD.getColumnTypeName(i) + " " +
+	    					 ", getObj: " + this.rs.getObject(i).getClass());
+	    		}    		
+			
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#26173 - fetching rows via cursor retrieves
+	 * corrupted data.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug26173() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+	
+		 createTable("testBug26173", 
+				 "(fkey int, fdate date, fprice decimal(15, 2), fdiscount decimal(5,3))");
+         this.stmt.executeUpdate("insert into testBug26173 values (1, '2007-02-23', 99.9, 0.02)");
+		 
+         Connection fetchConn = null;
+         Statement stmtRead = null;
+         
+         Properties props = new Properties();
+         props.setProperty("useServerPrepStmts", "true");
+         props.setProperty("useCursorFetch", "true");
+         
+         try {
+        	 
+        	 fetchConn = getConnectionWithProps(props);
+        	 stmtRead = fetchConn.createStatement();
+             stmtRead.setFetchSize(1000);
+          
+             this.rs = stmtRead.executeQuery("select extract(year from fdate) as fyear, fprice * (1 - fdiscount) as fvalue from testBug26173");
+            
+             assertTrue(this.rs.next());
+             assertEquals(2007, this.rs.getInt(1));
+             assertEquals("97.90200", this.rs.getString(2));          
+         } finally {
+        	 if (stmtRead != null) {
+        		 stmtRead.close();
+        	 }
+        	 
+        	 if (fetchConn != null) {
+        		 fetchConn.close();
+        	 }
+        	 
+        	 closeMemberJDBCResources();
+         }
+	}
+	
+	/**
+	 * Tests fix for BUG#26789 - fast date/time parsing doesn't take into
+	 * account 00:00:00 as a legal value.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug26789() throws Exception {
+		try {
+			this.rs = this.stmt.executeQuery("SELECT '00:00:00'");
+			this.rs.next();
+			this.rs.getTime(1);
+			assertEquals("00:00:00", this.rs.getTime(1).toString());
+			assertEquals("1970-01-01 00:00:00.0", this.rs.getTimestamp(1)
+					.toString());
+			assertEquals("1970-01-01", this.rs.getDate(1).toString());
+
+			this.rs.close();
+
+			this.rs = this.stmt.executeQuery("SELECT '00/00/0000 00:00:00'");
+			this.rs.next();
+
+			try {
+				this.rs.getTime(1);
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
+						.getSQLState());
+			}
+
+			try {
+				this.rs.getTimestamp(1);
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
+						.getSQLState());
+			}
+
+			try {
+				this.rs.getDate(1);
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
+						.getSQLState());
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#27317 - column index < 1 returns misleading
+	 * error message.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug27317() throws Exception {
+		try {
+			this.rs = this.stmt.executeQuery("SELECT NULL");
+			this.rs.next();
+			String messageLowBound = null;
+
+			Method[] getterMethods = ResultSet.class.getMethods();
+			Integer zeroIndex = new Integer(0);
+			Integer twoIndex = new Integer(2);
+
+			for (int i = 0; i < getterMethods.length; i++) {
+				Class[] parameterTypes = getterMethods[i].getParameterTypes();
+
+				if (getterMethods[i].getName().startsWith("get")
+						&& parameterTypes.length == 1
+						&& (parameterTypes[0].equals(Integer.TYPE) || parameterTypes[0]
+								.equals(Integer.class))) {
+					if (getterMethods[i].getName().equals("getRowId")) {
+						continue; // we don't support this yet, ever?
+					}
+					
+					try {
+						getterMethods[i].invoke(this.rs,
+								new Object[] { zeroIndex });
+					} catch (InvocationTargetException invokeEx) {
+						Throwable ex = invokeEx.getTargetException();
+
+						if (ex != null && ex instanceof SQLException) {
+							SQLException sqlEx = (SQLException) ex;
+
+							assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
+									sqlEx.getSQLState());
+
+							messageLowBound = sqlEx.getMessage();
+						} else {
+							throw new RuntimeException(Util.stackTraceToString(ex), ex);
+						}
+					}
+
+					String messageHighBound = null;
+
+					try {
+						getterMethods[i].invoke(this.rs,
+								new Object[] { twoIndex });
+					} catch (InvocationTargetException invokeEx) {
+						Throwable ex = invokeEx.getTargetException();
+
+						if (ex != null && ex instanceof SQLException) {
+							SQLException sqlEx = (SQLException) ex;
+
+							assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT,
+									sqlEx.getSQLState());
+
+							messageHighBound = sqlEx.getMessage();
+						} else {
+							throw new RuntimeException(ex);
+						}
+					}
+
+					assertNotNull("Exception message null for method "
+							+ getterMethods[i], messageHighBound);
+					assertNotNull("Exception message null for method "
+							+ getterMethods[i], messageLowBound);
+
+					assertTrue(!messageHighBound.equals(messageLowBound));
+				}
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#28085 - Need more useful error messages for diagnostics
+	 * when the driver thinks a result set isn't updatable.
+	 * 
+	 * @throws Exception if the tests fail.
+	 */
+	public void testBug28085() throws Exception {
+
+		Statement updStmt = null;
+		
+		try {
+			createTable("testBug28085_oneKey", 
+				"(pk int primary key not null, field2 varchar(3))");
+			
+			this.stmt.executeUpdate("INSERT INTO testBug28085_oneKey (pk, field2) VALUES (1, 'abc')");
+			
+			createTable("testBug28085_multiKey", 
+				"(pk1 int not null, pk2 int not null, field2 varchar(3), primary key (pk1, pk2))");
+			
+			this.stmt.executeUpdate("INSERT INTO testBug28085_multiKey VALUES (1,2,'abc')");
+			
+			createTable("testBug28085_noKey", 
+					"(field1 varchar(3) not null)");
+	
+			this.stmt.executeUpdate("INSERT INTO testBug28085_noKey VALUES ('abc')");
+			
+			updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
+					ResultSet.CONCUR_UPDATABLE);
+			
+			this.rs = updStmt.executeQuery("SELECT field2 FROM testBug28085_oneKey");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.4");
+			
+			this.rs = updStmt.executeQuery("SELECT pk1, field2 FROM testBug28085_multiKey");
+			this.rs.next();
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.7");
+			
+			this.rs = updStmt.executeQuery("SELECT t1.field2, t1.pk, t2.pk1 FROM testBug28085_oneKey t1 INNER JOIN testBug28085_multiKey t2 ON t1.pk = t2.pk1");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.0");
+			
+			this.rs = updStmt.executeQuery("SELECT field1 FROM testBug28085_noKey");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.5");
+			
+			this.rs = updStmt.executeQuery("SELECT 1");
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.3");
+			
+			this.rs = updStmt.executeQuery("SELECT pk1, pk2, LEFT(field2, 2) FROM testBug28085_multiKey");
+			this.rs.next();
+			exerciseUpdatableResultSet(1, "NotUpdatableReason.3");
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (updStmt != null) {
+				updStmt.close();
+			}
+		}
+	}
+	
+	private void exerciseUpdatableResultSet(int columnUpdateIndex,
+			String messageToCheck) throws Exception {
+		this.rs.next();
+		
+		try {
+			this.rs.updateString(columnUpdateIndex, "def");
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		try {
+			this.rs.moveToInsertRow();
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+		
+		try {
+			this.rs.deleteRow();
+		} catch (SQLException sqlEx) {
+			checkUpdatabilityMessage(sqlEx, 
+					messageToCheck);
+		}
+
+		this.rs.close();
+	}
+	
+	private void checkUpdatabilityMessage(SQLException sqlEx,
+			String messageToCheck) throws Exception {
+
+		String message = sqlEx.getMessage();
+
+		assertNotNull(message);
+
+		String localizedMessage = Messages.getString(messageToCheck);
+
+		assertTrue("Didn't find required message component '"
+				+ localizedMessage + "', instead found:\n\n" + message,
+				message.indexOf(localizedMessage) != -1);
+	}
+
+	public void testBug24886() throws Exception {
+	    Properties props = new Properties();
+	    props.setProperty("blobsAreStrings", "true");
+
+	    Connection noBlobConn = getConnectionWithProps(props);
+
+	    createTable("testBug24886", "(sepallength double,"
+	            + "sepalwidth double,"
+	            + "petallength double,"
+	            + "petalwidth double,"
+	            + "Class mediumtext, "
+	            + "fy TIMESTAMP)");
+
+	    noBlobConn.createStatement().executeUpdate("INSERT INTO testBug24886 VALUES (1,2,3,4,'1234', now()),(5,6,7,8,'12345678', now())");
+	    this.rs = noBlobConn.createStatement().executeQuery("SELECT concat(Class,petallength), COUNT(*) FROM `testBug24886` GROUP BY `concat(Class,petallength)`");
+	    this.rs.next();
+	    assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName());
+
+	    props.clear();
+	    props.setProperty("functionsNeverReturnBlobs", "true");
+	    noBlobConn = getConnectionWithProps(props);
+	    this.rs = noBlobConn.createStatement().executeQuery("SELECT concat(Class,petallength), COUNT(*) FROM `testBug24886` GROUP BY `concat(Class,petallength)`");
+        this.rs.next();
+        assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName());
+	}
+
+	
+	/**
+	 * Tests fix for BUG#30664. Note that this fix only works
+	 * for MySQL server 5.0.25 and newer, since earlier versions
+	 * didn't consistently return correct metadata for functions,
+	 * and thus results from subqueries and functions were indistinguishable
+	 * from each other, leading to type-related bugs.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug30664() throws Exception {
+		if (!versionMeetsMinimum(5, 0, 25)) {
+			return;
+		}
+		
+		createTable("testBug30664_1", "(id int)");
+		createTable("testBug30664_2", "(id int, binaryvalue varbinary(10))");
+
+		try {
+			this.stmt
+					.executeUpdate("insert into testBug30664_1 values (1),(2),(3)");
+			this.stmt
+					.executeUpdate("insert into testBug30664_2 values (1,'¢‚¤'),(2,'‚¢‚¤'),(3,' ‚¢¤')");
+			this.rs = this.stmt
+					.executeQuery("select testBug30664_1.id, (select testBug30664_2.binaryvalue from testBug30664_2 where testBug30664_2.id=testBug30664_1.id) as value from testBug30664_1");
+			ResultSetMetaData tblMD = this.rs.getMetaData();
+
+			for (int i = 1; i < tblMD.getColumnCount() + 1; i++) {
+				switch (i) {
+				case 1:
+					assertEquals("INT", tblMD.getColumnTypeName(i)
+							.toUpperCase());
+					break;
+				case 2:
+					assertEquals("VARBINARY", tblMD.getColumnTypeName(i)
+							.toUpperCase());
+					break;
+				}
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#30851, NPE with null column values when
+	 * "padCharsWithSpace" is set to "true".
+	 * 
+	 * @throws Exception
+	 */
+	public void testbug30851() throws Exception {
+		Connection padConn = getConnectionWithProps("padCharsWithSpace=true");
+		
+    	try {
+        	createTable("bug30851", "(CharCol CHAR(10) DEFAULT NULL)");
+    		this.stmt.execute("INSERT INTO bug30851 VALUES (NULL)");
+    		this.rs = padConn.createStatement().executeQuery("SELECT * FROM bug30851");
+    		this.rs.first();
+    		String strvar = this.rs.getString(1);
+    		//assertNotNull("Should be null", strvar);
+
+    	} finally {
+			closeMemberJDBCResources();
+			
+			if (padConn != null) {
+				padConn.close();
+			}
+		}        
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/regression/StatementRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/StatementRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/StatementRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -47,10 +47,8 @@
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.sql.Statement;
-import java.sql.Time;
 import java.sql.Timestamp;
 import java.sql.Types;
-import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
 import java.util.Properties;
@@ -440,7 +438,7 @@
 		Statement stmt2 = null;
 
 		try {
-			conn2 = getConnectionWithProps(null);
+			conn2 = getConnectionWithProps((Properties)null);
 			stmt2 = conn2.createStatement();
 
 			conn2.close();
@@ -1233,7 +1231,13 @@
 	 *             if test fails.
 	 */
 	public void testBug3557() throws Exception {
+		boolean populateDefaults = ((com.mysql.jdbc.ConnectionProperties) this.conn)
+				.getPopulateInsertRowWithDefaultValues();
+
 		try {
+			((com.mysql.jdbc.ConnectionProperties) this.conn)
+					.setPopulateInsertRowWithDefaultValues(true);
+
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
 
 			this.stmt.executeUpdate("CREATE TABLE testBug3557 ( "
@@ -1253,6 +1257,9 @@
 			assertEquals("XYZ", this.rs.getObject(1));
 			assertEquals("123", this.rs.getObject(2));
 		} finally {
+			((com.mysql.jdbc.ConnectionProperties) this.conn)
+					.setPopulateInsertRowWithDefaultValues(populateDefaults);
+
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
 		}
 	}
@@ -1269,6 +1276,12 @@
 			return;
 		}
 		
+		if (isRunningOnJdk131()) {
+			// bug with timezones, no update
+			// for new DST in USA
+			return;
+		}
+		
 		long epsillon = 3000; // 3 seconds time difference
 
 		try {
@@ -1828,7 +1841,7 @@
 
 			try {
 				Properties props = new Properties();
-				props.setProperty("characterEncoding", "utf8");
+				props.setProperty("characterEncoding", "utf-8");
 
 				Connection utf8Conn = getConnectionWithProps(props);
 				Statement utfStmt = utf8Conn.createStatement();
@@ -1937,6 +1950,7 @@
 	 *             if the test fails.
 	 */
 	public void testBug5874() throws Exception {
+		/*
 		try {
 			String clientTimezoneName = "America/Los_Angeles";
 			String serverTimezoneName = "America/Chicago";
@@ -2018,7 +2032,7 @@
 			}
 		} finally {
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS timeTest");
-		}
+		} */
 	}
 
 	public void testBug6823() throws SQLException {
@@ -2730,11 +2744,9 @@
 
 			assertTrue(this.rs.next());
 
-			assertTrue("171576".equals(this.rs.getString(1)));
+			assertEquals("171576", this.rs.getString(1));
 
-			Date retDt = this.rs.getDate(2);
-
-			assertTrue(dt.equals(this.rs.getDate(2)));
+			assertEquals(dt, this.rs.getDate(2));
 		} finally {
 			this.stmt
 					.executeUpdate("DROP TABLE IF EXISTS testServerPrepStmtAndDate");
@@ -2743,7 +2755,7 @@
 
 	public void testServerPrepStmtDeadlock() throws Exception {
 
-		Connection c = getConnectionWithProps(null);
+		Connection c = getConnectionWithProps((Properties)null);
 
 		Thread testThread1 = new PrepareThread(c);
 		Thread testThread2 = new PrepareThread(c);
@@ -3133,9 +3145,15 @@
 						+ "(10003, 'data3')," + "(10004999, 'data4'),"
 						+ "(10005, 'data5')");
 			} catch (SQLException sqlEx) {
-				assertEquals("01004", sqlEx.getSQLState());
-				assertEquals("01004", sqlEx.getNextException().getSQLState());
+				String sqlStateToCompare = "01004";
+				
+				if (isJdbc4()) {
+					sqlStateToCompare = "22001";
+				}
 
+				assertEquals(sqlStateToCompare, sqlEx.getSQLState());
+				assertEquals(sqlStateToCompare, sqlEx.getNextException().getSQLState());
+
 				SQLWarning sqlWarn = this.stmt.getWarnings();
 				assertEquals("01000", sqlWarn.getSQLState());
 				assertEquals("01000", sqlWarn.getNextWarning().getSQLState());
@@ -3143,6 +3161,20 @@
 		}
 	}
 
+	protected boolean isJdbc4() {
+		boolean isJdbc4;
+		
+		try {
+			Class.forName("java.sql.Wrapper");
+			isJdbc4 = true;
+		} catch (Throwable t) {
+			isJdbc4 = false;
+		}
+		
+		return isJdbc4;
+	}
+
+	
 	/**
 	 * Tests fix for BUG#19615, PreparedStatement.setObject(int, Object, int)
 	 * doesn't respect scale of BigDecimals.
@@ -3259,7 +3291,7 @@
 			
 			Properties props = new Properties();
 			props.setProperty("cachePrepStmts", "true");
-			
+			props.setProperty("useServerPrepStmts", "true");
 			PreparedStatement pstmt1 = null;
 			PreparedStatement pstmt2  = null;
 			
@@ -3346,7 +3378,7 @@
 		Statement cancelStmt = null;
 		
 		try {
-			closedConn = getConnectionWithProps(null);
+			closedConn = getConnectionWithProps((String)null);
 			cancelStmt = closedConn.createStatement();
 		
 			closedConn.close();
@@ -3399,7 +3431,42 @@
 			closeMemberJDBCResources();
 		}
 	}
+
+	/**
+	 * Tests BUG#21438, server-side PS fails when using jdbcCompliantTruncation.
+	 * If either is set to FALSE (&useServerPrepStmts=false or
+	 * &jdbcCompliantTruncation=false) test succedes.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+
+	public void testBug21438() throws Exception {
+		createTable("testBug21438","(t_id int(10), test_date timestamp(30) NOT NULL,primary key t_pk (t_id));");		
+		
+		assertEquals(1, this.stmt.executeUpdate("insert into testBug21438 values (1,NOW());"));
+		
+		if (this.versionMeetsMinimum(4, 1)) {
+			this.pstmt = ((com.mysql.jdbc.Connection)this.conn)
+			.serverPrepareStatement("UPDATE testBug21438 SET test_date=ADDDATE(?,INTERVAL 1 YEAR) WHERE t_id=1;");
+	    	
+			try {
+	    		Timestamp ts = new Timestamp(System.currentTimeMillis());
+	    		ts.setNanos(999999999);
+	    		
+	    		this.pstmt.setTimestamp(1, ts);	
+	    	
+	    		assertEquals(1, this.pstmt.executeUpdate());
+	    		
+	    		Timestamp future = (Timestamp)getSingleIndexedValueWithQuery(1, "SELECT test_date FROM testBug21438");
+	    		assertEquals(future.getYear() - ts.getYear(), 1);
 	
+	    	} finally {
+				closeMemberJDBCResources();
+			}        
+		}
+	}
+
 	/**
 	 * Tests fix for BUG#22359 - Driver was using millis for
 	 * Statement.setQueryTimeout() when spec says argument is
@@ -3494,5 +3561,560 @@
 			closeMemberJDBCResources();
 		}
 	}
+	
+	/**
+	 * Tests fix for BUG#24360 .setFetchSize() breaks prepared 
+	 * SHOW and other commands.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug24360() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+	
+		Connection c = null;
+		
+		Properties props = new Properties();
+		props.setProperty("useServerPrepStmts", "true");
+		
+		try {
+			c = getConnectionWithProps(props);
+			
+			this.pstmt = c.prepareStatement("SHOW PROCESSLIST");
+			this.pstmt.setFetchSize(5);
+			this.pstmt.execute();
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (c != null) {
+				c.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#24344 - useJDBCCompliantTimezoneShift with server-side prepared
+	 * statements gives different behavior than when using client-side prepared
+	 * statements. (this is now fixed if moving from server-side prepared statements
+	 * to client-side prepared statements by setting "useSSPSCompatibleTimezoneShift" to
+	 * "true", as the driver can't tell if this is a new deployment that never used 
+	 * server-side prepared statements, or if it is an existing deployment that is
+	 * switching to client-side prepared statements from server-side prepared statements.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug24344() throws Exception {
+		
+		if (!versionMeetsMinimum(4, 1)) {
+			return; // need SSPS
+		}
+		
+		super.createTable("testBug24344", 
+				"(i INT AUTO_INCREMENT, t1 DATETIME, PRIMARY KEY (i)) ENGINE = MyISAM");
+		
+		Connection conn2 = null;
+		
+		try {
+			Properties props = new Properties();
+			props.setProperty("useServerPrepStmts", "true");
+			props.setProperty("useJDBCCompliantTimezoneShift", "true");
+			conn2 = super.getConnectionWithProps(props);
+			this.pstmt = conn2.prepareStatement("INSERT INTO testBug24344 (t1) VALUES (?)");
+			Calendar c = Calendar.getInstance();
+			this.pstmt.setTimestamp(1, new Timestamp(c.getTime().getTime()));
+			this.pstmt.execute();
+			this.pstmt.close();
+			conn2.close();
+			
+			props.setProperty("useServerPrepStmts", "false");
+			props.setProperty("useJDBCCompliantTimezoneShift", "true");
+			props.setProperty("useSSPSCompatibleTimezoneShift", "true");
+			
+			conn2 = super.getConnectionWithProps(props);
+			this.pstmt = conn2.prepareStatement("INSERT INTO testBug24344 (t1) VALUES (?)");
+			this.pstmt.setTimestamp(1, new Timestamp(c.getTime().getTime()));
+			this.pstmt.execute();
+			this.pstmt.close();
+			conn2.close();
+			
+			props.setProperty("useServerPrepStmts", "false");
+			props.setProperty("useJDBCCompliantTimezoneShift", "false");
+			props.setProperty("useSSPSCompatibleTimezoneShift", "false");
+			conn2 = super.getConnectionWithProps(props);
+			this.pstmt = conn2.prepareStatement("INSERT INTO testBug24344 (t1) VALUES (?)");
+			this.pstmt.setTimestamp(1, new Timestamp(c.getTime().getTime()));
+			this.pstmt.execute();
+			this.pstmt.close();
+			
+			Statement s = conn2.createStatement();
+			 this.rs = s.executeQuery("SELECT t1 FROM testBug24344 ORDER BY i ASC");
+			
+			 Timestamp[] dates = new Timestamp[3];
+			
+			int i = 0;
+			
+			while(rs.next()){
+				dates[i++] = rs.getTimestamp(1);
+			}
+			
+			assertEquals( "Number of rows should be 3.", 3, i);
+			assertEquals(dates[0], dates[1]);
+			assertTrue(!dates[1].equals(dates[2]));
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (conn2 != null) {
+				conn2.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#25073 - rewriting batched statements leaks internal statement
+	 * instances, and causes a memory leak.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25073() throws Exception {
+		if (isRunningOnJdk131()) {
+			return;
+		}
+		
+		Properties props = new Properties();
+		props.setProperty("rewriteBatchedStatements", "true");
+		Connection multiConn = getConnectionWithProps(props);
+		createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+		Statement multiStmt = multiConn.createStatement();
+		multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (1)");
+		multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (2)");
+		multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (3)");
+		multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (4)");
+		multiStmt.addBatch("UPDATE testBug25073 SET field1=5 WHERE field1=1");
+		multiStmt.addBatch("UPDATE testBug25073 SET field1=6 WHERE field1=2 OR field1=3");
+		
+		int beforeOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		multiStmt.executeBatch();
+		
+		int afterOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		assertEquals(beforeOpenStatementCount, afterOpenStatementCount);
+		
 
+		createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+		props.clear();
+		props.setProperty("rewriteBatchedStatements", "true");
+		props.setProperty("sessionVariables", "max_allowed_packet=1024");
+		multiConn = getConnectionWithProps(props);
+		multiStmt = multiConn.createStatement();
+		
+		for (int i = 0; i < 1000; i++) {
+			multiStmt.addBatch("INSERT INTO testBug25073(field1) VALUES (" + i + ")");
+		}
+		
+		beforeOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		multiStmt.executeBatch();
+		
+		afterOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		assertEquals(beforeOpenStatementCount, afterOpenStatementCount);
+		
+		createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+		
+		props.clear();
+		props.setProperty("useServerPrepStmts", "false");
+		props.setProperty("rewriteBatchedStatements", "true");
+		multiConn = getConnectionWithProps(props);
+		PreparedStatement pStmt = multiConn.prepareStatement("INSERT INTO testBug25073(field1) VALUES (?)", 
+				Statement.RETURN_GENERATED_KEYS);
+		
+		for (int i = 0; i < 1000; i++) {
+			pStmt.setInt(1, i);
+			pStmt.addBatch();
+		}
+		
+		beforeOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		pStmt.executeBatch();
+		
+		afterOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		assertEquals(beforeOpenStatementCount, afterOpenStatementCount);
+		
+		createTable("testBug25073", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+		props.setProperty("useServerPrepStmts", "false");
+		props.setProperty("rewriteBatchedStatements", "true");
+		props.setProperty("sessionVariables", "max_allowed_packet=1024");
+		multiConn = getConnectionWithProps(props);
+		pStmt = multiConn.prepareStatement("INSERT INTO testBug25073(field1) VALUES (?)", 
+				Statement.RETURN_GENERATED_KEYS);
+		
+		for (int i = 0; i < 1000; i++) {
+			pStmt.setInt(1, i);
+			pStmt.addBatch();
+		}
+		
+		beforeOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		pStmt.executeBatch();
+
+		afterOpenStatementCount = ((com.mysql.jdbc.Connection)multiConn).getActiveStatementCount();
+		
+		assertEquals(beforeOpenStatementCount, afterOpenStatementCount);
+	}
+	
+	/**
+	 * Tests fix for BUG#25009 - Results from updates not handled correctly in multi-statement
+	 * queries.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25009() throws Exception {
+		if (!versionMeetsMinimum(4, 1)) {
+			return;
+		}
+		
+		Properties props = new Properties();
+		props.setProperty("allowMultiQueries", "true");
+		
+		Connection multiConn = getConnectionWithProps(props);
+		createTable("testBug25009", "(field1 INT)");
+		
+		try {
+			Statement multiStmt = multiConn.createStatement();
+			multiStmt.execute("SELECT 1;SET @a=1; SET @b=2; SET @c=3; INSERT INTO testBug25009 VALUES (1)");
+			
+			assertEquals(-1, multiStmt.getUpdateCount());
+			
+			this.rs = multiStmt.getResultSet();
+			assertTrue(this.rs.next());
+			assertEquals(multiStmt.getMoreResults(), false);
+			
+			for (int i = 0; i < 3; i++) {
+				assertEquals(0, multiStmt.getUpdateCount());
+				assertEquals(multiStmt.getMoreResults(), false);
+			}
+			
+			assertEquals(1, multiStmt.getUpdateCount());
+
+			this.rs = multiStmt.executeQuery("SELECT field1 FROM testBug25009");
+			assertTrue(this.rs.next());
+			assertEquals(1, this.rs.getInt(1));
+			
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (multiConn != null) {
+				multiConn.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#25025 - Client-side prepared statement parser gets confused by
+	 * in-line (slash-star) comments and therefore can't rewrite batched statements or
+	 * reliably detect type of statements when they're used.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25025() throws Exception {
+		
+		Connection multiConn = null;
+		
+		createTable("testBug25025", "(field1 INT)");
+		
+		try {
+			Properties props = new Properties();
+			props.setProperty("rewriteBatchedStatements", "true");
+			props.setProperty("useServerPrepStmts", "false");
+			
+			multiConn = getConnectionWithProps(props);
+			
+			this.pstmt = multiConn.prepareStatement("/* insert foo.bar.baz INSERT INTO foo VALUES (?,?,?,?) to trick parser */ INSERT into testBug25025 VALUES (?)");
+			this.pstmt.setInt(1, 1);
+			this.pstmt.addBatch();
+			this.pstmt.setInt(1, 2);
+			this.pstmt.addBatch();
+			this.pstmt.setInt(1, 3);
+			this.pstmt.addBatch();
+			
+			int[] counts = this.pstmt.executeBatch();
+			
+			assertEquals(3, counts.length);
+			assertEquals(1, counts[0]);
+			assertEquals(1, counts[1]);
+			assertEquals(1, counts[2]);
+			assertEquals(true, 
+					((com.mysql.jdbc.PreparedStatement)this.pstmt).canRewriteAsMultivalueInsertStatement());
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (multiConn != null) {
+				multiConn.close();
+			}
+		}
+	}
+	
+	public void testBustedGGKWithPSExecute() throws Exception {
+		createTable("sequence", "(sequence_name VARCHAR(255) NOT NULL PRIMARY KEY, next_val BIGINT NOT NULL)");
+		
+		// Populate with the initial value
+		stmt.executeUpdate("INSERT INTO sequence VALUES ('test-sequence', 1234)");
+		
+		// Atomic operation to increment and return next value
+		PreparedStatement pStmt = null;
+		
+		try {
+			pStmt = this.conn.prepareStatement("UPDATE sequence SET next_val=LAST_INSERT_ID(next_val + ?) WHERE sequence_name = ?",
+				Statement.RETURN_GENERATED_KEYS);
+
+			pStmt.setInt(1, 4);
+			pStmt.setString(2, "test-sequence");
+			pStmt.execute();
+		
+			this.rs = pStmt.getGeneratedKeys();
+			this.rs.next();
+			assertEquals(1238, this.rs.getLong(1));
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#28256 - When connection is in read-only
+	 * mode, queries that are parentheized incorrectly identified
+	 * as DML.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug28256() throws Exception {
+		try {
+			this.conn.setReadOnly(true);
+			this.stmt.execute("(SELECT 1) UNION (SELECT 2)");
+			this.conn.prepareStatement("(SELECT 1) UNION (SELECT 2)").execute();
+			((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement("(SELECT 1) UNION (SELECT 2)").execute();
+		} finally {
+			this.conn.setReadOnly(false);
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#28469 - PreparedStatement.getMetaData()
+	 * for statements containing leading one-line comments
+	 * is not returned correctly.
+	 * 
+	 * As part of this fix, we also overhauled detection of
+	 * DML for executeQuery() and SELECTs for executeUpdate() in
+	 * plain and prepared statements to be aware of the same 
+	 * types of comments.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug28469() throws Exception {
+		PreparedStatement commentStmt = null;
+		
+		try {
+			String[] statementsToTest = {"-- COMMENT\nSELECT 1",
+					"# COMMENT\nSELECT 1",
+					"/* comment */ SELECT 1"};
+			
+			for (int i = 0; i < statementsToTest.length; i++) {
+				commentStmt = this.conn.prepareStatement(statementsToTest[i]);
+				
+				assertNotNull(commentStmt.getMetaData());
+				
+				try {
+					commentStmt.executeUpdate();
+					fail("Should not be able to call executeUpdate() on a SELECT statement!");
+				} catch (SQLException sqlEx) {
+					// expected
+				}
+
+				this.rs = commentStmt.executeQuery();
+				this.rs.next();
+				assertEquals(1, this.rs.getInt(1));
+			}
+			
+			createTable("testBug28469", "(field1 INT)");
+			
+			String[] updatesToTest = {"-- COMMENT\nUPDATE testBug28469 SET field1 = 2",
+					"# COMMENT\nUPDATE testBug28469 SET field1 = 2",
+				"/* comment */ UPDATE testBug28469 SET field1 = 2"};
+			
+			for (int i = 0; i < updatesToTest.length; i++) {
+				commentStmt = this.conn.prepareStatement(updatesToTest[i]);
+				
+				assertNull(commentStmt.getMetaData());
+				
+				try {
+					commentStmt.executeQuery();
+					fail("Should not be able to call executeQuery() on a SELECT statement!");
+				} catch (SQLException sqlEx) {
+					// expected
+				}
+				
+				try {
+					this.stmt.executeQuery(updatesToTest[i]);
+					fail("Should not be able to call executeQuery() on a SELECT statement!");
+				} catch (SQLException sqlEx) {
+					// expected
+				}
+			}
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (commentStmt != null) {
+				commentStmt.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests error with slash-star comment at EOL
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testCommentParsing() throws Exception {
+		createTable("PERSON", "(NAME VARCHAR(32), PERID VARCHAR(32))");
+		
+		try {
+			this.pstmt = this.conn.prepareStatement("SELECT NAME AS name2749_0_, PERID AS perid2749_0_ FROM PERSON WHERE PERID=? /*FOR UPDATE*/");
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#28851 - parser in client-side prepared statements
+	 * eats character following '/' if it's not a multi-line comment.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug28851() throws Exception {
+
+		try {
+			this.pstmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement("SELECT 1/?");
+			this.pstmt.setInt(1, 1);
+			this.rs = this.pstmt.executeQuery();
+
+			assertTrue(this.rs.next());
+
+			assertEquals(1, this.rs.getInt(1));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#28596 - parser in client-side prepared statements
+	 * runs to end of statement, rather than end-of-line for '#' comments.
+	 * 
+	 * Also added support for '--' single-line comments
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug28596() throws Exception {
+		String query = "SELECT #\n" + 
+			"?, #\n" + 
+			"? #?\r\n" + 
+			",-- abcdefg \n" +
+			"?";
+
+		try {
+			this.pstmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement(query);
+			this.pstmt.setInt(1, 1);
+			this.pstmt.setInt(2, 2);
+			this.pstmt.setInt(3, 3);
+			
+			assertEquals(3, this.pstmt.getParameterMetaData().getParameterCount());
+			this.rs = this.pstmt.executeQuery();
+
+			assertTrue(this.rs.next());
+
+			assertEquals(1, this.rs.getInt(1));
+			assertEquals(2, this.rs.getInt(2));
+			assertEquals(3, this.rs.getInt(3));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#30550 - executeBatch() on an empty
+	 * batch when there are no elements in the batch causes a
+	 * divide-by-zero error when rewriting is enabled.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug30550() throws Exception {
+		createTable("testBug30550", "(field1 int)");
+
+		Connection rewriteConn = getConnectionWithProps("rewriteBatchedStatements=true");
+		PreparedStatement batchPStmt = null;
+		Statement batchStmt = null;
+		
+		try {
+			batchStmt = rewriteConn.createStatement();
+			assertEquals(0, batchStmt.executeBatch().length);
+			
+			batchStmt.addBatch("INSERT INTO testBug30550 VALUES (1)");
+			int[] counts = batchStmt.executeBatch();
+			assertEquals(1, counts.length);
+			assertEquals(1, counts[0]);
+			assertEquals(0, batchStmt.executeBatch().length);
+			
+			batchPStmt = rewriteConn.prepareStatement("INSERT INTO testBug30550 VALUES (?)");
+			batchPStmt.setInt(1, 1);
+			assertEquals(0, batchPStmt.executeBatch().length);
+			batchPStmt.addBatch();
+			counts = batchPStmt.executeBatch();
+			assertEquals(1, counts.length);
+			assertEquals(1, counts[0]);
+			assertEquals(0, batchPStmt.executeBatch().length);
+		} finally {
+			if (batchPStmt != null) {
+				batchPStmt.close();
+	}
+			
+			if (batchStmt != null) {
+				batchStmt.close();
 }
+			if (rewriteConn != null) {
+				rewriteConn.close();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for Bug#27412 - cached metadata with PreparedStatement.execute()
+	 * throws NullPointerException.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug27412() throws Exception {
+		try {
+			Properties props = new Properties();
+			props.put("useServerPrepStmts", "false");
+			props.put("cachePreparedStatements", "true");
+			props.put("cacheResultSetMetadata", "true");
+			Connection conn2 = getConnectionWithProps(props);
+			PreparedStatement pstm = conn2.prepareStatement("SELECT 1");
+			try {
+				assertTrue(pstm.execute());
+			} finally {
+				pstm.close();
+				conn2.close();
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+}

Modified: trunk/mysql-connector-java/src/testsuite/regression/StressRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/StressRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/StressRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -147,7 +147,7 @@
 	 *             ...
 	 */
 	public void testCreateConnections() throws Exception {
-		new CreateThread().run();
+		new CreateThread().start();
 	}
 
 	/**
@@ -157,7 +157,7 @@
 	 *             ...
 	 */
 	public void testCreateConnectionsUnderLoad() throws Exception {
-		new CreateThread(new BusyThread()).run();
+		new CreateThread(new BusyThread()).start();
 	}
 
 	void contentiousWork(Connection threadConn, Statement threadStmt,

Modified: trunk/mysql-connector-java/src/testsuite/regression/StringRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/StringRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/StringRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -36,6 +36,7 @@
 
 import testsuite.BaseTestCase;
 
+import com.mysql.jdbc.CharsetMapping;
 import com.mysql.jdbc.StringUtils;
 
 /**
@@ -835,4 +836,85 @@
 			assertEquals(this.rs.getString(1), codePage1252);
 		}
 	}
+
+	/**
+	 * Tests fix for BUG#23645 - Some collations/character sets reported as "unknown"
+	 * (specifically cias variants of existing character sets), and inability to override
+	 * the detected server character set.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug23645() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			// Part of this isn't easily testable, hence the assertion in CharsetMapping
+			// that checks for mappings existing in both directions...
+			
+			// What we test here is the ability to override the character mapping
+			// when the server returns an "unknown" character encoding.
+			
+			String currentlyConfiguredCharacterSet = getSingleIndexedValueWithQuery(2, "SHOW VARIABLES LIKE 'character_set_connection'").toString();
+			System.out.println(currentlyConfiguredCharacterSet);
+			
+			String javaNameForMysqlName = CharsetMapping.getJavaEncodingForMysqlEncoding(currentlyConfiguredCharacterSet, null);
+			System.out.println(javaNameForMysqlName);
+			
+			for (int i = 1; i < CharsetMapping.INDEX_TO_CHARSET.length; i++) {
+				String possibleCharset = CharsetMapping.INDEX_TO_CHARSET[i];
+				
+				if (!javaNameForMysqlName.equals(possibleCharset)) {
+					System.out.println(possibleCharset);
+					
+					Properties props = new Properties();
+					props.setProperty("characterEncoding", possibleCharset);
+					props.setProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex", "65535");
+					
+					Connection forcedCharConn = null;
+					
+					forcedCharConn = getConnectionWithProps(props);
+					
+					String forcedCharset = getSingleIndexedValueWithQuery(forcedCharConn, 2, "SHOW VARIABLES LIKE 'character_set_connection'").toString();
+					
+					System.out.println(forcedCharset);
+					
+					break;
+				}
+			}
+			
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#24840 - character encoding of "US-ASCII"
+	 * doesn't map correctly for 4.1 or newer
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug24840() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("characterEncoding", "US-ASCII");
+		
+		getConnectionWithProps(props).close();
+	}
+
+	/**
+	 * Tests fix for BUG#25047 - StringUtils.indexOfIgnoreCaseRespectQuotes() isn't
+	 * case-insensitive on the first character of the target.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug25047() throws Exception {
+		assertEquals(26, StringUtils.indexOfIgnoreCaseRespectQuotes(0, "insert into Test (TestID) values (?)",
+				"VALUES", '`', false));
+		assertEquals(26, StringUtils.indexOfIgnoreCaseRespectQuotes(0, "insert into Test (TestID) VALUES (?)",
+				"values", '`', false));
+		
+		assertEquals(StringUtils.indexOfIgnoreCaseRespectQuotes(0, 
+				"insert into Test (TestID) values (?)", "VALUES",'`', false),
+				StringUtils.indexOfIgnoreCaseRespectQuotes(0, 
+						"insert into Test (TestID) VALUES (?)",  "VALUES",'`', false));
+		assertEquals(StringUtils.indexOfIgnoreCaseRespectQuotes(0,  
+				"insert into Test (TestID) values (?)", "values", '`', false),
+				StringUtils.indexOfIgnoreCaseRespectQuotes(0, 
+						"insert into Test (TestID) VALUES (?)", "values", '`', false));
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/regression/SubqueriesRegressionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/regression/SubqueriesRegressionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/regression/SubqueriesRegressionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -49,8 +49,9 @@
 	 * @see junit.framework.TestCase#setUp()
 	 */
 	public void setUp() throws Exception {
-		// TODO Auto-generated method stub
 		super.setUp();
+
+		createTables();
 	}
 
 	/*
@@ -59,7 +60,8 @@
 	 * @see junit.framework.TestCase#tearDown()
 	 */
 	public void tearDown() throws Exception {
-		// TODO Auto-generated method stub
+		dropTables();
+		
 		super.tearDown();
 	}
 
@@ -81,7 +83,6 @@
 	public void testSubQuery1() throws Exception {
 		if (versionMeetsMinimum(4, 1)) {
 			for (int i = 0; i < REPETITIONS; i++) {
-				createTables();
 
 				try {
 					this.rs = this.stmt
@@ -90,12 +91,8 @@
 					assertTrue("bbbb".equals(this.rs.getString(1)));
 					assertTrue(!this.rs.next());
 				} finally {
-					try {
-						if (this.rs != null) {
-							this.rs.close();
-						}
-					} finally {
-						dropTables();
+					if (this.rs != null) {
+						this.rs.close();
 					}
 				}
 			}
@@ -111,8 +108,6 @@
 	public void testSubQuery2() throws Exception {
 		if (versionMeetsMinimum(4, 1)) {
 			for (int i = 0; i < REPETITIONS; i++) {
-				createTables();
-
 				try {
 					this.rs = this.stmt
 							.executeQuery("select t3.colA from t3, t1 where t3.colA = 'bbbb' and t3.colB = t1.colA and exists (select 'X' from t2 where t2.colB = 2)");
@@ -120,12 +115,8 @@
 					assertTrue("bbbb".equals(this.rs.getString(1)));
 					assertTrue(!this.rs.next());
 				} finally {
-					try {
-						if (this.rs != null) {
-							this.rs.close();
-						}
-					} finally {
-						dropTables();
+					if (this.rs != null) {
+						this.rs.close();
 					}
 				}
 			}
@@ -141,8 +132,6 @@
 	public void testSubQuery3() throws Exception {
 		if (versionMeetsMinimum(4, 1)) {
 			for (int i = 0; i < REPETITIONS; i++) {
-				createTables();
-
 				try {
 					this.rs = this.stmt
 							.executeQuery("select * from t1 where t1.colA = 'efgh' and exists (select 'X' from t2 where t2.colB = t1.colB)");
@@ -151,13 +140,10 @@
 					assertTrue("2".equals(this.rs.getString(2)));
 					assertTrue(!this.rs.next());
 				} finally {
-					try {
-						if (this.rs != null) {
-							this.rs.close();
-						}
-					} finally {
-						dropTables();
+					if (this.rs != null) {
+						this.rs.close();
 					}
+
 				}
 			}
 		}
@@ -173,8 +159,6 @@
 		// not really a subquery, but we want to have this in our testsuite
 		if (versionMeetsMinimum(4, 1)) {
 			for (int i = 0; i < REPETITIONS; i++) {
-				createTables();
-
 				try {
 					this.rs = this.stmt
 							.executeQuery("select colA, '' from t2 union select colA, colB from t3");
@@ -209,12 +193,8 @@
 
 					assertTrue(!this.rs.next());
 				} finally {
-					try {
-						if (this.rs != null) {
-							this.rs.close();
-						}
-					} finally {
-						dropTables();
+					if (this.rs != null) {
+						this.rs.close();
 					}
 				}
 			}
@@ -230,7 +210,6 @@
 	public void testSubQuery5() throws Exception {
 		if (versionMeetsMinimum(4, 1)) {
 			for (int i = 0; i < REPETITIONS; i++) {
-				createTables();
 
 				try {
 					this.rs = this.stmt
@@ -243,12 +222,8 @@
 					assertTrue("ijkl".equals(this.rs.getString(1)));
 					assertTrue(!this.rs.next());
 				} finally {
-					try {
-						if (this.rs != null) {
-							this.rs.close();
-						}
-					} finally {
-						dropTables();
+					if (this.rs != null) {
+						this.rs.close();
 					}
 				}
 			}

Modified: trunk/mysql-connector-java/src/testsuite/simple/BlobTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/BlobTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/BlobTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -35,25 +35,32 @@
 import java.io.InputStream;
 
 import java.sql.Connection;
-import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.util.Properties;
 
 /**
  * Tests BLOB functionality in the driver.
  * 
  * @author Mark Matthews
- * @version $Id: BlobTest.java 4312 2005-09-26 19:31:34Z mmatthews $
+ * @version $Id: BlobTest.java 6437 2007-05-24 20:17:01Z mmatthews $
  */
 public class BlobTest extends BaseTestCase {
-	// ~ Static fields/initializers
-	// ---------------------------------------------
 
 	private static File testBlobFile;
 
-	// ~ Constructors
-	// -----------------------------------------------------------
-
+	static {
+		Runtime.getRuntime().addShutdownHook(new Thread() {
+			public void run() {
+				for (int i = 0; i < 5; i++) {
+					try {
+						if (testBlobFile.delete()) {
+							break;
+						}
+					} catch (Throwable t) {
+					}
+				}
+			}
+		});
+	}
 	/**
 	 * Creates a new BlobTest object.
 	 * 
@@ -64,9 +71,6 @@
 		super(name);
 	}
 
-	// ~ Methods
-	// ----------------------------------------------------------------
-
 	/**
 	 * Runs all test cases in this test suite
 	 * 
@@ -112,6 +116,8 @@
 	public void tearDown() throws Exception {
 		try {
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS BLOBTEST");
+		} catch (Exception e) {
+			e.printStackTrace();
 		} finally {
 			super.tearDown();
 		}
@@ -259,13 +265,17 @@
 				passed);
 	}
 
+	private final static String TEST_BLOB_FILE_PREFIX = "cmj-testblob";
+	
 	private void createBlobFile(int size) throws Exception {
 		if (testBlobFile != null && testBlobFile.length() != size) {
 			testBlobFile.delete();
 		}
 
-		testBlobFile = File.createTempFile("testblob", ".dat");
+		testBlobFile = File.createTempFile(TEST_BLOB_FILE_PREFIX, ".dat");
 		testBlobFile.deleteOnExit();
+		
+		cleanupTempFiles(testBlobFile, TEST_BLOB_FILE_PREFIX);
 
 		BufferedOutputStream bOut = new BufferedOutputStream(
 				new FileOutputStream(testBlobFile));

Modified: trunk/mysql-connector-java/src/testsuite/simple/CallableStatementTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/CallableStatementTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/CallableStatementTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -25,11 +25,13 @@
 package testsuite.simple;
 
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.log.StandardLogger;
 
 import testsuite.BaseTestCase;
 
 import java.sql.CallableStatement;
 import java.sql.Connection;
+import java.sql.ParameterMetaData;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
@@ -97,47 +99,72 @@
 
 	public void testBatch() throws Exception {
 		if (versionMeetsMinimum(5, 0)) {
-			CallableStatement storedProc = null;
-
+			Connection batchedConn = null;
+			
 			try {
-				this.stmt
-						.executeUpdate("DROP PROCEDURE IF EXISTS testBatch");
 				createTable("testBatchTable", "(field1 INT)");
-				
-				this.stmt
-						.executeUpdate("create procedure testBatch(IN foo VARCHAR(15))\n"
+				createProcedure("testBatch", "(IN foo VARCHAR(15))\n"
 								+ "begin\n"
 								+ "INSERT INTO testBatchTable VALUES (foo);\n"
 								+ "end\n");
 
-				storedProc = this.conn.prepareCall("{call testBatch(?)}");
-
-				storedProc.setInt(1, 1);
-				storedProc.addBatch();
-				storedProc.setInt(1, 2);
-				storedProc.addBatch();
-				int[] counts = storedProc.executeBatch();
+				executeBatchedStoredProc(this.conn);
 				
-				assertEquals(2, counts.length);
-				assertEquals(1, counts[0]);
-				assertEquals(1, counts[1]);
+				batchedConn = getConnectionWithProps("rewriteBatchedStatements=true,profileSQL=true");
 				
-				this.rs = this.stmt.executeQuery("SELECT field1 FROM testBatchTable ORDER BY field1 ASC");
-				assertTrue(this.rs.next());
-				assertEquals(1, this.rs.getInt(1));
-				assertTrue(this.rs.next());
-				assertEquals(2, this.rs.getInt(1));
+				StringBuffer outBuf = new StringBuffer();
+				StandardLogger.bufferedLog = outBuf;
+				executeBatchedStoredProc(batchedConn);
+				String[] log = outBuf.toString().split(";");
+				assertTrue(log.length > 20);
 			} finally {
-				if (this.rs != null) {
-					this.rs.close();
-					this.rs = null;
+				StandardLogger.bufferedLog = null;
+				
+				closeMemberJDBCResources();
+				
+				if (batchedConn != null) {
+					batchedConn.close();
 				}
-				
-				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBatch");
 			}
 		}
 	}
+	
+	private void executeBatchedStoredProc(Connection c) throws Exception {
+		this.stmt.executeUpdate("TRUNCATE TABLE testBatchTable");
+		
+		CallableStatement storedProc = c.prepareCall("{call testBatch(?)}");
 
+		try {
+			int numBatches = 300;
+			
+			for (int i = 0; i < numBatches; i++) {
+				storedProc.setInt(1, i + 1);
+				storedProc.addBatch();
+			}
+			
+			int[] counts = storedProc.executeBatch();
+			
+			assertEquals(numBatches, counts.length);
+			
+			for (int i = 0; i < numBatches; i++) {
+				assertEquals(1, counts[i]);
+			}
+	
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBatchTable ORDER BY field1 ASC");
+			
+			for (int i = 0; i < numBatches; i++) {
+				assertTrue(this.rs.next());
+				assertEquals(i + 1, this.rs.getInt(1));
+			}
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (storedProc != null) {
+				storedProc.close();
+			}
+		}
+	}
+
 	/**
 	 * Tests functioning of output parameters.
 	 * 
@@ -167,38 +194,53 @@
 				System.out.println(storedProc);
 
 				int indexedOutParamToTest = storedProc.getInt(2);
-				int namedOutParamToTest = storedProc.getInt("y");
-
-				assertTrue("Named and indexed parameter are not the same",
+				
+				if (!isRunningOnJdk131()) {
+					int namedOutParamToTest = storedProc.getInt("y");
+				
+					assertTrue("Named and indexed parameter are not the same",
 						indexedOutParamToTest == namedOutParamToTest);
-				assertTrue("Output value not returned correctly",
+					assertTrue("Output value not returned correctly",
 						indexedOutParamToTest == 6);
-
-				// Start over, using named parameters, this time
-				storedProc.clearParameters();
-				storedProc.setInt("x", 32);
-				storedProc.registerOutParameter("y", Types.INTEGER);
-
-				storedProc.execute();
-
-				indexedOutParamToTest = storedProc.getInt(2);
-				namedOutParamToTest = storedProc.getInt("y");
-
-				assertTrue("Named and indexed parameter are not the same",
-						indexedOutParamToTest == namedOutParamToTest);
-				assertTrue("Output value not returned correctly",
-						indexedOutParamToTest == 33);
-
-				try {
-					storedProc.registerOutParameter("x", Types.INTEGER);
-					assertTrue(
-							"Should not be able to register an out parameter on a non-out parameter",
-							true);
-				} catch (SQLException sqlEx) {
-					if (!SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
-							.getSQLState())) {
-						throw sqlEx;
+				
+					// Start over, using named parameters, this time
+					storedProc.clearParameters();
+					storedProc.setInt("x", 32);
+					storedProc.registerOutParameter("y", Types.INTEGER);
+	
+					storedProc.execute();
+	
+					indexedOutParamToTest = storedProc.getInt(2);
+					namedOutParamToTest = storedProc.getInt("y");
+	
+					assertTrue("Named and indexed parameter are not the same",
+							indexedOutParamToTest == namedOutParamToTest);
+					assertTrue("Output value not returned correctly",
+							indexedOutParamToTest == 33);
+	
+					try {
+						storedProc.registerOutParameter("x", Types.INTEGER);
+						assertTrue(
+								"Should not be able to register an out parameter on a non-out parameter",
+								true);
+					} catch (SQLException sqlEx) {
+						if (!SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+								.getSQLState())) {
+							throw sqlEx;
+						}
 					}
+					
+					try {
+						storedProc.getInt("x");
+						assertTrue(
+								"Should not be able to retreive an out parameter on a non-out parameter",
+								true);
+					} catch (SQLException sqlEx) {
+						if (!SQLError.SQL_STATE_COLUMN_NOT_FOUND.equals(sqlEx
+								.getSQLState())) {
+							throw sqlEx;
+						}
+					}
 				}
 
 				try {
@@ -211,19 +253,7 @@
 							.getSQLState())) {
 						throw sqlEx;
 					}
-				}
-
-				try {
-					storedProc.getInt("x");
-					assertTrue(
-							"Should not be able to retreive an out parameter on a non-out parameter",
-							true);
-				} catch (SQLException sqlEx) {
-					if (!SQLError.SQL_STATE_COLUMN_NOT_FOUND.equals(sqlEx
-							.getSQLState())) {
-						throw sqlEx;
-					}
-				}
+				}				
 			} finally {
 				this.stmt.executeUpdate("DROP PROCEDURE testOutParam");
 			}
@@ -377,6 +407,9 @@
 	 *             if an error occurs.
 	 */
 	public void testSPCache() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // no support for LRUCache
+		}
 
 		if (versionMeetsMinimum(5, 0)) {
 
@@ -389,7 +422,7 @@
 						.executeUpdate("CREATE PROCEDURE testSpParse(IN FOO VARCHAR(15))\n"
 								+ "BEGIN\n" + "SELECT 1;\n" + "end\n");
 
-				int numIterations = 10000;
+				int numIterations = 10;
 
 				long startTime = System.currentTimeMillis();
 
@@ -496,4 +529,56 @@
 	public static void main(String[] args) {
 		junit.textui.TestRunner.run(CallableStatementTest.class);
 	}
+	
+	/** Tests the new parameter parser that doesn't require "BEGIN" or "\n" at end
+	 * of parameter declaration
+	 * @throws Exception
+	 */
+	public void testParameterParser() throws Exception {
+
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+
+		CallableStatement cstmt = null;
+
+		try {
+
+			createTable("t1",
+					"(id   char(16) not null default '', data int not null)");
+			createTable("t2", "(s   char(16),  i   int,  d   double)");
+
+			createProcedure("foo42",
+					"() insert into test.t1 values ('foo', 42);");
+			this.conn.prepareCall("{CALL foo42()}");
+			this.conn.prepareCall("{CALL foo42}");
+
+			createProcedure("bar",
+					"(x char(16), y int, z DECIMAL(10)) insert into test.t1 values (x, y);");
+			cstmt = this.conn.prepareCall("{CALL bar(?, ?, ?)}");
+
+			if (!isRunningOnJdk131()) {
+				ParameterMetaData md = cstmt.getParameterMetaData();
+				assertEquals(3, md.getParameterCount());
+				assertEquals(Types.CHAR, md.getParameterType(1));
+				assertEquals(Types.INTEGER, md.getParameterType(2));
+				assertEquals(Types.DECIMAL, md.getParameterType(3));
+			}
+
+			createProcedure("p", "() label1: WHILE @a=0 DO SET @a=1; END WHILE");
+			this.conn.prepareCall("{CALL p()}");
+
+			createFunction("f", "() RETURNS INT return 1; ");
+			cstmt = this.conn.prepareCall("{? = CALL f()}");
+
+			if (!isRunningOnJdk131()) {
+				ParameterMetaData md = cstmt.getParameterMetaData();
+				assertEquals(Types.INTEGER, md.getParameterType(1));
+			}
+		} finally {
+			if (cstmt != null) {
+				cstmt.close();
+			}
+		}
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/simple/CharsetTests.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/CharsetTests.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/CharsetTests.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -23,7 +23,9 @@
 package testsuite.simple;
 
 import java.sql.Connection;
+import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.Types;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -290,4 +292,158 @@
 			}
 		}
 	}
+	
+	public void testUtf8OutsideBMPInBlob() throws Exception {
+		createTable("utf8Test", "(include_blob BLOB, include_tinyblob TINYBLOB, include_longblob LONGBLOB, exclude_tinyblob TINYBLOB, exclude_blob BLOB, exclude_longblob LONGBLOB)");
+		
+		// We know this gets truncated in MySQL currently, even though it's valid UTF-8, it's just 4 bytes encoded
+		String outsideBmp = new String(new byte[] {(byte) 0xF0, (byte) 0x90, (byte) 0x80, (byte) 0x80}, "UTF-8");
+		byte[] outsideBmpBytes = outsideBmp.getBytes("UTF-8");
+		System.out.println(outsideBmpBytes.length);
+		
+		Connection utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8");
+		
+		String insertStatement = "INSERT INTO utf8Test VALUES (?, ?, ?, ?, ?, ?)";
+		
+		this.pstmt = utf8Conn.prepareStatement(insertStatement);
+		
+		this.pstmt.setString(1, outsideBmp);
+		this.pstmt.setString(2, outsideBmp);
+		this.pstmt.setString(3, outsideBmp);
+		this.pstmt.setString(4, outsideBmp);
+		this.pstmt.setString(5, outsideBmp);
+		this.pstmt.setString(6, outsideBmp);
+		this.pstmt.executeUpdate();
+		
+		String query = "SELECT include_blob, include_tinyblob, include_longblob, exclude_tinyblob, exclude_blob, exclude_longblob FROM utf8Test";
+		this.rs = utf8Conn.createStatement().executeQuery(query);
+		this.rs.next();
+		
+		assertEquals(this.rs.getObject(1).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(2).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(3).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(4).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(5).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(6).toString(), outsideBmp);
+		
+		assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(1));
+		assertEquals(Types.VARCHAR, this.rs.getMetaData().getColumnType(1));
+		
+		assertEquals("java.lang.String", this.rs.getObject(2).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(2));
+		assertEquals(Types.VARCHAR, this.rs.getMetaData().getColumnType(2));
+		
+		assertEquals("java.lang.String", this.rs.getObject(3).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(3));
+		assertEquals(Types.LONGVARCHAR, this.rs.getMetaData().getColumnType(3));
+		
+		assertEquals("java.lang.String", this.rs.getObject(4).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(4));
+		assertEquals(Types.VARCHAR, this.rs.getMetaData().getColumnType(4));
+		
+		assertEquals("java.lang.String", this.rs.getObject(5).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(5));
+		assertEquals(Types.VARCHAR, this.rs.getMetaData().getColumnType(5));
+		
+		assertEquals("java.lang.String", this.rs.getObject(6).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(6));
+		assertEquals(Types.LONGVARCHAR, this.rs.getMetaData().getColumnType(6));
+		
+		utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern=.*include.*,utf8OutsideBmpExcludedColumnNamePattern=.*blob");
+		
+		this.rs = utf8Conn.createStatement().executeQuery(query);
+		this.rs.next();
+		
+		// Should walk/talk like a string, encoded in utf-8 on the server (4-byte)
+		assertEquals(this.rs.getObject(1).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(2).toString(), outsideBmp);
+		assertEquals(this.rs.getObject(3).toString(), outsideBmp);
+		
+		assertEquals("java.lang.String", this.rs.getObject(1).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(1));
+		assertEquals(Types.VARCHAR, this.rs.getMetaData().getColumnType(1));
+		
+		assertEquals("java.lang.String", this.rs.getObject(2).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(2));
+		assertEquals(Types.VARCHAR, this.rs.getMetaData().getColumnType(2));
+		
+		assertEquals("java.lang.String", this.rs.getObject(3).getClass().getName());
+		assertEquals("java.lang.String", this.rs.getMetaData().getColumnClassName(3));
+		assertEquals(Types.LONGVARCHAR, this.rs.getMetaData().getColumnType(3));
+		
+		// These should be left as a blob, since it matches the exclusion regex
+		assertTrue(bytesAreSame(this.rs.getBytes(4), outsideBmpBytes));
+		assertEquals("[B", this.rs.getObject(4).getClass().getName());
+		assertEquals("[B", this.rs.getMetaData().getColumnClassName(4));
+		assertEquals(Types.VARBINARY, this.rs.getMetaData().getColumnType(4));
+		
+		// Should behave types-wise just like BLOB, including LONGVARBINARY type mapping
+		assertTrue(bytesAreSame(this.rs.getBytes(5), outsideBmpBytes));
+		assertEquals("[B", this.rs.getObject(5).getClass().getName());
+		assertEquals("[B", this.rs.getMetaData().getColumnClassName(5));
+		assertEquals(Types.LONGVARBINARY, this.rs.getMetaData().getColumnType(5));
+		
+		assertTrue(bytesAreSame(this.rs.getBytes(6), outsideBmpBytes));
+		assertEquals("[B", this.rs.getObject(6).getClass().getName());
+		assertEquals("[B", this.rs.getMetaData().getColumnClassName(6));
+		assertEquals(Types.LONGVARBINARY, this.rs.getMetaData().getColumnType(6));
+		
+		//
+		// Check error handling
+		//
+		
+		utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern={{");
+		
+		try {
+			utf8Conn.createStatement().executeQuery(query);
+			fail("Expected an exception");
+		} catch (SQLException sqlEx) {
+			assertNotNull(sqlEx.getCause());
+			assertEquals("java.util.regex.PatternSyntaxException", sqlEx.getCause().getClass().getName());
+		}
+		
+		utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern=.*");
+		
+		try {
+			utf8Conn.createStatement().executeQuery(query);
+			fail("Expected an exception");
+		} catch (SQLException sqlEx) {
+			assertNotNull(sqlEx.getCause());
+			assertEquals("java.util.regex.PatternSyntaxException", sqlEx.getCause().getClass().getName());
+		}
+		
+		utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern={{,paranoid=true");
+		
+		try {
+			utf8Conn.createStatement().executeQuery(query);
+			fail("Expected an exception");
+		} catch (SQLException sqlEx) {
+			assertNull(sqlEx.getCause());
+		}
+		
+		utf8Conn = getConnectionWithProps("useBlobToStoreUTF8OutsideBMP=true, characterEncoding=UTF-8,utf8OutsideBmpIncludedColumnNamePattern={{,utf8OutsideBmpExcludedColumnNamePattern=.*,paranoid=true");
+		
+		try {
+			utf8Conn.createStatement().executeQuery(query);
+			fail("Expected an exception");
+		} catch (SQLException sqlEx) {
+			assertNull(sqlEx.getCause());
+		}
+		
+	}
+	
+	private boolean bytesAreSame(byte[] byte1, byte[] byte2) {
+		if (byte1.length != byte2.length) {
+			return false;
+		}
+
+		for (int i = 0; i < byte1.length; i++) {
+			if (byte1[i] != byte2[i]) {
+				return false;
+			}
+		}
+
+		return true;
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/simple/ConnectionTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/ConnectionTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/ConnectionTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -29,8 +29,15 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileWriter;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
+import java.sql.ParameterMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
@@ -38,13 +45,19 @@
 import java.sql.Savepoint;
 import java.sql.Statement;
 
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Properties;
 import java.util.StringTokenizer;
 
 import com.mysql.jdbc.ConnectionPropertiesTransform;
+import com.mysql.jdbc.Driver;
 import com.mysql.jdbc.NonRegisteringDriver;
 import com.mysql.jdbc.SQLError;
 import com.mysql.jdbc.StringUtils;
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
 import com.mysql.jdbc.log.StandardLogger;
 
 /**
@@ -187,7 +200,10 @@
 			this.conn.createStatement().executeQuery(
 					"SELECT * FROM t1 WHERE id=0 FOR UPDATE");
 
-			Connection deadlockConn = getConnectionWithProps(new Properties());
+			Properties props = new Properties();
+			props.setProperty("includeInnodbStatusInDeadlockExceptions", "true");
+			
+			Connection deadlockConn = getConnectionWithProps(props);
 			deadlockConn.setAutoCommit(false);
 
 			// The following query should hang because con1 is locking the page
@@ -208,6 +224,8 @@
 			//
 			assertTrue(SQLError.SQL_STATE_DEADLOCK.equals(sqlEx.getSQLState()));
 			assertTrue(sqlEx.getErrorCode() == 1205);
+			// Make sure INNODB Status is getting dumped into error message
+			assertTrue(sqlEx.getMessage().indexOf("INNODB MONITOR") != -1);
 		} finally {
 			this.conn.setAutoCommit(true);
 			this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
@@ -653,7 +671,7 @@
 	public void testNonStandardConnectionCollation() throws Exception {
 		if (versionMeetsMinimum(4, 1)) {
 			String collationToSet = "utf8_bin";
-			String characterSet = "utf8";
+			String characterSet = "utf-8";
 
 			Properties props = new Properties();
 			props.setProperty("connectionCollation", collationToSet);
@@ -829,6 +847,36 @@
 		}
 	}
 
+	public void testLocalInfileDisabled() throws Exception {
+		createTable("testLocalInfileDisabled", "(field1 varchar(255))");
+		
+		File infile = File.createTempFile("foo", "txt");
+		infile.deleteOnExit();
+		String url = infile.toURL().toExternalForm();
+		FileWriter output = new FileWriter(infile);
+		output.write("Test");
+		output.flush();
+		output.close();
+		
+		Connection loadConn = getConnectionWithProps(new Properties());
+		
+		try {
+			// have to do this after connect, otherwise it's the server
+			// that's enforcing it
+			((com.mysql.jdbc.Connection)loadConn).setAllowLoadLocalInfile(false);
+			try {
+				loadConn.createStatement().execute("LOAD DATA LOCAL INFILE '" + infile.getCanonicalPath() + "' INTO TABLE testLocalInfileDisabled");
+				fail("Should've thrown an exception.");
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_GENERAL_ERROR, sqlEx.getSQLState());
+			}
+			
+			assertFalse(loadConn.createStatement().executeQuery("SELECT * FROM testLocalInfileDisabled").next());
+		} finally {
+			loadConn.close();
+		}
+	}
+	
 	public void testServerConfigurationCache() throws Exception {
 		Properties props = new Properties();
 
@@ -1084,14 +1132,14 @@
 				noTrackStatement.close();
 			}
 
-			if (noTrackConn != null & !noTrackConn.isClosed()) {
+			if (noTrackConn != null && !noTrackConn.isClosed()) {
 				noTrackConn.close();
 			}
 		}
 	}
 
 	public void testPing() throws SQLException {
-		Connection conn2 = getConnectionWithProps(null);
+		Connection conn2 = getConnectionWithProps((String)null);
 
 		((com.mysql.jdbc.Connection) conn2).ping();
 		conn2.close();
@@ -1200,4 +1248,346 @@
         stmt1.close();
         conn1.close();
     }
+    
+    /**
+     * Tests feature of "localSocketAddress", by enumerating local IF's and
+     * trying each one in turn. This test might take a long time to run, since
+     * we can't set timeouts if we're using localSocketAddress. We try and keep
+     * the time down on the testcase by spawning the checking of each interface
+     * off into separate threads.
+     * 
+     * @throws Exception if the test can't use at least one of the local machine's
+     *                   interfaces to make an outgoing connection to the server.
+     */
+    public void testLocalSocketAddress() throws Exception {
+    	if (isRunningOnJdk131()) { 
+    		return;
+    	}
+    	
+    	Enumeration allInterfaces = NetworkInterface.getNetworkInterfaces();
+    	
+    	
+    	SpawnedWorkerCounter counter = new SpawnedWorkerCounter();
+    	
+    	List allChecks = new ArrayList();
+    	
+    	while (allInterfaces.hasMoreElements()) {
+    		NetworkInterface intf = (NetworkInterface)allInterfaces.nextElement();
+    		
+    		Enumeration allAddresses = intf.getInetAddresses();
+
+    		allChecks.add(new LocalSocketAddressCheckThread(allAddresses, counter));
+    	}
+    	
+    	counter.setWorkerCount(allChecks.size());
+    	
+    	for (Iterator it = allChecks.iterator(); it.hasNext();) {
+    		LocalSocketAddressCheckThread t = (LocalSocketAddressCheckThread)it.next();
+    		t.start();
+    	}
+    	
+    	// Wait for tests to complete....
+    	synchronized (counter) {
+    	
+    		while (counter.workerCount > 0 /* safety valve */) {
+    		
+    			counter.wait();
+
+    			if (counter.workerCount == 0) {
+    				System.out.println("Done!");
+    				break;
+    			}
+    		}
+    	}
+    	
+    	boolean didOneWork = false;
+    	boolean didOneFail = false;
+    	
+    	for (Iterator it = allChecks.iterator(); it.hasNext();) {
+    		LocalSocketAddressCheckThread t = (LocalSocketAddressCheckThread)it.next();
+
+    		if (t.atLeastOneWorked) {
+    			didOneWork = true;
+    			
+    			break;
+    		} else {
+    			if (!didOneFail) {
+    				didOneFail = true;
+    			}
+    		}
+    	}
+    	
+    	assertTrue("At least one connection was made with the localSocketAddress set", didOneWork);
+    	
+    	NonRegisteringDriver d = new NonRegisteringDriver();
+    	
+    	String hostname = d.host(d.parseURL(dbUrl, null));
+    	
+    	if (!hostname.startsWith(":") && !hostname.startsWith("localhost")) {
+    		
+    		int indexOfColon = hostname.indexOf(":");
+    		
+    		if (indexOfColon != -1) {
+    			hostname = hostname.substring(0, indexOfColon);
+    		}
+    		
+    		boolean isLocalIf = false;
+    		
+    		isLocalIf = (null != NetworkInterface.getByName(hostname));
+    		
+    		if (!isLocalIf) {
+    			try {
+    				isLocalIf = (null != NetworkInterface.getByInetAddress(InetAddress.getByName(hostname)));
+    			} catch (Throwable t) {
+    				isLocalIf = false;
+    			}
+    		}
+    		
+    		if (!isLocalIf) {
+    			assertTrue("At least one connection didn't fail with localSocketAddress set", didOneFail);
+    		}
+    	}
+    }
+    
+    class SpawnedWorkerCounter {
+    	private int workerCount = 0;
+    	
+    	synchronized void setWorkerCount(int i) {
+    		workerCount = i;
+    	}
+    	
+    	synchronized void decrementWorkerCount() {
+    		workerCount--;
+    		notify();
+    	}
+    }
+    
+    class LocalSocketAddressCheckThread extends Thread {
+    	boolean atLeastOneWorked = false;
+    	Enumeration allAddresses = null;
+    	SpawnedWorkerCounter counter = null;
+    	
+    	LocalSocketAddressCheckThread(Enumeration e, SpawnedWorkerCounter c) {
+    		allAddresses = e;
+    		counter = c;
+    	}
+    	
+    	public void run() {
+    		
+    		while (allAddresses.hasMoreElements()) {
+    			InetAddress addr = (InetAddress)allAddresses.nextElement();
+    			
+    			try {
+    				Properties props = new Properties();
+    				props.setProperty("localSocketAddress", addr.getHostAddress());
+    				props.setProperty("connectTimeout", "2000");
+    				getConnectionWithProps(props).close();
+    				
+    				atLeastOneWorked = true;
+    				
+    				break;
+    			} catch (SQLException sqlEx) {
+    				// ignore, we're only seeing if one of these tests succeeds
+    			}
+    		}
+    		
+    		counter.decrementWorkerCount();
+    	}
+    }
+    
+    public void testUsageAdvisorTooLargeResultSet() throws Exception {
+    	Connection uaConn = null;
+    	
+    	PrintStream stderr = System.err;
+    	
+    	StringBuffer logBuf = new StringBuffer();
+    	
+    	StandardLogger.bufferedLog = logBuf;
+    	
+    	try {
+    		Properties props = new Properties();
+    		props.setProperty("useUsageAdvisor", "true");
+    		props.setProperty("resultSetSizeThreshold", "4");
+    		props.setProperty("logger", "StandardLogger");
+    		
+    		uaConn = getConnectionWithProps(props);
+    		
+    		assertTrue("Result set threshold message not present", 
+    				logBuf.toString().indexOf("larger than \"resultSetSizeThreshold\" of 4 rows") != -1);
+    	} finally {
+    		System.setErr(stderr);
+    		
+    		closeMemberJDBCResources();
+    		
+    		if (uaConn != null) {
+    			uaConn.close();
+    		}
+    	}
+    }
+    
+    public void testUseLocalSessionStateRollback() throws Exception {
+    	if (!versionMeetsMinimum(5, 0, 0)) {
+    		return;
+    	}
+    	
+    	Properties props = new Properties();
+    	props.setProperty("useLocalSessionState", "true");
+    	props.setProperty("profileSQL", "true");
+    	
+    	StringBuffer buf = new StringBuffer();
+    	StandardLogger.bufferedLog = buf;
+    	
+    	createTable("testUseLocalSessionState", "(field1 varchar(32)) ENGINE=InnoDB");
+    	
+    	Connection localStateConn = null;
+    	Statement localStateStmt = null;
+    	
+    	try {
+    		localStateConn = getConnectionWithProps(props);
+        	localStateStmt = localStateConn.createStatement();
+        	
+	    	localStateConn.setAutoCommit(false);
+	    	localStateStmt.executeUpdate("INSERT INTO testUseLocalSessionState VALUES ('abc')");
+	    	localStateConn.rollback();
+	    	localStateConn.rollback();
+	    	localStateStmt.executeUpdate("INSERT INTO testUseLocalSessionState VALUES ('abc')");
+	    	localStateConn.commit();
+	    	localStateConn.commit();
+	    	localStateStmt.close();
+    	} finally {
+    		StandardLogger.bufferedLog = null;
+    		 
+    		if (localStateStmt != null) {
+    			localStateStmt.close();
+    		}
+    		
+    		if (localStateConn != null) {
+    			localStateConn.close();
+    		}
+    	}
+    	
+    	int rollbackCount = 0;
+    	int rollbackPos = 0;
+    	
+    	String searchIn = buf.toString();
+    	
+    	while (rollbackPos != -1) {
+    		rollbackPos = searchIn.indexOf("rollback", rollbackPos);
+    		
+    		if (rollbackPos != -1) {
+    			rollbackPos += "rollback".length();
+    			rollbackCount++;
+    		}
+    	}
+    	
+    	assertEquals(1, rollbackCount);
+    	
+    	int commitCount = 0;
+    	int commitPos = 0;
+    	
+    	// space is important here, we don't want to count "autocommit"
+    	while (commitPos != -1) {
+    		commitPos = searchIn.indexOf(" commit", commitPos);
+    		
+    		if (commitPos != -1) {
+    			commitPos += " commit".length();
+    			commitCount++;
+    		}
+    	}
+    	
+    	assertEquals(1, commitCount);
+    }
+    
+    /**
+     * Checks if setting useCursorFetch to "true" automatically
+     * enables server-side prepared statements.
+     */
+     
+    public void testCouplingOfCursorFetch() throws Exception {
+    	if (!versionMeetsMinimum(5, 0)) {
+    		return;
+    	}
+    	
+    	Connection fetchConn = null;
+    	
+    	try {
+    		Properties props = new Properties();
+    		props.setProperty("useServerPrepStmts", "false"); // force the issue
+    		props.setProperty("useCursorFetch", "true");
+    		fetchConn = getConnectionWithProps(props);
+    		assertEquals("com.mysql.jdbc.ServerPreparedStatement",
+    				fetchConn.prepareStatement("SELECT 1").getClass().getName());
+    	} finally {
+    		if (fetchConn != null) {
+    			fetchConn.close();
+    		}
+    	}
+    }
+    
+    public void testInterfaceImplementation() throws Exception {
+    	testInterfaceImplementation(getConnectionWithProps((Properties)null));
+    	MysqlConnectionPoolDataSource cpds = new MysqlConnectionPoolDataSource();
+    	cpds.setUrl(dbUrl);
+    	testInterfaceImplementation(cpds.getPooledConnection().getConnection());
+    }
+    
+    private void testInterfaceImplementation(Connection connToCheck) throws Exception {
+    	Method[] dbmdMethods = java.sql.DatabaseMetaData.class.getMethods();
+    	
+    	// can't do this statically, as we return different
+    	// implementations depending on JDBC version
+    	DatabaseMetaData dbmd = connToCheck.getMetaData();
+    	
+    	checkInterfaceImplemented(dbmdMethods, dbmd.getClass(), dbmd);
+    	
+    	Statement stmtToCheck = connToCheck.createStatement();
+    	
+    	checkInterfaceImplemented(java.sql.Statement.class.getMethods(), stmtToCheck.getClass(), stmtToCheck);
+    	
+    	PreparedStatement pStmtToCheck = connToCheck.prepareStatement("SELECT 1");
+    	ParameterMetaData paramMd = pStmtToCheck.getParameterMetaData();
+    	
+    	checkInterfaceImplemented(java.sql.PreparedStatement.class.getMethods(), pStmtToCheck.getClass(), pStmtToCheck);
+    	checkInterfaceImplemented(java.sql.ParameterMetaData.class.getMethods(), paramMd.getClass(), paramMd);
+    	
+    	pStmtToCheck = ((com.mysql.jdbc.Connection) connToCheck).serverPrepareStatement("SELECT 1");
+    	
+    	checkInterfaceImplemented(java.sql.PreparedStatement.class.getMethods(), pStmtToCheck.getClass(), pStmtToCheck);
+    	ResultSet toCheckRs = connToCheck.createStatement().executeQuery("SELECT 1");
+    	checkInterfaceImplemented(java.sql.ResultSet.class.getMethods(), toCheckRs.getClass(), toCheckRs);
+    	toCheckRs = connToCheck.createStatement().executeQuery("SELECT 1");
+    	checkInterfaceImplemented(java.sql.ResultSetMetaData.class.getMethods(), toCheckRs.getMetaData().getClass(), toCheckRs.getMetaData());
+    	
+    	if (versionMeetsMinimum(5, 0, 0)) {
+    		createProcedure("interfaceImpl", "(IN p1 INT)\nBEGIN\nSELECT 1;\nEND");
+    		
+    		CallableStatement cstmt = connToCheck.prepareCall("{CALL interfaceImpl(?)}");
+    		
+    		checkInterfaceImplemented(java.sql.CallableStatement.class.getMethods(), cstmt.getClass(), cstmt);
+    	}
+    	checkInterfaceImplemented(java.sql.Connection.class.getMethods(), connToCheck.getClass(), connToCheck);
+    }
+
+	private void checkInterfaceImplemented(Method[] interfaceMethods,
+			Class implementingClass, Object invokeOn) throws NoSuchMethodException {
+		for (int i = 0; i < interfaceMethods.length; i++) {
+    		Method toFind = interfaceMethods[i];
+    		Method toMatch = implementingClass.getMethod(toFind.getName(), toFind.getParameterTypes());
+    		assertNotNull(toFind.toString(), toMatch);
+
+    		Object[] args = new Object[toFind.getParameterTypes().length];
+    		
+    		try {
+				toMatch.invoke(invokeOn, args);
+			} catch (IllegalArgumentException e) {
+				
+			} catch (IllegalAccessException e) {
+				
+			} catch (InvocationTargetException e) {
+				
+			} catch (java.lang.AbstractMethodError e) {
+				throw e;
+			}
+    	}
+	}
 }

Modified: trunk/mysql-connector-java/src/testsuite/simple/DataSourceTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/DataSourceTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/DataSourceTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License as 
@@ -43,11 +43,12 @@
 import javax.sql.PooledConnection;
 
 import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
+import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
 
 /**
  * 
  * @author Mark Matthews
- * @version $Id: DataSourceTest.java 4251 2005-09-15 15:41:26Z mmatthews $
+ * @version $Id: DataSourceTest.java 6562 2007-09-05 16:02:17Z mmatthews $
  */
 public class DataSourceTest extends BaseTestCase {
 	// ~ Instance fields
@@ -181,7 +182,7 @@
 			assertEquals(toCheck, this.rs.getString(1));
 
 			this.rs = connToMySQL.createStatement().executeQuery(
-					"SHOW VARIABLES LIKE 'character_set_client'");
+					"SHOW SESSION VARIABLES LIKE 'character_set_client'");
 			assertTrue(this.rs.next());
 			assertEquals("utf8", this.rs.getString(2));
 
@@ -194,7 +195,7 @@
 			assertEquals(toCheck, this.rs.getString(1));
 
 			this.rs = connToMySQL.createStatement().executeQuery(
-					"SHOW VARIABLES LIKE 'character_set_client'");
+					"SHOW SESSION VARIABLES LIKE 'character_set_client'");
 			assertTrue(this.rs.next());
 			assertEquals("utf8", this.rs.getString(2));
 
@@ -203,6 +204,24 @@
 	}
 
 	/**
+	 * Tests whether XADataSources can be bound into JNDI
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testXADataSource() throws Exception {
+	
+		MysqlXADataSource ds = new MysqlXADataSource();
+		ds.setUrl(dbUrl);
+		
+		String name = "XA";
+		this.ctx.rebind(name, ds);
+
+		Object result = this.ctx.lookup(name);
+
+		assertNotNull("XADataSource not bound into JNDI", result);
+	}
+	
+	/**
 	 * This method is separated from the rest of the example since you normally
 	 * would NOT register a JDBC driver in your code. It would likely be
 	 * configered into your naming and directory service using some GUI.

Modified: trunk/mysql-connector-java/src/testsuite/simple/MetadataTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/MetadataTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/MetadataTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -540,7 +540,9 @@
                 assertEquals("t1", this.rs.getString("TABLE_NAME"));
                 assertEquals("c1", this.rs.getString("COLUMN_NAME"));
             } finally {
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }
@@ -566,7 +568,9 @@
                 assertEquals("1", this.rs.getString("NON_UNIQUE"));
                 assertEquals("index1", this.rs.getString("INDEX_NAME"));
             } finally {
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }
@@ -591,7 +595,9 @@
                 assertEquals("char", this.rs.getString("TYPE_NAME"));
                 assertEquals("1", this.rs.getString("COLUMN_SIZE"));
             } finally {
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }
@@ -623,7 +629,9 @@
                 }
                 assertTrue(tableNames.isEmpty());
             } finally {
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }
@@ -717,7 +725,9 @@
                 assertEquals("sp1", this.rs.getString("PROCEDURE_NAME"));
                 assertEquals("1", this.rs.getString("PROCEDURE_TYPE"));
             } finally {
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
                 this.stmt.executeUpdate("DROP PROCEDURE sp1");
             }
         }
@@ -749,7 +759,9 @@
             } finally {
                 this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
                 this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }
@@ -780,7 +792,9 @@
             } finally {
                 this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
                 this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }
@@ -811,7 +825,9 @@
             } finally {
                 this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
                 this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
-                conn1.close();
+                if (conn1 != null) {
+					conn1.close();
+				}
             }
         }
     }

Added: trunk/mysql-connector-java/src/testsuite/simple/ResultSetTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/ResultSetTest.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/simple/ResultSetTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,260 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+
+package testsuite.simple;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.Properties;
+
+import com.mysql.jdbc.CharsetMapping;
+
+import testsuite.BaseTestCase;
+
+public class ResultSetTest extends BaseTestCase {
+
+	public ResultSetTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(ResultSetTest.class);
+	}
+
+	public void testPadding() throws Exception {
+		if (!versionMeetsMinimum(4, 1, 0)) {
+			return;
+		}
+
+		Connection paddedConn = null;
+
+		int numChars = 32;
+
+		Iterator charsetNames = CharsetMapping.STATIC_CHARSET_TO_NUM_BYTES_MAP
+				.keySet().iterator();
+		StringBuffer columns = new StringBuffer();
+		StringBuffer emptyBuf = new StringBuffer();
+		StringBuffer abcBuf = new StringBuffer();
+		StringBuffer repeatBuf = new StringBuffer();
+		StringBuffer selectBuf = new StringBuffer();
+
+		int counter = 0;
+
+		while (charsetNames.hasNext()) {
+			String charsetName = charsetNames.next().toString();
+
+			if (charsetName.equalsIgnoreCase("LATIN7")
+					|| charsetName.equalsIgnoreCase("BINARY")) {
+				continue; // no mapping in Java
+			}
+
+			if (counter != 0) {
+				columns.append(",");
+				emptyBuf.append(",");
+				abcBuf.append(",");
+				repeatBuf.append(",");
+				selectBuf.append(",");
+			}
+
+			emptyBuf.append("''");
+			abcBuf.append("'abc'");
+			repeatBuf.append("REPEAT('b', " + numChars + ")");
+
+			columns.append("field_");
+			columns.append(charsetName);
+
+			columns.append(" CHAR(");
+			columns.append(numChars);
+			columns.append(") CHARACTER SET ");
+			columns.append(charsetName);
+
+			selectBuf.append("field_");
+			selectBuf.append(charsetName);
+
+			counter++;
+		}
+
+		createTable("testPadding", "(" + columns.toString() + ", ord INT)");
+
+		this.stmt.executeUpdate("INSERT INTO testPadding VALUES ("
+				+ emptyBuf.toString() + ", 1), (" + abcBuf.toString()
+				+ ", 2), (" + repeatBuf.toString() + ", 3)");
+
+		try {
+			Properties props = new Properties();
+			props.setProperty("padCharsWithSpace", "true");
+
+			paddedConn = getConnectionWithProps(props);
+
+			testPaddingForConnection(paddedConn, numChars, selectBuf);
+
+			props.setProperty("useDynamicCharsetInfo", "true");
+
+			paddedConn = getConnectionWithProps(props);
+
+			testPaddingForConnection(paddedConn, numChars, selectBuf);
+		} finally {
+			closeMemberJDBCResources();
+
+			if (paddedConn != null) {
+				paddedConn.close();
+			}
+		}
+	}
+
+	private void testPaddingForConnection(Connection paddedConn, int numChars,
+			StringBuffer selectBuf) throws SQLException {
+
+		String query = "SELECT " + selectBuf.toString()
+				+ " FROM testPadding ORDER by ord";
+
+		this.rs = paddedConn.createStatement().executeQuery(query);
+		int numCols = this.rs.getMetaData().getColumnCount();
+
+		while (this.rs.next()) {
+			for (int i = 0; i < numCols; i++) {
+				assertEquals("For column '"
+						+ this.rs.getMetaData().getColumnName(i + 1)
+						+ "' of collation "
+						+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+								.getMetaData()).getColumnCharacterSet(i + 1),
+						numChars, this.rs.getString(i + 1).length());
+			}
+		}
+
+		this.rs = ((com.mysql.jdbc.Connection) paddedConn)
+				.clientPrepareStatement(query).executeQuery();
+
+		while (this.rs.next()) {
+			for (int i = 0; i < numCols; i++) {
+				assertEquals("For column '"
+						+ this.rs.getMetaData().getColumnName(i + 1)
+						+ "' of collation "
+						+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+								.getMetaData()).getColumnCharacterSet(i + 1),
+						numChars, this.rs.getString(i + 1).length());
+			}
+		}
+
+		if (versionMeetsMinimum(4, 1)) {
+			this.rs = ((com.mysql.jdbc.Connection) paddedConn).serverPrepareStatement(
+					query).executeQuery();
+
+			while (this.rs.next()) {
+				for (int i = 0; i < numCols; i++) {
+					assertEquals("For column '"
+							+ this.rs.getMetaData().getColumnName(i + 1)
+							+ "' of collation "
+							+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData())
+									.getColumnCharacterSet(i + 1), numChars,
+							this.rs.getString(i + 1).length());
+				}
+			}
+		}
+
+		this.rs = this.stmt.executeQuery(query);
+
+		while (this.rs.next()) {
+			for (int i = 0; i < numCols; i++) {
+				if (this.rs.getRow() != 3) {
+					assertTrue("For column '"
+							+ this.rs.getMetaData().getColumnName(i + 1)
+							+ "' of collation "
+							+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData())
+									.getColumnCharacterSet(i + 1),
+							numChars != this.rs.getString(i + 1).length());
+				} else {
+					assertEquals("For column '"
+							+ this.rs.getMetaData().getColumnName(i + 1)
+							+ "' of collation "
+							+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData())
+									.getColumnCharacterSet(i + 1), numChars,
+							this.rs.getString(i + 1).length());
+				}
+			}
+		}
+
+		this.rs = ((com.mysql.jdbc.Connection) this.conn)
+				.clientPrepareStatement(query).executeQuery();
+
+		while (this.rs.next()) {
+			for (int i = 0; i < numCols; i++) {
+				if (this.rs.getRow() != 3) {
+					assertTrue("For column '"
+							+ this.rs.getMetaData().getColumnName(i + 1)
+							+ "' of collation "
+							+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData())
+									.getColumnCharacterSet(i + 1),
+							numChars != this.rs.getString(i + 1).length());
+				} else {
+					assertEquals("For column '"
+							+ this.rs.getMetaData().getColumnName(i + 1)
+							+ "' of collation "
+							+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData())
+									.getColumnCharacterSet(i + 1), numChars,
+							this.rs.getString(i + 1).length());
+				}
+			}
+		}
+
+		if (versionMeetsMinimum(4, 1)) {
+			this.rs = ((com.mysql.jdbc.Connection) this.conn).serverPrepareStatement(
+					query).executeQuery();
+
+			while (this.rs.next()) {
+				for (int i = 0; i < numCols; i++) {
+					if (this.rs.getRow() != 3) {
+						assertTrue("For column '"
+								+ this.rs.getMetaData().getColumnName(i + 1)
+								+ "' of collation "
+								+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+										.getMetaData())
+										.getColumnCharacterSet(i + 1),
+								numChars != this.rs.getString(i + 1).length());
+					} else {
+						assertEquals("For column '"
+								+ this.rs.getMetaData().getColumnName(i + 1)
+								+ "' of collation "
+								+ ((com.mysql.jdbc.ResultSetMetaData) this.rs
+										.getMetaData())
+										.getColumnCharacterSet(i + 1),
+								numChars, this.rs.getString(i + 1).length());
+					}
+				}
+			}
+		}
+	}
+}

Modified: trunk/mysql-connector-java/src/testsuite/simple/StatementsTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/StatementsTest.java	2007-11-30 10:09:36 UTC (rev 4920)
+++ trunk/mysql-connector-java/src/testsuite/simple/StatementsTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -1,13 +1,13 @@
 /*
- Copyright (C) 2002-2004 MySQL AB
+ Copyright (C) 2002-2007 MySQL AB
 
  This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as 
+ it under the terms of version 2 of the GNU General Public License as
  published by the Free Software Foundation.
 
- There are special exceptions to the terms and conditions of the GPL 
- as it is applied to this software. View the full text of the 
- exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
  software distribution.
 
  This program is distributed in the hope that it will be useful,
@@ -25,26 +25,39 @@
 package testsuite.simple;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.CharArrayReader;
+import java.io.InputStream;
 import java.io.Reader;
+import java.io.StringReader;
+import java.math.BigDecimal;
 import java.rmi.server.UID;
+import java.sql.BatchUpdateException;
 import java.sql.CallableStatement;
 import java.sql.Connection;
+import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
 import java.sql.Types;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
 import java.util.Properties;
 
 import testsuite.BaseTestCase;
 
 import com.mysql.jdbc.NotImplemented;
+import com.mysql.jdbc.ParameterBindings;
 import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.StatementImpl;
+import com.mysql.jdbc.StringUtils;
 
 /**
  * DOCUMENT ME!
- * 
+ *
  * @author Mark Matthews
  * @version $Id: StatementsTest.java 4494 2005-10-31 22:30:34 -0600 (Mon, 31 Oct
  *          2005) mmatthews $
@@ -60,7 +73,7 @@
 
 	/**
 	 * Runs all test cases in this test suite
-	 * 
+	 *
 	 * @param args
 	 */
 	public static void main(String[] args) {
@@ -69,7 +82,7 @@
 
 	/**
 	 * Creates a new StatementsTest object.
-	 * 
+	 *
 	 * @param name
 	 *            DOCUMENT ME!
 	 */
@@ -79,7 +92,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws Exception
 	 *             DOCUMENT ME!
 	 */
@@ -152,7 +165,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws Exception
 	 *             DOCUMENT ME!
 	 */
@@ -177,7 +190,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -269,7 +282,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -330,7 +343,7 @@
 	/**
 	 * Tests all variants of numerical types (signed/unsigned) for correct
 	 * operation when used as return values from a prepared statement.
-	 * 
+	 *
 	 * @throws Exception
 	 */
 	public void testBinaryResultSetNumericTypes() throws Exception {
@@ -448,7 +461,7 @@
 
 	/**
 	 * Tests stored procedure functionality
-	 * 
+	 *
 	 * @throws Exception
 	 *             if an error occurs.
 	 */
@@ -528,15 +541,15 @@
 
 		if (versionMeetsMinimum(5, 0)) {
 			Connection cancelConn = null;
-	
+
 			try {
-				cancelConn = getConnectionWithProps(null);
+				cancelConn = getConnectionWithProps((String)null);
 				final Statement cancelStmt = cancelConn.createStatement();
-			
+
 				cancelStmt.setQueryTimeout(1);
-	
+
 				long begin = System.currentTimeMillis();
-	
+
 				try {
 					cancelStmt.execute("SELECT SLEEP(30)");
 				} catch (SQLException sqlEx) {
@@ -544,7 +557,7 @@
 							.currentTimeMillis()
 							- begin < 30000);
 				}
-	
+
 				for (int i = 0; i < 1000; i++) {
 					try {
 						cancelStmt.executeQuery("SELECT 1");
@@ -552,19 +565,19 @@
 						break;
 					}
 				}
-				
+
 				// Make sure we can still use the connection...
-	
+
 				cancelStmt.setQueryTimeout(0);
 				this.rs = cancelStmt.executeQuery("SELECT 1");
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
-	
+
 				cancelStmt.setQueryTimeout(0);
-	
+
 				new Thread() {
-	
+
 					public void run() {
 						try {
 							try {
@@ -572,17 +585,17 @@
 							} catch (InterruptedException iEx) {
 								// ignore
 							}
-	
+
 							cancelStmt.cancel();
 						} catch (SQLException sqlEx) {
 							throw new RuntimeException(sqlEx.toString());
 						}
 					}
-	
+
 				}.start();
-	
+
 				begin = System.currentTimeMillis();
-	
+
 				try {
 					cancelStmt.execute("SELECT SLEEP(30)");
 				} catch (SQLException sqlEx) {
@@ -590,7 +603,7 @@
 							.currentTimeMillis()
 							- begin < 30000);
 				}
-	
+
 				for (int i = 0; i < 1000; i++) {
 					try {
 						cancelStmt.executeQuery("SELECT 1");
@@ -598,20 +611,20 @@
 						break;
 					}
 				}
-				
+
 				// Make sure we can still use the connection...
-	
+
 				this.rs = cancelStmt.executeQuery("SELECT 1");
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
-				
+
 				final PreparedStatement cancelPstmt = cancelConn.prepareStatement("SELECT SLEEP(30)");
-				
+
 				cancelPstmt.setQueryTimeout(1);
-	
+
 				begin = System.currentTimeMillis();
-	
+
 				try {
 					cancelPstmt.execute();
 				} catch (SQLException sqlEx) {
@@ -619,7 +632,7 @@
 							.currentTimeMillis()
 							- begin < 30000);
 				}
-	
+
 				for (int i = 0; i < 1000; i++) {
 					try {
 						cancelPstmt.executeQuery("SELECT 1");
@@ -627,18 +640,18 @@
 						break;
 					}
 				}
-				
+
 				// Make sure we can still use the connection...
-	
+
 				this.rs = cancelStmt.executeQuery("SELECT 1");
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
-	
+
 				cancelPstmt.setQueryTimeout(0);
-	
+
 				new Thread() {
-	
+
 					public void run() {
 						try {
 							try {
@@ -646,18 +659,18 @@
 							} catch (InterruptedException iEx) {
 								// ignore
 							}
-	
-							
+
+
 							cancelPstmt.cancel();
 						} catch (SQLException sqlEx) {
 							throw new RuntimeException(sqlEx.toString());
 						}
 					}
-	
+
 				}.start();
-	
+
 				begin = System.currentTimeMillis();
-	
+
 				try {
 					cancelPstmt.execute();
 				} catch (SQLException sqlEx) {
@@ -665,7 +678,7 @@
 							.currentTimeMillis()
 							- begin < 30000);
 				}
-	
+
 				for (int i = 0; i < 1000; i++) {
 					try {
 						cancelPstmt.executeQuery("SELECT 1");
@@ -673,20 +686,20 @@
 						break;
 					}
 				}
-				
+
 				// Make sure we can still use the connection...
-	
+
 				this.rs = cancelStmt.executeQuery("SELECT 1");
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
-				
+
 				final PreparedStatement cancelClientPstmt = ((com.mysql.jdbc.Connection)cancelConn).clientPrepareStatement("SELECT SLEEP(30)");
-				
+
 				cancelClientPstmt.setQueryTimeout(1);
-	
+
 				begin = System.currentTimeMillis();
-	
+
 				try {
 					cancelClientPstmt.execute();
 				} catch (SQLException sqlEx) {
@@ -694,7 +707,7 @@
 							.currentTimeMillis()
 							- begin < 30000);
 				}
-	
+
 				for (int i = 0; i < 1000; i++) {
 					try {
 						cancelStmt.executeQuery("SELECT 1");
@@ -702,18 +715,18 @@
 						break;
 					}
 				}
-				
+
 				// Make sure we can still use the connection...
-	
+
 				this.rs = cancelStmt.executeQuery("SELECT 1");
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
-	
+
 				cancelClientPstmt.setQueryTimeout(0);
-	
+
 				new Thread() {
-	
+
 					public void run() {
 						try {
 							try {
@@ -721,18 +734,18 @@
 							} catch (InterruptedException iEx) {
 								// ignore
 							}
-	
-							
+
+
 							cancelClientPstmt.cancel();
 						} catch (SQLException sqlEx) {
 							throw new RuntimeException(sqlEx.toString());
 						}
 					}
-	
+
 				}.start();
-	
+
 				begin = System.currentTimeMillis();
-	
+
 				try {
 					cancelClientPstmt.execute();
 				} catch (SQLException sqlEx) {
@@ -740,7 +753,7 @@
 							.currentTimeMillis()
 							- begin < 30000);
 				}
-	
+
 				for (int i = 0; i < 1000; i++) {
 					try {
 						cancelClientPstmt.executeQuery("SELECT 1");
@@ -748,11 +761,11 @@
 						break;
 					}
 				}
-				
+
 				// Make sure we can still use the connection...
-	
+
 				this.rs = cancelStmt.executeQuery("SELECT 1");
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
 			} finally {
@@ -761,7 +774,7 @@
 					this.rs = null;
 					toClose.close();
 				}
-	
+
 				if (cancelConn != null) {
 					cancelConn.close();
 				}
@@ -771,7 +784,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -894,7 +907,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -970,7 +983,7 @@
 
 	/**
 	 * Tests multiple statement support
-	 * 
+	 *
 	 * @throws Exception
 	 *             DOCUMENT ME!
 	 */
@@ -1040,7 +1053,7 @@
 
 	/**
 	 * Tests that NULLs and '' work correctly.
-	 * 
+	 *
 	 * @throws SQLException
 	 *             if an error occurs
 	 */
@@ -1110,7 +1123,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -1144,7 +1157,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -1173,27 +1186,27 @@
 			this.stmt.executeUpdate("INSERT INTO testRowFetch VALUES (1)");
 
 			Connection fetchConn = null;
-			
+
 			Properties props = new Properties();
 			props.setProperty("useCursorFetch", "true");
-			
-			
+
+
 			try {
 				fetchConn = getConnectionWithProps(props);
-				
+
 				PreparedStatement fetchStmt = fetchConn
 						.prepareStatement("SELECT field1 FROM testRowFetch WHERE field1=1");
 				fetchStmt.setFetchSize(10);
 				this.rs = fetchStmt.executeQuery();
 				assertTrue(this.rs.next());
-	
+
 				this.stmt.executeUpdate("INSERT INTO testRowFetch VALUES (2), (3)");
-	
+
 				fetchStmt = fetchConn
 						.prepareStatement("SELECT field1 FROM testRowFetch ORDER BY field1");
 				fetchStmt.setFetchSize(1);
 				this.rs = fetchStmt.executeQuery();
-	
+
 				assertTrue(this.rs.next());
 				assertEquals(1, this.rs.getInt(1));
 				assertTrue(this.rs.next());
@@ -1201,7 +1214,7 @@
 				assertTrue(this.rs.next());
 				assertEquals(3, this.rs.getInt(1));
 				assertEquals(false, this.rs.next());
-	
+
 				fetchStmt.executeQuery();
 			} finally {
 				if (fetchConn != null) {
@@ -1214,7 +1227,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -1235,7 +1248,7 @@
 
 	/**
 	 * Tests for PreparedStatement.setObject()
-	 * 
+	 *
 	 * @throws Exception
 	 */
 	public void testSetObject() throws Exception {
@@ -1288,109 +1301,348 @@
 				.getString(7));
 	}
 
-	// Server-side prepared statements can only reset streamed data
-	// in-toto, not piecemiel.
+	public void testStatementRewriteBatch() throws Exception {
+		for (int j = 0; j < 2; j++) {
+			Properties props = new Properties();
 
-	public void testStatementRewriteBatch() throws SQLException {
-		Properties props = new Properties();
-		props.setProperty("rewriteBatchedStatements", "true");
-		Connection multiConn = getConnectionWithProps(props);
-		createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
-		Statement multiStmt = multiConn.createStatement();
-		multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (1)");
-		multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (2)");
-		multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (3)");
-		multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (4)");
-		multiStmt.addBatch("UPDATE testStatementRewriteBatch SET field1=5 WHERE field1=1");
-		multiStmt.addBatch("UPDATE testStatementRewriteBatch SET field1=6 WHERE field1=2 OR field1=3");
-		
-		int[] counts = multiStmt.executeBatch();
-		ResultSet genKeys = multiStmt.getGeneratedKeys();
-		
-		for (int i = 1; i < 5; i++) {
-			genKeys.next();
-			assertEquals(i, genKeys.getInt(1));
+			if (j == 0) {
+				props.setProperty("useServerPrepStmts", "true");
+			}
+
+			props.setProperty("rewriteBatchedStatements", "true");
+			Connection multiConn = getConnectionWithProps(props);
+			createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+			Statement multiStmt = multiConn.createStatement();
+			multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (1)");
+			multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (2)");
+			multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (3)");
+			multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (4)");
+			multiStmt.addBatch("UPDATE testStatementRewriteBatch SET field1=5 WHERE field1=1");
+			multiStmt.addBatch("UPDATE testStatementRewriteBatch SET field1=6 WHERE field1=2 OR field1=3");
+
+			int[] counts = multiStmt.executeBatch();
+
+			if (!isRunningOnJdk131()) {
+				ResultSet genKeys = multiStmt.getGeneratedKeys();
+
+				for (int i = 1; i < 5; i++) {
+					genKeys.next();
+					assertEquals(i, genKeys.getInt(1));
+				}
+			}
+
+			assertEquals(counts.length, 6);
+			assertEquals(counts[0], 1);
+			assertEquals(counts[1], 1);
+			assertEquals(counts[2], 1);
+			assertEquals(counts[3], 1);
+			assertEquals(counts[4], 1);
+			assertEquals(counts[5], 2);
+
+			this.rs = multiStmt.executeQuery("SELECT field1 FROM testStatementRewriteBatch ORDER BY field1");
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 4);
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 5);
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 6);
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getInt(1), 6);
+
+			createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+			props.clear();
+			props.setProperty("rewriteBatchedStatements", "true");
+			props.setProperty("sessionVariables", "max_allowed_packet=1024");
+			multiConn = getConnectionWithProps(props);
+			multiStmt = multiConn.createStatement();
+
+			for (int i = 0; i < 1000; i++) {
+				multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (" + i + ")");
+			}
+
+			multiStmt.executeBatch();
+
+			if (!isRunningOnJdk131()) {
+				ResultSet genKeys = multiStmt.getGeneratedKeys();
+
+				for (int i = 1; i < 1000; i++) {
+					genKeys.next();
+					assertEquals(i, genKeys.getInt(1));
+				}
+			}
+
+			createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+
+			props.clear();
+			props.setProperty("useServerPrepStmts", j == 0 ? "true" : "false");
+			props.setProperty("rewriteBatchedStatements", "true");
+			multiConn = getConnectionWithProps(props);
+
+			PreparedStatement pStmt = null;
+
+			if (!isRunningOnJdk131()) {
+				pStmt = multiConn.prepareStatement("INSERT INTO testStatementRewriteBatch(field1) VALUES (?)",
+						Statement.RETURN_GENERATED_KEYS);
+
+				for (int i = 0; i < 1000; i++) {
+					pStmt.setInt(1, i);
+					pStmt.addBatch();
+				}
+
+				pStmt.executeBatch();
+
+				ResultSet genKeys = pStmt.getGeneratedKeys();
+
+				for (int i = 1; i < 1000; i++) {
+					genKeys.next();
+					assertEquals(i, genKeys.getInt(1));
+				}
+			}
+
+			createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
+			props.setProperty("useServerPrepStmts", j == 0 ? "true" : "false");
+			props.setProperty("rewriteBatchedStatements", "true");
+			props.setProperty("sessionVariables", "max_allowed_packet=1024");
+			multiConn = getConnectionWithProps(props);
+
+			if (!isRunningOnJdk131()) {
+
+				pStmt = multiConn.prepareStatement("INSERT INTO testStatementRewriteBatch(field1) VALUES (?)",
+					Statement.RETURN_GENERATED_KEYS);
+
+				for (int i = 0; i < 1000; i++) {
+					pStmt.setInt(1, i);
+					pStmt.addBatch();
+				}
+
+				pStmt.executeBatch();
+
+
+				ResultSet genKeys = pStmt.getGeneratedKeys();
+
+				for (int i = 1; i < 1000; i++) {
+					genKeys.next();
+					assertEquals(i, genKeys.getInt(1));
+				}
+			}
+
+			Object[][] differentTypes = new Object[1000][14];
+
+			createTable("rewriteBatchTypes", "(internalOrder int, f1 tinyint null, "
+					+ "f2 smallint null, f3 int null, f4 bigint null, "
+					+ "f5 decimal(8, 2) null, f6 float null, f7 double null, "
+					+ "f8 varchar(255) null, f9 text null, f10 blob null, "
+					+ "f11 blob null, f12 datetime null, f13 time null, f14 date null)");
+
+			for (int i = 0; i < 1000; i++) {
+				differentTypes[i][0] = Math.random() < .5 ? null : new Byte((byte)(Math.random() * 127));
+				differentTypes[i][1] = Math.random() < .5 ? null : new Short((short)(Math.random() * Short.MAX_VALUE));
+				differentTypes[i][2] = Math.random() < .5 ? null : new Integer((int)(Math.random() * Integer.MAX_VALUE));
+				differentTypes[i][3] = Math.random() < .5 ? null : new Long((long)(Math.random() * Long.MAX_VALUE));
+				differentTypes[i][4] = Math.random() < .5 ? null : new BigDecimal("19.95");
+				differentTypes[i][5] = Math.random() < .5 ? null : new Float(3 + ((float)(Math.random())));
+				differentTypes[i][6] = Math.random() < .5 ? null : new Double(3 + (Math.random()));
+				differentTypes[i][7] = Math.random() < .5 ? null : randomString();
+				differentTypes[i][8] = Math.random() < .5 ? null : randomString();
+				differentTypes[i][9] = Math.random() < .5 ? null : randomString().getBytes();
+				differentTypes[i][10] = Math.random() < .5 ? null : randomString().getBytes();
+				differentTypes[i][11] = Math.random() < .5 ? null : new Timestamp(System.currentTimeMillis());
+				differentTypes[i][12] = Math.random() < .5 ? null : new Time(System.currentTimeMillis());
+				differentTypes[i][13] = Math.random() < .5 ? null : new Date(System.currentTimeMillis());
+			}
+
+			props.setProperty("useServerPrepStmts", j == 0 ? "true" : "false");
+			props.setProperty("rewriteBatchedStatements", "true");
+			props.setProperty("sessionVariables", "max_allowed_packet=1024");
+			multiConn = getConnectionWithProps(props);
+			pStmt = multiConn.prepareStatement("INSERT INTO rewriteBatchTypes(internalOrder,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13,f14) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
+
+			for (int i = 0; i < 1000; i++) {
+				pStmt.setInt(1, i);
+				for (int k = 0; k < 14; k++) {
+					if (k == 8) {
+						String asString = (String)differentTypes[i][k];
+
+						if (asString == null) {
+							pStmt.setObject(k + 2, null);
+						} else {
+							pStmt.setCharacterStream(k + 2, new StringReader(asString), asString.length());
+						}
+					} else if (k == 9) {
+						byte[] asBytes = (byte[])differentTypes[i][k];
+
+						if (asBytes == null) {
+							pStmt.setObject(k + 2, null);
+						} else {
+							pStmt.setBinaryStream(k + 2, new ByteArrayInputStream(asBytes), asBytes.length);
+						}
+					} else {
+						pStmt.setObject(k + 2, differentTypes[i][k]);
+					}
+				}
+				pStmt.addBatch();
+			}
+
+			pStmt.executeBatch();
+
+			this.rs = this.stmt.executeQuery("SELECT f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14 FROM rewriteBatchTypes ORDER BY internalOrder");
+
+			int idx = 0;
+
+			// We need to format this ourselves, since we have to strip the nanos off of
+			// TIMESTAMPs, so .equals() doesn't really work...
+
+			SimpleDateFormat sdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US);
+
+			while (this.rs.next()) {
+				for (int k = 0; k < 14; k++) {
+					if (differentTypes[idx][k] == null) {
+						assertTrue("On row " + idx + " expected NULL, found " + this.rs.getObject(k + 1)
+								+ " in column " + (k + 1), this.rs.getObject(k + 1) == null);
+					} else {
+						String className = differentTypes[idx][k].getClass().getName();
+
+						if (className.equals("java.io.StringReader")) {
+							StringReader reader = (StringReader)differentTypes[idx][k];
+							StringBuffer buf = new StringBuffer();
+
+							int c = 0;
+
+							while ((c = reader.read()) != -1) {
+								buf.append((char)c);
+							}
+
+							String asString = this.rs.getString(k + 1);
+
+							assertEquals("On row " + idx + ", column " + (k + 1), buf.toString(), asString);
+
+						} else if (differentTypes[idx][k] instanceof java.io.InputStream) {
+							ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+							int bytesRead = 0;
+
+							byte[] buf = new byte[128];
+							InputStream in = (InputStream)differentTypes[idx][k];
+
+							while ((bytesRead = in.read(buf)) != -1) {
+								bOut.write(buf, 0, bytesRead);
+							}
+
+							byte[] expected = bOut.toByteArray();
+							byte[] actual = this.rs.getBytes(k + 1);
+
+							assertEquals("On row " + idx + ", column " + (k + 1), StringUtils.dumpAsHex(expected, expected.length), StringUtils.dumpAsHex(actual, actual.length));
+						} else if (differentTypes[idx][k] instanceof byte[]) {
+							byte[] expected = (byte[])differentTypes[idx][k];
+							byte[] actual = this.rs.getBytes(k + 1);
+							assertEquals("On row " + idx + ", column " + (k + 1), StringUtils.dumpAsHex(expected, expected.length), StringUtils.dumpAsHex(actual, actual.length));
+						} else if (differentTypes[idx][k] instanceof Timestamp) {
+							assertEquals("On row " + idx + ", column " + (k + 1), sdf.format(differentTypes[idx][k]), sdf.format(this.rs.getObject(k + 1)));
+						} else if (differentTypes[idx][k] instanceof Double) {
+							assertEquals("On row " + idx + ", column " + (k + 1), ((Double)differentTypes[idx][k]).doubleValue(), this.rs.getDouble(k + 1), .1);
+						} else if (differentTypes[idx][k] instanceof Float) {
+							assertEquals("On row " + idx + ", column " + (k + 1), ((Float)differentTypes[idx][k]).floatValue(), this.rs.getFloat(k + 1), .1);
+						} else if (className.equals("java.lang.Byte")) {
+							// special mapping in JDBC for ResultSet.getObject()
+							assertEquals("On row " + idx + ", column " + (k + 1), new Integer(((Byte)differentTypes[idx][k]).byteValue()), this.rs.getObject(k + 1));
+						} else if (className.equals("java.lang.Short")) {
+							// special mapping in JDBC for ResultSet.getObject()
+							assertEquals("On row " + idx + ", column " + (k + 1), new Integer(((Short)differentTypes[idx][k]).shortValue()), this.rs.getObject(k + 1));
+						} else {
+							assertEquals("On row " + idx + ", column " + (k + 1) + " (" + differentTypes[idx][k].getClass() + "/" + this.rs.getObject(k + 1).getClass(), differentTypes[idx][k].toString(), this.rs.getObject(k + 1).toString());
+						}
+					}
+				}
+
+				idx++;
+			}
 		}
-		
-		assertEquals(counts.length, 6);
-		assertEquals(counts[0], 1);
-		assertEquals(counts[1], 1);
-		assertEquals(counts[2], 1);
-		assertEquals(counts[3], 1);
-		assertEquals(counts[4], 1);
-		assertEquals(counts[5], 2);
-		
-		this.rs = multiStmt.executeQuery("SELECT field1 FROM testStatementRewriteBatch ORDER BY field1");
-		assertTrue(this.rs.next());
-		assertEquals(this.rs.getInt(1), 4);
-		assertTrue(this.rs.next());
-		assertEquals(this.rs.getInt(1), 5);
-		assertTrue(this.rs.next());
-		assertEquals(this.rs.getInt(1), 6);
-		assertTrue(this.rs.next());
-		assertEquals(this.rs.getInt(1), 6);
+	}
+	
+	public void testBatchRewriteErrors() throws Exception {
+		createTable("rewriteErrors", "(field1 int not null primary key)");
 
-		createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
-		props.clear();
-		props.setProperty("rewriteBatchedStatements", "true");
-		props.setProperty("sessionVariables", "max_allowed_packet=1024");
-		multiConn = getConnectionWithProps(props);
-		multiStmt = multiConn.createStatement();
-		
-		for (int i = 0; i < 1000; i++) {
-			multiStmt.addBatch("INSERT INTO testStatementRewriteBatch(field1) VALUES (" + i + ")");
+		Properties props = new Properties();
+		Connection multiConn = null;
+
+		for (int j = 0; j < 2; j++) {
+			props.setProperty("useServerPrepStmts", "false");
+	
+			if (j == 1) {
+				props.setProperty("continueBatchOnError", "false");
+			}
+			
+			props.setProperty("sessionVariables", "max_allowed_packet=1024");
+			props.setProperty("rewriteBatchedStatements", "true");
+			multiConn = getConnectionWithProps(props);
+			this.pstmt = multiConn.prepareStatement("INSERT INTO rewriteErrors VALUES (?)");
+			Statement multiStmt = multiConn.createStatement();
+			
+			for (int i = 0; i < 4096; i++) {
+				multiStmt.addBatch("INSERT INTO rewriteErrors VALUES (" + i + ")");
+				this.pstmt.setInt(1, i);
+				this.pstmt.addBatch();
+			}
+			
+			multiStmt.addBatch("INSERT INTO rewriteErrors VALUES (2048)");
+			
+			this.pstmt.setInt(1, 2048);
+			this.pstmt.addBatch();
+			
+			try {
+				this.pstmt.executeBatch();
+			} catch (BatchUpdateException bUpE) {
+				int[] counts = bUpE.getUpdateCounts();
+	
+				for (int i = 4059; i < counts.length; i++) {
+					assertEquals(counts[i], Statement.EXECUTE_FAILED);
+				}
+				
+				assertEquals(4096, getRowCount("rewriteErrors"));
+			}
+			
+			this.stmt.execute("TRUNCATE TABLE rewriteErrors");
+			
+			try {
+				multiStmt.executeBatch();
+			} catch (BatchUpdateException bUpE) {
+				int[] counts = bUpE.getUpdateCounts();
+	
+				for (int i = 4094; i < counts.length; i++) {
+					assertEquals(counts[i], Statement.EXECUTE_FAILED);
+				}
+				
+				assertEquals(4096, getRowCount("rewriteErrors"));
+			}
+			
+			if (versionMeetsMinimum(5, 0)) {
+				this.stmt.execute("TRUNCATE TABLE rewriteErrors");
+				
+				createProcedure("sp_rewriteErrors", "(param1 INT)\nBEGIN\nINSERT INTO rewriteErrors VALUES (param1);\nEND");
+				
+				CallableStatement cStmt = multiConn.prepareCall("{ CALL sp_rewriteErrors(?)}");
+				
+				for (int i = 0; i < 4096; i++) {
+					cStmt.setInt(1, i);
+					cStmt.addBatch();
+				}
+				
+				cStmt.setInt(1, 2048);
+				cStmt.addBatch();
+				
+				try {
+					cStmt.executeBatch();
+				} catch (BatchUpdateException bUpE) {
+					int[] counts = bUpE.getUpdateCounts();
+	
+					for (int i = 4093; i < counts.length; i++) {
+						assertEquals(counts[i], Statement.EXECUTE_FAILED);
+					}
+					
+					assertEquals(4096, getRowCount("rewriteErrors"));
+				}
+			}
 		}
-		
-		multiStmt.executeBatch();
-		genKeys = multiStmt.getGeneratedKeys();
-		
-		for (int i = 1; i < 1000; i++) {
-			genKeys.next();
-			assertEquals(i, genKeys.getInt(1));
-		}
-		
-		createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
-		
-		props.clear();
-		props.setProperty("useServerPrepStmts", "false");
-		props.setProperty("rewriteBatchedStatements", "true");
-		multiConn = getConnectionWithProps(props);
-		PreparedStatement pStmt = multiConn.prepareStatement("INSERT INTO testStatementRewriteBatch(field1) VALUES (?)", 
-				Statement.RETURN_GENERATED_KEYS);
-		
-		for (int i = 0; i < 1000; i++) {
-			pStmt.setInt(1, i);
-			pStmt.addBatch();
-		}
-		
-		pStmt.executeBatch();
-		genKeys = pStmt.getGeneratedKeys();
-		
-		for (int i = 1; i < 1000; i++) {
-			genKeys.next();
-			assertEquals(i, genKeys.getInt(1));
-		}
-		
-		createTable("testStatementRewriteBatch", "(pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT)");
-		props.setProperty("useServerPrepStmts", "false");
-		props.setProperty("rewriteBatchedStatements", "true");
-		props.setProperty("sessionVariables", "max_allowed_packet=1024");
-		multiConn = getConnectionWithProps(props);
-		pStmt = multiConn.prepareStatement("INSERT INTO testStatementRewriteBatch(field1) VALUES (?)", 
-				Statement.RETURN_GENERATED_KEYS);
-		
-		for (int i = 0; i < 1000; i++) {
-			pStmt.setInt(1, i);
-			pStmt.addBatch();
-		}
-		
-		pStmt.executeBatch();
-		genKeys = pStmt.getGeneratedKeys();
-		
-		for (int i = 1; i < 1000; i++) {
-			genKeys.next();
-			assertEquals(i, genKeys.getInt(1));
-		}
 	}
 
 	public void testStreamChange() throws Exception {
@@ -1447,7 +1699,7 @@
 
 	/**
 	 * DOCUMENT ME!
-	 * 
+	 *
 	 * @throws SQLException
 	 *             DOCUMENT ME!
 	 */
@@ -1460,7 +1712,7 @@
 			}
 		}
 	}
-	
+
 	public void testTruncationOnRead() throws Exception {
 		this.rs = this.stmt.executeQuery("SELECT '" + Long.MAX_VALUE + "'");
 		this.rs.next();
@@ -1585,4 +1837,98 @@
 		}
 
 	}
+
+	public void testStatementInterceptors() throws Exception {
+		Connection interceptedConn = null;
+
+		/*
+		try {
+			Properties props = new Properties();
+			props.setProperty("statementInterceptors", "com.mysql.jdbc.interceptors.ResultSetScannerInterceptor");
+			props.setProperty("resultSetScannerRegex", ".*");
+			interceptedConn = getConnectionWithProps(props);
+			this.rs = interceptedConn.createStatement().executeQuery("SELECT 'abc'");
+			this.rs.next();
+			this.rs.getString(1);
+		} finally {
+			closeMemberJDBCResources();
+
+			if (interceptedConn != null) {
+				interceptedConn.close();
+			}
+		}
+		*/
+
+		try {
+			Properties props = new Properties();
+			props.setProperty("statementInterceptors", "com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor");
+
+			interceptedConn = getConnectionWithProps(props);
+			this.rs = interceptedConn.createStatement().executeQuery("SELECT 'abc'");
+		} finally {
+			closeMemberJDBCResources();
+
+			if (interceptedConn != null) {
+				interceptedConn.close();
+			}
+		}
+	}
+
+	public void testParameterBindings() throws Exception {
+		// Need to check character set stuff, so need a new connection
+		Connection utfConn = getConnectionWithProps("characterEncoding=utf-8");
+
+		java.util.Date now = new java.util.Date();
+
+		Object[] valuesToTest = new Object[] {
+				new Byte(Byte.MIN_VALUE),
+				new Short(Short.MIN_VALUE),
+				new Integer(Integer.MIN_VALUE),
+				new Long(Long.MIN_VALUE),
+				new Double(Double.MIN_VALUE),
+				"\u4E2D\u6587",
+				new BigDecimal(Math.PI),
+				null, // to test isNull
+				now // to test serialization
+		};
+
+		StringBuffer statementText = new StringBuffer("SELECT ?");
+
+		for (int i = 1; i < valuesToTest.length; i++) {
+			statementText.append(",?");
+		}
+
+		this.pstmt = utfConn.prepareStatement(statementText.toString());
+
+		for (int i = 0; i < valuesToTest.length; i++) {
+			this.pstmt.setObject(i + 1, valuesToTest[i]);
+		}
+
+		ParameterBindings bindings = ((com.mysql.jdbc.PreparedStatement)this.pstmt).getParameterBindings();
+
+		for (int i = 0; i < valuesToTest.length; i++) {
+			assertEquals(bindings.getObject(i + 1), valuesToTest[i]);
+		}
+	}
+
+	public void testLocalInfileHooked() throws Exception {
+	    createTable("localInfileHooked", "(field1 int, field2 varchar(255))");
+	    String streamData = "1\tabcd\n2\tefgh\n3\tijkl";
+	    InputStream stream = new ByteArrayInputStream(streamData.getBytes());
+	    try {
+	        ((com.mysql.jdbc.Statement) this.stmt).setLocalInfileInputStream(stream);
+	        this.stmt.execute("LOAD DATA LOCAL INFILE 'bogusFileName' INTO TABLE localInfileHooked");
+	        assertEquals(-1, stream.read());
+	        this.rs = this.stmt.executeQuery("SELECT field2 FROM localInfileHooked ORDER BY field1 ASC");
+	        this.rs.next();
+	        assertEquals("abcd", this.rs.getString(1));
+	        this.rs.next();
+            assertEquals("efgh", this.rs.getString(1));
+            this.rs.next();
+            assertEquals("ijkl", this.rs.getString(1));
+	    } finally {
+	        ((com.mysql.jdbc.Statement) this.stmt).setLocalInfileInputStream(null);
+	        closeMemberJDBCResources();
+	    }
+	}
 }

Added: trunk/mysql-connector-java/src/testsuite/simple/jdbc4/StatementsTest.java
===================================================================
--- trunk/mysql-connector-java/src/testsuite/simple/jdbc4/StatementsTest.java	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/simple/jdbc4/StatementsTest.java	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,623 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple.jdbc4;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+import testsuite.BaseTestCase;
+
+public class StatementsTest extends BaseTestCase {
+
+	public StatementsTest(String name) {
+		super(name);
+	
+	}
+
+	/**
+	 * Tests for ResultSet.getNCharacterStream()
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetNCharacterSteram() throws Exception {
+	    createTable("testGetNCharacterStream", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))");
+	    this.stmt.executeUpdate("INSERT INTO testGetNCharacterStream (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')");
+	    this.rs = this.stmt.executeQuery("SELECT c1, c2 FROM testGetNCharacterStream");
+	    this.rs.next();
+	    char[] c1 = new char[3];
+	    this.rs.getNCharacterStream(1).read(c1);
+	    assertEquals("aaa", new String(c1));
+	    char[] c2 = new char[3];
+	    this.rs.getNCharacterStream("c2").read(c2);
+	    assertEquals("bbb", new String(c2));
+	    this.rs.close();
+	}
+
+	/**
+	 * Tests for ResultSet.getNClob()
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetNClob() throws Exception {
+	    createTable("testGetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))");
+	    this.stmt.executeUpdate("INSERT INTO testGetNClob (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')");
+	    this.rs = this.stmt.executeQuery("SELECT c1, c2 FROM testGetNClob");
+	    this.rs.next();
+	    char[] c1 = new char[3];
+	    this.rs.getNClob(1).getCharacterStream().read(c1);
+	    assertEquals("aaa", new String(c1));
+	    char[] c2 = new char[3];
+	    this.rs.getNClob("c2").getCharacterStream().read(c2);
+	    assertEquals("bbb", new String(c2));
+	    this.rs.close();
+	    
+	    // for isBinaryEncoded = true, using PreparedStatement
+	    createTable("testGetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))");
+	    this.stmt.executeUpdate("INSERT INTO testGetNClob (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')");
+	    this.pstmt = this.conn.prepareStatement("SELECT c1, c2 FROM testGetNClob");
+	    this.rs = this.pstmt.executeQuery();
+	    this.rs.next();
+	    c1 = new char[3];
+	    this.rs.getNClob(1).getCharacterStream().read(c1);
+	    assertEquals("aaa", new String(c1));
+	    c2 = new char[3];
+	    this.rs.getNClob("c2").getCharacterStream().read(c2);
+	    assertEquals("bbb", new String(c2));
+	    this.rs.close();
+	}
+
+	/**
+	 * Tests for ResultSet.getNString()
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetNString() throws Exception {
+	    createTable("testGetNString", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))");
+	    this.stmt.executeUpdate("INSERT INTO testGetNString (c1, c2) VALUES (_utf8 'aaa', _utf8 'bbb')");
+	    this.rs = this.stmt.executeQuery("SELECT c1, c2 FROM testGetNString");
+	    this.rs.next();
+	    assertEquals("aaa", this.rs.getNString(1));
+	    assertEquals("bbb", this.rs.getNString("c2"));
+	    this.rs.close();
+	}
+
+	/**
+	 * Tests for PreparedStatement.setNCharacterSteam()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetNCharacterStream() throws Exception {
+	    // suppose sql_mode don't include "NO_BACKSLASH_ESCAPES"
+	    
+	    createTable("testSetNCharacterStream", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), " +
+	            "c3 NATIONAL CHARACTER(10))");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "false"); // use client-side prepared statement
+	    props1.put("useUnicode", "true");
+	    props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    com.mysql.jdbc.PreparedStatement pstmt1 = (com.mysql.jdbc.PreparedStatement)
+	        conn1.prepareStatement("INSERT INTO testSetNCharacterStream (c1, c2, c3) VALUES (?, ?, ?)");
+	    pstmt1.setNCharacterStream(1, null, 0);
+	    pstmt1.setNCharacterStream(2, new StringReader("aaa"), 3);
+	    pstmt1.setNCharacterStream(3, new StringReader("\'aaa\'"), 5);
+	    pstmt1.execute();
+	    ResultSet rs1 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNCharacterStream");
+	    rs1.next();
+	    assertEquals(null, rs1.getString(1));
+	    assertEquals("aaa", rs1.getString(2));
+	    assertEquals("\'aaa\'", rs1.getString(3));
+	    rs1.close();
+	    pstmt1.close();
+	    conn1.close();
+	    
+	    createTable("testSetNCharacterStream", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), " +
+	    "c3 NATIONAL CHARACTER(10))");
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "false"); // use client-side prepared statement
+	    props2.put("useUnicode", "true");
+	    props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    com.mysql.jdbc.PreparedStatement pstmt2 = (com.mysql.jdbc.PreparedStatement)
+	        conn2.prepareStatement("INSERT INTO testSetNCharacterStream (c1, c2, c3) VALUES (?, ?, ?)");
+	    pstmt2.setNCharacterStream(1, null, 0);
+	    pstmt2.setNCharacterStream(2, new StringReader("aaa"), 3);
+	    pstmt2.setNCharacterStream(3, new StringReader("\'aaa\'"), 5);
+	    pstmt2.execute();
+	    ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNCharacterStream");
+	    rs2.next();
+	    assertEquals(null, rs2.getString(1));
+	    assertEquals("aaa", rs2.getString(2));
+	    assertEquals("\'aaa\'", rs2.getString(3));
+	    rs2.close();
+	    pstmt2.close();
+	    conn2.close();
+	}
+
+	/**
+	 * Tests for ServerPreparedStatement.setNCharacterSteam()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetNCharacterStreamServer() throws Exception {
+	    createTable("testSetNCharacterStreamServer", "(c1 NATIONAL CHARACTER(10))");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props1.put("useUnicode", "true");
+	    props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 =  conn1.prepareStatement("INSERT INTO testSetNCharacterStreamServer (c1) VALUES (?)");
+	    try {
+	        pstmt1.setNCharacterStream(1, new StringReader("aaa"), 3);
+	        fail();
+	    } catch (SQLException e) {
+	        // ok
+	        assertEquals("Can not call setNCharacterStream() when connection character set isn't UTF-8",
+	            e.getMessage());  
+	    }
+	    pstmt1.close();
+	    conn1.close();
+	    
+	    createTable("testSetNCharacterStreamServer", "(c1 LONGTEXT charset utf8)");
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props2.put("useUnicode", "true");
+	    props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = 
+	        conn2.prepareStatement("INSERT INTO testSetNCharacterStreamServer (c1) VALUES (?)");
+	    pstmt2.setNCharacterStream(1, new StringReader(
+	            new String(new char[81921])), 81921); // 10 Full Long Data Packet's chars + 1 char
+	    pstmt2.execute();
+	    ResultSet rs2 = this.stmt.executeQuery("SELECT c1 FROM testSetNCharacterStreamServer");
+	    rs2.next();
+	    assertEquals(new String(new char[81921]), rs2.getString(1));
+	    rs2.close();
+	    pstmt2.close();
+	    conn2.close();
+	}
+
+	/**
+	 * Tests for PreparedStatement.setNClob()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetNClob() throws Exception {
+	    // suppose sql_mode don't include "NO_BACKSLASH_ESCAPES"
+	    
+	    createTable("testSetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), " +
+	            "c3 NATIONAL CHARACTER(10))");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "false"); // use client-side prepared statement
+	    props1.put("useUnicode", "true");
+	    props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = 
+	        conn1.prepareStatement("INSERT INTO testSetNClob (c1, c2, c3) VALUES (?, ?, ?)");
+	    pstmt1.setNClob(1, (NClob)null);
+	    NClob nclob2 = conn1.createNClob();
+	    nclob2.setString(1, "aaa");
+	    pstmt1.setNClob(2, nclob2);                   // for setNClob(int, NClob)
+	    Reader reader3 = new StringReader("\'aaa\'");
+	    pstmt1.setNClob(3, reader3, 5);               // for setNClob(int, Reader, long)
+	    pstmt1.execute();
+	    ResultSet rs1 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNClob");
+	    rs1.next();
+	    assertEquals(null, rs1.getString(1));
+	    assertEquals("aaa", rs1.getString(2));
+	    assertEquals("\'aaa\'", rs1.getString(3));
+	    rs1.close();
+	    pstmt1.close();
+	    conn1.close();
+	    
+	    createTable("testSetNClob", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), " +
+	    "c3 NATIONAL CHARACTER(10))");
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "false"); // use client-side prepared statement
+	    props2.put("useUnicode", "true");
+	    props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = 
+	        conn2.prepareStatement("INSERT INTO testSetNClob (c1, c2, c3) VALUES (?, ?, ?)");
+	    pstmt2.setNClob(1, (NClob)null);
+	    nclob2 = conn2.createNClob();
+	    nclob2.setString(1, "aaa");
+	    pstmt2.setNClob(2, nclob2);             // for setNClob(int, NClob)
+	    reader3 = new StringReader("\'aaa\'");
+	    pstmt2.setNClob(3, reader3, 5);         // for setNClob(int, Reader, long)
+	    pstmt2.execute();
+	    ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNClob");
+	    rs2.next();
+	    assertEquals(null, rs2.getString(1));
+	    assertEquals("aaa", rs2.getString(2));
+	    assertEquals("\'aaa\'", rs2.getString(3));
+	    rs2.close();
+	    pstmt2.close();
+	    conn2.close();
+	}
+
+	/**
+	 * Tests for ServerPreparedStatement.setNClob()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetNClobServer() throws Exception {
+	    createTable("testSetNClobServer", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10))");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props1.put("useUnicode", "true");
+	    props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = 
+	        conn1.prepareStatement("INSERT INTO testSetNClobServer (c1, c2) VALUES (?, ?)");
+	    NClob nclob1 = conn1.createNClob();
+	    nclob1.setString(1, "aaa");
+	    Reader reader2 = new StringReader("aaa");
+	    try {
+	        pstmt1.setNClob(1, nclob1);
+	        fail();
+	    } catch (SQLException e) {
+	        // ok
+	        assertEquals("Can not call setNClob() when connection character set isn't UTF-8",
+	            e.getMessage());  
+	    }
+	    try {
+	        pstmt1.setNClob(2, reader2, 3);
+	        fail();
+	    } catch (SQLException e) {
+	        // ok
+	        assertEquals("Can not call setNClob() when connection character set isn't UTF-8",
+	            e.getMessage());  
+	    }
+	    pstmt1.close();
+	    conn1.close();
+	    
+	    createTable("testSetNClobServer", "(c1 NATIONAL CHARACTER(10), c2 LONGTEXT charset utf8)");
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props2.put("useUnicode", "true");
+	    props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = 
+	        conn2.prepareStatement("INSERT INTO testSetNClobServer (c1, c2) VALUES (?, ?)");
+	    nclob1 = conn2.createNClob();
+	    nclob1.setString(1, "aaa");
+	    pstmt2.setNClob(1, nclob1);
+	    pstmt2.setNClob(2, new StringReader(
+	            new String(new char[81921])), 81921); // 10 Full Long Data Packet's chars + 1 char
+	    pstmt2.execute();
+	    ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2 FROM testSetNClobServer");
+	    rs2.next();
+	    assertEquals("aaa", rs2.getString(1));
+	    assertEquals(new String(new char[81921]), rs2.getString(2));
+	    rs2.close();
+	    pstmt2.close();
+	    conn2.close();
+	}
+
+	/**
+	 * Tests for PreparedStatement.setNString()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetNString() throws Exception {
+	    // suppose sql_mode don't include "NO_BACKSLASH_ESCAPES"
+	    
+	    createTable("testSetNString", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), " +
+	            "c3 NATIONAL CHARACTER(10)) DEFAULT CHARACTER SET cp932");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "false"); // use client-side prepared statement
+	    props1.put("useUnicode", "true");
+	    props1.put("characterEncoding", "MS932"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = 
+	        conn1.prepareStatement("INSERT INTO testSetNString (c1, c2, c3) VALUES (?, ?, ?)");
+	    pstmt1.setNString(1, null);
+	    pstmt1.setNString(2, "aaa");
+	    pstmt1.setNString(3, "\'aaa\'");
+	    pstmt1.execute();
+	    ResultSet rs1 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNString");
+	    rs1.next();
+	    assertEquals(null, rs1.getString(1));
+	    assertEquals("aaa", rs1.getString(2));
+	    assertEquals("\'aaa\'", rs1.getString(3));
+	    rs1.close();
+	    pstmt1.close();
+	    conn1.close();
+	    
+	    createTable("testSetNString", "(c1 NATIONAL CHARACTER(10), c2 NATIONAL CHARACTER(10), " +
+	    "c3 NATIONAL CHARACTER(10)) DEFAULT CHARACTER SET cp932");
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "false"); // use client-side prepared statement
+	    props2.put("useUnicode", "true");
+	    props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = 
+	        conn2.prepareStatement("INSERT INTO testSetNString (c1, c2, c3) VALUES (?, ?, ?)");
+	    pstmt2.setNString(1, null);
+	    pstmt2.setNString(2, "aaa");
+	    pstmt2.setNString(3, "\'aaa\'");
+	    pstmt2.execute();
+	    ResultSet rs2 = this.stmt.executeQuery("SELECT c1, c2, c3 FROM testSetNString");
+	    rs2.next();
+	    assertEquals(null, rs2.getString(1));
+	    assertEquals("aaa", rs2.getString(2));
+	    assertEquals("\'aaa\'", rs2.getString(3));
+	    rs2.close();
+	    pstmt2.close();
+	    conn2.close();
+	}
+
+	/**
+	 * Tests for ServerPreparedStatement.setNString()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetNStringServer() throws Exception {
+	    createTable("testSetNStringServer", "(c1 NATIONAL CHARACTER(10))");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props1.put("useUnicode", "true");
+	    props1.put("characterEncoding", "latin1"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = 
+	        conn1.prepareStatement("INSERT INTO testSetNStringServer (c1) VALUES (?)");
+	    try {
+	        pstmt1.setNString(1, "aaa");
+	        fail();
+	    } catch (SQLException e) {
+	        // ok
+	        assertEquals("Can not call setNString() when connection character set isn't UTF-8",
+	            e.getMessage());  
+	    }
+	    pstmt1.close();
+	    conn1.close();
+	    
+	    createTable("testSetNStringServer", "(c1 NATIONAL CHARACTER(10))");
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props2.put("useUnicode", "true");
+	    props2.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = 
+	        conn2.prepareStatement("INSERT INTO testSetNStringServer (c1) VALUES (?)");
+	    pstmt2.setNString(1, "\'aaa\'");
+	    pstmt2.execute();
+	    ResultSet rs2 = this.stmt.executeQuery("SELECT c1 FROM testSetNStringServer");
+	    rs2.next();
+	    assertEquals("\'aaa\'", rs2.getString(1));
+	    rs2.close();
+	    pstmt2.close();
+	    conn2.close();
+	}
+
+	/**
+	 * Tests for ResultSet.updateNCharacterStream()
+	 * 
+	 * @throws Exception
+	 */
+	public void testUpdateNCharacterStream() throws Exception {
+	    createTable("testUpdateNCharacterStream", 
+	            "(c1 CHAR(10) PRIMARY KEY, c2 NATIONAL CHARACTER(10)) default character set sjis");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props1.put("characterEncoding", "UTF-8"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = conn1.prepareStatement(
+	            "INSERT INTO testUpdateNCharacterStream (c1, c2) VALUES (?, ?)");
+	    pstmt1.setString(1, "1");
+	    pstmt1.setNCharacterStream(2, new StringReader("aaa"), 3);
+	    pstmt1.execute();
+	    Statement stmt1 = conn1.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+	    ResultSet rs1 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNCharacterStream");
+	    rs1.next();
+	    rs1.updateNCharacterStream("c2", new StringReader("bbb"), 3);
+	    rs1.updateRow();
+	    rs1.moveToInsertRow();
+	    rs1.updateString("c1", "2");
+	    rs1.updateNCharacterStream("c2", new StringReader("ccc"), 3);
+	    rs1.insertRow();
+	    ResultSet rs2 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNCharacterStream");
+	    rs2.next();
+	    assertEquals("1", rs2.getString("c1"));
+	    assertEquals("bbb", rs2.getNString("c2"));
+	    rs2.next();
+	    assertEquals("2", rs2.getString("c1"));
+	    assertEquals("ccc", rs2.getNString("c2"));
+	    pstmt1.close();
+	    stmt1.close();
+	    conn1.close();
+	    
+	    createTable("testUpdateNCharacterStream", 
+	            "(c1 CHAR(10) PRIMARY KEY, c2 CHAR(10)) default character set sjis"); // sjis field
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props2.put("characterEncoding", "SJIS"); // ensure charset isn't utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = conn2.prepareStatement(
+	            "INSERT INTO testUpdateNCharacterStream (c1, c2) VALUES (?, ?)");
+	    pstmt2.setString(1, "1");
+	    pstmt2.setString(2, "aaa");
+	    pstmt2.execute();
+	    Statement stmt2 = conn2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+	    ResultSet rs3 = stmt2.executeQuery("SELECT c1, c2 FROM testUpdateNCharacterStream");
+	    rs3.next();
+	    try {
+	        rs3.updateNCharacterStream("c2", new StringReader("bbb"), 3); // field's charset isn't utf8
+	        fail();
+	    } catch (SQLException ex) {
+	        assertEquals("Can not call updateNCharacterStream() when field's character set isn't UTF-8",
+	                ex.getMessage());
+	    }
+	    rs3.close();
+	    pstmt2.close();
+	    stmt2.close();
+	    conn2.close();  
+	}
+
+	/**
+	 * Tests for ResultSet.updateNClob()
+	 * 
+	 * @throws Exception
+	 */
+	public void testUpdateNClob() throws Exception {
+	    createTable("testUpdateNChlob", 
+	            "(c1 CHAR(10) PRIMARY KEY, c2 NATIONAL CHARACTER(10)) default character set sjis");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props1.put("characterEncoding", "UTF-8"); // ensure charset isn't utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = conn1.prepareStatement(
+	            "INSERT INTO testUpdateNChlob (c1, c2) VALUES (?, ?)");
+	    pstmt1.setString(1, "1");
+	    NClob nClob1 = conn1.createNClob();
+	    nClob1.setString(1, "aaa");
+	    pstmt1.setNClob(2, nClob1);
+	    pstmt1.execute();
+	    Statement stmt1 = conn1.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+	    ResultSet rs1 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNChlob");
+	    rs1.next();
+	    NClob nClob2 = conn1.createNClob();
+	    nClob2.setString(1, "bbb");
+	    rs1.updateNClob("c2", nClob2);
+	    rs1.updateRow();
+	    rs1.moveToInsertRow();
+	    rs1.updateString("c1", "2");
+	    NClob nClob3 = conn1.createNClob();
+	    nClob3.setString(1, "ccc");
+	    rs1.updateNClob("c2", nClob3);
+	    rs1.insertRow();
+	    ResultSet rs2 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNChlob");
+	    rs2.next();
+	    assertEquals("1", rs2.getString("c1"));
+	    assertEquals("bbb", rs2.getNString("c2"));
+	    rs2.next();
+	    assertEquals("2", rs2.getString("c1"));
+	    assertEquals("ccc", rs2.getNString("c2"));
+	    pstmt1.close();
+	    stmt1.close();
+	    conn1.close();
+	    
+	    createTable("testUpdateNChlob", 
+	            "(c1 CHAR(10) PRIMARY KEY, c2 CHAR(10)) default character set sjis"); // sjis field
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props2.put("characterEncoding", "SJIS"); // ensure charset isn't utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = conn2.prepareStatement(
+	            "INSERT INTO testUpdateNChlob (c1, c2) VALUES (?, ?)");
+	    pstmt2.setString(1, "1");
+	    pstmt2.setString(2, "aaa");
+	    pstmt2.execute();
+	    Statement stmt2 = conn2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+	    ResultSet rs3 = stmt2.executeQuery("SELECT c1, c2 FROM testUpdateNChlob");
+	    rs3.next();
+	    NClob nClob4 = conn2.createNClob();
+	    nClob4.setString(1, "bbb");
+	    try {
+	        rs3.updateNClob("c2", nClob4); // field's charset isn't utf8
+	        fail();
+	    } catch (SQLException ex) {
+	        assertEquals("Can not call updateNClob() when field's character set isn't UTF-8",
+	                ex.getMessage());
+	    }
+	    rs3.close();
+	    pstmt2.close();
+	    stmt2.close();
+	    conn2.close();  
+	}
+
+	/**
+	 * Tests for ResultSet.updateNString()
+	 * 
+	 * @throws Exception
+	 */
+	public void testUpdateNString() throws Exception {
+	    createTable("testUpdateNString", 
+	            "(c1 CHAR(10) PRIMARY KEY, c2 NATIONAL CHARACTER(10)) default character set sjis");
+	    Properties props1 = new Properties();
+	    props1.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props1.put("characterEncoding", "UTF-8"); // ensure charset is utf8 here
+	    Connection conn1 = getConnectionWithProps(props1);
+	    PreparedStatement pstmt1 = conn1.prepareStatement(
+	            "INSERT INTO testUpdateNString (c1, c2) VALUES (?, ?)");
+	    pstmt1.setString(1, "1");
+	    pstmt1.setNString(2, "aaa");
+	    pstmt1.execute();
+	    Statement stmt1 = conn1.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+	    ResultSet rs1 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNString");
+	    rs1.next();
+	    rs1.updateNString("c2", "bbb");
+	    rs1.updateRow();
+	    rs1.moveToInsertRow();
+	    rs1.updateString("c1", "2");
+	    rs1.updateNString("c2", "ccc");
+	    rs1.insertRow();
+	    ResultSet rs2 = stmt1.executeQuery("SELECT c1, c2 FROM testUpdateNString");
+	    rs2.next();
+	    assertEquals("1", rs2.getString("c1"));
+	    assertEquals("bbb", rs2.getNString("c2"));
+	    rs2.next();
+	    assertEquals("2", rs2.getString("c1"));
+	    assertEquals("ccc", rs2.getNString("c2"));
+	    pstmt1.close();
+	    stmt1.close();
+	    conn1.close();
+	    
+	    createTable("testUpdateNString", 
+	            "(c1 CHAR(10) PRIMARY KEY, c2 CHAR(10)) default character set sjis"); // sjis field
+	    Properties props2 = new Properties();
+	    props2.put("useServerPrepStmts", "true"); // use server-side prepared statement
+	    props2.put("characterEncoding", "SJIS"); // ensure charset isn't utf8 here
+	    Connection conn2 = getConnectionWithProps(props2);
+	    PreparedStatement pstmt2 = conn2.prepareStatement(
+	            "INSERT INTO testUpdateNString (c1, c2) VALUES (?, ?)");
+	    pstmt2.setString(1, "1");
+	    pstmt2.setString(2, "aaa");
+	    pstmt2.execute();
+	    Statement stmt2 = conn2.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+	    ResultSet rs3 = stmt2.executeQuery("SELECT c1, c2 FROM testUpdateNString");
+	    rs3.next();
+	    try {
+	        rs3.updateNString("c2", "bbb"); // field's charset isn't utf8
+	        fail();
+	    } catch (SQLException ex) {
+	        assertEquals("Can not call updateNString() when field's character set isn't UTF-8",
+	                ex.getMessage());
+	    }
+	    rs3.close();
+	    pstmt2.close();
+	    stmt2.close();
+	    conn2.close();      
+	}
+}
\ No newline at end of file

Added: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-cert.pem
===================================================================
--- trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-cert.pem	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-cert.pem	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEODCCAyCgAwIBAgIBADANBgkqhkiG9w0BAQQFADB1MQswCQYDVQQGEwJVUzER
+MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xDjAMBgNVBAoTBU15
+U1FMMRIwEAYDVQQDEwlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEWDm1hcmtAbXlz
+cWwuY29tMB4XDTA3MDMwMTE5MjQwOFoXDTM0MDcxNzE5MjQwOFowdTELMAkGA1UE
+BhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMQ4wDAYD
+VQQKEwVNeVNRTDESMBAGA1UEAxMJbG9jYWxob3N0MR0wGwYJKoZIhvcNAQkBFg5t
+YXJrQG15c3FsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXs
+7+0oCBm6e8l5BKS4FsJ6pBrpRWTkPStvDdR798bHhdagG/5cbIuWR9p+ldD9tlW1
+oiZ1bCfNoioI6wqq0C9Odq2Eah1JLcpY4JOVR26ykDvi6zMdYy2UKBnsjf0sg6g6
+z9wdAOxncpawLe/iHAfNxxjFkGd0tsy4xkLwTj8Zu5WZ6X8hdq/smWZySQxXcX49
+ebShZ4aRZHP55zF//nkvS2JOn2VDjmtsUDmk5q13ck/LKkyoQNNW2FOw0XPFUgVo
+u/pjX3R2MuVGXS4xG/11B8IDFT50HsjbyoxUbtcz50C5vUumAgrc6pW+MMgrpbEX
+mhNMcH+DxbV5LpdcM6cCAwEAAaOB0jCBzzAdBgNVHQ4EFgQU0jDZNm09mgWbUDhB
+sQmavi3M/yEwgZ8GA1UdIwSBlzCBlIAU0jDZNm09mgWbUDhBsQmavi3M/yGheaR3
+MHUxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhJbGxpbm9pczEQMA4GA1UEBxMHQ2hp
+Y2FnbzEOMAwGA1UEChMFTXlTUUwxEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsGCSqG
+SIb3DQEJARYObWFya0BteXNxbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG
+9w0BAQQFAAOCAQEAGdOVI7DhEEcd6jLY2gj2cnDGiNMEFDQWbVX6mBoNpxmsEA6b
++h+pxlEtnzgW0itCeeKSW5Vl0hoSfoUxdY8Qx716DIdgTfl+FF8dhP1yRtUistYA
+Qsm4+fZi1IvyIBybtXIpvO3rXFtGEyp1Hl8nLZc23Q/svK+A75Cb8yO9tpunKBlA
+KBBJYnx9hOgmTVIPbYxex8Xu9IADZp4YHCwoQlCdUW3T/oaoeiiGj7KWH01K3M7Y
+iyHzjrCIFO1/ztbrknoCTrN5SjiN4+UpTcM1NzoWa8+9dz+FYQgngwvEdSEY+SqJ
+ui5YU0e/AIDSj/TtKApZGRrIbMn9/XNrSTOzNg==
+-----END CERTIFICATE-----

Added: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-key.pem
===================================================================
--- trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-key.pem	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/ssl-test-certs/ca-key.pem	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEApezv7SgIGbp7yXkEpLgWwnqkGulFZOQ9K28N1Hv3xseF1qAb
+/lxsi5ZH2n6V0P22VbWiJnVsJ82iKgjrCqrQL052rYRqHUktyljgk5VHbrKQO+Lr
+Mx1jLZQoGeyN/SyDqDrP3B0A7GdylrAt7+IcB83HGMWQZ3S2zLjGQvBOPxm7lZnp
+fyF2r+yZZnJJDFdxfj15tKFnhpFkc/nnMX/+eS9LYk6fZUOOa2xQOaTmrXdyT8sq
+TKhA01bYU7DRc8VSBWi7+mNfdHYy5UZdLjEb/XUHwgMVPnQeyNvKjFRu1zPnQLm9
+S6YCCtzqlb4wyCulsReaE0xwf4PFtXkul1wzpwIDAQABAoIBAFzmFMsOT8K/81Cx
+LhVfcHbAnaLnmk/LrifkTLT8F5U23st051d5vdhFcsN1fkH92xmP0VKBCeYbLsEk
+LxzQ2DC0WKP78EGnWDjjnBFlUhI7bZ/qA8+hk4bQXcdt922Zf7kv/HL71imGHdUa
+nQ2i5hjEcMWAz8HsyfBDP9X4n4aZ5W9FjJ6uD/mPvbJkWWh3HDPSICWIJ/SanJyb
+7ftKs8Bn77i5t85eTeX33gJfRWdoyg9mtNbhMPo+4TGyroMYS9HF3/tMGA5U0a8G
+PUUvNz5xmKu2yZs/9QFGIEVrbA9mMxJb+Yvq/xuGij/StgSRARWjiQAVmlIPYEG1
+fypeq0ECgYEA3DMFpwUbZXSmL2pnXO9G4wea2B4KpDzog9uidqe5H0oRVu0KeMPO
+s4vUzfwmtO4uYxBGFlRGFc0Pz8+72K+3rtECGzU464shVzqODQ3U/qCelMOgwIpd
+zD6hJWCPBYxiPx+fK0CnnlouQGNEKfkArK5sDIEyub2M3vsP0O9IumECgYEAwOb4
+nWWVeJTk5blp8NvrKcK6obPHG9UCrmjMPb7YOtSk2cDSbNvPSVefWdCeLz6o3J+C
+JWTr5Ch1Na3lN3YDFQt2jsostfcDjN1rxLS2Famh7U8U5mgnmGBN9c4cER7gkePs
+gNbBKvGznOrzYd5c+lhyFmqdTr/xfC2Ce7Ti+wcCgYAmVjO7umXkqgRs6OeKBJag
+3CgXTFOp+Hi6zDhi1Byk1+5g5fAWynBoTgYEvsu2YGlvEdxB8vYowYTpN0+LEDy4
+kOb++aspRxXM7ALjPu9ATZKOb+jC3NQrxROlQERmQOSx/L1neGA2b5jx2NZ7QEDf
+z5Y8Lxo3wEqQX8GHLMHKwQKBgE9vDRNrYFiOH4/FgS2Qsc65hDbQG/6HAZqmRuxq
+KQpYxCVy9HOlND+EWHcYo/rad5iaCnKxkz2Zt2JKYGgtLWm+Fa0I7c5kkE6yYw1W
+SOC0wJxDA105qIxjo9bDb+VQ7LLGw33ucQ7pBshoK56yfWENiLVYY7yo55z19JRS
+UWGDAoGAHipLHIVjo19t+TKd7G3HPuAuoVuL6whLu6N97blcxMjNb00bjC+D3ErD
+PpHrqO2aetJIisMPK4tVieEGSVe9x5tiHcCkl54ymlLKIVAXpywwiEEDO1NULOw8
+5hl+JBHmVZLm1AkOeoaTanTXsIrfomSX0jXZGEmBXpiHwuMdzaE=
+-----END RSA PRIVATE KEY-----

Added: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-cert.pem
===================================================================
--- trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-cert.pem	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-cert.pem	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDZzCCAk8CAQEwDQYJKoZIhvcNAQEEBQAwdTELMAkGA1UEBhMCVVMxETAPBgNV
+BAgTCElsbGlub2lzMRAwDgYDVQQHEwdDaGljYWdvMQ4wDAYDVQQKEwVNeVNRTDES
+MBAGA1UEAxMJbG9jYWxob3N0MR0wGwYJKoZIhvcNAQkBFg5tYXJrQG15c3FsLmNv
+bTAeFw0wNzAzMDExOTI1MzNaFw0zNDA3MTcxOTI1MzNaMH4xCzAJBgNVBAYTAlVT
+MREwDwYDVQQIEwhJbGxpbm9pczEQMA4GA1UEBxMHQ2hpY2FnbzEXMBUGA1UEChMO
+TXkgQ29tcGFueSBMdGQxEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsGCSqGSIb3DQEJ
+ARYObWFya0BteXNxbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCumPtqI5UGMMBPnm0Pw0VS4QTeFJx85z7e6YNxUc0hgRk5+wMGfbimrBQgiwqQ
+gB7o5LeKwtIAXbimL2T7EgO+f6PHoZG0x6DT2ejAreST6AjaBSn32pq0qPbyA1jB
+dJWbulqnXoq5m+pwE8YBkWMb9gJs5NqcPiruPPSaDeGGKUz0ROQczamQV0PvF9c8
+HYjHwp/yiyitIU8Y6gQJUO3CsX7BFR0Tr3Sp7qqPrTJMGHcY/STxgRKiEjyJ732P
+B1BjQ9ZPV+YujZ9m/GCmOYvnVgjI+bPTxuIH9LcDqV3mI6I7VGVCR+sUaJO4hZ77
+TQMSlwNYndCHgwVeCvVGF/2RAgMBAAEwDQYJKoZIhvcNAQEEBQADggEBAB8Y80Bm
+Xei4miEi+nhaYoJA9OK4EBnb00O+/H3Ow/8cv3/hoRQYuEsP83qjBt11vlif2Qpx
+J0Ip0RCXgLUy+PQqmOpMiNuzjCyb9UuzhkVjHKw8mFWhRYKhJ5BVp0KbHy6YEaAH
+Pb/Jq2ymrkMxKjbGXorZ0ZtjX+mv3RY2YEM0toQIKBylOLyrcVbQ4Q4mr0r8OVnU
+HtIpOgLIJX7+GboEz6uafRb+2JDSfobnS+miFbURhGbkQtYor9TcBdwGItEo0SkS
+9eaPq5pw6Q10t9uxOA90Tc183mftt70pwg0f/oMsOD1fIuGlUPksIp1SmVvW3cD/
+0a1JGmTYzLLxMv0=
+-----END CERTIFICATE-----

Added: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-key.pem
===================================================================
--- trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-key.pem	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-key.pem	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEArpj7aiOVBjDAT55tD8NFUuEE3hScfOc+3umDcVHNIYEZOfsD
+Bn24pqwUIIsKkIAe6OS3isLSAF24pi9k+xIDvn+jx6GRtMeg09nowK3kk+gI2gUp
+99qatKj28gNYwXSVm7pap16KuZvqcBPGAZFjG/YCbOTanD4q7jz0mg3hhilM9ETk
+HM2pkFdD7xfXPB2Ix8Kf8osorSFPGOoECVDtwrF+wRUdE690qe6qj60yTBh3GP0k
+8YESohI8ie99jwdQY0PWT1fmLo2fZvxgpjmL51YIyPmz08biB/S3A6ld5iOiO1Rl
+QkfrFGiTuIWe+00DEpcDWJ3Qh4MFXgr1Rhf9kQIDAQABAoIBAQCtVFg7TWmzRJCW
+OhvhSyS2G4c+hU25PcSPfVitFd1EBqqpgcTGG+dFJ42vpPju7RJCeFSx36ilDmWy
+LVjdW0v2QTaxh3dj9c52olzLc5Z5MyAeeIPqd82fziGhrqrdGsUvXTiD8Xpcuylo
+7GhkxBWU+4v62NMhyyHJcyANE/ywtwmcsPgrg2iitOitZRKKoq2H1ypDLsXGfQ2w
+zErJE/iz8Khlf+eNFToP9S3spALzRuXblrHdwLanw+Bn+uc6nYdfQkJtPgNUBV4Q
++6pRcq6qmpezOFR5+9EtZgvQ3uFisCPFkMcogiaherY4g1nfpEk+0pPyxQep6Xkf
+mTsBlupBAoGBANp6Y67ksONlJkQDkGHodkpdbhY7JYrhRvSjpEwp8FirtQ29Tto5
+HRIUivA2nysB6kWnZsAV9duQbA9x0CkFzUv8Ilxpg2v/do8svPQB5kSlJlRpjPxm
+fUohZrJPiVwzE8jpxEq/20bNnLXA29vUh2jnn0CnP74wo63hci3KlPt5AoGBAMyV
+V5T/V9ynzKlcnos5k6PgEUzbH99uYBRncBDdIKavuXtv9K/yABP4VxYCTyOCQGIP
+Tnv5/iHi3dc052yYWVF+Yfzpa7Mz2jQ3rOuBmtrz8UQ3gqBiN8KmqT4AhhDPiCYS
+RR2l9kWBUh1cD3l9GZ8tJv2OPOlg7d1l/nyXvHTZAoGAJdci4szbUldXnFodrMeN
+jW3HPnR6GSbRmlepgkOVTFZZIosSWDuiLFqAggCD6vgG8o9+/XAQSDoobThiPGiz
+2SBPd0ATMiZqC3xNMdoOXv9XtzEc6S57LtbAKcwa12xFVfwfMm8udIHyTYycx+N0
+nJfZfmI5aSCViO1OUF4RFckCgYBf4m7WCocniA3IIvy7zBSCbfYty1W45oZpicFg
+ZgNznFCWpfZMsGOQ+If4bMSio92BGCN0v8p4H1hSER2WLCFQrKdIXi54IwlWB/In
+vurRTBv8GPhEK0RvGqWfxacQIfRxw4ZZSvuNv/q4f/lQu21SyVrI4+1whFl3tuH7
+izJsoQKBgF18X5RBAgr9jURXBMl1PZND3I7BmaSyOrJanBwF08ljNPFPY4XSI05e
+v7Lwq8EqsnM27OPZlWwL7lCi92j7CbHLYRmOCtCCltWyIZ0UAjN592YvuipBRwcW
+3pvmi/QG7JVCMa97IjWOCg8eHtBWZ+OQCTbzOChb8ea8StD3Qwi1
+-----END RSA PRIVATE KEY-----

Added: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-req.pem
===================================================================
--- trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-req.pem	                        (rev 0)
+++ trunk/mysql-connector-java/src/testsuite/ssl-test-certs/server-req.pem	2007-11-30 10:46:51 UTC (rev 4921)
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICwzCCAasCAQAwfjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAw
+DgYDVQQHEwdDaGljYWdvMRcwFQYDVQQKEw5NeSBDb21wYW55IEx0ZDESMBAGA1UE
+AxMJbG9jYWxob3N0MR0wGwYJKoZIhvcNAQkBFg5tYXJrQG15c3FsLmNvbTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK6Y+2ojlQYwwE+ebQ/DRVLhBN4U
+nHznPt7pg3FRzSGBGTn7AwZ9uKasFCCLCpCAHujkt4rC0gBduKYvZPsSA75/o8eh
+kbTHoNPZ6MCt5JPoCNoFKffamrSo9vIDWMF0lZu6Wqdeirmb6nATxgGRYxv2Amzk
+2pw+Ku489JoN4YYpTPRE5BzNqZBXQ+8X1zwdiMfCn/KLKK0hTxjqBAlQ7cKxfsEV
+HROvdKnuqo+tMkwYdxj9JPGBEqISPInvfY8HUGND1k9X5i6Nn2b8YKY5i+dWCMj5
+s9PG4gf0twOpXeYjojtUZUJH6xRok7iFnvtNAxKXA1id0IeDBV4K9UYX/ZECAwEA
+AaAAMA0GCSqGSIb3DQEBBAUAA4IBAQAEnnyg8NQVrvjkvGgtXrzNGV4gVMhTeLBO
+kb1njtvgePgTaOplMFnW/ZRysfHTFW+96gPzPAdzNHgdvbM1gmcYvmWdm0DbAu13
+VV7QU/9yfaspbuhgcFI/UP4IPe202NZRwJQmaiZmloI5PtUoHHDieybKdnu0yZKQ
+WLDE4DkvNjJ8XIG4C7SjjPauK9nQP0Gv7JUKpliR7Vc6bMUffxrtDgb+0MJw806l
+x20tA7x06sk49a4u0tQquRi1EMijIAVhMjbqZ4/aWzDuBSdCdMGNYEnZHELLe7Aq
+dqphd24vd+r4paQ9wFXxr71mXIlw1thWXkCwYNFUIj9Yo9v0ZTny
+-----END CERTIFICATE REQUEST-----

Added: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/test-cert-store
===================================================================
(Binary files differ)


Property changes on: trunk/mysql-connector-java/src/testsuite/ssl-test-certs/test-cert-store
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream




More information about the pkg-java-commits mailing list